@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
package/dist/hooks/stop.js
CHANGED
|
@@ -2611,6 +2611,14 @@ async function ensureSchema() {
|
|
|
2611
2611
|
);
|
|
2612
2612
|
} catch {
|
|
2613
2613
|
}
|
|
2614
|
+
try {
|
|
2615
|
+
await client.execute(
|
|
2616
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2617
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2618
|
+
WHERE content_hash IS NOT NULL`
|
|
2619
|
+
);
|
|
2620
|
+
} catch {
|
|
2621
|
+
}
|
|
2614
2622
|
await client.executeMultiple(`
|
|
2615
2623
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2616
2624
|
id TEXT PRIMARY KEY,
|
|
@@ -3481,6 +3489,197 @@ var init_state_bus = __esm({
|
|
|
3481
3489
|
}
|
|
3482
3490
|
});
|
|
3483
3491
|
|
|
3492
|
+
// src/lib/memory-write-governor.ts
|
|
3493
|
+
import { createHash } from "crypto";
|
|
3494
|
+
function normalizeMemoryText(text) {
|
|
3495
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3496
|
+
}
|
|
3497
|
+
function classifyMemoryType(input2) {
|
|
3498
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3499
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3500
|
+
const text = input2.raw_text.toLowerCase();
|
|
3501
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3502
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3503
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3504
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3505
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3506
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3507
|
+
return "raw";
|
|
3508
|
+
}
|
|
3509
|
+
function shouldDropMemory(text) {
|
|
3510
|
+
const normalized = normalizeMemoryText(text);
|
|
3511
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3512
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3513
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3514
|
+
}
|
|
3515
|
+
return { drop: false };
|
|
3516
|
+
}
|
|
3517
|
+
function shouldSkipEmbedding(input2) {
|
|
3518
|
+
const type = classifyMemoryType(input2);
|
|
3519
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3520
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3521
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3522
|
+
return false;
|
|
3523
|
+
}
|
|
3524
|
+
function hashMemoryContent(text) {
|
|
3525
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3526
|
+
}
|
|
3527
|
+
function scopedDedupArgs(input2) {
|
|
3528
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3529
|
+
}
|
|
3530
|
+
function governMemoryRecord(record) {
|
|
3531
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3532
|
+
const memoryType = classifyMemoryType({
|
|
3533
|
+
raw_text: normalized,
|
|
3534
|
+
agent_id: record.agent_id,
|
|
3535
|
+
project_name: record.project_name,
|
|
3536
|
+
tool_name: record.tool_name,
|
|
3537
|
+
memory_type: record.memory_type
|
|
3538
|
+
});
|
|
3539
|
+
const drop = shouldDropMemory(normalized);
|
|
3540
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3541
|
+
raw_text: normalized,
|
|
3542
|
+
agent_id: record.agent_id,
|
|
3543
|
+
project_name: record.project_name,
|
|
3544
|
+
tool_name: record.tool_name,
|
|
3545
|
+
memory_type: memoryType
|
|
3546
|
+
});
|
|
3547
|
+
return {
|
|
3548
|
+
record: {
|
|
3549
|
+
...record,
|
|
3550
|
+
raw_text: normalized,
|
|
3551
|
+
memory_type: memoryType,
|
|
3552
|
+
vector: skipEmbedding ? null : record.vector
|
|
3553
|
+
},
|
|
3554
|
+
contentHash: hashMemoryContent(normalized),
|
|
3555
|
+
shouldDrop: drop.drop,
|
|
3556
|
+
dropReason: drop.reason,
|
|
3557
|
+
skipEmbedding,
|
|
3558
|
+
hygiene: {
|
|
3559
|
+
dedup: true,
|
|
3560
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3563
|
+
}
|
|
3564
|
+
async function findScopedDuplicate(input2) {
|
|
3565
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3566
|
+
const client = getClient2();
|
|
3567
|
+
const args = scopedDedupArgs(input2);
|
|
3568
|
+
let sql = `SELECT id FROM memories
|
|
3569
|
+
WHERE content_hash = ?
|
|
3570
|
+
AND agent_id = ?
|
|
3571
|
+
AND project_name = ?
|
|
3572
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3573
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3574
|
+
if (input2.excludeId) {
|
|
3575
|
+
sql += " AND id != ?";
|
|
3576
|
+
args.push(input2.excludeId);
|
|
3577
|
+
}
|
|
3578
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3579
|
+
const result = await client.execute({ sql, args });
|
|
3580
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3581
|
+
}
|
|
3582
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3583
|
+
try {
|
|
3584
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3585
|
+
const client = getClient2();
|
|
3586
|
+
const current = await client.execute({
|
|
3587
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3588
|
+
importance, timestamp
|
|
3589
|
+
FROM memories
|
|
3590
|
+
WHERE id = ?
|
|
3591
|
+
LIMIT 1`,
|
|
3592
|
+
args: [memoryId]
|
|
3593
|
+
});
|
|
3594
|
+
const row = current.rows[0];
|
|
3595
|
+
if (!row) return;
|
|
3596
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3597
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3598
|
+
const agentId = String(row.agent_id);
|
|
3599
|
+
const projectName = String(row.project_name);
|
|
3600
|
+
if (contentHash) {
|
|
3601
|
+
await client.execute({
|
|
3602
|
+
sql: `UPDATE memories
|
|
3603
|
+
SET status = 'deleted',
|
|
3604
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3605
|
+
WHERE id != ?
|
|
3606
|
+
AND content_hash = ?
|
|
3607
|
+
AND agent_id = ?
|
|
3608
|
+
AND project_name = ?
|
|
3609
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3610
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3611
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3612
|
+
});
|
|
3613
|
+
}
|
|
3614
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3615
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3616
|
+
const old = await client.execute({
|
|
3617
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3618
|
+
args: [supersedesId]
|
|
3619
|
+
});
|
|
3620
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3621
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3622
|
+
await client.batch([
|
|
3623
|
+
{
|
|
3624
|
+
sql: `UPDATE memories
|
|
3625
|
+
SET status = 'archived',
|
|
3626
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3627
|
+
WHERE id = ?`,
|
|
3628
|
+
args: [supersedesId]
|
|
3629
|
+
},
|
|
3630
|
+
{
|
|
3631
|
+
sql: `UPDATE memories
|
|
3632
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3633
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3634
|
+
WHERE id = ?`,
|
|
3635
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3636
|
+
}
|
|
3637
|
+
], "write");
|
|
3638
|
+
}
|
|
3639
|
+
} catch (err) {
|
|
3640
|
+
process.stderr.write(
|
|
3641
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3642
|
+
`
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3647
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3648
|
+
if (memoryIds.length === 0) return;
|
|
3649
|
+
const run = () => {
|
|
3650
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3651
|
+
};
|
|
3652
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3653
|
+
else setTimeout(run, 0);
|
|
3654
|
+
}
|
|
3655
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3656
|
+
var init_memory_write_governor = __esm({
|
|
3657
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3658
|
+
"use strict";
|
|
3659
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3660
|
+
"decision",
|
|
3661
|
+
"adr",
|
|
3662
|
+
"behavior",
|
|
3663
|
+
"procedure"
|
|
3664
|
+
]);
|
|
3665
|
+
NOISE_DROP_PATTERNS = [
|
|
3666
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3667
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3668
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3669
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3670
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3671
|
+
];
|
|
3672
|
+
SKIP_EMBED_PATTERNS = [
|
|
3673
|
+
/tmux capture-pane\b/i,
|
|
3674
|
+
/docker ps\b/i,
|
|
3675
|
+
/docker images\b/i,
|
|
3676
|
+
/git status\b/i,
|
|
3677
|
+
/grep .*node_modules/i,
|
|
3678
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3679
|
+
];
|
|
3680
|
+
}
|
|
3681
|
+
});
|
|
3682
|
+
|
|
3484
3683
|
// src/lib/shard-manager.ts
|
|
3485
3684
|
var shard_manager_exports = {};
|
|
3486
3685
|
__export(shard_manager_exports, {
|
|
@@ -3645,7 +3844,8 @@ async function ensureShardSchema(client) {
|
|
|
3645
3844
|
}
|
|
3646
3845
|
for (const idx of [
|
|
3647
3846
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3648
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3847
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3848
|
+
"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"
|
|
3649
3849
|
]) {
|
|
3650
3850
|
try {
|
|
3651
3851
|
await client.execute(idx);
|
|
@@ -3839,7 +4039,7 @@ var init_platform_procedures = __esm({
|
|
|
3839
4039
|
title: "Chain of command \u2014 who talks to whom",
|
|
3840
4040
|
domain: "workflow",
|
|
3841
4041
|
priority: "p0",
|
|
3842
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4042
|
+
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."
|
|
3843
4043
|
},
|
|
3844
4044
|
{
|
|
3845
4045
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4070,7 +4270,6 @@ __export(store_exports, {
|
|
|
4070
4270
|
vectorToBlob: () => vectorToBlob,
|
|
4071
4271
|
writeMemory: () => writeMemory
|
|
4072
4272
|
});
|
|
4073
|
-
import { createHash } from "crypto";
|
|
4074
4273
|
function isBusyError2(err) {
|
|
4075
4274
|
if (err instanceof Error) {
|
|
4076
4275
|
const msg = err.message.toLowerCase();
|
|
@@ -4184,17 +4383,24 @@ async function writeMemory(record) {
|
|
|
4184
4383
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4185
4384
|
);
|
|
4186
4385
|
}
|
|
4187
|
-
const
|
|
4188
|
-
if (
|
|
4386
|
+
const governed = governMemoryRecord(record);
|
|
4387
|
+
if (governed.shouldDrop) return;
|
|
4388
|
+
record = governed.record;
|
|
4389
|
+
const contentHash = governed.contentHash;
|
|
4390
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4391
|
+
if (_pendingRecords.some(
|
|
4392
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4393
|
+
)) {
|
|
4189
4394
|
return;
|
|
4190
4395
|
}
|
|
4191
4396
|
try {
|
|
4192
|
-
const
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4397
|
+
const existing = await findScopedDuplicate({
|
|
4398
|
+
contentHash,
|
|
4399
|
+
agentId: record.agent_id,
|
|
4400
|
+
projectName: record.project_name,
|
|
4401
|
+
memoryType
|
|
4196
4402
|
});
|
|
4197
|
-
if (existing
|
|
4403
|
+
if (existing) return;
|
|
4198
4404
|
} catch {
|
|
4199
4405
|
}
|
|
4200
4406
|
const dbRow = {
|
|
@@ -4225,7 +4431,7 @@ async function writeMemory(record) {
|
|
|
4225
4431
|
tier: record.tier ?? classifyTier(record),
|
|
4226
4432
|
supersedes_id: record.supersedes_id ?? null,
|
|
4227
4433
|
draft: record.draft ? 1 : 0,
|
|
4228
|
-
memory_type:
|
|
4434
|
+
memory_type: memoryType,
|
|
4229
4435
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4230
4436
|
content_hash: contentHash,
|
|
4231
4437
|
intent: record.intent ?? null,
|
|
@@ -4384,6 +4590,7 @@ async function flushBatch() {
|
|
|
4384
4590
|
const globalClient = getClient();
|
|
4385
4591
|
const globalStmts = batch.map(buildStmt);
|
|
4386
4592
|
await globalClient.batch(globalStmts, "write");
|
|
4593
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4387
4594
|
_pendingRecords.splice(0, batch.length);
|
|
4388
4595
|
try {
|
|
4389
4596
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4504,7 +4711,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4504
4711
|
sql += ` AND timestamp >= ?`;
|
|
4505
4712
|
args.push(options.since);
|
|
4506
4713
|
}
|
|
4507
|
-
if (options?.
|
|
4714
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4715
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4716
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4717
|
+
args.push(...uniqueTypes);
|
|
4718
|
+
} else if (options?.memoryType) {
|
|
4508
4719
|
sql += ` AND memory_type = ?`;
|
|
4509
4720
|
args.push(options.memoryType);
|
|
4510
4721
|
}
|
|
@@ -4642,6 +4853,7 @@ var init_store = __esm({
|
|
|
4642
4853
|
init_keychain();
|
|
4643
4854
|
init_config();
|
|
4644
4855
|
init_state_bus();
|
|
4856
|
+
init_memory_write_governor();
|
|
4645
4857
|
INIT_MAX_RETRIES = 3;
|
|
4646
4858
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4647
4859
|
_pendingRecords = [];
|
|
@@ -2592,6 +2592,14 @@ async function ensureSchema() {
|
|
|
2592
2592
|
);
|
|
2593
2593
|
} catch {
|
|
2594
2594
|
}
|
|
2595
|
+
try {
|
|
2596
|
+
await client.execute(
|
|
2597
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2598
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2599
|
+
WHERE content_hash IS NOT NULL`
|
|
2600
|
+
);
|
|
2601
|
+
} catch {
|
|
2602
|
+
}
|
|
2595
2603
|
await client.executeMultiple(`
|
|
2596
2604
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2597
2605
|
id TEXT PRIMARY KEY,
|
|
@@ -3462,6 +3470,197 @@ var init_state_bus = __esm({
|
|
|
3462
3470
|
}
|
|
3463
3471
|
});
|
|
3464
3472
|
|
|
3473
|
+
// src/lib/memory-write-governor.ts
|
|
3474
|
+
import { createHash } from "crypto";
|
|
3475
|
+
function normalizeMemoryText(text) {
|
|
3476
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3477
|
+
}
|
|
3478
|
+
function classifyMemoryType(input2) {
|
|
3479
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3480
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3481
|
+
const text = input2.raw_text.toLowerCase();
|
|
3482
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3483
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3484
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3485
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3486
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3487
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3488
|
+
return "raw";
|
|
3489
|
+
}
|
|
3490
|
+
function shouldDropMemory(text) {
|
|
3491
|
+
const normalized = normalizeMemoryText(text);
|
|
3492
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3493
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3494
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3495
|
+
}
|
|
3496
|
+
return { drop: false };
|
|
3497
|
+
}
|
|
3498
|
+
function shouldSkipEmbedding(input2) {
|
|
3499
|
+
const type = classifyMemoryType(input2);
|
|
3500
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3501
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3502
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3503
|
+
return false;
|
|
3504
|
+
}
|
|
3505
|
+
function hashMemoryContent(text) {
|
|
3506
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3507
|
+
}
|
|
3508
|
+
function scopedDedupArgs(input2) {
|
|
3509
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3510
|
+
}
|
|
3511
|
+
function governMemoryRecord(record) {
|
|
3512
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3513
|
+
const memoryType = classifyMemoryType({
|
|
3514
|
+
raw_text: normalized,
|
|
3515
|
+
agent_id: record.agent_id,
|
|
3516
|
+
project_name: record.project_name,
|
|
3517
|
+
tool_name: record.tool_name,
|
|
3518
|
+
memory_type: record.memory_type
|
|
3519
|
+
});
|
|
3520
|
+
const drop = shouldDropMemory(normalized);
|
|
3521
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3522
|
+
raw_text: normalized,
|
|
3523
|
+
agent_id: record.agent_id,
|
|
3524
|
+
project_name: record.project_name,
|
|
3525
|
+
tool_name: record.tool_name,
|
|
3526
|
+
memory_type: memoryType
|
|
3527
|
+
});
|
|
3528
|
+
return {
|
|
3529
|
+
record: {
|
|
3530
|
+
...record,
|
|
3531
|
+
raw_text: normalized,
|
|
3532
|
+
memory_type: memoryType,
|
|
3533
|
+
vector: skipEmbedding ? null : record.vector
|
|
3534
|
+
},
|
|
3535
|
+
contentHash: hashMemoryContent(normalized),
|
|
3536
|
+
shouldDrop: drop.drop,
|
|
3537
|
+
dropReason: drop.reason,
|
|
3538
|
+
skipEmbedding,
|
|
3539
|
+
hygiene: {
|
|
3540
|
+
dedup: true,
|
|
3541
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3542
|
+
}
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
async function findScopedDuplicate(input2) {
|
|
3546
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3547
|
+
const client = getClient2();
|
|
3548
|
+
const args = scopedDedupArgs(input2);
|
|
3549
|
+
let sql = `SELECT id FROM memories
|
|
3550
|
+
WHERE content_hash = ?
|
|
3551
|
+
AND agent_id = ?
|
|
3552
|
+
AND project_name = ?
|
|
3553
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3554
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3555
|
+
if (input2.excludeId) {
|
|
3556
|
+
sql += " AND id != ?";
|
|
3557
|
+
args.push(input2.excludeId);
|
|
3558
|
+
}
|
|
3559
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3560
|
+
const result = await client.execute({ sql, args });
|
|
3561
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3562
|
+
}
|
|
3563
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3564
|
+
try {
|
|
3565
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3566
|
+
const client = getClient2();
|
|
3567
|
+
const current = await client.execute({
|
|
3568
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3569
|
+
importance, timestamp
|
|
3570
|
+
FROM memories
|
|
3571
|
+
WHERE id = ?
|
|
3572
|
+
LIMIT 1`,
|
|
3573
|
+
args: [memoryId]
|
|
3574
|
+
});
|
|
3575
|
+
const row = current.rows[0];
|
|
3576
|
+
if (!row) return;
|
|
3577
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3578
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3579
|
+
const agentId = String(row.agent_id);
|
|
3580
|
+
const projectName = String(row.project_name);
|
|
3581
|
+
if (contentHash) {
|
|
3582
|
+
await client.execute({
|
|
3583
|
+
sql: `UPDATE memories
|
|
3584
|
+
SET status = 'deleted',
|
|
3585
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3586
|
+
WHERE id != ?
|
|
3587
|
+
AND content_hash = ?
|
|
3588
|
+
AND agent_id = ?
|
|
3589
|
+
AND project_name = ?
|
|
3590
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3591
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3592
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3596
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3597
|
+
const old = await client.execute({
|
|
3598
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3599
|
+
args: [supersedesId]
|
|
3600
|
+
});
|
|
3601
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3602
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3603
|
+
await client.batch([
|
|
3604
|
+
{
|
|
3605
|
+
sql: `UPDATE memories
|
|
3606
|
+
SET status = 'archived',
|
|
3607
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3608
|
+
WHERE id = ?`,
|
|
3609
|
+
args: [supersedesId]
|
|
3610
|
+
},
|
|
3611
|
+
{
|
|
3612
|
+
sql: `UPDATE memories
|
|
3613
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3614
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3615
|
+
WHERE id = ?`,
|
|
3616
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3617
|
+
}
|
|
3618
|
+
], "write");
|
|
3619
|
+
}
|
|
3620
|
+
} catch (err) {
|
|
3621
|
+
process.stderr.write(
|
|
3622
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3623
|
+
`
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3628
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3629
|
+
if (memoryIds.length === 0) return;
|
|
3630
|
+
const run = () => {
|
|
3631
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3632
|
+
};
|
|
3633
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3634
|
+
else setTimeout(run, 0);
|
|
3635
|
+
}
|
|
3636
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3637
|
+
var init_memory_write_governor = __esm({
|
|
3638
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3639
|
+
"use strict";
|
|
3640
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3641
|
+
"decision",
|
|
3642
|
+
"adr",
|
|
3643
|
+
"behavior",
|
|
3644
|
+
"procedure"
|
|
3645
|
+
]);
|
|
3646
|
+
NOISE_DROP_PATTERNS = [
|
|
3647
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3648
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3649
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3650
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3651
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3652
|
+
];
|
|
3653
|
+
SKIP_EMBED_PATTERNS = [
|
|
3654
|
+
/tmux capture-pane\b/i,
|
|
3655
|
+
/docker ps\b/i,
|
|
3656
|
+
/docker images\b/i,
|
|
3657
|
+
/git status\b/i,
|
|
3658
|
+
/grep .*node_modules/i,
|
|
3659
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3660
|
+
];
|
|
3661
|
+
}
|
|
3662
|
+
});
|
|
3663
|
+
|
|
3465
3664
|
// src/lib/shard-manager.ts
|
|
3466
3665
|
var shard_manager_exports = {};
|
|
3467
3666
|
__export(shard_manager_exports, {
|
|
@@ -3626,7 +3825,8 @@ async function ensureShardSchema(client) {
|
|
|
3626
3825
|
}
|
|
3627
3826
|
for (const idx of [
|
|
3628
3827
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3629
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3828
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3829
|
+
"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"
|
|
3630
3830
|
]) {
|
|
3631
3831
|
try {
|
|
3632
3832
|
await client.execute(idx);
|
|
@@ -3820,7 +4020,7 @@ var init_platform_procedures = __esm({
|
|
|
3820
4020
|
title: "Chain of command \u2014 who talks to whom",
|
|
3821
4021
|
domain: "workflow",
|
|
3822
4022
|
priority: "p0",
|
|
3823
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4023
|
+
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."
|
|
3824
4024
|
},
|
|
3825
4025
|
{
|
|
3826
4026
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4051,7 +4251,6 @@ __export(store_exports, {
|
|
|
4051
4251
|
vectorToBlob: () => vectorToBlob,
|
|
4052
4252
|
writeMemory: () => writeMemory
|
|
4053
4253
|
});
|
|
4054
|
-
import { createHash } from "crypto";
|
|
4055
4254
|
function isBusyError2(err) {
|
|
4056
4255
|
if (err instanceof Error) {
|
|
4057
4256
|
const msg = err.message.toLowerCase();
|
|
@@ -4165,17 +4364,24 @@ async function writeMemory(record) {
|
|
|
4165
4364
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4166
4365
|
);
|
|
4167
4366
|
}
|
|
4168
|
-
const
|
|
4169
|
-
if (
|
|
4367
|
+
const governed = governMemoryRecord(record);
|
|
4368
|
+
if (governed.shouldDrop) return;
|
|
4369
|
+
record = governed.record;
|
|
4370
|
+
const contentHash = governed.contentHash;
|
|
4371
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4372
|
+
if (_pendingRecords.some(
|
|
4373
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4374
|
+
)) {
|
|
4170
4375
|
return;
|
|
4171
4376
|
}
|
|
4172
4377
|
try {
|
|
4173
|
-
const
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4378
|
+
const existing = await findScopedDuplicate({
|
|
4379
|
+
contentHash,
|
|
4380
|
+
agentId: record.agent_id,
|
|
4381
|
+
projectName: record.project_name,
|
|
4382
|
+
memoryType
|
|
4177
4383
|
});
|
|
4178
|
-
if (existing
|
|
4384
|
+
if (existing) return;
|
|
4179
4385
|
} catch {
|
|
4180
4386
|
}
|
|
4181
4387
|
const dbRow = {
|
|
@@ -4206,7 +4412,7 @@ async function writeMemory(record) {
|
|
|
4206
4412
|
tier: record.tier ?? classifyTier(record),
|
|
4207
4413
|
supersedes_id: record.supersedes_id ?? null,
|
|
4208
4414
|
draft: record.draft ? 1 : 0,
|
|
4209
|
-
memory_type:
|
|
4415
|
+
memory_type: memoryType,
|
|
4210
4416
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4211
4417
|
content_hash: contentHash,
|
|
4212
4418
|
intent: record.intent ?? null,
|
|
@@ -4365,6 +4571,7 @@ async function flushBatch() {
|
|
|
4365
4571
|
const globalClient = getClient();
|
|
4366
4572
|
const globalStmts = batch.map(buildStmt);
|
|
4367
4573
|
await globalClient.batch(globalStmts, "write");
|
|
4574
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4368
4575
|
_pendingRecords.splice(0, batch.length);
|
|
4369
4576
|
try {
|
|
4370
4577
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4485,7 +4692,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4485
4692
|
sql += ` AND timestamp >= ?`;
|
|
4486
4693
|
args.push(options.since);
|
|
4487
4694
|
}
|
|
4488
|
-
if (options?.
|
|
4695
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4696
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4697
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4698
|
+
args.push(...uniqueTypes);
|
|
4699
|
+
} else if (options?.memoryType) {
|
|
4489
4700
|
sql += ` AND memory_type = ?`;
|
|
4490
4701
|
args.push(options.memoryType);
|
|
4491
4702
|
}
|
|
@@ -4623,6 +4834,7 @@ var init_store = __esm({
|
|
|
4623
4834
|
init_keychain();
|
|
4624
4835
|
init_config();
|
|
4625
4836
|
init_state_bus();
|
|
4837
|
+
init_memory_write_governor();
|
|
4626
4838
|
INIT_MAX_RETRIES = 3;
|
|
4627
4839
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4628
4840
|
_pendingRecords = [];
|
|
@@ -2349,6 +2349,14 @@ async function ensureSchema() {
|
|
|
2349
2349
|
);
|
|
2350
2350
|
} catch {
|
|
2351
2351
|
}
|
|
2352
|
+
try {
|
|
2353
|
+
await client.execute(
|
|
2354
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2355
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2356
|
+
WHERE content_hash IS NOT NULL`
|
|
2357
|
+
);
|
|
2358
|
+
} catch {
|
|
2359
|
+
}
|
|
2352
2360
|
await client.executeMultiple(`
|
|
2353
2361
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2354
2362
|
id TEXT PRIMARY KEY,
|
|
@@ -3373,7 +3381,8 @@ async function ensureShardSchema(client) {
|
|
|
3373
3381
|
}
|
|
3374
3382
|
for (const idx of [
|
|
3375
3383
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3376
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3384
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3385
|
+
"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"
|
|
3377
3386
|
]) {
|
|
3378
3387
|
try {
|
|
3379
3388
|
await client.execute(idx);
|
|
@@ -3567,7 +3576,7 @@ var init_platform_procedures = __esm({
|
|
|
3567
3576
|
title: "Chain of command \u2014 who talks to whom",
|
|
3568
3577
|
domain: "workflow",
|
|
3569
3578
|
priority: "p0",
|
|
3570
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3579
|
+
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."
|
|
3571
3580
|
},
|
|
3572
3581
|
{
|
|
3573
3582
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -6598,7 +6607,11 @@ init_database();
|
|
|
6598
6607
|
init_keychain();
|
|
6599
6608
|
init_config();
|
|
6600
6609
|
init_state_bus();
|
|
6610
|
+
|
|
6611
|
+
// src/lib/memory-write-governor.ts
|
|
6601
6612
|
import { createHash } from "crypto";
|
|
6613
|
+
|
|
6614
|
+
// src/lib/store.ts
|
|
6602
6615
|
var INIT_MAX_RETRIES = 3;
|
|
6603
6616
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
6604
6617
|
function isBusyError2(err) {
|