@askexenow/exe-os 0.9.34 → 0.9.36
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 +212 -11
- package/dist/bin/backfill-responses.js +212 -11
- package/dist/bin/backfill-vectors.js +14 -3
- package/dist/bin/cleanup-stale-review-tasks.js +224 -12
- package/dist/bin/cli.js +265 -23
- package/dist/bin/exe-agent.js +1 -1
- package/dist/bin/exe-assign.js +217 -12
- package/dist/bin/exe-boot.js +25 -4
- package/dist/bin/exe-call.js +7 -5
- package/dist/bin/exe-dispatch.js +229 -13
- package/dist/bin/exe-doctor.js +14 -3
- package/dist/bin/exe-export-behaviors.js +301 -14
- package/dist/bin/exe-forget.js +245 -21
- package/dist/bin/exe-gateway.js +229 -13
- package/dist/bin/exe-heartbeat.js +224 -12
- package/dist/bin/exe-kill.js +224 -12
- package/dist/bin/exe-launch-agent.js +177 -9
- package/dist/bin/exe-link.js +8 -0
- package/dist/bin/exe-new-employee.js +26 -2
- package/dist/bin/exe-pending-messages.js +224 -12
- package/dist/bin/exe-pending-notifications.js +224 -12
- package/dist/bin/exe-pending-reviews.js +224 -12
- package/dist/bin/exe-rename.js +9 -1
- package/dist/bin/exe-review.js +224 -12
- package/dist/bin/exe-search.js +246 -21
- package/dist/bin/exe-session-cleanup.js +229 -13
- package/dist/bin/exe-start-codex.js +161 -5
- package/dist/bin/exe-start-opencode.js +172 -5
- package/dist/bin/exe-status.js +224 -12
- package/dist/bin/exe-team.js +224 -12
- package/dist/bin/git-sweep.js +229 -13
- package/dist/bin/graph-backfill.js +94 -3
- package/dist/bin/graph-export.js +224 -12
- package/dist/bin/install.js +25 -1
- package/dist/bin/intercom-check.js +229 -13
- package/dist/bin/scan-tasks.js +229 -13
- package/dist/bin/setup.js +15 -5
- package/dist/bin/shard-migrate.js +94 -3
- package/dist/gateway/index.js +229 -13
- package/dist/hooks/bug-report-worker.js +229 -13
- package/dist/hooks/codex-stop-task-finalizer.js +224 -12
- package/dist/hooks/commit-complete.js +229 -13
- package/dist/hooks/error-recall.js +246 -21
- package/dist/hooks/ingest.js +224 -12
- package/dist/hooks/instructions-loaded.js +224 -12
- package/dist/hooks/notification.js +224 -12
- package/dist/hooks/post-compact.js +224 -12
- package/dist/hooks/post-tool-combined.js +246 -21
- package/dist/hooks/pre-compact.js +229 -13
- package/dist/hooks/pre-tool-use.js +234 -18
- package/dist/hooks/prompt-submit.js +346 -23
- package/dist/hooks/session-end.js +229 -13
- package/dist/hooks/session-start.js +418 -42
- package/dist/hooks/stop.js +224 -12
- package/dist/hooks/subagent-stop.js +224 -12
- package/dist/hooks/summary-worker.js +15 -2
- package/dist/index.js +229 -13
- 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/employee-templates.js +7 -5
- package/dist/lib/exe-daemon.js +1776 -1433
- package/dist/lib/hybrid-search.js +246 -21
- package/dist/lib/schedules.js +14 -3
- package/dist/lib/store.js +217 -12
- package/dist/lib/tasks.js +5 -1
- package/dist/lib/tmux-routing.js +5 -1
- package/dist/mcp/server.js +331 -37
- package/dist/mcp/tools/create-task.js +5 -1
- package/dist/mcp/tools/update-task.js +5 -1
- package/dist/runtime/index.js +229 -13
- package/dist/tui/App.js +229 -13
- package/package.json +1 -1
- package/src/commands/exe/save.md +48 -0
|
@@ -2299,6 +2299,14 @@ async function ensureSchema() {
|
|
|
2299
2299
|
);
|
|
2300
2300
|
} catch {
|
|
2301
2301
|
}
|
|
2302
|
+
try {
|
|
2303
|
+
await client.execute(
|
|
2304
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2305
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2306
|
+
WHERE content_hash IS NOT NULL`
|
|
2307
|
+
);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2302
2310
|
await client.executeMultiple(`
|
|
2303
2311
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2304
2312
|
id TEXT PRIMARY KEY,
|
|
@@ -3518,6 +3526,197 @@ var init_state_bus = __esm({
|
|
|
3518
3526
|
}
|
|
3519
3527
|
});
|
|
3520
3528
|
|
|
3529
|
+
// src/lib/memory-write-governor.ts
|
|
3530
|
+
import { createHash } from "crypto";
|
|
3531
|
+
function normalizeMemoryText(text) {
|
|
3532
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3533
|
+
}
|
|
3534
|
+
function classifyMemoryType(input) {
|
|
3535
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3536
|
+
const tool = input.tool_name.toLowerCase();
|
|
3537
|
+
const text = input.raw_text.toLowerCase();
|
|
3538
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3539
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3540
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3541
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3542
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3543
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3544
|
+
return "raw";
|
|
3545
|
+
}
|
|
3546
|
+
function shouldDropMemory(text) {
|
|
3547
|
+
const normalized = normalizeMemoryText(text);
|
|
3548
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3549
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3550
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3551
|
+
}
|
|
3552
|
+
return { drop: false };
|
|
3553
|
+
}
|
|
3554
|
+
function shouldSkipEmbedding(input) {
|
|
3555
|
+
const type = classifyMemoryType(input);
|
|
3556
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3557
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3558
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3559
|
+
return false;
|
|
3560
|
+
}
|
|
3561
|
+
function hashMemoryContent(text) {
|
|
3562
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3563
|
+
}
|
|
3564
|
+
function scopedDedupArgs(input) {
|
|
3565
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3566
|
+
}
|
|
3567
|
+
function governMemoryRecord(record) {
|
|
3568
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3569
|
+
const memoryType = classifyMemoryType({
|
|
3570
|
+
raw_text: normalized,
|
|
3571
|
+
agent_id: record.agent_id,
|
|
3572
|
+
project_name: record.project_name,
|
|
3573
|
+
tool_name: record.tool_name,
|
|
3574
|
+
memory_type: record.memory_type
|
|
3575
|
+
});
|
|
3576
|
+
const drop = shouldDropMemory(normalized);
|
|
3577
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3578
|
+
raw_text: normalized,
|
|
3579
|
+
agent_id: record.agent_id,
|
|
3580
|
+
project_name: record.project_name,
|
|
3581
|
+
tool_name: record.tool_name,
|
|
3582
|
+
memory_type: memoryType
|
|
3583
|
+
});
|
|
3584
|
+
return {
|
|
3585
|
+
record: {
|
|
3586
|
+
...record,
|
|
3587
|
+
raw_text: normalized,
|
|
3588
|
+
memory_type: memoryType,
|
|
3589
|
+
vector: skipEmbedding ? null : record.vector
|
|
3590
|
+
},
|
|
3591
|
+
contentHash: hashMemoryContent(normalized),
|
|
3592
|
+
shouldDrop: drop.drop,
|
|
3593
|
+
dropReason: drop.reason,
|
|
3594
|
+
skipEmbedding,
|
|
3595
|
+
hygiene: {
|
|
3596
|
+
dedup: true,
|
|
3597
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
async function findScopedDuplicate(input) {
|
|
3602
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3603
|
+
const client = getClient2();
|
|
3604
|
+
const args = scopedDedupArgs(input);
|
|
3605
|
+
let sql = `SELECT id FROM memories
|
|
3606
|
+
WHERE content_hash = ?
|
|
3607
|
+
AND agent_id = ?
|
|
3608
|
+
AND project_name = ?
|
|
3609
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3610
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3611
|
+
if (input.excludeId) {
|
|
3612
|
+
sql += " AND id != ?";
|
|
3613
|
+
args.push(input.excludeId);
|
|
3614
|
+
}
|
|
3615
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3616
|
+
const result = await client.execute({ sql, args });
|
|
3617
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3618
|
+
}
|
|
3619
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3620
|
+
try {
|
|
3621
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3622
|
+
const client = getClient2();
|
|
3623
|
+
const current = await client.execute({
|
|
3624
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3625
|
+
importance, timestamp
|
|
3626
|
+
FROM memories
|
|
3627
|
+
WHERE id = ?
|
|
3628
|
+
LIMIT 1`,
|
|
3629
|
+
args: [memoryId]
|
|
3630
|
+
});
|
|
3631
|
+
const row = current.rows[0];
|
|
3632
|
+
if (!row) return;
|
|
3633
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3634
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3635
|
+
const agentId = String(row.agent_id);
|
|
3636
|
+
const projectName = String(row.project_name);
|
|
3637
|
+
if (contentHash) {
|
|
3638
|
+
await client.execute({
|
|
3639
|
+
sql: `UPDATE memories
|
|
3640
|
+
SET status = 'deleted',
|
|
3641
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3642
|
+
WHERE id != ?
|
|
3643
|
+
AND content_hash = ?
|
|
3644
|
+
AND agent_id = ?
|
|
3645
|
+
AND project_name = ?
|
|
3646
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3647
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3648
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3649
|
+
});
|
|
3650
|
+
}
|
|
3651
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3652
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3653
|
+
const old = await client.execute({
|
|
3654
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3655
|
+
args: [supersedesId]
|
|
3656
|
+
});
|
|
3657
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3658
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3659
|
+
await client.batch([
|
|
3660
|
+
{
|
|
3661
|
+
sql: `UPDATE memories
|
|
3662
|
+
SET status = 'archived',
|
|
3663
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3664
|
+
WHERE id = ?`,
|
|
3665
|
+
args: [supersedesId]
|
|
3666
|
+
},
|
|
3667
|
+
{
|
|
3668
|
+
sql: `UPDATE memories
|
|
3669
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3670
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3671
|
+
WHERE id = ?`,
|
|
3672
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3673
|
+
}
|
|
3674
|
+
], "write");
|
|
3675
|
+
}
|
|
3676
|
+
} catch (err) {
|
|
3677
|
+
process.stderr.write(
|
|
3678
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3679
|
+
`
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3684
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3685
|
+
if (memoryIds.length === 0) return;
|
|
3686
|
+
const run = () => {
|
|
3687
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3688
|
+
};
|
|
3689
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3690
|
+
else setTimeout(run, 0);
|
|
3691
|
+
}
|
|
3692
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3693
|
+
var init_memory_write_governor = __esm({
|
|
3694
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3695
|
+
"use strict";
|
|
3696
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3697
|
+
"decision",
|
|
3698
|
+
"adr",
|
|
3699
|
+
"behavior",
|
|
3700
|
+
"procedure"
|
|
3701
|
+
]);
|
|
3702
|
+
NOISE_DROP_PATTERNS = [
|
|
3703
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3704
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3705
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3706
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3707
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3708
|
+
];
|
|
3709
|
+
SKIP_EMBED_PATTERNS = [
|
|
3710
|
+
/tmux capture-pane\b/i,
|
|
3711
|
+
/docker ps\b/i,
|
|
3712
|
+
/docker images\b/i,
|
|
3713
|
+
/git status\b/i,
|
|
3714
|
+
/grep .*node_modules/i,
|
|
3715
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3716
|
+
];
|
|
3717
|
+
}
|
|
3718
|
+
});
|
|
3719
|
+
|
|
3521
3720
|
// src/lib/shard-manager.ts
|
|
3522
3721
|
var shard_manager_exports = {};
|
|
3523
3722
|
__export(shard_manager_exports, {
|
|
@@ -3682,7 +3881,8 @@ async function ensureShardSchema(client) {
|
|
|
3682
3881
|
}
|
|
3683
3882
|
for (const idx of [
|
|
3684
3883
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3685
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3884
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3885
|
+
"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"
|
|
3686
3886
|
]) {
|
|
3687
3887
|
try {
|
|
3688
3888
|
await client.execute(idx);
|
|
@@ -3876,7 +4076,7 @@ var init_platform_procedures = __esm({
|
|
|
3876
4076
|
title: "Chain of command \u2014 who talks to whom",
|
|
3877
4077
|
domain: "workflow",
|
|
3878
4078
|
priority: "p0",
|
|
3879
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4079
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3880
4080
|
},
|
|
3881
4081
|
{
|
|
3882
4082
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4107,7 +4307,6 @@ __export(store_exports, {
|
|
|
4107
4307
|
vectorToBlob: () => vectorToBlob,
|
|
4108
4308
|
writeMemory: () => writeMemory
|
|
4109
4309
|
});
|
|
4110
|
-
import { createHash } from "crypto";
|
|
4111
4310
|
function isBusyError2(err) {
|
|
4112
4311
|
if (err instanceof Error) {
|
|
4113
4312
|
const msg = err.message.toLowerCase();
|
|
@@ -4221,17 +4420,24 @@ async function writeMemory(record) {
|
|
|
4221
4420
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4222
4421
|
);
|
|
4223
4422
|
}
|
|
4224
|
-
const
|
|
4225
|
-
if (
|
|
4423
|
+
const governed = governMemoryRecord(record);
|
|
4424
|
+
if (governed.shouldDrop) return;
|
|
4425
|
+
record = governed.record;
|
|
4426
|
+
const contentHash = governed.contentHash;
|
|
4427
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4428
|
+
if (_pendingRecords.some(
|
|
4429
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4430
|
+
)) {
|
|
4226
4431
|
return;
|
|
4227
4432
|
}
|
|
4228
4433
|
try {
|
|
4229
|
-
const
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4434
|
+
const existing = await findScopedDuplicate({
|
|
4435
|
+
contentHash,
|
|
4436
|
+
agentId: record.agent_id,
|
|
4437
|
+
projectName: record.project_name,
|
|
4438
|
+
memoryType
|
|
4233
4439
|
});
|
|
4234
|
-
if (existing
|
|
4440
|
+
if (existing) return;
|
|
4235
4441
|
} catch {
|
|
4236
4442
|
}
|
|
4237
4443
|
const dbRow = {
|
|
@@ -4262,7 +4468,7 @@ async function writeMemory(record) {
|
|
|
4262
4468
|
tier: record.tier ?? classifyTier(record),
|
|
4263
4469
|
supersedes_id: record.supersedes_id ?? null,
|
|
4264
4470
|
draft: record.draft ? 1 : 0,
|
|
4265
|
-
memory_type:
|
|
4471
|
+
memory_type: memoryType,
|
|
4266
4472
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4267
4473
|
content_hash: contentHash,
|
|
4268
4474
|
intent: record.intent ?? null,
|
|
@@ -4421,6 +4627,7 @@ async function flushBatch() {
|
|
|
4421
4627
|
const globalClient = getClient();
|
|
4422
4628
|
const globalStmts = batch.map(buildStmt);
|
|
4423
4629
|
await globalClient.batch(globalStmts, "write");
|
|
4630
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4424
4631
|
_pendingRecords.splice(0, batch.length);
|
|
4425
4632
|
try {
|
|
4426
4633
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4541,7 +4748,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4541
4748
|
sql += ` AND timestamp >= ?`;
|
|
4542
4749
|
args.push(options.since);
|
|
4543
4750
|
}
|
|
4544
|
-
if (options?.
|
|
4751
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4752
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4753
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4754
|
+
args.push(...uniqueTypes);
|
|
4755
|
+
} else if (options?.memoryType) {
|
|
4545
4756
|
sql += ` AND memory_type = ?`;
|
|
4546
4757
|
args.push(options.memoryType);
|
|
4547
4758
|
}
|
|
@@ -4679,6 +4890,7 @@ var init_store = __esm({
|
|
|
4679
4890
|
init_keychain();
|
|
4680
4891
|
init_config();
|
|
4681
4892
|
init_state_bus();
|
|
4893
|
+
init_memory_write_governor();
|
|
4682
4894
|
INIT_MAX_RETRIES = 3;
|
|
4683
4895
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4684
4896
|
_pendingRecords = [];
|
|
@@ -2299,6 +2299,14 @@ async function ensureSchema() {
|
|
|
2299
2299
|
);
|
|
2300
2300
|
} catch {
|
|
2301
2301
|
}
|
|
2302
|
+
try {
|
|
2303
|
+
await client.execute(
|
|
2304
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2305
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2306
|
+
WHERE content_hash IS NOT NULL`
|
|
2307
|
+
);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2302
2310
|
await client.executeMultiple(`
|
|
2303
2311
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2304
2312
|
id TEXT PRIMARY KEY,
|
|
@@ -3557,6 +3565,197 @@ var init_keychain = __esm({
|
|
|
3557
3565
|
}
|
|
3558
3566
|
});
|
|
3559
3567
|
|
|
3568
|
+
// src/lib/memory-write-governor.ts
|
|
3569
|
+
import { createHash } from "crypto";
|
|
3570
|
+
function normalizeMemoryText(text) {
|
|
3571
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3572
|
+
}
|
|
3573
|
+
function classifyMemoryType(input) {
|
|
3574
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3575
|
+
const tool = input.tool_name.toLowerCase();
|
|
3576
|
+
const text = input.raw_text.toLowerCase();
|
|
3577
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3578
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3579
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3580
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3581
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3582
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3583
|
+
return "raw";
|
|
3584
|
+
}
|
|
3585
|
+
function shouldDropMemory(text) {
|
|
3586
|
+
const normalized = normalizeMemoryText(text);
|
|
3587
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3588
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3589
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3590
|
+
}
|
|
3591
|
+
return { drop: false };
|
|
3592
|
+
}
|
|
3593
|
+
function shouldSkipEmbedding(input) {
|
|
3594
|
+
const type = classifyMemoryType(input);
|
|
3595
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3596
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3597
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3600
|
+
function hashMemoryContent(text) {
|
|
3601
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3602
|
+
}
|
|
3603
|
+
function scopedDedupArgs(input) {
|
|
3604
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3605
|
+
}
|
|
3606
|
+
function governMemoryRecord(record) {
|
|
3607
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3608
|
+
const memoryType = classifyMemoryType({
|
|
3609
|
+
raw_text: normalized,
|
|
3610
|
+
agent_id: record.agent_id,
|
|
3611
|
+
project_name: record.project_name,
|
|
3612
|
+
tool_name: record.tool_name,
|
|
3613
|
+
memory_type: record.memory_type
|
|
3614
|
+
});
|
|
3615
|
+
const drop = shouldDropMemory(normalized);
|
|
3616
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3617
|
+
raw_text: normalized,
|
|
3618
|
+
agent_id: record.agent_id,
|
|
3619
|
+
project_name: record.project_name,
|
|
3620
|
+
tool_name: record.tool_name,
|
|
3621
|
+
memory_type: memoryType
|
|
3622
|
+
});
|
|
3623
|
+
return {
|
|
3624
|
+
record: {
|
|
3625
|
+
...record,
|
|
3626
|
+
raw_text: normalized,
|
|
3627
|
+
memory_type: memoryType,
|
|
3628
|
+
vector: skipEmbedding ? null : record.vector
|
|
3629
|
+
},
|
|
3630
|
+
contentHash: hashMemoryContent(normalized),
|
|
3631
|
+
shouldDrop: drop.drop,
|
|
3632
|
+
dropReason: drop.reason,
|
|
3633
|
+
skipEmbedding,
|
|
3634
|
+
hygiene: {
|
|
3635
|
+
dedup: true,
|
|
3636
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
async function findScopedDuplicate(input) {
|
|
3641
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3642
|
+
const client = getClient2();
|
|
3643
|
+
const args = scopedDedupArgs(input);
|
|
3644
|
+
let sql = `SELECT id FROM memories
|
|
3645
|
+
WHERE content_hash = ?
|
|
3646
|
+
AND agent_id = ?
|
|
3647
|
+
AND project_name = ?
|
|
3648
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3649
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3650
|
+
if (input.excludeId) {
|
|
3651
|
+
sql += " AND id != ?";
|
|
3652
|
+
args.push(input.excludeId);
|
|
3653
|
+
}
|
|
3654
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3655
|
+
const result = await client.execute({ sql, args });
|
|
3656
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3657
|
+
}
|
|
3658
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3659
|
+
try {
|
|
3660
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3661
|
+
const client = getClient2();
|
|
3662
|
+
const current = await client.execute({
|
|
3663
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3664
|
+
importance, timestamp
|
|
3665
|
+
FROM memories
|
|
3666
|
+
WHERE id = ?
|
|
3667
|
+
LIMIT 1`,
|
|
3668
|
+
args: [memoryId]
|
|
3669
|
+
});
|
|
3670
|
+
const row = current.rows[0];
|
|
3671
|
+
if (!row) return;
|
|
3672
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3673
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3674
|
+
const agentId = String(row.agent_id);
|
|
3675
|
+
const projectName = String(row.project_name);
|
|
3676
|
+
if (contentHash) {
|
|
3677
|
+
await client.execute({
|
|
3678
|
+
sql: `UPDATE memories
|
|
3679
|
+
SET status = 'deleted',
|
|
3680
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3681
|
+
WHERE id != ?
|
|
3682
|
+
AND content_hash = ?
|
|
3683
|
+
AND agent_id = ?
|
|
3684
|
+
AND project_name = ?
|
|
3685
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3686
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3687
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3690
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3691
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3692
|
+
const old = await client.execute({
|
|
3693
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3694
|
+
args: [supersedesId]
|
|
3695
|
+
});
|
|
3696
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3697
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3698
|
+
await client.batch([
|
|
3699
|
+
{
|
|
3700
|
+
sql: `UPDATE memories
|
|
3701
|
+
SET status = 'archived',
|
|
3702
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3703
|
+
WHERE id = ?`,
|
|
3704
|
+
args: [supersedesId]
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
sql: `UPDATE memories
|
|
3708
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3709
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3710
|
+
WHERE id = ?`,
|
|
3711
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3712
|
+
}
|
|
3713
|
+
], "write");
|
|
3714
|
+
}
|
|
3715
|
+
} catch (err) {
|
|
3716
|
+
process.stderr.write(
|
|
3717
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3718
|
+
`
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3723
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3724
|
+
if (memoryIds.length === 0) return;
|
|
3725
|
+
const run = () => {
|
|
3726
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3727
|
+
};
|
|
3728
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3729
|
+
else setTimeout(run, 0);
|
|
3730
|
+
}
|
|
3731
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3732
|
+
var init_memory_write_governor = __esm({
|
|
3733
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3734
|
+
"use strict";
|
|
3735
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3736
|
+
"decision",
|
|
3737
|
+
"adr",
|
|
3738
|
+
"behavior",
|
|
3739
|
+
"procedure"
|
|
3740
|
+
]);
|
|
3741
|
+
NOISE_DROP_PATTERNS = [
|
|
3742
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3743
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3744
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3745
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3746
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3747
|
+
];
|
|
3748
|
+
SKIP_EMBED_PATTERNS = [
|
|
3749
|
+
/tmux capture-pane\b/i,
|
|
3750
|
+
/docker ps\b/i,
|
|
3751
|
+
/docker images\b/i,
|
|
3752
|
+
/git status\b/i,
|
|
3753
|
+
/grep .*node_modules/i,
|
|
3754
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3755
|
+
];
|
|
3756
|
+
}
|
|
3757
|
+
});
|
|
3758
|
+
|
|
3560
3759
|
// src/lib/shard-manager.ts
|
|
3561
3760
|
var shard_manager_exports = {};
|
|
3562
3761
|
__export(shard_manager_exports, {
|
|
@@ -3721,7 +3920,8 @@ async function ensureShardSchema(client) {
|
|
|
3721
3920
|
}
|
|
3722
3921
|
for (const idx of [
|
|
3723
3922
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3724
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3923
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3924
|
+
"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"
|
|
3725
3925
|
]) {
|
|
3726
3926
|
try {
|
|
3727
3927
|
await client.execute(idx);
|
|
@@ -3915,7 +4115,7 @@ var init_platform_procedures = __esm({
|
|
|
3915
4115
|
title: "Chain of command \u2014 who talks to whom",
|
|
3916
4116
|
domain: "workflow",
|
|
3917
4117
|
priority: "p0",
|
|
3918
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4118
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3919
4119
|
},
|
|
3920
4120
|
{
|
|
3921
4121
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4146,7 +4346,6 @@ __export(store_exports, {
|
|
|
4146
4346
|
vectorToBlob: () => vectorToBlob,
|
|
4147
4347
|
writeMemory: () => writeMemory
|
|
4148
4348
|
});
|
|
4149
|
-
import { createHash } from "crypto";
|
|
4150
4349
|
function isBusyError2(err) {
|
|
4151
4350
|
if (err instanceof Error) {
|
|
4152
4351
|
const msg = err.message.toLowerCase();
|
|
@@ -4260,17 +4459,24 @@ async function writeMemory(record) {
|
|
|
4260
4459
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4261
4460
|
);
|
|
4262
4461
|
}
|
|
4263
|
-
const
|
|
4264
|
-
if (
|
|
4462
|
+
const governed = governMemoryRecord(record);
|
|
4463
|
+
if (governed.shouldDrop) return;
|
|
4464
|
+
record = governed.record;
|
|
4465
|
+
const contentHash = governed.contentHash;
|
|
4466
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4467
|
+
if (_pendingRecords.some(
|
|
4468
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4469
|
+
)) {
|
|
4265
4470
|
return;
|
|
4266
4471
|
}
|
|
4267
4472
|
try {
|
|
4268
|
-
const
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4473
|
+
const existing = await findScopedDuplicate({
|
|
4474
|
+
contentHash,
|
|
4475
|
+
agentId: record.agent_id,
|
|
4476
|
+
projectName: record.project_name,
|
|
4477
|
+
memoryType
|
|
4272
4478
|
});
|
|
4273
|
-
if (existing
|
|
4479
|
+
if (existing) return;
|
|
4274
4480
|
} catch {
|
|
4275
4481
|
}
|
|
4276
4482
|
const dbRow = {
|
|
@@ -4301,7 +4507,7 @@ async function writeMemory(record) {
|
|
|
4301
4507
|
tier: record.tier ?? classifyTier(record),
|
|
4302
4508
|
supersedes_id: record.supersedes_id ?? null,
|
|
4303
4509
|
draft: record.draft ? 1 : 0,
|
|
4304
|
-
memory_type:
|
|
4510
|
+
memory_type: memoryType,
|
|
4305
4511
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4306
4512
|
content_hash: contentHash,
|
|
4307
4513
|
intent: record.intent ?? null,
|
|
@@ -4460,6 +4666,7 @@ async function flushBatch() {
|
|
|
4460
4666
|
const globalClient = getClient();
|
|
4461
4667
|
const globalStmts = batch.map(buildStmt);
|
|
4462
4668
|
await globalClient.batch(globalStmts, "write");
|
|
4669
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4463
4670
|
_pendingRecords.splice(0, batch.length);
|
|
4464
4671
|
try {
|
|
4465
4672
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4580,7 +4787,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4580
4787
|
sql += ` AND timestamp >= ?`;
|
|
4581
4788
|
args.push(options.since);
|
|
4582
4789
|
}
|
|
4583
|
-
if (options?.
|
|
4790
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4791
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4792
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4793
|
+
args.push(...uniqueTypes);
|
|
4794
|
+
} else if (options?.memoryType) {
|
|
4584
4795
|
sql += ` AND memory_type = ?`;
|
|
4585
4796
|
args.push(options.memoryType);
|
|
4586
4797
|
}
|
|
@@ -4718,6 +4929,7 @@ var init_store = __esm({
|
|
|
4718
4929
|
init_keychain();
|
|
4719
4930
|
init_config();
|
|
4720
4931
|
init_state_bus();
|
|
4932
|
+
init_memory_write_governor();
|
|
4721
4933
|
INIT_MAX_RETRIES = 3;
|
|
4722
4934
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4723
4935
|
_pendingRecords = [];
|
package/dist/bin/exe-rename.js
CHANGED
|
@@ -2037,6 +2037,14 @@ async function ensureSchema() {
|
|
|
2037
2037
|
);
|
|
2038
2038
|
} catch {
|
|
2039
2039
|
}
|
|
2040
|
+
try {
|
|
2041
|
+
await client.execute(
|
|
2042
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2043
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2044
|
+
WHERE content_hash IS NOT NULL`
|
|
2045
|
+
);
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2040
2048
|
await client.executeMultiple(`
|
|
2041
2049
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2042
2050
|
id TEXT PRIMARY KEY,
|
|
@@ -2585,7 +2593,7 @@ var PLATFORM_PROCEDURES = [
|
|
|
2585
2593
|
title: "Chain of command \u2014 who talks to whom",
|
|
2586
2594
|
domain: "workflow",
|
|
2587
2595
|
priority: "p0",
|
|
2588
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
2596
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
2589
2597
|
},
|
|
2590
2598
|
{
|
|
2591
2599
|
title: "Single dispatch path \u2014 create_task only",
|