@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.
Files changed (76) hide show
  1. package/dist/bin/backfill-conversations.js +212 -11
  2. package/dist/bin/backfill-responses.js +212 -11
  3. package/dist/bin/backfill-vectors.js +14 -3
  4. package/dist/bin/cleanup-stale-review-tasks.js +224 -12
  5. package/dist/bin/cli.js +265 -23
  6. package/dist/bin/exe-agent.js +1 -1
  7. package/dist/bin/exe-assign.js +217 -12
  8. package/dist/bin/exe-boot.js +25 -4
  9. package/dist/bin/exe-call.js +7 -5
  10. package/dist/bin/exe-dispatch.js +229 -13
  11. package/dist/bin/exe-doctor.js +14 -3
  12. package/dist/bin/exe-export-behaviors.js +301 -14
  13. package/dist/bin/exe-forget.js +245 -21
  14. package/dist/bin/exe-gateway.js +229 -13
  15. package/dist/bin/exe-heartbeat.js +224 -12
  16. package/dist/bin/exe-kill.js +224 -12
  17. package/dist/bin/exe-launch-agent.js +177 -9
  18. package/dist/bin/exe-link.js +8 -0
  19. package/dist/bin/exe-new-employee.js +26 -2
  20. package/dist/bin/exe-pending-messages.js +224 -12
  21. package/dist/bin/exe-pending-notifications.js +224 -12
  22. package/dist/bin/exe-pending-reviews.js +224 -12
  23. package/dist/bin/exe-rename.js +9 -1
  24. package/dist/bin/exe-review.js +224 -12
  25. package/dist/bin/exe-search.js +246 -21
  26. package/dist/bin/exe-session-cleanup.js +229 -13
  27. package/dist/bin/exe-start-codex.js +161 -5
  28. package/dist/bin/exe-start-opencode.js +172 -5
  29. package/dist/bin/exe-status.js +224 -12
  30. package/dist/bin/exe-team.js +224 -12
  31. package/dist/bin/git-sweep.js +229 -13
  32. package/dist/bin/graph-backfill.js +94 -3
  33. package/dist/bin/graph-export.js +224 -12
  34. package/dist/bin/install.js +25 -1
  35. package/dist/bin/intercom-check.js +229 -13
  36. package/dist/bin/scan-tasks.js +229 -13
  37. package/dist/bin/setup.js +15 -5
  38. package/dist/bin/shard-migrate.js +94 -3
  39. package/dist/gateway/index.js +229 -13
  40. package/dist/hooks/bug-report-worker.js +229 -13
  41. package/dist/hooks/codex-stop-task-finalizer.js +224 -12
  42. package/dist/hooks/commit-complete.js +229 -13
  43. package/dist/hooks/error-recall.js +246 -21
  44. package/dist/hooks/ingest.js +224 -12
  45. package/dist/hooks/instructions-loaded.js +224 -12
  46. package/dist/hooks/notification.js +224 -12
  47. package/dist/hooks/post-compact.js +224 -12
  48. package/dist/hooks/post-tool-combined.js +246 -21
  49. package/dist/hooks/pre-compact.js +229 -13
  50. package/dist/hooks/pre-tool-use.js +234 -18
  51. package/dist/hooks/prompt-submit.js +346 -23
  52. package/dist/hooks/session-end.js +229 -13
  53. package/dist/hooks/session-start.js +418 -42
  54. package/dist/hooks/stop.js +224 -12
  55. package/dist/hooks/subagent-stop.js +224 -12
  56. package/dist/hooks/summary-worker.js +15 -2
  57. package/dist/index.js +229 -13
  58. package/dist/lib/cloud-sync.js +8 -0
  59. package/dist/lib/consolidation.js +3 -1
  60. package/dist/lib/database.js +8 -0
  61. package/dist/lib/db.js +8 -0
  62. package/dist/lib/device-registry.js +8 -0
  63. package/dist/lib/employee-templates.js +7 -5
  64. package/dist/lib/exe-daemon.js +1776 -1433
  65. package/dist/lib/hybrid-search.js +246 -21
  66. package/dist/lib/schedules.js +14 -3
  67. package/dist/lib/store.js +217 -12
  68. package/dist/lib/tasks.js +5 -1
  69. package/dist/lib/tmux-routing.js +5 -1
  70. package/dist/mcp/server.js +331 -37
  71. package/dist/mcp/tools/create-task.js +5 -1
  72. package/dist/mcp/tools/update-task.js +5 -1
  73. package/dist/runtime/index.js +229 -13
  74. package/dist/tui/App.js +229 -13
  75. package/package.json +1 -1
  76. package/src/commands/exe/save.md +48 -0
package/dist/lib/store.js CHANGED
@@ -2072,6 +2072,14 @@ async function ensureSchema() {
2072
2072
  );
2073
2073
  } catch {
2074
2074
  }
2075
+ try {
2076
+ await client.execute(
2077
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2078
+ ON memories(content_hash, agent_id, project_name, memory_type)
2079
+ WHERE content_hash IS NOT NULL`
2080
+ );
2081
+ } catch {
2082
+ }
2075
2083
  await client.executeMultiple(`
2076
2084
  CREATE TABLE IF NOT EXISTS entities (
2077
2085
  id TEXT PRIMARY KEY,
@@ -2741,7 +2749,8 @@ async function ensureShardSchema(client) {
2741
2749
  }
2742
2750
  for (const idx of [
2743
2751
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
2744
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
2752
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2753
+ "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"
2745
2754
  ]) {
2746
2755
  try {
2747
2756
  await client.execute(idx);
@@ -2935,7 +2944,7 @@ var init_platform_procedures = __esm({
2935
2944
  title: "Chain of command \u2014 who talks to whom",
2936
2945
  domain: "workflow",
2937
2946
  priority: "p0",
2938
- content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO 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."
2947
+ 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."
2939
2948
  },
2940
2949
  {
2941
2950
  title: "Single dispatch path \u2014 create_task only",
@@ -3151,7 +3160,6 @@ ${p.content}`).join("\n\n");
3151
3160
  // src/lib/store.ts
3152
3161
  init_memory();
3153
3162
  init_database();
3154
- import { createHash } from "crypto";
3155
3163
 
3156
3164
  // src/lib/keychain.ts
3157
3165
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -3384,6 +3392,191 @@ var StateBus = class {
3384
3392
  };
3385
3393
  var orgBus = new StateBus();
3386
3394
 
3395
+ // src/lib/memory-write-governor.ts
3396
+ import { createHash } from "crypto";
3397
+ var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3398
+ "decision",
3399
+ "adr",
3400
+ "behavior",
3401
+ "procedure"
3402
+ ]);
3403
+ var NOISE_DROP_PATTERNS = [
3404
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3405
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3406
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3407
+ /^\s*Intercom is a speedup, not delivery/im,
3408
+ /^\s*Context bar reads as USAGE not remaining/im
3409
+ ];
3410
+ var SKIP_EMBED_PATTERNS = [
3411
+ /tmux capture-pane\b/i,
3412
+ /docker ps\b/i,
3413
+ /docker images\b/i,
3414
+ /git status\b/i,
3415
+ /grep .*node_modules/i,
3416
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3417
+ ];
3418
+ function normalizeMemoryText(text) {
3419
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3420
+ }
3421
+ function classifyMemoryType(input) {
3422
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3423
+ const tool = input.tool_name.toLowerCase();
3424
+ const text = input.raw_text.toLowerCase();
3425
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3426
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3427
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3428
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3429
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3430
+ if (tool === "store_memory" || tool === "manual") return "observation";
3431
+ return "raw";
3432
+ }
3433
+ function shouldDropMemory(text) {
3434
+ const normalized = normalizeMemoryText(text);
3435
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3436
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3437
+ return { drop: true, reason: "known_boilerplate_noise" };
3438
+ }
3439
+ return { drop: false };
3440
+ }
3441
+ function shouldSkipEmbedding(input) {
3442
+ const type = classifyMemoryType(input);
3443
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3444
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3445
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3446
+ return false;
3447
+ }
3448
+ function hashMemoryContent(text) {
3449
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3450
+ }
3451
+ function scopedDedupArgs(input) {
3452
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3453
+ }
3454
+ function governMemoryRecord(record) {
3455
+ const normalized = normalizeMemoryText(record.raw_text);
3456
+ const memoryType = classifyMemoryType({
3457
+ raw_text: normalized,
3458
+ agent_id: record.agent_id,
3459
+ project_name: record.project_name,
3460
+ tool_name: record.tool_name,
3461
+ memory_type: record.memory_type
3462
+ });
3463
+ const drop = shouldDropMemory(normalized);
3464
+ const skipEmbedding = shouldSkipEmbedding({
3465
+ raw_text: normalized,
3466
+ agent_id: record.agent_id,
3467
+ project_name: record.project_name,
3468
+ tool_name: record.tool_name,
3469
+ memory_type: memoryType
3470
+ });
3471
+ return {
3472
+ record: {
3473
+ ...record,
3474
+ raw_text: normalized,
3475
+ memory_type: memoryType,
3476
+ vector: skipEmbedding ? null : record.vector
3477
+ },
3478
+ contentHash: hashMemoryContent(normalized),
3479
+ shouldDrop: drop.drop,
3480
+ dropReason: drop.reason,
3481
+ skipEmbedding,
3482
+ hygiene: {
3483
+ dedup: true,
3484
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3485
+ }
3486
+ };
3487
+ }
3488
+ async function findScopedDuplicate(input) {
3489
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3490
+ const client = getClient2();
3491
+ const args = scopedDedupArgs(input);
3492
+ let sql = `SELECT id FROM memories
3493
+ WHERE content_hash = ?
3494
+ AND agent_id = ?
3495
+ AND project_name = ?
3496
+ AND COALESCE(memory_type, 'raw') = ?
3497
+ AND COALESCE(status, 'active') != 'deleted'`;
3498
+ if (input.excludeId) {
3499
+ sql += " AND id != ?";
3500
+ args.push(input.excludeId);
3501
+ }
3502
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3503
+ const result = await client.execute({ sql, args });
3504
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3505
+ }
3506
+ async function runPostWriteMemoryHygiene(memoryId) {
3507
+ try {
3508
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3509
+ const client = getClient2();
3510
+ const current = await client.execute({
3511
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3512
+ importance, timestamp
3513
+ FROM memories
3514
+ WHERE id = ?
3515
+ LIMIT 1`,
3516
+ args: [memoryId]
3517
+ });
3518
+ const row = current.rows[0];
3519
+ if (!row) return;
3520
+ const memoryType = String(row.memory_type ?? "raw");
3521
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3522
+ const agentId = String(row.agent_id);
3523
+ const projectName = String(row.project_name);
3524
+ if (contentHash) {
3525
+ await client.execute({
3526
+ sql: `UPDATE memories
3527
+ SET status = 'deleted',
3528
+ outcome = COALESCE(outcome, 'superseded')
3529
+ WHERE id != ?
3530
+ AND content_hash = ?
3531
+ AND agent_id = ?
3532
+ AND project_name = ?
3533
+ AND COALESCE(memory_type, 'raw') = ?
3534
+ AND COALESCE(status, 'active') = 'active'`,
3535
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3536
+ });
3537
+ }
3538
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3539
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3540
+ const old = await client.execute({
3541
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3542
+ args: [supersedesId]
3543
+ });
3544
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3545
+ const newImportance = Number(row.importance ?? 0);
3546
+ await client.batch([
3547
+ {
3548
+ sql: `UPDATE memories
3549
+ SET status = 'archived',
3550
+ outcome = COALESCE(outcome, 'superseded')
3551
+ WHERE id = ?`,
3552
+ args: [supersedesId]
3553
+ },
3554
+ {
3555
+ sql: `UPDATE memories
3556
+ SET importance = MAX(COALESCE(importance, 5), ?),
3557
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3558
+ WHERE id = ?`,
3559
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3560
+ }
3561
+ ], "write");
3562
+ }
3563
+ } catch (err) {
3564
+ process.stderr.write(
3565
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3566
+ `
3567
+ );
3568
+ }
3569
+ }
3570
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3571
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3572
+ if (memoryIds.length === 0) return;
3573
+ const run = () => {
3574
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3575
+ };
3576
+ if (typeof setImmediate === "function") setImmediate(run);
3577
+ else setTimeout(run, 0);
3578
+ }
3579
+
3387
3580
  // src/lib/store.ts
3388
3581
  var INIT_MAX_RETRIES = 3;
3389
3582
  var INIT_RETRY_DELAY_MS = 1e3;
@@ -3506,17 +3699,24 @@ async function writeMemory(record) {
3506
3699
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3507
3700
  );
3508
3701
  }
3509
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3510
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3702
+ const governed = governMemoryRecord(record);
3703
+ if (governed.shouldDrop) return;
3704
+ record = governed.record;
3705
+ const contentHash = governed.contentHash;
3706
+ const memoryType = record.memory_type ?? "raw";
3707
+ if (_pendingRecords.some(
3708
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3709
+ )) {
3511
3710
  return;
3512
3711
  }
3513
3712
  try {
3514
- const client = getClient();
3515
- const existing = await client.execute({
3516
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3517
- args: [contentHash, record.agent_id]
3713
+ const existing = await findScopedDuplicate({
3714
+ contentHash,
3715
+ agentId: record.agent_id,
3716
+ projectName: record.project_name,
3717
+ memoryType
3518
3718
  });
3519
- if (existing.rows.length > 0) return;
3719
+ if (existing) return;
3520
3720
  } catch {
3521
3721
  }
3522
3722
  const dbRow = {
@@ -3547,7 +3747,7 @@ async function writeMemory(record) {
3547
3747
  tier: record.tier ?? classifyTier(record),
3548
3748
  supersedes_id: record.supersedes_id ?? null,
3549
3749
  draft: record.draft ? 1 : 0,
3550
- memory_type: record.memory_type ?? "raw",
3750
+ memory_type: memoryType,
3551
3751
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3552
3752
  content_hash: contentHash,
3553
3753
  intent: record.intent ?? null,
@@ -3706,6 +3906,7 @@ async function flushBatch() {
3706
3906
  const globalClient = getClient();
3707
3907
  const globalStmts = batch.map(buildStmt);
3708
3908
  await globalClient.batch(globalStmts, "write");
3909
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3709
3910
  _pendingRecords.splice(0, batch.length);
3710
3911
  try {
3711
3912
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -3826,7 +4027,11 @@ async function searchMemories(queryVector, agentId, options) {
3826
4027
  sql += ` AND timestamp >= ?`;
3827
4028
  args.push(options.since);
3828
4029
  }
3829
- if (options?.memoryType) {
4030
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4031
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4032
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4033
+ args.push(...uniqueTypes);
4034
+ } else if (options?.memoryType) {
3830
4035
  sql += ` AND memory_type = ?`;
3831
4036
  args.push(options.memoryType);
3832
4037
  }
package/dist/lib/tasks.js CHANGED
@@ -2389,7 +2389,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2389
2389
  }
2390
2390
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
2391
2391
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
2392
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
2392
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
2393
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
2394
+ ccModel += "[1m]";
2395
+ }
2396
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
2393
2397
  }
2394
2398
  }
2395
2399
  let spawnCommand;
@@ -4415,7 +4415,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4415
4415
  }
4416
4416
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
4417
4417
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
4418
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
4418
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
4419
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
4420
+ ccModel += "[1m]";
4421
+ }
4422
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
4419
4423
  }
4420
4424
  }
4421
4425
  let spawnCommand;