@askexenow/exe-os 0.9.112 → 0.9.113

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 (95) hide show
  1. package/README.md +9 -7
  2. package/dist/bin/agentic-ontology-backfill.js +54 -11
  3. package/dist/bin/agentic-reflection-backfill.js +29 -1
  4. package/dist/bin/agentic-semantic-label.js +29 -1
  5. package/dist/bin/backfill-conversations.js +53 -10
  6. package/dist/bin/backfill-responses.js +54 -11
  7. package/dist/bin/backfill-vectors.js +29 -1
  8. package/dist/bin/bulk-sync-postgres.js +55 -12
  9. package/dist/bin/cleanup-stale-review-tasks.js +75 -15
  10. package/dist/bin/cli.js +293 -76
  11. package/dist/bin/exe-agent-config.js +7 -1
  12. package/dist/bin/exe-agent.js +28 -2
  13. package/dist/bin/exe-assign.js +54 -11
  14. package/dist/bin/exe-boot.js +481 -147
  15. package/dist/bin/exe-call.js +45 -4
  16. package/dist/bin/exe-cloud.js +93 -15
  17. package/dist/bin/exe-dispatch.js +369 -24
  18. package/dist/bin/exe-doctor.js +53 -10
  19. package/dist/bin/exe-export-behaviors.js +54 -11
  20. package/dist/bin/exe-forget.js +54 -11
  21. package/dist/bin/exe-gateway.js +128 -23
  22. package/dist/bin/exe-heartbeat.js +75 -15
  23. package/dist/bin/exe-kill.js +54 -11
  24. package/dist/bin/exe-launch-agent.js +70 -12
  25. package/dist/bin/exe-new-employee.js +175 -7
  26. package/dist/bin/exe-pending-messages.js +75 -15
  27. package/dist/bin/exe-pending-notifications.js +75 -15
  28. package/dist/bin/exe-pending-reviews.js +75 -15
  29. package/dist/bin/exe-rename.js +54 -11
  30. package/dist/bin/exe-review.js +54 -11
  31. package/dist/bin/exe-search.js +54 -11
  32. package/dist/bin/exe-session-cleanup.js +491 -146
  33. package/dist/bin/exe-settings.js +10 -4
  34. package/dist/bin/exe-start-codex.js +524 -245
  35. package/dist/bin/exe-start-opencode.js +534 -165
  36. package/dist/bin/exe-status.js +75 -15
  37. package/dist/bin/exe-support.js +1 -1
  38. package/dist/bin/exe-team.js +54 -11
  39. package/dist/bin/git-sweep.js +369 -24
  40. package/dist/bin/graph-backfill.js +54 -11
  41. package/dist/bin/graph-export.js +54 -11
  42. package/dist/bin/install.js +62 -4
  43. package/dist/bin/intercom-check.js +491 -146
  44. package/dist/bin/pre-publish.js +13 -1
  45. package/dist/bin/scan-tasks.js +369 -24
  46. package/dist/bin/setup.js +91 -13
  47. package/dist/bin/shard-migrate.js +54 -11
  48. package/dist/bin/stack-update.js +1 -1
  49. package/dist/bin/update.js +3 -3
  50. package/dist/gateway/index.js +128 -23
  51. package/dist/hooks/bug-report-worker.js +128 -23
  52. package/dist/hooks/codex-stop-task-finalizer.js +512 -140
  53. package/dist/hooks/commit-complete.js +369 -24
  54. package/dist/hooks/error-recall.js +54 -11
  55. package/dist/hooks/ingest.js +4575 -252
  56. package/dist/hooks/instructions-loaded.js +54 -11
  57. package/dist/hooks/notification.js +54 -11
  58. package/dist/hooks/post-compact.js +75 -15
  59. package/dist/hooks/post-tool-combined.js +75 -15
  60. package/dist/hooks/pre-compact.js +449 -104
  61. package/dist/hooks/pre-tool-use.js +90 -15
  62. package/dist/hooks/prompt-submit.js +129 -24
  63. package/dist/hooks/session-end.js +451 -109
  64. package/dist/hooks/session-start.js +104 -16
  65. package/dist/hooks/stop.js +74 -14
  66. package/dist/hooks/subagent-stop.js +75 -15
  67. package/dist/hooks/summary-worker.js +73 -7
  68. package/dist/index.js +128 -23
  69. package/dist/lib/agent-config.js +16 -1
  70. package/dist/lib/cloud-sync.js +38 -1
  71. package/dist/lib/consolidation.js +16 -1
  72. package/dist/lib/database.js +16 -0
  73. package/dist/lib/db.js +16 -0
  74. package/dist/lib/device-registry.js +16 -0
  75. package/dist/lib/employee-templates.js +29 -3
  76. package/dist/lib/employees.js +16 -1
  77. package/dist/lib/exe-daemon.js +268 -42
  78. package/dist/lib/hybrid-search.js +54 -11
  79. package/dist/lib/license.js +3 -3
  80. package/dist/lib/messaging.js +21 -4
  81. package/dist/lib/schedules.js +29 -1
  82. package/dist/lib/skill-learning.js +458 -70
  83. package/dist/lib/status-brief.js +14 -1
  84. package/dist/lib/store.js +54 -11
  85. package/dist/lib/tasks.js +393 -91
  86. package/dist/lib/tmux-routing.js +316 -14
  87. package/dist/mcp/server.js +169 -30
  88. package/dist/mcp/tools/create-task.js +75 -13
  89. package/dist/mcp/tools/deactivate-behavior.js +33 -24
  90. package/dist/mcp/tools/list-tasks.js +21 -4
  91. package/dist/mcp/tools/send-message.js +21 -4
  92. package/dist/mcp/tools/update-task.js +390 -91
  93. package/dist/runtime/index.js +446 -101
  94. package/dist/tui/App.js +208 -54
  95. package/package.json +1 -1
@@ -3320,6 +3320,22 @@ async function ensureSchema() {
3320
3320
  } catch (e) {
3321
3321
  logCatchDebug("migration", e);
3322
3322
  }
3323
+ try {
3324
+ await client.execute({
3325
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
3326
+ args: []
3327
+ });
3328
+ } catch (e) {
3329
+ logCatchDebug("migration", e);
3330
+ }
3331
+ try {
3332
+ await client.execute({
3333
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
3334
+ args: []
3335
+ });
3336
+ } catch (e) {
3337
+ logCatchDebug("migration", e);
3338
+ }
3323
3339
  }
3324
3340
  async function disposeDatabase() {
3325
3341
  if (_walCheckpointTimer) {
@@ -4437,11 +4453,17 @@ var init_platform_procedures = __esm({
4437
4453
  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."
4438
4454
  },
4439
4455
  {
4440
- title: "Customer orchestration maturity \u2014 recommend, never trap",
4456
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
4441
4457
  domain: "workflow",
4442
4458
  priority: "p1",
4443
4459
  content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
4444
4460
  },
4461
+ {
4462
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
4463
+ domain: "identity",
4464
+ priority: "p0",
4465
+ 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."
4466
+ },
4445
4467
  {
4446
4468
  title: "Single dispatch path \u2014 create_task only",
4447
4469
  domain: "workflow",
@@ -4475,6 +4497,12 @@ var init_platform_procedures = __esm({
4475
4497
  priority: "p0",
4476
4498
  content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
4477
4499
  },
4500
+ {
4501
+ title: "Destructive operations \u2014 mandatory reviewer gate",
4502
+ domain: "security",
4503
+ priority: "p0",
4504
+ content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
4505
+ },
4478
4506
  {
4479
4507
  title: "Customer patch triage \u2014 upstream bug vs customization",
4480
4508
  domain: "support",
@@ -5400,7 +5428,7 @@ async function assertVpsLicense(opts) {
5400
5428
  }
5401
5429
  if (!transientFailure) {
5402
5430
  throw new Error(
5403
- "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
5431
+ "License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
5404
5432
  );
5405
5433
  }
5406
5434
  const fresh = await getCachedLicense();
@@ -5437,7 +5465,7 @@ async function assertVpsLicense(opts) {
5437
5465
  } catch {
5438
5466
  }
5439
5467
  throw new Error(
5440
- `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
5468
+ `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
5441
5469
  );
5442
5470
  }
5443
5471
  function startLicenseRevalidation(intervalMs = 36e5) {
@@ -5469,7 +5497,7 @@ var init_license = __esm({
5469
5497
  LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
5470
5498
  CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
5471
5499
  DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
5472
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
5500
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
5473
5501
  RETRY_DELAY_MS = 500;
5474
5502
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
5475
5503
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -5658,6 +5686,18 @@ function extractRootExe(name) {
5658
5686
  const parts = name.split("-").filter(Boolean);
5659
5687
  return parts.length > 0 ? parts[parts.length - 1] : null;
5660
5688
  }
5689
+ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5690
+ if (!existsSync14(SESSION_CACHE)) {
5691
+ mkdirSync7(SESSION_CACHE, { recursive: true });
5692
+ }
5693
+ const rootExe = extractRootExe(parentExe) ?? parentExe;
5694
+ const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5695
+ writeFileSync6(filePath, JSON.stringify({
5696
+ parentExe: rootExe,
5697
+ dispatchedBy: dispatchedBy || rootExe,
5698
+ registeredAt: (/* @__PURE__ */ new Date()).toISOString()
5699
+ }));
5700
+ }
5661
5701
  function getParentExe(sessionKey) {
5662
5702
  try {
5663
5703
  const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
@@ -5667,11 +5707,12 @@ function getParentExe(sessionKey) {
5667
5707
  }
5668
5708
  }
5669
5709
  function resolveExeSession() {
5710
+ if (process.env.EXE_SESSION_NAME) {
5711
+ const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
5712
+ if (fromEnv) return fromEnv;
5713
+ }
5670
5714
  const mySession = getMySession();
5671
5715
  if (!mySession) {
5672
- if (process.env.EXE_SESSION_NAME) {
5673
- return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
5674
- }
5675
5716
  return null;
5676
5717
  }
5677
5718
  const fromSessionName = extractRootExe(mySession);
@@ -5686,6 +5727,10 @@ function resolveExeSession() {
5686
5727
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
5687
5728
  `
5688
5729
  );
5730
+ try {
5731
+ registerParentExe(key, fromSessionName);
5732
+ } catch {
5733
+ }
5689
5734
  candidate = fromSessionName;
5690
5735
  } else {
5691
5736
  candidate = fromCache;
@@ -6851,6 +6896,27 @@ async function cloudSync(config) {
6851
6896
  if (stmts.length > 0) await client.batch(stmts, "write");
6852
6897
  pulled = pullResult.records.length;
6853
6898
  } else {
6899
+ try {
6900
+ const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
6901
+ if (incomingIds.length > 0) {
6902
+ const ph = incomingIds.map(() => "?").join(",");
6903
+ const existing = await client.execute({
6904
+ sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
6905
+ args: incomingIds
6906
+ });
6907
+ const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
6908
+ for (const rec of pullResult.records) {
6909
+ const local = localMap.get(String(rec.id));
6910
+ if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
6911
+ process.stderr.write(
6912
+ `[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
6913
+ `
6914
+ );
6915
+ }
6916
+ }
6917
+ }
6918
+ } catch {
6919
+ }
6854
6920
  const stmts = pullResult.records.map((rec) => ({
6855
6921
  sql: `INSERT OR REPLACE INTO memories
6856
6922
  (id, agent_id, agent_role, session_id, timestamp,
package/dist/index.js CHANGED
@@ -368,6 +368,7 @@ __export(agent_config_exports, {
368
368
  getAgentRuntime: () => getAgentRuntime,
369
369
  loadAgentConfig: () => loadAgentConfig,
370
370
  saveAgentConfig: () => saveAgentConfig,
371
+ setAgentMcps: () => setAgentMcps,
371
372
  setAgentRuntime: () => setAgentRuntime
372
373
  });
373
374
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
@@ -394,7 +395,7 @@ function getAgentRuntime(agentId) {
394
395
  if (orgDefault) return orgDefault;
395
396
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
396
397
  }
397
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
398
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
398
399
  const knownModels = KNOWN_RUNTIMES[runtime];
399
400
  if (!knownModels) {
400
401
  return {
@@ -409,12 +410,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
409
410
  };
410
411
  }
411
412
  const config2 = loadAgentConfig();
413
+ const existing = config2[agentId];
412
414
  const entry = { runtime, model };
413
415
  if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
416
+ if (mcps !== void 0) {
417
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
418
+ } else if (existing?.mcps) {
419
+ entry.mcps = existing.mcps;
420
+ }
414
421
  config2[agentId] = entry;
415
422
  saveAgentConfig(config2);
416
423
  return { ok: true };
417
424
  }
425
+ function setAgentMcps(agentId, mcps) {
426
+ const config2 = loadAgentConfig();
427
+ const existing = config2[agentId] ?? getAgentRuntime(agentId);
428
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
429
+ config2[agentId] = existing;
430
+ saveAgentConfig(config2);
431
+ return { ok: true };
432
+ }
418
433
  function clearAgentRuntime(agentId) {
419
434
  const config2 = loadAgentConfig();
420
435
  delete config2[agentId];
@@ -4058,6 +4073,22 @@ async function ensureSchema() {
4058
4073
  } catch (e) {
4059
4074
  logCatchDebug("migration", e);
4060
4075
  }
4076
+ try {
4077
+ await client.execute({
4078
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
4079
+ args: []
4080
+ });
4081
+ } catch (e) {
4082
+ logCatchDebug("migration", e);
4083
+ }
4084
+ try {
4085
+ await client.execute({
4086
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
4087
+ args: []
4088
+ });
4089
+ } catch (e) {
4090
+ logCatchDebug("migration", e);
4091
+ }
4061
4092
  }
4062
4093
  async function disposeDatabase() {
4063
4094
  if (_walCheckpointTimer) {
@@ -4476,7 +4507,7 @@ async function assertVpsLicense(opts) {
4476
4507
  }
4477
4508
  if (!transientFailure) {
4478
4509
  throw new Error(
4479
- "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
4510
+ "License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
4480
4511
  );
4481
4512
  }
4482
4513
  const fresh = await getCachedLicense();
@@ -4513,7 +4544,7 @@ async function assertVpsLicense(opts) {
4513
4544
  } catch {
4514
4545
  }
4515
4546
  throw new Error(
4516
- `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
4547
+ `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
4517
4548
  );
4518
4549
  }
4519
4550
  function startLicenseRevalidation(intervalMs = 36e5) {
@@ -4545,7 +4576,7 @@ var init_license = __esm({
4545
4576
  LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
4546
4577
  CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
4547
4578
  DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
4548
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
4579
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
4549
4580
  RETRY_DELAY_MS = 500;
4550
4581
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
4551
4582
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -5089,6 +5120,19 @@ async function resolveTask(client, identifier, scopeSession) {
5089
5120
  args: [identifier, ...scope.args]
5090
5121
  });
5091
5122
  if (result.rows.length === 1) return result.rows[0];
5123
+ if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
5124
+ result = await client.execute({
5125
+ sql: `SELECT * FROM tasks WHERE id LIKE ?`,
5126
+ args: [`${identifier}%`]
5127
+ });
5128
+ if (result.rows.length === 1) return result.rows[0];
5129
+ if (result.rows.length > 1) {
5130
+ const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
5131
+ throw new Error(
5132
+ `Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
5133
+ );
5134
+ }
5135
+ }
5092
5136
  result = await client.execute({
5093
5137
  sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
5094
5138
  args: [`%${identifier}%`, ...scope.args]
@@ -5943,12 +5987,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
5943
5987
  WHERE blocked_by = ? AND status = 'blocked'`,
5944
5988
  args: [now, taskId]
5945
5989
  });
5946
- if (baseDir && unblocked.rowsAffected > 0) {
5947
- const ubScope = sessionScopeFilter();
5948
- const unblockedRows = await client.execute({
5949
- sql: `SELECT task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
5950
- args: [now, ...ubScope.args]
5951
- });
5990
+ if (unblocked.rowsAffected === 0) return;
5991
+ const ubScope = sessionScopeFilter();
5992
+ const unblockedRows = await client.execute({
5993
+ sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
5994
+ args: [now, ...ubScope.args]
5995
+ });
5996
+ if (baseDir) {
5952
5997
  for (const ur of unblockedRows.rows) {
5953
5998
  try {
5954
5999
  const ubFile = path17.join(baseDir, String(ur.task_file));
@@ -5960,6 +6005,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
5960
6005
  }
5961
6006
  }
5962
6007
  }
6008
+ if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
6009
+ try {
6010
+ const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
6011
+ const dispatched = /* @__PURE__ */ new Set();
6012
+ for (const ur of unblockedRows.rows) {
6013
+ const assignee = String(ur.assigned_to);
6014
+ if (dispatched.has(assignee)) continue;
6015
+ dispatched.add(assignee);
6016
+ queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
6017
+ }
6018
+ } catch {
6019
+ }
6020
+ }
5963
6021
  }
5964
6022
  async function findNextTask(assignedTo) {
5965
6023
  const client = getClient();
@@ -6176,6 +6234,15 @@ __export(behaviors_exports, {
6176
6234
  });
6177
6235
  import crypto5 from "crypto";
6178
6236
  async function storeBehavior(opts) {
6237
+ try {
6238
+ const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
6239
+ const roster = loadEmployeesSync2();
6240
+ if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
6241
+ throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
6242
+ }
6243
+ } catch (e) {
6244
+ if (e instanceof Error && e.message.includes("not found in roster")) throw e;
6245
+ }
6179
6246
  const client = getClient();
6180
6247
  const id = crypto5.randomUUID();
6181
6248
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -6699,6 +6766,12 @@ async function updateTask(input) {
6699
6766
  }
6700
6767
  }
6701
6768
  }
6769
+ if (input.status === "cancelled") {
6770
+ try {
6771
+ await cascadeUnblock(taskId, input.baseDir, now);
6772
+ } catch {
6773
+ }
6774
+ }
6702
6775
  if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
6703
6776
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
6704
6777
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
@@ -7230,11 +7303,12 @@ function getDispatchedBy(sessionKey) {
7230
7303
  }
7231
7304
  }
7232
7305
  function resolveExeSession() {
7306
+ if (process.env.EXE_SESSION_NAME) {
7307
+ const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
7308
+ if (fromEnv) return fromEnv;
7309
+ }
7233
7310
  const mySession = getMySession();
7234
7311
  if (!mySession) {
7235
- if (process.env.EXE_SESSION_NAME) {
7236
- return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
7237
- }
7238
7312
  return null;
7239
7313
  }
7240
7314
  const fromSessionName = extractRootExe(mySession);
@@ -7249,6 +7323,10 @@ function resolveExeSession() {
7249
7323
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
7250
7324
  `
7251
7325
  );
7326
+ try {
7327
+ registerParentExe(key, fromSessionName);
7328
+ } catch {
7329
+ }
7252
7330
  candidate = fromSessionName;
7253
7331
  } else {
7254
7332
  candidate = fromCache;
@@ -8943,11 +9021,17 @@ var init_platform_procedures = __esm({
8943
9021
  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."
8944
9022
  },
8945
9023
  {
8946
- title: "Customer orchestration maturity \u2014 recommend, never trap",
9024
+ title: "Orchestration phase guidance \u2014 recommend, never trap",
8947
9025
  domain: "workflow",
8948
9026
  priority: "p1",
8949
9027
  content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
8950
9028
  },
9029
+ {
9030
+ title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
9031
+ domain: "identity",
9032
+ priority: "p0",
9033
+ 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."
9034
+ },
8951
9035
  {
8952
9036
  title: "Single dispatch path \u2014 create_task only",
8953
9037
  domain: "workflow",
@@ -8981,6 +9065,12 @@ var init_platform_procedures = __esm({
8981
9065
  priority: "p0",
8982
9066
  content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
8983
9067
  },
9068
+ {
9069
+ title: "Destructive operations \u2014 mandatory reviewer gate",
9070
+ domain: "security",
9071
+ priority: "p0",
9072
+ content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
9073
+ },
8984
9074
  {
8985
9075
  title: "Customer patch triage \u2014 upstream bug vs customization",
8986
9076
  domain: "support",
@@ -9266,10 +9356,24 @@ function stableId(memoryId, type, content) {
9266
9356
  return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
9267
9357
  }
9268
9358
  function cleanText(text) {
9269
- return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
9359
+ let cleaned = text.replace(
9360
+ /```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
9361
+ (_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
9362
+ );
9363
+ cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
9364
+ return cleaned;
9270
9365
  }
9271
- function splitSentences(text) {
9272
- return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
9366
+ function splitSegments(text) {
9367
+ const cleaned = cleanText(text);
9368
+ const segments = cleaned.split(/(?<=[.!?:;])\s+|\n{2,}|(?<=\))\s+(?=[A-Z])|\s*[|│]\s*/).map((s) => s.trim()).filter((s) => s.length >= MIN_SEGMENT_CHARS && s.length <= MAX_SEGMENT_CHARS);
9369
+ if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
9370
+ const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
9371
+ if (lines.length > 0) return lines;
9372
+ if (cleaned.length >= MIN_SEGMENT_CHARS) {
9373
+ return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
9374
+ }
9375
+ }
9376
+ return segments;
9273
9377
  }
9274
9378
  function inferCardType(sentence, toolName) {
9275
9379
  const lower = sentence.toLowerCase();
@@ -9301,12 +9405,12 @@ function predicateFor(type) {
9301
9405
  }
9302
9406
  }
9303
9407
  function extractMemoryCards(row) {
9304
- const sentences = splitSentences(row.raw_text);
9408
+ const segments = splitSegments(row.raw_text);
9305
9409
  const cards = [];
9306
- for (const sentence of sentences) {
9410
+ for (const sentence of segments) {
9307
9411
  const type = inferCardType(sentence, row.tool_name);
9308
9412
  const subject = extractSubject(sentence, row.agent_id);
9309
- const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
9413
+ const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
9310
9414
  cards.push({
9311
9415
  id: stableId(row.id, type, content),
9312
9416
  memory_id: row.id,
@@ -9402,13 +9506,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
9402
9506
  last_accessed: String(row.timestamp)
9403
9507
  }));
9404
9508
  }
9405
- var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
9509
+ var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
9406
9510
  var init_memory_cards = __esm({
9407
9511
  "src/lib/memory-cards.ts"() {
9408
9512
  "use strict";
9409
9513
  init_database();
9410
- MAX_CARDS_PER_MEMORY = 6;
9411
- MAX_SENTENCE_CHARS = 360;
9514
+ MAX_CARDS_PER_MEMORY = 8;
9515
+ MAX_SEGMENT_CHARS = 500;
9516
+ MIN_SEGMENT_CHARS = 20;
9412
9517
  }
9413
9518
  });
9414
9519
 
@@ -175,7 +175,7 @@ function getAgentRuntime(agentId) {
175
175
  if (orgDefault) return orgDefault;
176
176
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
177
177
  }
178
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
178
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
179
179
  const knownModels = KNOWN_RUNTIMES[runtime];
180
180
  if (!knownModels) {
181
181
  return {
@@ -190,12 +190,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
190
190
  };
191
191
  }
192
192
  const config = loadAgentConfig();
193
+ const existing = config[agentId];
193
194
  const entry = { runtime, model };
194
195
  if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
196
+ if (mcps !== void 0) {
197
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
198
+ } else if (existing?.mcps) {
199
+ entry.mcps = existing.mcps;
200
+ }
195
201
  config[agentId] = entry;
196
202
  saveAgentConfig(config);
197
203
  return { ok: true };
198
204
  }
205
+ function setAgentMcps(agentId, mcps) {
206
+ const config = loadAgentConfig();
207
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
208
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
209
+ config[agentId] = existing;
210
+ saveAgentConfig(config);
211
+ return { ok: true };
212
+ }
199
213
  function clearAgentRuntime(agentId) {
200
214
  const config = loadAgentConfig();
201
215
  delete config[agentId];
@@ -210,5 +224,6 @@ export {
210
224
  getAgentRuntime,
211
225
  loadAgentConfig,
212
226
  saveAgentConfig,
227
+ setAgentMcps,
213
228
  setAgentRuntime
214
229
  };
@@ -2972,6 +2972,22 @@ async function ensureSchema() {
2972
2972
  } catch (e) {
2973
2973
  logCatchDebug("migration", e);
2974
2974
  }
2975
+ try {
2976
+ await client.execute({
2977
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
2978
+ args: []
2979
+ });
2980
+ } catch (e) {
2981
+ logCatchDebug("migration", e);
2982
+ }
2983
+ try {
2984
+ await client.execute({
2985
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
2986
+ args: []
2987
+ });
2988
+ } catch (e) {
2989
+ logCatchDebug("migration", e);
2990
+ }
2975
2991
  }
2976
2992
  async function disposeDatabase() {
2977
2993
  if (_walCheckpointTimer) {
@@ -3942,7 +3958,7 @@ import { jwtVerify, importSPKI } from "jose";
3942
3958
  var LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
3943
3959
  var CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
3944
3960
  var DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
3945
- var API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3961
+ var API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
3946
3962
  function loadDeviceId() {
3947
3963
  const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
3948
3964
  try {
@@ -4367,6 +4383,27 @@ async function cloudSync(config) {
4367
4383
  if (stmts.length > 0) await client.batch(stmts, "write");
4368
4384
  pulled = pullResult.records.length;
4369
4385
  } else {
4386
+ try {
4387
+ const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
4388
+ if (incomingIds.length > 0) {
4389
+ const ph = incomingIds.map(() => "?").join(",");
4390
+ const existing = await client.execute({
4391
+ sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
4392
+ args: incomingIds
4393
+ });
4394
+ const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
4395
+ for (const rec of pullResult.records) {
4396
+ const local = localMap.get(String(rec.id));
4397
+ if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
4398
+ process.stderr.write(
4399
+ `[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
4400
+ `
4401
+ );
4402
+ }
4403
+ }
4404
+ }
4405
+ } catch {
4406
+ }
4370
4407
  const stmts = pullResult.records.map((rec) => ({
4371
4408
  sql: `INSERT OR REPLACE INTO memories
4372
4409
  (id, agent_id, agent_role, session_id, timestamp,
@@ -184,6 +184,7 @@ __export(agent_config_exports, {
184
184
  getAgentRuntime: () => getAgentRuntime,
185
185
  loadAgentConfig: () => loadAgentConfig,
186
186
  saveAgentConfig: () => saveAgentConfig,
187
+ setAgentMcps: () => setAgentMcps,
187
188
  setAgentRuntime: () => setAgentRuntime
188
189
  });
189
190
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
@@ -210,7 +211,7 @@ function getAgentRuntime(agentId) {
210
211
  if (orgDefault) return orgDefault;
211
212
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
212
213
  }
213
- function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
214
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
214
215
  const knownModels = KNOWN_RUNTIMES[runtime];
215
216
  if (!knownModels) {
216
217
  return {
@@ -225,12 +226,26 @@ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
225
226
  };
226
227
  }
227
228
  const config = loadAgentConfig();
229
+ const existing = config[agentId];
228
230
  const entry = { runtime, model };
229
231
  if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
232
+ if (mcps !== void 0) {
233
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
234
+ } else if (existing?.mcps) {
235
+ entry.mcps = existing.mcps;
236
+ }
230
237
  config[agentId] = entry;
231
238
  saveAgentConfig(config);
232
239
  return { ok: true };
233
240
  }
241
+ function setAgentMcps(agentId, mcps) {
242
+ const config = loadAgentConfig();
243
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
244
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
245
+ config[agentId] = existing;
246
+ saveAgentConfig(config);
247
+ return { ok: true };
248
+ }
234
249
  function clearAgentRuntime(agentId) {
235
250
  const config = loadAgentConfig();
236
251
  delete config[agentId];
@@ -2885,6 +2885,22 @@ async function ensureSchema() {
2885
2885
  } catch (e) {
2886
2886
  logCatchDebug("migration", e);
2887
2887
  }
2888
+ try {
2889
+ await client.execute({
2890
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
2891
+ args: []
2892
+ });
2893
+ } catch (e) {
2894
+ logCatchDebug("migration", e);
2895
+ }
2896
+ try {
2897
+ await client.execute({
2898
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
2899
+ args: []
2900
+ });
2901
+ } catch (e) {
2902
+ logCatchDebug("migration", e);
2903
+ }
2888
2904
  }
2889
2905
  var disposeTurso = disposeDatabase;
2890
2906
  async function disposeDatabase() {
package/dist/lib/db.js CHANGED
@@ -2885,6 +2885,22 @@ async function ensureSchema() {
2885
2885
  } catch (e) {
2886
2886
  logCatchDebug("migration", e);
2887
2887
  }
2888
+ try {
2889
+ await client.execute({
2890
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
2891
+ args: []
2892
+ });
2893
+ } catch (e) {
2894
+ logCatchDebug("migration", e);
2895
+ }
2896
+ try {
2897
+ await client.execute({
2898
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
2899
+ args: []
2900
+ });
2901
+ } catch (e) {
2902
+ logCatchDebug("migration", e);
2903
+ }
2888
2904
  }
2889
2905
  var disposeTurso = disposeDatabase;
2890
2906
  async function disposeDatabase() {
@@ -2911,6 +2911,22 @@ async function ensureSchema() {
2911
2911
  } catch (e) {
2912
2912
  logCatchDebug("migration", e);
2913
2913
  }
2914
+ try {
2915
+ await client.execute({
2916
+ sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
2917
+ args: []
2918
+ });
2919
+ } catch (e) {
2920
+ logCatchDebug("migration", e);
2921
+ }
2922
+ try {
2923
+ await client.execute({
2924
+ sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
2925
+ args: []
2926
+ });
2927
+ } catch (e) {
2928
+ logCatchDebug("migration", e);
2929
+ }
2914
2930
  }
2915
2931
  async function disposeDatabase() {
2916
2932
  if (_walCheckpointTimer) {