@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
@@ -3175,6 +3175,20 @@ async function ensureSchema() {
3175
3175
  });
3176
3176
  } catch {
3177
3177
  }
3178
+ try {
3179
+ await client.execute({
3180
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
3181
+ args: []
3182
+ });
3183
+ } catch {
3184
+ }
3185
+ try {
3186
+ await client.execute({
3187
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
3188
+ args: []
3189
+ });
3190
+ } catch {
3191
+ }
3178
3192
  await client.executeMultiple(`
3179
3193
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
3180
3194
  content_text,
@@ -3426,6 +3440,22 @@ async function ensureSchema() {
3426
3440
  );
3427
3441
  } catch {
3428
3442
  }
3443
+ for (const col of [
3444
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3445
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3446
+ ]) {
3447
+ try {
3448
+ await client.execute(col);
3449
+ } catch {
3450
+ }
3451
+ }
3452
+ try {
3453
+ await client.execute({
3454
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3455
+ args: []
3456
+ });
3457
+ } catch {
3458
+ }
3429
3459
  try {
3430
3460
  await client.execute({
3431
3461
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3468,6 +3498,13 @@ async function ensureSchema() {
3468
3498
  } catch {
3469
3499
  }
3470
3500
  }
3501
+ try {
3502
+ await client.execute({
3503
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3504
+ args: []
3505
+ });
3506
+ } catch {
3507
+ }
3471
3508
  try {
3472
3509
  await client.execute({
3473
3510
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -4606,6 +4643,20 @@ var init_platform_procedures = __esm({
4606
4643
  priority: "p1",
4607
4644
  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."
4608
4645
  },
4646
+ // --- Tool guidance ---
4647
+ {
4648
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4649
+ domain: "tools",
4650
+ priority: "p2",
4651
+ 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."
4652
+ },
4653
+ // --- Release awareness ---
4654
+ {
4655
+ title: "What's New check \u2014 surface new features after update",
4656
+ domain: "support",
4657
+ priority: "p1",
4658
+ 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."
4659
+ },
4609
4660
  // --- Platform vs Customer ownership ---
4610
4661
  {
4611
4662
  title: "What the platform provides vs what you customize",
@@ -4694,13 +4745,13 @@ var init_platform_procedures = __esm({
4694
4745
  title: "MCP tools \u2014 memory, decision, and search",
4695
4746
  domain: "tool-use",
4696
4747
  priority: "p1",
4697
- 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.`
4748
+ 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.`
4698
4749
  },
4699
4750
  {
4700
4751
  title: "MCP tools \u2014 task orchestration",
4701
4752
  domain: "tool-use",
4702
4753
  priority: "p1",
4703
- 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.'
4754
+ 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.'
4704
4755
  },
4705
4756
  {
4706
4757
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4730,7 +4781,7 @@ var init_platform_procedures = __esm({
4730
4781
  title: "MCP tools \u2014 admin, config, and operations",
4731
4782
  domain: "tool-use",
4732
4783
  priority: "p1",
4733
- 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.'
4784
+ 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.'
4734
4785
  }
4735
4786
  ];
4736
4787
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5414,6 +5465,8 @@ async function writeMemory(record) {
5414
5465
  source_type: record.source_type ?? null,
5415
5466
  tier: record.tier ?? classifyTier(record),
5416
5467
  supersedes_id: record.supersedes_id ?? null,
5468
+ valid_from: record.valid_from ?? record.timestamp,
5469
+ invalid_at: record.invalid_at ?? null,
5417
5470
  draft: record.draft ? 1 : 0,
5418
5471
  memory_type: memoryType,
5419
5472
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5432,7 +5485,8 @@ async function writeMemory(record) {
5432
5485
  token_cost: record.token_cost ?? null,
5433
5486
  audience: record.audience ?? null,
5434
5487
  language_type: record.language_type ?? inferLanguageType(record),
5435
- parent_memory_id: record.parent_memory_id ?? null
5488
+ parent_memory_id: record.parent_memory_id ?? null,
5489
+ procedure_for: record.procedure_for ?? null
5436
5490
  };
5437
5491
  _pendingRecords.push(dbRow);
5438
5492
  orgBus.emit({
@@ -5487,6 +5541,8 @@ async function flushBatch() {
5487
5541
  const sourceType = row.source_type ?? null;
5488
5542
  const tier = row.tier ?? 3;
5489
5543
  const supersedesId = row.supersedes_id ?? null;
5544
+ const validFrom = row.valid_from ?? row.timestamp;
5545
+ const invalidAt = row.invalid_at ?? null;
5490
5546
  const draft = row.draft ? 1 : 0;
5491
5547
  const memoryType = row.memory_type ?? "raw";
5492
5548
  const trajectory = row.trajectory ?? null;
@@ -5506,15 +5562,16 @@ async function flushBatch() {
5506
5562
  const audience = row.audience ?? null;
5507
5563
  const languageType = row.language_type ?? null;
5508
5564
  const parentMemoryId = row.parent_memory_id ?? null;
5565
+ const procedureFor = row.procedure_for ?? null;
5509
5566
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5510
5567
  tool_name, project_name,
5511
5568
  has_error, raw_text, vector, version, task_id, importance, status,
5512
5569
  confidence, last_accessed,
5513
5570
  workspace_id, document_id, user_id, char_offset, page_number,
5514
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5571
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5515
5572
  intent, outcome, domain, referenced_entities, retrieval_count,
5516
5573
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5517
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5574
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5518
5575
  const metaArgs = [
5519
5576
  intent,
5520
5577
  outcome,
@@ -5530,7 +5587,8 @@ async function flushBatch() {
5530
5587
  tokenCost,
5531
5588
  audience,
5532
5589
  languageType,
5533
- parentMemoryId
5590
+ parentMemoryId,
5591
+ procedureFor
5534
5592
  ];
5535
5593
  const baseArgs = [
5536
5594
  row.id,
@@ -5559,6 +5617,8 @@ async function flushBatch() {
5559
5617
  sourceType,
5560
5618
  tier,
5561
5619
  supersedesId,
5620
+ validFrom,
5621
+ invalidAt,
5562
5622
  draft,
5563
5623
  memoryType,
5564
5624
  trajectory,
@@ -5566,8 +5626,8 @@ async function flushBatch() {
5566
5626
  ];
5567
5627
  return {
5568
5628
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5569
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5570
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5629
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5630
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5571
5631
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5572
5632
  };
5573
5633
  };
@@ -5683,6 +5743,12 @@ async function searchMemories(queryVector, agentId, options) {
5683
5743
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5684
5744
  AND COALESCE(confidence, 0.7) >= 0.3`;
5685
5745
  const args = [agentId];
5746
+ if (options?.asOf) {
5747
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5748
+ args.push(options.asOf, options.asOf);
5749
+ } else {
5750
+ sql += ` AND invalid_at IS NULL`;
5751
+ }
5686
5752
  const scope = buildWikiScopeFilter(options, "");
5687
5753
  sql += scope.clause;
5688
5754
  args.push(...scope.args);
@@ -6285,7 +6351,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
6285
6351
  import os8 from "os";
6286
6352
  import path11 from "path";
6287
6353
  import { jwtVerify, importSPKI } from "jose";
6288
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
6354
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, PLAN_LIMITS;
6289
6355
  var init_license = __esm({
6290
6356
  "src/lib/license.ts"() {
6291
6357
  "use strict";
@@ -6293,6 +6359,7 @@ var init_license = __esm({
6293
6359
  LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
6294
6360
  CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
6295
6361
  DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
6362
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
6296
6363
  PLAN_LIMITS = {
6297
6364
  free: { devices: 1, employees: 1, memories: 5e3 },
6298
6365
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -7244,9 +7311,13 @@ function getDispatchedBy(sessionKey) {
7244
7311
  }
7245
7312
  }
7246
7313
  function resolveExeSession() {
7314
+ if (process.env.EXE_SESSION_NAME) {
7315
+ return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
7316
+ }
7247
7317
  const mySession = getMySession();
7248
7318
  if (!mySession) return null;
7249
7319
  const fromSessionName = extractRootExe(mySession);
7320
+ let candidate = null;
7250
7321
  try {
7251
7322
  const key = getSessionKey();
7252
7323
  const parentExe = getParentExe(key);
@@ -7257,13 +7328,47 @@ function resolveExeSession() {
7257
7328
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
7258
7329
  `
7259
7330
  );
7260
- return fromSessionName;
7331
+ candidate = fromSessionName;
7332
+ } else {
7333
+ candidate = fromCache;
7261
7334
  }
7262
- return fromCache;
7263
7335
  }
7264
7336
  } catch {
7265
7337
  }
7266
- return fromSessionName ?? mySession;
7338
+ if (!candidate) {
7339
+ candidate = fromSessionName ?? mySession;
7340
+ }
7341
+ if (candidate && isRootSession(candidate)) {
7342
+ try {
7343
+ const transport = getTransport();
7344
+ const liveSessions = transport.listSessions();
7345
+ if (!liveSessions.includes(candidate)) {
7346
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
7347
+ if (liveRoots.length === 1) {
7348
+ process.stderr.write(
7349
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
7350
+ `
7351
+ );
7352
+ return liveRoots[0];
7353
+ } else if (liveRoots.length > 1) {
7354
+ const base = candidate.replace(/\d+$/, "");
7355
+ const match = liveRoots.find((s) => s.startsWith(base));
7356
+ const chosen = match ?? liveRoots[0];
7357
+ process.stderr.write(
7358
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
7359
+ `
7360
+ );
7361
+ return chosen;
7362
+ }
7363
+ process.stderr.write(
7364
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
7365
+ `
7366
+ );
7367
+ }
7368
+ } catch {
7369
+ }
7370
+ }
7371
+ return candidate;
7267
7372
  }
7268
7373
  function isEmployeeAlive(sessionName) {
7269
7374
  return getTransport().isAlive(sessionName);
@@ -7665,7 +7770,12 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7665
7770
  }
7666
7771
  const spawnCwd = opts?.cwd ?? projectDir;
7667
7772
  const useExeAgent = !!(opts?.model && opts?.provider);
7668
- const agentRtConfig = getAgentRuntime(employeeName);
7773
+ const baseRtConfig = getAgentRuntime(employeeName);
7774
+ const agentRtConfig = {
7775
+ ...baseRtConfig,
7776
+ ...opts?.runtimeOverride ? { runtime: opts.runtimeOverride } : {},
7777
+ ...opts?.modelOverride ? { model: opts.modelOverride } : {}
7778
+ };
7669
7779
  const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
7670
7780
  const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
7671
7781
  const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
@@ -8303,8 +8413,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
8303
8413
  const complexity = input.complexity ?? "standard";
8304
8414
  const sessionScope = earlySessionScope;
8305
8415
  await client.execute({
8306
- sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
8307
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8416
+ sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, spawn_runtime, spawn_model, created_at, updated_at)
8417
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8308
8418
  args: [
8309
8419
  id,
8310
8420
  input.title,
@@ -8324,6 +8434,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
8324
8434
  0,
8325
8435
  null,
8326
8436
  sessionScope,
8437
+ input.spawnRuntime ?? null,
8438
+ input.spawnModel ?? null,
8327
8439
  now,
8328
8440
  now
8329
8441
  ]
@@ -8380,7 +8492,9 @@ ${input.context}
8380
8492
  budgetTokens: input.budgetTokens ?? null,
8381
8493
  budgetFallbackModel: input.budgetFallbackModel ?? null,
8382
8494
  tokensUsed: 0,
8383
- tokensWarnedAt: null
8495
+ tokensWarnedAt: null,
8496
+ spawnRuntime: input.spawnRuntime ?? null,
8497
+ spawnModel: input.spawnModel ?? null
8384
8498
  };
8385
8499
  }
8386
8500
  async function listTasks(input) {
@@ -8430,7 +8544,9 @@ async function listTasks(input) {
8430
8544
  budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
8431
8545
  budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
8432
8546
  tokensUsed: Number(r.tokens_used ?? 0),
8433
- tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
8547
+ tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null,
8548
+ spawnRuntime: r.spawn_runtime !== null && r.spawn_runtime !== void 0 ? String(r.spawn_runtime) : null,
8549
+ spawnModel: r.spawn_model !== null && r.spawn_model !== void 0 ? String(r.spawn_model) : null
8434
8550
  }));
8435
8551
  }
8436
8552
  function isTmuxSessionAlive(identifier) {
@@ -9419,6 +9535,8 @@ async function updateTask(input) {
9419
9535
  budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
9420
9536
  tokensUsed: Number(row.tokens_used ?? 0),
9421
9537
  tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
9538
+ spawnRuntime: row.spawn_runtime !== void 0 && row.spawn_runtime !== null ? String(row.spawn_runtime) : null,
9539
+ spawnModel: row.spawn_model !== void 0 && row.spawn_model !== null ? String(row.spawn_model) : null,
9422
9540
  nextTask
9423
9541
  };
9424
9542
  }
@@ -2858,6 +2858,20 @@ async function ensureSchema() {
2858
2858
  });
2859
2859
  } catch {
2860
2860
  }
2861
+ try {
2862
+ await client.execute({
2863
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2864
+ args: []
2865
+ });
2866
+ } catch {
2867
+ }
2868
+ try {
2869
+ await client.execute({
2870
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2871
+ args: []
2872
+ });
2873
+ } catch {
2874
+ }
2861
2875
  await client.executeMultiple(`
2862
2876
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2863
2877
  content_text,
@@ -3109,6 +3123,22 @@ async function ensureSchema() {
3109
3123
  );
3110
3124
  } catch {
3111
3125
  }
3126
+ for (const col of [
3127
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3128
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3129
+ ]) {
3130
+ try {
3131
+ await client.execute(col);
3132
+ } catch {
3133
+ }
3134
+ }
3135
+ try {
3136
+ await client.execute({
3137
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3138
+ args: []
3139
+ });
3140
+ } catch {
3141
+ }
3112
3142
  try {
3113
3143
  await client.execute({
3114
3144
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3151,6 +3181,13 @@ async function ensureSchema() {
3151
3181
  } catch {
3152
3182
  }
3153
3183
  }
3184
+ try {
3185
+ await client.execute({
3186
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3187
+ args: []
3188
+ });
3189
+ } catch {
3190
+ }
3154
3191
  try {
3155
3192
  await client.execute({
3156
3193
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -4289,6 +4326,20 @@ var init_platform_procedures = __esm({
4289
4326
  priority: "p1",
4290
4327
  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."
4291
4328
  },
4329
+ // --- Tool guidance ---
4330
+ {
4331
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4332
+ domain: "tools",
4333
+ priority: "p2",
4334
+ 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."
4335
+ },
4336
+ // --- Release awareness ---
4337
+ {
4338
+ title: "What's New check \u2014 surface new features after update",
4339
+ domain: "support",
4340
+ priority: "p1",
4341
+ 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."
4342
+ },
4292
4343
  // --- Platform vs Customer ownership ---
4293
4344
  {
4294
4345
  title: "What the platform provides vs what you customize",
@@ -4377,13 +4428,13 @@ var init_platform_procedures = __esm({
4377
4428
  title: "MCP tools \u2014 memory, decision, and search",
4378
4429
  domain: "tool-use",
4379
4430
  priority: "p1",
4380
- 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.`
4431
+ 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.`
4381
4432
  },
4382
4433
  {
4383
4434
  title: "MCP tools \u2014 task orchestration",
4384
4435
  domain: "tool-use",
4385
4436
  priority: "p1",
4386
- 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.'
4437
+ 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.'
4387
4438
  },
4388
4439
  {
4389
4440
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4413,7 +4464,7 @@ var init_platform_procedures = __esm({
4413
4464
  title: "MCP tools \u2014 admin, config, and operations",
4414
4465
  domain: "tool-use",
4415
4466
  priority: "p1",
4416
- 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.'
4467
+ 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.'
4417
4468
  }
4418
4469
  ];
4419
4470
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5097,6 +5148,8 @@ async function writeMemory(record) {
5097
5148
  source_type: record.source_type ?? null,
5098
5149
  tier: record.tier ?? classifyTier(record),
5099
5150
  supersedes_id: record.supersedes_id ?? null,
5151
+ valid_from: record.valid_from ?? record.timestamp,
5152
+ invalid_at: record.invalid_at ?? null,
5100
5153
  draft: record.draft ? 1 : 0,
5101
5154
  memory_type: memoryType,
5102
5155
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5115,7 +5168,8 @@ async function writeMemory(record) {
5115
5168
  token_cost: record.token_cost ?? null,
5116
5169
  audience: record.audience ?? null,
5117
5170
  language_type: record.language_type ?? inferLanguageType(record),
5118
- parent_memory_id: record.parent_memory_id ?? null
5171
+ parent_memory_id: record.parent_memory_id ?? null,
5172
+ procedure_for: record.procedure_for ?? null
5119
5173
  };
5120
5174
  _pendingRecords.push(dbRow);
5121
5175
  orgBus.emit({
@@ -5170,6 +5224,8 @@ async function flushBatch() {
5170
5224
  const sourceType = row.source_type ?? null;
5171
5225
  const tier = row.tier ?? 3;
5172
5226
  const supersedesId = row.supersedes_id ?? null;
5227
+ const validFrom = row.valid_from ?? row.timestamp;
5228
+ const invalidAt = row.invalid_at ?? null;
5173
5229
  const draft = row.draft ? 1 : 0;
5174
5230
  const memoryType = row.memory_type ?? "raw";
5175
5231
  const trajectory = row.trajectory ?? null;
@@ -5189,15 +5245,16 @@ async function flushBatch() {
5189
5245
  const audience = row.audience ?? null;
5190
5246
  const languageType = row.language_type ?? null;
5191
5247
  const parentMemoryId = row.parent_memory_id ?? null;
5248
+ const procedureFor = row.procedure_for ?? null;
5192
5249
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5193
5250
  tool_name, project_name,
5194
5251
  has_error, raw_text, vector, version, task_id, importance, status,
5195
5252
  confidence, last_accessed,
5196
5253
  workspace_id, document_id, user_id, char_offset, page_number,
5197
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5254
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5198
5255
  intent, outcome, domain, referenced_entities, retrieval_count,
5199
5256
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5200
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5257
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5201
5258
  const metaArgs = [
5202
5259
  intent,
5203
5260
  outcome,
@@ -5213,7 +5270,8 @@ async function flushBatch() {
5213
5270
  tokenCost,
5214
5271
  audience,
5215
5272
  languageType,
5216
- parentMemoryId
5273
+ parentMemoryId,
5274
+ procedureFor
5217
5275
  ];
5218
5276
  const baseArgs = [
5219
5277
  row.id,
@@ -5242,6 +5300,8 @@ async function flushBatch() {
5242
5300
  sourceType,
5243
5301
  tier,
5244
5302
  supersedesId,
5303
+ validFrom,
5304
+ invalidAt,
5245
5305
  draft,
5246
5306
  memoryType,
5247
5307
  trajectory,
@@ -5249,8 +5309,8 @@ async function flushBatch() {
5249
5309
  ];
5250
5310
  return {
5251
5311
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5252
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5253
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5312
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5313
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5254
5314
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5255
5315
  };
5256
5316
  };
@@ -5366,6 +5426,12 @@ async function searchMemories(queryVector, agentId, options) {
5366
5426
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5367
5427
  AND COALESCE(confidence, 0.7) >= 0.3`;
5368
5428
  const args = [agentId];
5429
+ if (options?.asOf) {
5430
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5431
+ args.push(options.asOf, options.asOf);
5432
+ } else {
5433
+ sql += ` AND invalid_at IS NULL`;
5434
+ }
5369
5435
  const scope = buildWikiScopeFilter(options, "");
5370
5436
  sql += scope.clause;
5371
5437
  args.push(...scope.args);
@@ -5963,7 +6029,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
5963
6029
  import os8 from "os";
5964
6030
  import path11 from "path";
5965
6031
  import { jwtVerify, importSPKI } from "jose";
5966
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
6032
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE;
5967
6033
  var init_license = __esm({
5968
6034
  "src/lib/license.ts"() {
5969
6035
  "use strict";
@@ -5971,6 +6037,7 @@ var init_license = __esm({
5971
6037
  LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
5972
6038
  CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
5973
6039
  DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
6040
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
5974
6041
  }
5975
6042
  });
5976
6043
 
@@ -6014,6 +6081,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
6014
6081
  function getMySession() {
6015
6082
  return getTransport().getMySession();
6016
6083
  }
6084
+ function isRootSession(name) {
6085
+ return name.length > 0 && !name.includes("-");
6086
+ }
6017
6087
  function extractRootExe(name) {
6018
6088
  if (!name) return null;
6019
6089
  if (!name.includes("-")) return name;
@@ -6040,9 +6110,13 @@ function getDispatchedBy(sessionKey) {
6040
6110
  }
6041
6111
  }
6042
6112
  function resolveExeSession() {
6113
+ if (process.env.EXE_SESSION_NAME) {
6114
+ return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
6115
+ }
6043
6116
  const mySession = getMySession();
6044
6117
  if (!mySession) return null;
6045
6118
  const fromSessionName = extractRootExe(mySession);
6119
+ let candidate = null;
6046
6120
  try {
6047
6121
  const key = getSessionKey();
6048
6122
  const parentExe = getParentExe(key);
@@ -6053,13 +6127,47 @@ function resolveExeSession() {
6053
6127
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
6054
6128
  `
6055
6129
  );
6056
- return fromSessionName;
6130
+ candidate = fromSessionName;
6131
+ } else {
6132
+ candidate = fromCache;
6057
6133
  }
6058
- return fromCache;
6059
6134
  }
6060
6135
  } catch {
6061
6136
  }
6062
- return fromSessionName ?? mySession;
6137
+ if (!candidate) {
6138
+ candidate = fromSessionName ?? mySession;
6139
+ }
6140
+ if (candidate && isRootSession(candidate)) {
6141
+ try {
6142
+ const transport = getTransport();
6143
+ const liveSessions = transport.listSessions();
6144
+ if (!liveSessions.includes(candidate)) {
6145
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
6146
+ if (liveRoots.length === 1) {
6147
+ process.stderr.write(
6148
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
6149
+ `
6150
+ );
6151
+ return liveRoots[0];
6152
+ } else if (liveRoots.length > 1) {
6153
+ const base = candidate.replace(/\d+$/, "");
6154
+ const match = liveRoots.find((s) => s.startsWith(base));
6155
+ const chosen = match ?? liveRoots[0];
6156
+ process.stderr.write(
6157
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
6158
+ `
6159
+ );
6160
+ return chosen;
6161
+ }
6162
+ process.stderr.write(
6163
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
6164
+ `
6165
+ );
6166
+ }
6167
+ } catch {
6168
+ }
6169
+ }
6170
+ return candidate;
6063
6171
  }
6064
6172
  function readDebounceState() {
6065
6173
  try {
@@ -6505,7 +6613,9 @@ async function listTasks(input) {
6505
6613
  budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
6506
6614
  budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
6507
6615
  tokensUsed: Number(r.tokens_used ?? 0),
6508
- tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
6616
+ tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null,
6617
+ spawnRuntime: r.spawn_runtime !== null && r.spawn_runtime !== void 0 ? String(r.spawn_runtime) : null,
6618
+ spawnModel: r.spawn_model !== null && r.spawn_model !== void 0 ? String(r.spawn_model) : null
6509
6619
  }));
6510
6620
  }
6511
6621
  function isTmuxSessionAlive(identifier) {
@@ -7424,6 +7534,8 @@ async function updateTask(input) {
7424
7534
  budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
7425
7535
  tokensUsed: Number(row.tokens_used ?? 0),
7426
7536
  tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
7537
+ spawnRuntime: row.spawn_runtime !== void 0 && row.spawn_runtime !== null ? String(row.spawn_runtime) : null,
7538
+ spawnModel: row.spawn_model !== void 0 && row.spawn_model !== null ? String(row.spawn_model) : null,
7427
7539
  nextTask
7428
7540
  };
7429
7541
  }