@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
@@ -3353,6 +3353,20 @@ async function ensureSchema() {
3353
3353
  });
3354
3354
  } catch {
3355
3355
  }
3356
+ try {
3357
+ await client.execute({
3358
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
3359
+ args: []
3360
+ });
3361
+ } catch {
3362
+ }
3363
+ try {
3364
+ await client.execute({
3365
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
3366
+ args: []
3367
+ });
3368
+ } catch {
3369
+ }
3356
3370
  await client.executeMultiple(`
3357
3371
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
3358
3372
  content_text,
@@ -3604,6 +3618,22 @@ async function ensureSchema() {
3604
3618
  );
3605
3619
  } catch {
3606
3620
  }
3621
+ for (const col of [
3622
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3623
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3624
+ ]) {
3625
+ try {
3626
+ await client.execute(col);
3627
+ } catch {
3628
+ }
3629
+ }
3630
+ try {
3631
+ await client.execute({
3632
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3633
+ args: []
3634
+ });
3635
+ } catch {
3636
+ }
3607
3637
  try {
3608
3638
  await client.execute({
3609
3639
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3646,6 +3676,13 @@ async function ensureSchema() {
3646
3676
  } catch {
3647
3677
  }
3648
3678
  }
3679
+ try {
3680
+ await client.execute({
3681
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3682
+ args: []
3683
+ });
3684
+ } catch {
3685
+ }
3649
3686
  try {
3650
3687
  await client.execute({
3651
3688
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -3706,7 +3743,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3706
3743
  import os7 from "os";
3707
3744
  import path9 from "path";
3708
3745
  import { jwtVerify, importSPKI } from "jose";
3709
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
3746
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, PLAN_LIMITS;
3710
3747
  var init_license = __esm({
3711
3748
  "src/lib/license.ts"() {
3712
3749
  "use strict";
@@ -3714,6 +3751,7 @@ var init_license = __esm({
3714
3751
  LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3715
3752
  CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3716
3753
  DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3754
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
3717
3755
  PLAN_LIMITS = {
3718
3756
  free: { devices: 1, employees: 1, memories: 5e3 },
3719
3757
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -4347,8 +4385,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
4347
4385
  const complexity = input.complexity ?? "standard";
4348
4386
  const sessionScope = earlySessionScope;
4349
4387
  await client.execute({
4350
- 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)
4351
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4388
+ 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)
4389
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4352
4390
  args: [
4353
4391
  id,
4354
4392
  input.title,
@@ -4368,6 +4406,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
4368
4406
  0,
4369
4407
  null,
4370
4408
  sessionScope,
4409
+ input.spawnRuntime ?? null,
4410
+ input.spawnModel ?? null,
4371
4411
  now,
4372
4412
  now
4373
4413
  ]
@@ -4424,7 +4464,9 @@ ${input.context}
4424
4464
  budgetTokens: input.budgetTokens ?? null,
4425
4465
  budgetFallbackModel: input.budgetFallbackModel ?? null,
4426
4466
  tokensUsed: 0,
4427
- tokensWarnedAt: null
4467
+ tokensWarnedAt: null,
4468
+ spawnRuntime: input.spawnRuntime ?? null,
4469
+ spawnModel: input.spawnModel ?? null
4428
4470
  };
4429
4471
  }
4430
4472
  async function listTasks(input) {
@@ -4474,7 +4516,9 @@ async function listTasks(input) {
4474
4516
  budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
4475
4517
  budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
4476
4518
  tokensUsed: Number(r.tokens_used ?? 0),
4477
- tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
4519
+ tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null,
4520
+ spawnRuntime: r.spawn_runtime !== null && r.spawn_runtime !== void 0 ? String(r.spawn_runtime) : null,
4521
+ spawnModel: r.spawn_model !== null && r.spawn_model !== void 0 ? String(r.spawn_model) : null
4478
4522
  }));
4479
4523
  }
4480
4524
  function isTmuxSessionAlive(identifier) {
@@ -5771,6 +5815,8 @@ async function updateTask(input) {
5771
5815
  budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
5772
5816
  tokensUsed: Number(row.tokens_used ?? 0),
5773
5817
  tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
5818
+ spawnRuntime: row.spawn_runtime !== void 0 && row.spawn_runtime !== null ? String(row.spawn_runtime) : null,
5819
+ spawnModel: row.spawn_model !== void 0 && row.spawn_model !== null ? String(row.spawn_model) : null,
5774
5820
  nextTask
5775
5821
  };
5776
5822
  }
@@ -6266,6 +6312,7 @@ function resolveExeSession() {
6266
6312
  const mySession = getMySession();
6267
6313
  if (!mySession) return null;
6268
6314
  const fromSessionName = extractRootExe(mySession);
6315
+ let candidate = null;
6269
6316
  try {
6270
6317
  const key = getSessionKey();
6271
6318
  const parentExe = getParentExe(key);
@@ -6276,13 +6323,47 @@ function resolveExeSession() {
6276
6323
  `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
6277
6324
  `
6278
6325
  );
6279
- return fromSessionName;
6326
+ candidate = fromSessionName;
6327
+ } else {
6328
+ candidate = fromCache;
6280
6329
  }
6281
- return fromCache;
6282
6330
  }
6283
6331
  } catch {
6284
6332
  }
6285
- return fromSessionName ?? mySession;
6333
+ if (!candidate) {
6334
+ candidate = fromSessionName ?? mySession;
6335
+ }
6336
+ if (candidate && isRootSession(candidate)) {
6337
+ try {
6338
+ const transport = getTransport();
6339
+ const liveSessions = transport.listSessions();
6340
+ if (!liveSessions.includes(candidate)) {
6341
+ const liveRoots = liveSessions.filter((s) => isRootSession(s));
6342
+ if (liveRoots.length === 1) {
6343
+ process.stderr.write(
6344
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
6345
+ `
6346
+ );
6347
+ return liveRoots[0];
6348
+ } else if (liveRoots.length > 1) {
6349
+ const base = candidate.replace(/\d+$/, "");
6350
+ const match = liveRoots.find((s) => s.startsWith(base));
6351
+ const chosen = match ?? liveRoots[0];
6352
+ process.stderr.write(
6353
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
6354
+ `
6355
+ );
6356
+ return chosen;
6357
+ }
6358
+ process.stderr.write(
6359
+ `[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
6360
+ `
6361
+ );
6362
+ }
6363
+ } catch {
6364
+ }
6365
+ }
6366
+ return candidate;
6286
6367
  }
6287
6368
  function isEmployeeAlive(sessionName) {
6288
6369
  return getTransport().isAlive(sessionName);
@@ -6684,7 +6765,12 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6684
6765
  }
6685
6766
  const spawnCwd = opts?.cwd ?? projectDir;
6686
6767
  const useExeAgent = !!(opts?.model && opts?.provider);
6687
- const agentRtConfig = getAgentRuntime(employeeName);
6768
+ const baseRtConfig = getAgentRuntime(employeeName);
6769
+ const agentRtConfig = {
6770
+ ...baseRtConfig,
6771
+ ...opts?.runtimeOverride ? { runtime: opts.runtimeOverride } : {},
6772
+ ...opts?.modelOverride ? { model: opts.modelOverride } : {}
6773
+ };
6688
6774
  const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
6689
6775
  const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
6690
6776
  const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
@@ -7977,6 +8063,20 @@ var init_platform_procedures = __esm({
7977
8063
  priority: "p1",
7978
8064
  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."
7979
8065
  },
8066
+ // --- Tool guidance ---
8067
+ {
8068
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
8069
+ domain: "tools",
8070
+ priority: "p2",
8071
+ 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."
8072
+ },
8073
+ // --- Release awareness ---
8074
+ {
8075
+ title: "What's New check \u2014 surface new features after update",
8076
+ domain: "support",
8077
+ priority: "p1",
8078
+ 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."
8079
+ },
7980
8080
  // --- Platform vs Customer ownership ---
7981
8081
  {
7982
8082
  title: "What the platform provides vs what you customize",
@@ -8065,13 +8165,13 @@ var init_platform_procedures = __esm({
8065
8165
  title: "MCP tools \u2014 memory, decision, and search",
8066
8166
  domain: "tool-use",
8067
8167
  priority: "p1",
8068
- 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.`
8168
+ 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.`
8069
8169
  },
8070
8170
  {
8071
8171
  title: "MCP tools \u2014 task orchestration",
8072
8172
  domain: "tool-use",
8073
8173
  priority: "p1",
8074
- 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.'
8174
+ 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.`
8075
8175
  },
8076
8176
  {
8077
8177
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -8101,7 +8201,7 @@ var init_platform_procedures = __esm({
8101
8201
  title: "MCP tools \u2014 admin, config, and operations",
8102
8202
  domain: "tool-use",
8103
8203
  priority: "p1",
8104
- 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.'
8204
+ 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.'
8105
8205
  }
8106
8206
  ];
8107
8207
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -8785,6 +8885,8 @@ async function writeMemory(record) {
8785
8885
  source_type: record.source_type ?? null,
8786
8886
  tier: record.tier ?? classifyTier(record),
8787
8887
  supersedes_id: record.supersedes_id ?? null,
8888
+ valid_from: record.valid_from ?? record.timestamp,
8889
+ invalid_at: record.invalid_at ?? null,
8788
8890
  draft: record.draft ? 1 : 0,
8789
8891
  memory_type: memoryType,
8790
8892
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -8803,7 +8905,8 @@ async function writeMemory(record) {
8803
8905
  token_cost: record.token_cost ?? null,
8804
8906
  audience: record.audience ?? null,
8805
8907
  language_type: record.language_type ?? inferLanguageType(record),
8806
- parent_memory_id: record.parent_memory_id ?? null
8908
+ parent_memory_id: record.parent_memory_id ?? null,
8909
+ procedure_for: record.procedure_for ?? null
8807
8910
  };
8808
8911
  _pendingRecords.push(dbRow);
8809
8912
  orgBus.emit({
@@ -8858,6 +8961,8 @@ async function flushBatch() {
8858
8961
  const sourceType = row.source_type ?? null;
8859
8962
  const tier = row.tier ?? 3;
8860
8963
  const supersedesId = row.supersedes_id ?? null;
8964
+ const validFrom = row.valid_from ?? row.timestamp;
8965
+ const invalidAt = row.invalid_at ?? null;
8861
8966
  const draft = row.draft ? 1 : 0;
8862
8967
  const memoryType = row.memory_type ?? "raw";
8863
8968
  const trajectory = row.trajectory ?? null;
@@ -8877,15 +8982,16 @@ async function flushBatch() {
8877
8982
  const audience = row.audience ?? null;
8878
8983
  const languageType = row.language_type ?? null;
8879
8984
  const parentMemoryId = row.parent_memory_id ?? null;
8985
+ const procedureFor = row.procedure_for ?? null;
8880
8986
  const cols = `id, agent_id, agent_role, session_id, timestamp,
8881
8987
  tool_name, project_name,
8882
8988
  has_error, raw_text, vector, version, task_id, importance, status,
8883
8989
  confidence, last_accessed,
8884
8990
  workspace_id, document_id, user_id, char_offset, page_number,
8885
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
8991
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
8886
8992
  intent, outcome, domain, referenced_entities, retrieval_count,
8887
8993
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
8888
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
8994
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
8889
8995
  const metaArgs = [
8890
8996
  intent,
8891
8997
  outcome,
@@ -8901,7 +9007,8 @@ async function flushBatch() {
8901
9007
  tokenCost,
8902
9008
  audience,
8903
9009
  languageType,
8904
- parentMemoryId
9010
+ parentMemoryId,
9011
+ procedureFor
8905
9012
  ];
8906
9013
  const baseArgs = [
8907
9014
  row.id,
@@ -8930,6 +9037,8 @@ async function flushBatch() {
8930
9037
  sourceType,
8931
9038
  tier,
8932
9039
  supersedesId,
9040
+ validFrom,
9041
+ invalidAt,
8933
9042
  draft,
8934
9043
  memoryType,
8935
9044
  trajectory,
@@ -8937,8 +9046,8 @@ async function flushBatch() {
8937
9046
  ];
8938
9047
  return {
8939
9048
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
8940
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
8941
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
9049
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
9050
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8942
9051
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
8943
9052
  };
8944
9053
  };
@@ -9054,6 +9163,12 @@ async function searchMemories(queryVector, agentId, options) {
9054
9163
  AND vector IS NOT NULL${statusFilter}${draftFilter}
9055
9164
  AND COALESCE(confidence, 0.7) >= 0.3`;
9056
9165
  const args = [agentId];
9166
+ if (options?.asOf) {
9167
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
9168
+ args.push(options.asOf, options.asOf);
9169
+ } else {
9170
+ sql += ` AND invalid_at IS NULL`;
9171
+ }
9057
9172
  const scope = buildWikiScopeFilter(options, "");
9058
9173
  sql += scope.clause;
9059
9174
  args.push(...scope.args);
@@ -2766,6 +2766,20 @@ async function ensureSchema() {
2766
2766
  });
2767
2767
  } catch {
2768
2768
  }
2769
+ try {
2770
+ await client.execute({
2771
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2772
+ args: []
2773
+ });
2774
+ } catch {
2775
+ }
2776
+ try {
2777
+ await client.execute({
2778
+ sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2779
+ args: []
2780
+ });
2781
+ } catch {
2782
+ }
2769
2783
  await client.executeMultiple(`
2770
2784
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
2771
2785
  content_text,
@@ -3017,6 +3031,22 @@ async function ensureSchema() {
3017
3031
  );
3018
3032
  } catch {
3019
3033
  }
3034
+ for (const col of [
3035
+ "ALTER TABLE memories ADD COLUMN valid_from TEXT",
3036
+ "ALTER TABLE memories ADD COLUMN invalid_at TEXT"
3037
+ ]) {
3038
+ try {
3039
+ await client.execute(col);
3040
+ } catch {
3041
+ }
3042
+ }
3043
+ try {
3044
+ await client.execute({
3045
+ sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3046
+ args: []
3047
+ });
3048
+ } catch {
3049
+ }
3020
3050
  try {
3021
3051
  await client.execute({
3022
3052
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
@@ -3059,6 +3089,13 @@ async function ensureSchema() {
3059
3089
  } catch {
3060
3090
  }
3061
3091
  }
3092
+ try {
3093
+ await client.execute({
3094
+ sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3095
+ args: []
3096
+ });
3097
+ } catch {
3098
+ }
3062
3099
  try {
3063
3100
  await client.execute({
3064
3101
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
@@ -4197,6 +4234,20 @@ var init_platform_procedures = __esm({
4197
4234
  priority: "p1",
4198
4235
  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."
4199
4236
  },
4237
+ // --- Tool guidance ---
4238
+ {
4239
+ title: "How to use company_actions \u2014 execute business actions through gateway connectors",
4240
+ domain: "tools",
4241
+ priority: "p2",
4242
+ 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."
4243
+ },
4244
+ // --- Release awareness ---
4245
+ {
4246
+ title: "What's New check \u2014 surface new features after update",
4247
+ domain: "support",
4248
+ priority: "p1",
4249
+ 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."
4250
+ },
4200
4251
  // --- Platform vs Customer ownership ---
4201
4252
  {
4202
4253
  title: "What the platform provides vs what you customize",
@@ -4285,13 +4336,13 @@ var init_platform_procedures = __esm({
4285
4336
  title: "MCP tools \u2014 memory, decision, and search",
4286
4337
  domain: "tool-use",
4287
4338
  priority: "p1",
4288
- 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.`
4339
+ 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.`
4289
4340
  },
4290
4341
  {
4291
4342
  title: "MCP tools \u2014 task orchestration",
4292
4343
  domain: "tool-use",
4293
4344
  priority: "p1",
4294
- 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.'
4345
+ 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.`
4295
4346
  },
4296
4347
  {
4297
4348
  title: "MCP tools \u2014 knowledge graph (GraphRAG)",
@@ -4321,7 +4372,7 @@ var init_platform_procedures = __esm({
4321
4372
  title: "MCP tools \u2014 admin, config, and operations",
4322
4373
  domain: "tool-use",
4323
4374
  priority: "p1",
4324
- 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.'
4375
+ 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.'
4325
4376
  }
4326
4377
  ];
4327
4378
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5005,6 +5056,8 @@ async function writeMemory(record) {
5005
5056
  source_type: record.source_type ?? null,
5006
5057
  tier: record.tier ?? classifyTier(record),
5007
5058
  supersedes_id: record.supersedes_id ?? null,
5059
+ valid_from: record.valid_from ?? record.timestamp,
5060
+ invalid_at: record.invalid_at ?? null,
5008
5061
  draft: record.draft ? 1 : 0,
5009
5062
  memory_type: memoryType,
5010
5063
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
@@ -5023,7 +5076,8 @@ async function writeMemory(record) {
5023
5076
  token_cost: record.token_cost ?? null,
5024
5077
  audience: record.audience ?? null,
5025
5078
  language_type: record.language_type ?? inferLanguageType(record),
5026
- parent_memory_id: record.parent_memory_id ?? null
5079
+ parent_memory_id: record.parent_memory_id ?? null,
5080
+ procedure_for: record.procedure_for ?? null
5027
5081
  };
5028
5082
  _pendingRecords.push(dbRow);
5029
5083
  orgBus.emit({
@@ -5078,6 +5132,8 @@ async function flushBatch() {
5078
5132
  const sourceType = row.source_type ?? null;
5079
5133
  const tier = row.tier ?? 3;
5080
5134
  const supersedesId = row.supersedes_id ?? null;
5135
+ const validFrom = row.valid_from ?? row.timestamp;
5136
+ const invalidAt = row.invalid_at ?? null;
5081
5137
  const draft = row.draft ? 1 : 0;
5082
5138
  const memoryType = row.memory_type ?? "raw";
5083
5139
  const trajectory = row.trajectory ?? null;
@@ -5097,15 +5153,16 @@ async function flushBatch() {
5097
5153
  const audience = row.audience ?? null;
5098
5154
  const languageType = row.language_type ?? null;
5099
5155
  const parentMemoryId = row.parent_memory_id ?? null;
5156
+ const procedureFor = row.procedure_for ?? null;
5100
5157
  const cols = `id, agent_id, agent_role, session_id, timestamp,
5101
5158
  tool_name, project_name,
5102
5159
  has_error, raw_text, vector, version, task_id, importance, status,
5103
5160
  confidence, last_accessed,
5104
5161
  workspace_id, document_id, user_id, char_offset, page_number,
5105
- source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
5162
+ source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
5106
5163
  intent, outcome, domain, referenced_entities, retrieval_count,
5107
5164
  chain_position, review_status, context_window_pct, file_paths, commit_hash,
5108
- duration_ms, token_cost, audience, language_type, parent_memory_id`;
5165
+ duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
5109
5166
  const metaArgs = [
5110
5167
  intent,
5111
5168
  outcome,
@@ -5121,7 +5178,8 @@ async function flushBatch() {
5121
5178
  tokenCost,
5122
5179
  audience,
5123
5180
  languageType,
5124
- parentMemoryId
5181
+ parentMemoryId,
5182
+ procedureFor
5125
5183
  ];
5126
5184
  const baseArgs = [
5127
5185
  row.id,
@@ -5150,6 +5208,8 @@ async function flushBatch() {
5150
5208
  sourceType,
5151
5209
  tier,
5152
5210
  supersedesId,
5211
+ validFrom,
5212
+ invalidAt,
5153
5213
  draft,
5154
5214
  memoryType,
5155
5215
  trajectory,
@@ -5157,8 +5217,8 @@ async function flushBatch() {
5157
5217
  ];
5158
5218
  return {
5159
5219
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
5160
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5161
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5220
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
5221
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
5162
5222
  args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
5163
5223
  };
5164
5224
  };
@@ -5274,6 +5334,12 @@ async function searchMemories(queryVector, agentId, options) {
5274
5334
  AND vector IS NOT NULL${statusFilter}${draftFilter}
5275
5335
  AND COALESCE(confidence, 0.7) >= 0.3`;
5276
5336
  const args = [agentId];
5337
+ if (options?.asOf) {
5338
+ sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
5339
+ args.push(options.asOf, options.asOf);
5340
+ } else {
5341
+ sql += ` AND invalid_at IS NULL`;
5342
+ }
5277
5343
  const scope = buildWikiScopeFilter(options, "");
5278
5344
  sql += scope.clause;
5279
5345
  args.push(...scope.args);
@@ -6702,6 +6768,19 @@ init_store();
6702
6768
  init_store();
6703
6769
  init_database();
6704
6770
  var RRF_K = 60;
6771
+ function buildTemporalFilter(options, columnPrefix) {
6772
+ const asOf = options?.asOf;
6773
+ if (asOf) {
6774
+ return {
6775
+ clause: ` AND (${columnPrefix}valid_from IS NULL OR ${columnPrefix}valid_from <= ?) AND (${columnPrefix}invalid_at IS NULL OR ${columnPrefix}invalid_at > ?)`,
6776
+ args: [asOf, asOf]
6777
+ };
6778
+ }
6779
+ return {
6780
+ clause: ` AND ${columnPrefix}invalid_at IS NULL`,
6781
+ args: []
6782
+ };
6783
+ }
6705
6784
  function appendMemoryTypeFilter(sql, args, column, options) {
6706
6785
  if (options?.memoryTypes && options.memoryTypes.length > 0) {
6707
6786
  const uniqueTypes = [...new Set(options.memoryTypes)];
@@ -6934,6 +7013,9 @@ async function estimateCardinality(agentId, options) {
6934
7013
  AND COALESCE(status, 'active') = 'active'
6935
7014
  AND COALESCE(confidence, 0.7) >= 0.3`;
6936
7015
  const args = [agentId];
7016
+ const temporal = buildTemporalFilter(options, "");
7017
+ sql += temporal.clause;
7018
+ args.push(...temporal.args);
6937
7019
  const rawVisibility = buildRawVisibilityFilter(options, "");
6938
7020
  sql += rawVisibility.clause;
6939
7021
  args.push(...rawVisibility.args);
@@ -7059,6 +7141,9 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
7059
7141
  AND m.agent_id = ?${statusFilter}${draftFilter}
7060
7142
  AND COALESCE(m.confidence, 0.7) >= 0.3`;
7061
7143
  const args = [matchExpr, agentId];
7144
+ const temporal = buildTemporalFilter(options, "m.");
7145
+ sql += temporal.clause;
7146
+ args.push(...temporal.args);
7062
7147
  const scope = buildWikiScopeFilter(options, "m.");
7063
7148
  sql += scope.clause;
7064
7149
  args.push(...scope.args);
@@ -7171,6 +7256,9 @@ async function recentRecords(agentId, options, limit, textFilter) {
7171
7256
  WHERE agent_id = ?${statusFilter}${draftFilter}
7172
7257
  AND COALESCE(confidence, 0.7) >= 0.3`;
7173
7258
  const args = [agentId];
7259
+ const temporal = buildTemporalFilter(options, "");
7260
+ sql += temporal.clause;
7261
+ args.push(...temporal.args);
7174
7262
  const scope = buildWikiScopeFilter(options, "");
7175
7263
  sql += scope.clause;
7176
7264
  args.push(...scope.args);
@@ -7283,6 +7371,9 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
7283
7371
  AND json_extract(trajectory, '$.tool') = ?
7284
7372
  AND agent_id = ?${statusFilter}${draftFilter}`;
7285
7373
  const args = [toolName, agentId];
7374
+ const temporal = buildTemporalFilter(options, "");
7375
+ sql += temporal.clause;
7376
+ args.push(...temporal.args);
7286
7377
  const rawVisibility = buildRawVisibilityFilter(options, "");
7287
7378
  sql += rawVisibility.clause;
7288
7379
  args.push(...rawVisibility.args);