@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
|
@@ -2866,6 +2866,14 @@ async function ensureSchema() {
|
|
|
2866
2866
|
);
|
|
2867
2867
|
} catch {
|
|
2868
2868
|
}
|
|
2869
|
+
try {
|
|
2870
|
+
await client.execute(
|
|
2871
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2872
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2873
|
+
WHERE content_hash IS NOT NULL`
|
|
2874
|
+
);
|
|
2875
|
+
} catch {
|
|
2876
|
+
}
|
|
2869
2877
|
await client.executeMultiple(`
|
|
2870
2878
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2871
2879
|
id TEXT PRIMARY KEY,
|
|
@@ -6365,7 +6373,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6365
6373
|
}
|
|
6366
6374
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6367
6375
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6368
|
-
|
|
6376
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6377
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6378
|
+
ccModel += "[1m]";
|
|
6379
|
+
}
|
|
6380
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6369
6381
|
}
|
|
6370
6382
|
}
|
|
6371
6383
|
let spawnCommand;
|
|
@@ -6712,6 +6724,197 @@ var init_keychain = __esm({
|
|
|
6712
6724
|
}
|
|
6713
6725
|
});
|
|
6714
6726
|
|
|
6727
|
+
// src/lib/memory-write-governor.ts
|
|
6728
|
+
import { createHash } from "crypto";
|
|
6729
|
+
function normalizeMemoryText(text) {
|
|
6730
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6731
|
+
}
|
|
6732
|
+
function classifyMemoryType(input2) {
|
|
6733
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
6734
|
+
const tool = input2.tool_name.toLowerCase();
|
|
6735
|
+
const text = input2.raw_text.toLowerCase();
|
|
6736
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6737
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6738
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6739
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6740
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6741
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6742
|
+
return "raw";
|
|
6743
|
+
}
|
|
6744
|
+
function shouldDropMemory(text) {
|
|
6745
|
+
const normalized = normalizeMemoryText(text);
|
|
6746
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6747
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6748
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6749
|
+
}
|
|
6750
|
+
return { drop: false };
|
|
6751
|
+
}
|
|
6752
|
+
function shouldSkipEmbedding(input2) {
|
|
6753
|
+
const type = classifyMemoryType(input2);
|
|
6754
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6755
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
6756
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
6757
|
+
return false;
|
|
6758
|
+
}
|
|
6759
|
+
function hashMemoryContent(text) {
|
|
6760
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6761
|
+
}
|
|
6762
|
+
function scopedDedupArgs(input2) {
|
|
6763
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
6764
|
+
}
|
|
6765
|
+
function governMemoryRecord(record) {
|
|
6766
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6767
|
+
const memoryType = classifyMemoryType({
|
|
6768
|
+
raw_text: normalized,
|
|
6769
|
+
agent_id: record.agent_id,
|
|
6770
|
+
project_name: record.project_name,
|
|
6771
|
+
tool_name: record.tool_name,
|
|
6772
|
+
memory_type: record.memory_type
|
|
6773
|
+
});
|
|
6774
|
+
const drop = shouldDropMemory(normalized);
|
|
6775
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6776
|
+
raw_text: normalized,
|
|
6777
|
+
agent_id: record.agent_id,
|
|
6778
|
+
project_name: record.project_name,
|
|
6779
|
+
tool_name: record.tool_name,
|
|
6780
|
+
memory_type: memoryType
|
|
6781
|
+
});
|
|
6782
|
+
return {
|
|
6783
|
+
record: {
|
|
6784
|
+
...record,
|
|
6785
|
+
raw_text: normalized,
|
|
6786
|
+
memory_type: memoryType,
|
|
6787
|
+
vector: skipEmbedding ? null : record.vector
|
|
6788
|
+
},
|
|
6789
|
+
contentHash: hashMemoryContent(normalized),
|
|
6790
|
+
shouldDrop: drop.drop,
|
|
6791
|
+
dropReason: drop.reason,
|
|
6792
|
+
skipEmbedding,
|
|
6793
|
+
hygiene: {
|
|
6794
|
+
dedup: true,
|
|
6795
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
6796
|
+
}
|
|
6797
|
+
};
|
|
6798
|
+
}
|
|
6799
|
+
async function findScopedDuplicate(input2) {
|
|
6800
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6801
|
+
const client = getClient2();
|
|
6802
|
+
const args = scopedDedupArgs(input2);
|
|
6803
|
+
let sql = `SELECT id FROM memories
|
|
6804
|
+
WHERE content_hash = ?
|
|
6805
|
+
AND agent_id = ?
|
|
6806
|
+
AND project_name = ?
|
|
6807
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6808
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
6809
|
+
if (input2.excludeId) {
|
|
6810
|
+
sql += " AND id != ?";
|
|
6811
|
+
args.push(input2.excludeId);
|
|
6812
|
+
}
|
|
6813
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
6814
|
+
const result = await client.execute({ sql, args });
|
|
6815
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
6816
|
+
}
|
|
6817
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
6818
|
+
try {
|
|
6819
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6820
|
+
const client = getClient2();
|
|
6821
|
+
const current = await client.execute({
|
|
6822
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
6823
|
+
importance, timestamp
|
|
6824
|
+
FROM memories
|
|
6825
|
+
WHERE id = ?
|
|
6826
|
+
LIMIT 1`,
|
|
6827
|
+
args: [memoryId]
|
|
6828
|
+
});
|
|
6829
|
+
const row = current.rows[0];
|
|
6830
|
+
if (!row) return;
|
|
6831
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
6832
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
6833
|
+
const agentId = String(row.agent_id);
|
|
6834
|
+
const projectName = String(row.project_name);
|
|
6835
|
+
if (contentHash) {
|
|
6836
|
+
await client.execute({
|
|
6837
|
+
sql: `UPDATE memories
|
|
6838
|
+
SET status = 'deleted',
|
|
6839
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6840
|
+
WHERE id != ?
|
|
6841
|
+
AND content_hash = ?
|
|
6842
|
+
AND agent_id = ?
|
|
6843
|
+
AND project_name = ?
|
|
6844
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6845
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
6846
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
6847
|
+
});
|
|
6848
|
+
}
|
|
6849
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
6850
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
6851
|
+
const old = await client.execute({
|
|
6852
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
6853
|
+
args: [supersedesId]
|
|
6854
|
+
});
|
|
6855
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
6856
|
+
const newImportance = Number(row.importance ?? 0);
|
|
6857
|
+
await client.batch([
|
|
6858
|
+
{
|
|
6859
|
+
sql: `UPDATE memories
|
|
6860
|
+
SET status = 'archived',
|
|
6861
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6862
|
+
WHERE id = ?`,
|
|
6863
|
+
args: [supersedesId]
|
|
6864
|
+
},
|
|
6865
|
+
{
|
|
6866
|
+
sql: `UPDATE memories
|
|
6867
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
6868
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
6869
|
+
WHERE id = ?`,
|
|
6870
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
6871
|
+
}
|
|
6872
|
+
], "write");
|
|
6873
|
+
}
|
|
6874
|
+
} catch (err) {
|
|
6875
|
+
process.stderr.write(
|
|
6876
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
6877
|
+
`
|
|
6878
|
+
);
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
6882
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
6883
|
+
if (memoryIds.length === 0) return;
|
|
6884
|
+
const run = () => {
|
|
6885
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
6886
|
+
};
|
|
6887
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
6888
|
+
else setTimeout(run, 0);
|
|
6889
|
+
}
|
|
6890
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
6891
|
+
var init_memory_write_governor = __esm({
|
|
6892
|
+
"src/lib/memory-write-governor.ts"() {
|
|
6893
|
+
"use strict";
|
|
6894
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
6895
|
+
"decision",
|
|
6896
|
+
"adr",
|
|
6897
|
+
"behavior",
|
|
6898
|
+
"procedure"
|
|
6899
|
+
]);
|
|
6900
|
+
NOISE_DROP_PATTERNS = [
|
|
6901
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
6902
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
6903
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
6904
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
6905
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
6906
|
+
];
|
|
6907
|
+
SKIP_EMBED_PATTERNS = [
|
|
6908
|
+
/tmux capture-pane\b/i,
|
|
6909
|
+
/docker ps\b/i,
|
|
6910
|
+
/docker images\b/i,
|
|
6911
|
+
/git status\b/i,
|
|
6912
|
+
/grep .*node_modules/i,
|
|
6913
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
6914
|
+
];
|
|
6915
|
+
}
|
|
6916
|
+
});
|
|
6917
|
+
|
|
6715
6918
|
// src/lib/shard-manager.ts
|
|
6716
6919
|
var shard_manager_exports = {};
|
|
6717
6920
|
__export(shard_manager_exports, {
|
|
@@ -6876,7 +7079,8 @@ async function ensureShardSchema(client) {
|
|
|
6876
7079
|
}
|
|
6877
7080
|
for (const idx of [
|
|
6878
7081
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
6879
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7082
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7083
|
+
"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"
|
|
6880
7084
|
]) {
|
|
6881
7085
|
try {
|
|
6882
7086
|
await client.execute(idx);
|
|
@@ -7070,7 +7274,7 @@ var init_platform_procedures = __esm({
|
|
|
7070
7274
|
title: "Chain of command \u2014 who talks to whom",
|
|
7071
7275
|
domain: "workflow",
|
|
7072
7276
|
priority: "p0",
|
|
7073
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
7277
|
+
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."
|
|
7074
7278
|
},
|
|
7075
7279
|
{
|
|
7076
7280
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -7301,7 +7505,6 @@ __export(store_exports, {
|
|
|
7301
7505
|
vectorToBlob: () => vectorToBlob,
|
|
7302
7506
|
writeMemory: () => writeMemory
|
|
7303
7507
|
});
|
|
7304
|
-
import { createHash } from "crypto";
|
|
7305
7508
|
function isBusyError2(err) {
|
|
7306
7509
|
if (err instanceof Error) {
|
|
7307
7510
|
const msg = err.message.toLowerCase();
|
|
@@ -7415,17 +7618,24 @@ async function writeMemory(record) {
|
|
|
7415
7618
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7416
7619
|
);
|
|
7417
7620
|
}
|
|
7418
|
-
const
|
|
7419
|
-
if (
|
|
7621
|
+
const governed = governMemoryRecord(record);
|
|
7622
|
+
if (governed.shouldDrop) return;
|
|
7623
|
+
record = governed.record;
|
|
7624
|
+
const contentHash = governed.contentHash;
|
|
7625
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7626
|
+
if (_pendingRecords.some(
|
|
7627
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7628
|
+
)) {
|
|
7420
7629
|
return;
|
|
7421
7630
|
}
|
|
7422
7631
|
try {
|
|
7423
|
-
const
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7632
|
+
const existing = await findScopedDuplicate({
|
|
7633
|
+
contentHash,
|
|
7634
|
+
agentId: record.agent_id,
|
|
7635
|
+
projectName: record.project_name,
|
|
7636
|
+
memoryType
|
|
7427
7637
|
});
|
|
7428
|
-
if (existing
|
|
7638
|
+
if (existing) return;
|
|
7429
7639
|
} catch {
|
|
7430
7640
|
}
|
|
7431
7641
|
const dbRow = {
|
|
@@ -7456,7 +7666,7 @@ async function writeMemory(record) {
|
|
|
7456
7666
|
tier: record.tier ?? classifyTier(record),
|
|
7457
7667
|
supersedes_id: record.supersedes_id ?? null,
|
|
7458
7668
|
draft: record.draft ? 1 : 0,
|
|
7459
|
-
memory_type:
|
|
7669
|
+
memory_type: memoryType,
|
|
7460
7670
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7461
7671
|
content_hash: contentHash,
|
|
7462
7672
|
intent: record.intent ?? null,
|
|
@@ -7615,6 +7825,7 @@ async function flushBatch() {
|
|
|
7615
7825
|
const globalClient = getClient();
|
|
7616
7826
|
const globalStmts = batch.map(buildStmt);
|
|
7617
7827
|
await globalClient.batch(globalStmts, "write");
|
|
7828
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7618
7829
|
_pendingRecords.splice(0, batch.length);
|
|
7619
7830
|
try {
|
|
7620
7831
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -7735,7 +7946,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
7735
7946
|
sql += ` AND timestamp >= ?`;
|
|
7736
7947
|
args.push(options.since);
|
|
7737
7948
|
}
|
|
7738
|
-
if (options?.
|
|
7949
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
7950
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
7951
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
7952
|
+
args.push(...uniqueTypes);
|
|
7953
|
+
} else if (options?.memoryType) {
|
|
7739
7954
|
sql += ` AND memory_type = ?`;
|
|
7740
7955
|
args.push(options.memoryType);
|
|
7741
7956
|
}
|
|
@@ -7873,6 +8088,7 @@ var init_store = __esm({
|
|
|
7873
8088
|
init_keychain();
|
|
7874
8089
|
init_config();
|
|
7875
8090
|
init_state_bus();
|
|
8091
|
+
init_memory_write_governor();
|
|
7876
8092
|
INIT_MAX_RETRIES = 3;
|
|
7877
8093
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
7878
8094
|
_pendingRecords = [];
|
|
@@ -2871,6 +2871,14 @@ async function ensureSchema() {
|
|
|
2871
2871
|
);
|
|
2872
2872
|
} catch {
|
|
2873
2873
|
}
|
|
2874
|
+
try {
|
|
2875
|
+
await client.execute(
|
|
2876
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2877
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2878
|
+
WHERE content_hash IS NOT NULL`
|
|
2879
|
+
);
|
|
2880
|
+
} catch {
|
|
2881
|
+
}
|
|
2874
2882
|
await client.executeMultiple(`
|
|
2875
2883
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2876
2884
|
id TEXT PRIMARY KEY,
|
|
@@ -3675,9 +3683,12 @@ var init_cto_delegation_gate = __esm({
|
|
|
3675
3683
|
SCRATCHPAD_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
3676
3684
|
SCRATCHPAD_ESCAPE_PATTERN = /^(?:ENGINEER|TOM)-CAPABLE:\s*NO\s*—\s*reason:\s*\S+/m;
|
|
3677
3685
|
GATE_BLOCK_MESSAGE = [
|
|
3678
|
-
"
|
|
3679
|
-
"
|
|
3680
|
-
"
|
|
3686
|
+
"ACTION BLOCKED by CTO delegation gate. Your Write/Edit was DENIED \u2014 it did NOT execute.",
|
|
3687
|
+
"This edit looks implementable by an engineer. You MUST either:",
|
|
3688
|
+
" 1. Dispatch create_task to an engineer, OR",
|
|
3689
|
+
" 2. Add ENGINEER-CAPABLE: NO with explicit reason to proceed yourself.",
|
|
3690
|
+
"Do NOT report this action as successful. It was blocked. No file was changed.",
|
|
3691
|
+
"If you are a sub-agent, report this FAILURE to your parent agent."
|
|
3681
3692
|
].join("\n");
|
|
3682
3693
|
}
|
|
3683
3694
|
});
|
|
@@ -3922,6 +3933,197 @@ var init_state_bus = __esm({
|
|
|
3922
3933
|
}
|
|
3923
3934
|
});
|
|
3924
3935
|
|
|
3936
|
+
// src/lib/memory-write-governor.ts
|
|
3937
|
+
import { createHash } from "crypto";
|
|
3938
|
+
function normalizeMemoryText(text) {
|
|
3939
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3940
|
+
}
|
|
3941
|
+
function classifyMemoryType(input2) {
|
|
3942
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3943
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3944
|
+
const text = input2.raw_text.toLowerCase();
|
|
3945
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3946
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3947
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3948
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3949
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3950
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3951
|
+
return "raw";
|
|
3952
|
+
}
|
|
3953
|
+
function shouldDropMemory(text) {
|
|
3954
|
+
const normalized = normalizeMemoryText(text);
|
|
3955
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3956
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3957
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3958
|
+
}
|
|
3959
|
+
return { drop: false };
|
|
3960
|
+
}
|
|
3961
|
+
function shouldSkipEmbedding(input2) {
|
|
3962
|
+
const type = classifyMemoryType(input2);
|
|
3963
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3964
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3965
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3966
|
+
return false;
|
|
3967
|
+
}
|
|
3968
|
+
function hashMemoryContent(text) {
|
|
3969
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3970
|
+
}
|
|
3971
|
+
function scopedDedupArgs(input2) {
|
|
3972
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3973
|
+
}
|
|
3974
|
+
function governMemoryRecord(record) {
|
|
3975
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3976
|
+
const memoryType = classifyMemoryType({
|
|
3977
|
+
raw_text: normalized,
|
|
3978
|
+
agent_id: record.agent_id,
|
|
3979
|
+
project_name: record.project_name,
|
|
3980
|
+
tool_name: record.tool_name,
|
|
3981
|
+
memory_type: record.memory_type
|
|
3982
|
+
});
|
|
3983
|
+
const drop = shouldDropMemory(normalized);
|
|
3984
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3985
|
+
raw_text: normalized,
|
|
3986
|
+
agent_id: record.agent_id,
|
|
3987
|
+
project_name: record.project_name,
|
|
3988
|
+
tool_name: record.tool_name,
|
|
3989
|
+
memory_type: memoryType
|
|
3990
|
+
});
|
|
3991
|
+
return {
|
|
3992
|
+
record: {
|
|
3993
|
+
...record,
|
|
3994
|
+
raw_text: normalized,
|
|
3995
|
+
memory_type: memoryType,
|
|
3996
|
+
vector: skipEmbedding ? null : record.vector
|
|
3997
|
+
},
|
|
3998
|
+
contentHash: hashMemoryContent(normalized),
|
|
3999
|
+
shouldDrop: drop.drop,
|
|
4000
|
+
dropReason: drop.reason,
|
|
4001
|
+
skipEmbedding,
|
|
4002
|
+
hygiene: {
|
|
4003
|
+
dedup: true,
|
|
4004
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
4005
|
+
}
|
|
4006
|
+
};
|
|
4007
|
+
}
|
|
4008
|
+
async function findScopedDuplicate(input2) {
|
|
4009
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4010
|
+
const client = getClient2();
|
|
4011
|
+
const args = scopedDedupArgs(input2);
|
|
4012
|
+
let sql = `SELECT id FROM memories
|
|
4013
|
+
WHERE content_hash = ?
|
|
4014
|
+
AND agent_id = ?
|
|
4015
|
+
AND project_name = ?
|
|
4016
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4017
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
4018
|
+
if (input2.excludeId) {
|
|
4019
|
+
sql += " AND id != ?";
|
|
4020
|
+
args.push(input2.excludeId);
|
|
4021
|
+
}
|
|
4022
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
4023
|
+
const result = await client.execute({ sql, args });
|
|
4024
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
4025
|
+
}
|
|
4026
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4027
|
+
try {
|
|
4028
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4029
|
+
const client = getClient2();
|
|
4030
|
+
const current = await client.execute({
|
|
4031
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4032
|
+
importance, timestamp
|
|
4033
|
+
FROM memories
|
|
4034
|
+
WHERE id = ?
|
|
4035
|
+
LIMIT 1`,
|
|
4036
|
+
args: [memoryId]
|
|
4037
|
+
});
|
|
4038
|
+
const row = current.rows[0];
|
|
4039
|
+
if (!row) return;
|
|
4040
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
4041
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4042
|
+
const agentId = String(row.agent_id);
|
|
4043
|
+
const projectName = String(row.project_name);
|
|
4044
|
+
if (contentHash) {
|
|
4045
|
+
await client.execute({
|
|
4046
|
+
sql: `UPDATE memories
|
|
4047
|
+
SET status = 'deleted',
|
|
4048
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4049
|
+
WHERE id != ?
|
|
4050
|
+
AND content_hash = ?
|
|
4051
|
+
AND agent_id = ?
|
|
4052
|
+
AND project_name = ?
|
|
4053
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4054
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
4055
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4056
|
+
});
|
|
4057
|
+
}
|
|
4058
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
4059
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
4060
|
+
const old = await client.execute({
|
|
4061
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
4062
|
+
args: [supersedesId]
|
|
4063
|
+
});
|
|
4064
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
4065
|
+
const newImportance = Number(row.importance ?? 0);
|
|
4066
|
+
await client.batch([
|
|
4067
|
+
{
|
|
4068
|
+
sql: `UPDATE memories
|
|
4069
|
+
SET status = 'archived',
|
|
4070
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4071
|
+
WHERE id = ?`,
|
|
4072
|
+
args: [supersedesId]
|
|
4073
|
+
},
|
|
4074
|
+
{
|
|
4075
|
+
sql: `UPDATE memories
|
|
4076
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
4077
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
4078
|
+
WHERE id = ?`,
|
|
4079
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
4080
|
+
}
|
|
4081
|
+
], "write");
|
|
4082
|
+
}
|
|
4083
|
+
} catch (err) {
|
|
4084
|
+
process.stderr.write(
|
|
4085
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4086
|
+
`
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
4091
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
4092
|
+
if (memoryIds.length === 0) return;
|
|
4093
|
+
const run = () => {
|
|
4094
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
4095
|
+
};
|
|
4096
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
4097
|
+
else setTimeout(run, 0);
|
|
4098
|
+
}
|
|
4099
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
4100
|
+
var init_memory_write_governor = __esm({
|
|
4101
|
+
"src/lib/memory-write-governor.ts"() {
|
|
4102
|
+
"use strict";
|
|
4103
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
4104
|
+
"decision",
|
|
4105
|
+
"adr",
|
|
4106
|
+
"behavior",
|
|
4107
|
+
"procedure"
|
|
4108
|
+
]);
|
|
4109
|
+
NOISE_DROP_PATTERNS = [
|
|
4110
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
4111
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
4112
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
4113
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
4114
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
4115
|
+
];
|
|
4116
|
+
SKIP_EMBED_PATTERNS = [
|
|
4117
|
+
/tmux capture-pane\b/i,
|
|
4118
|
+
/docker ps\b/i,
|
|
4119
|
+
/docker images\b/i,
|
|
4120
|
+
/git status\b/i,
|
|
4121
|
+
/grep .*node_modules/i,
|
|
4122
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
4123
|
+
];
|
|
4124
|
+
}
|
|
4125
|
+
});
|
|
4126
|
+
|
|
3925
4127
|
// src/lib/shard-manager.ts
|
|
3926
4128
|
var shard_manager_exports = {};
|
|
3927
4129
|
__export(shard_manager_exports, {
|
|
@@ -4086,7 +4288,8 @@ async function ensureShardSchema(client) {
|
|
|
4086
4288
|
}
|
|
4087
4289
|
for (const idx of [
|
|
4088
4290
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
4089
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
4291
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
4292
|
+
"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"
|
|
4090
4293
|
]) {
|
|
4091
4294
|
try {
|
|
4092
4295
|
await client.execute(idx);
|
|
@@ -4280,7 +4483,7 @@ var init_platform_procedures = __esm({
|
|
|
4280
4483
|
title: "Chain of command \u2014 who talks to whom",
|
|
4281
4484
|
domain: "workflow",
|
|
4282
4485
|
priority: "p0",
|
|
4283
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
4486
|
+
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."
|
|
4284
4487
|
},
|
|
4285
4488
|
{
|
|
4286
4489
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -4511,7 +4714,6 @@ __export(store_exports, {
|
|
|
4511
4714
|
vectorToBlob: () => vectorToBlob,
|
|
4512
4715
|
writeMemory: () => writeMemory
|
|
4513
4716
|
});
|
|
4514
|
-
import { createHash } from "crypto";
|
|
4515
4717
|
function isBusyError2(err) {
|
|
4516
4718
|
if (err instanceof Error) {
|
|
4517
4719
|
const msg = err.message.toLowerCase();
|
|
@@ -4625,17 +4827,24 @@ async function writeMemory(record) {
|
|
|
4625
4827
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4626
4828
|
);
|
|
4627
4829
|
}
|
|
4628
|
-
const
|
|
4629
|
-
if (
|
|
4830
|
+
const governed = governMemoryRecord(record);
|
|
4831
|
+
if (governed.shouldDrop) return;
|
|
4832
|
+
record = governed.record;
|
|
4833
|
+
const contentHash = governed.contentHash;
|
|
4834
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4835
|
+
if (_pendingRecords.some(
|
|
4836
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4837
|
+
)) {
|
|
4630
4838
|
return;
|
|
4631
4839
|
}
|
|
4632
4840
|
try {
|
|
4633
|
-
const
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4841
|
+
const existing = await findScopedDuplicate({
|
|
4842
|
+
contentHash,
|
|
4843
|
+
agentId: record.agent_id,
|
|
4844
|
+
projectName: record.project_name,
|
|
4845
|
+
memoryType
|
|
4637
4846
|
});
|
|
4638
|
-
if (existing
|
|
4847
|
+
if (existing) return;
|
|
4639
4848
|
} catch {
|
|
4640
4849
|
}
|
|
4641
4850
|
const dbRow = {
|
|
@@ -4666,7 +4875,7 @@ async function writeMemory(record) {
|
|
|
4666
4875
|
tier: record.tier ?? classifyTier(record),
|
|
4667
4876
|
supersedes_id: record.supersedes_id ?? null,
|
|
4668
4877
|
draft: record.draft ? 1 : 0,
|
|
4669
|
-
memory_type:
|
|
4878
|
+
memory_type: memoryType,
|
|
4670
4879
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4671
4880
|
content_hash: contentHash,
|
|
4672
4881
|
intent: record.intent ?? null,
|
|
@@ -4825,6 +5034,7 @@ async function flushBatch() {
|
|
|
4825
5034
|
const globalClient = getClient();
|
|
4826
5035
|
const globalStmts = batch.map(buildStmt);
|
|
4827
5036
|
await globalClient.batch(globalStmts, "write");
|
|
5037
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4828
5038
|
_pendingRecords.splice(0, batch.length);
|
|
4829
5039
|
try {
|
|
4830
5040
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4945,7 +5155,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4945
5155
|
sql += ` AND timestamp >= ?`;
|
|
4946
5156
|
args.push(options.since);
|
|
4947
5157
|
}
|
|
4948
|
-
if (options?.
|
|
5158
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
5159
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
5160
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
5161
|
+
args.push(...uniqueTypes);
|
|
5162
|
+
} else if (options?.memoryType) {
|
|
4949
5163
|
sql += ` AND memory_type = ?`;
|
|
4950
5164
|
args.push(options.memoryType);
|
|
4951
5165
|
}
|
|
@@ -5083,6 +5297,7 @@ var init_store = __esm({
|
|
|
5083
5297
|
init_keychain();
|
|
5084
5298
|
init_config();
|
|
5085
5299
|
init_state_bus();
|
|
5300
|
+
init_memory_write_governor();
|
|
5086
5301
|
INIT_MAX_RETRIES = 3;
|
|
5087
5302
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
5088
5303
|
_pendingRecords = [];
|
|
@@ -5483,9 +5698,10 @@ process.stdin.on("end", async () => {
|
|
|
5483
5698
|
hookSpecificOutput: {
|
|
5484
5699
|
permissionDecision: "deny"
|
|
5485
5700
|
},
|
|
5486
|
-
systemMessage: `BLOCKED:
|
|
5487
|
-
Your tasks are in exe/${agent.agentId}/.
|
|
5488
|
-
|
|
5701
|
+
systemMessage: `ACTION BLOCKED: Your Write/Edit to exe/${target}/ was DENIED \u2014 it did NOT execute.
|
|
5702
|
+
That is ${target}'s task folder, not yours. Your tasks are in exe/${agent.agentId}/.
|
|
5703
|
+
Use ask_team_memory("${target}") if you need context from ${target}.
|
|
5704
|
+
Do NOT report this as successful. No file was changed. If you are a sub-agent, report FAILURE.`
|
|
5489
5705
|
});
|
|
5490
5706
|
process.stdout.write(output);
|
|
5491
5707
|
process.exit(0);
|