@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
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -2933,6 +2933,14 @@ async function ensureSchema() {
|
|
|
2933
2933
|
);
|
|
2934
2934
|
} catch {
|
|
2935
2935
|
}
|
|
2936
|
+
try {
|
|
2937
|
+
await client.execute(
|
|
2938
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2939
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2940
|
+
WHERE content_hash IS NOT NULL`
|
|
2941
|
+
);
|
|
2942
|
+
} catch {
|
|
2943
|
+
}
|
|
2936
2944
|
await client.executeMultiple(`
|
|
2937
2945
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2938
2946
|
id TEXT PRIMARY KEY,
|
|
@@ -3691,6 +3699,196 @@ var init_keychain = __esm({
|
|
|
3691
3699
|
}
|
|
3692
3700
|
});
|
|
3693
3701
|
|
|
3702
|
+
// src/lib/memory-write-governor.ts
|
|
3703
|
+
import { createHash } from "crypto";
|
|
3704
|
+
function normalizeMemoryText(text) {
|
|
3705
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3706
|
+
}
|
|
3707
|
+
function classifyMemoryType(input) {
|
|
3708
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3709
|
+
const tool = input.tool_name.toLowerCase();
|
|
3710
|
+
const text = input.raw_text.toLowerCase();
|
|
3711
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3712
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3713
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3714
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3715
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3716
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3717
|
+
return "raw";
|
|
3718
|
+
}
|
|
3719
|
+
function shouldDropMemory(text) {
|
|
3720
|
+
const normalized = normalizeMemoryText(text);
|
|
3721
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3722
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3723
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3724
|
+
}
|
|
3725
|
+
return { drop: false };
|
|
3726
|
+
}
|
|
3727
|
+
function shouldSkipEmbedding(input) {
|
|
3728
|
+
const type = classifyMemoryType(input);
|
|
3729
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3730
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3731
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3732
|
+
return false;
|
|
3733
|
+
}
|
|
3734
|
+
function hashMemoryContent(text) {
|
|
3735
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3736
|
+
}
|
|
3737
|
+
function scopedDedupArgs(input) {
|
|
3738
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3739
|
+
}
|
|
3740
|
+
function governMemoryRecord(record) {
|
|
3741
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3742
|
+
const memoryType = classifyMemoryType({
|
|
3743
|
+
raw_text: normalized,
|
|
3744
|
+
agent_id: record.agent_id,
|
|
3745
|
+
project_name: record.project_name,
|
|
3746
|
+
tool_name: record.tool_name,
|
|
3747
|
+
memory_type: record.memory_type
|
|
3748
|
+
});
|
|
3749
|
+
const drop = shouldDropMemory(normalized);
|
|
3750
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3751
|
+
raw_text: normalized,
|
|
3752
|
+
agent_id: record.agent_id,
|
|
3753
|
+
project_name: record.project_name,
|
|
3754
|
+
tool_name: record.tool_name,
|
|
3755
|
+
memory_type: memoryType
|
|
3756
|
+
});
|
|
3757
|
+
return {
|
|
3758
|
+
record: {
|
|
3759
|
+
...record,
|
|
3760
|
+
raw_text: normalized,
|
|
3761
|
+
memory_type: memoryType,
|
|
3762
|
+
vector: skipEmbedding ? null : record.vector
|
|
3763
|
+
},
|
|
3764
|
+
contentHash: hashMemoryContent(normalized),
|
|
3765
|
+
shouldDrop: drop.drop,
|
|
3766
|
+
dropReason: drop.reason,
|
|
3767
|
+
skipEmbedding,
|
|
3768
|
+
hygiene: {
|
|
3769
|
+
dedup: true,
|
|
3770
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3771
|
+
}
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
async function findScopedDuplicate(input) {
|
|
3775
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3776
|
+
const client = getClient2();
|
|
3777
|
+
const args = scopedDedupArgs(input);
|
|
3778
|
+
let sql = `SELECT id FROM memories
|
|
3779
|
+
WHERE content_hash = ?
|
|
3780
|
+
AND agent_id = ?
|
|
3781
|
+
AND project_name = ?
|
|
3782
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3783
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3784
|
+
if (input.excludeId) {
|
|
3785
|
+
sql += " AND id != ?";
|
|
3786
|
+
args.push(input.excludeId);
|
|
3787
|
+
}
|
|
3788
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3789
|
+
const result = await client.execute({ sql, args });
|
|
3790
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3791
|
+
}
|
|
3792
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3793
|
+
try {
|
|
3794
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3795
|
+
const client = getClient2();
|
|
3796
|
+
const current = await client.execute({
|
|
3797
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3798
|
+
importance, timestamp
|
|
3799
|
+
FROM memories
|
|
3800
|
+
WHERE id = ?
|
|
3801
|
+
LIMIT 1`,
|
|
3802
|
+
args: [memoryId]
|
|
3803
|
+
});
|
|
3804
|
+
const row = current.rows[0];
|
|
3805
|
+
if (!row) return;
|
|
3806
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3807
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3808
|
+
const agentId = String(row.agent_id);
|
|
3809
|
+
const projectName = String(row.project_name);
|
|
3810
|
+
if (contentHash) {
|
|
3811
|
+
await client.execute({
|
|
3812
|
+
sql: `UPDATE memories
|
|
3813
|
+
SET status = 'deleted',
|
|
3814
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3815
|
+
WHERE id != ?
|
|
3816
|
+
AND content_hash = ?
|
|
3817
|
+
AND agent_id = ?
|
|
3818
|
+
AND project_name = ?
|
|
3819
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3820
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3821
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3822
|
+
});
|
|
3823
|
+
}
|
|
3824
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3825
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3826
|
+
const old = await client.execute({
|
|
3827
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3828
|
+
args: [supersedesId]
|
|
3829
|
+
});
|
|
3830
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3831
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3832
|
+
await client.batch([
|
|
3833
|
+
{
|
|
3834
|
+
sql: `UPDATE memories
|
|
3835
|
+
SET status = 'archived',
|
|
3836
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3837
|
+
WHERE id = ?`,
|
|
3838
|
+
args: [supersedesId]
|
|
3839
|
+
},
|
|
3840
|
+
{
|
|
3841
|
+
sql: `UPDATE memories
|
|
3842
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3843
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3844
|
+
WHERE id = ?`,
|
|
3845
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3846
|
+
}
|
|
3847
|
+
], "write");
|
|
3848
|
+
}
|
|
3849
|
+
} catch (err) {
|
|
3850
|
+
process.stderr.write(
|
|
3851
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3852
|
+
`
|
|
3853
|
+
);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3857
|
+
if (memoryIds.length === 0) return;
|
|
3858
|
+
const run = () => {
|
|
3859
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3860
|
+
};
|
|
3861
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3862
|
+
else setTimeout(run, 0);
|
|
3863
|
+
}
|
|
3864
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3865
|
+
var init_memory_write_governor = __esm({
|
|
3866
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3867
|
+
"use strict";
|
|
3868
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3869
|
+
"decision",
|
|
3870
|
+
"adr",
|
|
3871
|
+
"behavior",
|
|
3872
|
+
"procedure"
|
|
3873
|
+
]);
|
|
3874
|
+
NOISE_DROP_PATTERNS = [
|
|
3875
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3876
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3877
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3878
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3879
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3880
|
+
];
|
|
3881
|
+
SKIP_EMBED_PATTERNS = [
|
|
3882
|
+
/tmux capture-pane\b/i,
|
|
3883
|
+
/docker ps\b/i,
|
|
3884
|
+
/docker images\b/i,
|
|
3885
|
+
/git status\b/i,
|
|
3886
|
+
/grep .*node_modules/i,
|
|
3887
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3888
|
+
];
|
|
3889
|
+
}
|
|
3890
|
+
});
|
|
3891
|
+
|
|
3694
3892
|
// src/lib/shard-manager.ts
|
|
3695
3893
|
var shard_manager_exports = {};
|
|
3696
3894
|
__export(shard_manager_exports, {
|
|
@@ -3855,7 +4053,8 @@ async function ensureShardSchema(client) {
|
|
|
3855
4053
|
}
|
|
3856
4054
|
for (const idx of [
|
|
3857
4055
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3858
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
4056
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
4057
|
+
"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"
|
|
3859
4058
|
]) {
|
|
3860
4059
|
try {
|
|
3861
4060
|
await client.execute(idx);
|
|
@@ -4280,7 +4479,6 @@ __export(store_exports, {
|
|
|
4280
4479
|
vectorToBlob: () => vectorToBlob,
|
|
4281
4480
|
writeMemory: () => writeMemory
|
|
4282
4481
|
});
|
|
4283
|
-
import { createHash } from "crypto";
|
|
4284
4482
|
function isBusyError2(err) {
|
|
4285
4483
|
if (err instanceof Error) {
|
|
4286
4484
|
const msg = err.message.toLowerCase();
|
|
@@ -4394,17 +4592,24 @@ async function writeMemory(record) {
|
|
|
4394
4592
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4395
4593
|
);
|
|
4396
4594
|
}
|
|
4397
|
-
const
|
|
4398
|
-
if (
|
|
4595
|
+
const governed = governMemoryRecord(record);
|
|
4596
|
+
if (governed.shouldDrop) return;
|
|
4597
|
+
record = governed.record;
|
|
4598
|
+
const contentHash = governed.contentHash;
|
|
4599
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4600
|
+
if (_pendingRecords.some(
|
|
4601
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4602
|
+
)) {
|
|
4399
4603
|
return;
|
|
4400
4604
|
}
|
|
4401
4605
|
try {
|
|
4402
|
-
const
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4606
|
+
const existing = await findScopedDuplicate({
|
|
4607
|
+
contentHash,
|
|
4608
|
+
agentId: record.agent_id,
|
|
4609
|
+
projectName: record.project_name,
|
|
4610
|
+
memoryType
|
|
4406
4611
|
});
|
|
4407
|
-
if (existing
|
|
4612
|
+
if (existing) return;
|
|
4408
4613
|
} catch {
|
|
4409
4614
|
}
|
|
4410
4615
|
const dbRow = {
|
|
@@ -4435,7 +4640,7 @@ async function writeMemory(record) {
|
|
|
4435
4640
|
tier: record.tier ?? classifyTier(record),
|
|
4436
4641
|
supersedes_id: record.supersedes_id ?? null,
|
|
4437
4642
|
draft: record.draft ? 1 : 0,
|
|
4438
|
-
memory_type:
|
|
4643
|
+
memory_type: memoryType,
|
|
4439
4644
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4440
4645
|
content_hash: contentHash,
|
|
4441
4646
|
intent: record.intent ?? null,
|
|
@@ -4594,6 +4799,7 @@ async function flushBatch() {
|
|
|
4594
4799
|
const globalClient = getClient();
|
|
4595
4800
|
const globalStmts = batch.map(buildStmt);
|
|
4596
4801
|
await globalClient.batch(globalStmts, "write");
|
|
4802
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4597
4803
|
_pendingRecords.splice(0, batch.length);
|
|
4598
4804
|
try {
|
|
4599
4805
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4852,6 +5058,7 @@ var init_store = __esm({
|
|
|
4852
5058
|
init_keychain();
|
|
4853
5059
|
init_config();
|
|
4854
5060
|
init_state_bus();
|
|
5061
|
+
init_memory_write_governor();
|
|
4855
5062
|
INIT_MAX_RETRIES = 3;
|
|
4856
5063
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4857
5064
|
_pendingRecords = [];
|
|
@@ -11377,7 +11584,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
11377
11584
|
}
|
|
11378
11585
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
11379
11586
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
11380
|
-
|
|
11587
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
11588
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
11589
|
+
ccModel += "[1m]";
|
|
11590
|
+
}
|
|
11591
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
11381
11592
|
}
|
|
11382
11593
|
}
|
|
11383
11594
|
let spawnCommand;
|
|
@@ -2318,6 +2318,14 @@ async function ensureSchema() {
|
|
|
2318
2318
|
);
|
|
2319
2319
|
} catch {
|
|
2320
2320
|
}
|
|
2321
|
+
try {
|
|
2322
|
+
await client.execute(
|
|
2323
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2324
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2325
|
+
WHERE content_hash IS NOT NULL`
|
|
2326
|
+
);
|
|
2327
|
+
} catch {
|
|
2328
|
+
}
|
|
2321
2329
|
await client.executeMultiple(`
|
|
2322
2330
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2323
2331
|
id TEXT PRIMARY KEY,
|
|
@@ -3063,6 +3071,196 @@ var init_state_bus = __esm({
|
|
|
3063
3071
|
}
|
|
3064
3072
|
});
|
|
3065
3073
|
|
|
3074
|
+
// src/lib/memory-write-governor.ts
|
|
3075
|
+
import { createHash } from "crypto";
|
|
3076
|
+
function normalizeMemoryText(text) {
|
|
3077
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3078
|
+
}
|
|
3079
|
+
function classifyMemoryType(input) {
|
|
3080
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3081
|
+
const tool = input.tool_name.toLowerCase();
|
|
3082
|
+
const text = input.raw_text.toLowerCase();
|
|
3083
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3084
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3085
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3086
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3087
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3088
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3089
|
+
return "raw";
|
|
3090
|
+
}
|
|
3091
|
+
function shouldDropMemory(text) {
|
|
3092
|
+
const normalized = normalizeMemoryText(text);
|
|
3093
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3094
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3095
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3096
|
+
}
|
|
3097
|
+
return { drop: false };
|
|
3098
|
+
}
|
|
3099
|
+
function shouldSkipEmbedding(input) {
|
|
3100
|
+
const type = classifyMemoryType(input);
|
|
3101
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3102
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3103
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
function hashMemoryContent(text) {
|
|
3107
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3108
|
+
}
|
|
3109
|
+
function scopedDedupArgs(input) {
|
|
3110
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3111
|
+
}
|
|
3112
|
+
function governMemoryRecord(record) {
|
|
3113
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3114
|
+
const memoryType = classifyMemoryType({
|
|
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: record.memory_type
|
|
3120
|
+
});
|
|
3121
|
+
const drop = shouldDropMemory(normalized);
|
|
3122
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3123
|
+
raw_text: normalized,
|
|
3124
|
+
agent_id: record.agent_id,
|
|
3125
|
+
project_name: record.project_name,
|
|
3126
|
+
tool_name: record.tool_name,
|
|
3127
|
+
memory_type: memoryType
|
|
3128
|
+
});
|
|
3129
|
+
return {
|
|
3130
|
+
record: {
|
|
3131
|
+
...record,
|
|
3132
|
+
raw_text: normalized,
|
|
3133
|
+
memory_type: memoryType,
|
|
3134
|
+
vector: skipEmbedding ? null : record.vector
|
|
3135
|
+
},
|
|
3136
|
+
contentHash: hashMemoryContent(normalized),
|
|
3137
|
+
shouldDrop: drop.drop,
|
|
3138
|
+
dropReason: drop.reason,
|
|
3139
|
+
skipEmbedding,
|
|
3140
|
+
hygiene: {
|
|
3141
|
+
dedup: true,
|
|
3142
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3143
|
+
}
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
async function findScopedDuplicate(input) {
|
|
3147
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3148
|
+
const client = getClient2();
|
|
3149
|
+
const args = scopedDedupArgs(input);
|
|
3150
|
+
let sql = `SELECT id FROM memories
|
|
3151
|
+
WHERE content_hash = ?
|
|
3152
|
+
AND agent_id = ?
|
|
3153
|
+
AND project_name = ?
|
|
3154
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3155
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3156
|
+
if (input.excludeId) {
|
|
3157
|
+
sql += " AND id != ?";
|
|
3158
|
+
args.push(input.excludeId);
|
|
3159
|
+
}
|
|
3160
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3161
|
+
const result = await client.execute({ sql, args });
|
|
3162
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3163
|
+
}
|
|
3164
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3165
|
+
try {
|
|
3166
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3167
|
+
const client = getClient2();
|
|
3168
|
+
const current = await client.execute({
|
|
3169
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3170
|
+
importance, timestamp
|
|
3171
|
+
FROM memories
|
|
3172
|
+
WHERE id = ?
|
|
3173
|
+
LIMIT 1`,
|
|
3174
|
+
args: [memoryId]
|
|
3175
|
+
});
|
|
3176
|
+
const row = current.rows[0];
|
|
3177
|
+
if (!row) return;
|
|
3178
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3179
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3180
|
+
const agentId = String(row.agent_id);
|
|
3181
|
+
const projectName = String(row.project_name);
|
|
3182
|
+
if (contentHash) {
|
|
3183
|
+
await client.execute({
|
|
3184
|
+
sql: `UPDATE memories
|
|
3185
|
+
SET status = 'deleted',
|
|
3186
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3187
|
+
WHERE id != ?
|
|
3188
|
+
AND content_hash = ?
|
|
3189
|
+
AND agent_id = ?
|
|
3190
|
+
AND project_name = ?
|
|
3191
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3192
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3193
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3197
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3198
|
+
const old = await client.execute({
|
|
3199
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3200
|
+
args: [supersedesId]
|
|
3201
|
+
});
|
|
3202
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3203
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3204
|
+
await client.batch([
|
|
3205
|
+
{
|
|
3206
|
+
sql: `UPDATE memories
|
|
3207
|
+
SET status = 'archived',
|
|
3208
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3209
|
+
WHERE id = ?`,
|
|
3210
|
+
args: [supersedesId]
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
sql: `UPDATE memories
|
|
3214
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3215
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3216
|
+
WHERE id = ?`,
|
|
3217
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3218
|
+
}
|
|
3219
|
+
], "write");
|
|
3220
|
+
}
|
|
3221
|
+
} catch (err) {
|
|
3222
|
+
process.stderr.write(
|
|
3223
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3224
|
+
`
|
|
3225
|
+
);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3229
|
+
if (memoryIds.length === 0) return;
|
|
3230
|
+
const run = () => {
|
|
3231
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3232
|
+
};
|
|
3233
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3234
|
+
else setTimeout(run, 0);
|
|
3235
|
+
}
|
|
3236
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3237
|
+
var init_memory_write_governor = __esm({
|
|
3238
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3239
|
+
"use strict";
|
|
3240
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3241
|
+
"decision",
|
|
3242
|
+
"adr",
|
|
3243
|
+
"behavior",
|
|
3244
|
+
"procedure"
|
|
3245
|
+
]);
|
|
3246
|
+
NOISE_DROP_PATTERNS = [
|
|
3247
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3248
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3249
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3250
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3251
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3252
|
+
];
|
|
3253
|
+
SKIP_EMBED_PATTERNS = [
|
|
3254
|
+
/tmux capture-pane\b/i,
|
|
3255
|
+
/docker ps\b/i,
|
|
3256
|
+
/docker images\b/i,
|
|
3257
|
+
/git status\b/i,
|
|
3258
|
+
/grep .*node_modules/i,
|
|
3259
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3260
|
+
];
|
|
3261
|
+
}
|
|
3262
|
+
});
|
|
3263
|
+
|
|
3066
3264
|
// src/lib/shard-manager.ts
|
|
3067
3265
|
var shard_manager_exports = {};
|
|
3068
3266
|
__export(shard_manager_exports, {
|
|
@@ -3227,7 +3425,8 @@ async function ensureShardSchema(client) {
|
|
|
3227
3425
|
}
|
|
3228
3426
|
for (const idx of [
|
|
3229
3427
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3230
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3428
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3429
|
+
"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"
|
|
3231
3430
|
]) {
|
|
3232
3431
|
try {
|
|
3233
3432
|
await client.execute(idx);
|
|
@@ -3652,7 +3851,6 @@ __export(store_exports, {
|
|
|
3652
3851
|
vectorToBlob: () => vectorToBlob,
|
|
3653
3852
|
writeMemory: () => writeMemory
|
|
3654
3853
|
});
|
|
3655
|
-
import { createHash } from "crypto";
|
|
3656
3854
|
function isBusyError2(err) {
|
|
3657
3855
|
if (err instanceof Error) {
|
|
3658
3856
|
const msg = err.message.toLowerCase();
|
|
@@ -3766,17 +3964,24 @@ async function writeMemory(record) {
|
|
|
3766
3964
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3767
3965
|
);
|
|
3768
3966
|
}
|
|
3769
|
-
const
|
|
3770
|
-
if (
|
|
3967
|
+
const governed = governMemoryRecord(record);
|
|
3968
|
+
if (governed.shouldDrop) return;
|
|
3969
|
+
record = governed.record;
|
|
3970
|
+
const contentHash = governed.contentHash;
|
|
3971
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3972
|
+
if (_pendingRecords.some(
|
|
3973
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3974
|
+
)) {
|
|
3771
3975
|
return;
|
|
3772
3976
|
}
|
|
3773
3977
|
try {
|
|
3774
|
-
const
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3978
|
+
const existing = await findScopedDuplicate({
|
|
3979
|
+
contentHash,
|
|
3980
|
+
agentId: record.agent_id,
|
|
3981
|
+
projectName: record.project_name,
|
|
3982
|
+
memoryType
|
|
3778
3983
|
});
|
|
3779
|
-
if (existing
|
|
3984
|
+
if (existing) return;
|
|
3780
3985
|
} catch {
|
|
3781
3986
|
}
|
|
3782
3987
|
const dbRow = {
|
|
@@ -3807,7 +4012,7 @@ async function writeMemory(record) {
|
|
|
3807
4012
|
tier: record.tier ?? classifyTier(record),
|
|
3808
4013
|
supersedes_id: record.supersedes_id ?? null,
|
|
3809
4014
|
draft: record.draft ? 1 : 0,
|
|
3810
|
-
memory_type:
|
|
4015
|
+
memory_type: memoryType,
|
|
3811
4016
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3812
4017
|
content_hash: contentHash,
|
|
3813
4018
|
intent: record.intent ?? null,
|
|
@@ -3966,6 +4171,7 @@ async function flushBatch() {
|
|
|
3966
4171
|
const globalClient = getClient();
|
|
3967
4172
|
const globalStmts = batch.map(buildStmt);
|
|
3968
4173
|
await globalClient.batch(globalStmts, "write");
|
|
4174
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3969
4175
|
_pendingRecords.splice(0, batch.length);
|
|
3970
4176
|
try {
|
|
3971
4177
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4224,6 +4430,7 @@ var init_store = __esm({
|
|
|
4224
4430
|
init_keychain();
|
|
4225
4431
|
init_config();
|
|
4226
4432
|
init_state_bus();
|
|
4433
|
+
init_memory_write_governor();
|
|
4227
4434
|
INIT_MAX_RETRIES = 3;
|
|
4228
4435
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4229
4436
|
_pendingRecords = [];
|