@askexenow/exe-os 0.9.93 → 0.9.94

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 +118 -13
  10. package/dist/bin/cli.js +1558 -466
  11. package/dist/bin/customer-readiness.js +51 -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 +111 -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 +133 -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 +132 -18
  22. package/dist/bin/exe-heartbeat.js +118 -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 +118 -13
  27. package/dist/bin/exe-pending-notifications.js +118 -13
  28. package/dist/bin/exe-pending-reviews.js +118 -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 +133 -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 +118 -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 +133 -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 +133 -18
  43. package/dist/bin/scan-tasks.js +133 -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 +133 -18
  49. package/dist/hooks/bug-report-worker.js +133 -18
  50. package/dist/hooks/codex-stop-task-finalizer.js +123 -14
  51. package/dist/hooks/commit-complete.js +133 -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 +310 -50
  57. package/dist/hooks/post-tool-combined.js +433 -13
  58. package/dist/hooks/pre-compact.js +133 -18
  59. package/dist/hooks/pre-tool-use.js +118 -13
  60. package/dist/hooks/prompt-submit.js +191 -19
  61. package/dist/hooks/session-end.js +133 -18
  62. package/dist/hooks/session-start.js +143 -13
  63. package/dist/hooks/stop.js +118 -13
  64. package/dist/hooks/subagent-stop.js +118 -13
  65. package/dist/hooks/summary-worker.js +96 -7
  66. package/dist/index.js +133 -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 +913 -42
  74. package/dist/lib/hybrid-search.js +100 -9
  75. package/dist/lib/license.js +1 -1
  76. package/dist/lib/messaging.js +40 -4
  77. package/dist/lib/schedules.js +54 -3
  78. package/dist/lib/store.js +75 -9
  79. package/dist/lib/tasks.js +58 -9
  80. package/dist/lib/tmux-routing.js +58 -9
  81. package/dist/mcp/server.js +875 -42
  82. package/dist/mcp/tools/create-task.js +67 -12
  83. package/dist/mcp/tools/list-tasks.js +46 -5
  84. package/dist/mcp/tools/send-message.js +40 -4
  85. package/dist/mcp/tools/update-task.js +58 -9
  86. package/dist/runtime/index.js +133 -18
  87. package/dist/tui/App.js +132 -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;
@@ -3491,6 +3532,7 @@ function resolveExeSession() {
3491
3532
  const mySession = getMySession();
3492
3533
  if (!mySession) return null;
3493
3534
  const fromSessionName = extractRootExe(mySession);
3535
+ let candidate = null;
3494
3536
  try {
3495
3537
  const key = getSessionKey();
3496
3538
  const parentExe = getParentExe(key);
@@ -3501,13 +3543,47 @@ function resolveExeSession() {
3501
3543
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3502
3544
  `
3503
3545
  );
3504
- return fromSessionName;
3546
+ candidate = fromSessionName;
3547
+ } else {
3548
+ candidate = fromCache;
3505
3549
  }
3506
- return fromCache;
3507
3550
  }
3508
3551
  } catch {
3509
3552
  }
3510
- return fromSessionName ?? mySession;
3553
+ if (!candidate) {
3554
+ candidate = fromSessionName ?? mySession;
3555
+ }
3556
+ if (candidate && isRootSession(candidate)) {
3557
+ try {
3558
+ const transport = getTransport();
3559
+ const liveSessions = transport.listSessions();
3560
+ if (!liveSessions.includes(candidate)) {
3561
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
3562
+ if (liveRoots.length === 1) {
3563
+ process.stderr.write(
3564
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
3565
+ `
3566
+ );
3567
+ return liveRoots[0];
3568
+ } else if (liveRoots.length > 1) {
3569
+ const base = candidate.replace(/\d+$/, "");
3570
+ const match = liveRoots.find((s) => s.startsWith(base));
3571
+ const chosen = match ?? liveRoots[0];
3572
+ process.stderr.write(
3573
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
3574
+ `
3575
+ );
3576
+ return chosen;
3577
+ }
3578
+ process.stderr.write(
3579
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
3580
+ `
3581
+ );
3582
+ }
3583
+ } catch {
3584
+ }
3585
+ }
3586
+ return candidate;
3511
3587
  }
3512
3588
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
3513
3589
  var init_tmux_routing = __esm({
@@ -4643,6 +4719,20 @@ var init_platform_procedures = __esm({
4643
4719
  priority: "p1",
4644
4720
  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
4721
  },
4722
+ // --- Tool guidance ---
4723
+ {
4724
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4725
+ domain: "tools",
4726
+ priority: "p2",
4727
+ 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."
4728
+ },
4729
+ // --- Release awareness ---
4730
+ {
4731
+ title: "What's New check \u2014 surface new features after update",
4732
+ domain: "support",
4733
+ priority: "p1",
4734
+ 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."
4735
+ },
4646
4736
  // --- Platform vs Customer ownership ---
4647
4737
  {
4648
4738
  title: "What the platform provides vs what you customize",
@@ -4731,13 +4821,13 @@ var init_platform_procedures = __esm({
4731
4821
  title: "MCP tools \u2014 memory, decision, and search",
4732
4822
  domain: "tool-use",
4733
4823
  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.`
4824
+ content: `memory(action="recall") / recall_my_memory: search your own memories (semantic + FTS). Supports as_of param for bi-temporal queries (what did I know at time X?), kind param to filter by memory type (decision, procedure, observation, raw, conversation, behavior). memory(action="ask_team") / ask_team_memory: search a colleague's memories by agent name. memory(action="store") / store_memory: persist a memory. Supports kind param and procedure_for domain tag for procedure-type memories. 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. memory(action="supersede") / 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
4825
  },
4736
4826
  {
4737
4827
  title: "MCP tools \u2014 task orchestration",
4738
4828
  domain: "tool-use",
4739
4829
  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.'
4830
+ content: `task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. Supports spawn_runtime and spawn_model params to override the agent's default runtime/model for a specific task. 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
4831
  },
4742
4832
  {
4743
4833
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4767,7 +4857,7 @@ var init_platform_procedures = __esm({
4767
4857
  title: "MCP tools \u2014 admin, config, and operations",
4768
4858
  domain: "tool-use",
4769
4859
  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.'
4860
+ 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. diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. Returns top-K tools ranked by relevance. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role identity. Returns drift score + recommendations. mcp_ping(): daemon health + license status + tool usage stats.'
4771
4861
  }
4772
4862
  ];
4773
4863
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5451,6 +5541,8 @@ async function writeMemory(record) {
5451
5541
  source_type: record.source_type ?? null,
5452
5542
  tier: record.tier ?? classifyTier(record),
5453
5543
  supersedes_id: record.supersedes_id ?? null,
5544
+ valid_from: record.valid_from ?? record.timestamp,
5545
+ invalid_at: record.invalid_at ?? null,
5454
5546
  draft: record.draft ? 1 : 0,
5455
5547
  memory_type: memoryType,
5456
5548
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5469,7 +5561,8 @@ async function writeMemory(record) {
5469
5561
  token_cost: record.token_cost ?? null,
5470
5562
  audience: record.audience ?? null,
5471
5563
  language_type: record.language_type ?? inferLanguageType(record),
5472
- parent_memory_id: record.parent_memory_id ?? null
5564
+ parent_memory_id: record.parent_memory_id ?? null,
5565
+ procedure_for: record.procedure_for ?? null
5473
5566
  };
5474
5567
  _pendingRecords.push(dbRow);
5475
5568
  orgBus.emit({
@@ -5524,6 +5617,8 @@ async function flushBatch() {
5524
5617
  const sourceType = row.source_type ?? null;
5525
5618
  const tier = row.tier ?? 3;
5526
5619
  const supersedesId = row.supersedes_id ?? null;
5620
+ const validFrom = row.valid_from ?? row.timestamp;
5621
+ const invalidAt = row.invalid_at ?? null;
5527
5622
  const draft = row.draft ? 1 : 0;
5528
5623
  const memoryType = row.memory_type ?? "raw";
5529
5624
  const trajectory = row.trajectory ?? null;
@@ -5543,15 +5638,16 @@ async function flushBatch() {
5543
5638
  const audience = row.audience ?? null;
5544
5639
  const languageType = row.language_type ?? null;
5545
5640
  const parentMemoryId = row.parent_memory_id ?? null;
5641
+ const procedureFor = row.procedure_for ?? null;
5546
5642
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5547
5643
  tool_name, project_name,
5548
5644
  has_error, raw_text, vector, version, task_id, importance, status,
5549
5645
  confidence, last_accessed,
5550
5646
  workspace_id, document_id, user_id, char_offset, page_number,
5551
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5647
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5552
5648
  intent, outcome, domain, referenced_entities, retrieval_count,
5553
5649
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5554
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5650
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5555
5651
  const metaArgs = [
5556
5652
  intent,
5557
5653
  outcome,
@@ -5567,7 +5663,8 @@ async function flushBatch() {
5567
5663
  tokenCost,
5568
5664
  audience,
5569
5665
  languageType,
5570
- parentMemoryId
5666
+ parentMemoryId,
5667
+ procedureFor
5571
5668
  ];
5572
5669
  const baseArgs = [
5573
5670
  row.id,
@@ -5596,6 +5693,8 @@ async function flushBatch() {
5596
5693
  sourceType,
5597
5694
  tier,
5598
5695
  supersedesId,
5696
+ validFrom,
5697
+ invalidAt,
5599
5698
  draft,
5600
5699
  memoryType,
5601
5700
  trajectory,
@@ -5603,8 +5702,8 @@ async function flushBatch() {
5603
5702
  ];
5604
5703
  return {
5605
5704
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5606
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5607
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5705
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5706
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5608
5707
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5609
5708
  };
5610
5709
  };
@@ -5720,6 +5819,12 @@ async function searchMemories(queryVector, agentId, options) {
5720
5819
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5721
5820
  AND COALESCE(confidence, 0.7) >= 0.3`;
5722
5821
  const args = [agentId];
5822
+ if (options?.asOf) {
5823
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5824
+ args.push(options.asOf, options.asOf);
5825
+ } else {
5826
+ sql += ` AND invalid_at IS NULL`;
5827
+ }
5723
5828
  const scope = buildWikiScopeFilter(options, "");
5724
5829
  sql += scope.clause;
5725
5830
  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 your own memories (semantic + FTS). Supports as_of param for bi-temporal queries (what did I know at time X?), kind param to filter by memory type (decision, procedure, observation, raw, conversation, behavior). memory(action="ask_team") / ask_team_memory: search a colleague's memories by agent name. memory(action="store") / store_memory: persist a memory. Supports kind param and procedure_for domain tag for procedure-type memories. 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. memory(action="supersede") / 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. Supports spawn_runtime and spawn_model params to override the agent's default runtime/model for a specific task. 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="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. diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. Returns top-K tools ranked by relevance. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role identity. Returns drift score + recommendations. 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;
@@ -5365,6 +5419,7 @@ function resolveExeSession() {
5365
5419
  const mySession = getMySession();
5366
5420
  if (!mySession) return null;
5367
5421
  const fromSessionName = extractRootExe(mySession);
5422
+ let candidate = null;
5368
5423
  try {
5369
5424
  const key = getSessionKey();
5370
5425
  const parentExe = getParentExe(key);
@@ -5375,13 +5430,47 @@ function resolveExeSession() {
5375
5430
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
5376
5431
  `
5377
5432
  );
5378
- return fromSessionName;
5433
+ candidate = fromSessionName;
5434
+ } else {
5435
+ candidate = fromCache;
5379
5436
  }
5380
- return fromCache;
5381
5437
  }
5382
5438
  } catch {
5383
5439
  }
5384
- return fromSessionName ?? mySession;
5440
+ if (!candidate) {
5441
+ candidate = fromSessionName ?? mySession;
5442
+ }
5443
+ if (candidate && isRootSession(candidate)) {
5444
+ try {
5445
+ const transport = getTransport();
5446
+ const liveSessions = transport.listSessions();
5447
+ if (!liveSessions.includes(candidate)) {
5448
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
5449
+ if (liveRoots.length === 1) {
5450
+ process.stderr.write(
5451
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
5452
+ `
5453
+ );
5454
+ return liveRoots[0];
5455
+ } else if (liveRoots.length > 1) {
5456
+ const base = candidate.replace(/\d+$/, "");
5457
+ const match = liveRoots.find((s) => s.startsWith(base));
5458
+ const chosen = match ?? liveRoots[0];
5459
+ process.stderr.write(
5460
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
5461
+ `
5462
+ );
5463
+ return chosen;
5464
+ }
5465
+ process.stderr.write(
5466
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
5467
+ `
5468
+ );
5469
+ }
5470
+ } catch {
5471
+ }
5472
+ }
5473
+ return candidate;
5385
5474
  }
5386
5475
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
5387
5476
  var init_tmux_routing = __esm({