@askexenow/exe-os 0.9.92 → 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 +1605 -456
  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 +57 -1
  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
@@ -3018,6 +3018,20 @@ async function ensureSchema() {
3018
3018
  });
3019
3019
  } catch {
3020
3020
  }
3021
+ try {
3022
+ await client.execute({
3023
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
3024
+ args: []
3025
+ });
3026
+ } catch {
3027
+ }
3028
+ try {
3029
+ await client.execute({
3030
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
3031
+ args: []
3032
+ });
3033
+ } catch {
3034
+ }
3021
3035
  await client.executeMultiple(`
3022
3036
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
3023
3037
  content_text,
@@ -3269,6 +3283,22 @@ async function ensureSchema() {
3269
3283
  );
3270
3284
  } catch {
3271
3285
  }
3286
+ for (const col of [
3287
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3288
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3289
+ ]) {
3290
+ try {
3291
+ await client.execute(col);
3292
+ } catch {
3293
+ }
3294
+ }
3295
+ try {
3296
+ await client.execute({
3297
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3298
+ args: []
3299
+ });
3300
+ } catch {
3301
+ }
3272
3302
  try {
3273
3303
  await client.execute({
3274
3304
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3311,6 +3341,13 @@ async function ensureSchema() {
3311
3341
  } catch {
3312
3342
  }
3313
3343
  }
3344
+ try {
3345
+ await client.execute({
3346
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3347
+ args: []
3348
+ });
3349
+ } catch {
3350
+ }
3314
3351
  try {
3315
3352
  await client.execute({
3316
3353
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -4449,6 +4486,20 @@ var init_platform_procedures = __esm({
4449
4486
  priority: "p1",
4450
4487
  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."
4451
4488
  },
4489
+ // --- Tool guidance ---
4490
+ {
4491
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4492
+ domain: "tools",
4493
+ priority: "p2",
4494
+ 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."
4495
+ },
4496
+ // --- Release awareness ---
4497
+ {
4498
+ title: "What's New check \u2014 surface new features after update",
4499
+ domain: "support",
4500
+ priority: "p1",
4501
+ 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."
4502
+ },
4452
4503
  // --- Platform vs Customer ownership ---
4453
4504
  {
4454
4505
  title: "What the platform provides vs what you customize",
@@ -4537,13 +4588,13 @@ var init_platform_procedures = __esm({
4537
4588
  title: "MCP tools \u2014 memory, decision, and search",
4538
4589
  domain: "tool-use",
4539
4590
  priority: "p1",
4540
- 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.`
4591
+ 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.`
4541
4592
  },
4542
4593
  {
4543
4594
  title: "MCP tools \u2014 task orchestration",
4544
4595
  domain: "tool-use",
4545
4596
  priority: "p1",
4546
- 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.'
4597
+ 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.`
4547
4598
  },
4548
4599
  {
4549
4600
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4573,7 +4624,7 @@ var init_platform_procedures = __esm({
4573
4624
  title: "MCP tools \u2014 admin, config, and operations",
4574
4625
  domain: "tool-use",
4575
4626
  priority: "p1",
4576
- 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.'
4627
+ 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.'
4577
4628
  }
4578
4629
  ];
4579
4630
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5257,6 +5308,8 @@ async function writeMemory(record) {
5257
5308
  source_type: record.source_type ?? null,
5258
5309
  tier: record.tier ?? classifyTier(record),
5259
5310
  supersedes_id: record.supersedes_id ?? null,
5311
+ valid_from: record.valid_from ?? record.timestamp,
5312
+ invalid_at: record.invalid_at ?? null,
5260
5313
  draft: record.draft ? 1 : 0,
5261
5314
  memory_type: memoryType,
5262
5315
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5275,7 +5328,8 @@ async function writeMemory(record) {
5275
5328
  token_cost: record.token_cost ?? null,
5276
5329
  audience: record.audience ?? null,
5277
5330
  language_type: record.language_type ?? inferLanguageType(record),
5278
- parent_memory_id: record.parent_memory_id ?? null
5331
+ parent_memory_id: record.parent_memory_id ?? null,
5332
+ procedure_for: record.procedure_for ?? null
5279
5333
  };
5280
5334
  _pendingRecords.push(dbRow);
5281
5335
  orgBus.emit({
@@ -5330,6 +5384,8 @@ async function flushBatch() {
5330
5384
  const sourceType = row.source_type ?? null;
5331
5385
  const tier = row.tier ?? 3;
5332
5386
  const supersedesId = row.supersedes_id ?? null;
5387
+ const validFrom = row.valid_from ?? row.timestamp;
5388
+ const invalidAt = row.invalid_at ?? null;
5333
5389
  const draft = row.draft ? 1 : 0;
5334
5390
  const memoryType = row.memory_type ?? "raw";
5335
5391
  const trajectory = row.trajectory ?? null;
@@ -5349,15 +5405,16 @@ async function flushBatch() {
5349
5405
  const audience = row.audience ?? null;
5350
5406
  const languageType = row.language_type ?? null;
5351
5407
  const parentMemoryId = row.parent_memory_id ?? null;
5408
+ const procedureFor = row.procedure_for ?? null;
5352
5409
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5353
5410
  tool_name, project_name,
5354
5411
  has_error, raw_text, vector, version, task_id, importance, status,
5355
5412
  confidence, last_accessed,
5356
5413
  workspace_id, document_id, user_id, char_offset, page_number,
5357
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5414
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5358
5415
  intent, outcome, domain, referenced_entities, retrieval_count,
5359
5416
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5360
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5417
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5361
5418
  const metaArgs = [
5362
5419
  intent,
5363
5420
  outcome,
@@ -5373,7 +5430,8 @@ async function flushBatch() {
5373
5430
  tokenCost,
5374
5431
  audience,
5375
5432
  languageType,
5376
- parentMemoryId
5433
+ parentMemoryId,
5434
+ procedureFor
5377
5435
  ];
5378
5436
  const baseArgs = [
5379
5437
  row.id,
@@ -5402,6 +5460,8 @@ async function flushBatch() {
5402
5460
  sourceType,
5403
5461
  tier,
5404
5462
  supersedesId,
5463
+ validFrom,
5464
+ invalidAt,
5405
5465
  draft,
5406
5466
  memoryType,
5407
5467
  trajectory,
@@ -5409,8 +5469,8 @@ async function flushBatch() {
5409
5469
  ];
5410
5470
  return {
5411
5471
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5412
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5413
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5472
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5473
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5414
5474
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5415
5475
  };
5416
5476
  };
@@ -5526,6 +5586,12 @@ async function searchMemories(queryVector, agentId, options) {
5526
5586
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5527
5587
  AND COALESCE(confidence, 0.7) >= 0.3`;
5528
5588
  const args = [agentId];
5589
+ if (options?.asOf) {
5590
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5591
+ args.push(options.asOf, options.asOf);
5592
+ } else {
5593
+ sql += ` AND invalid_at IS NULL`;
5594
+ }
5529
5595
  const scope = buildWikiScopeFilter(options, "");
5530
5596
  sql += scope.clause;
5531
5597
  args.push(...scope.args);
@@ -6850,6 +6916,19 @@ __export(hybrid_search_exports, {
6850
6916
  rrfMerge: () => rrfMerge,
6851
6917
  rrfMergeMulti: () => rrfMergeMulti
6852
6918
  });
6919
+ function buildTemporalFilter(options, columnPrefix) {
6920
+ const asOf = options?.asOf;
6921
+ if (asOf) {
6922
+ return {
6923
+ clause: ` AND (${columnPrefix}valid_from IS NULL OR ${columnPrefix}valid_from <= ?) AND (${columnPrefix}invalid_at IS NULL OR ${columnPrefix}invalid_at > ?)`,
6924
+ args: [asOf, asOf]
6925
+ };
6926
+ }
6927
+ return {
6928
+ clause: ` AND ${columnPrefix}invalid_at IS NULL`,
6929
+ args: []
6930
+ };
6931
+ }
6853
6932
  function appendMemoryTypeFilter(sql, args, column, options) {
6854
6933
  if (options?.memoryTypes && options.memoryTypes.length > 0) {
6855
6934
  const uniqueTypes = [...new Set(options.memoryTypes)];
@@ -7082,6 +7161,9 @@ async function estimateCardinality(agentId, options) {
7082
7161
  AND COALESCE(status, 'active') = 'active'
7083
7162
  AND COALESCE(confidence, 0.7) >= 0.3`;
7084
7163
  const args = [agentId];
7164
+ const temporal = buildTemporalFilter(options, "");
7165
+ sql += temporal.clause;
7166
+ args.push(...temporal.args);
7085
7167
  const rawVisibility = buildRawVisibilityFilter(options, "");
7086
7168
  sql += rawVisibility.clause;
7087
7169
  args.push(...rawVisibility.args);
@@ -7210,6 +7292,9 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
7210
7292
  AND m.agent_id = ?${statusFilter}${draftFilter}
7211
7293
  AND COALESCE(m.confidence, 0.7) >= 0.3`;
7212
7294
  const args = [matchExpr, agentId];
7295
+ const temporal = buildTemporalFilter(options, "m.");
7296
+ sql += temporal.clause;
7297
+ args.push(...temporal.args);
7213
7298
  const scope = buildWikiScopeFilter(options, "m.");
7214
7299
  sql += scope.clause;
7215
7300
  args.push(...scope.args);
@@ -7322,6 +7407,9 @@ async function recentRecords(agentId, options, limit, textFilter) {
7322
7407
  WHERE agent_id = ?${statusFilter}${draftFilter}
7323
7408
  AND COALESCE(confidence, 0.7) >= 0.3`;
7324
7409
  const args = [agentId];
7410
+ const temporal = buildTemporalFilter(options, "");
7411
+ sql += temporal.clause;
7412
+ args.push(...temporal.args);
7325
7413
  const scope = buildWikiScopeFilter(options, "");
7326
7414
  sql += scope.clause;
7327
7415
  args.push(...scope.args);
@@ -7422,6 +7510,9 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
7422
7510
  AND json_extract(trajectory, '$.tool') = ?
7423
7511
  AND agent_id = ?${statusFilter}${draftFilter}`;
7424
7512
  const args = [toolName, agentId];
7513
+ const temporal = buildTemporalFilter(options, "");
7514
+ sql += temporal.clause;
7515
+ args.push(...temporal.args);
7425
7516
  const rawVisibility = buildRawVisibilityFilter(options, "");
7426
7517
  sql += rawVisibility.clause;
7427
7518
  args.push(...rawVisibility.args);
@@ -7754,6 +7845,236 @@ var init_active_agent2 = __esm({
7754
7845
  }
7755
7846
  });
7756
7847
 
7848
+ // src/mcp/tool-gates.ts
7849
+ var tool_gates_exports = {};
7850
+ __export(tool_gates_exports, {
7851
+ TOOL_CATEGORIES: () => TOOL_CATEGORIES,
7852
+ TOOL_GATES: () => TOOL_GATES,
7853
+ isToolAllowed: () => isToolAllowed,
7854
+ isToolAllowedForPlan: () => isToolAllowedForPlan
7855
+ });
7856
+ function isToolAllowedForPlan(registerFnName, plan) {
7857
+ const category = TOOL_CATEGORIES[registerFnName];
7858
+ if (!category) return true;
7859
+ const requiredPlan = PLAN_GATES[category] ?? "free";
7860
+ return PLAN_TIERS[plan] >= PLAN_TIERS[requiredPlan];
7861
+ }
7862
+ function isChiefOfStaffRole(role) {
7863
+ const normalized = (role ?? "").trim().toLowerCase();
7864
+ return normalized === "chief of staff" || normalized === "executive assistant";
7865
+ }
7866
+ function isToolAllowed(registerFnName) {
7867
+ const role = process.env.AGENT_ROLE;
7868
+ if (!role) return true;
7869
+ if (isChiefOfStaffRole(role)) {
7870
+ return CHIEF_OF_STAFF_READ_TOOLS.has(registerFnName);
7871
+ }
7872
+ const category = TOOL_CATEGORIES[registerFnName];
7873
+ if (!category) return true;
7874
+ const allowedRoles = TOOL_GATES[category];
7875
+ if (!allowedRoles || allowedRoles.length === 0) return true;
7876
+ return allowedRoles.includes(role);
7877
+ }
7878
+ var TOOL_GATES, TOOL_CATEGORIES, PLAN_TIERS, PLAN_GATES, CHIEF_OF_STAFF_READ_TOOLS;
7879
+ var init_tool_gates = __esm({
7880
+ "src/mcp/tool-gates.ts"() {
7881
+ "use strict";
7882
+ TOOL_GATES = {
7883
+ core: [],
7884
+ // all agents — recall, store, ask_team, commit, search, session, consolidate, cardinality, behavior, identity, decisions
7885
+ tasks: [],
7886
+ // all agents — create, list, get, update, close, checkpoint, resume
7887
+ comms: [],
7888
+ // all agents — send_message, acknowledge
7889
+ reminders: [],
7890
+ // all agents — create, list, complete, reminder
7891
+ "graph-read": [],
7892
+ // all agents — query_relationships, get_entity_neighbors, get_hot_entities, get_graph_stats, find_similar_trajectories, code_context
7893
+ "graph-write": ["COO", "CTO"],
7894
+ // merge_entities, export_graph
7895
+ wiki: ["COO", "Wiki Agent"],
7896
+ // read/list curated wiki pages
7897
+ crm: ["COO", "CRM Agent"],
7898
+ // CRM read/list/get and legacy add/list/get person
7899
+ "raw-data": ["COO", "CTO", "Gateway Agent", "CRM Agent", "Wiki Agent"],
7900
+ // capped read-only raw landing-pad access
7901
+ documents: ["COO", "CTO", "Wiki Agent"],
7902
+ // ingest, list, purge, set_importance, rerank
7903
+ gateway: ["COO", "Gateway Agent"],
7904
+ // send_whatsapp, query_conversations, query_company_brain, company_actions
7905
+ admin: ["COO"],
7906
+ // deploy, backup, daemon health, auto-wake, worker gate, memory audit, consolidation, cloud sync, agent config, list employees, agent spend, sessions, session kills
7907
+ licensing: ["COO"],
7908
+ // get/create/list/activate license
7909
+ orchestration: ["COO"]
7910
+ // triggers, load_skill, starter_pack, export/import orchestration, global procedures
7911
+ };
7912
+ TOOL_CATEGORIES = {
7913
+ // core
7914
+ registerMemory: "core",
7915
+ registerRecallMyMemory: "core",
7916
+ registerAskTeamMemory: "core",
7917
+ registerGetSessionContext: "core",
7918
+ registerGetMemoryById: "core",
7919
+ registerStoreMemory: "core",
7920
+ registerCommitMemory: "core",
7921
+ registerSearchEverything: "core",
7922
+ registerConsolidateMemories: "core",
7923
+ registerGetMemoryCardinality: "core",
7924
+ registerStoreBehavior: "core",
7925
+ registerListBehaviors: "core",
7926
+ registerDeactivateBehavior: "core",
7927
+ registerBehavior: "core",
7928
+ registerStoreDecision: "core",
7929
+ registerGetDecision: "core",
7930
+ registerCreateBugReport: "core",
7931
+ registerSupportTools: "core",
7932
+ registerCliParityTools: "admin",
7933
+ registerGetSessionEvents: "core",
7934
+ registerGetLastAssistantResponse: "core",
7935
+ registerCodeContext: "graph-read",
7936
+ // consolidated tools
7937
+ registerDecision: "core",
7938
+ registerSession: "core",
7939
+ registerSupportConsolidated: "core",
7940
+ registerDiagnostics: "admin",
7941
+ registerListBugReports: "admin",
7942
+ registerGetBugReport: "admin",
7943
+ registerTriageBugReport: "admin",
7944
+ registerIdentity: "core",
7945
+ registerGetIdentity: "core",
7946
+ registerUpdateIdentity: "core",
7947
+ // tasks
7948
+ registerTask: "tasks",
7949
+ registerCreateTask: "tasks",
7950
+ registerListTasks: "tasks",
7951
+ registerUpdateTask: "tasks",
7952
+ registerCloseTask: "tasks",
7953
+ registerGetTask: "tasks",
7954
+ registerCheckpointTask: "tasks",
7955
+ registerResumeEmployee: "tasks",
7956
+ // comms
7957
+ registerMessage: "comms",
7958
+ registerSendMessage: "comms",
7959
+ registerAcknowledgeMessages: "comms",
7960
+ // reminders
7961
+ registerCreateReminder: "reminders",
7962
+ registerListReminders: "reminders",
7963
+ registerCompleteReminder: "reminders",
7964
+ registerReminder: "reminders",
7965
+ // graph-read
7966
+ registerGraph: "graph-read",
7967
+ registerQueryRelationships: "graph-read",
7968
+ registerGetEntityNeighbors: "graph-read",
7969
+ registerGetHotEntities: "graph-read",
7970
+ registerGetGraphStats: "graph-read",
7971
+ registerFindSimilarTrajectories: "graph-read",
7972
+ // graph-write
7973
+ registerMergeEntities: "graph-write",
7974
+ registerExportGraph: "graph-write",
7975
+ // wiki
7976
+ registerWiki: "wiki",
7977
+ registerCreateWikiPage: "wiki",
7978
+ registerListWikiPages: "wiki",
7979
+ registerGetWikiPage: "wiki",
7980
+ // crm
7981
+ registerCrm: "crm",
7982
+ registerRawData: "raw-data",
7983
+ registerAddPerson: "crm",
7984
+ registerListPeople: "crm",
7985
+ registerGetPerson: "crm",
7986
+ // documents
7987
+ registerDocument: "documents",
7988
+ registerIngestDocument: "documents",
7989
+ registerListDocuments: "documents",
7990
+ registerPurgeDocument: "documents",
7991
+ registerSetDocumentImportance: "documents",
7992
+ registerRerankDocuments: "documents",
7993
+ // gateway
7994
+ registerGateway: "gateway",
7995
+ registerSendWhatsapp: "gateway",
7996
+ registerQueryConversations: "gateway",
7997
+ registerQueryCompanyBrain: "gateway",
7998
+ registerCompanyActions: "gateway",
7999
+ // admin
8000
+ registerConfig: "admin",
8001
+ registerDeployClient: "admin",
8002
+ registerBackupVps: "admin",
8003
+ registerGetDaemonHealth: "admin",
8004
+ registerMcpPing: "core",
8005
+ registerGetAutoWakeStatus: "admin",
8006
+ registerGetWorkerGate: "admin",
8007
+ registerRunMemoryAudit: "admin",
8008
+ registerRunConsolidation: "admin",
8009
+ registerCloudSync: "admin",
8010
+ registerOrchestrationPhase: "admin",
8011
+ registerSetAgentConfig: "admin",
8012
+ registerListEmployees: "admin",
8013
+ registerGetAgentSpend: "admin",
8014
+ registerListAgentSessions: "admin",
8015
+ registerGetSessionKills: "admin",
8016
+ // licensing
8017
+ registerGetLicenseStatus: "licensing",
8018
+ registerCreateLicense: "licensing",
8019
+ registerListLicenses: "licensing",
8020
+ registerActivateLicense: "licensing",
8021
+ // orchestration
8022
+ registerCreateTrigger: "orchestration",
8023
+ registerListTriggers: "orchestration",
8024
+ registerApplyStarterPack: "orchestration",
8025
+ registerLoadSkill: "orchestration",
8026
+ registerExportOrchestration: "orchestration",
8027
+ registerImportOrchestration: "orchestration",
8028
+ registerCompanyProcedure: "orchestration",
8029
+ registerGlobalProcedure: "orchestration",
8030
+ registerStoreGlobalProcedure: "orchestration",
8031
+ registerListGlobalProcedures: "orchestration",
8032
+ registerDeactivateGlobalProcedure: "orchestration"
8033
+ };
8034
+ PLAN_TIERS = {
8035
+ free: 0,
8036
+ pro: 1,
8037
+ team: 2,
8038
+ agency: 3,
8039
+ enterprise: 4
8040
+ };
8041
+ PLAN_GATES = {
8042
+ core: "free",
8043
+ tasks: "free",
8044
+ comms: "free",
8045
+ reminders: "free",
8046
+ "graph-read": "free",
8047
+ licensing: "free",
8048
+ // always allow checking/managing own license
8049
+ "graph-write": "pro",
8050
+ wiki: "pro",
8051
+ crm: "pro",
8052
+ "raw-data": "pro",
8053
+ documents: "pro",
8054
+ gateway: "pro",
8055
+ admin: "pro",
8056
+ orchestration: "pro"
8057
+ };
8058
+ CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
8059
+ "registerRecallMyMemory",
8060
+ "registerAskTeamMemory",
8061
+ "registerGetMemoryById",
8062
+ "registerGetSessionContext",
8063
+ "registerGetSessionEvents",
8064
+ "registerGetLastAssistantResponse",
8065
+ "registerSearchEverything",
8066
+ "registerGetDecision",
8067
+ "registerGetIdentity",
8068
+ "registerMcpPing",
8069
+ "registerListTasks",
8070
+ "registerGetTask",
8071
+ "registerListReminders",
8072
+ "registerQueryConversations",
8073
+ "registerQueryCompanyBrain"
8074
+ ]);
8075
+ }
8076
+ });
8077
+
7757
8078
  // src/bin/fast-db-init.ts
7758
8079
  var fast_db_init_exports = {};
7759
8080
  __export(fast_db_init_exports, {
@@ -8036,7 +8357,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
8036
8357
  import os8 from "os";
8037
8358
  import path15 from "path";
8038
8359
  import { jwtVerify, importSPKI } from "jose";
8039
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
8360
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE;
8040
8361
  var init_license = __esm({
8041
8362
  "src/lib/license.ts"() {
8042
8363
  "use strict";
@@ -8044,6 +8365,7 @@ var init_license = __esm({
8044
8365
  LICENSE_PATH = path15.join(EXE_AI_DIR, "license.key");
8045
8366
  CACHE_PATH = path15.join(EXE_AI_DIR, "license-cache.json");
8046
8367
  DEVICE_ID_PATH = path15.join(EXE_AI_DIR, "device-id");
8368
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
8047
8369
  }
8048
8370
  });
8049
8371
 
@@ -8087,6 +8409,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
8087
8409
  function getMySession() {
8088
8410
  return getTransport().getMySession();
8089
8411
  }
8412
+ function isRootSession(name) {
8413
+ return name.length > 0 && !name.includes("-");
8414
+ }
8090
8415
  function extractRootExe(name) {
8091
8416
  if (!name) return null;
8092
8417
  if (!name.includes("-")) return name;
@@ -8105,6 +8430,7 @@ function resolveExeSession() {
8105
8430
  const mySession = getMySession();
8106
8431
  if (!mySession) return null;
8107
8432
  const fromSessionName = extractRootExe(mySession);
8433
+ let candidate = null;
8108
8434
  try {
8109
8435
  const key = getSessionKey();
8110
8436
  const parentExe = getParentExe(key);
@@ -8115,13 +8441,47 @@ function resolveExeSession() {
8115
8441
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
8116
8442
  `
8117
8443
  );
8118
- return fromSessionName;
8444
+ candidate = fromSessionName;
8445
+ } else {
8446
+ candidate = fromCache;
8119
8447
  }
8120
- return fromCache;
8121
8448
  }
8122
8449
  } catch {
8123
8450
  }
8124
- return fromSessionName ?? mySession;
8451
+ if (!candidate) {
8452
+ candidate = fromSessionName ?? mySession;
8453
+ }
8454
+ if (candidate && isRootSession(candidate)) {
8455
+ try {
8456
+ const transport = getTransport();
8457
+ const liveSessions = transport.listSessions();
8458
+ if (!liveSessions.includes(candidate)) {
8459
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
8460
+ if (liveRoots.length === 1) {
8461
+ process.stderr.write(
8462
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
8463
+ `
8464
+ );
8465
+ return liveRoots[0];
8466
+ } else if (liveRoots.length > 1) {
8467
+ const base = candidate.replace(/\d+$/, "");
8468
+ const match = liveRoots.find((s) => s.startsWith(base));
8469
+ const chosen = match ?? liveRoots[0];
8470
+ process.stderr.write(
8471
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
8472
+ `
8473
+ );
8474
+ return chosen;
8475
+ }
8476
+ process.stderr.write(
8477
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
8478
+ `
8479
+ );
8480
+ }
8481
+ } catch {
8482
+ }
8483
+ }
8484
+ return candidate;
8125
8485
  }
8126
8486
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
8127
8487
  var init_tmux_routing = __esm({
@@ -8651,6 +9011,66 @@ ${context}`
8651
9011
  }
8652
9012
  } catch {
8653
9013
  }
9014
+ try {
9015
+ const payload = JSON.parse(input);
9016
+ const toolName = String(payload.tool_name ?? "");
9017
+ const { getActiveAgent: getActiveAgent2 } = await Promise.resolve().then(() => (init_active_agent2(), active_agent_exports));
9018
+ const driftAgent = getActiveAgent2();
9019
+ if (driftAgent.agentId !== "default" && toolName) {
9020
+ const { existsSync: driftExists, readFileSync: driftRead, writeFileSync: driftWrite, mkdirSync: driftMkdir } = await import("fs");
9021
+ const driftPath = await import("path");
9022
+ const { TOOL_CATEGORIES: TOOL_CATEGORIES2, TOOL_GATES: TOOL_GATES2 } = await Promise.resolve().then(() => (init_tool_gates(), tool_gates_exports));
9023
+ const normalized = toolName.replace(/[_-]/g, "").toLowerCase();
9024
+ let toolCategory = "unknown";
9025
+ for (const [registerFn, cat] of Object.entries(TOOL_CATEGORIES2)) {
9026
+ const fnNormalized = registerFn.replace(/^register/, "").toLowerCase();
9027
+ if (fnNormalized === normalized) {
9028
+ toolCategory = cat;
9029
+ break;
9030
+ }
9031
+ }
9032
+ const gateRoles = TOOL_GATES2[toolCategory];
9033
+ const isRestricted = gateRoles && gateRoles.length > 0;
9034
+ const isAllowed = !isRestricted || gateRoles.includes(driftAgent.agentRole);
9035
+ if (isRestricted && !isAllowed) {
9036
+ const cacheDir = driftPath.join(
9037
+ process.env.EXE_OS_DIR ?? driftPath.join(process.env.HOME ?? "", ".exe-os"),
9038
+ "session-cache"
9039
+ );
9040
+ driftMkdir(cacheDir, { recursive: true });
9041
+ const counterFile = driftPath.join(cacheDir, `drift-counter-${driftAgent.agentId}.json`);
9042
+ let mismatches = [];
9043
+ try {
9044
+ if (driftExists(counterFile)) {
9045
+ const data = JSON.parse(driftRead(counterFile, "utf8"));
9046
+ mismatches = Array.isArray(data.mismatches) ? data.mismatches : [];
9047
+ }
9048
+ } catch {
9049
+ }
9050
+ mismatches.push(`${toolName} (${toolCategory})`);
9051
+ driftWrite(counterFile, JSON.stringify({
9052
+ mismatches,
9053
+ count: mismatches.length,
9054
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9055
+ }));
9056
+ const MISMATCH_THRESHOLD = 3;
9057
+ if (mismatches.length >= MISMATCH_THRESHOLD) {
9058
+ process.stderr.write(
9059
+ `[post-tool] ${driftAgent.agentId} has ${mismatches.length} tool-category mismatches: ${mismatches.slice(-3).join(", ")}. Injecting scope reminder.
9060
+ `
9061
+ );
9062
+ process.stdout.write(JSON.stringify({
9063
+ hookSpecificOutput: {
9064
+ hookEventName: "PostToolUse",
9065
+ additionalContext: `## Scope Reminder
9066
+ You are **${driftAgent.agentId}** (${driftAgent.agentRole}). You've used ${mismatches.length} tools outside your expected scope: ${mismatches.slice(-3).join(", ")}. Stay within your role. If this work was delegated to you, continue \u2014 otherwise, route it to the appropriate team member.`
9067
+ }
9068
+ }));
9069
+ }
9070
+ }
9071
+ }
9072
+ } catch {
9073
+ }
8654
9074
  try {
8655
9075
  const data = JSON.parse(input);
8656
9076
  const { getActiveAgent: getActiveAgent2 } = await Promise.resolve().then(() => (init_active_agent2(), active_agent_exports));