@askexenow/exe-os 0.9.115 → 0.9.117

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 (115) hide show
  1. package/deploy/compose/.env.example +21 -11
  2. package/deploy/compose/generate-env.ts +2 -2
  3. package/dist/bin/age-ontology-load.js +2 -2
  4. package/dist/bin/agentic-ontology-backfill.js +13 -4
  5. package/dist/bin/agentic-reflection-backfill.js +13 -4
  6. package/dist/bin/agentic-semantic-label.js +13 -4
  7. package/dist/bin/backfill-conversations.js +13 -4
  8. package/dist/bin/backfill-responses.js +13 -4
  9. package/dist/bin/backfill-vectors.js +13 -4
  10. package/dist/bin/bulk-sync-postgres.js +13 -4
  11. package/dist/bin/cc-doctor.js +2 -2
  12. package/dist/bin/cleanup-stale-review-tasks.js +61 -9
  13. package/dist/bin/cli.js +85 -55
  14. package/dist/bin/customer-readiness.js +1 -1
  15. package/dist/bin/exe-agent-config.js +2 -2
  16. package/dist/bin/exe-agent.js +3 -3
  17. package/dist/bin/exe-assign.js +13 -4
  18. package/dist/bin/exe-boot.js +85 -21
  19. package/dist/bin/exe-call.js +3 -3
  20. package/dist/bin/exe-cloud.js +16 -6
  21. package/dist/bin/exe-dispatch.js +80 -12
  22. package/dist/bin/exe-doctor.js +13 -4
  23. package/dist/bin/exe-export-behaviors.js +13 -4
  24. package/dist/bin/exe-forget.js +13 -4
  25. package/dist/bin/exe-gateway.js +82 -13
  26. package/dist/bin/exe-healthcheck.js +2 -2
  27. package/dist/bin/exe-heartbeat.js +61 -9
  28. package/dist/bin/exe-kill.js +13 -4
  29. package/dist/bin/exe-launch-agent.js +15 -6
  30. package/dist/bin/exe-new-employee.js +3 -30
  31. package/dist/bin/exe-pending-messages.js +61 -9
  32. package/dist/bin/exe-pending-notifications.js +61 -9
  33. package/dist/bin/exe-pending-reviews.js +61 -9
  34. package/dist/bin/exe-rename.js +13 -4
  35. package/dist/bin/exe-review.js +13 -4
  36. package/dist/bin/exe-search.js +13 -4
  37. package/dist/bin/exe-session-cleanup.js +80 -12
  38. package/dist/bin/exe-settings.js +2 -2
  39. package/dist/bin/exe-start-codex.js +15 -6
  40. package/dist/bin/exe-start-opencode.js +15 -6
  41. package/dist/bin/exe-status.js +61 -9
  42. package/dist/bin/exe-support.js +2 -2
  43. package/dist/bin/exe-team.js +13 -4
  44. package/dist/bin/git-sweep.js +80 -12
  45. package/dist/bin/graph-backfill.js +15 -5
  46. package/dist/bin/graph-export.js +13 -4
  47. package/dist/bin/install.js +2 -29
  48. package/dist/bin/intercom-check.js +80 -12
  49. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  50. package/dist/bin/postgres-agentic-semantic-backfill.js +2 -2
  51. package/dist/bin/pre-publish.js +1 -1
  52. package/dist/bin/scan-tasks.js +80 -12
  53. package/dist/bin/setup.js +6 -32
  54. package/dist/bin/shard-migrate.js +13 -4
  55. package/dist/bin/stack-update.js +2 -2
  56. package/dist/bin/update.js +2 -2
  57. package/dist/gateway/index.js +82 -13
  58. package/dist/hooks/bug-report-worker.js +80 -12
  59. package/dist/hooks/codex-stop-task-finalizer.js +79 -10
  60. package/dist/hooks/commit-complete.js +80 -12
  61. package/dist/hooks/error-recall.js +14 -5
  62. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  63. package/dist/hooks/ingest-worker.js +2 -2
  64. package/dist/hooks/ingest.js +82 -22
  65. package/dist/hooks/instructions-loaded.js +14 -5
  66. package/dist/hooks/notification.js +14 -5
  67. package/dist/hooks/post-compact.js +63 -19
  68. package/dist/hooks/post-tool-combined.js +68 -21
  69. package/dist/hooks/pre-compact.js +82 -22
  70. package/dist/hooks/pre-tool-use.js +63 -19
  71. package/dist/hooks/prompt-submit.js +82 -22
  72. package/dist/hooks/session-end.js +121 -23
  73. package/dist/hooks/session-start.js +50 -11
  74. package/dist/hooks/stop.js +127 -26
  75. package/dist/hooks/subagent-stop.js +63 -19
  76. package/dist/hooks/summary-worker.js +64 -11
  77. package/dist/index.js +82 -13
  78. package/dist/lib/agent-config.js +2 -2
  79. package/dist/lib/cloud-sync.js +5 -4
  80. package/dist/lib/config.js +2 -2
  81. package/dist/lib/consolidation.js +2 -2
  82. package/dist/lib/database.js +2 -2
  83. package/dist/lib/db-daemon-client.js +2 -2
  84. package/dist/lib/db.js +2 -2
  85. package/dist/lib/device-registry.js +2 -2
  86. package/dist/lib/embedder.js +2 -2
  87. package/dist/lib/employee-templates.js +3 -3
  88. package/dist/lib/employees.js +2 -2
  89. package/dist/lib/exe-daemon-client.js +2 -2
  90. package/dist/lib/exe-daemon.js +10098 -28646
  91. package/dist/lib/hybrid-search.js +13 -4
  92. package/dist/lib/identity.js +2 -2
  93. package/dist/lib/license.js +2 -2
  94. package/dist/lib/messaging.js +65 -7
  95. package/dist/lib/reminders.js +2 -2
  96. package/dist/lib/schedules.js +13 -4
  97. package/dist/lib/session-wrappers.js +0 -27
  98. package/dist/lib/skill-learning.js +5 -3
  99. package/dist/lib/store.js +13 -4
  100. package/dist/lib/task-router.js +2 -2
  101. package/dist/lib/tasks.js +69 -10
  102. package/dist/lib/tmux-routing.js +69 -10
  103. package/dist/lib/token-spend.js +2 -2
  104. package/dist/mcp/server.js +79 -22
  105. package/dist/mcp/tools/complete-reminder.js +2 -2
  106. package/dist/mcp/tools/create-reminder.js +2 -2
  107. package/dist/mcp/tools/create-task.js +71 -20
  108. package/dist/mcp/tools/deactivate-behavior.js +3 -3
  109. package/dist/mcp/tools/list-reminders.js +2 -2
  110. package/dist/mcp/tools/list-tasks.js +52 -17
  111. package/dist/mcp/tools/send-message.js +67 -17
  112. package/dist/mcp/tools/update-task.js +69 -23
  113. package/dist/runtime/index.js +80 -12
  114. package/dist/tui/App.js +80 -12
  115. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -291,8 +291,8 @@ var init_config = __esm({
291
291
  rerankerAutoTrigger: {
292
292
  enabled: true,
293
293
  broadQueryMinCardinality: 5e4,
294
- fetchTopK: 200,
295
- returnTopK: 20
294
+ fetchTopK: 150,
295
+ returnTopK: 5
296
296
  }
297
297
  },
298
298
  graphRagEnabled: true,
@@ -7316,10 +7316,11 @@ async function cloudPull(sinceVersion, config) {
7316
7316
  continue;
7317
7317
  }
7318
7318
  }
7319
+ const finalMaxVersion = maxReadableVersion;
7319
7320
  if (skippedBlobs > 0) {
7320
- logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
7321
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); cursor advancing to ${finalMaxVersion} (last readable: ${maxReadableVersion})`);
7321
7322
  }
7322
- return { records: allRecords, maxVersion: maxReadableVersion };
7323
+ return { records: allRecords, maxVersion: finalMaxVersion };
7323
7324
  } catch (err) {
7324
7325
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
7325
7326
  return { records: [], maxVersion: sinceVersion };
@@ -8961,7 +8962,16 @@ async function ensureShardSchema(client) {
8961
8962
  "ALTER TABLE memories ADD COLUMN audience TEXT",
8962
8963
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
8963
8964
  "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
8964
- "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
8965
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT",
8966
+ // Temporal validity (must match database.ts)
8967
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
8968
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT",
8969
+ // Multi-agent visibility (must match database.ts)
8970
+ "ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'",
8971
+ // Procedure binding (AFM-3, must match database.ts)
8972
+ "ALTER TABLE memories ADD COLUMN procedure_for TEXT",
8973
+ // Memory strength scoring — Ebbinghaus decay (must match database.ts)
8974
+ "ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0"
8965
8975
  ]) {
8966
8976
  try {
8967
8977
  await client.execute(col);
@@ -9226,7 +9236,7 @@ var init_platform_procedures = __esm({
9226
9236
  title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
9227
9237
  domain: "identity",
9228
9238
  priority: "p0",
9229
- content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
9239
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The internal routing slot stays unchanged for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
9230
9240
  },
9231
9241
  {
9232
9242
  title: "Single dispatch path \u2014 create_task only",
@@ -14188,6 +14198,27 @@ var init_plan_limits = __esm({
14188
14198
  }
14189
14199
  });
14190
14200
 
14201
+ // src/lib/agent-context.ts
14202
+ var agent_context_exports = {};
14203
+ __export(agent_context_exports, {
14204
+ getAgentContext: () => getAgentContext,
14205
+ runWithAgent: () => runWithAgent
14206
+ });
14207
+ import { AsyncLocalStorage } from "async_hooks";
14208
+ function runWithAgent(ctx, fn) {
14209
+ return agentStore.run(ctx, fn);
14210
+ }
14211
+ function getAgentContext() {
14212
+ return agentStore.getStore();
14213
+ }
14214
+ var agentStore;
14215
+ var init_agent_context = __esm({
14216
+ "src/lib/agent-context.ts"() {
14217
+ "use strict";
14218
+ agentStore = new AsyncLocalStorage();
14219
+ }
14220
+ });
14221
+
14191
14222
  // src/lib/task-scope.ts
14192
14223
  var task_scope_exports = {};
14193
14224
  __export(task_scope_exports, {
@@ -15835,7 +15866,9 @@ async function storeBehavior(opts) {
15835
15866
  const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
15836
15867
  const roster = loadEmployeesSync2();
15837
15868
  if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
15838
- throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
15869
+ if (process.env.NODE_ENV !== "test") {
15870
+ console.warn(`[behaviors] Agent "${opts.agentId}" not found in roster \u2014 storing anyway.`);
15871
+ }
15839
15872
  }
15840
15873
  } catch (e) {
15841
15874
  if (e instanceof Error && e.message.includes("not found in roster")) throw e;
@@ -16876,15 +16909,37 @@ function getDispatchedBy(sessionKey) {
16876
16909
  }
16877
16910
  }
16878
16911
  function resolveExeSession() {
16879
- if (process.env.EXE_SESSION_NAME) {
16880
- const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
16881
- if (fromEnv) return fromEnv;
16882
- }
16883
16912
  const mySession = getMySession();
16913
+ const fromSessionName = mySession ? extractRootExe(mySession) ?? null : null;
16914
+ const alsHint = (() => {
16915
+ try {
16916
+ const { getAgentContext: getAgentContext2 } = (init_agent_context(), __toCommonJS(agent_context_exports));
16917
+ return getAgentContext2()?.sessionHint ?? "";
16918
+ } catch {
16919
+ return "";
16920
+ }
16921
+ })();
16922
+ const sessionHintRaw = alsHint || process.env.EXE_SESSION_NAME || "";
16923
+ if (sessionHintRaw) {
16924
+ const fromEnv = extractRootExe(sessionHintRaw) ?? sessionHintRaw;
16925
+ if (fromEnv) {
16926
+ if (!mySession) {
16927
+ return fromEnv;
16928
+ }
16929
+ if (fromSessionName && fromEnv !== fromSessionName) {
16930
+ process.stderr.write(
16931
+ `[tmux-routing] WARN: EXE_SESSION_NAME="${fromEnv}" but tmux says "${fromSessionName}". Trusting tmux, correcting env var.
16932
+ `
16933
+ );
16934
+ process.env.EXE_SESSION_NAME = fromSessionName;
16935
+ } else {
16936
+ return fromEnv;
16937
+ }
16938
+ }
16939
+ }
16884
16940
  if (!mySession) {
16885
16941
  return null;
16886
16942
  }
16887
- const fromSessionName = extractRootExe(mySession);
16888
16943
  let candidate = null;
16889
16944
  try {
16890
16945
  const key = getSessionKey();
@@ -17068,6 +17123,21 @@ function isExeSession(sessionName) {
17068
17123
  }
17069
17124
  function sendIntercom(targetSession) {
17070
17125
  const transport = getTransport();
17126
+ try {
17127
+ const callerScope = resolveExeSession();
17128
+ if (callerScope && isExeSession(callerScope) && targetSession.includes("-")) {
17129
+ const targetScope = extractRootExe(targetSession);
17130
+ if (targetScope && targetScope !== callerScope) {
17131
+ logIntercom(`BLOCKED \u2192 ${targetSession} (scope violation: caller=${callerScope}, target=${targetScope})`);
17132
+ process.stderr.write(
17133
+ `[intercom] BLOCKED: cross-session intercom from ${callerScope} to ${targetSession}. Session isolation enforced.
17134
+ `
17135
+ );
17136
+ return "failed";
17137
+ }
17138
+ }
17139
+ } catch {
17140
+ }
17071
17141
  if (isExeSession(targetSession)) {
17072
17142
  logIntercom(`SKIP_COORDINATOR \u2192 ${targetSession} (coordinator sessions use prompt-submit hook)`);
17073
17143
  return "skipped_exe";
@@ -17442,8 +17512,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
17442
17512
  }
17443
17513
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
17444
17514
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
17445
- const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
17446
- const ccModel = normalizeCcModelName2(agentRtConfig.model);
17515
+ const ccModel = normalizeCcModelName(agentRtConfig.model);
17447
17516
  envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
17448
17517
  }
17449
17518
  }
@@ -17848,19 +17917,6 @@ var init_messaging = __esm({
17848
17917
  }
17849
17918
  });
17850
17919
 
17851
- // src/mcp/agent-context.ts
17852
- import { AsyncLocalStorage } from "async_hooks";
17853
- function getAgentContext() {
17854
- return agentStore.getStore();
17855
- }
17856
- var agentStore;
17857
- var init_agent_context = __esm({
17858
- "src/mcp/agent-context.ts"() {
17859
- "use strict";
17860
- agentStore = new AsyncLocalStorage();
17861
- }
17862
- });
17863
-
17864
17920
  // src/lib/active-agent.ts
17865
17921
  var active_agent_exports = {};
17866
17922
  __export(active_agent_exports, {
@@ -20575,33 +20631,6 @@ exec "${exeStartDst}" "$0" "$@"
20575
20631
  writeWrapper(path40.join(globalBinDir, `${emp.name}${n}`), wrapperContent);
20576
20632
  }
20577
20633
  created++;
20578
- writeWrapper(path40.join(binDir, `${emp.name}${n}-codex`), wrapperContent);
20579
- if (globalBinDir) {
20580
- writeWrapper(path40.join(globalBinDir, `${emp.name}${n}-codex`), wrapperContent);
20581
- }
20582
- created++;
20583
- }
20584
- }
20585
- const codexLauncherCandidates = [
20586
- path40.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
20587
- path40.join(packageRoot, "src", "bin", "exe-start-codex.ts")
20588
- ];
20589
- let codexLauncher = null;
20590
- for (const c of codexLauncherCandidates) {
20591
- if (existsSync34(c)) {
20592
- codexLauncher = c;
20593
- break;
20594
- }
20595
- }
20596
- if (codexLauncher) {
20597
- for (const emp of employees) {
20598
- const wrapperPath = path40.join(binDir, `${emp.name}-codex`);
20599
- const content = `#!/bin/bash
20600
- exec node "${codexLauncher}" --agent ${emp.name} "$@"
20601
- `;
20602
- writeFileSync24(wrapperPath, content);
20603
- chmodSync4(wrapperPath, 493);
20604
- created++;
20605
20634
  }
20606
20635
  }
20607
20636
  const pathConfigured = ensurePath(home, binDir);
@@ -37342,7 +37371,8 @@ function summarizeChunk(chunk, filePath) {
37342
37371
  }
37343
37372
  }
37344
37373
  function isChunkable(filePath) {
37345
- return Boolean(languageForFile(filePath));
37374
+ const lang = languageForFile(filePath);
37375
+ return lang === "typescript" || lang === "javascript";
37346
37376
  }
37347
37377
  var LANGUAGE_BY_EXTENSION, TEXT_LIKE_LANGUAGES;
37348
37378
  var init_code_chunker = __esm({
@@ -174,7 +174,7 @@ test("Support intake \u2014 API health route and cloud-sync license fallback exi
174
174
  });
175
175
  test("CLI/MCP parity \u2014 customer ops wrappers are registered", () => {
176
176
  const registry = readSrc("src/mcp/register-tools.ts");
177
- const gates = readSrc("src/mcp/tool-gates.ts");
177
+ const gates = readSrc("src/lib/tool-gates.ts");
178
178
  const parity = readSrc("src/mcp/tools/cli-parity.ts");
179
179
  const requiredTools = [
180
180
  "healthcheck",
@@ -85,8 +85,8 @@ var init_config = __esm({
85
85
  rerankerAutoTrigger: {
86
86
  enabled: true,
87
87
  broadQueryMinCardinality: 5e4,
88
- fetchTopK: 200,
89
- returnTopK: 20
88
+ fetchTopK: 150,
89
+ returnTopK: 5
90
90
  }
91
91
  },
92
92
  graphRagEnabled: true,
@@ -69,8 +69,8 @@ var init_config = __esm({
69
69
  rerankerAutoTrigger: {
70
70
  enabled: true,
71
71
  broadQueryMinCardinality: 5e4,
72
- fetchTopK: 200,
73
- returnTopK: 20
72
+ fetchTopK: 150,
73
+ returnTopK: 5
74
74
  }
75
75
  },
76
76
  graphRagEnabled: true,
@@ -1362,7 +1362,7 @@ var PLATFORM_PROCEDURES = [
1362
1362
  title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
1363
1363
  domain: "identity",
1364
1364
  priority: "p0",
1365
- content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
1365
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The internal routing slot stays unchanged for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
1366
1366
  },
1367
1367
  {
1368
1368
  title: "Single dispatch path \u2014 create_task only",
@@ -214,8 +214,8 @@ var init_config = __esm({
214
214
  rerankerAutoTrigger: {
215
215
  enabled: true,
216
216
  broadQueryMinCardinality: 5e4,
217
- fetchTopK: 200,
218
- returnTopK: 20
217
+ fetchTopK: 150,
218
+ returnTopK: 5
219
219
  }
220
220
  },
221
221
  graphRagEnabled: true,
@@ -3522,7 +3522,16 @@ async function ensureShardSchema(client) {
3522
3522
  "ALTER TABLE memories ADD COLUMN audience TEXT",
3523
3523
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
3524
3524
  "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
3525
- "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
3525
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT",
3526
+ // Temporal validity (must match database.ts)
3527
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3528
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT",
3529
+ // Multi-agent visibility (must match database.ts)
3530
+ "ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'",
3531
+ // Procedure binding (AFM-3, must match database.ts)
3532
+ "ALTER TABLE memories ADD COLUMN procedure_for TEXT",
3533
+ // Memory strength scoring — Ebbinghaus decay (must match database.ts)
3534
+ "ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0"
3526
3535
  ]) {
3527
3536
  try {
3528
3537
  await client.execute(col);
@@ -3787,7 +3796,7 @@ var init_platform_procedures = __esm({
3787
3796
  title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
3788
3797
  domain: "identity",
3789
3798
  priority: "p0",
3790
- content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
3799
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The internal routing slot stays unchanged for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
3791
3800
  },
3792
3801
  {
3793
3802
  title: "Single dispatch path \u2014 create_task only",
@@ -291,8 +291,8 @@ var init_config = __esm({
291
291
  rerankerAutoTrigger: {
292
292
  enabled: true,
293
293
  broadQueryMinCardinality: 5e4,
294
- fetchTopK: 200,
295
- returnTopK: 20
294
+ fetchTopK: 150,
295
+ returnTopK: 5
296
296
  }
297
297
  },
298
298
  graphRagEnabled: true,
@@ -3757,7 +3757,7 @@ var init_platform_procedures = __esm({
3757
3757
  title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
3758
3758
  domain: "identity",
3759
3759
  priority: "p0",
3760
- content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
3760
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The internal routing slot stays unchanged for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
3761
3761
  },
3762
3762
  {
3763
3763
  title: "Single dispatch path \u2014 create_task only",
@@ -4902,7 +4902,16 @@ async function ensureShardSchema(client) {
4902
4902
  "ALTER TABLE memories ADD COLUMN audience TEXT",
4903
4903
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
4904
4904
  "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
4905
- "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
4905
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT",
4906
+ // Temporal validity (must match database.ts)
4907
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
4908
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT",
4909
+ // Multi-agent visibility (must match database.ts)
4910
+ "ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'",
4911
+ // Procedure binding (AFM-3, must match database.ts)
4912
+ "ALTER TABLE memories ADD COLUMN procedure_for TEXT",
4913
+ // Memory strength scoring — Ebbinghaus decay (must match database.ts)
4914
+ "ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0"
4906
4915
  ]) {
4907
4916
  try {
4908
4917
  await client.execute(col);
@@ -6291,6 +6300,27 @@ var init_agent_symlinks = __esm({
6291
6300
  }
6292
6301
  });
6293
6302
 
6303
+ // src/lib/agent-context.ts
6304
+ var agent_context_exports = {};
6305
+ __export(agent_context_exports, {
6306
+ getAgentContext: () => getAgentContext,
6307
+ runWithAgent: () => runWithAgent
6308
+ });
6309
+ import { AsyncLocalStorage } from "async_hooks";
6310
+ function runWithAgent(ctx, fn) {
6311
+ return agentStore.run(ctx, fn);
6312
+ }
6313
+ function getAgentContext() {
6314
+ return agentStore.getStore();
6315
+ }
6316
+ var agentStore;
6317
+ var init_agent_context = __esm({
6318
+ "src/lib/agent-context.ts"() {
6319
+ "use strict";
6320
+ agentStore = new AsyncLocalStorage();
6321
+ }
6322
+ });
6323
+
6294
6324
  // src/lib/notifications.ts
6295
6325
  var notifications_exports = {};
6296
6326
  __export(notifications_exports, {
@@ -7962,7 +7992,9 @@ async function storeBehavior(opts) {
7962
7992
  const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
7963
7993
  const roster = loadEmployeesSync2();
7964
7994
  if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
7965
- throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
7995
+ if (process.env.NODE_ENV !== "test") {
7996
+ console.warn(`[behaviors] Agent "${opts.agentId}" not found in roster \u2014 storing anyway.`);
7997
+ }
7966
7998
  }
7967
7999
  } catch (e) {
7968
8000
  if (e instanceof Error && e.message.includes("not found in roster")) throw e;
@@ -9003,15 +9035,37 @@ function getDispatchedBy(sessionKey) {
9003
9035
  }
9004
9036
  }
9005
9037
  function resolveExeSession() {
9006
- if (process.env.EXE_SESSION_NAME) {
9007
- const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
9008
- if (fromEnv) return fromEnv;
9009
- }
9010
9038
  const mySession = getMySession();
9039
+ const fromSessionName = mySession ? extractRootExe(mySession) ?? null : null;
9040
+ const alsHint = (() => {
9041
+ try {
9042
+ const { getAgentContext: getAgentContext2 } = (init_agent_context(), __toCommonJS(agent_context_exports));
9043
+ return getAgentContext2()?.sessionHint ?? "";
9044
+ } catch {
9045
+ return "";
9046
+ }
9047
+ })();
9048
+ const sessionHintRaw = alsHint || process.env.EXE_SESSION_NAME || "";
9049
+ if (sessionHintRaw) {
9050
+ const fromEnv = extractRootExe(sessionHintRaw) ?? sessionHintRaw;
9051
+ if (fromEnv) {
9052
+ if (!mySession) {
9053
+ return fromEnv;
9054
+ }
9055
+ if (fromSessionName && fromEnv !== fromSessionName) {
9056
+ process.stderr.write(
9057
+ `[tmux-routing] WARN: EXE_SESSION_NAME="${fromEnv}" but tmux says "${fromSessionName}". Trusting tmux, correcting env var.
9058
+ `
9059
+ );
9060
+ process.env.EXE_SESSION_NAME = fromSessionName;
9061
+ } else {
9062
+ return fromEnv;
9063
+ }
9064
+ }
9065
+ }
9011
9066
  if (!mySession) {
9012
9067
  return null;
9013
9068
  }
9014
- const fromSessionName = extractRootExe(mySession);
9015
9069
  let candidate = null;
9016
9070
  try {
9017
9071
  const key = getSessionKey();
@@ -9195,6 +9249,21 @@ function isExeSession(sessionName) {
9195
9249
  }
9196
9250
  function sendIntercom(targetSession) {
9197
9251
  const transport = getTransport();
9252
+ try {
9253
+ const callerScope = resolveExeSession();
9254
+ if (callerScope && isExeSession(callerScope) && targetSession.includes("-")) {
9255
+ const targetScope = extractRootExe(targetSession);
9256
+ if (targetScope && targetScope !== callerScope) {
9257
+ logIntercom(`BLOCKED \u2192 ${targetSession} (scope violation: caller=${callerScope}, target=${targetScope})`);
9258
+ process.stderr.write(
9259
+ `[intercom] BLOCKED: cross-session intercom from ${callerScope} to ${targetSession}. Session isolation enforced.
9260
+ `
9261
+ );
9262
+ return "failed";
9263
+ }
9264
+ }
9265
+ } catch {
9266
+ }
9198
9267
  if (isExeSession(targetSession)) {
9199
9268
  logIntercom(`SKIP_COORDINATOR \u2192 ${targetSession} (coordinator sessions use prompt-submit hook)`);
9200
9269
  return "skipped_exe";
@@ -9569,8 +9638,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9569
9638
  }
9570
9639
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
9571
9640
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
9572
- const { normalizeCcModelName: normalizeCcModelName2 } = (init_agent_config(), __toCommonJS(agent_config_exports));
9573
- const ccModel = normalizeCcModelName2(agentRtConfig.model);
9641
+ const ccModel = normalizeCcModelName(agentRtConfig.model);
9574
9642
  envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
9575
9643
  }
9576
9644
  }
@@ -10616,10 +10684,11 @@ async function cloudPull(sinceVersion, config) {
10616
10684
  continue;
10617
10685
  }
10618
10686
  }
10687
+ const finalMaxVersion = maxReadableVersion;
10619
10688
  if (skippedBlobs > 0) {
10620
- logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
10689
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); cursor advancing to ${finalMaxVersion} (last readable: ${maxReadableVersion})`);
10621
10690
  }
10622
- return { records: allRecords, maxVersion: maxReadableVersion };
10691
+ return { records: allRecords, maxVersion: finalMaxVersion };
10623
10692
  } catch (err) {
10624
10693
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
10625
10694
  return { records: [], maxVersion: sinceVersion };
@@ -12524,16 +12593,11 @@ init_notifications();
12524
12593
  // src/lib/active-agent.ts
12525
12594
  init_config();
12526
12595
  init_session_key();
12596
+ init_agent_context();
12597
+ init_employees();
12527
12598
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, unlinkSync as unlinkSync8, readdirSync as readdirSync5 } from "fs";
12528
12599
  import { execSync as execSync10 } from "child_process";
12529
12600
  import path21 from "path";
12530
-
12531
- // src/mcp/agent-context.ts
12532
- import { AsyncLocalStorage } from "async_hooks";
12533
- var agentStore = new AsyncLocalStorage();
12534
-
12535
- // src/lib/active-agent.ts
12536
- init_employees();
12537
12601
  var CACHE_DIR = path21.join(EXE_AI_DIR, "session-cache");
12538
12602
  var STALE_MS = 24 * 60 * 60 * 1e3;
12539
12603
  function getMarkerPath() {
@@ -90,8 +90,8 @@ var init_config = __esm({
90
90
  rerankerAutoTrigger: {
91
91
  enabled: true,
92
92
  broadQueryMinCardinality: 5e4,
93
- fetchTopK: 200,
94
- returnTopK: 20
93
+ fetchTopK: 150,
94
+ returnTopK: 5
95
95
  }
96
96
  },
97
97
  graphRagEnabled: true,
@@ -299,7 +299,7 @@ var init_platform_procedures = __esm({
299
299
  title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
300
300
  domain: "identity",
301
301
  priority: "p0",
302
- content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
302
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The internal routing slot stays unchanged for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
303
303
  },
304
304
  {
305
305
  title: "Single dispatch path \u2014 create_task only",
@@ -729,8 +729,8 @@ var init_config = __esm({
729
729
  rerankerAutoTrigger: {
730
730
  enabled: true,
731
731
  broadQueryMinCardinality: 5e4,
732
- fetchTopK: 200,
733
- returnTopK: 20
732
+ fetchTopK: 150,
733
+ returnTopK: 5
734
734
  }
735
735
  },
736
736
  graphRagEnabled: true,
@@ -5069,10 +5069,11 @@ async function cloudPull(sinceVersion, config) {
5069
5069
  continue;
5070
5070
  }
5071
5071
  }
5072
+ const finalMaxVersion = maxReadableVersion;
5072
5073
  if (skippedBlobs > 0) {
5073
- logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
5074
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); cursor advancing to ${finalMaxVersion} (last readable: ${maxReadableVersion})`);
5074
5075
  }
5075
- return { records: allRecords, maxVersion: maxReadableVersion };
5076
+ return { records: allRecords, maxVersion: finalMaxVersion };
5076
5077
  } catch (err) {
5077
5078
  logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
5078
5079
  return { records: [], maxVersion: sinceVersion };
@@ -6714,7 +6715,16 @@ async function ensureShardSchema(client) {
6714
6715
  "ALTER TABLE memories ADD COLUMN audience TEXT",
6715
6716
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
6716
6717
  "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
6717
- "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
6718
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT",
6719
+ // Temporal validity (must match database.ts)
6720
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
6721
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT",
6722
+ // Multi-agent visibility (must match database.ts)
6723
+ "ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'",
6724
+ // Procedure binding (AFM-3, must match database.ts)
6725
+ "ALTER TABLE memories ADD COLUMN procedure_for TEXT",
6726
+ // Memory strength scoring — Ebbinghaus decay (must match database.ts)
6727
+ "ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0"
6718
6728
  ]) {
6719
6729
  try {
6720
6730
  await client.execute(col);
@@ -6979,7 +6989,7 @@ var init_platform_procedures = __esm({
6979
6989
  title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
6980
6990
  domain: "identity",
6981
6991
  priority: "p0",
6982
- content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
6992
+ content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The internal routing slot stays unchanged for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
6983
6993
  },
6984
6994
  {
6985
6995
  title: "Single dispatch path \u2014 create_task only",