@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
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
6
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
7
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
8
|
}) : x)(function(x) {
|
|
@@ -14,6 +16,15 @@ var __export = (target, all) => {
|
|
|
14
16
|
for (var name in all)
|
|
15
17
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
18
|
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
|
+
for (let key of __getOwnPropNames(from))
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
23
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
17
28
|
|
|
18
29
|
// src/types/memory.ts
|
|
19
30
|
var EMBEDDING_DIM;
|
|
@@ -118,6 +129,21 @@ var init_secure_files = __esm({
|
|
|
118
129
|
});
|
|
119
130
|
|
|
120
131
|
// src/lib/config.ts
|
|
132
|
+
var config_exports = {};
|
|
133
|
+
__export(config_exports, {
|
|
134
|
+
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
135
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
136
|
+
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
137
|
+
DB_PATH: () => DB_PATH,
|
|
138
|
+
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
139
|
+
LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
|
|
140
|
+
MODELS_DIR: () => MODELS_DIR,
|
|
141
|
+
loadConfig: () => loadConfig,
|
|
142
|
+
loadConfigFrom: () => loadConfigFrom,
|
|
143
|
+
loadConfigSync: () => loadConfigSync,
|
|
144
|
+
migrateConfig: () => migrateConfig,
|
|
145
|
+
saveConfig: () => saveConfig
|
|
146
|
+
});
|
|
121
147
|
import { readFile, writeFile } from "fs/promises";
|
|
122
148
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
123
149
|
import path from "path";
|
|
@@ -221,6 +247,46 @@ async function loadConfig() {
|
|
|
221
247
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
222
248
|
}
|
|
223
249
|
}
|
|
250
|
+
function loadConfigSync() {
|
|
251
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
252
|
+
const configPath = path.join(dir, "config.json");
|
|
253
|
+
if (!existsSync2(configPath)) {
|
|
254
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
258
|
+
let parsed = JSON.parse(raw);
|
|
259
|
+
parsed = migrateLegacyConfig(parsed);
|
|
260
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
261
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
262
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
263
|
+
normalizeAutoUpdate(migratedCfg);
|
|
264
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
265
|
+
} catch {
|
|
266
|
+
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function saveConfig(config) {
|
|
270
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
271
|
+
await ensurePrivateDir(dir);
|
|
272
|
+
const configPath = path.join(dir, "config.json");
|
|
273
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
274
|
+
await enforcePrivateFile(configPath);
|
|
275
|
+
}
|
|
276
|
+
async function loadConfigFrom(configPath) {
|
|
277
|
+
const raw = await readFile(configPath, "utf-8");
|
|
278
|
+
try {
|
|
279
|
+
let parsed = JSON.parse(raw);
|
|
280
|
+
parsed = migrateLegacyConfig(parsed);
|
|
281
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
282
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
283
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
284
|
+
normalizeAutoUpdate(migratedCfg);
|
|
285
|
+
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
286
|
+
} catch {
|
|
287
|
+
return { ...DEFAULT_CONFIG };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
224
290
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
225
291
|
var init_config = __esm({
|
|
226
292
|
"src/lib/config.ts"() {
|
|
@@ -2084,6 +2150,14 @@ async function ensureSchema() {
|
|
|
2084
2150
|
);
|
|
2085
2151
|
} catch {
|
|
2086
2152
|
}
|
|
2153
|
+
try {
|
|
2154
|
+
await client.execute(
|
|
2155
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2156
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2157
|
+
WHERE content_hash IS NOT NULL`
|
|
2158
|
+
);
|
|
2159
|
+
} catch {
|
|
2160
|
+
}
|
|
2087
2161
|
await client.executeMultiple(`
|
|
2088
2162
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2089
2163
|
id TEXT PRIMARY KEY,
|
|
@@ -2753,7 +2827,8 @@ async function ensureShardSchema(client) {
|
|
|
2753
2827
|
}
|
|
2754
2828
|
for (const idx of [
|
|
2755
2829
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2756
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2830
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2831
|
+
"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"
|
|
2757
2832
|
]) {
|
|
2758
2833
|
try {
|
|
2759
2834
|
await client.execute(idx);
|
|
@@ -2947,7 +3022,7 @@ var init_platform_procedures = __esm({
|
|
|
2947
3022
|
title: "Chain of command \u2014 who talks to whom",
|
|
2948
3023
|
domain: "workflow",
|
|
2949
3024
|
priority: "p0",
|
|
2950
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3025
|
+
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."
|
|
2951
3026
|
},
|
|
2952
3027
|
{
|
|
2953
3028
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3490,6 +3565,7 @@ var init_preferences = __esm({
|
|
|
3490
3565
|
// src/adapters/claude/installer.ts
|
|
3491
3566
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
|
|
3492
3567
|
import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync6, copyFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
3568
|
+
import { createHash as createHash2 } from "crypto";
|
|
3493
3569
|
import path12 from "path";
|
|
3494
3570
|
import os9 from "os";
|
|
3495
3571
|
import { execSync as execSync5 } from "child_process";
|
|
@@ -3827,7 +3903,6 @@ import { spawnSync } from "child_process";
|
|
|
3827
3903
|
// src/lib/store.ts
|
|
3828
3904
|
init_memory();
|
|
3829
3905
|
init_database();
|
|
3830
|
-
import { createHash } from "crypto";
|
|
3831
3906
|
|
|
3832
3907
|
// src/lib/keychain.ts
|
|
3833
3908
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -4060,6 +4135,88 @@ var StateBus = class {
|
|
|
4060
4135
|
};
|
|
4061
4136
|
var orgBus = new StateBus();
|
|
4062
4137
|
|
|
4138
|
+
// src/lib/memory-write-governor.ts
|
|
4139
|
+
import { createHash } from "crypto";
|
|
4140
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
4141
|
+
"decision",
|
|
4142
|
+
"adr",
|
|
4143
|
+
"behavior",
|
|
4144
|
+
"procedure"
|
|
4145
|
+
]);
|
|
4146
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4147
|
+
try {
|
|
4148
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4149
|
+
const client = getClient2();
|
|
4150
|
+
const current = await client.execute({
|
|
4151
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4152
|
+
importance, timestamp
|
|
4153
|
+
FROM memories
|
|
4154
|
+
WHERE id = ?
|
|
4155
|
+
LIMIT 1`,
|
|
4156
|
+
args: [memoryId]
|
|
4157
|
+
});
|
|
4158
|
+
const row = current.rows[0];
|
|
4159
|
+
if (!row) return;
|
|
4160
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
4161
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4162
|
+
const agentId = String(row.agent_id);
|
|
4163
|
+
const projectName = String(row.project_name);
|
|
4164
|
+
if (contentHash) {
|
|
4165
|
+
await client.execute({
|
|
4166
|
+
sql: `UPDATE memories
|
|
4167
|
+
SET status = 'deleted',
|
|
4168
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4169
|
+
WHERE id != ?
|
|
4170
|
+
AND content_hash = ?
|
|
4171
|
+
AND agent_id = ?
|
|
4172
|
+
AND project_name = ?
|
|
4173
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4174
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
4175
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4176
|
+
});
|
|
4177
|
+
}
|
|
4178
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
4179
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
4180
|
+
const old = await client.execute({
|
|
4181
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
4182
|
+
args: [supersedesId]
|
|
4183
|
+
});
|
|
4184
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
4185
|
+
const newImportance = Number(row.importance ?? 0);
|
|
4186
|
+
await client.batch([
|
|
4187
|
+
{
|
|
4188
|
+
sql: `UPDATE memories
|
|
4189
|
+
SET status = 'archived',
|
|
4190
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4191
|
+
WHERE id = ?`,
|
|
4192
|
+
args: [supersedesId]
|
|
4193
|
+
},
|
|
4194
|
+
{
|
|
4195
|
+
sql: `UPDATE memories
|
|
4196
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
4197
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
4198
|
+
WHERE id = ?`,
|
|
4199
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
4200
|
+
}
|
|
4201
|
+
], "write");
|
|
4202
|
+
}
|
|
4203
|
+
} catch (err) {
|
|
4204
|
+
process.stderr.write(
|
|
4205
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4206
|
+
`
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
4211
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
4212
|
+
if (memoryIds.length === 0) return;
|
|
4213
|
+
const run = () => {
|
|
4214
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
4215
|
+
};
|
|
4216
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
4217
|
+
else setTimeout(run, 0);
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4063
4220
|
// src/lib/store.ts
|
|
4064
4221
|
var INIT_MAX_RETRIES = 3;
|
|
4065
4222
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -4259,6 +4416,7 @@ async function flushBatch() {
|
|
|
4259
4416
|
const globalClient = getClient();
|
|
4260
4417
|
const globalStmts = batch.map(buildStmt);
|
|
4261
4418
|
await globalClient.batch(globalStmts, "write");
|
|
4419
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4262
4420
|
_pendingRecords.splice(0, batch.length);
|
|
4263
4421
|
try {
|
|
4264
4422
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4370,7 +4528,16 @@ var BEHAVIORS_EXPORT_DIR = path8.join(
|
|
|
4370
4528
|
"behaviors-export"
|
|
4371
4529
|
);
|
|
4372
4530
|
var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
|
|
4373
|
-
var
|
|
4531
|
+
var DEFAULT_BEHAVIOR_LIMIT = 30;
|
|
4532
|
+
function getBehaviorLimit() {
|
|
4533
|
+
try {
|
|
4534
|
+
const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
|
|
4535
|
+
const cfg = loadConfigSync2();
|
|
4536
|
+
return cfg.behaviorExportLimit ?? DEFAULT_BEHAVIOR_LIMIT;
|
|
4537
|
+
} catch {
|
|
4538
|
+
return DEFAULT_BEHAVIOR_LIMIT;
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4374
4541
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
4375
4542
|
if (!existsSync8(BEHAVIORS_EXPORT_DIR)) return;
|
|
4376
4543
|
let entries;
|
|
@@ -4424,7 +4591,7 @@ function exportFilePath(agentId, projectName, sessionKey) {
|
|
|
4424
4591
|
async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
4425
4592
|
mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
|
|
4426
4593
|
sweepStaleBehaviorExports();
|
|
4427
|
-
const behaviors = await listBehaviors(agentId, projectName,
|
|
4594
|
+
const behaviors = await listBehaviors(agentId, projectName, getBehaviorLimit());
|
|
4428
4595
|
if (behaviors.length === 0) return null;
|
|
4429
4596
|
const body = renderBehaviorExport(behaviors);
|
|
4430
4597
|
const target = exportFilePath(agentId, projectName, sessionKey);
|
package/dist/bin/exe-status.js
CHANGED
|
@@ -2310,6 +2310,14 @@ async function ensureSchema() {
|
|
|
2310
2310
|
);
|
|
2311
2311
|
} catch {
|
|
2312
2312
|
}
|
|
2313
|
+
try {
|
|
2314
|
+
await client.execute(
|
|
2315
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2316
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2317
|
+
WHERE content_hash IS NOT NULL`
|
|
2318
|
+
);
|
|
2319
|
+
} catch {
|
|
2320
|
+
}
|
|
2313
2321
|
await client.executeMultiple(`
|
|
2314
2322
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2315
2323
|
id TEXT PRIMARY KEY,
|
|
@@ -3055,6 +3063,197 @@ var init_state_bus = __esm({
|
|
|
3055
3063
|
}
|
|
3056
3064
|
});
|
|
3057
3065
|
|
|
3066
|
+
// src/lib/memory-write-governor.ts
|
|
3067
|
+
import { createHash } from "crypto";
|
|
3068
|
+
function normalizeMemoryText(text) {
|
|
3069
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3070
|
+
}
|
|
3071
|
+
function classifyMemoryType(input) {
|
|
3072
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3073
|
+
const tool = input.tool_name.toLowerCase();
|
|
3074
|
+
const text = input.raw_text.toLowerCase();
|
|
3075
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3076
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3077
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3078
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3079
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3080
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3081
|
+
return "raw";
|
|
3082
|
+
}
|
|
3083
|
+
function shouldDropMemory(text) {
|
|
3084
|
+
const normalized = normalizeMemoryText(text);
|
|
3085
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3086
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3087
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3088
|
+
}
|
|
3089
|
+
return { drop: false };
|
|
3090
|
+
}
|
|
3091
|
+
function shouldSkipEmbedding(input) {
|
|
3092
|
+
const type = classifyMemoryType(input);
|
|
3093
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3094
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3095
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3096
|
+
return false;
|
|
3097
|
+
}
|
|
3098
|
+
function hashMemoryContent(text) {
|
|
3099
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3100
|
+
}
|
|
3101
|
+
function scopedDedupArgs(input) {
|
|
3102
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3103
|
+
}
|
|
3104
|
+
function governMemoryRecord(record) {
|
|
3105
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3106
|
+
const memoryType = classifyMemoryType({
|
|
3107
|
+
raw_text: normalized,
|
|
3108
|
+
agent_id: record.agent_id,
|
|
3109
|
+
project_name: record.project_name,
|
|
3110
|
+
tool_name: record.tool_name,
|
|
3111
|
+
memory_type: record.memory_type
|
|
3112
|
+
});
|
|
3113
|
+
const drop = shouldDropMemory(normalized);
|
|
3114
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3115
|
+
raw_text: normalized,
|
|
3116
|
+
agent_id: record.agent_id,
|
|
3117
|
+
project_name: record.project_name,
|
|
3118
|
+
tool_name: record.tool_name,
|
|
3119
|
+
memory_type: memoryType
|
|
3120
|
+
});
|
|
3121
|
+
return {
|
|
3122
|
+
record: {
|
|
3123
|
+
...record,
|
|
3124
|
+
raw_text: normalized,
|
|
3125
|
+
memory_type: memoryType,
|
|
3126
|
+
vector: skipEmbedding ? null : record.vector
|
|
3127
|
+
},
|
|
3128
|
+
contentHash: hashMemoryContent(normalized),
|
|
3129
|
+
shouldDrop: drop.drop,
|
|
3130
|
+
dropReason: drop.reason,
|
|
3131
|
+
skipEmbedding,
|
|
3132
|
+
hygiene: {
|
|
3133
|
+
dedup: true,
|
|
3134
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
async function findScopedDuplicate(input) {
|
|
3139
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3140
|
+
const client = getClient2();
|
|
3141
|
+
const args = scopedDedupArgs(input);
|
|
3142
|
+
let sql = `SELECT id FROM memories
|
|
3143
|
+
WHERE content_hash = ?
|
|
3144
|
+
AND agent_id = ?
|
|
3145
|
+
AND project_name = ?
|
|
3146
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3147
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3148
|
+
if (input.excludeId) {
|
|
3149
|
+
sql += " AND id != ?";
|
|
3150
|
+
args.push(input.excludeId);
|
|
3151
|
+
}
|
|
3152
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3153
|
+
const result = await client.execute({ sql, args });
|
|
3154
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3155
|
+
}
|
|
3156
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3157
|
+
try {
|
|
3158
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3159
|
+
const client = getClient2();
|
|
3160
|
+
const current = await client.execute({
|
|
3161
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3162
|
+
importance, timestamp
|
|
3163
|
+
FROM memories
|
|
3164
|
+
WHERE id = ?
|
|
3165
|
+
LIMIT 1`,
|
|
3166
|
+
args: [memoryId]
|
|
3167
|
+
});
|
|
3168
|
+
const row = current.rows[0];
|
|
3169
|
+
if (!row) return;
|
|
3170
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3171
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3172
|
+
const agentId = String(row.agent_id);
|
|
3173
|
+
const projectName = String(row.project_name);
|
|
3174
|
+
if (contentHash) {
|
|
3175
|
+
await client.execute({
|
|
3176
|
+
sql: `UPDATE memories
|
|
3177
|
+
SET status = 'deleted',
|
|
3178
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3179
|
+
WHERE id != ?
|
|
3180
|
+
AND content_hash = ?
|
|
3181
|
+
AND agent_id = ?
|
|
3182
|
+
AND project_name = ?
|
|
3183
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3184
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3185
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3189
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3190
|
+
const old = await client.execute({
|
|
3191
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3192
|
+
args: [supersedesId]
|
|
3193
|
+
});
|
|
3194
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3195
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3196
|
+
await client.batch([
|
|
3197
|
+
{
|
|
3198
|
+
sql: `UPDATE memories
|
|
3199
|
+
SET status = 'archived',
|
|
3200
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3201
|
+
WHERE id = ?`,
|
|
3202
|
+
args: [supersedesId]
|
|
3203
|
+
},
|
|
3204
|
+
{
|
|
3205
|
+
sql: `UPDATE memories
|
|
3206
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3207
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3208
|
+
WHERE id = ?`,
|
|
3209
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3210
|
+
}
|
|
3211
|
+
], "write");
|
|
3212
|
+
}
|
|
3213
|
+
} catch (err) {
|
|
3214
|
+
process.stderr.write(
|
|
3215
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3216
|
+
`
|
|
3217
|
+
);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3221
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3222
|
+
if (memoryIds.length === 0) return;
|
|
3223
|
+
const run = () => {
|
|
3224
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3225
|
+
};
|
|
3226
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3227
|
+
else setTimeout(run, 0);
|
|
3228
|
+
}
|
|
3229
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3230
|
+
var init_memory_write_governor = __esm({
|
|
3231
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3232
|
+
"use strict";
|
|
3233
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3234
|
+
"decision",
|
|
3235
|
+
"adr",
|
|
3236
|
+
"behavior",
|
|
3237
|
+
"procedure"
|
|
3238
|
+
]);
|
|
3239
|
+
NOISE_DROP_PATTERNS = [
|
|
3240
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3241
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3242
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3243
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3244
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3245
|
+
];
|
|
3246
|
+
SKIP_EMBED_PATTERNS = [
|
|
3247
|
+
/tmux capture-pane\b/i,
|
|
3248
|
+
/docker ps\b/i,
|
|
3249
|
+
/docker images\b/i,
|
|
3250
|
+
/git status\b/i,
|
|
3251
|
+
/grep .*node_modules/i,
|
|
3252
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3253
|
+
];
|
|
3254
|
+
}
|
|
3255
|
+
});
|
|
3256
|
+
|
|
3058
3257
|
// src/lib/shard-manager.ts
|
|
3059
3258
|
var shard_manager_exports = {};
|
|
3060
3259
|
__export(shard_manager_exports, {
|
|
@@ -3219,7 +3418,8 @@ async function ensureShardSchema(client) {
|
|
|
3219
3418
|
}
|
|
3220
3419
|
for (const idx of [
|
|
3221
3420
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3222
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3421
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3422
|
+
"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"
|
|
3223
3423
|
]) {
|
|
3224
3424
|
try {
|
|
3225
3425
|
await client.execute(idx);
|
|
@@ -3413,7 +3613,7 @@ var init_platform_procedures = __esm({
|
|
|
3413
3613
|
title: "Chain of command \u2014 who talks to whom",
|
|
3414
3614
|
domain: "workflow",
|
|
3415
3615
|
priority: "p0",
|
|
3416
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3616
|
+
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."
|
|
3417
3617
|
},
|
|
3418
3618
|
{
|
|
3419
3619
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3644,7 +3844,6 @@ __export(store_exports, {
|
|
|
3644
3844
|
vectorToBlob: () => vectorToBlob,
|
|
3645
3845
|
writeMemory: () => writeMemory
|
|
3646
3846
|
});
|
|
3647
|
-
import { createHash } from "crypto";
|
|
3648
3847
|
function isBusyError2(err) {
|
|
3649
3848
|
if (err instanceof Error) {
|
|
3650
3849
|
const msg = err.message.toLowerCase();
|
|
@@ -3758,17 +3957,24 @@ async function writeMemory(record) {
|
|
|
3758
3957
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3759
3958
|
);
|
|
3760
3959
|
}
|
|
3761
|
-
const
|
|
3762
|
-
if (
|
|
3960
|
+
const governed = governMemoryRecord(record);
|
|
3961
|
+
if (governed.shouldDrop) return;
|
|
3962
|
+
record = governed.record;
|
|
3963
|
+
const contentHash = governed.contentHash;
|
|
3964
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3965
|
+
if (_pendingRecords.some(
|
|
3966
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3967
|
+
)) {
|
|
3763
3968
|
return;
|
|
3764
3969
|
}
|
|
3765
3970
|
try {
|
|
3766
|
-
const
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3971
|
+
const existing = await findScopedDuplicate({
|
|
3972
|
+
contentHash,
|
|
3973
|
+
agentId: record.agent_id,
|
|
3974
|
+
projectName: record.project_name,
|
|
3975
|
+
memoryType
|
|
3770
3976
|
});
|
|
3771
|
-
if (existing
|
|
3977
|
+
if (existing) return;
|
|
3772
3978
|
} catch {
|
|
3773
3979
|
}
|
|
3774
3980
|
const dbRow = {
|
|
@@ -3799,7 +4005,7 @@ async function writeMemory(record) {
|
|
|
3799
4005
|
tier: record.tier ?? classifyTier(record),
|
|
3800
4006
|
supersedes_id: record.supersedes_id ?? null,
|
|
3801
4007
|
draft: record.draft ? 1 : 0,
|
|
3802
|
-
memory_type:
|
|
4008
|
+
memory_type: memoryType,
|
|
3803
4009
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3804
4010
|
content_hash: contentHash,
|
|
3805
4011
|
intent: record.intent ?? null,
|
|
@@ -3958,6 +4164,7 @@ async function flushBatch() {
|
|
|
3958
4164
|
const globalClient = getClient();
|
|
3959
4165
|
const globalStmts = batch.map(buildStmt);
|
|
3960
4166
|
await globalClient.batch(globalStmts, "write");
|
|
4167
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3961
4168
|
_pendingRecords.splice(0, batch.length);
|
|
3962
4169
|
try {
|
|
3963
4170
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4078,7 +4285,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4078
4285
|
sql += ` AND timestamp >= ?`;
|
|
4079
4286
|
args.push(options.since);
|
|
4080
4287
|
}
|
|
4081
|
-
if (options?.
|
|
4288
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4289
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4290
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4291
|
+
args.push(...uniqueTypes);
|
|
4292
|
+
} else if (options?.memoryType) {
|
|
4082
4293
|
sql += ` AND memory_type = ?`;
|
|
4083
4294
|
args.push(options.memoryType);
|
|
4084
4295
|
}
|
|
@@ -4216,6 +4427,7 @@ var init_store = __esm({
|
|
|
4216
4427
|
init_keychain();
|
|
4217
4428
|
init_config();
|
|
4218
4429
|
init_state_bus();
|
|
4430
|
+
init_memory_write_governor();
|
|
4219
4431
|
INIT_MAX_RETRIES = 3;
|
|
4220
4432
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4221
4433
|
_pendingRecords = [];
|