@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
@@ -2223,6 +2223,14 @@ async function ensureSchema() {
2223
2223
  );
2224
2224
  } catch {
2225
2225
  }
2226
+ try {
2227
+ await client.execute(
2228
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2229
+ ON memories(content_hash, agent_id, project_name, memory_type)
2230
+ WHERE content_hash IS NOT NULL`
2231
+ );
2232
+ } catch {
2233
+ }
2226
2234
  await client.executeMultiple(`
2227
2235
  CREATE TABLE IF NOT EXISTS entities (
2228
2236
  id TEXT PRIMARY KEY,
@@ -2892,7 +2900,8 @@ async function ensureShardSchema(client) {
2892
2900
  }
2893
2901
  for (const idx of [
2894
2902
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
2895
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
2903
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
2904
+ "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"
2896
2905
  ]) {
2897
2906
  try {
2898
2907
  await client.execute(idx);
@@ -3086,7 +3095,7 @@ var init_platform_procedures = __esm({
3086
3095
  title: "Chain of command \u2014 who talks to whom",
3087
3096
  domain: "workflow",
3088
3097
  priority: "p0",
3089
- 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."
3098
+ 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."
3090
3099
  },
3091
3100
  {
3092
3101
  title: "Single dispatch path \u2014 create_task only",
@@ -3448,7 +3457,6 @@ async function embed(text) {
3448
3457
  // src/lib/store.ts
3449
3458
  init_memory();
3450
3459
  init_database();
3451
- import { createHash } from "crypto";
3452
3460
 
3453
3461
  // src/lib/keychain.ts
3454
3462
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
@@ -3681,6 +3689,191 @@ var StateBus = class {
3681
3689
  };
3682
3690
  var orgBus = new StateBus();
3683
3691
 
3692
+ // src/lib/memory-write-governor.ts
3693
+ import { createHash } from "crypto";
3694
+ var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3695
+ "decision",
3696
+ "adr",
3697
+ "behavior",
3698
+ "procedure"
3699
+ ]);
3700
+ var NOISE_DROP_PATTERNS = [
3701
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3702
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3703
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3704
+ /^\s*Intercom is a speedup, not delivery/im,
3705
+ /^\s*Context bar reads as USAGE not remaining/im
3706
+ ];
3707
+ var SKIP_EMBED_PATTERNS = [
3708
+ /tmux capture-pane\b/i,
3709
+ /docker ps\b/i,
3710
+ /docker images\b/i,
3711
+ /git status\b/i,
3712
+ /grep .*node_modules/i,
3713
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3714
+ ];
3715
+ function normalizeMemoryText(text) {
3716
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3717
+ }
3718
+ function classifyMemoryType(input) {
3719
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3720
+ const tool = input.tool_name.toLowerCase();
3721
+ const text = input.raw_text.toLowerCase();
3722
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3723
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3724
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3725
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3726
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3727
+ if (tool === "store_memory" || tool === "manual") return "observation";
3728
+ return "raw";
3729
+ }
3730
+ function shouldDropMemory(text) {
3731
+ const normalized = normalizeMemoryText(text);
3732
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3733
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3734
+ return { drop: true, reason: "known_boilerplate_noise" };
3735
+ }
3736
+ return { drop: false };
3737
+ }
3738
+ function shouldSkipEmbedding(input) {
3739
+ const type = classifyMemoryType(input);
3740
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3741
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3742
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3743
+ return false;
3744
+ }
3745
+ function hashMemoryContent(text) {
3746
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3747
+ }
3748
+ function scopedDedupArgs(input) {
3749
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3750
+ }
3751
+ function governMemoryRecord(record) {
3752
+ const normalized = normalizeMemoryText(record.raw_text);
3753
+ const memoryType = classifyMemoryType({
3754
+ raw_text: normalized,
3755
+ agent_id: record.agent_id,
3756
+ project_name: record.project_name,
3757
+ tool_name: record.tool_name,
3758
+ memory_type: record.memory_type
3759
+ });
3760
+ const drop = shouldDropMemory(normalized);
3761
+ const skipEmbedding = shouldSkipEmbedding({
3762
+ raw_text: normalized,
3763
+ agent_id: record.agent_id,
3764
+ project_name: record.project_name,
3765
+ tool_name: record.tool_name,
3766
+ memory_type: memoryType
3767
+ });
3768
+ return {
3769
+ record: {
3770
+ ...record,
3771
+ raw_text: normalized,
3772
+ memory_type: memoryType,
3773
+ vector: skipEmbedding ? null : record.vector
3774
+ },
3775
+ contentHash: hashMemoryContent(normalized),
3776
+ shouldDrop: drop.drop,
3777
+ dropReason: drop.reason,
3778
+ skipEmbedding,
3779
+ hygiene: {
3780
+ dedup: true,
3781
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3782
+ }
3783
+ };
3784
+ }
3785
+ async function findScopedDuplicate(input) {
3786
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3787
+ const client = getClient2();
3788
+ const args = scopedDedupArgs(input);
3789
+ let sql = `SELECT id FROM memories
3790
+ WHERE content_hash = ?
3791
+ AND agent_id = ?
3792
+ AND project_name = ?
3793
+ AND COALESCE(memory_type, 'raw') = ?
3794
+ AND COALESCE(status, 'active') != 'deleted'`;
3795
+ if (input.excludeId) {
3796
+ sql += " AND id != ?";
3797
+ args.push(input.excludeId);
3798
+ }
3799
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3800
+ const result = await client.execute({ sql, args });
3801
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3802
+ }
3803
+ async function runPostWriteMemoryHygiene(memoryId) {
3804
+ try {
3805
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3806
+ const client = getClient2();
3807
+ const current = await client.execute({
3808
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3809
+ importance, timestamp
3810
+ FROM memories
3811
+ WHERE id = ?
3812
+ LIMIT 1`,
3813
+ args: [memoryId]
3814
+ });
3815
+ const row = current.rows[0];
3816
+ if (!row) return;
3817
+ const memoryType = String(row.memory_type ?? "raw");
3818
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3819
+ const agentId = String(row.agent_id);
3820
+ const projectName = String(row.project_name);
3821
+ if (contentHash) {
3822
+ await client.execute({
3823
+ sql: `UPDATE memories
3824
+ SET status = 'deleted',
3825
+ outcome = COALESCE(outcome, 'superseded')
3826
+ WHERE id != ?
3827
+ AND content_hash = ?
3828
+ AND agent_id = ?
3829
+ AND project_name = ?
3830
+ AND COALESCE(memory_type, 'raw') = ?
3831
+ AND COALESCE(status, 'active') = 'active'`,
3832
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3833
+ });
3834
+ }
3835
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3836
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3837
+ const old = await client.execute({
3838
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3839
+ args: [supersedesId]
3840
+ });
3841
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3842
+ const newImportance = Number(row.importance ?? 0);
3843
+ await client.batch([
3844
+ {
3845
+ sql: `UPDATE memories
3846
+ SET status = 'archived',
3847
+ outcome = COALESCE(outcome, 'superseded')
3848
+ WHERE id = ?`,
3849
+ args: [supersedesId]
3850
+ },
3851
+ {
3852
+ sql: `UPDATE memories
3853
+ SET importance = MAX(COALESCE(importance, 5), ?),
3854
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3855
+ WHERE id = ?`,
3856
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3857
+ }
3858
+ ], "write");
3859
+ }
3860
+ } catch (err) {
3861
+ process.stderr.write(
3862
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3863
+ `
3864
+ );
3865
+ }
3866
+ }
3867
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3868
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3869
+ if (memoryIds.length === 0) return;
3870
+ const run = () => {
3871
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3872
+ };
3873
+ if (typeof setImmediate === "function") setImmediate(run);
3874
+ else setTimeout(run, 0);
3875
+ }
3876
+
3684
3877
  // src/lib/store.ts
3685
3878
  var INIT_MAX_RETRIES = 3;
3686
3879
  var INIT_RETRY_DELAY_MS = 1e3;
@@ -3803,17 +3996,24 @@ async function writeMemory(record) {
3803
3996
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3804
3997
  );
3805
3998
  }
3806
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3807
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3999
+ const governed = governMemoryRecord(record);
4000
+ if (governed.shouldDrop) return;
4001
+ record = governed.record;
4002
+ const contentHash = governed.contentHash;
4003
+ const memoryType = record.memory_type ?? "raw";
4004
+ if (_pendingRecords.some(
4005
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4006
+ )) {
3808
4007
  return;
3809
4008
  }
3810
4009
  try {
3811
- const client = getClient();
3812
- const existing = await client.execute({
3813
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3814
- args: [contentHash, record.agent_id]
4010
+ const existing = await findScopedDuplicate({
4011
+ contentHash,
4012
+ agentId: record.agent_id,
4013
+ projectName: record.project_name,
4014
+ memoryType
3815
4015
  });
3816
- if (existing.rows.length > 0) return;
4016
+ if (existing) return;
3817
4017
  } catch {
3818
4018
  }
3819
4019
  const dbRow = {
@@ -3844,7 +4044,7 @@ async function writeMemory(record) {
3844
4044
  tier: record.tier ?? classifyTier(record),
3845
4045
  supersedes_id: record.supersedes_id ?? null,
3846
4046
  draft: record.draft ? 1 : 0,
3847
- memory_type: record.memory_type ?? "raw",
4047
+ memory_type: memoryType,
3848
4048
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3849
4049
  content_hash: contentHash,
3850
4050
  intent: record.intent ?? null,
@@ -4003,6 +4203,7 @@ async function flushBatch() {
4003
4203
  const globalClient = getClient();
4004
4204
  const globalStmts = batch.map(buildStmt);
4005
4205
  await globalClient.batch(globalStmts, "write");
4206
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4006
4207
  _pendingRecords.splice(0, batch.length);
4007
4208
  try {
4008
4209
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4123,7 +4324,11 @@ async function searchMemories(queryVector, agentId, options) {
4123
4324
  sql += ` AND timestamp >= ?`;
4124
4325
  args.push(options.since);
4125
4326
  }
4126
- if (options?.memoryType) {
4327
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4328
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4329
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4330
+ args.push(...uniqueTypes);
4331
+ } else if (options?.memoryType) {
4127
4332
  sql += ` AND memory_type = ?`;
4128
4333
  args.push(options.memoryType);
4129
4334
  }
@@ -2365,6 +2365,14 @@ async function ensureSchema() {
2365
2365
  );
2366
2366
  } catch {
2367
2367
  }
2368
+ try {
2369
+ await client.execute(
2370
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2371
+ ON memories(content_hash, agent_id, project_name, memory_type)
2372
+ WHERE content_hash IS NOT NULL`
2373
+ );
2374
+ } catch {
2375
+ }
2368
2376
  await client.executeMultiple(`
2369
2377
  CREATE TABLE IF NOT EXISTS entities (
2370
2378
  id TEXT PRIMARY KEY,
@@ -2906,7 +2914,7 @@ var init_platform_procedures = __esm({
2906
2914
  title: "Chain of command \u2014 who talks to whom",
2907
2915
  domain: "workflow",
2908
2916
  priority: "p0",
2909
- 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."
2917
+ 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."
2910
2918
  },
2911
2919
  {
2912
2920
  title: "Single dispatch path \u2014 create_task only",
@@ -3474,6 +3482,14 @@ var init_state_bus = __esm({
3474
3482
  }
3475
3483
  });
3476
3484
 
3485
+ // src/lib/memory-write-governor.ts
3486
+ import { createHash } from "crypto";
3487
+ var init_memory_write_governor = __esm({
3488
+ "src/lib/memory-write-governor.ts"() {
3489
+ "use strict";
3490
+ }
3491
+ });
3492
+
3477
3493
  // src/lib/shard-manager.ts
3478
3494
  var shard_manager_exports = {};
3479
3495
  __export(shard_manager_exports, {
@@ -3638,7 +3654,8 @@ async function ensureShardSchema(client) {
3638
3654
  }
3639
3655
  for (const idx of [
3640
3656
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3641
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3657
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3658
+ "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"
3642
3659
  ]) {
3643
3660
  try {
3644
3661
  await client.execute(idx);
@@ -3797,7 +3814,6 @@ var init_shard_manager = __esm({
3797
3814
  });
3798
3815
 
3799
3816
  // src/lib/store.ts
3800
- import { createHash } from "crypto";
3801
3817
  function isBusyError2(err) {
3802
3818
  if (err instanceof Error) {
3803
3819
  const msg = err.message.toLowerCase();
@@ -3882,6 +3898,7 @@ var init_store = __esm({
3882
3898
  init_keychain();
3883
3899
  init_config();
3884
3900
  init_state_bus();
3901
+ init_memory_write_governor();
3885
3902
  INIT_MAX_RETRIES = 3;
3886
3903
  INIT_RETRY_DELAY_MS = 1e3;
3887
3904
  _pendingRecords = [];
@@ -7996,7 +8013,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7996
8013
  }
7997
8014
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7998
8015
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
7999
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
8016
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
8017
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
8018
+ ccModel += "[1m]";
8019
+ }
8020
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
8000
8021
  }
8001
8022
  }
8002
8023
  let spawnCommand;
@@ -581,7 +581,7 @@ var init_platform_procedures = __esm({
581
581
  title: "Chain of command \u2014 who talks to whom",
582
582
  domain: "workflow",
583
583
  priority: "p0",
584
- 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."
584
+ 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."
585
585
  },
586
586
  {
587
587
  title: "Single dispatch path \u2014 create_task only",
@@ -786,8 +786,9 @@ function personalizePrompt(prompt, templateName, actualName) {
786
786
  return prompt.replace(new RegExp(`\\bYou are ${escaped}\\b`, "g"), `You are ${actualName}`);
787
787
  }
788
788
  function renderClientCOOTemplate(vars) {
789
+ const resolved = { ...vars, title: vars.title || "Chief Operating Officer" };
789
790
  for (const key of CLIENT_COO_PLACEHOLDERS) {
790
- const value = vars[key];
791
+ const value = resolved[key];
791
792
  if (typeof value !== "string" || value.length === 0) {
792
793
  throw new Error(
793
794
  `renderClientCOOTemplate: missing required variable "${key}"`
@@ -796,7 +797,7 @@ function renderClientCOOTemplate(vars) {
796
797
  }
797
798
  let out = CLIENT_COO_TEMPLATE;
798
799
  for (const key of CLIENT_COO_PLACEHOLDERS) {
799
- out = out.split(`{{${key}}}`).join(vars[key]);
800
+ out = out.split(`{{${key}}}`).join(resolved[key]);
800
801
  }
801
802
  if (vars.industry_context) {
802
803
  out += "\n" + vars.industry_context;
@@ -1273,7 +1274,7 @@ created_by: system
1273
1274
  ---
1274
1275
  ## Identity
1275
1276
 
1276
- You are {{agent_name}}, the Chief Operating Officer at {{company_name}}.
1277
+ You are {{agent_name}}, the {{title}} at {{company_name}}.
1277
1278
 
1278
1279
  You are {{founder_name}}'s most reliable teammate in business \u2014 the knowledgeable older sibling who has been through it all. You have seen projects succeed and fail. You know what matters and what is noise. You do not get anxious about problems; you see them coming, stay calm, and handle them.
1279
1280
 
@@ -1360,7 +1361,8 @@ All memory, tasks, behaviors, documents, and wiki content belonging to {{company
1360
1361
  CLIENT_COO_PLACEHOLDERS = [
1361
1362
  "agent_name",
1362
1363
  "company_name",
1363
- "founder_name"
1364
+ "founder_name",
1365
+ "title"
1364
1366
  ];
1365
1367
  }
1366
1368
  });