@askexenow/exe-os 0.9.11 → 0.9.13

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 (67) hide show
  1. package/dist/bin/backfill-conversations.js +22 -1
  2. package/dist/bin/backfill-responses.js +22 -1
  3. package/dist/bin/backfill-vectors.js +22 -1
  4. package/dist/bin/cleanup-stale-review-tasks.js +22 -1
  5. package/dist/bin/cli.js +22 -1
  6. package/dist/bin/exe-assign.js +22 -1
  7. package/dist/bin/exe-boot.js +22 -1
  8. package/dist/bin/exe-dispatch.js +22 -1
  9. package/dist/bin/exe-doctor.js +22 -1
  10. package/dist/bin/exe-export-behaviors.js +22 -1
  11. package/dist/bin/exe-forget.js +22 -1
  12. package/dist/bin/exe-gateway.js +22 -1
  13. package/dist/bin/exe-heartbeat.js +22 -1
  14. package/dist/bin/exe-kill.js +22 -1
  15. package/dist/bin/exe-launch-agent.js +22 -1
  16. package/dist/bin/exe-link.js +22 -1
  17. package/dist/bin/exe-pending-messages.js +22 -1
  18. package/dist/bin/exe-pending-notifications.js +22 -1
  19. package/dist/bin/exe-pending-reviews.js +22 -1
  20. package/dist/bin/exe-rename.js +22 -1
  21. package/dist/bin/exe-review.js +22 -1
  22. package/dist/bin/exe-search.js +22 -1
  23. package/dist/bin/exe-session-cleanup.js +22 -1
  24. package/dist/bin/exe-start-codex.js +22 -1
  25. package/dist/bin/exe-start-opencode.js +22 -1
  26. package/dist/bin/exe-status.js +22 -1
  27. package/dist/bin/exe-team.js +22 -1
  28. package/dist/bin/git-sweep.js +22 -1
  29. package/dist/bin/graph-backfill.js +22 -1
  30. package/dist/bin/graph-export.js +22 -1
  31. package/dist/bin/scan-tasks.js +22 -1
  32. package/dist/bin/setup.js +22 -1
  33. package/dist/bin/shard-migrate.js +22 -1
  34. package/dist/bin/wiki-sync.js +22 -1
  35. package/dist/gateway/index.js +22 -1
  36. package/dist/hooks/bug-report-worker.js +22 -1
  37. package/dist/hooks/codex-stop-task-finalizer.js +22 -1
  38. package/dist/hooks/commit-complete.js +22 -1
  39. package/dist/hooks/error-recall.js +22 -1
  40. package/dist/hooks/ingest-worker.js +22 -1
  41. package/dist/hooks/ingest.js +3345 -232
  42. package/dist/hooks/instructions-loaded.js +22 -1
  43. package/dist/hooks/notification.js +22 -1
  44. package/dist/hooks/post-compact.js +22 -1
  45. package/dist/hooks/pre-compact.js +22 -1
  46. package/dist/hooks/pre-tool-use.js +22 -1
  47. package/dist/hooks/prompt-ingest-worker.js +22 -1
  48. package/dist/hooks/prompt-submit.js +1700 -1396
  49. package/dist/hooks/response-ingest-worker.js +22 -1
  50. package/dist/hooks/session-end.js +345 -187
  51. package/dist/hooks/session-start.js +304 -15
  52. package/dist/hooks/stop.js +22 -1
  53. package/dist/hooks/subagent-stop.js +22 -1
  54. package/dist/hooks/summary-worker.js +22 -1
  55. package/dist/index.js +22 -1
  56. package/dist/lib/cloud-sync.js +22 -1
  57. package/dist/lib/database.js +22 -1
  58. package/dist/lib/db.js +22 -1
  59. package/dist/lib/device-registry.js +22 -1
  60. package/dist/lib/exe-daemon.js +39 -1
  61. package/dist/lib/hybrid-search.js +22 -1
  62. package/dist/lib/schedules.js +22 -1
  63. package/dist/lib/store.js +22 -1
  64. package/dist/mcp/server.js +126 -1
  65. package/dist/runtime/index.js +22 -1
  66. package/dist/tui/App.js +22 -1
  67. package/package.json +1 -1
@@ -2827,12 +2827,26 @@ async function ensureSchema() {
2827
2827
  session_name TEXT,
2828
2828
  task_id TEXT,
2829
2829
  project_name TEXT,
2830
- started_at TEXT NOT NULL
2830
+ started_at TEXT NOT NULL,
2831
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
2831
2832
  );
2832
2833
 
2833
2834
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2834
2835
  ON session_agent_map(agent_id);
2835
2836
  `);
2837
+ await client.executeMultiple(`
2838
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
2839
+ session_uuid TEXT NOT NULL,
2840
+ agent_id TEXT NOT NULL,
2841
+ file_path TEXT NOT NULL,
2842
+ read_at TEXT NOT NULL,
2843
+ commit_hash TEXT,
2844
+ PRIMARY KEY (session_uuid, file_path)
2845
+ );
2846
+
2847
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
2848
+ ON agent_file_reads(agent_id, read_at);
2849
+ `);
2836
2850
  try {
2837
2851
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2838
2852
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -2847,6 +2861,13 @@ async function ensureSchema() {
2847
2861
  }
2848
2862
  } catch {
2849
2863
  }
2864
+ try {
2865
+ await client.execute({
2866
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2867
+ args: []
2868
+ });
2869
+ } catch {
2870
+ }
2850
2871
  try {
2851
2872
  await client.execute({
2852
2873
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -4493,8 +4514,8 @@ async function embedDirect(text) {
4493
4514
  const llamaCpp = await import("node-llama-cpp");
4494
4515
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4495
4516
  const { existsSync: existsSync16 } = await import("fs");
4496
- const path19 = await import("path");
4497
- const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4517
+ const path20 = await import("path");
4518
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4498
4519
  if (!existsSync16(modelPath)) {
4499
4520
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
4500
4521
  }
@@ -6471,9 +6492,240 @@ var init_task_scope = __esm({
6471
6492
  }
6472
6493
  });
6473
6494
 
6495
+ // src/lib/catchup-brief.ts
6496
+ var catchup_brief_exports = {};
6497
+ __export(catchup_brief_exports, {
6498
+ buildCatchupBrief: () => buildCatchupBrief
6499
+ });
6500
+ import { execSync as execSync7 } from "child_process";
6501
+ function clipText(text, maxChars) {
6502
+ if (text.length <= maxChars) return text;
6503
+ if (maxChars <= 1) return "\u2026";
6504
+ return `${text.slice(0, maxChars - 1).trimEnd()}\u2026`;
6505
+ }
6506
+ function clampSections(header, sections, maxChars) {
6507
+ const bounded = [];
6508
+ let remaining = Math.max(0, maxChars - header.length);
6509
+ for (const section of sections) {
6510
+ const separatorChars = 2;
6511
+ if (remaining <= separatorChars) break;
6512
+ remaining -= separatorChars;
6513
+ if (section.length <= remaining) {
6514
+ bounded.push(section);
6515
+ remaining -= section.length;
6516
+ continue;
6517
+ }
6518
+ const clipped = clipText(section, remaining);
6519
+ if (clipped.trim().length > 0) bounded.push(clipped);
6520
+ break;
6521
+ }
6522
+ return bounded;
6523
+ }
6524
+ async function buildCatchupBrief(agentId, projectName, cwd, sessionScope) {
6525
+ const sections = [];
6526
+ let lastTimestamp = new Date(Date.now() - DEFAULT_LOOKBACK_MS).toISOString();
6527
+ try {
6528
+ const checkpointMemories = await lightweightSearch(
6529
+ "CONTEXT CHECKPOINT",
6530
+ agentId,
6531
+ { projectName, limit: 1 }
6532
+ );
6533
+ const checkpoint = checkpointMemories[0];
6534
+ if (checkpoint?.timestamp) {
6535
+ lastTimestamp = checkpoint.timestamp;
6536
+ }
6537
+ if (checkpoint?.raw_text) {
6538
+ sections.push(
6539
+ `### Last Checkpoint
6540
+ ${clipText(checkpoint.raw_text, MAX_CHECKPOINT_CHARS)}`
6541
+ );
6542
+ }
6543
+ } catch {
6544
+ }
6545
+ try {
6546
+ const gitLog = execSync7(
6547
+ `git log --oneline --since=${JSON.stringify(lastTimestamp)} --format="%h %an: %s" | head -10`,
6548
+ { cwd, timeout: 3e3, encoding: "utf-8" }
6549
+ ).trim();
6550
+ if (gitLog) {
6551
+ sections.push(`### Git Activity Since You Left
6552
+ \`\`\`
6553
+ ${gitLog}
6554
+ \`\`\``);
6555
+ }
6556
+ } catch {
6557
+ }
6558
+ try {
6559
+ const client = getClient();
6560
+ const taskScope = sessionScopeFilter(sessionScope);
6561
+ const taskChanges = await client.execute({
6562
+ sql: `SELECT title, status, priority FROM tasks
6563
+ WHERE assigned_to = ? AND project_name = ?
6564
+ AND updated_at > ? AND status IN ('open', 'in_progress')${taskScope.sql}
6565
+ ORDER BY priority ASC, updated_at DESC LIMIT 5`,
6566
+ args: [agentId, projectName, lastTimestamp, ...taskScope.args]
6567
+ });
6568
+ if (taskChanges.rows.length > 0) {
6569
+ const taskLines = taskChanges.rows.map((row) => {
6570
+ const record = row;
6571
+ return `- [${String(record.priority).toUpperCase()}] ${String(record.title)} (${String(record.status)})`;
6572
+ }).join("\n");
6573
+ sections.push(`### Your Task Queue
6574
+ ${taskLines}`);
6575
+ }
6576
+ } catch {
6577
+ }
6578
+ try {
6579
+ const client = getClient();
6580
+ const messageScope = strictSessionScopeFilter(sessionScope);
6581
+ const messages = await client.execute({
6582
+ sql: `SELECT from_agent, content, created_at FROM messages
6583
+ WHERE target_agent = ? AND status = 'pending'${messageScope.sql}
6584
+ ORDER BY created_at DESC LIMIT 3`,
6585
+ args: [agentId, ...messageScope.args]
6586
+ });
6587
+ if (messages.rows.length > 0) {
6588
+ const msgLines = messages.rows.map((row) => {
6589
+ const record = row;
6590
+ return `- From ${String(record.from_agent)}: ${clipText(String(record.content ?? ""), MAX_MESSAGE_CHARS)}`;
6591
+ }).join("\n");
6592
+ sections.push(`### Unread Messages
6593
+ ${msgLines}`);
6594
+ }
6595
+ } catch {
6596
+ }
6597
+ return {
6598
+ header: HEADER,
6599
+ sections: clampSections(HEADER, sections, MAX_BRIEF_CHARS)
6600
+ };
6601
+ }
6602
+ var HEADER, DEFAULT_LOOKBACK_MS, MAX_BRIEF_CHARS, MAX_CHECKPOINT_CHARS, MAX_MESSAGE_CHARS;
6603
+ var init_catchup_brief = __esm({
6604
+ "src/lib/catchup-brief.ts"() {
6605
+ "use strict";
6606
+ init_database();
6607
+ init_hybrid_search();
6608
+ init_task_scope();
6609
+ HEADER = "## Catchup Brief\nHere's what happened while you were offline:";
6610
+ DEFAULT_LOOKBACK_MS = 60 * 60 * 1e3;
6611
+ MAX_BRIEF_CHARS = 2e3;
6612
+ MAX_CHECKPOINT_CHARS = 500;
6613
+ MAX_MESSAGE_CHARS = 100;
6614
+ }
6615
+ });
6616
+
6617
+ // src/lib/git-staleness.ts
6618
+ var git_staleness_exports = {};
6619
+ __export(git_staleness_exports, {
6620
+ clearSessionFileReads: () => clearSessionFileReads,
6621
+ detectStaleFiles: () => detectStaleFiles,
6622
+ recordFileRead: () => recordFileRead
6623
+ });
6624
+ import { execSync as execSync8 } from "child_process";
6625
+ import path18 from "path";
6626
+ function getHeadCommit(cwd) {
6627
+ try {
6628
+ return execSync8("git rev-parse --short HEAD", {
6629
+ cwd,
6630
+ timeout: GIT_TIMEOUT_MS,
6631
+ encoding: "utf-8"
6632
+ }).trim() || null;
6633
+ } catch {
6634
+ return null;
6635
+ }
6636
+ }
6637
+ function normalizeTrackedPath(cwd, filePath) {
6638
+ const resolved = path18.resolve(cwd, filePath);
6639
+ const relative = path18.relative(cwd, resolved);
6640
+ if (!relative || relative.startsWith("..") || path18.isAbsolute(relative)) {
6641
+ return null;
6642
+ }
6643
+ return relative;
6644
+ }
6645
+ function formatGitSummary(filePath, summary) {
6646
+ const match = summary.trim().match(/^([a-f0-9]+)\s+([^:]+):\s+(.+)$/i);
6647
+ if (!match) return `${filePath} (${summary.trim()})`;
6648
+ const [, hash, author, subject] = match;
6649
+ return `${filePath} (modified by ${author} in commit ${hash}: "${subject}")`;
6650
+ }
6651
+ async function recordFileRead(sessionUuid, agentId, cwd, filePath) {
6652
+ if (!sessionUuid || !filePath) return;
6653
+ const trackedPath = normalizeTrackedPath(cwd, filePath);
6654
+ if (!trackedPath) return;
6655
+ const client = getClient();
6656
+ await client.execute({
6657
+ sql: `INSERT INTO agent_file_reads (session_uuid, agent_id, file_path, read_at, commit_hash)
6658
+ VALUES (?, ?, ?, ?, ?)
6659
+ ON CONFLICT(session_uuid, file_path) DO UPDATE SET
6660
+ read_at = excluded.read_at,
6661
+ commit_hash = excluded.commit_hash`,
6662
+ args: [
6663
+ sessionUuid,
6664
+ agentId,
6665
+ trackedPath,
6666
+ (/* @__PURE__ */ new Date()).toISOString(),
6667
+ getHeadCommit(cwd)
6668
+ ]
6669
+ });
6670
+ }
6671
+ async function detectStaleFiles(agentId, cwd) {
6672
+ const client = getClient();
6673
+ const recentCutoff = new Date(Date.now() - RECENT_READ_LOOKBACK_MS).toISOString();
6674
+ const result = await client.execute({
6675
+ sql: `SELECT file_path, MAX(read_at) AS read_at
6676
+ FROM agent_file_reads
6677
+ WHERE agent_id = ? AND read_at >= ?
6678
+ GROUP BY file_path
6679
+ ORDER BY MAX(read_at) DESC
6680
+ LIMIT 10`,
6681
+ args: [agentId, recentCutoff]
6682
+ });
6683
+ const stale = [];
6684
+ for (const row of result.rows) {
6685
+ if (stale.length >= MAX_STALE_FILES) break;
6686
+ const record = row;
6687
+ const filePath = String(record.file_path ?? "");
6688
+ const readAt = String(record.read_at ?? "");
6689
+ if (!filePath || !readAt) continue;
6690
+ try {
6691
+ const gitSummary = execSync8(
6692
+ `git log -1 --oneline --after=${JSON.stringify(readAt)} --format="%h %an: %s" -- ${JSON.stringify(filePath)}`,
6693
+ {
6694
+ cwd,
6695
+ timeout: GIT_TIMEOUT_MS,
6696
+ encoding: "utf-8"
6697
+ }
6698
+ ).trim();
6699
+ if (gitSummary) {
6700
+ stale.push(formatGitSummary(filePath, gitSummary));
6701
+ }
6702
+ } catch {
6703
+ }
6704
+ }
6705
+ return stale.slice(0, MAX_STALE_FILES);
6706
+ }
6707
+ async function clearSessionFileReads(sessionUuid) {
6708
+ if (!sessionUuid) return;
6709
+ const client = getClient();
6710
+ await client.execute({
6711
+ sql: "DELETE FROM agent_file_reads WHERE session_uuid = ?",
6712
+ args: [sessionUuid]
6713
+ });
6714
+ }
6715
+ var RECENT_READ_LOOKBACK_MS, MAX_STALE_FILES, GIT_TIMEOUT_MS;
6716
+ var init_git_staleness = __esm({
6717
+ "src/lib/git-staleness.ts"() {
6718
+ "use strict";
6719
+ init_database();
6720
+ RECENT_READ_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
6721
+ MAX_STALE_FILES = 10;
6722
+ GIT_TIMEOUT_MS = 400;
6723
+ }
6724
+ });
6725
+
6474
6726
  // src/adapters/claude/hooks/session-start.ts
6475
6727
  init_config();
6476
- import path18 from "path";
6728
+ import path19 from "path";
6477
6729
  import { unlinkSync as unlinkSync4, existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
6478
6730
  if (!process.env.AGENT_ID) {
6479
6731
  process.env.AGENT_ID = "default";
@@ -6500,15 +6752,16 @@ process.stdin.on("end", async () => {
6500
6752
  const { lightweightSearch: lightweightSearch2 } = await Promise.resolve().then(() => (init_hybrid_search(), hybrid_search_exports));
6501
6753
  const { getActiveAgent: getActiveAgent2 } = await Promise.resolve().then(() => (init_active_agent2(), active_agent_exports));
6502
6754
  const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
6503
- const { sessionScopeFilter: sessionScopeFilter2 } = await Promise.resolve().then(() => (init_task_scope(), task_scope_exports));
6755
+ const { sessionScopeFilter: sessionScopeFilter2, getCurrentSessionScope: getCurrentSessionScope2 } = await Promise.resolve().then(() => (init_task_scope(), task_scope_exports));
6504
6756
  const { canCoordinate: canCoordinate2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
6505
6757
  const data = JSON.parse(input);
6506
6758
  const projectName = getProjectName2(data.cwd || void 0);
6507
6759
  const source = data.source ?? "startup";
6760
+ const sessionScope = getCurrentSessionScope2();
6508
6761
  if (source === "startup") {
6509
6762
  try {
6510
- const undefinedPath = path18.join(
6511
- process.env.EXE_OS_DIR ?? path18.join(process.env.HOME ?? "", ".exe-os"),
6763
+ const undefinedPath = path19.join(
6764
+ process.env.EXE_OS_DIR ?? path19.join(process.env.HOME ?? "", ".exe-os"),
6512
6765
  "session-cache",
6513
6766
  "active-agent-undefined.json"
6514
6767
  );
@@ -6521,18 +6774,19 @@ process.stdin.on("end", async () => {
6521
6774
  const agentId = agent.agentId;
6522
6775
  if (agentId !== "default") {
6523
6776
  try {
6524
- const cacheDir = path18.join(
6525
- process.env.EXE_OS_DIR ?? path18.join(process.env.HOME ?? "", ".exe-os"),
6777
+ const cacheDir = path19.join(
6778
+ process.env.EXE_OS_DIR ?? path19.join(process.env.HOME ?? "", ".exe-os"),
6526
6779
  "session-cache"
6527
6780
  );
6528
- const markerPath = path18.join(cacheDir, `current-task-${agentId}.json`);
6781
+ const markerPath = path19.join(cacheDir, `current-task-${agentId}.json`);
6529
6782
  if (existsSync15(markerPath)) {
6530
6783
  const marker = JSON.parse(readFileSync12(markerPath, "utf-8"));
6531
6784
  if (marker.taskId) {
6532
6785
  const client = getClient2();
6786
+ const markerScope = sessionScopeFilter2(sessionScope);
6533
6787
  const result = await client.execute({
6534
- sql: "SELECT status FROM tasks WHERE id = ? LIMIT 1",
6535
- args: [marker.taskId]
6788
+ sql: `SELECT status FROM tasks WHERE id = ?${markerScope.sql} LIMIT 1`,
6789
+ args: [marker.taskId, ...markerScope.args]
6536
6790
  });
6537
6791
  const status = result.rows[0]?.status;
6538
6792
  if (!status || status !== "in_progress") {
@@ -6549,8 +6803,14 @@ process.stdin.on("end", async () => {
6549
6803
  const client = getClient2();
6550
6804
  const sessionName = process.env.EXE_SESSION_NAME ?? "";
6551
6805
  await client.execute({
6552
- sql: `INSERT OR REPLACE INTO session_agent_map (session_uuid, agent_id, session_name, project_name, started_at)
6553
- VALUES (?, ?, ?, ?, ?)`,
6806
+ sql: `INSERT INTO session_agent_map (session_uuid, agent_id, session_name, project_name, started_at, cache_cold_count)
6807
+ VALUES (?, ?, ?, ?, ?, 0)
6808
+ ON CONFLICT(session_uuid) DO UPDATE SET
6809
+ agent_id = excluded.agent_id,
6810
+ session_name = excluded.session_name,
6811
+ project_name = excluded.project_name,
6812
+ started_at = excluded.started_at,
6813
+ cache_cold_count = COALESCE(session_agent_map.cache_cold_count, 0)`,
6554
6814
  args: [sessionId, agentId, sessionName, projectName, (/* @__PURE__ */ new Date()).toISOString()]
6555
6815
  });
6556
6816
  }
@@ -6558,9 +6818,32 @@ process.stdin.on("end", async () => {
6558
6818
  }
6559
6819
  let query;
6560
6820
  let header;
6821
+ let extraContext = "";
6561
6822
  if (source === "resume") {
6562
6823
  query = `last actions on ${projectName}`;
6563
6824
  header = "## Resuming Session\nHere's where you left off:";
6825
+ try {
6826
+ const { buildCatchupBrief: buildCatchupBrief2 } = await Promise.resolve().then(() => (init_catchup_brief(), catchup_brief_exports));
6827
+ const brief = await buildCatchupBrief2(
6828
+ agentId,
6829
+ projectName,
6830
+ data.cwd || process.cwd(),
6831
+ sessionScope
6832
+ );
6833
+ header = brief.header;
6834
+ extraContext = brief.sections.join("\n\n");
6835
+ } catch {
6836
+ }
6837
+ try {
6838
+ const { detectStaleFiles: detectStaleFiles2 } = await Promise.resolve().then(() => (init_git_staleness(), git_staleness_exports));
6839
+ const staleFiles = await detectStaleFiles2(agentId, data.cwd || process.cwd());
6840
+ if (staleFiles.length > 0) {
6841
+ extraContext += `${extraContext.length > 0 ? "\n\n" : ""}## \u26A0\uFE0F Stale File Warning
6842
+ The following files have been modified since you last read them:
6843
+ ` + staleFiles.slice(0, 10).map((line) => `- ${line}`).join("\n") + "\n\nRe-read these files before making changes to avoid conflicts.";
6844
+ }
6845
+ } catch {
6846
+ }
6564
6847
  } else {
6565
6848
  query = `recent work on ${projectName}`;
6566
6849
  header = "## Memory Brief\nRecent memories from this project:";
@@ -6573,7 +6856,7 @@ process.stdin.on("end", async () => {
6573
6856
  let taskBrief = "";
6574
6857
  try {
6575
6858
  const client = getClient2();
6576
- const ssScope = sessionScopeFilter2();
6859
+ const ssScope = sessionScopeFilter2(sessionScope);
6577
6860
  const taskResult = await client.execute({
6578
6861
  sql: `SELECT id, title, priority, project_name, status FROM tasks WHERE assigned_to = ? AND status IN ('open', 'in_progress')${ssScope.sql} ORDER BY priority ASC`,
6579
6862
  args: [agentId, ...ssScope.args]
@@ -6612,6 +6895,12 @@ flow, so bug fixes are fast.`;
6612
6895
  additionalContext += `${header}
6613
6896
  ${brief}`;
6614
6897
  }
6898
+ if (extraContext.length > 0) {
6899
+ if (additionalContext.length === 0) {
6900
+ additionalContext += header;
6901
+ }
6902
+ additionalContext += `${additionalContext.length > 0 ? "\n\n" : ""}${extraContext}`;
6903
+ }
6615
6904
  additionalContext += taskBrief;
6616
6905
  if (additionalContext.length > 0) {
6617
6906
  const output = JSON.stringify({
@@ -2591,12 +2591,26 @@ async function ensureSchema() {
2591
2591
  session_name TEXT,
2592
2592
  task_id TEXT,
2593
2593
  project_name TEXT,
2594
- started_at TEXT NOT NULL
2594
+ started_at TEXT NOT NULL,
2595
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
2595
2596
  );
2596
2597
 
2597
2598
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2598
2599
  ON session_agent_map(agent_id);
2599
2600
  `);
2601
+ await client.executeMultiple(`
2602
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
2603
+ session_uuid TEXT NOT NULL,
2604
+ agent_id TEXT NOT NULL,
2605
+ file_path TEXT NOT NULL,
2606
+ read_at TEXT NOT NULL,
2607
+ commit_hash TEXT,
2608
+ PRIMARY KEY (session_uuid, file_path)
2609
+ );
2610
+
2611
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
2612
+ ON agent_file_reads(agent_id, read_at);
2613
+ `);
2600
2614
  try {
2601
2615
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2602
2616
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -2611,6 +2625,13 @@ async function ensureSchema() {
2611
2625
  }
2612
2626
  } catch {
2613
2627
  }
2628
+ try {
2629
+ await client.execute({
2630
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2631
+ args: []
2632
+ });
2633
+ } catch {
2634
+ }
2614
2635
  try {
2615
2636
  await client.execute({
2616
2637
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -2566,12 +2566,26 @@ async function ensureSchema() {
2566
2566
  session_name TEXT,
2567
2567
  task_id TEXT,
2568
2568
  project_name TEXT,
2569
- started_at TEXT NOT NULL
2569
+ started_at TEXT NOT NULL,
2570
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
2570
2571
  );
2571
2572
 
2572
2573
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2573
2574
  ON session_agent_map(agent_id);
2574
2575
  `);
2576
+ await client.executeMultiple(`
2577
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
2578
+ session_uuid TEXT NOT NULL,
2579
+ agent_id TEXT NOT NULL,
2580
+ file_path TEXT NOT NULL,
2581
+ read_at TEXT NOT NULL,
2582
+ commit_hash TEXT,
2583
+ PRIMARY KEY (session_uuid, file_path)
2584
+ );
2585
+
2586
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
2587
+ ON agent_file_reads(agent_id, read_at);
2588
+ `);
2575
2589
  try {
2576
2590
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2577
2591
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -2586,6 +2600,13 @@ async function ensureSchema() {
2586
2600
  }
2587
2601
  } catch {
2588
2602
  }
2603
+ try {
2604
+ await client.execute({
2605
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2606
+ args: []
2607
+ });
2608
+ } catch {
2609
+ }
2589
2610
  try {
2590
2611
  await client.execute({
2591
2612
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -2563,12 +2563,26 @@ async function ensureSchema() {
2563
2563
  session_name TEXT,
2564
2564
  task_id TEXT,
2565
2565
  project_name TEXT,
2566
- started_at TEXT NOT NULL
2566
+ started_at TEXT NOT NULL,
2567
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
2567
2568
  );
2568
2569
 
2569
2570
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2570
2571
  ON session_agent_map(agent_id);
2571
2572
  `);
2573
+ await client.executeMultiple(`
2574
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
2575
+ session_uuid TEXT NOT NULL,
2576
+ agent_id TEXT NOT NULL,
2577
+ file_path TEXT NOT NULL,
2578
+ read_at TEXT NOT NULL,
2579
+ commit_hash TEXT,
2580
+ PRIMARY KEY (session_uuid, file_path)
2581
+ );
2582
+
2583
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
2584
+ ON agent_file_reads(agent_id, read_at);
2585
+ `);
2572
2586
  try {
2573
2587
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2574
2588
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -2583,6 +2597,13 @@ async function ensureSchema() {
2583
2597
  }
2584
2598
  } catch {
2585
2599
  }
2600
+ try {
2601
+ await client.execute({
2602
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2603
+ args: []
2604
+ });
2605
+ } catch {
2606
+ }
2586
2607
  try {
2587
2608
  await client.execute({
2588
2609
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
package/dist/index.js CHANGED
@@ -3268,12 +3268,26 @@ async function ensureSchema() {
3268
3268
  session_name TEXT,
3269
3269
  task_id TEXT,
3270
3270
  project_name TEXT,
3271
- started_at TEXT NOT NULL
3271
+ started_at TEXT NOT NULL,
3272
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
3272
3273
  );
3273
3274
 
3274
3275
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
3275
3276
  ON session_agent_map(agent_id);
3276
3277
  `);
3278
+ await client.executeMultiple(`
3279
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
3280
+ session_uuid TEXT NOT NULL,
3281
+ agent_id TEXT NOT NULL,
3282
+ file_path TEXT NOT NULL,
3283
+ read_at TEXT NOT NULL,
3284
+ commit_hash TEXT,
3285
+ PRIMARY KEY (session_uuid, file_path)
3286
+ );
3287
+
3288
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
3289
+ ON agent_file_reads(agent_id, read_at);
3290
+ `);
3277
3291
  try {
3278
3292
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
3279
3293
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -3288,6 +3302,13 @@ async function ensureSchema() {
3288
3302
  }
3289
3303
  } catch {
3290
3304
  }
3305
+ try {
3306
+ await client.execute({
3307
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
3308
+ args: []
3309
+ });
3310
+ } catch {
3311
+ }
3291
3312
  try {
3292
3313
  await client.execute({
3293
3314
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -2220,12 +2220,26 @@ async function ensureSchema() {
2220
2220
  session_name TEXT,
2221
2221
  task_id TEXT,
2222
2222
  project_name TEXT,
2223
- started_at TEXT NOT NULL
2223
+ started_at TEXT NOT NULL,
2224
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
2224
2225
  );
2225
2226
 
2226
2227
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2227
2228
  ON session_agent_map(agent_id);
2228
2229
  `);
2230
+ await client.executeMultiple(`
2231
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
2232
+ session_uuid TEXT NOT NULL,
2233
+ agent_id TEXT NOT NULL,
2234
+ file_path TEXT NOT NULL,
2235
+ read_at TEXT NOT NULL,
2236
+ commit_hash TEXT,
2237
+ PRIMARY KEY (session_uuid, file_path)
2238
+ );
2239
+
2240
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
2241
+ ON agent_file_reads(agent_id, read_at);
2242
+ `);
2229
2243
  try {
2230
2244
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2231
2245
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -2240,6 +2254,13 @@ async function ensureSchema() {
2240
2254
  }
2241
2255
  } catch {
2242
2256
  }
2257
+ try {
2258
+ await client.execute({
2259
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2260
+ args: []
2261
+ });
2262
+ } catch {
2263
+ }
2243
2264
  try {
2244
2265
  await client.execute({
2245
2266
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
@@ -2143,12 +2143,26 @@ async function ensureSchema() {
2143
2143
  session_name TEXT,
2144
2144
  task_id TEXT,
2145
2145
  project_name TEXT,
2146
- started_at TEXT NOT NULL
2146
+ started_at TEXT NOT NULL,
2147
+ cache_cold_count INTEGER NOT NULL DEFAULT 0
2147
2148
  );
2148
2149
 
2149
2150
  CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
2150
2151
  ON session_agent_map(agent_id);
2151
2152
  `);
2153
+ await client.executeMultiple(`
2154
+ CREATE TABLE IF NOT EXISTS agent_file_reads (
2155
+ session_uuid TEXT NOT NULL,
2156
+ agent_id TEXT NOT NULL,
2157
+ file_path TEXT NOT NULL,
2158
+ read_at TEXT NOT NULL,
2159
+ commit_hash TEXT,
2160
+ PRIMARY KEY (session_uuid, file_path)
2161
+ );
2162
+
2163
+ CREATE INDEX IF NOT EXISTS idx_agent_file_reads_agent_read_at
2164
+ ON agent_file_reads(agent_id, read_at);
2165
+ `);
2152
2166
  try {
2153
2167
  const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
2154
2168
  if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
@@ -2163,6 +2177,13 @@ async function ensureSchema() {
2163
2177
  }
2164
2178
  } catch {
2165
2179
  }
2180
+ try {
2181
+ await client.execute({
2182
+ sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2183
+ args: []
2184
+ });
2185
+ } catch {
2186
+ }
2166
2187
  try {
2167
2188
  await client.execute({
2168
2189
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,