@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
|
@@ -2213,6 +2213,14 @@ async function ensureSchema() {
|
|
|
2213
2213
|
);
|
|
2214
2214
|
} catch {
|
|
2215
2215
|
}
|
|
2216
|
+
try {
|
|
2217
|
+
await client.execute(
|
|
2218
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2219
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2220
|
+
WHERE content_hash IS NOT NULL`
|
|
2221
|
+
);
|
|
2222
|
+
} catch {
|
|
2223
|
+
}
|
|
2216
2224
|
await client.executeMultiple(`
|
|
2217
2225
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2218
2226
|
id TEXT PRIMARY KEY,
|
|
@@ -2882,7 +2890,8 @@ async function ensureShardSchema(client) {
|
|
|
2882
2890
|
}
|
|
2883
2891
|
for (const idx of [
|
|
2884
2892
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2885
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2893
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2894
|
+
"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"
|
|
2886
2895
|
]) {
|
|
2887
2896
|
try {
|
|
2888
2897
|
await client.execute(idx);
|
|
@@ -3076,7 +3085,7 @@ var init_platform_procedures = __esm({
|
|
|
3076
3085
|
title: "Chain of command \u2014 who talks to whom",
|
|
3077
3086
|
domain: "workflow",
|
|
3078
3087
|
priority: "p0",
|
|
3079
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3088
|
+
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."
|
|
3080
3089
|
},
|
|
3081
3090
|
{
|
|
3082
3091
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3301,7 +3310,6 @@ import { parseArgs } from "util";
|
|
|
3301
3310
|
// src/lib/store.ts
|
|
3302
3311
|
init_memory();
|
|
3303
3312
|
init_database();
|
|
3304
|
-
import { createHash } from "crypto";
|
|
3305
3313
|
|
|
3306
3314
|
// src/lib/keychain.ts
|
|
3307
3315
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -3534,6 +3542,191 @@ var StateBus = class {
|
|
|
3534
3542
|
};
|
|
3535
3543
|
var orgBus = new StateBus();
|
|
3536
3544
|
|
|
3545
|
+
// src/lib/memory-write-governor.ts
|
|
3546
|
+
import { createHash } from "crypto";
|
|
3547
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3548
|
+
"decision",
|
|
3549
|
+
"adr",
|
|
3550
|
+
"behavior",
|
|
3551
|
+
"procedure"
|
|
3552
|
+
]);
|
|
3553
|
+
var NOISE_DROP_PATTERNS = [
|
|
3554
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3555
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3556
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3557
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3558
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3559
|
+
];
|
|
3560
|
+
var SKIP_EMBED_PATTERNS = [
|
|
3561
|
+
/tmux capture-pane\b/i,
|
|
3562
|
+
/docker ps\b/i,
|
|
3563
|
+
/docker images\b/i,
|
|
3564
|
+
/git status\b/i,
|
|
3565
|
+
/grep .*node_modules/i,
|
|
3566
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3567
|
+
];
|
|
3568
|
+
function normalizeMemoryText(text) {
|
|
3569
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3570
|
+
}
|
|
3571
|
+
function classifyMemoryType(input) {
|
|
3572
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3573
|
+
const tool = input.tool_name.toLowerCase();
|
|
3574
|
+
const text = input.raw_text.toLowerCase();
|
|
3575
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3576
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3577
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3578
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3579
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3580
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3581
|
+
return "raw";
|
|
3582
|
+
}
|
|
3583
|
+
function shouldDropMemory(text) {
|
|
3584
|
+
const normalized = normalizeMemoryText(text);
|
|
3585
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3586
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3587
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3588
|
+
}
|
|
3589
|
+
return { drop: false };
|
|
3590
|
+
}
|
|
3591
|
+
function shouldSkipEmbedding(input) {
|
|
3592
|
+
const type = classifyMemoryType(input);
|
|
3593
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3594
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3595
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3596
|
+
return false;
|
|
3597
|
+
}
|
|
3598
|
+
function hashMemoryContent(text) {
|
|
3599
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3600
|
+
}
|
|
3601
|
+
function scopedDedupArgs(input) {
|
|
3602
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3603
|
+
}
|
|
3604
|
+
function governMemoryRecord(record) {
|
|
3605
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3606
|
+
const memoryType = classifyMemoryType({
|
|
3607
|
+
raw_text: normalized,
|
|
3608
|
+
agent_id: record.agent_id,
|
|
3609
|
+
project_name: record.project_name,
|
|
3610
|
+
tool_name: record.tool_name,
|
|
3611
|
+
memory_type: record.memory_type
|
|
3612
|
+
});
|
|
3613
|
+
const drop = shouldDropMemory(normalized);
|
|
3614
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3615
|
+
raw_text: normalized,
|
|
3616
|
+
agent_id: record.agent_id,
|
|
3617
|
+
project_name: record.project_name,
|
|
3618
|
+
tool_name: record.tool_name,
|
|
3619
|
+
memory_type: memoryType
|
|
3620
|
+
});
|
|
3621
|
+
return {
|
|
3622
|
+
record: {
|
|
3623
|
+
...record,
|
|
3624
|
+
raw_text: normalized,
|
|
3625
|
+
memory_type: memoryType,
|
|
3626
|
+
vector: skipEmbedding ? null : record.vector
|
|
3627
|
+
},
|
|
3628
|
+
contentHash: hashMemoryContent(normalized),
|
|
3629
|
+
shouldDrop: drop.drop,
|
|
3630
|
+
dropReason: drop.reason,
|
|
3631
|
+
skipEmbedding,
|
|
3632
|
+
hygiene: {
|
|
3633
|
+
dedup: true,
|
|
3634
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3635
|
+
}
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
async function findScopedDuplicate(input) {
|
|
3639
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3640
|
+
const client = getClient2();
|
|
3641
|
+
const args = scopedDedupArgs(input);
|
|
3642
|
+
let sql = `SELECT id FROM memories
|
|
3643
|
+
WHERE content_hash = ?
|
|
3644
|
+
AND agent_id = ?
|
|
3645
|
+
AND project_name = ?
|
|
3646
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3647
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3648
|
+
if (input.excludeId) {
|
|
3649
|
+
sql += " AND id != ?";
|
|
3650
|
+
args.push(input.excludeId);
|
|
3651
|
+
}
|
|
3652
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3653
|
+
const result = await client.execute({ sql, args });
|
|
3654
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3655
|
+
}
|
|
3656
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3657
|
+
try {
|
|
3658
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3659
|
+
const client = getClient2();
|
|
3660
|
+
const current = await client.execute({
|
|
3661
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3662
|
+
importance, timestamp
|
|
3663
|
+
FROM memories
|
|
3664
|
+
WHERE id = ?
|
|
3665
|
+
LIMIT 1`,
|
|
3666
|
+
args: [memoryId]
|
|
3667
|
+
});
|
|
3668
|
+
const row = current.rows[0];
|
|
3669
|
+
if (!row) return;
|
|
3670
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3671
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3672
|
+
const agentId = String(row.agent_id);
|
|
3673
|
+
const projectName = String(row.project_name);
|
|
3674
|
+
if (contentHash) {
|
|
3675
|
+
await client.execute({
|
|
3676
|
+
sql: `UPDATE memories
|
|
3677
|
+
SET status = 'deleted',
|
|
3678
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3679
|
+
WHERE id != ?
|
|
3680
|
+
AND content_hash = ?
|
|
3681
|
+
AND agent_id = ?
|
|
3682
|
+
AND project_name = ?
|
|
3683
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3684
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3685
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3686
|
+
});
|
|
3687
|
+
}
|
|
3688
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3689
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3690
|
+
const old = await client.execute({
|
|
3691
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3692
|
+
args: [supersedesId]
|
|
3693
|
+
});
|
|
3694
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3695
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3696
|
+
await client.batch([
|
|
3697
|
+
{
|
|
3698
|
+
sql: `UPDATE memories
|
|
3699
|
+
SET status = 'archived',
|
|
3700
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3701
|
+
WHERE id = ?`,
|
|
3702
|
+
args: [supersedesId]
|
|
3703
|
+
},
|
|
3704
|
+
{
|
|
3705
|
+
sql: `UPDATE memories
|
|
3706
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3707
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3708
|
+
WHERE id = ?`,
|
|
3709
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3710
|
+
}
|
|
3711
|
+
], "write");
|
|
3712
|
+
}
|
|
3713
|
+
} catch (err) {
|
|
3714
|
+
process.stderr.write(
|
|
3715
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3716
|
+
`
|
|
3717
|
+
);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3721
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3722
|
+
if (memoryIds.length === 0) return;
|
|
3723
|
+
const run = () => {
|
|
3724
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3725
|
+
};
|
|
3726
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3727
|
+
else setTimeout(run, 0);
|
|
3728
|
+
}
|
|
3729
|
+
|
|
3537
3730
|
// src/lib/store.ts
|
|
3538
3731
|
var INIT_MAX_RETRIES = 3;
|
|
3539
3732
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -3656,17 +3849,24 @@ async function writeMemory(record) {
|
|
|
3656
3849
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3657
3850
|
);
|
|
3658
3851
|
}
|
|
3659
|
-
const
|
|
3660
|
-
if (
|
|
3852
|
+
const governed = governMemoryRecord(record);
|
|
3853
|
+
if (governed.shouldDrop) return;
|
|
3854
|
+
record = governed.record;
|
|
3855
|
+
const contentHash = governed.contentHash;
|
|
3856
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3857
|
+
if (_pendingRecords.some(
|
|
3858
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3859
|
+
)) {
|
|
3661
3860
|
return;
|
|
3662
3861
|
}
|
|
3663
3862
|
try {
|
|
3664
|
-
const
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3863
|
+
const existing = await findScopedDuplicate({
|
|
3864
|
+
contentHash,
|
|
3865
|
+
agentId: record.agent_id,
|
|
3866
|
+
projectName: record.project_name,
|
|
3867
|
+
memoryType
|
|
3668
3868
|
});
|
|
3669
|
-
if (existing
|
|
3869
|
+
if (existing) return;
|
|
3670
3870
|
} catch {
|
|
3671
3871
|
}
|
|
3672
3872
|
const dbRow = {
|
|
@@ -3697,7 +3897,7 @@ async function writeMemory(record) {
|
|
|
3697
3897
|
tier: record.tier ?? classifyTier(record),
|
|
3698
3898
|
supersedes_id: record.supersedes_id ?? null,
|
|
3699
3899
|
draft: record.draft ? 1 : 0,
|
|
3700
|
-
memory_type:
|
|
3900
|
+
memory_type: memoryType,
|
|
3701
3901
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3702
3902
|
content_hash: contentHash,
|
|
3703
3903
|
intent: record.intent ?? null,
|
|
@@ -3856,6 +4056,7 @@ async function flushBatch() {
|
|
|
3856
4056
|
const globalClient = getClient();
|
|
3857
4057
|
const globalStmts = batch.map(buildStmt);
|
|
3858
4058
|
await globalClient.batch(globalStmts, "write");
|
|
4059
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3859
4060
|
_pendingRecords.splice(0, batch.length);
|
|
3860
4061
|
try {
|
|
3861
4062
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -2213,6 +2213,14 @@ async function ensureSchema() {
|
|
|
2213
2213
|
);
|
|
2214
2214
|
} catch {
|
|
2215
2215
|
}
|
|
2216
|
+
try {
|
|
2217
|
+
await client.execute(
|
|
2218
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2219
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2220
|
+
WHERE content_hash IS NOT NULL`
|
|
2221
|
+
);
|
|
2222
|
+
} catch {
|
|
2223
|
+
}
|
|
2216
2224
|
await client.executeMultiple(`
|
|
2217
2225
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2218
2226
|
id TEXT PRIMARY KEY,
|
|
@@ -2882,7 +2890,8 @@ async function ensureShardSchema(client) {
|
|
|
2882
2890
|
}
|
|
2883
2891
|
for (const idx of [
|
|
2884
2892
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2885
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2893
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2894
|
+
"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"
|
|
2886
2895
|
]) {
|
|
2887
2896
|
try {
|
|
2888
2897
|
await client.execute(idx);
|
|
@@ -3076,7 +3085,7 @@ var init_platform_procedures = __esm({
|
|
|
3076
3085
|
title: "Chain of command \u2014 who talks to whom",
|
|
3077
3086
|
domain: "workflow",
|
|
3078
3087
|
priority: "p0",
|
|
3079
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3088
|
+
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."
|
|
3080
3089
|
},
|
|
3081
3090
|
{
|
|
3082
3091
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3300,7 +3309,6 @@ import { homedir } from "os";
|
|
|
3300
3309
|
// src/lib/store.ts
|
|
3301
3310
|
init_memory();
|
|
3302
3311
|
init_database();
|
|
3303
|
-
import { createHash } from "crypto";
|
|
3304
3312
|
|
|
3305
3313
|
// src/lib/keychain.ts
|
|
3306
3314
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -3533,6 +3541,191 @@ var StateBus = class {
|
|
|
3533
3541
|
};
|
|
3534
3542
|
var orgBus = new StateBus();
|
|
3535
3543
|
|
|
3544
|
+
// src/lib/memory-write-governor.ts
|
|
3545
|
+
import { createHash } from "crypto";
|
|
3546
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3547
|
+
"decision",
|
|
3548
|
+
"adr",
|
|
3549
|
+
"behavior",
|
|
3550
|
+
"procedure"
|
|
3551
|
+
]);
|
|
3552
|
+
var NOISE_DROP_PATTERNS = [
|
|
3553
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3554
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3555
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3556
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3557
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3558
|
+
];
|
|
3559
|
+
var SKIP_EMBED_PATTERNS = [
|
|
3560
|
+
/tmux capture-pane\b/i,
|
|
3561
|
+
/docker ps\b/i,
|
|
3562
|
+
/docker images\b/i,
|
|
3563
|
+
/git status\b/i,
|
|
3564
|
+
/grep .*node_modules/i,
|
|
3565
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3566
|
+
];
|
|
3567
|
+
function normalizeMemoryText(text) {
|
|
3568
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3569
|
+
}
|
|
3570
|
+
function classifyMemoryType(input) {
|
|
3571
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3572
|
+
const tool = input.tool_name.toLowerCase();
|
|
3573
|
+
const text = input.raw_text.toLowerCase();
|
|
3574
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3575
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3576
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3577
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3578
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3579
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3580
|
+
return "raw";
|
|
3581
|
+
}
|
|
3582
|
+
function shouldDropMemory(text) {
|
|
3583
|
+
const normalized = normalizeMemoryText(text);
|
|
3584
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3585
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3586
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3587
|
+
}
|
|
3588
|
+
return { drop: false };
|
|
3589
|
+
}
|
|
3590
|
+
function shouldSkipEmbedding(input) {
|
|
3591
|
+
const type = classifyMemoryType(input);
|
|
3592
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3593
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3594
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3595
|
+
return false;
|
|
3596
|
+
}
|
|
3597
|
+
function hashMemoryContent(text) {
|
|
3598
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3599
|
+
}
|
|
3600
|
+
function scopedDedupArgs(input) {
|
|
3601
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3602
|
+
}
|
|
3603
|
+
function governMemoryRecord(record) {
|
|
3604
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3605
|
+
const memoryType = classifyMemoryType({
|
|
3606
|
+
raw_text: normalized,
|
|
3607
|
+
agent_id: record.agent_id,
|
|
3608
|
+
project_name: record.project_name,
|
|
3609
|
+
tool_name: record.tool_name,
|
|
3610
|
+
memory_type: record.memory_type
|
|
3611
|
+
});
|
|
3612
|
+
const drop = shouldDropMemory(normalized);
|
|
3613
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3614
|
+
raw_text: normalized,
|
|
3615
|
+
agent_id: record.agent_id,
|
|
3616
|
+
project_name: record.project_name,
|
|
3617
|
+
tool_name: record.tool_name,
|
|
3618
|
+
memory_type: memoryType
|
|
3619
|
+
});
|
|
3620
|
+
return {
|
|
3621
|
+
record: {
|
|
3622
|
+
...record,
|
|
3623
|
+
raw_text: normalized,
|
|
3624
|
+
memory_type: memoryType,
|
|
3625
|
+
vector: skipEmbedding ? null : record.vector
|
|
3626
|
+
},
|
|
3627
|
+
contentHash: hashMemoryContent(normalized),
|
|
3628
|
+
shouldDrop: drop.drop,
|
|
3629
|
+
dropReason: drop.reason,
|
|
3630
|
+
skipEmbedding,
|
|
3631
|
+
hygiene: {
|
|
3632
|
+
dedup: true,
|
|
3633
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3634
|
+
}
|
|
3635
|
+
};
|
|
3636
|
+
}
|
|
3637
|
+
async function findScopedDuplicate(input) {
|
|
3638
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3639
|
+
const client = getClient2();
|
|
3640
|
+
const args = scopedDedupArgs(input);
|
|
3641
|
+
let sql = `SELECT id FROM memories
|
|
3642
|
+
WHERE content_hash = ?
|
|
3643
|
+
AND agent_id = ?
|
|
3644
|
+
AND project_name = ?
|
|
3645
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3646
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3647
|
+
if (input.excludeId) {
|
|
3648
|
+
sql += " AND id != ?";
|
|
3649
|
+
args.push(input.excludeId);
|
|
3650
|
+
}
|
|
3651
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3652
|
+
const result = await client.execute({ sql, args });
|
|
3653
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3654
|
+
}
|
|
3655
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3656
|
+
try {
|
|
3657
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3658
|
+
const client = getClient2();
|
|
3659
|
+
const current = await client.execute({
|
|
3660
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3661
|
+
importance, timestamp
|
|
3662
|
+
FROM memories
|
|
3663
|
+
WHERE id = ?
|
|
3664
|
+
LIMIT 1`,
|
|
3665
|
+
args: [memoryId]
|
|
3666
|
+
});
|
|
3667
|
+
const row = current.rows[0];
|
|
3668
|
+
if (!row) return;
|
|
3669
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3670
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3671
|
+
const agentId = String(row.agent_id);
|
|
3672
|
+
const projectName = String(row.project_name);
|
|
3673
|
+
if (contentHash) {
|
|
3674
|
+
await client.execute({
|
|
3675
|
+
sql: `UPDATE memories
|
|
3676
|
+
SET status = 'deleted',
|
|
3677
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3678
|
+
WHERE id != ?
|
|
3679
|
+
AND content_hash = ?
|
|
3680
|
+
AND agent_id = ?
|
|
3681
|
+
AND project_name = ?
|
|
3682
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3683
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3684
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3685
|
+
});
|
|
3686
|
+
}
|
|
3687
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3688
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3689
|
+
const old = await client.execute({
|
|
3690
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3691
|
+
args: [supersedesId]
|
|
3692
|
+
});
|
|
3693
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3694
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3695
|
+
await client.batch([
|
|
3696
|
+
{
|
|
3697
|
+
sql: `UPDATE memories
|
|
3698
|
+
SET status = 'archived',
|
|
3699
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3700
|
+
WHERE id = ?`,
|
|
3701
|
+
args: [supersedesId]
|
|
3702
|
+
},
|
|
3703
|
+
{
|
|
3704
|
+
sql: `UPDATE memories
|
|
3705
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3706
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3707
|
+
WHERE id = ?`,
|
|
3708
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3709
|
+
}
|
|
3710
|
+
], "write");
|
|
3711
|
+
}
|
|
3712
|
+
} catch (err) {
|
|
3713
|
+
process.stderr.write(
|
|
3714
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3715
|
+
`
|
|
3716
|
+
);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3720
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3721
|
+
if (memoryIds.length === 0) return;
|
|
3722
|
+
const run = () => {
|
|
3723
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3724
|
+
};
|
|
3725
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3726
|
+
else setTimeout(run, 0);
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3536
3729
|
// src/lib/store.ts
|
|
3537
3730
|
var INIT_MAX_RETRIES = 3;
|
|
3538
3731
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -3655,17 +3848,24 @@ async function writeMemory(record) {
|
|
|
3655
3848
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3656
3849
|
);
|
|
3657
3850
|
}
|
|
3658
|
-
const
|
|
3659
|
-
if (
|
|
3851
|
+
const governed = governMemoryRecord(record);
|
|
3852
|
+
if (governed.shouldDrop) return;
|
|
3853
|
+
record = governed.record;
|
|
3854
|
+
const contentHash = governed.contentHash;
|
|
3855
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3856
|
+
if (_pendingRecords.some(
|
|
3857
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3858
|
+
)) {
|
|
3660
3859
|
return;
|
|
3661
3860
|
}
|
|
3662
3861
|
try {
|
|
3663
|
-
const
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3862
|
+
const existing = await findScopedDuplicate({
|
|
3863
|
+
contentHash,
|
|
3864
|
+
agentId: record.agent_id,
|
|
3865
|
+
projectName: record.project_name,
|
|
3866
|
+
memoryType
|
|
3667
3867
|
});
|
|
3668
|
-
if (existing
|
|
3868
|
+
if (existing) return;
|
|
3669
3869
|
} catch {
|
|
3670
3870
|
}
|
|
3671
3871
|
const dbRow = {
|
|
@@ -3696,7 +3896,7 @@ async function writeMemory(record) {
|
|
|
3696
3896
|
tier: record.tier ?? classifyTier(record),
|
|
3697
3897
|
supersedes_id: record.supersedes_id ?? null,
|
|
3698
3898
|
draft: record.draft ? 1 : 0,
|
|
3699
|
-
memory_type:
|
|
3899
|
+
memory_type: memoryType,
|
|
3700
3900
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3701
3901
|
content_hash: contentHash,
|
|
3702
3902
|
intent: record.intent ?? null,
|
|
@@ -3855,6 +4055,7 @@ async function flushBatch() {
|
|
|
3855
4055
|
const globalClient = getClient();
|
|
3856
4056
|
const globalStmts = batch.map(buildStmt);
|
|
3857
4057
|
await globalClient.batch(globalStmts, "write");
|
|
4058
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3858
4059
|
_pendingRecords.splice(0, batch.length);
|
|
3859
4060
|
try {
|
|
3860
4061
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -2209,6 +2209,14 @@ async function ensureSchema() {
|
|
|
2209
2209
|
);
|
|
2210
2210
|
} catch {
|
|
2211
2211
|
}
|
|
2212
|
+
try {
|
|
2213
|
+
await client.execute(
|
|
2214
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2215
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2216
|
+
WHERE content_hash IS NOT NULL`
|
|
2217
|
+
);
|
|
2218
|
+
} catch {
|
|
2219
|
+
}
|
|
2212
2220
|
await client.executeMultiple(`
|
|
2213
2221
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2214
2222
|
id TEXT PRIMARY KEY,
|
|
@@ -2878,7 +2886,8 @@ async function ensureShardSchema(client) {
|
|
|
2878
2886
|
}
|
|
2879
2887
|
for (const idx of [
|
|
2880
2888
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2881
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2889
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2890
|
+
"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"
|
|
2882
2891
|
]) {
|
|
2883
2892
|
try {
|
|
2884
2893
|
await client.execute(idx);
|
|
@@ -3072,7 +3081,7 @@ var init_platform_procedures = __esm({
|
|
|
3072
3081
|
title: "Chain of command \u2014 who talks to whom",
|
|
3073
3082
|
domain: "workflow",
|
|
3074
3083
|
priority: "p0",
|
|
3075
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3084
|
+
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."
|
|
3076
3085
|
},
|
|
3077
3086
|
{
|
|
3078
3087
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3288,7 +3297,6 @@ ${p.content}`).join("\n\n");
|
|
|
3288
3297
|
// src/lib/store.ts
|
|
3289
3298
|
init_memory();
|
|
3290
3299
|
init_database();
|
|
3291
|
-
import { createHash } from "crypto";
|
|
3292
3300
|
|
|
3293
3301
|
// src/lib/keychain.ts
|
|
3294
3302
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -3521,6 +3529,9 @@ var StateBus = class {
|
|
|
3521
3529
|
};
|
|
3522
3530
|
var orgBus = new StateBus();
|
|
3523
3531
|
|
|
3532
|
+
// src/lib/memory-write-governor.ts
|
|
3533
|
+
import { createHash } from "crypto";
|
|
3534
|
+
|
|
3524
3535
|
// src/lib/store.ts
|
|
3525
3536
|
var INIT_MAX_RETRIES = 3;
|
|
3526
3537
|
var INIT_RETRY_DELAY_MS = 1e3;
|