@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
@@ -2776,6 +2776,20 @@ async function ensureSchema() {
2776
2776
  });
2777
2777
  } catch {
2778
2778
  }
2779
+ try {
2780
+ await client.execute({
2781
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2782
+ args: []
2783
+ });
2784
+ } catch {
2785
+ }
2786
+ try {
2787
+ await client.execute({
2788
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2789
+ args: []
2790
+ });
2791
+ } catch {
2792
+ }
2779
2793
  await client.executeMultiple(`
2780
2794
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2781
2795
  content_text,
@@ -3027,6 +3041,22 @@ async function ensureSchema() {
3027
3041
  );
3028
3042
  } catch {
3029
3043
  }
3044
+ for (const col of [
3045
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3046
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3047
+ ]) {
3048
+ try {
3049
+ await client.execute(col);
3050
+ } catch {
3051
+ }
3052
+ }
3053
+ try {
3054
+ await client.execute({
3055
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3056
+ args: []
3057
+ });
3058
+ } catch {
3059
+ }
3030
3060
  try {
3031
3061
  await client.execute({
3032
3062
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3069,6 +3099,13 @@ async function ensureSchema() {
3069
3099
  } catch {
3070
3100
  }
3071
3101
  }
3102
+ try {
3103
+ await client.execute({
3104
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3105
+ args: []
3106
+ });
3107
+ } catch {
3108
+ }
3072
3109
  try {
3073
3110
  await client.execute({
3074
3111
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -3413,7 +3450,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3413
3450
  import os7 from "os";
3414
3451
  import path9 from "path";
3415
3452
  import { jwtVerify, importSPKI } from "jose";
3416
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
3453
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE;
3417
3454
  var init_license = __esm({
3418
3455
  "src/lib/license.ts"() {
3419
3456
  "use strict";
@@ -3421,6 +3458,7 @@ var init_license = __esm({
3421
3458
  LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3422
3459
  CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3423
3460
  DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3461
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3424
3462
  }
3425
3463
  });
3426
3464
 
@@ -3464,6 +3502,9 @@ import { fileURLToPath as fileURLToPath3 } from "url";
3464
3502
  function getMySession() {
3465
3503
  return getTransport().getMySession();
3466
3504
  }
3505
+ function isRootSession(name) {
3506
+ return name.length > 0 && !name.includes("-");
3507
+ }
3467
3508
  function extractRootExe(name) {
3468
3509
  if (!name) return null;
3469
3510
  if (!name.includes("-")) return name;
@@ -3479,9 +3520,13 @@ function getParentExe(sessionKey) {
3479
3520
  }
3480
3521
  }
3481
3522
  function resolveExeSession() {
3523
+ if (process.env.EXE_SESSION_NAME) {
3524
+ return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
3525
+ }
3482
3526
  const mySession = getMySession();
3483
3527
  if (!mySession) return null;
3484
3528
  const fromSessionName = extractRootExe(mySession);
3529
+ let candidate = null;
3485
3530
  try {
3486
3531
  const key = getSessionKey();
3487
3532
  const parentExe = getParentExe(key);
@@ -3492,13 +3537,47 @@ function resolveExeSession() {
3492
3537
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
3493
3538
  `
3494
3539
  );
3495
- return fromSessionName;
3540
+ candidate = fromSessionName;
3541
+ } else {
3542
+ candidate = fromCache;
3496
3543
  }
3497
- return fromCache;
3498
3544
  }
3499
3545
  } catch {
3500
3546
  }
3501
- return fromSessionName ?? mySession;
3547
+ if (!candidate) {
3548
+ candidate = fromSessionName ?? mySession;
3549
+ }
3550
+ if (candidate && isRootSession(candidate)) {
3551
+ try {
3552
+ const transport = getTransport();
3553
+ const liveSessions = transport.listSessions();
3554
+ if (!liveSessions.includes(candidate)) {
3555
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
3556
+ if (liveRoots.length === 1) {
3557
+ process.stderr.write(
3558
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
3559
+ `
3560
+ );
3561
+ return liveRoots[0];
3562
+ } else if (liveRoots.length > 1) {
3563
+ const base = candidate.replace(/\d+$/, "");
3564
+ const match = liveRoots.find((s) => s.startsWith(base));
3565
+ const chosen = match ?? liveRoots[0];
3566
+ process.stderr.write(
3567
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
3568
+ `
3569
+ );
3570
+ return chosen;
3571
+ }
3572
+ process.stderr.write(
3573
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
3574
+ `
3575
+ );
3576
+ }
3577
+ } catch {
3578
+ }
3579
+ }
3580
+ return candidate;
3502
3581
  }
3503
3582
  function isExeSession(sessionName) {
3504
3583
  const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
@@ -4738,6 +4817,20 @@ var init_platform_procedures = __esm({
4738
4817
  priority: "p1",
4739
4818
  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."
4740
4819
  },
4820
+ // --- Tool guidance ---
4821
+ {
4822
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4823
+ domain: "tools",
4824
+ priority: "p2",
4825
+ 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."
4826
+ },
4827
+ // --- Release awareness ---
4828
+ {
4829
+ title: "What's New check \u2014 surface new features after update",
4830
+ domain: "support",
4831
+ priority: "p1",
4832
+ 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."
4833
+ },
4741
4834
  // --- Platform vs Customer ownership ---
4742
4835
  {
4743
4836
  title: "What the platform provides vs what you customize",
@@ -4826,13 +4919,13 @@ var init_platform_procedures = __esm({
4826
4919
  title: "MCP tools \u2014 memory, decision, and search",
4827
4920
  domain: "tool-use",
4828
4921
  priority: "p1",
4829
- 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.`
4922
+ 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.`
4830
4923
  },
4831
4924
  {
4832
4925
  title: "MCP tools \u2014 task orchestration",
4833
4926
  domain: "tool-use",
4834
4927
  priority: "p1",
4835
- 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.'
4928
+ 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.'
4836
4929
  },
4837
4930
  {
4838
4931
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4862,7 +4955,7 @@ var init_platform_procedures = __esm({
4862
4955
  title: "MCP tools \u2014 admin, config, and operations",
4863
4956
  domain: "tool-use",
4864
4957
  priority: "p1",
4865
- 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.'
4958
+ 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.'
4866
4959
  }
4867
4960
  ];
4868
4961
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5546,6 +5639,8 @@ async function writeMemory(record) {
5546
5639
  source_type: record.source_type ?? null,
5547
5640
  tier: record.tier ?? classifyTier(record),
5548
5641
  supersedes_id: record.supersedes_id ?? null,
5642
+ valid_from: record.valid_from ?? record.timestamp,
5643
+ invalid_at: record.invalid_at ?? null,
5549
5644
  draft: record.draft ? 1 : 0,
5550
5645
  memory_type: memoryType,
5551
5646
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5564,7 +5659,8 @@ async function writeMemory(record) {
5564
5659
  token_cost: record.token_cost ?? null,
5565
5660
  audience: record.audience ?? null,
5566
5661
  language_type: record.language_type ?? inferLanguageType(record),
5567
- parent_memory_id: record.parent_memory_id ?? null
5662
+ parent_memory_id: record.parent_memory_id ?? null,
5663
+ procedure_for: record.procedure_for ?? null
5568
5664
  };
5569
5665
  _pendingRecords.push(dbRow);
5570
5666
  orgBus.emit({
@@ -5619,6 +5715,8 @@ async function flushBatch() {
5619
5715
  const sourceType = row.source_type ?? null;
5620
5716
  const tier = row.tier ?? 3;
5621
5717
  const supersedesId = row.supersedes_id ?? null;
5718
+ const validFrom = row.valid_from ?? row.timestamp;
5719
+ const invalidAt = row.invalid_at ?? null;
5622
5720
  const draft = row.draft ? 1 : 0;
5623
5721
  const memoryType = row.memory_type ?? "raw";
5624
5722
  const trajectory = row.trajectory ?? null;
@@ -5638,15 +5736,16 @@ async function flushBatch() {
5638
5736
  const audience = row.audience ?? null;
5639
5737
  const languageType = row.language_type ?? null;
5640
5738
  const parentMemoryId = row.parent_memory_id ?? null;
5739
+ const procedureFor = row.procedure_for ?? null;
5641
5740
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5642
5741
  tool_name, project_name,
5643
5742
  has_error, raw_text, vector, version, task_id, importance, status,
5644
5743
  confidence, last_accessed,
5645
5744
  workspace_id, document_id, user_id, char_offset, page_number,
5646
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5745
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5647
5746
  intent, outcome, domain, referenced_entities, retrieval_count,
5648
5747
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5649
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5748
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5650
5749
  const metaArgs = [
5651
5750
  intent,
5652
5751
  outcome,
@@ -5662,7 +5761,8 @@ async function flushBatch() {
5662
5761
  tokenCost,
5663
5762
  audience,
5664
5763
  languageType,
5665
- parentMemoryId
5764
+ parentMemoryId,
5765
+ procedureFor
5666
5766
  ];
5667
5767
  const baseArgs = [
5668
5768
  row.id,
@@ -5691,6 +5791,8 @@ async function flushBatch() {
5691
5791
  sourceType,
5692
5792
  tier,
5693
5793
  supersedesId,
5794
+ validFrom,
5795
+ invalidAt,
5694
5796
  draft,
5695
5797
  memoryType,
5696
5798
  trajectory,
@@ -5698,8 +5800,8 @@ async function flushBatch() {
5698
5800
  ];
5699
5801
  return {
5700
5802
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5701
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5702
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5803
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5804
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5703
5805
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5704
5806
  };
5705
5807
  };
@@ -5815,6 +5917,12 @@ async function searchMemories(queryVector, agentId, options) {
5815
5917
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5816
5918
  AND COALESCE(confidence, 0.7) >= 0.3`;
5817
5919
  const args = [agentId];
5920
+ if (options?.asOf) {
5921
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5922
+ args.push(options.asOf, options.asOf);
5923
+ } else {
5924
+ sql += ` AND invalid_at IS NULL`;
5925
+ }
5818
5926
  const scope = buildWikiScopeFilter(options, "");
5819
5927
  sql += scope.clause;
5820
5928
  args.push(...scope.args);
@@ -2620,6 +2620,20 @@ async function ensureSchema() {
2620
2620
  });
2621
2621
  } catch {
2622
2622
  }
2623
+ try {
2624
+ await client.execute({
2625
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2626
+ args: []
2627
+ });
2628
+ } catch {
2629
+ }
2630
+ try {
2631
+ await client.execute({
2632
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2633
+ args: []
2634
+ });
2635
+ } catch {
2636
+ }
2623
2637
  await client.executeMultiple(`
2624
2638
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2625
2639
  content_text,
@@ -2871,6 +2885,22 @@ async function ensureSchema() {
2871
2885
  );
2872
2886
  } catch {
2873
2887
  }
2888
+ for (const col of [
2889
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
2890
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
2891
+ ]) {
2892
+ try {
2893
+ await client.execute(col);
2894
+ } catch {
2895
+ }
2896
+ }
2897
+ try {
2898
+ await client.execute({
2899
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
2900
+ args: []
2901
+ });
2902
+ } catch {
2903
+ }
2874
2904
  try {
2875
2905
  await client.execute({
2876
2906
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -2913,6 +2943,13 @@ async function ensureSchema() {
2913
2943
  } catch {
2914
2944
  }
2915
2945
  }
2946
+ try {
2947
+ await client.execute({
2948
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
2949
+ args: []
2950
+ });
2951
+ } catch {
2952
+ }
2916
2953
  try {
2917
2954
  await client.execute({
2918
2955
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -3079,6 +3116,20 @@ var init_platform_procedures = __esm({
3079
3116
  priority: "p1",
3080
3117
  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."
3081
3118
  },
3119
+ // --- Tool guidance ---
3120
+ {
3121
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
3122
+ domain: "tools",
3123
+ priority: "p2",
3124
+ 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."
3125
+ },
3126
+ // --- Release awareness ---
3127
+ {
3128
+ title: "What's New check \u2014 surface new features after update",
3129
+ domain: "support",
3130
+ priority: "p1",
3131
+ 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."
3132
+ },
3082
3133
  // --- Platform vs Customer ownership ---
3083
3134
  {
3084
3135
  title: "What the platform provides vs what you customize",
@@ -3167,13 +3218,13 @@ var init_platform_procedures = __esm({
3167
3218
  title: "MCP tools \u2014 memory, decision, and search",
3168
3219
  domain: "tool-use",
3169
3220
  priority: "p1",
3170
- 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.`
3221
+ 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.`
3171
3222
  },
3172
3223
  {
3173
3224
  title: "MCP tools \u2014 task orchestration",
3174
3225
  domain: "tool-use",
3175
3226
  priority: "p1",
3176
- 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.'
3227
+ 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.'
3177
3228
  },
3178
3229
  {
3179
3230
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -3203,7 +3254,7 @@ var init_platform_procedures = __esm({
3203
3254
  title: "MCP tools \u2014 admin, config, and operations",
3204
3255
  domain: "tool-use",
3205
3256
  priority: "p1",
3206
- 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.'
3257
+ 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.'
3207
3258
  }
3208
3259
  ];
3209
3260
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -4859,6 +4910,8 @@ async function writeMemory(record) {
4859
4910
  source_type: record.source_type ?? null,
4860
4911
  tier: record.tier ?? classifyTier(record),
4861
4912
  supersedes_id: record.supersedes_id ?? null,
4913
+ valid_from: record.valid_from ?? record.timestamp,
4914
+ invalid_at: record.invalid_at ?? null,
4862
4915
  draft: record.draft ? 1 : 0,
4863
4916
  memory_type: memoryType,
4864
4917
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -4877,7 +4930,8 @@ async function writeMemory(record) {
4877
4930
  token_cost: record.token_cost ?? null,
4878
4931
  audience: record.audience ?? null,
4879
4932
  language_type: record.language_type ?? inferLanguageType(record),
4880
- parent_memory_id: record.parent_memory_id ?? null
4933
+ parent_memory_id: record.parent_memory_id ?? null,
4934
+ procedure_for: record.procedure_for ?? null
4881
4935
  };
4882
4936
  _pendingRecords.push(dbRow);
4883
4937
  orgBus.emit({
@@ -4932,6 +4986,8 @@ async function flushBatch() {
4932
4986
  const sourceType = row.source_type ?? null;
4933
4987
  const tier = row.tier ?? 3;
4934
4988
  const supersedesId = row.supersedes_id ?? null;
4989
+ const validFrom = row.valid_from ?? row.timestamp;
4990
+ const invalidAt = row.invalid_at ?? null;
4935
4991
  const draft = row.draft ? 1 : 0;
4936
4992
  const memoryType = row.memory_type ?? "raw";
4937
4993
  const trajectory = row.trajectory ?? null;
@@ -4951,15 +5007,16 @@ async function flushBatch() {
4951
5007
  const audience = row.audience ?? null;
4952
5008
  const languageType = row.language_type ?? null;
4953
5009
  const parentMemoryId = row.parent_memory_id ?? null;
5010
+ const procedureFor = row.procedure_for ?? null;
4954
5011
  const cols = `id, agent_id, agent_role, session_id, timestamp,
4955
5012
  tool_name, project_name,
4956
5013
  has_error, raw_text, vector, version, task_id, importance, status,
4957
5014
  confidence, last_accessed,
4958
5015
  workspace_id, document_id, user_id, char_offset, page_number,
4959
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5016
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
4960
5017
  intent, outcome, domain, referenced_entities, retrieval_count,
4961
5018
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
4962
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5019
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
4963
5020
  const metaArgs = [
4964
5021
  intent,
4965
5022
  outcome,
@@ -4975,7 +5032,8 @@ async function flushBatch() {
4975
5032
  tokenCost,
4976
5033
  audience,
4977
5034
  languageType,
4978
- parentMemoryId
5035
+ parentMemoryId,
5036
+ procedureFor
4979
5037
  ];
4980
5038
  const baseArgs = [
4981
5039
  row.id,
@@ -5004,6 +5062,8 @@ async function flushBatch() {
5004
5062
  sourceType,
5005
5063
  tier,
5006
5064
  supersedesId,
5065
+ validFrom,
5066
+ invalidAt,
5007
5067
  draft,
5008
5068
  memoryType,
5009
5069
  trajectory,
@@ -5011,8 +5071,8 @@ async function flushBatch() {
5011
5071
  ];
5012
5072
  return {
5013
5073
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5014
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5015
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5074
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5075
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5016
5076
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5017
5077
  };
5018
5078
  };
@@ -5128,6 +5188,12 @@ async function searchMemories(queryVector, agentId, options) {
5128
5188
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5129
5189
  AND COALESCE(confidence, 0.7) >= 0.3`;
5130
5190
  const args = [agentId];
5191
+ if (options?.asOf) {
5192
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5193
+ args.push(options.asOf, options.asOf);
5194
+ } else {
5195
+ sql += ` AND invalid_at IS NULL`;
5196
+ }
5131
5197
  const scope = buildWikiScopeFilter(options, "");
5132
5198
  sql += scope.clause;
5133
5199
  args.push(...scope.args);