@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
@@ -2775,6 +2775,20 @@ async function ensureSchema() {
2775
2775
  });
2776
2776
  } catch {
2777
2777
  }
2778
+ try {
2779
+ await client.execute({
2780
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2781
+ args: []
2782
+ });
2783
+ } catch {
2784
+ }
2785
+ try {
2786
+ await client.execute({
2787
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2788
+ args: []
2789
+ });
2790
+ } catch {
2791
+ }
2778
2792
  await client.executeMultiple(`
2779
2793
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2780
2794
  content_text,
@@ -3026,6 +3040,22 @@ async function ensureSchema() {
3026
3040
  );
3027
3041
  } catch {
3028
3042
  }
3043
+ for (const col of [
3044
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3045
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3046
+ ]) {
3047
+ try {
3048
+ await client.execute(col);
3049
+ } catch {
3050
+ }
3051
+ }
3052
+ try {
3053
+ await client.execute({
3054
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3055
+ args: []
3056
+ });
3057
+ } catch {
3058
+ }
3029
3059
  try {
3030
3060
  await client.execute({
3031
3061
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3068,6 +3098,13 @@ async function ensureSchema() {
3068
3098
  } catch {
3069
3099
  }
3070
3100
  }
3101
+ try {
3102
+ await client.execute({
3103
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3104
+ args: []
3105
+ });
3106
+ } catch {
3107
+ }
3071
3108
  try {
3072
3109
  await client.execute({
3073
3110
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -3412,7 +3449,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3412
3449
  import os7 from "os";
3413
3450
  import path9 from "path";
3414
3451
  import { jwtVerify, importSPKI } from "jose";
3415
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
3452
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE;
3416
3453
  var init_license = __esm({
3417
3454
  "src/lib/license.ts"() {
3418
3455
  "use strict";
@@ -3420,6 +3457,7 @@ var init_license = __esm({
3420
3457
  LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3421
3458
  CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3422
3459
  DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3460
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3423
3461
  }
3424
3462
  });
3425
3463
 
@@ -3463,6 +3501,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
3463
3501
  function getMySession() {
3464
3502
  return getTransport().getMySession();
3465
3503
  }
3504
+ function isRootSession(name) {
3505
+ return name.length > 0 && !name.includes("-");
3506
+ }
3466
3507
  function extractRootExe(name) {
3467
3508
  if (!name) return null;
3468
3509
  if (!name.includes("-")) return name;
@@ -3481,6 +3522,7 @@ function resolveExeSession() {
3481
3522
  const mySession = getMySession();
3482
3523
  if (!mySession) return null;
3483
3524
  const fromSessionName = extractRootExe(mySession);
3525
+ let candidate = null;
3484
3526
  try {
3485
3527
  const key = getSessionKey();
3486
3528
  const parentExe = getParentExe(key);
@@ -3491,13 +3533,47 @@ function resolveExeSession() {
3491
3533
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3492
3534
  `
3493
3535
  );
3494
- return fromSessionName;
3536
+ candidate = fromSessionName;
3537
+ } else {
3538
+ candidate = fromCache;
3495
3539
  }
3496
- return fromCache;
3497
3540
  }
3498
3541
  } catch {
3499
3542
  }
3500
- return fromSessionName ?? mySession;
3543
+ if (!candidate) {
3544
+ candidate = fromSessionName ?? mySession;
3545
+ }
3546
+ if (candidate && isRootSession(candidate)) {
3547
+ try {
3548
+ const transport = getTransport();
3549
+ const liveSessions = transport.listSessions();
3550
+ if (!liveSessions.includes(candidate)) {
3551
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
3552
+ if (liveRoots.length === 1) {
3553
+ process.stderr.write(
3554
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
3555
+ `
3556
+ );
3557
+ return liveRoots[0];
3558
+ } else if (liveRoots.length > 1) {
3559
+ const base = candidate.replace(/\d+$/, "");
3560
+ const match = liveRoots.find((s) => s.startsWith(base));
3561
+ const chosen = match ?? liveRoots[0];
3562
+ process.stderr.write(
3563
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
3564
+ `
3565
+ );
3566
+ return chosen;
3567
+ }
3568
+ process.stderr.write(
3569
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
3570
+ `
3571
+ );
3572
+ }
3573
+ } catch {
3574
+ }
3575
+ }
3576
+ return candidate;
3501
3577
  }
3502
3578
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
3503
3579
  var init_tmux_routing = __esm({
@@ -4633,6 +4709,20 @@ var init_platform_procedures = __esm({
4633
4709
  priority: "p1",
4634
4710
  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."
4635
4711
  },
4712
+ // --- Tool guidance ---
4713
+ {
4714
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4715
+ domain: "tools",
4716
+ priority: "p2",
4717
+ 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."
4718
+ },
4719
+ // --- Release awareness ---
4720
+ {
4721
+ title: "What's New check \u2014 surface new features after update",
4722
+ domain: "support",
4723
+ priority: "p1",
4724
+ 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."
4725
+ },
4636
4726
  // --- Platform vs Customer ownership ---
4637
4727
  {
4638
4728
  title: "What the platform provides vs what you customize",
@@ -4721,13 +4811,13 @@ var init_platform_procedures = __esm({
4721
4811
  title: "MCP tools \u2014 memory, decision, and search",
4722
4812
  domain: "tool-use",
4723
4813
  priority: "p1",
4724
- 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.`
4814
+ 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.`
4725
4815
  },
4726
4816
  {
4727
4817
  title: "MCP tools \u2014 task orchestration",
4728
4818
  domain: "tool-use",
4729
4819
  priority: "p1",
4730
- 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.'
4820
+ 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.`
4731
4821
  },
4732
4822
  {
4733
4823
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4757,7 +4847,7 @@ var init_platform_procedures = __esm({
4757
4847
  title: "MCP tools \u2014 admin, config, and operations",
4758
4848
  domain: "tool-use",
4759
4849
  priority: "p1",
4760
- 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.'
4850
+ 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.'
4761
4851
  }
4762
4852
  ];
4763
4853
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5441,6 +5531,8 @@ async function writeMemory(record) {
5441
5531
  source_type: record.source_type ?? null,
5442
5532
  tier: record.tier ?? classifyTier(record),
5443
5533
  supersedes_id: record.supersedes_id ?? null,
5534
+ valid_from: record.valid_from ?? record.timestamp,
5535
+ invalid_at: record.invalid_at ?? null,
5444
5536
  draft: record.draft ? 1 : 0,
5445
5537
  memory_type: memoryType,
5446
5538
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5459,7 +5551,8 @@ async function writeMemory(record) {
5459
5551
  token_cost: record.token_cost ?? null,
5460
5552
  audience: record.audience ?? null,
5461
5553
  language_type: record.language_type ?? inferLanguageType(record),
5462
- parent_memory_id: record.parent_memory_id ?? null
5554
+ parent_memory_id: record.parent_memory_id ?? null,
5555
+ procedure_for: record.procedure_for ?? null
5463
5556
  };
5464
5557
  _pendingRecords.push(dbRow);
5465
5558
  orgBus.emit({
@@ -5514,6 +5607,8 @@ async function flushBatch() {
5514
5607
  const sourceType = row.source_type ?? null;
5515
5608
  const tier = row.tier ?? 3;
5516
5609
  const supersedesId = row.supersedes_id ?? null;
5610
+ const validFrom = row.valid_from ?? row.timestamp;
5611
+ const invalidAt = row.invalid_at ?? null;
5517
5612
  const draft = row.draft ? 1 : 0;
5518
5613
  const memoryType = row.memory_type ?? "raw";
5519
5614
  const trajectory = row.trajectory ?? null;
@@ -5533,15 +5628,16 @@ async function flushBatch() {
5533
5628
  const audience = row.audience ?? null;
5534
5629
  const languageType = row.language_type ?? null;
5535
5630
  const parentMemoryId = row.parent_memory_id ?? null;
5631
+ const procedureFor = row.procedure_for ?? null;
5536
5632
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5537
5633
  tool_name, project_name,
5538
5634
  has_error, raw_text, vector, version, task_id, importance, status,
5539
5635
  confidence, last_accessed,
5540
5636
  workspace_id, document_id, user_id, char_offset, page_number,
5541
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5637
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5542
5638
  intent, outcome, domain, referenced_entities, retrieval_count,
5543
5639
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5544
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5640
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5545
5641
  const metaArgs = [
5546
5642
  intent,
5547
5643
  outcome,
@@ -5557,7 +5653,8 @@ async function flushBatch() {
5557
5653
  tokenCost,
5558
5654
  audience,
5559
5655
  languageType,
5560
- parentMemoryId
5656
+ parentMemoryId,
5657
+ procedureFor
5561
5658
  ];
5562
5659
  const baseArgs = [
5563
5660
  row.id,
@@ -5586,6 +5683,8 @@ async function flushBatch() {
5586
5683
  sourceType,
5587
5684
  tier,
5588
5685
  supersedesId,
5686
+ validFrom,
5687
+ invalidAt,
5589
5688
  draft,
5590
5689
  memoryType,
5591
5690
  trajectory,
@@ -5593,8 +5692,8 @@ async function flushBatch() {
5593
5692
  ];
5594
5693
  return {
5595
5694
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5596
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5597
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5695
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5696
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5598
5697
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5599
5698
  };
5600
5699
  };
@@ -5710,6 +5809,12 @@ async function searchMemories(queryVector, agentId, options) {
5710
5809
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5711
5810
  AND COALESCE(confidence, 0.7) >= 0.3`;
5712
5811
  const args = [agentId];
5812
+ if (options?.asOf) {
5813
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5814
+ args.push(options.asOf, options.asOf);
5815
+ } else {
5816
+ sql += ` AND invalid_at IS NULL`;
5817
+ }
5713
5818
  const scope = buildWikiScopeFilter(options, "");
5714
5819
  sql += scope.clause;
5715
5820
  args.push(...scope.args);
@@ -2776,6 +2776,20 @@ async function ensureSchema() {
2776
2776
  });
2777
2777
  } catch {
2778
2778
  }
2779
+ try {
2780
+ await client.execute({
2781
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2782
+ args: []
2783
+ });
2784
+ } catch {
2785
+ }
2786
+ try {
2787
+ await client.execute({
2788
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2789
+ args: []
2790
+ });
2791
+ } catch {
2792
+ }
2779
2793
  await client.executeMultiple(`
2780
2794
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2781
2795
  content_text,
@@ -3027,6 +3041,22 @@ async function ensureSchema() {
3027
3041
  );
3028
3042
  } catch {
3029
3043
  }
3044
+ for (const col of [
3045
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3046
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3047
+ ]) {
3048
+ try {
3049
+ await client.execute(col);
3050
+ } catch {
3051
+ }
3052
+ }
3053
+ try {
3054
+ await client.execute({
3055
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3056
+ args: []
3057
+ });
3058
+ } catch {
3059
+ }
3030
3060
  try {
3031
3061
  await client.execute({
3032
3062
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3069,6 +3099,13 @@ async function ensureSchema() {
3069
3099
  } catch {
3070
3100
  }
3071
3101
  }
3102
+ try {
3103
+ await client.execute({
3104
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3105
+ args: []
3106
+ });
3107
+ } catch {
3108
+ }
3072
3109
  try {
3073
3110
  await client.execute({
3074
3111
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -3413,7 +3450,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3413
3450
  import os7 from "os";
3414
3451
  import path9 from "path";
3415
3452
  import { jwtVerify, importSPKI } from "jose";
3416
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
3453
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE;
3417
3454
  var init_license = __esm({
3418
3455
  "src/lib/license.ts"() {
3419
3456
  "use strict";
@@ -3421,6 +3458,7 @@ var init_license = __esm({
3421
3458
  LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3422
3459
  CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3423
3460
  DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3461
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3424
3462
  }
3425
3463
  });
3426
3464
 
@@ -3464,6 +3502,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
3464
3502
  function getMySession() {
3465
3503
  return getTransport().getMySession();
3466
3504
  }
3505
+ function isRootSession(name) {
3506
+ return name.length > 0 && !name.includes("-");
3507
+ }
3467
3508
  function extractRootExe(name) {
3468
3509
  if (!name) return null;
3469
3510
  if (!name.includes("-")) return name;
@@ -3482,6 +3523,7 @@ function resolveExeSession() {
3482
3523
  const mySession = getMySession();
3483
3524
  if (!mySession) return null;
3484
3525
  const fromSessionName = extractRootExe(mySession);
3526
+ let candidate = null;
3485
3527
  try {
3486
3528
  const key = getSessionKey();
3487
3529
  const parentExe = getParentExe(key);
@@ -3492,13 +3534,47 @@ function resolveExeSession() {
3492
3534
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3493
3535
  `
3494
3536
  );
3495
- return fromSessionName;
3537
+ candidate = fromSessionName;
3538
+ } else {
3539
+ candidate = fromCache;
3496
3540
  }
3497
- return fromCache;
3498
3541
  }
3499
3542
  } catch {
3500
3543
  }
3501
- return fromSessionName ?? mySession;
3544
+ if (!candidate) {
3545
+ candidate = fromSessionName ?? mySession;
3546
+ }
3547
+ if (candidate && isRootSession(candidate)) {
3548
+ try {
3549
+ const transport = getTransport();
3550
+ const liveSessions = transport.listSessions();
3551
+ if (!liveSessions.includes(candidate)) {
3552
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
3553
+ if (liveRoots.length === 1) {
3554
+ process.stderr.write(
3555
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
3556
+ `
3557
+ );
3558
+ return liveRoots[0];
3559
+ } else if (liveRoots.length > 1) {
3560
+ const base = candidate.replace(/\d+$/, "");
3561
+ const match = liveRoots.find((s) => s.startsWith(base));
3562
+ const chosen = match ?? liveRoots[0];
3563
+ process.stderr.write(
3564
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
3565
+ `
3566
+ );
3567
+ return chosen;
3568
+ }
3569
+ process.stderr.write(
3570
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
3571
+ `
3572
+ );
3573
+ }
3574
+ } catch {
3575
+ }
3576
+ }
3577
+ return candidate;
3502
3578
  }
3503
3579
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
3504
3580
  var init_tmux_routing = __esm({
@@ -4699,6 +4775,20 @@ var init_platform_procedures = __esm({
4699
4775
  priority: "p1",
4700
4776
  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."
4701
4777
  },
4778
+ // --- Tool guidance ---
4779
+ {
4780
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4781
+ domain: "tools",
4782
+ priority: "p2",
4783
+ 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."
4784
+ },
4785
+ // --- Release awareness ---
4786
+ {
4787
+ title: "What's New check \u2014 surface new features after update",
4788
+ domain: "support",
4789
+ priority: "p1",
4790
+ 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."
4791
+ },
4702
4792
  // --- Platform vs Customer ownership ---
4703
4793
  {
4704
4794
  title: "What the platform provides vs what you customize",
@@ -4787,13 +4877,13 @@ var init_platform_procedures = __esm({
4787
4877
  title: "MCP tools \u2014 memory, decision, and search",
4788
4878
  domain: "tool-use",
4789
4879
  priority: "p1",
4790
- 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.`
4880
+ 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.`
4791
4881
  },
4792
4882
  {
4793
4883
  title: "MCP tools \u2014 task orchestration",
4794
4884
  domain: "tool-use",
4795
4885
  priority: "p1",
4796
- 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.'
4886
+ 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.`
4797
4887
  },
4798
4888
  {
4799
4889
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4823,7 +4913,7 @@ var init_platform_procedures = __esm({
4823
4913
  title: "MCP tools \u2014 admin, config, and operations",
4824
4914
  domain: "tool-use",
4825
4915
  priority: "p1",
4826
- 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.'
4916
+ 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.'
4827
4917
  }
4828
4918
  ];
4829
4919
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5507,6 +5597,8 @@ async function writeMemory(record) {
5507
5597
  source_type: record.source_type ?? null,
5508
5598
  tier: record.tier ?? classifyTier(record),
5509
5599
  supersedes_id: record.supersedes_id ?? null,
5600
+ valid_from: record.valid_from ?? record.timestamp,
5601
+ invalid_at: record.invalid_at ?? null,
5510
5602
  draft: record.draft ? 1 : 0,
5511
5603
  memory_type: memoryType,
5512
5604
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5525,7 +5617,8 @@ async function writeMemory(record) {
5525
5617
  token_cost: record.token_cost ?? null,
5526
5618
  audience: record.audience ?? null,
5527
5619
  language_type: record.language_type ?? inferLanguageType(record),
5528
- parent_memory_id: record.parent_memory_id ?? null
5620
+ parent_memory_id: record.parent_memory_id ?? null,
5621
+ procedure_for: record.procedure_for ?? null
5529
5622
  };
5530
5623
  _pendingRecords.push(dbRow);
5531
5624
  orgBus.emit({
@@ -5580,6 +5673,8 @@ async function flushBatch() {
5580
5673
  const sourceType = row.source_type ?? null;
5581
5674
  const tier = row.tier ?? 3;
5582
5675
  const supersedesId = row.supersedes_id ?? null;
5676
+ const validFrom = row.valid_from ?? row.timestamp;
5677
+ const invalidAt = row.invalid_at ?? null;
5583
5678
  const draft = row.draft ? 1 : 0;
5584
5679
  const memoryType = row.memory_type ?? "raw";
5585
5680
  const trajectory = row.trajectory ?? null;
@@ -5599,15 +5694,16 @@ async function flushBatch() {
5599
5694
  const audience = row.audience ?? null;
5600
5695
  const languageType = row.language_type ?? null;
5601
5696
  const parentMemoryId = row.parent_memory_id ?? null;
5697
+ const procedureFor = row.procedure_for ?? null;
5602
5698
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5603
5699
  tool_name, project_name,
5604
5700
  has_error, raw_text, vector, version, task_id, importance, status,
5605
5701
  confidence, last_accessed,
5606
5702
  workspace_id, document_id, user_id, char_offset, page_number,
5607
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5703
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5608
5704
  intent, outcome, domain, referenced_entities, retrieval_count,
5609
5705
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5610
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5706
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5611
5707
  const metaArgs = [
5612
5708
  intent,
5613
5709
  outcome,
@@ -5623,7 +5719,8 @@ async function flushBatch() {
5623
5719
  tokenCost,
5624
5720
  audience,
5625
5721
  languageType,
5626
- parentMemoryId
5722
+ parentMemoryId,
5723
+ procedureFor
5627
5724
  ];
5628
5725
  const baseArgs = [
5629
5726
  row.id,
@@ -5652,6 +5749,8 @@ async function flushBatch() {
5652
5749
  sourceType,
5653
5750
  tier,
5654
5751
  supersedesId,
5752
+ validFrom,
5753
+ invalidAt,
5655
5754
  draft,
5656
5755
  memoryType,
5657
5756
  trajectory,
@@ -5659,8 +5758,8 @@ async function flushBatch() {
5659
5758
  ];
5660
5759
  return {
5661
5760
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5662
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5663
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5761
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5762
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5664
5763
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5665
5764
  };
5666
5765
  };
@@ -5776,6 +5875,12 @@ async function searchMemories(queryVector, agentId, options) {
5776
5875
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5777
5876
  AND COALESCE(confidence, 0.7) >= 0.3`;
5778
5877
  const args = [agentId];
5878
+ if (options?.asOf) {
5879
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5880
+ args.push(options.asOf, options.asOf);
5881
+ } else {
5882
+ sql += ` AND invalid_at IS NULL`;
5883
+ }
5779
5884
  const scope = buildWikiScopeFilter(options, "");
5780
5885
  sql += scope.clause;
5781
5886
  args.push(...scope.args);