@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
|
@@ -2585,6 +2585,14 @@ async function ensureSchema() {
|
|
|
2585
2585
|
);
|
|
2586
2586
|
} catch {
|
|
2587
2587
|
}
|
|
2588
|
+
try {
|
|
2589
|
+
await client.execute(
|
|
2590
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2591
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2592
|
+
WHERE content_hash IS NOT NULL`
|
|
2593
|
+
);
|
|
2594
|
+
} catch {
|
|
2595
|
+
}
|
|
2588
2596
|
await client.executeMultiple(`
|
|
2589
2597
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2590
2598
|
id TEXT PRIMARY KEY,
|
|
@@ -3455,6 +3463,197 @@ var init_state_bus = __esm({
|
|
|
3455
3463
|
}
|
|
3456
3464
|
});
|
|
3457
3465
|
|
|
3466
|
+
// src/lib/memory-write-governor.ts
|
|
3467
|
+
import { createHash } from "crypto";
|
|
3468
|
+
function normalizeMemoryText(text) {
|
|
3469
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3470
|
+
}
|
|
3471
|
+
function classifyMemoryType(input2) {
|
|
3472
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3473
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3474
|
+
const text = input2.raw_text.toLowerCase();
|
|
3475
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3476
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3477
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3478
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3479
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3480
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3481
|
+
return "raw";
|
|
3482
|
+
}
|
|
3483
|
+
function shouldDropMemory(text) {
|
|
3484
|
+
const normalized = normalizeMemoryText(text);
|
|
3485
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3486
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3487
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3488
|
+
}
|
|
3489
|
+
return { drop: false };
|
|
3490
|
+
}
|
|
3491
|
+
function shouldSkipEmbedding(input2) {
|
|
3492
|
+
const type = classifyMemoryType(input2);
|
|
3493
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3494
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3495
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3496
|
+
return false;
|
|
3497
|
+
}
|
|
3498
|
+
function hashMemoryContent(text) {
|
|
3499
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3500
|
+
}
|
|
3501
|
+
function scopedDedupArgs(input2) {
|
|
3502
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3503
|
+
}
|
|
3504
|
+
function governMemoryRecord(record) {
|
|
3505
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3506
|
+
const memoryType = classifyMemoryType({
|
|
3507
|
+
raw_text: normalized,
|
|
3508
|
+
agent_id: record.agent_id,
|
|
3509
|
+
project_name: record.project_name,
|
|
3510
|
+
tool_name: record.tool_name,
|
|
3511
|
+
memory_type: record.memory_type
|
|
3512
|
+
});
|
|
3513
|
+
const drop = shouldDropMemory(normalized);
|
|
3514
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3515
|
+
raw_text: normalized,
|
|
3516
|
+
agent_id: record.agent_id,
|
|
3517
|
+
project_name: record.project_name,
|
|
3518
|
+
tool_name: record.tool_name,
|
|
3519
|
+
memory_type: memoryType
|
|
3520
|
+
});
|
|
3521
|
+
return {
|
|
3522
|
+
record: {
|
|
3523
|
+
...record,
|
|
3524
|
+
raw_text: normalized,
|
|
3525
|
+
memory_type: memoryType,
|
|
3526
|
+
vector: skipEmbedding ? null : record.vector
|
|
3527
|
+
},
|
|
3528
|
+
contentHash: hashMemoryContent(normalized),
|
|
3529
|
+
shouldDrop: drop.drop,
|
|
3530
|
+
dropReason: drop.reason,
|
|
3531
|
+
skipEmbedding,
|
|
3532
|
+
hygiene: {
|
|
3533
|
+
dedup: true,
|
|
3534
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3535
|
+
}
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
async function findScopedDuplicate(input2) {
|
|
3539
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3540
|
+
const client = getClient2();
|
|
3541
|
+
const args = scopedDedupArgs(input2);
|
|
3542
|
+
let sql = `SELECT id FROM memories
|
|
3543
|
+
WHERE content_hash = ?
|
|
3544
|
+
AND agent_id = ?
|
|
3545
|
+
AND project_name = ?
|
|
3546
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3547
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3548
|
+
if (input2.excludeId) {
|
|
3549
|
+
sql += " AND id != ?";
|
|
3550
|
+
args.push(input2.excludeId);
|
|
3551
|
+
}
|
|
3552
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3553
|
+
const result = await client.execute({ sql, args });
|
|
3554
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3555
|
+
}
|
|
3556
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3557
|
+
try {
|
|
3558
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3559
|
+
const client = getClient2();
|
|
3560
|
+
const current = await client.execute({
|
|
3561
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3562
|
+
importance, timestamp
|
|
3563
|
+
FROM memories
|
|
3564
|
+
WHERE id = ?
|
|
3565
|
+
LIMIT 1`,
|
|
3566
|
+
args: [memoryId]
|
|
3567
|
+
});
|
|
3568
|
+
const row = current.rows[0];
|
|
3569
|
+
if (!row) return;
|
|
3570
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3571
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3572
|
+
const agentId = String(row.agent_id);
|
|
3573
|
+
const projectName = String(row.project_name);
|
|
3574
|
+
if (contentHash) {
|
|
3575
|
+
await client.execute({
|
|
3576
|
+
sql: `UPDATE memories
|
|
3577
|
+
SET status = 'deleted',
|
|
3578
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3579
|
+
WHERE id != ?
|
|
3580
|
+
AND content_hash = ?
|
|
3581
|
+
AND agent_id = ?
|
|
3582
|
+
AND project_name = ?
|
|
3583
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3584
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3585
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3586
|
+
});
|
|
3587
|
+
}
|
|
3588
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3589
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3590
|
+
const old = await client.execute({
|
|
3591
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3592
|
+
args: [supersedesId]
|
|
3593
|
+
});
|
|
3594
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3595
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3596
|
+
await client.batch([
|
|
3597
|
+
{
|
|
3598
|
+
sql: `UPDATE memories
|
|
3599
|
+
SET status = 'archived',
|
|
3600
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3601
|
+
WHERE id = ?`,
|
|
3602
|
+
args: [supersedesId]
|
|
3603
|
+
},
|
|
3604
|
+
{
|
|
3605
|
+
sql: `UPDATE memories
|
|
3606
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3607
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3608
|
+
WHERE id = ?`,
|
|
3609
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3610
|
+
}
|
|
3611
|
+
], "write");
|
|
3612
|
+
}
|
|
3613
|
+
} catch (err) {
|
|
3614
|
+
process.stderr.write(
|
|
3615
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3616
|
+
`
|
|
3617
|
+
);
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3621
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3622
|
+
if (memoryIds.length === 0) return;
|
|
3623
|
+
const run = () => {
|
|
3624
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3625
|
+
};
|
|
3626
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3627
|
+
else setTimeout(run, 0);
|
|
3628
|
+
}
|
|
3629
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3630
|
+
var init_memory_write_governor = __esm({
|
|
3631
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3632
|
+
"use strict";
|
|
3633
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3634
|
+
"decision",
|
|
3635
|
+
"adr",
|
|
3636
|
+
"behavior",
|
|
3637
|
+
"procedure"
|
|
3638
|
+
]);
|
|
3639
|
+
NOISE_DROP_PATTERNS = [
|
|
3640
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3641
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3642
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3643
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3644
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3645
|
+
];
|
|
3646
|
+
SKIP_EMBED_PATTERNS = [
|
|
3647
|
+
/tmux capture-pane\b/i,
|
|
3648
|
+
/docker ps\b/i,
|
|
3649
|
+
/docker images\b/i,
|
|
3650
|
+
/git status\b/i,
|
|
3651
|
+
/grep .*node_modules/i,
|
|
3652
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3653
|
+
];
|
|
3654
|
+
}
|
|
3655
|
+
});
|
|
3656
|
+
|
|
3458
3657
|
// src/lib/shard-manager.ts
|
|
3459
3658
|
var shard_manager_exports = {};
|
|
3460
3659
|
__export(shard_manager_exports, {
|
|
@@ -3619,7 +3818,8 @@ async function ensureShardSchema(client) {
|
|
|
3619
3818
|
}
|
|
3620
3819
|
for (const idx of [
|
|
3621
3820
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3622
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3821
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3822
|
+
"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"
|
|
3623
3823
|
]) {
|
|
3624
3824
|
try {
|
|
3625
3825
|
await client.execute(idx);
|
|
@@ -3813,7 +4013,7 @@ var init_platform_procedures = __esm({
|
|
|
3813
4013
|
title: "Chain of command \u2014 who talks to whom",
|
|
3814
4014
|
domain: "workflow",
|
|
3815
4015
|
priority: "p0",
|
|
3816
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4016
|
+
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."
|
|
3817
4017
|
},
|
|
3818
4018
|
{
|
|
3819
4019
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4044,7 +4244,6 @@ __export(store_exports, {
|
|
|
4044
4244
|
vectorToBlob: () => vectorToBlob,
|
|
4045
4245
|
writeMemory: () => writeMemory
|
|
4046
4246
|
});
|
|
4047
|
-
import { createHash } from "crypto";
|
|
4048
4247
|
function isBusyError2(err) {
|
|
4049
4248
|
if (err instanceof Error) {
|
|
4050
4249
|
const msg = err.message.toLowerCase();
|
|
@@ -4158,17 +4357,24 @@ async function writeMemory(record) {
|
|
|
4158
4357
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4159
4358
|
);
|
|
4160
4359
|
}
|
|
4161
|
-
const
|
|
4162
|
-
if (
|
|
4360
|
+
const governed = governMemoryRecord(record);
|
|
4361
|
+
if (governed.shouldDrop) return;
|
|
4362
|
+
record = governed.record;
|
|
4363
|
+
const contentHash = governed.contentHash;
|
|
4364
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4365
|
+
if (_pendingRecords.some(
|
|
4366
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4367
|
+
)) {
|
|
4163
4368
|
return;
|
|
4164
4369
|
}
|
|
4165
4370
|
try {
|
|
4166
|
-
const
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4371
|
+
const existing = await findScopedDuplicate({
|
|
4372
|
+
contentHash,
|
|
4373
|
+
agentId: record.agent_id,
|
|
4374
|
+
projectName: record.project_name,
|
|
4375
|
+
memoryType
|
|
4170
4376
|
});
|
|
4171
|
-
if (existing
|
|
4377
|
+
if (existing) return;
|
|
4172
4378
|
} catch {
|
|
4173
4379
|
}
|
|
4174
4380
|
const dbRow = {
|
|
@@ -4199,7 +4405,7 @@ async function writeMemory(record) {
|
|
|
4199
4405
|
tier: record.tier ?? classifyTier(record),
|
|
4200
4406
|
supersedes_id: record.supersedes_id ?? null,
|
|
4201
4407
|
draft: record.draft ? 1 : 0,
|
|
4202
|
-
memory_type:
|
|
4408
|
+
memory_type: memoryType,
|
|
4203
4409
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4204
4410
|
content_hash: contentHash,
|
|
4205
4411
|
intent: record.intent ?? null,
|
|
@@ -4358,6 +4564,7 @@ async function flushBatch() {
|
|
|
4358
4564
|
const globalClient = getClient();
|
|
4359
4565
|
const globalStmts = batch.map(buildStmt);
|
|
4360
4566
|
await globalClient.batch(globalStmts, "write");
|
|
4567
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4361
4568
|
_pendingRecords.splice(0, batch.length);
|
|
4362
4569
|
try {
|
|
4363
4570
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4478,7 +4685,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4478
4685
|
sql += ` AND timestamp >= ?`;
|
|
4479
4686
|
args.push(options.since);
|
|
4480
4687
|
}
|
|
4481
|
-
if (options?.
|
|
4688
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4689
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4690
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4691
|
+
args.push(...uniqueTypes);
|
|
4692
|
+
} else if (options?.memoryType) {
|
|
4482
4693
|
sql += ` AND memory_type = ?`;
|
|
4483
4694
|
args.push(options.memoryType);
|
|
4484
4695
|
}
|
|
@@ -4616,6 +4827,7 @@ var init_store = __esm({
|
|
|
4616
4827
|
init_keychain();
|
|
4617
4828
|
init_config();
|
|
4618
4829
|
init_state_bus();
|
|
4830
|
+
init_memory_write_governor();
|
|
4619
4831
|
INIT_MAX_RETRIES = 3;
|
|
4620
4832
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4621
4833
|
_pendingRecords = [];
|
|
@@ -2454,6 +2454,14 @@ async function ensureSchema() {
|
|
|
2454
2454
|
);
|
|
2455
2455
|
} catch {
|
|
2456
2456
|
}
|
|
2457
|
+
try {
|
|
2458
|
+
await client.execute(
|
|
2459
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2460
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2461
|
+
WHERE content_hash IS NOT NULL`
|
|
2462
|
+
);
|
|
2463
|
+
} catch {
|
|
2464
|
+
}
|
|
2457
2465
|
await client.executeMultiple(`
|
|
2458
2466
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2459
2467
|
id TEXT PRIMARY KEY,
|
|
@@ -3199,6 +3207,197 @@ var init_state_bus = __esm({
|
|
|
3199
3207
|
}
|
|
3200
3208
|
});
|
|
3201
3209
|
|
|
3210
|
+
// src/lib/memory-write-governor.ts
|
|
3211
|
+
import { createHash } from "crypto";
|
|
3212
|
+
function normalizeMemoryText(text) {
|
|
3213
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3214
|
+
}
|
|
3215
|
+
function classifyMemoryType(input2) {
|
|
3216
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3217
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3218
|
+
const text = input2.raw_text.toLowerCase();
|
|
3219
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3220
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3221
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3222
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3223
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3224
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3225
|
+
return "raw";
|
|
3226
|
+
}
|
|
3227
|
+
function shouldDropMemory(text) {
|
|
3228
|
+
const normalized = normalizeMemoryText(text);
|
|
3229
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3230
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3231
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3232
|
+
}
|
|
3233
|
+
return { drop: false };
|
|
3234
|
+
}
|
|
3235
|
+
function shouldSkipEmbedding(input2) {
|
|
3236
|
+
const type = classifyMemoryType(input2);
|
|
3237
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3238
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3239
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3240
|
+
return false;
|
|
3241
|
+
}
|
|
3242
|
+
function hashMemoryContent(text) {
|
|
3243
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3244
|
+
}
|
|
3245
|
+
function scopedDedupArgs(input2) {
|
|
3246
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3247
|
+
}
|
|
3248
|
+
function governMemoryRecord(record) {
|
|
3249
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3250
|
+
const memoryType = classifyMemoryType({
|
|
3251
|
+
raw_text: normalized,
|
|
3252
|
+
agent_id: record.agent_id,
|
|
3253
|
+
project_name: record.project_name,
|
|
3254
|
+
tool_name: record.tool_name,
|
|
3255
|
+
memory_type: record.memory_type
|
|
3256
|
+
});
|
|
3257
|
+
const drop = shouldDropMemory(normalized);
|
|
3258
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3259
|
+
raw_text: normalized,
|
|
3260
|
+
agent_id: record.agent_id,
|
|
3261
|
+
project_name: record.project_name,
|
|
3262
|
+
tool_name: record.tool_name,
|
|
3263
|
+
memory_type: memoryType
|
|
3264
|
+
});
|
|
3265
|
+
return {
|
|
3266
|
+
record: {
|
|
3267
|
+
...record,
|
|
3268
|
+
raw_text: normalized,
|
|
3269
|
+
memory_type: memoryType,
|
|
3270
|
+
vector: skipEmbedding ? null : record.vector
|
|
3271
|
+
},
|
|
3272
|
+
contentHash: hashMemoryContent(normalized),
|
|
3273
|
+
shouldDrop: drop.drop,
|
|
3274
|
+
dropReason: drop.reason,
|
|
3275
|
+
skipEmbedding,
|
|
3276
|
+
hygiene: {
|
|
3277
|
+
dedup: true,
|
|
3278
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3279
|
+
}
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
async function findScopedDuplicate(input2) {
|
|
3283
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3284
|
+
const client = getClient2();
|
|
3285
|
+
const args = scopedDedupArgs(input2);
|
|
3286
|
+
let sql = `SELECT id FROM memories
|
|
3287
|
+
WHERE content_hash = ?
|
|
3288
|
+
AND agent_id = ?
|
|
3289
|
+
AND project_name = ?
|
|
3290
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3291
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3292
|
+
if (input2.excludeId) {
|
|
3293
|
+
sql += " AND id != ?";
|
|
3294
|
+
args.push(input2.excludeId);
|
|
3295
|
+
}
|
|
3296
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3297
|
+
const result = await client.execute({ sql, args });
|
|
3298
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3299
|
+
}
|
|
3300
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3301
|
+
try {
|
|
3302
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3303
|
+
const client = getClient2();
|
|
3304
|
+
const current = await client.execute({
|
|
3305
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3306
|
+
importance, timestamp
|
|
3307
|
+
FROM memories
|
|
3308
|
+
WHERE id = ?
|
|
3309
|
+
LIMIT 1`,
|
|
3310
|
+
args: [memoryId]
|
|
3311
|
+
});
|
|
3312
|
+
const row = current.rows[0];
|
|
3313
|
+
if (!row) return;
|
|
3314
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3315
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3316
|
+
const agentId = String(row.agent_id);
|
|
3317
|
+
const projectName = String(row.project_name);
|
|
3318
|
+
if (contentHash) {
|
|
3319
|
+
await client.execute({
|
|
3320
|
+
sql: `UPDATE memories
|
|
3321
|
+
SET status = 'deleted',
|
|
3322
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3323
|
+
WHERE id != ?
|
|
3324
|
+
AND content_hash = ?
|
|
3325
|
+
AND agent_id = ?
|
|
3326
|
+
AND project_name = ?
|
|
3327
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3328
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3329
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3333
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3334
|
+
const old = await client.execute({
|
|
3335
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3336
|
+
args: [supersedesId]
|
|
3337
|
+
});
|
|
3338
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3339
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3340
|
+
await client.batch([
|
|
3341
|
+
{
|
|
3342
|
+
sql: `UPDATE memories
|
|
3343
|
+
SET status = 'archived',
|
|
3344
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3345
|
+
WHERE id = ?`,
|
|
3346
|
+
args: [supersedesId]
|
|
3347
|
+
},
|
|
3348
|
+
{
|
|
3349
|
+
sql: `UPDATE memories
|
|
3350
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3351
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3352
|
+
WHERE id = ?`,
|
|
3353
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3354
|
+
}
|
|
3355
|
+
], "write");
|
|
3356
|
+
}
|
|
3357
|
+
} catch (err) {
|
|
3358
|
+
process.stderr.write(
|
|
3359
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3360
|
+
`
|
|
3361
|
+
);
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3365
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3366
|
+
if (memoryIds.length === 0) return;
|
|
3367
|
+
const run = () => {
|
|
3368
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3369
|
+
};
|
|
3370
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3371
|
+
else setTimeout(run, 0);
|
|
3372
|
+
}
|
|
3373
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3374
|
+
var init_memory_write_governor = __esm({
|
|
3375
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3376
|
+
"use strict";
|
|
3377
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3378
|
+
"decision",
|
|
3379
|
+
"adr",
|
|
3380
|
+
"behavior",
|
|
3381
|
+
"procedure"
|
|
3382
|
+
]);
|
|
3383
|
+
NOISE_DROP_PATTERNS = [
|
|
3384
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3385
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3386
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3387
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3388
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3389
|
+
];
|
|
3390
|
+
SKIP_EMBED_PATTERNS = [
|
|
3391
|
+
/tmux capture-pane\b/i,
|
|
3392
|
+
/docker ps\b/i,
|
|
3393
|
+
/docker images\b/i,
|
|
3394
|
+
/git status\b/i,
|
|
3395
|
+
/grep .*node_modules/i,
|
|
3396
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3397
|
+
];
|
|
3398
|
+
}
|
|
3399
|
+
});
|
|
3400
|
+
|
|
3202
3401
|
// src/lib/shard-manager.ts
|
|
3203
3402
|
var shard_manager_exports = {};
|
|
3204
3403
|
__export(shard_manager_exports, {
|
|
@@ -3363,7 +3562,8 @@ async function ensureShardSchema(client) {
|
|
|
3363
3562
|
}
|
|
3364
3563
|
for (const idx of [
|
|
3365
3564
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3366
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3565
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3566
|
+
"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"
|
|
3367
3567
|
]) {
|
|
3368
3568
|
try {
|
|
3369
3569
|
await client.execute(idx);
|
|
@@ -3557,7 +3757,7 @@ var init_platform_procedures = __esm({
|
|
|
3557
3757
|
title: "Chain of command \u2014 who talks to whom",
|
|
3558
3758
|
domain: "workflow",
|
|
3559
3759
|
priority: "p0",
|
|
3560
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3760
|
+
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."
|
|
3561
3761
|
},
|
|
3562
3762
|
{
|
|
3563
3763
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3788,7 +3988,6 @@ __export(store_exports, {
|
|
|
3788
3988
|
vectorToBlob: () => vectorToBlob,
|
|
3789
3989
|
writeMemory: () => writeMemory
|
|
3790
3990
|
});
|
|
3791
|
-
import { createHash } from "crypto";
|
|
3792
3991
|
function isBusyError2(err) {
|
|
3793
3992
|
if (err instanceof Error) {
|
|
3794
3993
|
const msg = err.message.toLowerCase();
|
|
@@ -3902,17 +4101,24 @@ async function writeMemory(record) {
|
|
|
3902
4101
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3903
4102
|
);
|
|
3904
4103
|
}
|
|
3905
|
-
const
|
|
3906
|
-
if (
|
|
4104
|
+
const governed = governMemoryRecord(record);
|
|
4105
|
+
if (governed.shouldDrop) return;
|
|
4106
|
+
record = governed.record;
|
|
4107
|
+
const contentHash = governed.contentHash;
|
|
4108
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4109
|
+
if (_pendingRecords.some(
|
|
4110
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4111
|
+
)) {
|
|
3907
4112
|
return;
|
|
3908
4113
|
}
|
|
3909
4114
|
try {
|
|
3910
|
-
const
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
4115
|
+
const existing = await findScopedDuplicate({
|
|
4116
|
+
contentHash,
|
|
4117
|
+
agentId: record.agent_id,
|
|
4118
|
+
projectName: record.project_name,
|
|
4119
|
+
memoryType
|
|
3914
4120
|
});
|
|
3915
|
-
if (existing
|
|
4121
|
+
if (existing) return;
|
|
3916
4122
|
} catch {
|
|
3917
4123
|
}
|
|
3918
4124
|
const dbRow = {
|
|
@@ -3943,7 +4149,7 @@ async function writeMemory(record) {
|
|
|
3943
4149
|
tier: record.tier ?? classifyTier(record),
|
|
3944
4150
|
supersedes_id: record.supersedes_id ?? null,
|
|
3945
4151
|
draft: record.draft ? 1 : 0,
|
|
3946
|
-
memory_type:
|
|
4152
|
+
memory_type: memoryType,
|
|
3947
4153
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3948
4154
|
content_hash: contentHash,
|
|
3949
4155
|
intent: record.intent ?? null,
|
|
@@ -4102,6 +4308,7 @@ async function flushBatch() {
|
|
|
4102
4308
|
const globalClient = getClient();
|
|
4103
4309
|
const globalStmts = batch.map(buildStmt);
|
|
4104
4310
|
await globalClient.batch(globalStmts, "write");
|
|
4311
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4105
4312
|
_pendingRecords.splice(0, batch.length);
|
|
4106
4313
|
try {
|
|
4107
4314
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4222,7 +4429,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4222
4429
|
sql += ` AND timestamp >= ?`;
|
|
4223
4430
|
args.push(options.since);
|
|
4224
4431
|
}
|
|
4225
|
-
if (options?.
|
|
4432
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4433
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4434
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4435
|
+
args.push(...uniqueTypes);
|
|
4436
|
+
} else if (options?.memoryType) {
|
|
4226
4437
|
sql += ` AND memory_type = ?`;
|
|
4227
4438
|
args.push(options.memoryType);
|
|
4228
4439
|
}
|
|
@@ -4360,6 +4571,7 @@ var init_store = __esm({
|
|
|
4360
4571
|
init_keychain();
|
|
4361
4572
|
init_config();
|
|
4362
4573
|
init_state_bus();
|
|
4574
|
+
init_memory_write_governor();
|
|
4363
4575
|
INIT_MAX_RETRIES = 3;
|
|
4364
4576
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4365
4577
|
_pendingRecords = [];
|
|
@@ -5519,6 +5731,17 @@ __export(hybrid_search_exports, {
|
|
|
5519
5731
|
rrfMerge: () => rrfMerge,
|
|
5520
5732
|
rrfMergeMulti: () => rrfMergeMulti
|
|
5521
5733
|
});
|
|
5734
|
+
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
5735
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
5736
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
5737
|
+
sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
5738
|
+
args.push(...uniqueTypes);
|
|
5739
|
+
} else if (options?.memoryType) {
|
|
5740
|
+
sql += ` AND ${column} = ?`;
|
|
5741
|
+
args.push(options.memoryType);
|
|
5742
|
+
}
|
|
5743
|
+
return sql;
|
|
5744
|
+
}
|
|
5522
5745
|
async function hybridSearch(queryText, agentId, options) {
|
|
5523
5746
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5524
5747
|
const config = await loadConfig2();
|
|
@@ -5747,6 +5970,7 @@ async function estimateCardinality(agentId, options) {
|
|
|
5747
5970
|
sql += ` AND timestamp >= ?`;
|
|
5748
5971
|
args.push(options.since);
|
|
5749
5972
|
}
|
|
5973
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
5750
5974
|
try {
|
|
5751
5975
|
const result = await client.execute({ sql, args });
|
|
5752
5976
|
return Number(result.rows[0]?.cnt) || 0;
|
|
@@ -5868,10 +6092,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
5868
6092
|
sql += ` AND m.timestamp >= ?`;
|
|
5869
6093
|
args.push(options.since);
|
|
5870
6094
|
}
|
|
5871
|
-
|
|
5872
|
-
sql += ` AND m.memory_type = ?`;
|
|
5873
|
-
args.push(options.memoryType);
|
|
5874
|
-
}
|
|
6095
|
+
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
5875
6096
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
5876
6097
|
args.push(limit);
|
|
5877
6098
|
const result = await client.execute({ sql, args });
|
|
@@ -5928,9 +6149,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
5928
6149
|
AND timestamp >= ? AND timestamp <= ?
|
|
5929
6150
|
AND COALESCE(status, 'active') = 'active'
|
|
5930
6151
|
AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
|
|
6152
|
+
${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
|
|
5931
6153
|
AND COALESCE(confidence, 0.7) >= 0.3
|
|
5932
6154
|
ORDER BY timestamp DESC LIMIT ?`,
|
|
5933
|
-
args: [
|
|
6155
|
+
args: [
|
|
6156
|
+
agentId,
|
|
6157
|
+
windowStart,
|
|
6158
|
+
killedAt,
|
|
6159
|
+
...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
|
|
6160
|
+
boundarySlots
|
|
6161
|
+
]
|
|
5934
6162
|
});
|
|
5935
6163
|
for (const row of boundaryResult.rows) {
|
|
5936
6164
|
sessionBoundaryMemories.push(rowToMemoryRecord(row));
|
|
@@ -5976,10 +6204,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
5976
6204
|
sql += ` AND timestamp >= ?`;
|
|
5977
6205
|
args.push(options.since);
|
|
5978
6206
|
}
|
|
5979
|
-
|
|
5980
|
-
sql += ` AND memory_type = ?`;
|
|
5981
|
-
args.push(options.memoryType);
|
|
5982
|
-
}
|
|
6207
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
5983
6208
|
if (textFilter) {
|
|
5984
6209
|
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
5985
6210
|
args.push(textFilter);
|