@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
@@ -560,7 +560,7 @@ var init_license = __esm({
560
560
  LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
561
561
  CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
562
562
  DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
563
- API_BASE = "https://askexe.com/cloud";
563
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
564
564
  RETRY_DELAY_MS = 500;
565
565
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
566
566
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -3420,6 +3420,20 @@ async function ensureSchema() {
3420
3420
  });
3421
3421
  } catch {
3422
3422
  }
3423
+ try {
3424
+ await client.execute({
3425
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
3426
+ args: []
3427
+ });
3428
+ } catch {
3429
+ }
3430
+ try {
3431
+ await client.execute({
3432
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
3433
+ args: []
3434
+ });
3435
+ } catch {
3436
+ }
3423
3437
  await client.executeMultiple(`
3424
3438
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
3425
3439
  content_text,
@@ -3671,6 +3685,22 @@ async function ensureSchema() {
3671
3685
  );
3672
3686
  } catch {
3673
3687
  }
3688
+ for (const col of [
3689
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3690
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3691
+ ]) {
3692
+ try {
3693
+ await client.execute(col);
3694
+ } catch {
3695
+ }
3696
+ }
3697
+ try {
3698
+ await client.execute({
3699
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3700
+ args: []
3701
+ });
3702
+ } catch {
3703
+ }
3674
3704
  try {
3675
3705
  await client.execute({
3676
3706
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3713,6 +3743,13 @@ async function ensureSchema() {
3713
3743
  } catch {
3714
3744
  }
3715
3745
  }
3746
+ try {
3747
+ await client.execute({
3748
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3749
+ args: []
3750
+ });
3751
+ } catch {
3752
+ }
3716
3753
  try {
3717
3754
  await client.execute({
3718
3755
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -4864,6 +4901,20 @@ var init_platform_procedures = __esm({
4864
4901
  priority: "p1",
4865
4902
  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."
4866
4903
  },
4904
+ // --- Tool guidance ---
4905
+ {
4906
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4907
+ domain: "tools",
4908
+ priority: "p2",
4909
+ 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."
4910
+ },
4911
+ // --- Release awareness ---
4912
+ {
4913
+ title: "What's New check \u2014 surface new features after update",
4914
+ domain: "support",
4915
+ priority: "p1",
4916
+ 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."
4917
+ },
4867
4918
  // --- Platform vs Customer ownership ---
4868
4919
  {
4869
4920
  title: "What the platform provides vs what you customize",
@@ -4952,13 +5003,13 @@ var init_platform_procedures = __esm({
4952
5003
  title: "MCP tools \u2014 memory, decision, and search",
4953
5004
  domain: "tool-use",
4954
5005
  priority: "p1",
4955
- 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.`
5006
+ 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.`
4956
5007
  },
4957
5008
  {
4958
5009
  title: "MCP tools \u2014 task orchestration",
4959
5010
  domain: "tool-use",
4960
5011
  priority: "p1",
4961
- 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.'
5012
+ 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.`
4962
5013
  },
4963
5014
  {
4964
5015
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4988,7 +5039,7 @@ var init_platform_procedures = __esm({
4988
5039
  title: "MCP tools \u2014 admin, config, and operations",
4989
5040
  domain: "tool-use",
4990
5041
  priority: "p1",
4991
- 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.'
5042
+ 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.'
4992
5043
  }
4993
5044
  ];
4994
5045
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5672,6 +5723,8 @@ async function writeMemory(record) {
5672
5723
  source_type: record.source_type ?? null,
5673
5724
  tier: record.tier ?? classifyTier(record),
5674
5725
  supersedes_id: record.supersedes_id ?? null,
5726
+ valid_from: record.valid_from ?? record.timestamp,
5727
+ invalid_at: record.invalid_at ?? null,
5675
5728
  draft: record.draft ? 1 : 0,
5676
5729
  memory_type: memoryType,
5677
5730
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5690,7 +5743,8 @@ async function writeMemory(record) {
5690
5743
  token_cost: record.token_cost ?? null,
5691
5744
  audience: record.audience ?? null,
5692
5745
  language_type: record.language_type ?? inferLanguageType(record),
5693
- parent_memory_id: record.parent_memory_id ?? null
5746
+ parent_memory_id: record.parent_memory_id ?? null,
5747
+ procedure_for: record.procedure_for ?? null
5694
5748
  };
5695
5749
  _pendingRecords.push(dbRow);
5696
5750
  orgBus.emit({
@@ -5745,6 +5799,8 @@ async function flushBatch() {
5745
5799
  const sourceType = row.source_type ?? null;
5746
5800
  const tier = row.tier ?? 3;
5747
5801
  const supersedesId = row.supersedes_id ?? null;
5802
+ const validFrom = row.valid_from ?? row.timestamp;
5803
+ const invalidAt = row.invalid_at ?? null;
5748
5804
  const draft = row.draft ? 1 : 0;
5749
5805
  const memoryType = row.memory_type ?? "raw";
5750
5806
  const trajectory = row.trajectory ?? null;
@@ -5764,15 +5820,16 @@ async function flushBatch() {
5764
5820
  const audience = row.audience ?? null;
5765
5821
  const languageType = row.language_type ?? null;
5766
5822
  const parentMemoryId = row.parent_memory_id ?? null;
5823
+ const procedureFor = row.procedure_for ?? null;
5767
5824
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5768
5825
  tool_name, project_name,
5769
5826
  has_error, raw_text, vector, version, task_id, importance, status,
5770
5827
  confidence, last_accessed,
5771
5828
  workspace_id, document_id, user_id, char_offset, page_number,
5772
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5829
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5773
5830
  intent, outcome, domain, referenced_entities, retrieval_count,
5774
5831
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5775
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5832
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5776
5833
  const metaArgs = [
5777
5834
  intent,
5778
5835
  outcome,
@@ -5788,7 +5845,8 @@ async function flushBatch() {
5788
5845
  tokenCost,
5789
5846
  audience,
5790
5847
  languageType,
5791
- parentMemoryId
5848
+ parentMemoryId,
5849
+ procedureFor
5792
5850
  ];
5793
5851
  const baseArgs = [
5794
5852
  row.id,
@@ -5817,6 +5875,8 @@ async function flushBatch() {
5817
5875
  sourceType,
5818
5876
  tier,
5819
5877
  supersedesId,
5878
+ validFrom,
5879
+ invalidAt,
5820
5880
  draft,
5821
5881
  memoryType,
5822
5882
  trajectory,
@@ -5824,8 +5884,8 @@ async function flushBatch() {
5824
5884
  ];
5825
5885
  return {
5826
5886
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5827
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5828
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5887
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5888
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5829
5889
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5830
5890
  };
5831
5891
  };
@@ -5941,6 +6001,12 @@ async function searchMemories(queryVector, agentId, options) {
5941
6001
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5942
6002
  AND COALESCE(confidence, 0.7) >= 0.3`;
5943
6003
  const args = [agentId];
6004
+ if (options?.asOf) {
6005
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
6006
+ args.push(options.asOf, options.asOf);
6007
+ } else {
6008
+ sql += ` AND invalid_at IS NULL`;
6009
+ }
5944
6010
  const scope = buildWikiScopeFilter(options, "");
5945
6011
  sql += scope.clause;
5946
6012
  args.push(...scope.args);
@@ -7739,7 +7805,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
7739
7805
  import os9 from "os";
7740
7806
  import path12 from "path";
7741
7807
  import { jwtVerify, importSPKI } from "jose";
7742
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
7808
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, PLAN_LIMITS;
7743
7809
  var init_license = __esm({
7744
7810
  "src/lib/license.ts"() {
7745
7811
  "use strict";
@@ -7747,6 +7813,7 @@ var init_license = __esm({
7747
7813
  LICENSE_PATH = path12.join(EXE_AI_DIR, "license.key");
7748
7814
  CACHE_PATH = path12.join(EXE_AI_DIR, "license-cache.json");
7749
7815
  DEVICE_ID_PATH = path12.join(EXE_AI_DIR, "device-id");
7816
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
7750
7817
  PLAN_LIMITS = {
7751
7818
  free: { devices: 1, employees: 1, memories: 5e3 },
7752
7819
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -8342,8 +8409,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
8342
8409
  const complexity = input.complexity ?? "standard";
8343
8410
  const sessionScope = earlySessionScope;
8344
8411
  await client.execute({
8345
- 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)
8346
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8412
+ 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)
8413
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8347
8414
  args: [
8348
8415
  id,
8349
8416
  input.title,
@@ -8363,6 +8430,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
8363
8430
  0,
8364
8431
  null,
8365
8432
  sessionScope,
8433
+ input.spawnRuntime ?? null,
8434
+ input.spawnModel ?? null,
8366
8435
  now,
8367
8436
  now
8368
8437
  ]
@@ -8419,7 +8488,9 @@ ${input.context}
8419
8488
  budgetTokens: input.budgetTokens ?? null,
8420
8489
  budgetFallbackModel: input.budgetFallbackModel ?? null,
8421
8490
  tokensUsed: 0,
8422
- tokensWarnedAt: null
8491
+ tokensWarnedAt: null,
8492
+ spawnRuntime: input.spawnRuntime ?? null,
8493
+ spawnModel: input.spawnModel ?? null
8423
8494
  };
8424
8495
  }
8425
8496
  async function listTasks(input) {
@@ -8469,7 +8540,9 @@ async function listTasks(input) {
8469
8540
  budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
8470
8541
  budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
8471
8542
  tokensUsed: Number(r.tokens_used ?? 0),
8472
- tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
8543
+ tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null,
8544
+ spawnRuntime: r.spawn_runtime !== null && r.spawn_runtime !== void 0 ? String(r.spawn_runtime) : null,
8545
+ spawnModel: r.spawn_model !== null && r.spawn_model !== void 0 ? String(r.spawn_model) : null
8473
8546
  }));
8474
8547
  }
8475
8548
  function isTmuxSessionAlive(identifier) {
@@ -9698,6 +9771,8 @@ async function updateTask(input) {
9698
9771
  budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
9699
9772
  tokensUsed: Number(row.tokens_used ?? 0),
9700
9773
  tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
9774
+ spawnRuntime: row.spawn_runtime !== void 0 && row.spawn_runtime !== null ? String(row.spawn_runtime) : null,
9775
+ spawnModel: row.spawn_model !== void 0 && row.spawn_model !== null ? String(row.spawn_model) : null,
9701
9776
  nextTask
9702
9777
  };
9703
9778
  }
@@ -10193,6 +10268,7 @@ function resolveExeSession() {
10193
10268
  const mySession = getMySession();
10194
10269
  if (!mySession) return null;
10195
10270
  const fromSessionName = extractRootExe(mySession);
10271
+ let candidate = null;
10196
10272
  try {
10197
10273
  const key = getSessionKey();
10198
10274
  const parentExe = getParentExe(key);
@@ -10203,13 +10279,47 @@ function resolveExeSession() {
10203
10279
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
10204
10280
  `
10205
10281
  );
10206
- return fromSessionName;
10282
+ candidate = fromSessionName;
10283
+ } else {
10284
+ candidate = fromCache;
10207
10285
  }
10208
- return fromCache;
10209
10286
  }
10210
10287
  } catch {
10211
10288
  }
10212
- return fromSessionName ?? mySession;
10289
+ if (!candidate) {
10290
+ candidate = fromSessionName ?? mySession;
10291
+ }
10292
+ if (candidate && isRootSession(candidate)) {
10293
+ try {
10294
+ const transport = getTransport();
10295
+ const liveSessions = transport.listSessions();
10296
+ if (!liveSessions.includes(candidate)) {
10297
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
10298
+ if (liveRoots.length === 1) {
10299
+ process.stderr.write(
10300
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
10301
+ `
10302
+ );
10303
+ return liveRoots[0];
10304
+ } else if (liveRoots.length > 1) {
10305
+ const base = candidate.replace(/\d+$/, "");
10306
+ const match = liveRoots.find((s) => s.startsWith(base));
10307
+ const chosen = match ?? liveRoots[0];
10308
+ process.stderr.write(
10309
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
10310
+ `
10311
+ );
10312
+ return chosen;
10313
+ }
10314
+ process.stderr.write(
10315
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
10316
+ `
10317
+ );
10318
+ }
10319
+ } catch {
10320
+ }
10321
+ }
10322
+ return candidate;
10213
10323
  }
10214
10324
  function isEmployeeAlive(sessionName) {
10215
10325
  return getTransport().isAlive(sessionName);
@@ -10611,7 +10721,12 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
10611
10721
  }
10612
10722
  const spawnCwd = opts?.cwd ?? projectDir;
10613
10723
  const useExeAgent = !!(opts?.model && opts?.provider);
10614
- const agentRtConfig = getAgentRuntime(employeeName);
10724
+ const baseRtConfig = getAgentRuntime(employeeName);
10725
+ const agentRtConfig = {
10726
+ ...baseRtConfig,
10727
+ ...opts?.runtimeOverride ? { runtime: opts.runtimeOverride } : {},
10728
+ ...opts?.modelOverride ? { model: opts.modelOverride } : {}
10729
+ };
10615
10730
  const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
10616
10731
  const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
10617
10732
  const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();