@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
|
@@ -2687,6 +2687,14 @@ async function ensureSchema() {
|
|
|
2687
2687
|
);
|
|
2688
2688
|
} catch {
|
|
2689
2689
|
}
|
|
2690
|
+
try {
|
|
2691
|
+
await client.execute(
|
|
2692
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2693
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2694
|
+
WHERE content_hash IS NOT NULL`
|
|
2695
|
+
);
|
|
2696
|
+
} catch {
|
|
2697
|
+
}
|
|
2690
2698
|
await client.executeMultiple(`
|
|
2691
2699
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2692
2700
|
id TEXT PRIMARY KEY,
|
|
@@ -3432,6 +3440,197 @@ var init_state_bus = __esm({
|
|
|
3432
3440
|
}
|
|
3433
3441
|
});
|
|
3434
3442
|
|
|
3443
|
+
// src/lib/memory-write-governor.ts
|
|
3444
|
+
import { createHash } from "crypto";
|
|
3445
|
+
function normalizeMemoryText(text) {
|
|
3446
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3447
|
+
}
|
|
3448
|
+
function classifyMemoryType(input) {
|
|
3449
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3450
|
+
const tool = input.tool_name.toLowerCase();
|
|
3451
|
+
const text = input.raw_text.toLowerCase();
|
|
3452
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3453
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3454
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3455
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3456
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3457
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3458
|
+
return "raw";
|
|
3459
|
+
}
|
|
3460
|
+
function shouldDropMemory(text) {
|
|
3461
|
+
const normalized = normalizeMemoryText(text);
|
|
3462
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3463
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3464
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3465
|
+
}
|
|
3466
|
+
return { drop: false };
|
|
3467
|
+
}
|
|
3468
|
+
function shouldSkipEmbedding(input) {
|
|
3469
|
+
const type = classifyMemoryType(input);
|
|
3470
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3471
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3472
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3473
|
+
return false;
|
|
3474
|
+
}
|
|
3475
|
+
function hashMemoryContent(text) {
|
|
3476
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3477
|
+
}
|
|
3478
|
+
function scopedDedupArgs(input) {
|
|
3479
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3480
|
+
}
|
|
3481
|
+
function governMemoryRecord(record) {
|
|
3482
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3483
|
+
const memoryType = classifyMemoryType({
|
|
3484
|
+
raw_text: normalized,
|
|
3485
|
+
agent_id: record.agent_id,
|
|
3486
|
+
project_name: record.project_name,
|
|
3487
|
+
tool_name: record.tool_name,
|
|
3488
|
+
memory_type: record.memory_type
|
|
3489
|
+
});
|
|
3490
|
+
const drop = shouldDropMemory(normalized);
|
|
3491
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3492
|
+
raw_text: normalized,
|
|
3493
|
+
agent_id: record.agent_id,
|
|
3494
|
+
project_name: record.project_name,
|
|
3495
|
+
tool_name: record.tool_name,
|
|
3496
|
+
memory_type: memoryType
|
|
3497
|
+
});
|
|
3498
|
+
return {
|
|
3499
|
+
record: {
|
|
3500
|
+
...record,
|
|
3501
|
+
raw_text: normalized,
|
|
3502
|
+
memory_type: memoryType,
|
|
3503
|
+
vector: skipEmbedding ? null : record.vector
|
|
3504
|
+
},
|
|
3505
|
+
contentHash: hashMemoryContent(normalized),
|
|
3506
|
+
shouldDrop: drop.drop,
|
|
3507
|
+
dropReason: drop.reason,
|
|
3508
|
+
skipEmbedding,
|
|
3509
|
+
hygiene: {
|
|
3510
|
+
dedup: true,
|
|
3511
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3515
|
+
async function findScopedDuplicate(input) {
|
|
3516
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3517
|
+
const client = getClient2();
|
|
3518
|
+
const args = scopedDedupArgs(input);
|
|
3519
|
+
let sql = `SELECT id FROM memories
|
|
3520
|
+
WHERE content_hash = ?
|
|
3521
|
+
AND agent_id = ?
|
|
3522
|
+
AND project_name = ?
|
|
3523
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3524
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3525
|
+
if (input.excludeId) {
|
|
3526
|
+
sql += " AND id != ?";
|
|
3527
|
+
args.push(input.excludeId);
|
|
3528
|
+
}
|
|
3529
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3530
|
+
const result = await client.execute({ sql, args });
|
|
3531
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3532
|
+
}
|
|
3533
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3534
|
+
try {
|
|
3535
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3536
|
+
const client = getClient2();
|
|
3537
|
+
const current = await client.execute({
|
|
3538
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3539
|
+
importance, timestamp
|
|
3540
|
+
FROM memories
|
|
3541
|
+
WHERE id = ?
|
|
3542
|
+
LIMIT 1`,
|
|
3543
|
+
args: [memoryId]
|
|
3544
|
+
});
|
|
3545
|
+
const row = current.rows[0];
|
|
3546
|
+
if (!row) return;
|
|
3547
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3548
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3549
|
+
const agentId = String(row.agent_id);
|
|
3550
|
+
const projectName = String(row.project_name);
|
|
3551
|
+
if (contentHash) {
|
|
3552
|
+
await client.execute({
|
|
3553
|
+
sql: `UPDATE memories
|
|
3554
|
+
SET status = 'deleted',
|
|
3555
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3556
|
+
WHERE id != ?
|
|
3557
|
+
AND content_hash = ?
|
|
3558
|
+
AND agent_id = ?
|
|
3559
|
+
AND project_name = ?
|
|
3560
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3561
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3562
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3563
|
+
});
|
|
3564
|
+
}
|
|
3565
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3566
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3567
|
+
const old = await client.execute({
|
|
3568
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3569
|
+
args: [supersedesId]
|
|
3570
|
+
});
|
|
3571
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3572
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3573
|
+
await client.batch([
|
|
3574
|
+
{
|
|
3575
|
+
sql: `UPDATE memories
|
|
3576
|
+
SET status = 'archived',
|
|
3577
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3578
|
+
WHERE id = ?`,
|
|
3579
|
+
args: [supersedesId]
|
|
3580
|
+
},
|
|
3581
|
+
{
|
|
3582
|
+
sql: `UPDATE memories
|
|
3583
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3584
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3585
|
+
WHERE id = ?`,
|
|
3586
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3587
|
+
}
|
|
3588
|
+
], "write");
|
|
3589
|
+
}
|
|
3590
|
+
} catch (err) {
|
|
3591
|
+
process.stderr.write(
|
|
3592
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3593
|
+
`
|
|
3594
|
+
);
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3598
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3599
|
+
if (memoryIds.length === 0) return;
|
|
3600
|
+
const run = () => {
|
|
3601
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3602
|
+
};
|
|
3603
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3604
|
+
else setTimeout(run, 0);
|
|
3605
|
+
}
|
|
3606
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3607
|
+
var init_memory_write_governor = __esm({
|
|
3608
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3609
|
+
"use strict";
|
|
3610
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3611
|
+
"decision",
|
|
3612
|
+
"adr",
|
|
3613
|
+
"behavior",
|
|
3614
|
+
"procedure"
|
|
3615
|
+
]);
|
|
3616
|
+
NOISE_DROP_PATTERNS = [
|
|
3617
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3618
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3619
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3620
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3621
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3622
|
+
];
|
|
3623
|
+
SKIP_EMBED_PATTERNS = [
|
|
3624
|
+
/tmux capture-pane\b/i,
|
|
3625
|
+
/docker ps\b/i,
|
|
3626
|
+
/docker images\b/i,
|
|
3627
|
+
/git status\b/i,
|
|
3628
|
+
/grep .*node_modules/i,
|
|
3629
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3630
|
+
];
|
|
3631
|
+
}
|
|
3632
|
+
});
|
|
3633
|
+
|
|
3435
3634
|
// src/lib/shard-manager.ts
|
|
3436
3635
|
var shard_manager_exports = {};
|
|
3437
3636
|
__export(shard_manager_exports, {
|
|
@@ -3596,7 +3795,8 @@ async function ensureShardSchema(client) {
|
|
|
3596
3795
|
}
|
|
3597
3796
|
for (const idx of [
|
|
3598
3797
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3599
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3798
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3799
|
+
"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"
|
|
3600
3800
|
]) {
|
|
3601
3801
|
try {
|
|
3602
3802
|
await client.execute(idx);
|
|
@@ -3790,7 +3990,7 @@ var init_platform_procedures = __esm({
|
|
|
3790
3990
|
title: "Chain of command \u2014 who talks to whom",
|
|
3791
3991
|
domain: "workflow",
|
|
3792
3992
|
priority: "p0",
|
|
3793
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3993
|
+
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."
|
|
3794
3994
|
},
|
|
3795
3995
|
{
|
|
3796
3996
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4021,7 +4221,6 @@ __export(store_exports, {
|
|
|
4021
4221
|
vectorToBlob: () => vectorToBlob,
|
|
4022
4222
|
writeMemory: () => writeMemory
|
|
4023
4223
|
});
|
|
4024
|
-
import { createHash } from "crypto";
|
|
4025
4224
|
function isBusyError2(err) {
|
|
4026
4225
|
if (err instanceof Error) {
|
|
4027
4226
|
const msg = err.message.toLowerCase();
|
|
@@ -4135,17 +4334,24 @@ async function writeMemory(record) {
|
|
|
4135
4334
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4136
4335
|
);
|
|
4137
4336
|
}
|
|
4138
|
-
const
|
|
4139
|
-
if (
|
|
4337
|
+
const governed = governMemoryRecord(record);
|
|
4338
|
+
if (governed.shouldDrop) return;
|
|
4339
|
+
record = governed.record;
|
|
4340
|
+
const contentHash = governed.contentHash;
|
|
4341
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4342
|
+
if (_pendingRecords.some(
|
|
4343
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4344
|
+
)) {
|
|
4140
4345
|
return;
|
|
4141
4346
|
}
|
|
4142
4347
|
try {
|
|
4143
|
-
const
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4348
|
+
const existing = await findScopedDuplicate({
|
|
4349
|
+
contentHash,
|
|
4350
|
+
agentId: record.agent_id,
|
|
4351
|
+
projectName: record.project_name,
|
|
4352
|
+
memoryType
|
|
4147
4353
|
});
|
|
4148
|
-
if (existing
|
|
4354
|
+
if (existing) return;
|
|
4149
4355
|
} catch {
|
|
4150
4356
|
}
|
|
4151
4357
|
const dbRow = {
|
|
@@ -4176,7 +4382,7 @@ async function writeMemory(record) {
|
|
|
4176
4382
|
tier: record.tier ?? classifyTier(record),
|
|
4177
4383
|
supersedes_id: record.supersedes_id ?? null,
|
|
4178
4384
|
draft: record.draft ? 1 : 0,
|
|
4179
|
-
memory_type:
|
|
4385
|
+
memory_type: memoryType,
|
|
4180
4386
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4181
4387
|
content_hash: contentHash,
|
|
4182
4388
|
intent: record.intent ?? null,
|
|
@@ -4335,6 +4541,7 @@ async function flushBatch() {
|
|
|
4335
4541
|
const globalClient = getClient();
|
|
4336
4542
|
const globalStmts = batch.map(buildStmt);
|
|
4337
4543
|
await globalClient.batch(globalStmts, "write");
|
|
4544
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4338
4545
|
_pendingRecords.splice(0, batch.length);
|
|
4339
4546
|
try {
|
|
4340
4547
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4455,7 +4662,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4455
4662
|
sql += ` AND timestamp >= ?`;
|
|
4456
4663
|
args.push(options.since);
|
|
4457
4664
|
}
|
|
4458
|
-
if (options?.
|
|
4665
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4666
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4667
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4668
|
+
args.push(...uniqueTypes);
|
|
4669
|
+
} else if (options?.memoryType) {
|
|
4459
4670
|
sql += ` AND memory_type = ?`;
|
|
4460
4671
|
args.push(options.memoryType);
|
|
4461
4672
|
}
|
|
@@ -4593,6 +4804,7 @@ var init_store = __esm({
|
|
|
4593
4804
|
init_keychain();
|
|
4594
4805
|
init_config();
|
|
4595
4806
|
init_state_bus();
|
|
4807
|
+
init_memory_write_governor();
|
|
4596
4808
|
INIT_MAX_RETRIES = 3;
|
|
4597
4809
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4598
4810
|
_pendingRecords = [];
|
|
@@ -6434,7 +6646,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6434
6646
|
}
|
|
6435
6647
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6436
6648
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6437
|
-
|
|
6649
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6650
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6651
|
+
ccModel += "[1m]";
|
|
6652
|
+
}
|
|
6653
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6438
6654
|
}
|
|
6439
6655
|
}
|
|
6440
6656
|
let spawnCommand;
|
|
@@ -2371,6 +2371,14 @@ async function ensureSchema() {
|
|
|
2371
2371
|
);
|
|
2372
2372
|
} catch {
|
|
2373
2373
|
}
|
|
2374
|
+
try {
|
|
2375
|
+
await client.execute(
|
|
2376
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2377
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2378
|
+
WHERE content_hash IS NOT NULL`
|
|
2379
|
+
);
|
|
2380
|
+
} catch {
|
|
2381
|
+
}
|
|
2374
2382
|
await client.executeMultiple(`
|
|
2375
2383
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2376
2384
|
id TEXT PRIMARY KEY,
|
|
@@ -3116,6 +3124,197 @@ var init_state_bus = __esm({
|
|
|
3116
3124
|
}
|
|
3117
3125
|
});
|
|
3118
3126
|
|
|
3127
|
+
// src/lib/memory-write-governor.ts
|
|
3128
|
+
import { createHash } from "crypto";
|
|
3129
|
+
function normalizeMemoryText(text) {
|
|
3130
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3131
|
+
}
|
|
3132
|
+
function classifyMemoryType(input) {
|
|
3133
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3134
|
+
const tool = input.tool_name.toLowerCase();
|
|
3135
|
+
const text = input.raw_text.toLowerCase();
|
|
3136
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3137
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3138
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3139
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3140
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3141
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3142
|
+
return "raw";
|
|
3143
|
+
}
|
|
3144
|
+
function shouldDropMemory(text) {
|
|
3145
|
+
const normalized = normalizeMemoryText(text);
|
|
3146
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3147
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3148
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3149
|
+
}
|
|
3150
|
+
return { drop: false };
|
|
3151
|
+
}
|
|
3152
|
+
function shouldSkipEmbedding(input) {
|
|
3153
|
+
const type = classifyMemoryType(input);
|
|
3154
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3155
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3156
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3157
|
+
return false;
|
|
3158
|
+
}
|
|
3159
|
+
function hashMemoryContent(text) {
|
|
3160
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3161
|
+
}
|
|
3162
|
+
function scopedDedupArgs(input) {
|
|
3163
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3164
|
+
}
|
|
3165
|
+
function governMemoryRecord(record) {
|
|
3166
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3167
|
+
const memoryType = classifyMemoryType({
|
|
3168
|
+
raw_text: normalized,
|
|
3169
|
+
agent_id: record.agent_id,
|
|
3170
|
+
project_name: record.project_name,
|
|
3171
|
+
tool_name: record.tool_name,
|
|
3172
|
+
memory_type: record.memory_type
|
|
3173
|
+
});
|
|
3174
|
+
const drop = shouldDropMemory(normalized);
|
|
3175
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3176
|
+
raw_text: normalized,
|
|
3177
|
+
agent_id: record.agent_id,
|
|
3178
|
+
project_name: record.project_name,
|
|
3179
|
+
tool_name: record.tool_name,
|
|
3180
|
+
memory_type: memoryType
|
|
3181
|
+
});
|
|
3182
|
+
return {
|
|
3183
|
+
record: {
|
|
3184
|
+
...record,
|
|
3185
|
+
raw_text: normalized,
|
|
3186
|
+
memory_type: memoryType,
|
|
3187
|
+
vector: skipEmbedding ? null : record.vector
|
|
3188
|
+
},
|
|
3189
|
+
contentHash: hashMemoryContent(normalized),
|
|
3190
|
+
shouldDrop: drop.drop,
|
|
3191
|
+
dropReason: drop.reason,
|
|
3192
|
+
skipEmbedding,
|
|
3193
|
+
hygiene: {
|
|
3194
|
+
dedup: true,
|
|
3195
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3196
|
+
}
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
3199
|
+
async function findScopedDuplicate(input) {
|
|
3200
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3201
|
+
const client = getClient2();
|
|
3202
|
+
const args = scopedDedupArgs(input);
|
|
3203
|
+
let sql = `SELECT id FROM memories
|
|
3204
|
+
WHERE content_hash = ?
|
|
3205
|
+
AND agent_id = ?
|
|
3206
|
+
AND project_name = ?
|
|
3207
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3208
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3209
|
+
if (input.excludeId) {
|
|
3210
|
+
sql += " AND id != ?";
|
|
3211
|
+
args.push(input.excludeId);
|
|
3212
|
+
}
|
|
3213
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3214
|
+
const result = await client.execute({ sql, args });
|
|
3215
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3216
|
+
}
|
|
3217
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3218
|
+
try {
|
|
3219
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3220
|
+
const client = getClient2();
|
|
3221
|
+
const current = await client.execute({
|
|
3222
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3223
|
+
importance, timestamp
|
|
3224
|
+
FROM memories
|
|
3225
|
+
WHERE id = ?
|
|
3226
|
+
LIMIT 1`,
|
|
3227
|
+
args: [memoryId]
|
|
3228
|
+
});
|
|
3229
|
+
const row = current.rows[0];
|
|
3230
|
+
if (!row) return;
|
|
3231
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3232
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3233
|
+
const agentId = String(row.agent_id);
|
|
3234
|
+
const projectName = String(row.project_name);
|
|
3235
|
+
if (contentHash) {
|
|
3236
|
+
await client.execute({
|
|
3237
|
+
sql: `UPDATE memories
|
|
3238
|
+
SET status = 'deleted',
|
|
3239
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3240
|
+
WHERE id != ?
|
|
3241
|
+
AND content_hash = ?
|
|
3242
|
+
AND agent_id = ?
|
|
3243
|
+
AND project_name = ?
|
|
3244
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3245
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3246
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3250
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3251
|
+
const old = await client.execute({
|
|
3252
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3253
|
+
args: [supersedesId]
|
|
3254
|
+
});
|
|
3255
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3256
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3257
|
+
await client.batch([
|
|
3258
|
+
{
|
|
3259
|
+
sql: `UPDATE memories
|
|
3260
|
+
SET status = 'archived',
|
|
3261
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3262
|
+
WHERE id = ?`,
|
|
3263
|
+
args: [supersedesId]
|
|
3264
|
+
},
|
|
3265
|
+
{
|
|
3266
|
+
sql: `UPDATE memories
|
|
3267
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3268
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3269
|
+
WHERE id = ?`,
|
|
3270
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3271
|
+
}
|
|
3272
|
+
], "write");
|
|
3273
|
+
}
|
|
3274
|
+
} catch (err) {
|
|
3275
|
+
process.stderr.write(
|
|
3276
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3277
|
+
`
|
|
3278
|
+
);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3282
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3283
|
+
if (memoryIds.length === 0) return;
|
|
3284
|
+
const run = () => {
|
|
3285
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3286
|
+
};
|
|
3287
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3288
|
+
else setTimeout(run, 0);
|
|
3289
|
+
}
|
|
3290
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3291
|
+
var init_memory_write_governor = __esm({
|
|
3292
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3293
|
+
"use strict";
|
|
3294
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3295
|
+
"decision",
|
|
3296
|
+
"adr",
|
|
3297
|
+
"behavior",
|
|
3298
|
+
"procedure"
|
|
3299
|
+
]);
|
|
3300
|
+
NOISE_DROP_PATTERNS = [
|
|
3301
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3302
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3303
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3304
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3305
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3306
|
+
];
|
|
3307
|
+
SKIP_EMBED_PATTERNS = [
|
|
3308
|
+
/tmux capture-pane\b/i,
|
|
3309
|
+
/docker ps\b/i,
|
|
3310
|
+
/docker images\b/i,
|
|
3311
|
+
/git status\b/i,
|
|
3312
|
+
/grep .*node_modules/i,
|
|
3313
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3314
|
+
];
|
|
3315
|
+
}
|
|
3316
|
+
});
|
|
3317
|
+
|
|
3119
3318
|
// src/lib/shard-manager.ts
|
|
3120
3319
|
var shard_manager_exports = {};
|
|
3121
3320
|
__export(shard_manager_exports, {
|
|
@@ -3280,7 +3479,8 @@ async function ensureShardSchema(client) {
|
|
|
3280
3479
|
}
|
|
3281
3480
|
for (const idx of [
|
|
3282
3481
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3283
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3482
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3483
|
+
"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"
|
|
3284
3484
|
]) {
|
|
3285
3485
|
try {
|
|
3286
3486
|
await client.execute(idx);
|
|
@@ -3474,7 +3674,7 @@ var init_platform_procedures = __esm({
|
|
|
3474
3674
|
title: "Chain of command \u2014 who talks to whom",
|
|
3475
3675
|
domain: "workflow",
|
|
3476
3676
|
priority: "p0",
|
|
3477
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3677
|
+
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."
|
|
3478
3678
|
},
|
|
3479
3679
|
{
|
|
3480
3680
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3705,7 +3905,6 @@ __export(store_exports, {
|
|
|
3705
3905
|
vectorToBlob: () => vectorToBlob,
|
|
3706
3906
|
writeMemory: () => writeMemory
|
|
3707
3907
|
});
|
|
3708
|
-
import { createHash } from "crypto";
|
|
3709
3908
|
function isBusyError2(err) {
|
|
3710
3909
|
if (err instanceof Error) {
|
|
3711
3910
|
const msg = err.message.toLowerCase();
|
|
@@ -3819,17 +4018,24 @@ async function writeMemory(record) {
|
|
|
3819
4018
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3820
4019
|
);
|
|
3821
4020
|
}
|
|
3822
|
-
const
|
|
3823
|
-
if (
|
|
4021
|
+
const governed = governMemoryRecord(record);
|
|
4022
|
+
if (governed.shouldDrop) return;
|
|
4023
|
+
record = governed.record;
|
|
4024
|
+
const contentHash = governed.contentHash;
|
|
4025
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4026
|
+
if (_pendingRecords.some(
|
|
4027
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4028
|
+
)) {
|
|
3824
4029
|
return;
|
|
3825
4030
|
}
|
|
3826
4031
|
try {
|
|
3827
|
-
const
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
4032
|
+
const existing = await findScopedDuplicate({
|
|
4033
|
+
contentHash,
|
|
4034
|
+
agentId: record.agent_id,
|
|
4035
|
+
projectName: record.project_name,
|
|
4036
|
+
memoryType
|
|
3831
4037
|
});
|
|
3832
|
-
if (existing
|
|
4038
|
+
if (existing) return;
|
|
3833
4039
|
} catch {
|
|
3834
4040
|
}
|
|
3835
4041
|
const dbRow = {
|
|
@@ -3860,7 +4066,7 @@ async function writeMemory(record) {
|
|
|
3860
4066
|
tier: record.tier ?? classifyTier(record),
|
|
3861
4067
|
supersedes_id: record.supersedes_id ?? null,
|
|
3862
4068
|
draft: record.draft ? 1 : 0,
|
|
3863
|
-
memory_type:
|
|
4069
|
+
memory_type: memoryType,
|
|
3864
4070
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3865
4071
|
content_hash: contentHash,
|
|
3866
4072
|
intent: record.intent ?? null,
|
|
@@ -4019,6 +4225,7 @@ async function flushBatch() {
|
|
|
4019
4225
|
const globalClient = getClient();
|
|
4020
4226
|
const globalStmts = batch.map(buildStmt);
|
|
4021
4227
|
await globalClient.batch(globalStmts, "write");
|
|
4228
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4022
4229
|
_pendingRecords.splice(0, batch.length);
|
|
4023
4230
|
try {
|
|
4024
4231
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4139,7 +4346,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4139
4346
|
sql += ` AND timestamp >= ?`;
|
|
4140
4347
|
args.push(options.since);
|
|
4141
4348
|
}
|
|
4142
|
-
if (options?.
|
|
4349
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4350
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4351
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4352
|
+
args.push(...uniqueTypes);
|
|
4353
|
+
} else if (options?.memoryType) {
|
|
4143
4354
|
sql += ` AND memory_type = ?`;
|
|
4144
4355
|
args.push(options.memoryType);
|
|
4145
4356
|
}
|
|
@@ -4277,6 +4488,7 @@ var init_store = __esm({
|
|
|
4277
4488
|
init_keychain();
|
|
4278
4489
|
init_config();
|
|
4279
4490
|
init_state_bus();
|
|
4491
|
+
init_memory_write_governor();
|
|
4280
4492
|
INIT_MAX_RETRIES = 3;
|
|
4281
4493
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4282
4494
|
_pendingRecords = [];
|