@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
|
@@ -2387,6 +2387,14 @@ async function ensureSchema() {
|
|
|
2387
2387
|
);
|
|
2388
2388
|
} catch {
|
|
2389
2389
|
}
|
|
2390
|
+
try {
|
|
2391
|
+
await client.execute(
|
|
2392
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2393
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2394
|
+
WHERE content_hash IS NOT NULL`
|
|
2395
|
+
);
|
|
2396
|
+
} catch {
|
|
2397
|
+
}
|
|
2390
2398
|
await client.executeMultiple(`
|
|
2391
2399
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2392
2400
|
id TEXT PRIMARY KEY,
|
|
@@ -3132,6 +3140,197 @@ var init_state_bus = __esm({
|
|
|
3132
3140
|
}
|
|
3133
3141
|
});
|
|
3134
3142
|
|
|
3143
|
+
// src/lib/memory-write-governor.ts
|
|
3144
|
+
import { createHash } from "crypto";
|
|
3145
|
+
function normalizeMemoryText(text) {
|
|
3146
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3147
|
+
}
|
|
3148
|
+
function classifyMemoryType(input) {
|
|
3149
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3150
|
+
const tool = input.tool_name.toLowerCase();
|
|
3151
|
+
const text = input.raw_text.toLowerCase();
|
|
3152
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3153
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3154
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3155
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3156
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3157
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3158
|
+
return "raw";
|
|
3159
|
+
}
|
|
3160
|
+
function shouldDropMemory(text) {
|
|
3161
|
+
const normalized = normalizeMemoryText(text);
|
|
3162
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3163
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3164
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3165
|
+
}
|
|
3166
|
+
return { drop: false };
|
|
3167
|
+
}
|
|
3168
|
+
function shouldSkipEmbedding(input) {
|
|
3169
|
+
const type = classifyMemoryType(input);
|
|
3170
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3171
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3172
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3173
|
+
return false;
|
|
3174
|
+
}
|
|
3175
|
+
function hashMemoryContent(text) {
|
|
3176
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3177
|
+
}
|
|
3178
|
+
function scopedDedupArgs(input) {
|
|
3179
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3180
|
+
}
|
|
3181
|
+
function governMemoryRecord(record) {
|
|
3182
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3183
|
+
const memoryType = classifyMemoryType({
|
|
3184
|
+
raw_text: normalized,
|
|
3185
|
+
agent_id: record.agent_id,
|
|
3186
|
+
project_name: record.project_name,
|
|
3187
|
+
tool_name: record.tool_name,
|
|
3188
|
+
memory_type: record.memory_type
|
|
3189
|
+
});
|
|
3190
|
+
const drop = shouldDropMemory(normalized);
|
|
3191
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3192
|
+
raw_text: normalized,
|
|
3193
|
+
agent_id: record.agent_id,
|
|
3194
|
+
project_name: record.project_name,
|
|
3195
|
+
tool_name: record.tool_name,
|
|
3196
|
+
memory_type: memoryType
|
|
3197
|
+
});
|
|
3198
|
+
return {
|
|
3199
|
+
record: {
|
|
3200
|
+
...record,
|
|
3201
|
+
raw_text: normalized,
|
|
3202
|
+
memory_type: memoryType,
|
|
3203
|
+
vector: skipEmbedding ? null : record.vector
|
|
3204
|
+
},
|
|
3205
|
+
contentHash: hashMemoryContent(normalized),
|
|
3206
|
+
shouldDrop: drop.drop,
|
|
3207
|
+
dropReason: drop.reason,
|
|
3208
|
+
skipEmbedding,
|
|
3209
|
+
hygiene: {
|
|
3210
|
+
dedup: true,
|
|
3211
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3212
|
+
}
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
async function findScopedDuplicate(input) {
|
|
3216
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3217
|
+
const client = getClient2();
|
|
3218
|
+
const args = scopedDedupArgs(input);
|
|
3219
|
+
let sql = `SELECT id FROM memories
|
|
3220
|
+
WHERE content_hash = ?
|
|
3221
|
+
AND agent_id = ?
|
|
3222
|
+
AND project_name = ?
|
|
3223
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3224
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3225
|
+
if (input.excludeId) {
|
|
3226
|
+
sql += " AND id != ?";
|
|
3227
|
+
args.push(input.excludeId);
|
|
3228
|
+
}
|
|
3229
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3230
|
+
const result = await client.execute({ sql, args });
|
|
3231
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3232
|
+
}
|
|
3233
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3234
|
+
try {
|
|
3235
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3236
|
+
const client = getClient2();
|
|
3237
|
+
const current = await client.execute({
|
|
3238
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3239
|
+
importance, timestamp
|
|
3240
|
+
FROM memories
|
|
3241
|
+
WHERE id = ?
|
|
3242
|
+
LIMIT 1`,
|
|
3243
|
+
args: [memoryId]
|
|
3244
|
+
});
|
|
3245
|
+
const row = current.rows[0];
|
|
3246
|
+
if (!row) return;
|
|
3247
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3248
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3249
|
+
const agentId = String(row.agent_id);
|
|
3250
|
+
const projectName = String(row.project_name);
|
|
3251
|
+
if (contentHash) {
|
|
3252
|
+
await client.execute({
|
|
3253
|
+
sql: `UPDATE memories
|
|
3254
|
+
SET status = 'deleted',
|
|
3255
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3256
|
+
WHERE id != ?
|
|
3257
|
+
AND content_hash = ?
|
|
3258
|
+
AND agent_id = ?
|
|
3259
|
+
AND project_name = ?
|
|
3260
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3261
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3262
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3266
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3267
|
+
const old = await client.execute({
|
|
3268
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3269
|
+
args: [supersedesId]
|
|
3270
|
+
});
|
|
3271
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3272
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3273
|
+
await client.batch([
|
|
3274
|
+
{
|
|
3275
|
+
sql: `UPDATE memories
|
|
3276
|
+
SET status = 'archived',
|
|
3277
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3278
|
+
WHERE id = ?`,
|
|
3279
|
+
args: [supersedesId]
|
|
3280
|
+
},
|
|
3281
|
+
{
|
|
3282
|
+
sql: `UPDATE memories
|
|
3283
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3284
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3285
|
+
WHERE id = ?`,
|
|
3286
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3287
|
+
}
|
|
3288
|
+
], "write");
|
|
3289
|
+
}
|
|
3290
|
+
} catch (err) {
|
|
3291
|
+
process.stderr.write(
|
|
3292
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3293
|
+
`
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3298
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3299
|
+
if (memoryIds.length === 0) return;
|
|
3300
|
+
const run = () => {
|
|
3301
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3302
|
+
};
|
|
3303
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3304
|
+
else setTimeout(run, 0);
|
|
3305
|
+
}
|
|
3306
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3307
|
+
var init_memory_write_governor = __esm({
|
|
3308
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3309
|
+
"use strict";
|
|
3310
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3311
|
+
"decision",
|
|
3312
|
+
"adr",
|
|
3313
|
+
"behavior",
|
|
3314
|
+
"procedure"
|
|
3315
|
+
]);
|
|
3316
|
+
NOISE_DROP_PATTERNS = [
|
|
3317
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3318
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3319
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3320
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3321
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3322
|
+
];
|
|
3323
|
+
SKIP_EMBED_PATTERNS = [
|
|
3324
|
+
/tmux capture-pane\b/i,
|
|
3325
|
+
/docker ps\b/i,
|
|
3326
|
+
/docker images\b/i,
|
|
3327
|
+
/git status\b/i,
|
|
3328
|
+
/grep .*node_modules/i,
|
|
3329
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3330
|
+
];
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
3333
|
+
|
|
3135
3334
|
// src/lib/shard-manager.ts
|
|
3136
3335
|
var shard_manager_exports = {};
|
|
3137
3336
|
__export(shard_manager_exports, {
|
|
@@ -3296,7 +3495,8 @@ async function ensureShardSchema(client) {
|
|
|
3296
3495
|
}
|
|
3297
3496
|
for (const idx of [
|
|
3298
3497
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3299
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3498
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3499
|
+
"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"
|
|
3300
3500
|
]) {
|
|
3301
3501
|
try {
|
|
3302
3502
|
await client.execute(idx);
|
|
@@ -3490,7 +3690,7 @@ var init_platform_procedures = __esm({
|
|
|
3490
3690
|
title: "Chain of command \u2014 who talks to whom",
|
|
3491
3691
|
domain: "workflow",
|
|
3492
3692
|
priority: "p0",
|
|
3493
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3693
|
+
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."
|
|
3494
3694
|
},
|
|
3495
3695
|
{
|
|
3496
3696
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3721,7 +3921,6 @@ __export(store_exports, {
|
|
|
3721
3921
|
vectorToBlob: () => vectorToBlob,
|
|
3722
3922
|
writeMemory: () => writeMemory
|
|
3723
3923
|
});
|
|
3724
|
-
import { createHash } from "crypto";
|
|
3725
3924
|
function isBusyError2(err) {
|
|
3726
3925
|
if (err instanceof Error) {
|
|
3727
3926
|
const msg = err.message.toLowerCase();
|
|
@@ -3835,17 +4034,24 @@ async function writeMemory(record) {
|
|
|
3835
4034
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3836
4035
|
);
|
|
3837
4036
|
}
|
|
3838
|
-
const
|
|
3839
|
-
if (
|
|
4037
|
+
const governed = governMemoryRecord(record);
|
|
4038
|
+
if (governed.shouldDrop) return;
|
|
4039
|
+
record = governed.record;
|
|
4040
|
+
const contentHash = governed.contentHash;
|
|
4041
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4042
|
+
if (_pendingRecords.some(
|
|
4043
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4044
|
+
)) {
|
|
3840
4045
|
return;
|
|
3841
4046
|
}
|
|
3842
4047
|
try {
|
|
3843
|
-
const
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
4048
|
+
const existing = await findScopedDuplicate({
|
|
4049
|
+
contentHash,
|
|
4050
|
+
agentId: record.agent_id,
|
|
4051
|
+
projectName: record.project_name,
|
|
4052
|
+
memoryType
|
|
3847
4053
|
});
|
|
3848
|
-
if (existing
|
|
4054
|
+
if (existing) return;
|
|
3849
4055
|
} catch {
|
|
3850
4056
|
}
|
|
3851
4057
|
const dbRow = {
|
|
@@ -3876,7 +4082,7 @@ async function writeMemory(record) {
|
|
|
3876
4082
|
tier: record.tier ?? classifyTier(record),
|
|
3877
4083
|
supersedes_id: record.supersedes_id ?? null,
|
|
3878
4084
|
draft: record.draft ? 1 : 0,
|
|
3879
|
-
memory_type:
|
|
4085
|
+
memory_type: memoryType,
|
|
3880
4086
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3881
4087
|
content_hash: contentHash,
|
|
3882
4088
|
intent: record.intent ?? null,
|
|
@@ -4035,6 +4241,7 @@ async function flushBatch() {
|
|
|
4035
4241
|
const globalClient = getClient();
|
|
4036
4242
|
const globalStmts = batch.map(buildStmt);
|
|
4037
4243
|
await globalClient.batch(globalStmts, "write");
|
|
4244
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4038
4245
|
_pendingRecords.splice(0, batch.length);
|
|
4039
4246
|
try {
|
|
4040
4247
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4155,7 +4362,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4155
4362
|
sql += ` AND timestamp >= ?`;
|
|
4156
4363
|
args.push(options.since);
|
|
4157
4364
|
}
|
|
4158
|
-
if (options?.
|
|
4365
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4366
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4367
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4368
|
+
args.push(...uniqueTypes);
|
|
4369
|
+
} else if (options?.memoryType) {
|
|
4159
4370
|
sql += ` AND memory_type = ?`;
|
|
4160
4371
|
args.push(options.memoryType);
|
|
4161
4372
|
}
|
|
@@ -4293,6 +4504,7 @@ var init_store = __esm({
|
|
|
4293
4504
|
init_keychain();
|
|
4294
4505
|
init_config();
|
|
4295
4506
|
init_state_bus();
|
|
4507
|
+
init_memory_write_governor();
|
|
4296
4508
|
INIT_MAX_RETRIES = 3;
|
|
4297
4509
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4298
4510
|
_pendingRecords = [];
|
|
@@ -7451,7 +7663,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7451
7663
|
}
|
|
7452
7664
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
7453
7665
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
7454
|
-
|
|
7666
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
7667
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
7668
|
+
ccModel += "[1m]";
|
|
7669
|
+
}
|
|
7670
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
7455
7671
|
}
|
|
7456
7672
|
}
|
|
7457
7673
|
let spawnCommand;
|
package/dist/bin/scan-tasks.js
CHANGED
|
@@ -2879,6 +2879,14 @@ async function ensureSchema() {
|
|
|
2879
2879
|
);
|
|
2880
2880
|
} catch {
|
|
2881
2881
|
}
|
|
2882
|
+
try {
|
|
2883
|
+
await client.execute(
|
|
2884
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2885
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2886
|
+
WHERE content_hash IS NOT NULL`
|
|
2887
|
+
);
|
|
2888
|
+
} catch {
|
|
2889
|
+
}
|
|
2882
2890
|
await client.executeMultiple(`
|
|
2883
2891
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2884
2892
|
id TEXT PRIMARY KEY,
|
|
@@ -6387,7 +6395,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6387
6395
|
}
|
|
6388
6396
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6389
6397
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6390
|
-
|
|
6398
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6399
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6400
|
+
ccModel += "[1m]";
|
|
6401
|
+
}
|
|
6402
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6391
6403
|
}
|
|
6392
6404
|
}
|
|
6393
6405
|
let spawnCommand;
|
|
@@ -6734,6 +6746,197 @@ var init_keychain = __esm({
|
|
|
6734
6746
|
}
|
|
6735
6747
|
});
|
|
6736
6748
|
|
|
6749
|
+
// src/lib/memory-write-governor.ts
|
|
6750
|
+
import { createHash } from "crypto";
|
|
6751
|
+
function normalizeMemoryText(text) {
|
|
6752
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6753
|
+
}
|
|
6754
|
+
function classifyMemoryType(input) {
|
|
6755
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
6756
|
+
const tool = input.tool_name.toLowerCase();
|
|
6757
|
+
const text = input.raw_text.toLowerCase();
|
|
6758
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6759
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6760
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6761
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6762
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6763
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6764
|
+
return "raw";
|
|
6765
|
+
}
|
|
6766
|
+
function shouldDropMemory(text) {
|
|
6767
|
+
const normalized = normalizeMemoryText(text);
|
|
6768
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6769
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6770
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6771
|
+
}
|
|
6772
|
+
return { drop: false };
|
|
6773
|
+
}
|
|
6774
|
+
function shouldSkipEmbedding(input) {
|
|
6775
|
+
const type = classifyMemoryType(input);
|
|
6776
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6777
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
6778
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
6779
|
+
return false;
|
|
6780
|
+
}
|
|
6781
|
+
function hashMemoryContent(text) {
|
|
6782
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6783
|
+
}
|
|
6784
|
+
function scopedDedupArgs(input) {
|
|
6785
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
6786
|
+
}
|
|
6787
|
+
function governMemoryRecord(record) {
|
|
6788
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6789
|
+
const memoryType = classifyMemoryType({
|
|
6790
|
+
raw_text: normalized,
|
|
6791
|
+
agent_id: record.agent_id,
|
|
6792
|
+
project_name: record.project_name,
|
|
6793
|
+
tool_name: record.tool_name,
|
|
6794
|
+
memory_type: record.memory_type
|
|
6795
|
+
});
|
|
6796
|
+
const drop = shouldDropMemory(normalized);
|
|
6797
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6798
|
+
raw_text: normalized,
|
|
6799
|
+
agent_id: record.agent_id,
|
|
6800
|
+
project_name: record.project_name,
|
|
6801
|
+
tool_name: record.tool_name,
|
|
6802
|
+
memory_type: memoryType
|
|
6803
|
+
});
|
|
6804
|
+
return {
|
|
6805
|
+
record: {
|
|
6806
|
+
...record,
|
|
6807
|
+
raw_text: normalized,
|
|
6808
|
+
memory_type: memoryType,
|
|
6809
|
+
vector: skipEmbedding ? null : record.vector
|
|
6810
|
+
},
|
|
6811
|
+
contentHash: hashMemoryContent(normalized),
|
|
6812
|
+
shouldDrop: drop.drop,
|
|
6813
|
+
dropReason: drop.reason,
|
|
6814
|
+
skipEmbedding,
|
|
6815
|
+
hygiene: {
|
|
6816
|
+
dedup: true,
|
|
6817
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
6818
|
+
}
|
|
6819
|
+
};
|
|
6820
|
+
}
|
|
6821
|
+
async function findScopedDuplicate(input) {
|
|
6822
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6823
|
+
const client = getClient2();
|
|
6824
|
+
const args = scopedDedupArgs(input);
|
|
6825
|
+
let sql = `SELECT id FROM memories
|
|
6826
|
+
WHERE content_hash = ?
|
|
6827
|
+
AND agent_id = ?
|
|
6828
|
+
AND project_name = ?
|
|
6829
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6830
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
6831
|
+
if (input.excludeId) {
|
|
6832
|
+
sql += " AND id != ?";
|
|
6833
|
+
args.push(input.excludeId);
|
|
6834
|
+
}
|
|
6835
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
6836
|
+
const result = await client.execute({ sql, args });
|
|
6837
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
6838
|
+
}
|
|
6839
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
6840
|
+
try {
|
|
6841
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6842
|
+
const client = getClient2();
|
|
6843
|
+
const current = await client.execute({
|
|
6844
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
6845
|
+
importance, timestamp
|
|
6846
|
+
FROM memories
|
|
6847
|
+
WHERE id = ?
|
|
6848
|
+
LIMIT 1`,
|
|
6849
|
+
args: [memoryId]
|
|
6850
|
+
});
|
|
6851
|
+
const row = current.rows[0];
|
|
6852
|
+
if (!row) return;
|
|
6853
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
6854
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
6855
|
+
const agentId = String(row.agent_id);
|
|
6856
|
+
const projectName = String(row.project_name);
|
|
6857
|
+
if (contentHash) {
|
|
6858
|
+
await client.execute({
|
|
6859
|
+
sql: `UPDATE memories
|
|
6860
|
+
SET status = 'deleted',
|
|
6861
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6862
|
+
WHERE id != ?
|
|
6863
|
+
AND content_hash = ?
|
|
6864
|
+
AND agent_id = ?
|
|
6865
|
+
AND project_name = ?
|
|
6866
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6867
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
6868
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
6869
|
+
});
|
|
6870
|
+
}
|
|
6871
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
6872
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
6873
|
+
const old = await client.execute({
|
|
6874
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
6875
|
+
args: [supersedesId]
|
|
6876
|
+
});
|
|
6877
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
6878
|
+
const newImportance = Number(row.importance ?? 0);
|
|
6879
|
+
await client.batch([
|
|
6880
|
+
{
|
|
6881
|
+
sql: `UPDATE memories
|
|
6882
|
+
SET status = 'archived',
|
|
6883
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6884
|
+
WHERE id = ?`,
|
|
6885
|
+
args: [supersedesId]
|
|
6886
|
+
},
|
|
6887
|
+
{
|
|
6888
|
+
sql: `UPDATE memories
|
|
6889
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
6890
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
6891
|
+
WHERE id = ?`,
|
|
6892
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
6893
|
+
}
|
|
6894
|
+
], "write");
|
|
6895
|
+
}
|
|
6896
|
+
} catch (err) {
|
|
6897
|
+
process.stderr.write(
|
|
6898
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
6899
|
+
`
|
|
6900
|
+
);
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
6904
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
6905
|
+
if (memoryIds.length === 0) return;
|
|
6906
|
+
const run = () => {
|
|
6907
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
6908
|
+
};
|
|
6909
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
6910
|
+
else setTimeout(run, 0);
|
|
6911
|
+
}
|
|
6912
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
6913
|
+
var init_memory_write_governor = __esm({
|
|
6914
|
+
"src/lib/memory-write-governor.ts"() {
|
|
6915
|
+
"use strict";
|
|
6916
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
6917
|
+
"decision",
|
|
6918
|
+
"adr",
|
|
6919
|
+
"behavior",
|
|
6920
|
+
"procedure"
|
|
6921
|
+
]);
|
|
6922
|
+
NOISE_DROP_PATTERNS = [
|
|
6923
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
6924
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
6925
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
6926
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
6927
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
6928
|
+
];
|
|
6929
|
+
SKIP_EMBED_PATTERNS = [
|
|
6930
|
+
/tmux capture-pane\b/i,
|
|
6931
|
+
/docker ps\b/i,
|
|
6932
|
+
/docker images\b/i,
|
|
6933
|
+
/git status\b/i,
|
|
6934
|
+
/grep .*node_modules/i,
|
|
6935
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
6936
|
+
];
|
|
6937
|
+
}
|
|
6938
|
+
});
|
|
6939
|
+
|
|
6737
6940
|
// src/lib/shard-manager.ts
|
|
6738
6941
|
var shard_manager_exports = {};
|
|
6739
6942
|
__export(shard_manager_exports, {
|
|
@@ -6898,7 +7101,8 @@ async function ensureShardSchema(client) {
|
|
|
6898
7101
|
}
|
|
6899
7102
|
for (const idx of [
|
|
6900
7103
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
6901
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7104
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7105
|
+
"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"
|
|
6902
7106
|
]) {
|
|
6903
7107
|
try {
|
|
6904
7108
|
await client.execute(idx);
|
|
@@ -7092,7 +7296,7 @@ var init_platform_procedures = __esm({
|
|
|
7092
7296
|
title: "Chain of command \u2014 who talks to whom",
|
|
7093
7297
|
domain: "workflow",
|
|
7094
7298
|
priority: "p0",
|
|
7095
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
7299
|
+
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."
|
|
7096
7300
|
},
|
|
7097
7301
|
{
|
|
7098
7302
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -7323,7 +7527,6 @@ __export(store_exports, {
|
|
|
7323
7527
|
vectorToBlob: () => vectorToBlob,
|
|
7324
7528
|
writeMemory: () => writeMemory
|
|
7325
7529
|
});
|
|
7326
|
-
import { createHash } from "crypto";
|
|
7327
7530
|
function isBusyError2(err) {
|
|
7328
7531
|
if (err instanceof Error) {
|
|
7329
7532
|
const msg = err.message.toLowerCase();
|
|
@@ -7437,17 +7640,24 @@ async function writeMemory(record) {
|
|
|
7437
7640
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7438
7641
|
);
|
|
7439
7642
|
}
|
|
7440
|
-
const
|
|
7441
|
-
if (
|
|
7643
|
+
const governed = governMemoryRecord(record);
|
|
7644
|
+
if (governed.shouldDrop) return;
|
|
7645
|
+
record = governed.record;
|
|
7646
|
+
const contentHash = governed.contentHash;
|
|
7647
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7648
|
+
if (_pendingRecords.some(
|
|
7649
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7650
|
+
)) {
|
|
7442
7651
|
return;
|
|
7443
7652
|
}
|
|
7444
7653
|
try {
|
|
7445
|
-
const
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7654
|
+
const existing = await findScopedDuplicate({
|
|
7655
|
+
contentHash,
|
|
7656
|
+
agentId: record.agent_id,
|
|
7657
|
+
projectName: record.project_name,
|
|
7658
|
+
memoryType
|
|
7449
7659
|
});
|
|
7450
|
-
if (existing
|
|
7660
|
+
if (existing) return;
|
|
7451
7661
|
} catch {
|
|
7452
7662
|
}
|
|
7453
7663
|
const dbRow = {
|
|
@@ -7478,7 +7688,7 @@ async function writeMemory(record) {
|
|
|
7478
7688
|
tier: record.tier ?? classifyTier(record),
|
|
7479
7689
|
supersedes_id: record.supersedes_id ?? null,
|
|
7480
7690
|
draft: record.draft ? 1 : 0,
|
|
7481
|
-
memory_type:
|
|
7691
|
+
memory_type: memoryType,
|
|
7482
7692
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7483
7693
|
content_hash: contentHash,
|
|
7484
7694
|
intent: record.intent ?? null,
|
|
@@ -7637,6 +7847,7 @@ async function flushBatch() {
|
|
|
7637
7847
|
const globalClient = getClient();
|
|
7638
7848
|
const globalStmts = batch.map(buildStmt);
|
|
7639
7849
|
await globalClient.batch(globalStmts, "write");
|
|
7850
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7640
7851
|
_pendingRecords.splice(0, batch.length);
|
|
7641
7852
|
try {
|
|
7642
7853
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -7757,7 +7968,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
7757
7968
|
sql += ` AND timestamp >= ?`;
|
|
7758
7969
|
args.push(options.since);
|
|
7759
7970
|
}
|
|
7760
|
-
if (options?.
|
|
7971
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
7972
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
7973
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
7974
|
+
args.push(...uniqueTypes);
|
|
7975
|
+
} else if (options?.memoryType) {
|
|
7761
7976
|
sql += ` AND memory_type = ?`;
|
|
7762
7977
|
args.push(options.memoryType);
|
|
7763
7978
|
}
|
|
@@ -7895,6 +8110,7 @@ var init_store = __esm({
|
|
|
7895
8110
|
init_keychain();
|
|
7896
8111
|
init_config();
|
|
7897
8112
|
init_state_bus();
|
|
8113
|
+
init_memory_write_governor();
|
|
7898
8114
|
INIT_MAX_RETRIES = 3;
|
|
7899
8115
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
7900
8116
|
_pendingRecords = [];
|