@askexenow/exe-os 0.9.93 → 0.9.95

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 (88) hide show
  1. package/deploy/compose/docker-compose.yml +1 -0
  2. package/dist/bin/agentic-ontology-backfill.js +65 -8
  3. package/dist/bin/agentic-reflection-backfill.js +54 -3
  4. package/dist/bin/agentic-semantic-label.js +54 -3
  5. package/dist/bin/backfill-conversations.js +69 -9
  6. package/dist/bin/backfill-responses.js +69 -9
  7. package/dist/bin/backfill-vectors.js +54 -3
  8. package/dist/bin/bulk-sync-postgres.js +66 -8
  9. package/dist/bin/cleanup-stale-review-tasks.js +121 -13
  10. package/dist/bin/cli.js +1561 -466
  11. package/dist/bin/customer-readiness.js +61 -0
  12. package/dist/bin/exe-agent.js +17 -3
  13. package/dist/bin/exe-assign.js +75 -9
  14. package/dist/bin/exe-boot.js +114 -12
  15. package/dist/bin/exe-call.js +17 -3
  16. package/dist/bin/exe-cloud.js +76 -10
  17. package/dist/bin/exe-dispatch.js +136 -18
  18. package/dist/bin/exe-doctor.js +75 -9
  19. package/dist/bin/exe-export-behaviors.js +75 -9
  20. package/dist/bin/exe-forget.js +94 -9
  21. package/dist/bin/exe-gateway.js +135 -18
  22. package/dist/bin/exe-heartbeat.js +121 -13
  23. package/dist/bin/exe-kill.js +75 -9
  24. package/dist/bin/exe-launch-agent.js +75 -9
  25. package/dist/bin/exe-new-employee.js +18 -4
  26. package/dist/bin/exe-pending-messages.js +121 -13
  27. package/dist/bin/exe-pending-notifications.js +121 -13
  28. package/dist/bin/exe-pending-reviews.js +121 -13
  29. package/dist/bin/exe-rename.js +75 -9
  30. package/dist/bin/exe-review.js +75 -9
  31. package/dist/bin/exe-search.js +100 -9
  32. package/dist/bin/exe-session-cleanup.js +136 -18
  33. package/dist/bin/exe-settings.js +1 -0
  34. package/dist/bin/exe-start-codex.js +65 -8
  35. package/dist/bin/exe-start-opencode.js +65 -8
  36. package/dist/bin/exe-status.js +121 -13
  37. package/dist/bin/exe-support.js +1 -0
  38. package/dist/bin/exe-team.js +75 -9
  39. package/dist/bin/git-sweep.js +136 -18
  40. package/dist/bin/graph-backfill.js +65 -8
  41. package/dist/bin/graph-export.js +75 -9
  42. package/dist/bin/intercom-check.js +136 -18
  43. package/dist/bin/scan-tasks.js +136 -18
  44. package/dist/bin/setup.js +55 -4
  45. package/dist/bin/shard-migrate.js +65 -8
  46. package/dist/bin/stack-update.js +5 -6
  47. package/dist/bin/update.js +1 -1
  48. package/dist/gateway/index.js +136 -18
  49. package/dist/hooks/bug-report-worker.js +136 -18
  50. package/dist/hooks/codex-stop-task-finalizer.js +126 -14
  51. package/dist/hooks/commit-complete.js +136 -18
  52. package/dist/hooks/error-recall.js +100 -9
  53. package/dist/hooks/ingest.js +75 -9
  54. package/dist/hooks/instructions-loaded.js +75 -9
  55. package/dist/hooks/notification.js +75 -9
  56. package/dist/hooks/post-compact.js +313 -50
  57. package/dist/hooks/post-tool-combined.js +436 -13
  58. package/dist/hooks/pre-compact.js +136 -18
  59. package/dist/hooks/pre-tool-use.js +121 -13
  60. package/dist/hooks/prompt-submit.js +194 -19
  61. package/dist/hooks/session-end.js +136 -18
  62. package/dist/hooks/session-start.js +146 -13
  63. package/dist/hooks/stop.js +121 -13
  64. package/dist/hooks/subagent-stop.js +121 -13
  65. package/dist/hooks/summary-worker.js +99 -7
  66. package/dist/index.js +136 -18
  67. package/dist/lib/cloud-sync.js +38 -0
  68. package/dist/lib/consolidation.js +3 -1
  69. package/dist/lib/database.js +37 -0
  70. package/dist/lib/db.js +37 -0
  71. package/dist/lib/device-registry.js +37 -0
  72. package/dist/lib/employee-templates.js +17 -3
  73. package/dist/lib/exe-daemon.js +916 -42
  74. package/dist/lib/hybrid-search.js +100 -9
  75. package/dist/lib/license.js +1 -1
  76. package/dist/lib/messaging.js +43 -4
  77. package/dist/lib/schedules.js +54 -3
  78. package/dist/lib/store.js +75 -9
  79. package/dist/lib/tasks.js +61 -9
  80. package/dist/lib/tmux-routing.js +61 -9
  81. package/dist/mcp/server.js +878 -42
  82. package/dist/mcp/tools/create-task.js +70 -12
  83. package/dist/mcp/tools/list-tasks.js +49 -5
  84. package/dist/mcp/tools/send-message.js +43 -4
  85. package/dist/mcp/tools/update-task.js +61 -9
  86. package/dist/runtime/index.js +136 -18
  87. package/dist/tui/App.js +135 -18
  88. package/package.json +1 -1
@@ -3069,6 +3069,20 @@ async function ensureSchema() {
3069
3069
  });
3070
3070
  } catch {
3071
3071
  }
3072
+ try {
3073
+ await client.execute({
3074
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
3075
+ args: []
3076
+ });
3077
+ } catch {
3078
+ }
3079
+ try {
3080
+ await client.execute({
3081
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
3082
+ args: []
3083
+ });
3084
+ } catch {
3085
+ }
3072
3086
  await client.executeMultiple(`
3073
3087
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
3074
3088
  content_text,
@@ -3320,6 +3334,22 @@ async function ensureSchema() {
3320
3334
  );
3321
3335
  } catch {
3322
3336
  }
3337
+ for (const col of [
3338
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3339
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3340
+ ]) {
3341
+ try {
3342
+ await client.execute(col);
3343
+ } catch {
3344
+ }
3345
+ }
3346
+ try {
3347
+ await client.execute({
3348
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3349
+ args: []
3350
+ });
3351
+ } catch {
3352
+ }
3323
3353
  try {
3324
3354
  await client.execute({
3325
3355
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3362,6 +3392,13 @@ async function ensureSchema() {
3362
3392
  } catch {
3363
3393
  }
3364
3394
  }
3395
+ try {
3396
+ await client.execute({
3397
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3398
+ args: []
3399
+ });
3400
+ } catch {
3401
+ }
3365
3402
  try {
3366
3403
  await client.execute({
3367
3404
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -3422,7 +3459,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3422
3459
  import os7 from "os";
3423
3460
  import path10 from "path";
3424
3461
  import { jwtVerify, importSPKI } from "jose";
3425
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
3462
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE;
3426
3463
  var init_license = __esm({
3427
3464
  "src/lib/license.ts"() {
3428
3465
  "use strict";
@@ -3430,6 +3467,7 @@ var init_license = __esm({
3430
3467
  LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
3431
3468
  CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3432
3469
  DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
3470
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3433
3471
  }
3434
3472
  });
3435
3473
 
@@ -3473,6 +3511,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
3473
3511
  function getMySession() {
3474
3512
  return getTransport().getMySession();
3475
3513
  }
3514
+ function isRootSession(name) {
3515
+ return name.length > 0 && !name.includes("-");
3516
+ }
3476
3517
  function extractRootExe(name) {
3477
3518
  if (!name) return null;
3478
3519
  if (!name.includes("-")) return name;
@@ -3488,9 +3529,13 @@ function getParentExe(sessionKey) {
3488
3529
  }
3489
3530
  }
3490
3531
  function resolveExeSession() {
3532
+ if (process.env.EXE_SESSION_NAME) {
3533
+ return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
3534
+ }
3491
3535
  const mySession = getMySession();
3492
3536
  if (!mySession) return null;
3493
3537
  const fromSessionName = extractRootExe(mySession);
3538
+ let candidate = null;
3494
3539
  try {
3495
3540
  const key = getSessionKey();
3496
3541
  const parentExe = getParentExe(key);
@@ -3501,13 +3546,47 @@ function resolveExeSession() {
3501
3546
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3502
3547
  `
3503
3548
  );
3504
- return fromSessionName;
3549
+ candidate = fromSessionName;
3550
+ } else {
3551
+ candidate = fromCache;
3505
3552
  }
3506
- return fromCache;
3507
3553
  }
3508
3554
  } catch {
3509
3555
  }
3510
- return fromSessionName ?? mySession;
3556
+ if (!candidate) {
3557
+ candidate = fromSessionName ?? mySession;
3558
+ }
3559
+ if (candidate && isRootSession(candidate)) {
3560
+ try {
3561
+ const transport = getTransport();
3562
+ const liveSessions = transport.listSessions();
3563
+ if (!liveSessions.includes(candidate)) {
3564
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
3565
+ if (liveRoots.length === 1) {
3566
+ process.stderr.write(
3567
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
3568
+ `
3569
+ );
3570
+ return liveRoots[0];
3571
+ } else if (liveRoots.length > 1) {
3572
+ const base = candidate.replace(/\d+$/, "");
3573
+ const match = liveRoots.find((s) => s.startsWith(base));
3574
+ const chosen = match ?? liveRoots[0];
3575
+ process.stderr.write(
3576
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
3577
+ `
3578
+ );
3579
+ return chosen;
3580
+ }
3581
+ process.stderr.write(
3582
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
3583
+ `
3584
+ );
3585
+ }
3586
+ } catch {
3587
+ }
3588
+ }
3589
+ return candidate;
3511
3590
  }
3512
3591
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
3513
3592
  var init_tmux_routing = __esm({
@@ -4643,6 +4722,20 @@ var init_platform_procedures = __esm({
4643
4722
  priority: "p1",
4644
4723
  content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
4645
4724
  },
4725
+ // --- Tool guidance ---
4726
+ {
4727
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4728
+ domain: "tools",
4729
+ priority: "p2",
4730
+ content: "The company_actions tool executes business actions through gateway connectors (e.g. send WhatsApp, trigger workflows, update CRM). It routes through the exe-gateway on the VPS. Actions are defined by the customer's gateway configuration \u2014 each connector (WhatsApp, Shopify, email, etc.) exposes specific actions. Use query_company_brain to find available data first, then company_actions to act on it. Requires gateway auth token. Read-only founders should NOT use this \u2014 it mutates external state."
4731
+ },
4732
+ // --- Release awareness ---
4733
+ {
4734
+ title: "What's New check \u2014 surface new features after update",
4735
+ domain: "support",
4736
+ priority: "p1",
4737
+ content: "Once per session (COO boot only, never repeat), check if the installed exe-os version is newer than the last session. If it is, read the bundled release-notes.json (at the package root) and surface a brief summary to the founder: 'Updated to exe-os vX.Y.Z \u2014 N new features, M fixes.' List the top 3 features by name. This helps the founder know what they got from the update. If release-notes.json doesn't exist or the version hasn't changed, skip silently. Never repeat this check in the same session."
4738
+ },
4646
4739
  // --- Platform vs Customer ownership ---
4647
4740
  {
4648
4741
  title: "What the platform provides vs what you customize",
@@ -4731,13 +4824,13 @@ var init_platform_procedures = __esm({
4731
4824
  title: "MCP tools \u2014 memory, decision, and search",
4732
4825
  domain: "tool-use",
4733
4826
  priority: "p1",
4734
- content: `memory(action="recall") / recall_my_memory: search your own memories (semantic + FTS). memory(action="ask_team") / ask_team_memory: search a colleague's memories by agent name. memory(action="store") / store_memory: persist a memory. memory(action="commit") / commit_memory: high-importance memory that survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal memory window. Requires session_id + target_timestamp. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
4827
+ content: `memory(action="recall") / recall_my_memory: search memories (semantic + FTS). Params: as_of (bi-temporal \u2014 what did I know at time X?), kind (decision|procedure|observation|raw|conversation|behavior), retrieval_mode (all|decisions_only|procedures_only|operational|recent_high_value). memory(action="ask_team") / ask_team_memory: search a colleague's memories. memory(action="store") / store_memory: persist a memory. Params: kind, procedure_for (domain tag for procedures). memory(action="commit") / commit_memory: high-importance, survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal window. Requires session_id + target_timestamp. memory(action="get_by_id"): fetch one memory by UUID with full untruncated text. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. memory(action="supersede"): replace an old memory with a new version (old_id + new text). decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
4735
4828
  },
4736
4829
  {
4737
4830
  title: "MCP tools \u2014 task orchestration",
4738
4831
  domain: "tool-use",
4739
4832
  priority: "p1",
4740
- content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
4833
+ content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. Params: blocked_by (task ID for dependency), parent_task_id (subtask hierarchy), reviewer, complexity (routine|standard|complex|critical), budget_tokens (max token cap), budget_fallback_model, spawn_runtime (override runtime: claude|codex|opencode), spawn_model (override model). task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
4741
4834
  },
4742
4835
  {
4743
4836
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4767,7 +4860,7 @@ var init_platform_procedures = __esm({
4767
4860
  title: "MCP tools \u2014 admin, config, and operations",
4768
4861
  domain: "tool-use",
4769
4862
  priority: "p1",
4770
- content: 'config(action="list_employees"): view roster. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. mcp_ping(): daemon health + license status + tool usage stats.'
4863
+ content: 'config(action="list_employees"): view roster. config(action="set_agent_config"): view or change per-agent runtime + model. Call with no args to show all agents. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. Supports cloud_action param: status|sync|reupload. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="worker_gate"): check spawn slot availability \u2014 alive/stale/reserved counts vs max. Use before dispatching. config(action="auto_wake_status"): orphaned tasks, blocked tasks, auto-wake retry status. config(action="orchestration_phase"): view/change org phase (phase_1_coo|phase_2_executives|phase_3_parallel_org). config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. diagnostics(action="pending_work_summary"): pending reviews + messages + notifications in one call. diagnostics(action="rename_employee"): rename an agent across all systems (roster, identity, DB, symlinks). diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role. mcp_ping(): daemon health + license status + tool usage stats.'
4771
4864
  }
4772
4865
  ];
4773
4866
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5451,6 +5544,8 @@ async function writeMemory(record) {
5451
5544
  source_type: record.source_type ?? null,
5452
5545
  tier: record.tier ?? classifyTier(record),
5453
5546
  supersedes_id: record.supersedes_id ?? null,
5547
+ valid_from: record.valid_from ?? record.timestamp,
5548
+ invalid_at: record.invalid_at ?? null,
5454
5549
  draft: record.draft ? 1 : 0,
5455
5550
  memory_type: memoryType,
5456
5551
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5469,7 +5564,8 @@ async function writeMemory(record) {
5469
5564
  token_cost: record.token_cost ?? null,
5470
5565
  audience: record.audience ?? null,
5471
5566
  language_type: record.language_type ?? inferLanguageType(record),
5472
- parent_memory_id: record.parent_memory_id ?? null
5567
+ parent_memory_id: record.parent_memory_id ?? null,
5568
+ procedure_for: record.procedure_for ?? null
5473
5569
  };
5474
5570
  _pendingRecords.push(dbRow);
5475
5571
  orgBus.emit({
@@ -5524,6 +5620,8 @@ async function flushBatch() {
5524
5620
  const sourceType = row.source_type ?? null;
5525
5621
  const tier = row.tier ?? 3;
5526
5622
  const supersedesId = row.supersedes_id ?? null;
5623
+ const validFrom = row.valid_from ?? row.timestamp;
5624
+ const invalidAt = row.invalid_at ?? null;
5527
5625
  const draft = row.draft ? 1 : 0;
5528
5626
  const memoryType = row.memory_type ?? "raw";
5529
5627
  const trajectory = row.trajectory ?? null;
@@ -5543,15 +5641,16 @@ async function flushBatch() {
5543
5641
  const audience = row.audience ?? null;
5544
5642
  const languageType = row.language_type ?? null;
5545
5643
  const parentMemoryId = row.parent_memory_id ?? null;
5644
+ const procedureFor = row.procedure_for ?? null;
5546
5645
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5547
5646
  tool_name, project_name,
5548
5647
  has_error, raw_text, vector, version, task_id, importance, status,
5549
5648
  confidence, last_accessed,
5550
5649
  workspace_id, document_id, user_id, char_offset, page_number,
5551
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5650
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5552
5651
  intent, outcome, domain, referenced_entities, retrieval_count,
5553
5652
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5554
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5653
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5555
5654
  const metaArgs = [
5556
5655
  intent,
5557
5656
  outcome,
@@ -5567,7 +5666,8 @@ async function flushBatch() {
5567
5666
  tokenCost,
5568
5667
  audience,
5569
5668
  languageType,
5570
- parentMemoryId
5669
+ parentMemoryId,
5670
+ procedureFor
5571
5671
  ];
5572
5672
  const baseArgs = [
5573
5673
  row.id,
@@ -5596,6 +5696,8 @@ async function flushBatch() {
5596
5696
  sourceType,
5597
5697
  tier,
5598
5698
  supersedesId,
5699
+ validFrom,
5700
+ invalidAt,
5599
5701
  draft,
5600
5702
  memoryType,
5601
5703
  trajectory,
@@ -5603,8 +5705,8 @@ async function flushBatch() {
5603
5705
  ];
5604
5706
  return {
5605
5707
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5606
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5607
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5708
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5709
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5608
5710
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5609
5711
  };
5610
5712
  };
@@ -5720,6 +5822,12 @@ async function searchMemories(queryVector, agentId, options) {
5720
5822
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5721
5823
  AND COALESCE(confidence, 0.7) >= 0.3`;
5722
5824
  const args = [agentId];
5825
+ if (options?.asOf) {
5826
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5827
+ args.push(options.asOf, options.asOf);
5828
+ } else {
5829
+ sql += ` AND invalid_at IS NULL`;
5830
+ }
5723
5831
  const scope = buildWikiScopeFilter(options, "");
5724
5832
  sql += scope.clause;
5725
5833
  args.push(...scope.args);
@@ -2836,6 +2836,20 @@ async function ensureSchema() {
2836
2836
  });
2837
2837
  } catch {
2838
2838
  }
2839
+ try {
2840
+ await client.execute({
2841
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2842
+ args: []
2843
+ });
2844
+ } catch {
2845
+ }
2846
+ try {
2847
+ await client.execute({
2848
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2849
+ args: []
2850
+ });
2851
+ } catch {
2852
+ }
2839
2853
  await client.executeMultiple(`
2840
2854
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2841
2855
  content_text,
@@ -3087,6 +3101,22 @@ async function ensureSchema() {
3087
3101
  );
3088
3102
  } catch {
3089
3103
  }
3104
+ for (const col of [
3105
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3106
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3107
+ ]) {
3108
+ try {
3109
+ await client.execute(col);
3110
+ } catch {
3111
+ }
3112
+ }
3113
+ try {
3114
+ await client.execute({
3115
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3116
+ args: []
3117
+ });
3118
+ } catch {
3119
+ }
3090
3120
  try {
3091
3121
  await client.execute({
3092
3122
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3129,6 +3159,13 @@ async function ensureSchema() {
3129
3159
  } catch {
3130
3160
  }
3131
3161
  }
3162
+ try {
3163
+ await client.execute({
3164
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3165
+ args: []
3166
+ });
3167
+ } catch {
3168
+ }
3132
3169
  try {
3133
3170
  await client.execute({
3134
3171
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -4242,6 +4279,20 @@ var init_platform_procedures = __esm({
4242
4279
  priority: "p1",
4243
4280
  content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
4244
4281
  },
4282
+ // --- Tool guidance ---
4283
+ {
4284
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4285
+ domain: "tools",
4286
+ priority: "p2",
4287
+ content: "The company_actions tool executes business actions through gateway connectors (e.g. send WhatsApp, trigger workflows, update CRM). It routes through the exe-gateway on the VPS. Actions are defined by the customer's gateway configuration \u2014 each connector (WhatsApp, Shopify, email, etc.) exposes specific actions. Use query_company_brain to find available data first, then company_actions to act on it. Requires gateway auth token. Read-only founders should NOT use this \u2014 it mutates external state."
4288
+ },
4289
+ // --- Release awareness ---
4290
+ {
4291
+ title: "What's New check \u2014 surface new features after update",
4292
+ domain: "support",
4293
+ priority: "p1",
4294
+ content: "Once per session (COO boot only, never repeat), check if the installed exe-os version is newer than the last session. If it is, read the bundled release-notes.json (at the package root) and surface a brief summary to the founder: 'Updated to exe-os vX.Y.Z \u2014 N new features, M fixes.' List the top 3 features by name. This helps the founder know what they got from the update. If release-notes.json doesn't exist or the version hasn't changed, skip silently. Never repeat this check in the same session."
4295
+ },
4245
4296
  // --- Platform vs Customer ownership ---
4246
4297
  {
4247
4298
  title: "What the platform provides vs what you customize",
@@ -4330,13 +4381,13 @@ var init_platform_procedures = __esm({
4330
4381
  title: "MCP tools \u2014 memory, decision, and search",
4331
4382
  domain: "tool-use",
4332
4383
  priority: "p1",
4333
- content: `memory(action="recall") / recall_my_memory: search your own memories (semantic + FTS). memory(action="ask_team") / ask_team_memory: search a colleague's memories by agent name. memory(action="store") / store_memory: persist a memory. memory(action="commit") / commit_memory: high-importance memory that survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal memory window. Requires session_id + target_timestamp. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
4384
+ content: `memory(action="recall") / recall_my_memory: search memories (semantic + FTS). Params: as_of (bi-temporal \u2014 what did I know at time X?), kind (decision|procedure|observation|raw|conversation|behavior), retrieval_mode (all|decisions_only|procedures_only|operational|recent_high_value). memory(action="ask_team") / ask_team_memory: search a colleague's memories. memory(action="store") / store_memory: persist a memory. Params: kind, procedure_for (domain tag for procedures). memory(action="commit") / commit_memory: high-importance, survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal window. Requires session_id + target_timestamp. memory(action="get_by_id"): fetch one memory by UUID with full untruncated text. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. memory(action="supersede"): replace an old memory with a new version (old_id + new text). decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
4334
4385
  },
4335
4386
  {
4336
4387
  title: "MCP tools \u2014 task orchestration",
4337
4388
  domain: "tool-use",
4338
4389
  priority: "p1",
4339
- content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
4390
+ content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. Params: blocked_by (task ID for dependency), parent_task_id (subtask hierarchy), reviewer, complexity (routine|standard|complex|critical), budget_tokens (max token cap), budget_fallback_model, spawn_runtime (override runtime: claude|codex|opencode), spawn_model (override model). task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
4340
4391
  },
4341
4392
  {
4342
4393
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4366,7 +4417,7 @@ var init_platform_procedures = __esm({
4366
4417
  title: "MCP tools \u2014 admin, config, and operations",
4367
4418
  domain: "tool-use",
4368
4419
  priority: "p1",
4369
- content: 'config(action="list_employees"): view roster. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. mcp_ping(): daemon health + license status + tool usage stats.'
4420
+ content: 'config(action="list_employees"): view roster. config(action="set_agent_config"): view or change per-agent runtime + model. Call with no args to show all agents. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. Supports cloud_action param: status|sync|reupload. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="worker_gate"): check spawn slot availability \u2014 alive/stale/reserved counts vs max. Use before dispatching. config(action="auto_wake_status"): orphaned tasks, blocked tasks, auto-wake retry status. config(action="orchestration_phase"): view/change org phase (phase_1_coo|phase_2_executives|phase_3_parallel_org). config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. diagnostics(action="pending_work_summary"): pending reviews + messages + notifications in one call. diagnostics(action="rename_employee"): rename an agent across all systems (roster, identity, DB, symlinks). diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role. mcp_ping(): daemon health + license status + tool usage stats.'
4370
4421
  }
4371
4422
  ];
4372
4423
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5167,7 +5218,7 @@ var init_license = __esm({
5167
5218
  LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
5168
5219
  CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
5169
5220
  DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
5170
- API_BASE = "https://askexe.com/cloud";
5221
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
5171
5222
  RETRY_DELAY_MS = 500;
5172
5223
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
5173
5224
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -5347,6 +5398,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
5347
5398
  function getMySession() {
5348
5399
  return getTransport().getMySession();
5349
5400
  }
5401
+ function isRootSession(name) {
5402
+ return name.length > 0 && !name.includes("-");
5403
+ }
5350
5404
  function extractRootExe(name) {
5351
5405
  if (!name) return null;
5352
5406
  if (!name.includes("-")) return name;
@@ -5362,9 +5416,13 @@ function getParentExe(sessionKey) {
5362
5416
  }
5363
5417
  }
5364
5418
  function resolveExeSession() {
5419
+ if (process.env.EXE_SESSION_NAME) {
5420
+ return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
5421
+ }
5365
5422
  const mySession = getMySession();
5366
5423
  if (!mySession) return null;
5367
5424
  const fromSessionName = extractRootExe(mySession);
5425
+ let candidate = null;
5368
5426
  try {
5369
5427
  const key = getSessionKey();
5370
5428
  const parentExe = getParentExe(key);
@@ -5375,13 +5433,47 @@ function resolveExeSession() {
5375
5433
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
5376
5434
  `
5377
5435
  );
5378
- return fromSessionName;
5436
+ candidate = fromSessionName;
5437
+ } else {
5438
+ candidate = fromCache;
5379
5439
  }
5380
- return fromCache;
5381
5440
  }
5382
5441
  } catch {
5383
5442
  }
5384
- return fromSessionName ?? mySession;
5443
+ if (!candidate) {
5444
+ candidate = fromSessionName ?? mySession;
5445
+ }
5446
+ if (candidate && isRootSession(candidate)) {
5447
+ try {
5448
+ const transport = getTransport();
5449
+ const liveSessions = transport.listSessions();
5450
+ if (!liveSessions.includes(candidate)) {
5451
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
5452
+ if (liveRoots.length === 1) {
5453
+ process.stderr.write(
5454
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
5455
+ `
5456
+ );
5457
+ return liveRoots[0];
5458
+ } else if (liveRoots.length > 1) {
5459
+ const base = candidate.replace(/\d+$/, "");
5460
+ const match = liveRoots.find((s) => s.startsWith(base));
5461
+ const chosen = match ?? liveRoots[0];
5462
+ process.stderr.write(
5463
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
5464
+ `
5465
+ );
5466
+ return chosen;
5467
+ }
5468
+ process.stderr.write(
5469
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
5470
+ `
5471
+ );
5472
+ }
5473
+ } catch {
5474
+ }
5475
+ }
5476
+ return candidate;
5385
5477
  }
5386
5478
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
5387
5479
  var init_tmux_routing = __esm({