@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
|
@@ -2095,6 +2095,14 @@ async function ensureSchema() {
|
|
|
2095
2095
|
);
|
|
2096
2096
|
} catch {
|
|
2097
2097
|
}
|
|
2098
|
+
try {
|
|
2099
|
+
await client.execute(
|
|
2100
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2101
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2102
|
+
WHERE content_hash IS NOT NULL`
|
|
2103
|
+
);
|
|
2104
|
+
} catch {
|
|
2105
|
+
}
|
|
2098
2106
|
await client.executeMultiple(`
|
|
2099
2107
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2100
2108
|
id TEXT PRIMARY KEY,
|
|
@@ -2764,7 +2772,8 @@ async function ensureShardSchema(client) {
|
|
|
2764
2772
|
}
|
|
2765
2773
|
for (const idx of [
|
|
2766
2774
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2767
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2775
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2776
|
+
"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"
|
|
2768
2777
|
]) {
|
|
2769
2778
|
try {
|
|
2770
2779
|
await client.execute(idx);
|
|
@@ -3973,7 +3982,6 @@ import { spawnSync } from "child_process";
|
|
|
3973
3982
|
// src/lib/store.ts
|
|
3974
3983
|
init_memory();
|
|
3975
3984
|
init_database();
|
|
3976
|
-
import { createHash } from "crypto";
|
|
3977
3985
|
|
|
3978
3986
|
// src/lib/keychain.ts
|
|
3979
3987
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -4206,6 +4214,87 @@ var StateBus = class {
|
|
|
4206
4214
|
};
|
|
4207
4215
|
var orgBus = new StateBus();
|
|
4208
4216
|
|
|
4217
|
+
// src/lib/memory-write-governor.ts
|
|
4218
|
+
import { createHash } from "crypto";
|
|
4219
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
4220
|
+
"decision",
|
|
4221
|
+
"adr",
|
|
4222
|
+
"behavior",
|
|
4223
|
+
"procedure"
|
|
4224
|
+
]);
|
|
4225
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4226
|
+
try {
|
|
4227
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4228
|
+
const client = getClient2();
|
|
4229
|
+
const current = await client.execute({
|
|
4230
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4231
|
+
importance, timestamp
|
|
4232
|
+
FROM memories
|
|
4233
|
+
WHERE id = ?
|
|
4234
|
+
LIMIT 1`,
|
|
4235
|
+
args: [memoryId]
|
|
4236
|
+
});
|
|
4237
|
+
const row = current.rows[0];
|
|
4238
|
+
if (!row) return;
|
|
4239
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
4240
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4241
|
+
const agentId = String(row.agent_id);
|
|
4242
|
+
const projectName = String(row.project_name);
|
|
4243
|
+
if (contentHash) {
|
|
4244
|
+
await client.execute({
|
|
4245
|
+
sql: `UPDATE memories
|
|
4246
|
+
SET status = 'deleted',
|
|
4247
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4248
|
+
WHERE id != ?
|
|
4249
|
+
AND content_hash = ?
|
|
4250
|
+
AND agent_id = ?
|
|
4251
|
+
AND project_name = ?
|
|
4252
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4253
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
4254
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4255
|
+
});
|
|
4256
|
+
}
|
|
4257
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
4258
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
4259
|
+
const old = await client.execute({
|
|
4260
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
4261
|
+
args: [supersedesId]
|
|
4262
|
+
});
|
|
4263
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
4264
|
+
const newImportance = Number(row.importance ?? 0);
|
|
4265
|
+
await client.batch([
|
|
4266
|
+
{
|
|
4267
|
+
sql: `UPDATE memories
|
|
4268
|
+
SET status = 'archived',
|
|
4269
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4270
|
+
WHERE id = ?`,
|
|
4271
|
+
args: [supersedesId]
|
|
4272
|
+
},
|
|
4273
|
+
{
|
|
4274
|
+
sql: `UPDATE memories
|
|
4275
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
4276
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
4277
|
+
WHERE id = ?`,
|
|
4278
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
4279
|
+
}
|
|
4280
|
+
], "write");
|
|
4281
|
+
}
|
|
4282
|
+
} catch (err) {
|
|
4283
|
+
process.stderr.write(
|
|
4284
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4285
|
+
`
|
|
4286
|
+
);
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
4290
|
+
if (memoryIds.length === 0) return;
|
|
4291
|
+
const run = () => {
|
|
4292
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
4293
|
+
};
|
|
4294
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
4295
|
+
else setTimeout(run, 0);
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4209
4298
|
// src/lib/store.ts
|
|
4210
4299
|
var INIT_MAX_RETRIES = 3;
|
|
4211
4300
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -4405,6 +4494,7 @@ async function flushBatch() {
|
|
|
4405
4494
|
const globalClient = getClient();
|
|
4406
4495
|
const globalStmts = batch.map(buildStmt);
|
|
4407
4496
|
await globalClient.batch(globalStmts, "write");
|
|
4497
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4408
4498
|
_pendingRecords.splice(0, batch.length);
|
|
4409
4499
|
try {
|
|
4410
4500
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -2084,6 +2084,14 @@ async function ensureSchema() {
|
|
|
2084
2084
|
);
|
|
2085
2085
|
} catch {
|
|
2086
2086
|
}
|
|
2087
|
+
try {
|
|
2088
|
+
await client.execute(
|
|
2089
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2090
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2091
|
+
WHERE content_hash IS NOT NULL`
|
|
2092
|
+
);
|
|
2093
|
+
} catch {
|
|
2094
|
+
}
|
|
2087
2095
|
await client.executeMultiple(`
|
|
2088
2096
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2089
2097
|
id TEXT PRIMARY KEY,
|
|
@@ -2753,7 +2761,8 @@ async function ensureShardSchema(client) {
|
|
|
2753
2761
|
}
|
|
2754
2762
|
for (const idx of [
|
|
2755
2763
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2756
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2764
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2765
|
+
"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"
|
|
2757
2766
|
]) {
|
|
2758
2767
|
try {
|
|
2759
2768
|
await client.execute(idx);
|
|
@@ -3827,7 +3836,6 @@ import { spawnSync } from "child_process";
|
|
|
3827
3836
|
// src/lib/store.ts
|
|
3828
3837
|
init_memory();
|
|
3829
3838
|
init_database();
|
|
3830
|
-
import { createHash } from "crypto";
|
|
3831
3839
|
|
|
3832
3840
|
// src/lib/keychain.ts
|
|
3833
3841
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -4060,6 +4068,87 @@ var StateBus = class {
|
|
|
4060
4068
|
};
|
|
4061
4069
|
var orgBus = new StateBus();
|
|
4062
4070
|
|
|
4071
|
+
// src/lib/memory-write-governor.ts
|
|
4072
|
+
import { createHash } from "crypto";
|
|
4073
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
4074
|
+
"decision",
|
|
4075
|
+
"adr",
|
|
4076
|
+
"behavior",
|
|
4077
|
+
"procedure"
|
|
4078
|
+
]);
|
|
4079
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4080
|
+
try {
|
|
4081
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4082
|
+
const client = getClient2();
|
|
4083
|
+
const current = await client.execute({
|
|
4084
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4085
|
+
importance, timestamp
|
|
4086
|
+
FROM memories
|
|
4087
|
+
WHERE id = ?
|
|
4088
|
+
LIMIT 1`,
|
|
4089
|
+
args: [memoryId]
|
|
4090
|
+
});
|
|
4091
|
+
const row = current.rows[0];
|
|
4092
|
+
if (!row) return;
|
|
4093
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
4094
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4095
|
+
const agentId = String(row.agent_id);
|
|
4096
|
+
const projectName = String(row.project_name);
|
|
4097
|
+
if (contentHash) {
|
|
4098
|
+
await client.execute({
|
|
4099
|
+
sql: `UPDATE memories
|
|
4100
|
+
SET status = 'deleted',
|
|
4101
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4102
|
+
WHERE id != ?
|
|
4103
|
+
AND content_hash = ?
|
|
4104
|
+
AND agent_id = ?
|
|
4105
|
+
AND project_name = ?
|
|
4106
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4107
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
4108
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
4112
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
4113
|
+
const old = await client.execute({
|
|
4114
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
4115
|
+
args: [supersedesId]
|
|
4116
|
+
});
|
|
4117
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
4118
|
+
const newImportance = Number(row.importance ?? 0);
|
|
4119
|
+
await client.batch([
|
|
4120
|
+
{
|
|
4121
|
+
sql: `UPDATE memories
|
|
4122
|
+
SET status = 'archived',
|
|
4123
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4124
|
+
WHERE id = ?`,
|
|
4125
|
+
args: [supersedesId]
|
|
4126
|
+
},
|
|
4127
|
+
{
|
|
4128
|
+
sql: `UPDATE memories
|
|
4129
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
4130
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
4131
|
+
WHERE id = ?`,
|
|
4132
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
4133
|
+
}
|
|
4134
|
+
], "write");
|
|
4135
|
+
}
|
|
4136
|
+
} catch (err) {
|
|
4137
|
+
process.stderr.write(
|
|
4138
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4139
|
+
`
|
|
4140
|
+
);
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
4144
|
+
if (memoryIds.length === 0) return;
|
|
4145
|
+
const run = () => {
|
|
4146
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
4147
|
+
};
|
|
4148
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
4149
|
+
else setTimeout(run, 0);
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4063
4152
|
// src/lib/store.ts
|
|
4064
4153
|
var INIT_MAX_RETRIES = 3;
|
|
4065
4154
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -4259,6 +4348,7 @@ async function flushBatch() {
|
|
|
4259
4348
|
const globalClient = getClient();
|
|
4260
4349
|
const globalStmts = batch.map(buildStmt);
|
|
4261
4350
|
await globalClient.batch(globalStmts, "write");
|
|
4351
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4262
4352
|
_pendingRecords.splice(0, batch.length);
|
|
4263
4353
|
try {
|
|
4264
4354
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
package/dist/bin/exe-status.js
CHANGED
|
@@ -2310,6 +2310,14 @@ async function ensureSchema() {
|
|
|
2310
2310
|
);
|
|
2311
2311
|
} catch {
|
|
2312
2312
|
}
|
|
2313
|
+
try {
|
|
2314
|
+
await client.execute(
|
|
2315
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2316
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2317
|
+
WHERE content_hash IS NOT NULL`
|
|
2318
|
+
);
|
|
2319
|
+
} catch {
|
|
2320
|
+
}
|
|
2313
2321
|
await client.executeMultiple(`
|
|
2314
2322
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2315
2323
|
id TEXT PRIMARY KEY,
|
|
@@ -3055,6 +3063,196 @@ var init_state_bus = __esm({
|
|
|
3055
3063
|
}
|
|
3056
3064
|
});
|
|
3057
3065
|
|
|
3066
|
+
// src/lib/memory-write-governor.ts
|
|
3067
|
+
import { createHash } from "crypto";
|
|
3068
|
+
function normalizeMemoryText(text) {
|
|
3069
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3070
|
+
}
|
|
3071
|
+
function classifyMemoryType(input) {
|
|
3072
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3073
|
+
const tool = input.tool_name.toLowerCase();
|
|
3074
|
+
const text = input.raw_text.toLowerCase();
|
|
3075
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3076
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3077
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3078
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3079
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3080
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3081
|
+
return "raw";
|
|
3082
|
+
}
|
|
3083
|
+
function shouldDropMemory(text) {
|
|
3084
|
+
const normalized = normalizeMemoryText(text);
|
|
3085
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3086
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3087
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3088
|
+
}
|
|
3089
|
+
return { drop: false };
|
|
3090
|
+
}
|
|
3091
|
+
function shouldSkipEmbedding(input) {
|
|
3092
|
+
const type = classifyMemoryType(input);
|
|
3093
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3094
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3095
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3096
|
+
return false;
|
|
3097
|
+
}
|
|
3098
|
+
function hashMemoryContent(text) {
|
|
3099
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3100
|
+
}
|
|
3101
|
+
function scopedDedupArgs(input) {
|
|
3102
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3103
|
+
}
|
|
3104
|
+
function governMemoryRecord(record) {
|
|
3105
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3106
|
+
const memoryType = classifyMemoryType({
|
|
3107
|
+
raw_text: normalized,
|
|
3108
|
+
agent_id: record.agent_id,
|
|
3109
|
+
project_name: record.project_name,
|
|
3110
|
+
tool_name: record.tool_name,
|
|
3111
|
+
memory_type: record.memory_type
|
|
3112
|
+
});
|
|
3113
|
+
const drop = shouldDropMemory(normalized);
|
|
3114
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3115
|
+
raw_text: normalized,
|
|
3116
|
+
agent_id: record.agent_id,
|
|
3117
|
+
project_name: record.project_name,
|
|
3118
|
+
tool_name: record.tool_name,
|
|
3119
|
+
memory_type: memoryType
|
|
3120
|
+
});
|
|
3121
|
+
return {
|
|
3122
|
+
record: {
|
|
3123
|
+
...record,
|
|
3124
|
+
raw_text: normalized,
|
|
3125
|
+
memory_type: memoryType,
|
|
3126
|
+
vector: skipEmbedding ? null : record.vector
|
|
3127
|
+
},
|
|
3128
|
+
contentHash: hashMemoryContent(normalized),
|
|
3129
|
+
shouldDrop: drop.drop,
|
|
3130
|
+
dropReason: drop.reason,
|
|
3131
|
+
skipEmbedding,
|
|
3132
|
+
hygiene: {
|
|
3133
|
+
dedup: true,
|
|
3134
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
async function findScopedDuplicate(input) {
|
|
3139
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3140
|
+
const client = getClient2();
|
|
3141
|
+
const args = scopedDedupArgs(input);
|
|
3142
|
+
let sql = `SELECT id FROM memories
|
|
3143
|
+
WHERE content_hash = ?
|
|
3144
|
+
AND agent_id = ?
|
|
3145
|
+
AND project_name = ?
|
|
3146
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3147
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3148
|
+
if (input.excludeId) {
|
|
3149
|
+
sql += " AND id != ?";
|
|
3150
|
+
args.push(input.excludeId);
|
|
3151
|
+
}
|
|
3152
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3153
|
+
const result = await client.execute({ sql, args });
|
|
3154
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3155
|
+
}
|
|
3156
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3157
|
+
try {
|
|
3158
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3159
|
+
const client = getClient2();
|
|
3160
|
+
const current = await client.execute({
|
|
3161
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3162
|
+
importance, timestamp
|
|
3163
|
+
FROM memories
|
|
3164
|
+
WHERE id = ?
|
|
3165
|
+
LIMIT 1`,
|
|
3166
|
+
args: [memoryId]
|
|
3167
|
+
});
|
|
3168
|
+
const row = current.rows[0];
|
|
3169
|
+
if (!row) return;
|
|
3170
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3171
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3172
|
+
const agentId = String(row.agent_id);
|
|
3173
|
+
const projectName = String(row.project_name);
|
|
3174
|
+
if (contentHash) {
|
|
3175
|
+
await client.execute({
|
|
3176
|
+
sql: `UPDATE memories
|
|
3177
|
+
SET status = 'deleted',
|
|
3178
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3179
|
+
WHERE id != ?
|
|
3180
|
+
AND content_hash = ?
|
|
3181
|
+
AND agent_id = ?
|
|
3182
|
+
AND project_name = ?
|
|
3183
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3184
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3185
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3189
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3190
|
+
const old = await client.execute({
|
|
3191
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3192
|
+
args: [supersedesId]
|
|
3193
|
+
});
|
|
3194
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3195
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3196
|
+
await client.batch([
|
|
3197
|
+
{
|
|
3198
|
+
sql: `UPDATE memories
|
|
3199
|
+
SET status = 'archived',
|
|
3200
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3201
|
+
WHERE id = ?`,
|
|
3202
|
+
args: [supersedesId]
|
|
3203
|
+
},
|
|
3204
|
+
{
|
|
3205
|
+
sql: `UPDATE memories
|
|
3206
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3207
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3208
|
+
WHERE id = ?`,
|
|
3209
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3210
|
+
}
|
|
3211
|
+
], "write");
|
|
3212
|
+
}
|
|
3213
|
+
} catch (err) {
|
|
3214
|
+
process.stderr.write(
|
|
3215
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3216
|
+
`
|
|
3217
|
+
);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3221
|
+
if (memoryIds.length === 0) return;
|
|
3222
|
+
const run = () => {
|
|
3223
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3224
|
+
};
|
|
3225
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3226
|
+
else setTimeout(run, 0);
|
|
3227
|
+
}
|
|
3228
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3229
|
+
var init_memory_write_governor = __esm({
|
|
3230
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3231
|
+
"use strict";
|
|
3232
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3233
|
+
"decision",
|
|
3234
|
+
"adr",
|
|
3235
|
+
"behavior",
|
|
3236
|
+
"procedure"
|
|
3237
|
+
]);
|
|
3238
|
+
NOISE_DROP_PATTERNS = [
|
|
3239
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3240
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3241
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3242
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3243
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3244
|
+
];
|
|
3245
|
+
SKIP_EMBED_PATTERNS = [
|
|
3246
|
+
/tmux capture-pane\b/i,
|
|
3247
|
+
/docker ps\b/i,
|
|
3248
|
+
/docker images\b/i,
|
|
3249
|
+
/git status\b/i,
|
|
3250
|
+
/grep .*node_modules/i,
|
|
3251
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3252
|
+
];
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
|
|
3058
3256
|
// src/lib/shard-manager.ts
|
|
3059
3257
|
var shard_manager_exports = {};
|
|
3060
3258
|
__export(shard_manager_exports, {
|
|
@@ -3219,7 +3417,8 @@ async function ensureShardSchema(client) {
|
|
|
3219
3417
|
}
|
|
3220
3418
|
for (const idx of [
|
|
3221
3419
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3222
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3420
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3421
|
+
"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"
|
|
3223
3422
|
]) {
|
|
3224
3423
|
try {
|
|
3225
3424
|
await client.execute(idx);
|
|
@@ -3644,7 +3843,6 @@ __export(store_exports, {
|
|
|
3644
3843
|
vectorToBlob: () => vectorToBlob,
|
|
3645
3844
|
writeMemory: () => writeMemory
|
|
3646
3845
|
});
|
|
3647
|
-
import { createHash } from "crypto";
|
|
3648
3846
|
function isBusyError2(err) {
|
|
3649
3847
|
if (err instanceof Error) {
|
|
3650
3848
|
const msg = err.message.toLowerCase();
|
|
@@ -3758,17 +3956,24 @@ async function writeMemory(record) {
|
|
|
3758
3956
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3759
3957
|
);
|
|
3760
3958
|
}
|
|
3761
|
-
const
|
|
3762
|
-
if (
|
|
3959
|
+
const governed = governMemoryRecord(record);
|
|
3960
|
+
if (governed.shouldDrop) return;
|
|
3961
|
+
record = governed.record;
|
|
3962
|
+
const contentHash = governed.contentHash;
|
|
3963
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3964
|
+
if (_pendingRecords.some(
|
|
3965
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3966
|
+
)) {
|
|
3763
3967
|
return;
|
|
3764
3968
|
}
|
|
3765
3969
|
try {
|
|
3766
|
-
const
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3970
|
+
const existing = await findScopedDuplicate({
|
|
3971
|
+
contentHash,
|
|
3972
|
+
agentId: record.agent_id,
|
|
3973
|
+
projectName: record.project_name,
|
|
3974
|
+
memoryType
|
|
3770
3975
|
});
|
|
3771
|
-
if (existing
|
|
3976
|
+
if (existing) return;
|
|
3772
3977
|
} catch {
|
|
3773
3978
|
}
|
|
3774
3979
|
const dbRow = {
|
|
@@ -3799,7 +4004,7 @@ async function writeMemory(record) {
|
|
|
3799
4004
|
tier: record.tier ?? classifyTier(record),
|
|
3800
4005
|
supersedes_id: record.supersedes_id ?? null,
|
|
3801
4006
|
draft: record.draft ? 1 : 0,
|
|
3802
|
-
memory_type:
|
|
4007
|
+
memory_type: memoryType,
|
|
3803
4008
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3804
4009
|
content_hash: contentHash,
|
|
3805
4010
|
intent: record.intent ?? null,
|
|
@@ -3958,6 +4163,7 @@ async function flushBatch() {
|
|
|
3958
4163
|
const globalClient = getClient();
|
|
3959
4164
|
const globalStmts = batch.map(buildStmt);
|
|
3960
4165
|
await globalClient.batch(globalStmts, "write");
|
|
4166
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3961
4167
|
_pendingRecords.splice(0, batch.length);
|
|
3962
4168
|
try {
|
|
3963
4169
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4216,6 +4422,7 @@ var init_store = __esm({
|
|
|
4216
4422
|
init_keychain();
|
|
4217
4423
|
init_config();
|
|
4218
4424
|
init_state_bus();
|
|
4425
|
+
init_memory_write_governor();
|
|
4219
4426
|
INIT_MAX_RETRIES = 3;
|
|
4220
4427
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4221
4428
|
_pendingRecords = [];
|