@supen-ai/cli 1.4.1 → 1.4.3

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 (203) hide show
  1. package/README.md +1 -1
  2. package/daemon/dist/agent-sdk/app-server-stream.js +5 -5
  3. package/daemon/dist/agent-sdk/app-server-stream.js.map +1 -1
  4. package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.d.ts +3 -2
  5. package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.d.ts.map +1 -1
  6. package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.js +72 -49
  7. package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.js.map +1 -1
  8. package/daemon/dist/agent-sdk/drivers/driver.d.ts +8 -8
  9. package/daemon/dist/agent-sdk/drivers/driver.d.ts.map +1 -1
  10. package/daemon/dist/agent-sdk/index.d.ts +4 -4
  11. package/daemon/dist/agent-sdk/index.d.ts.map +1 -1
  12. package/daemon/dist/agent-sdk/index.js +2 -2
  13. package/daemon/dist/agent-sdk/index.js.map +1 -1
  14. package/daemon/dist/agent-sdk/intelligence/contracts.d.ts +2 -2
  15. package/daemon/dist/agent-sdk/intelligence/contracts.d.ts.map +1 -1
  16. package/daemon/dist/agent-sdk/memory/subsystem.d.ts +1 -1
  17. package/daemon/dist/agent-sdk/memory/subsystem.d.ts.map +1 -1
  18. package/daemon/dist/agent-sdk/{session-events.d.ts → thread-events.d.ts} +18 -18
  19. package/daemon/dist/agent-sdk/thread-events.d.ts.map +1 -0
  20. package/daemon/dist/agent-sdk/{session-events.js → thread-events.js} +15 -15
  21. package/daemon/dist/agent-sdk/thread-events.js.map +1 -0
  22. package/daemon/dist/agent-sdk/{session-manager.d.ts → thread-manager.d.ts} +9 -9
  23. package/daemon/dist/agent-sdk/thread-manager.d.ts.map +1 -0
  24. package/daemon/dist/agent-sdk/{session-manager.js → thread-manager.js} +7 -7
  25. package/daemon/dist/agent-sdk/thread-manager.js.map +1 -0
  26. package/daemon/dist/agent-sdk/types.d.ts +18 -18
  27. package/daemon/dist/agent-sdk/types.d.ts.map +1 -1
  28. package/daemon/dist/automation-event-listener.js +6 -6
  29. package/daemon/dist/automation-event-listener.js.map +1 -1
  30. package/daemon/dist/automation-runner.d.ts +1 -1
  31. package/daemon/dist/automation-runner.d.ts.map +1 -1
  32. package/daemon/dist/automation-runner.js +24 -24
  33. package/daemon/dist/automation-runner.js.map +1 -1
  34. package/daemon/dist/autonomy/memory-rules.d.ts +2 -2
  35. package/daemon/dist/autonomy/memory-rules.d.ts.map +1 -1
  36. package/daemon/dist/autonomy/memory-rules.js +1 -1
  37. package/daemon/dist/autonomy/memory-rules.js.map +1 -1
  38. package/daemon/dist/autonomy/session-autonomy.d.ts +4 -4
  39. package/daemon/dist/autonomy/session-autonomy.d.ts.map +1 -1
  40. package/daemon/dist/autonomy/session-autonomy.js +5 -5
  41. package/daemon/dist/autonomy/session-autonomy.js.map +1 -1
  42. package/daemon/dist/bin/mcp-os.js +1 -1
  43. package/daemon/dist/bin/mcp-os.js.map +1 -1
  44. package/daemon/dist/bin/mcp-scheduler.js +1 -1
  45. package/daemon/dist/bin/mcp-scheduler.js.map +1 -1
  46. package/daemon/dist/bin/supen-sys.js +1 -1
  47. package/daemon/dist/bin/supen-sys.js.map +1 -1
  48. package/daemon/dist/bootstrap/hub-bootstrap.js +1 -1
  49. package/daemon/dist/bootstrap/hub-bootstrap.js.map +1 -1
  50. package/daemon/dist/channels/http-routes.d.ts.map +1 -1
  51. package/daemon/dist/channels/http-routes.js +54 -60
  52. package/daemon/dist/channels/http-routes.js.map +1 -1
  53. package/daemon/dist/channels/http.js +2 -2
  54. package/daemon/dist/channels/http.js.map +1 -1
  55. package/daemon/dist/channels/index.d.ts +0 -1
  56. package/daemon/dist/channels/index.d.ts.map +1 -1
  57. package/daemon/dist/channels/index.js +0 -2
  58. package/daemon/dist/channels/index.js.map +1 -1
  59. package/daemon/dist/channels/registry.d.ts +1 -1
  60. package/daemon/dist/channels/registry.d.ts.map +1 -1
  61. package/daemon/dist/commands/builtin.d.ts +1 -1
  62. package/daemon/dist/commands/builtin.d.ts.map +1 -1
  63. package/daemon/dist/commands/builtin.js +2 -2
  64. package/daemon/dist/commands/builtin.js.map +1 -1
  65. package/daemon/dist/commands/catalog.js +3 -3
  66. package/daemon/dist/commands/catalog.js.map +1 -1
  67. package/daemon/dist/core/config.d.ts +0 -2
  68. package/daemon/dist/core/config.d.ts.map +1 -1
  69. package/daemon/dist/core/config.js +0 -5
  70. package/daemon/dist/core/config.js.map +1 -1
  71. package/daemon/dist/core/control-commands.d.ts +3 -3
  72. package/daemon/dist/core/control-commands.d.ts.map +1 -1
  73. package/daemon/dist/core/control-commands.js +6 -6
  74. package/daemon/dist/core/control-commands.js.map +1 -1
  75. package/daemon/dist/core/control-log.d.ts +4 -4
  76. package/daemon/dist/core/control-log.d.ts.map +1 -1
  77. package/daemon/dist/core/control-log.js +12 -12
  78. package/daemon/dist/core/control-log.js.map +1 -1
  79. package/daemon/dist/core/cortex.d.ts +4 -4
  80. package/daemon/dist/core/cortex.d.ts.map +1 -1
  81. package/daemon/dist/core/cortex.js +98 -117
  82. package/daemon/dist/core/cortex.js.map +1 -1
  83. package/daemon/dist/core/dispatcher.d.ts +3 -3
  84. package/daemon/dist/core/dispatcher.js +18 -18
  85. package/daemon/dist/core/dispatcher.js.map +1 -1
  86. package/daemon/dist/core/gateway-protocol.d.ts +0 -1
  87. package/daemon/dist/core/gateway-protocol.d.ts.map +1 -1
  88. package/daemon/dist/core/gateway.d.ts +3 -3
  89. package/daemon/dist/core/gateway.d.ts.map +1 -1
  90. package/daemon/dist/core/gateway.js +26 -26
  91. package/daemon/dist/core/gateway.js.map +1 -1
  92. package/daemon/dist/core/hub-snapshot.d.ts +1 -1
  93. package/daemon/dist/core/hub-snapshot.d.ts.map +1 -1
  94. package/daemon/dist/core/hub-snapshot.js +1 -1
  95. package/daemon/dist/core/hub-snapshot.js.map +1 -1
  96. package/daemon/dist/core/pairing.d.ts +2 -2
  97. package/daemon/dist/core/pairing.js +3 -3
  98. package/daemon/dist/core/pairing.js.map +1 -1
  99. package/daemon/dist/core/store.d.ts +38 -38
  100. package/daemon/dist/core/store.d.ts.map +1 -1
  101. package/daemon/dist/core/store.js +285 -289
  102. package/daemon/dist/core/store.js.map +1 -1
  103. package/daemon/dist/core/task-artifacts.d.ts +4 -4
  104. package/daemon/dist/core/task-artifacts.d.ts.map +1 -1
  105. package/daemon/dist/core/task-artifacts.js +10 -10
  106. package/daemon/dist/core/task-artifacts.js.map +1 -1
  107. package/daemon/dist/core/thread-context.js +1 -1
  108. package/daemon/dist/core/thread-context.js.map +1 -1
  109. package/daemon/dist/core/types.d.ts +28 -28
  110. package/daemon/dist/core/types.d.ts.map +1 -1
  111. package/daemon/dist/core/utils.js +1 -1
  112. package/daemon/dist/core/utils.js.map +1 -1
  113. package/daemon/dist/http/router.d.ts +2 -2
  114. package/daemon/dist/http/router.d.ts.map +1 -1
  115. package/daemon/dist/http/router.js +5 -5
  116. package/daemon/dist/http/router.js.map +1 -1
  117. package/daemon/dist/http/routes/agents.js +3 -3
  118. package/daemon/dist/http/routes/agents.js.map +1 -1
  119. package/daemon/dist/http/routes/automations.d.ts +2 -2
  120. package/daemon/dist/http/routes/automations.d.ts.map +1 -1
  121. package/daemon/dist/http/routes/automations.js +23 -23
  122. package/daemon/dist/http/routes/automations.js.map +1 -1
  123. package/daemon/dist/http/routes/chat-input.d.ts +1 -1
  124. package/daemon/dist/http/routes/chat-input.d.ts.map +1 -1
  125. package/daemon/dist/http/routes/chat-input.js +2 -2
  126. package/daemon/dist/http/routes/chat-input.js.map +1 -1
  127. package/daemon/dist/http/routes/plugins.d.ts.map +1 -1
  128. package/daemon/dist/http/routes/plugins.js +6 -74
  129. package/daemon/dist/http/routes/plugins.js.map +1 -1
  130. package/daemon/dist/http/routes/rpc.d.ts +3 -3
  131. package/daemon/dist/http/routes/rpc.d.ts.map +1 -1
  132. package/daemon/dist/http/routes/rpc.js +91 -92
  133. package/daemon/dist/http/routes/rpc.js.map +1 -1
  134. package/daemon/dist/http/routes/system.d.ts +7 -7
  135. package/daemon/dist/http/routes/system.d.ts.map +1 -1
  136. package/daemon/dist/http/routes/system.js +222 -108
  137. package/daemon/dist/http/routes/system.js.map +1 -1
  138. package/daemon/dist/http/routes/threads.d.ts +11 -0
  139. package/daemon/dist/http/routes/threads.d.ts.map +1 -0
  140. package/daemon/dist/http/routes/{sessions.js → threads.js} +157 -157
  141. package/daemon/dist/http/routes/threads.js.map +1 -0
  142. package/daemon/dist/http/stream.d.ts +2 -2
  143. package/daemon/dist/http/stream.d.ts.map +1 -1
  144. package/daemon/dist/http/stream.js +3 -3
  145. package/daemon/dist/http/stream.js.map +1 -1
  146. package/daemon/dist/http/thread-title.d.ts +1 -1
  147. package/daemon/dist/http/thread-title.d.ts.map +1 -1
  148. package/daemon/dist/http/thread-title.js +6 -6
  149. package/daemon/dist/http/thread-title.js.map +1 -1
  150. package/daemon/dist/http/websocket.d.ts +2 -2
  151. package/daemon/dist/http/websocket.d.ts.map +1 -1
  152. package/daemon/dist/http/websocket.js +11 -11
  153. package/daemon/dist/http/websocket.js.map +1 -1
  154. package/daemon/dist/index.d.ts +3 -3
  155. package/daemon/dist/index.d.ts.map +1 -1
  156. package/daemon/dist/index.js +64 -81
  157. package/daemon/dist/index.js.map +1 -1
  158. package/daemon/dist/mcp/aggregate-config.d.ts +1 -1
  159. package/daemon/dist/mcp/index.js +1 -1
  160. package/daemon/dist/mcp/index.js.map +1 -1
  161. package/daemon/dist/mcp/tools.d.ts +1 -1
  162. package/daemon/dist/mcp/tools.js +1 -1
  163. package/daemon/dist/plugins/hub.d.ts +2 -8
  164. package/daemon/dist/plugins/hub.d.ts.map +1 -1
  165. package/daemon/dist/plugins/hub.js +63 -214
  166. package/daemon/dist/plugins/hub.js.map +1 -1
  167. package/daemon/dist/plugins/types.d.ts +10 -0
  168. package/daemon/dist/plugins/types.d.ts.map +1 -1
  169. package/daemon/dist/sub-agent.d.ts +3 -3
  170. package/daemon/dist/sub-agent.d.ts.map +1 -1
  171. package/daemon/dist/sub-agent.js +8 -8
  172. package/daemon/dist/sub-agent.js.map +1 -1
  173. package/daemon/dist/sync/supabase-sync.js +18 -18
  174. package/daemon/dist/sync/supabase-sync.js.map +1 -1
  175. package/daemon/dist/task-executor.js +1 -1
  176. package/daemon/dist/task-executor.js.map +1 -1
  177. package/daemon/dist/tools/shell.js +1 -1
  178. package/daemon/dist/tools/shell.js.map +1 -1
  179. package/daemon/dist/tools/types.d.ts +1 -1
  180. package/daemon/dist/tools/types.d.ts.map +1 -1
  181. package/daemon/package.json +1 -1
  182. package/dist/computer.js +1 -1
  183. package/dist/index.js +1 -1
  184. package/package.json +1 -1
  185. package/daemon/dist/acp-client.d.ts +0 -42
  186. package/daemon/dist/acp-client.d.ts.map +0 -1
  187. package/daemon/dist/acp-client.js +0 -149
  188. package/daemon/dist/acp-client.js.map +0 -1
  189. package/daemon/dist/acp-types.d.ts +0 -98
  190. package/daemon/dist/acp-types.d.ts.map +0 -1
  191. package/daemon/dist/acp-types.js +0 -2
  192. package/daemon/dist/acp-types.js.map +0 -1
  193. package/daemon/dist/agent-sdk/session-events.d.ts.map +0 -1
  194. package/daemon/dist/agent-sdk/session-events.js.map +0 -1
  195. package/daemon/dist/agent-sdk/session-manager.d.ts.map +0 -1
  196. package/daemon/dist/agent-sdk/session-manager.js.map +0 -1
  197. package/daemon/dist/channels/acp.d.ts +0 -23
  198. package/daemon/dist/channels/acp.d.ts.map +0 -1
  199. package/daemon/dist/channels/acp.js +0 -915
  200. package/daemon/dist/channels/acp.js.map +0 -1
  201. package/daemon/dist/http/routes/sessions.d.ts +0 -11
  202. package/daemon/dist/http/routes/sessions.d.ts.map +0 -1
  203. package/daemon/dist/http/routes/sessions.js.map +0 -1
@@ -14,7 +14,7 @@ import { collectOsTelemetryFields } from '../../core/os-info.js';
14
14
  import { getCodingCliStatusResponse, startCodingCliStatusCache, } from '../../core/coding-cli-status-cache.js';
15
15
  import { getGatewayInstance, getLlmToken } from '../../core/gateway.js';
16
16
  import { normalizeGatewayUplinkUrl, readGatewayConfig, writeGatewayConfig, } from '../../core/gateway-config.js';
17
- import { ensureSession, getAllAgents, getDailyUsage, getGlobalUsage, getSessionsForAgent, getSessionUiEvents, updateSessionBackendDriverId, updateSessionSdkId, } from '../../core/store.js';
17
+ import { ensureThread, getAllAgents, getDailyUsage, getGlobalUsage, getThreadsForAgent, getThreadUiEvents, updateThreadBackendDriverId, updateThreadSdkSessionId, } from '../../core/store.js';
18
18
  import { listMcpEnvKeys, listMcpServers } from '../../mcp/default-servers.js';
19
19
  import { getMcpEnvOverrides, updateMcpEnvOverrides } from '../../mcp/settings.js';
20
20
  import { getMcpManager } from '../../mcp/index.js';
@@ -32,6 +32,9 @@ const SPACE_LOG_STREAM_PING_MS = 15000;
32
32
  const MIRRORED_THREAD_LIMIT = 80;
33
33
  const MIRRORED_THREAD_HISTORY_LIMIT = 200;
34
34
  const MIRRORED_THREAD_HISTORY_MAX_BYTES = 256 * 1024 * 1024;
35
+ const MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES = 8 * 1024 * 1024;
36
+ const MIRRORED_THREAD_HISTORY_FAST_MAX_LINES = 2_000;
37
+ const MIRRORED_THREAD_JSONL_CACHE_LIMIT = 64;
35
38
  const MIRRORED_THREAD_PROJECTION_VERSION = 2;
36
39
  const MIRRORED_THREAD_TEXT_LIMIT = 48_000;
37
40
  const MIRRORED_THREAD_INLINE_DATA_URL_LIMIT = 120_000;
@@ -114,6 +117,9 @@ function truncateStreamDelta(value, max = 160) {
114
117
  function currentSpaceId() {
115
118
  return (process.env.SUPEN_SPACE_ID || '').trim() || 'local';
116
119
  }
120
+ const recentJsonlLineCache = new Map();
121
+ const recentCodexHistoryLineCache = new Map();
122
+ const mirroredFileTextCache = new Map();
117
123
  function summarizeStreamEvent(raw) {
118
124
  const inner = raw.event && typeof raw.event === 'object'
119
125
  ? raw.event
@@ -384,13 +390,13 @@ function extractQuotaEvent(event) {
384
390
  };
385
391
  }
386
392
  function readLatestSpaceQuotaStatus() {
387
- const sessions = getAllAgents()
388
- .flatMap((agent) => getSessionsForAgent(agent.agent_id).map((session) => ({
393
+ const threads = getAllAgents()
394
+ .flatMap((agent) => getThreadsForAgent(agent.agent_id).map((thread) => ({
389
395
  agent_id: agent.agent_id,
390
- session_id: session.session_id,
396
+ thread_id: thread.thread_id,
391
397
  })));
392
- const latest = sessions
393
- .flatMap((session) => getSessionUiEvents(session.agent_id, session.session_id, 200)
398
+ const latest = threads
399
+ .flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, 200)
394
400
  .map((event) => extractQuotaEvent(event)))
395
401
  .filter((entry) => Boolean(entry))
396
402
  .sort((a, b) => a.updated_at.localeCompare(b.updated_at))
@@ -414,17 +420,84 @@ function parseJsonLine(line) {
414
420
  return null;
415
421
  }
416
422
  }
417
- export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTORY_MAX_BYTES) {
418
- const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
419
- const size = fs.statSync(filePath).size;
420
- if (size <= safeMaxBytes) {
421
- return fs.readFileSync(filePath, 'utf-8').split(/\r?\n/).filter(Boolean);
423
+ function jsonlCacheKey(parts) {
424
+ return parts.map((part) => part === undefined ? '' : String(part)).join('\u0000');
425
+ }
426
+ function getCachedJsonlLines(cache, key, stat) {
427
+ const cached = cache.get(key);
428
+ if (!cached)
429
+ return null;
430
+ if (cached.size !== stat.size || cached.mtimeMs !== stat.mtimeMs) {
431
+ cache.delete(key);
432
+ return null;
433
+ }
434
+ cached.accessedAt = Date.now();
435
+ return cached.lines;
436
+ }
437
+ function setCachedJsonlLines(cache, key, entry) {
438
+ if (cache.size >= MIRRORED_THREAD_JSONL_CACHE_LIMIT && !cache.has(key)) {
439
+ let oldestKey = null;
440
+ let oldestAccess = Number.POSITIVE_INFINITY;
441
+ for (const [candidateKey, candidate] of cache) {
442
+ if (candidate.accessedAt < oldestAccess) {
443
+ oldestAccess = candidate.accessedAt;
444
+ oldestKey = candidateKey;
445
+ }
446
+ }
447
+ if (oldestKey)
448
+ cache.delete(oldestKey);
449
+ }
450
+ cache.set(key, {
451
+ ...entry,
452
+ accessedAt: Date.now(),
453
+ });
454
+ return entry.lines;
455
+ }
456
+ function getCachedFileText(key, stat) {
457
+ const cached = mirroredFileTextCache.get(key);
458
+ if (!cached)
459
+ return null;
460
+ if (cached.size !== stat.size || cached.mtimeMs !== stat.mtimeMs) {
461
+ mirroredFileTextCache.delete(key);
462
+ return null;
463
+ }
464
+ cached.accessedAt = Date.now();
465
+ return cached.text;
466
+ }
467
+ function setCachedFileText(key, stat, text) {
468
+ if (mirroredFileTextCache.size >= MIRRORED_THREAD_JSONL_CACHE_LIMIT && !mirroredFileTextCache.has(key)) {
469
+ let oldestKey = null;
470
+ let oldestAccess = Number.POSITIVE_INFINITY;
471
+ for (const [candidateKey, candidate] of mirroredFileTextCache) {
472
+ if (candidate.accessedAt < oldestAccess) {
473
+ oldestAccess = candidate.accessedAt;
474
+ oldestKey = candidateKey;
475
+ }
476
+ }
477
+ if (oldestKey)
478
+ mirroredFileTextCache.delete(oldestKey);
422
479
  }
480
+ mirroredFileTextCache.set(key, {
481
+ size: stat.size,
482
+ mtimeMs: stat.mtimeMs,
483
+ text,
484
+ accessedAt: Date.now(),
485
+ });
486
+ return text;
487
+ }
488
+ export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES) {
489
+ const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
490
+ const stat = fs.statSync(filePath);
491
+ const cachedKey = jsonlCacheKey(['recent', filePath, safeMaxBytes]);
492
+ const cachedLines = getCachedJsonlLines(recentJsonlLineCache, cachedKey, stat);
493
+ if (cachedLines)
494
+ return cachedLines;
495
+ const size = stat.size;
423
496
  const readLength = Math.min(size, safeMaxBytes);
424
497
  const start = size - readLength;
425
498
  const fd = fs.openSync(filePath, 'r');
426
499
  try {
427
- const buffer = Buffer.alloc(readLength);
500
+ const buffer = Buffer.allocUnsafe(readLength);
428
501
  const bytesRead = fs.readSync(fd, buffer, 0, readLength, start);
429
502
  let text = buffer.subarray(0, bytesRead).toString('utf-8');
430
503
  if (start > 0) {
@@ -433,7 +506,14 @@ export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTOR
433
506
  return [];
434
507
  text = text.slice(firstLineEnd + 1);
435
508
  }
436
- return text.split(/\r?\n/).filter(Boolean);
509
+ const lines = text.split(/\r?\n/).filter(Boolean);
510
+ return setCachedJsonlLines(recentJsonlLineCache, cachedKey, {
511
+ filePath,
512
+ size: stat.size,
513
+ mtimeMs: stat.mtimeMs,
514
+ maxBytes: safeMaxBytes,
515
+ lines,
516
+ });
437
517
  }
438
518
  finally {
439
519
  fs.closeSync(fd);
@@ -520,10 +600,15 @@ function codexAppServerEventChunk(method, params = {}) {
520
600
  },
521
601
  };
522
602
  }
523
- function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRRORED_THREAD_HISTORY_MAX_BYTES) {
603
+ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES) {
524
604
  const safeTargetMessages = Math.max(1, Math.min(Math.floor(targetMessages), 5_000));
525
605
  const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
526
- const size = fs.statSync(filePath).size;
606
+ const stat = fs.statSync(filePath);
607
+ const cachedKey = jsonlCacheKey(['codex-history', filePath, safeTargetMessages, safeMaxBytes]);
608
+ const cachedLines = getCachedJsonlLines(recentCodexHistoryLineCache, cachedKey, stat);
609
+ if (cachedLines)
610
+ return cachedLines;
611
+ const size = stat.size;
527
612
  const fd = fs.openSync(filePath, 'r');
528
613
  try {
529
614
  let position = size;
@@ -531,7 +616,10 @@ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRROR
531
616
  let leadingPartial = '';
532
617
  let messageCount = 0;
533
618
  let collected = [];
534
- while (position > 0 && scannedBytes < safeMaxBytes && messageCount < safeTargetMessages) {
619
+ while (position > 0 &&
620
+ scannedBytes < safeMaxBytes &&
621
+ messageCount < safeTargetMessages &&
622
+ collected.length < MIRRORED_THREAD_HISTORY_FAST_MAX_LINES) {
535
623
  const readLength = Math.min(MIRRORED_THREAD_HISTORY_CHUNK_BYTES, position, safeMaxBytes - scannedBytes);
536
624
  position -= readLength;
537
625
  scannedBytes += readLength;
@@ -548,17 +636,28 @@ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRROR
548
636
  messageCount += historyLines.filter(isCodexVisibleUserHistoryLine).length;
549
637
  collected = [...historyLines, ...collected];
550
638
  }
551
- return collected.filter(Boolean);
639
+ const lines = collected.filter(Boolean);
640
+ return setCachedJsonlLines(recentCodexHistoryLineCache, cachedKey, {
641
+ filePath,
642
+ size: stat.size,
643
+ mtimeMs: stat.mtimeMs,
644
+ maxBytes: safeMaxBytes,
645
+ targetMessages: safeTargetMessages,
646
+ lines,
647
+ });
552
648
  }
553
649
  finally {
554
650
  fs.closeSync(fd);
555
651
  }
556
652
  }
557
653
  function readThreadIndex(limit) {
558
- const indexPath = path.join(localAgentHome(), 'session_index.jsonl');
654
+ const agentHome = localAgentHome();
655
+ const sessionIndexPath = path.join(agentHome, 'session_index.jsonl');
656
+ const threadIndexPath = path.join(agentHome, 'thread_index.jsonl');
657
+ const indexPath = fs.existsSync(sessionIndexPath) ? sessionIndexPath : threadIndexPath;
559
658
  if (!fs.existsSync(indexPath))
560
659
  return [];
561
- const lines = fs.readFileSync(indexPath, 'utf-8').split(/\r?\n/).filter(Boolean);
660
+ const lines = readRecentJsonlLines(indexPath);
562
661
  const byId = new Map();
563
662
  for (const line of lines) {
564
663
  const parsed = parseJsonLine(line);
@@ -651,8 +750,8 @@ function readThreadStateEntries(limit) {
651
750
  return [];
652
751
  }
653
752
  }
654
- let mirroredSessionFileIndexCache = null;
655
- function walkSessionFilesUncached(dir, out = new Map()) {
753
+ let mirroredThreadFileIndexCache = new Map();
754
+ function walkThreadFilesUncached(dir, out = new Map()) {
656
755
  if (!fs.existsSync(dir))
657
756
  return out;
658
757
  let entries;
@@ -665,7 +764,7 @@ function walkSessionFilesUncached(dir, out = new Map()) {
665
764
  for (const entry of entries) {
666
765
  const fullPath = path.join(dir, entry.name);
667
766
  if (entry.isDirectory()) {
668
- walkSessionFilesUncached(fullPath, out);
767
+ walkThreadFilesUncached(fullPath, out);
669
768
  continue;
670
769
  }
671
770
  if (!entry.isFile() || !entry.name.endsWith('.jsonl'))
@@ -676,40 +775,54 @@ function walkSessionFilesUncached(dir, out = new Map()) {
676
775
  }
677
776
  return out;
678
777
  }
679
- function walkSessionFiles(dir) {
778
+ function walkThreadFiles(dir) {
680
779
  const now = Date.now();
681
- if (mirroredSessionFileIndexCache &&
682
- mirroredSessionFileIndexCache.root === dir &&
683
- now - mirroredSessionFileIndexCache.indexedAt <= MIRRORED_THREAD_SESSION_FILE_INDEX_TTL_MS) {
684
- return new Map(mirroredSessionFileIndexCache.files);
685
- }
686
- const files = walkSessionFilesUncached(dir);
687
- mirroredSessionFileIndexCache = {
688
- root: dir,
780
+ const cached = mirroredThreadFileIndexCache.get(dir);
781
+ if (cached && now - cached.indexedAt <= MIRRORED_THREAD_SESSION_FILE_INDEX_TTL_MS) {
782
+ return new Map(cached.files);
783
+ }
784
+ const files = walkThreadFilesUncached(dir);
785
+ mirroredThreadFileIndexCache.set(dir, {
689
786
  indexedAt: now,
690
787
  files: new Map(files),
691
- };
788
+ });
692
789
  return files;
693
790
  }
791
+ function codexThreadFiles() {
792
+ const agentHome = localAgentHome();
793
+ return new Map([
794
+ ...walkThreadFiles(path.join(agentHome, 'threads')),
795
+ ...walkThreadFiles(path.join(agentHome, 'sessions')),
796
+ ]);
797
+ }
694
798
  function readFileHead(filePath, maxBytes = 64 * 1024) {
799
+ const stat = fs.statSync(filePath);
800
+ const cacheKey = jsonlCacheKey(['head', filePath, maxBytes]);
801
+ const cached = getCachedFileText(cacheKey, stat);
802
+ if (cached !== null)
803
+ return cached;
695
804
  const fd = fs.openSync(filePath, 'r');
696
805
  try {
697
806
  const buffer = Buffer.alloc(maxBytes);
698
807
  const bytesRead = fs.readSync(fd, buffer, 0, maxBytes, 0);
699
- return buffer.subarray(0, bytesRead).toString('utf-8');
808
+ return setCachedFileText(cacheKey, stat, buffer.subarray(0, bytesRead).toString('utf-8'));
700
809
  }
701
810
  finally {
702
811
  fs.closeSync(fd);
703
812
  }
704
813
  }
705
814
  function readFileTail(filePath, maxBytes = 256 * 1024) {
815
+ const stat = fs.statSync(filePath);
816
+ const cacheKey = jsonlCacheKey(['tail', filePath, maxBytes]);
817
+ const cached = getCachedFileText(cacheKey, stat);
818
+ if (cached !== null)
819
+ return cached;
706
820
  const fd = fs.openSync(filePath, 'r');
707
821
  try {
708
- const stat = fs.fstatSync(fd);
709
822
  const length = Math.min(maxBytes, stat.size);
710
823
  const buffer = Buffer.alloc(length);
711
824
  fs.readSync(fd, buffer, 0, length, Math.max(0, stat.size - length));
712
- return buffer.toString('utf-8');
825
+ return setCachedFileText(cacheKey, stat, buffer.toString('utf-8'));
713
826
  }
714
827
  finally {
715
828
  fs.closeSync(fd);
@@ -752,7 +865,7 @@ function readThreadWorkspacePath(filePath) {
752
865
  if (!line.trim())
753
866
  continue;
754
867
  const parsed = parseJsonLine(line);
755
- if (parsed?.type !== 'session_meta')
868
+ if (parsed?.type !== 'thread_meta' && parsed?.type !== 'session_meta')
756
869
  continue;
757
870
  const payload = parsed.payload && typeof parsed.payload === 'object'
758
871
  ? parsed.payload
@@ -1009,7 +1122,7 @@ function readMirroredThreadStatus(threadId, filePath) {
1009
1122
  return 'running';
1010
1123
  }
1011
1124
  catch {
1012
- // Fall back to the Codex session file status below.
1125
+ // Fall back to the Codex thread file status below.
1013
1126
  }
1014
1127
  return readThreadStatus(filePath);
1015
1128
  }
@@ -1106,18 +1219,19 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
1106
1219
  const safeLimit = Math.max(1, Math.min(limit, 200));
1107
1220
  const stateEntries = readThreadStateEntries(safeLimit);
1108
1221
  const indexEntries = readThreadIndex(safeLimit);
1109
- const sessionFiles = walkSessionFiles(path.join(localAgentHome(), 'sessions'));
1222
+ const indexEntriesById = new Map(indexEntries.map((entry) => [entry.id, entry]));
1223
+ const threadFiles = codexThreadFiles();
1110
1224
  const projects = new Map();
1111
1225
  if (stateEntries.length > 0) {
1112
1226
  for (const entry of stateEntries) {
1113
- const sessionPath = sessionFiles.get(entry.id) || null;
1114
- if (!sessionPath)
1227
+ const threadPath = threadFiles.get(entry.id) || null;
1228
+ if (!threadPath)
1115
1229
  continue;
1116
- const workspacePath = entry.cwd || readThreadWorkspacePath(sessionPath);
1230
+ const workspacePath = entry.cwd || readThreadWorkspacePath(threadPath);
1117
1231
  if (!isMirrorableCodexWorkspace(workspacePath))
1118
1232
  continue;
1119
1233
  const projectId = workspacePath || 'no-workspace';
1120
- const indexEntry = readThreadIndexEntry(entry.id);
1234
+ const indexEntry = indexEntriesById.get(entry.id) || null;
1121
1235
  const title = indexEntry?.thread_name || entry.title;
1122
1236
  if (isAutomationRunThreadTitle(title))
1123
1237
  continue;
@@ -1131,8 +1245,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
1131
1245
  id: entry.id,
1132
1246
  title,
1133
1247
  updated_at: entry.updated_at,
1134
- status: readMirroredThreadStatus(entry.id, sessionPath),
1135
- session_path: sessionPath,
1248
+ status: readMirroredThreadStatus(entry.id, threadPath),
1249
+ thread_path: threadPath,
1136
1250
  });
1137
1251
  projects.set(projectId, project);
1138
1252
  }
@@ -1145,8 +1259,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
1145
1259
  };
1146
1260
  }
1147
1261
  for (const entry of indexEntries) {
1148
- const sessionPath = sessionFiles.get(entry.id) || null;
1149
- const workspacePath = readThreadWorkspacePath(sessionPath);
1262
+ const threadPath = threadFiles.get(entry.id) || null;
1263
+ const workspacePath = readThreadWorkspacePath(threadPath);
1150
1264
  if (!isMirrorableCodexWorkspace(workspacePath))
1151
1265
  continue;
1152
1266
  if (isAutomationRunThreadTitle(entry.thread_name))
@@ -1162,8 +1276,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
1162
1276
  id: entry.id,
1163
1277
  title: entry.thread_name,
1164
1278
  updated_at: entry.updated_at,
1165
- status: readMirroredThreadStatus(entry.id, sessionPath),
1166
- session_path: sessionPath,
1279
+ status: readMirroredThreadStatus(entry.id, threadPath),
1280
+ thread_path: threadPath,
1167
1281
  });
1168
1282
  projects.set(projectId, project);
1169
1283
  }
@@ -1298,10 +1412,10 @@ function isMirroredGoalContextMessage(text) {
1298
1412
  const trimmed = text.trim();
1299
1413
  return trimmed.startsWith('<goal_context>') && trimmed.endsWith('</goal_context>');
1300
1414
  }
1301
- function mirroredThreadWorkspacePath(sessionPath, stateEntry) {
1415
+ function mirroredThreadWorkspacePath(threadPath, stateEntry) {
1302
1416
  const stateCwd = typeof stateEntry?.cwd === 'string' ? stateEntry.cwd : null;
1303
- const sessionWorkspace = readThreadWorkspacePath(sessionPath);
1304
- for (const candidate of [stateCwd, sessionWorkspace]) {
1417
+ const threadWorkspace = readThreadWorkspacePath(threadPath);
1418
+ for (const candidate of [stateCwd, threadWorkspace]) {
1305
1419
  if (candidate && isMirrorableCodexWorkspace(candidate))
1306
1420
  return candidate;
1307
1421
  }
@@ -1356,16 +1470,16 @@ function collapseCompletedAssistantProgressMessages(input) {
1356
1470
  }
1357
1471
  export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY_LIMIT, options = {}) {
1358
1472
  const safeLimit = Math.max(1, Math.min(limit, 1000));
1359
- const sessionPath = walkSessionFiles(path.join(localAgentHome(), 'sessions')).get(threadId);
1360
- if (!sessionPath)
1473
+ const threadPath = codexThreadFiles().get(threadId);
1474
+ if (!threadPath)
1361
1475
  return null;
1362
1476
  const eventLogThreadId = options.eventLogThreadId || threadId;
1363
1477
  const stateEntry = readThreadStateEntry(threadId);
1364
- const workspacePath = mirroredThreadWorkspacePath(sessionPath, stateEntry);
1478
+ const workspacePath = mirroredThreadWorkspacePath(threadPath, stateEntry);
1365
1479
  const indexEntry = readThreadIndexEntry(threadId);
1366
1480
  const eventLogHead = readThreadEventLogHead(eventLogThreadId);
1367
1481
  const historyLineTarget = Math.min(250, Math.max(3, Math.ceil(safeLimit / 4)));
1368
- const lines = readRecentCodexHistoryLines(sessionPath, historyLineTarget);
1482
+ const lines = readRecentCodexHistoryLines(threadPath, historyLineTarget);
1369
1483
  const messages = [];
1370
1484
  const events = [];
1371
1485
  let messageIndex = 0;
@@ -1573,7 +1687,7 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
1573
1687
  const collapsedMessages = collapseCompletedAssistantProgressMessages(messages);
1574
1688
  const slicedMessages = collapsedMessages.slice(-safeLimit);
1575
1689
  const slicedEvents = events.slice(-safeLimit * 8);
1576
- const status = readMirroredThreadStatus(eventLogThreadId, sessionPath);
1690
+ const status = readMirroredThreadStatus(eventLogThreadId, threadPath);
1577
1691
  const latestGoalContext = recentGoalContext;
1578
1692
  const context = readThreadContext({
1579
1693
  threadId,
@@ -1581,7 +1695,7 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
1581
1695
  workspace: workspacePath,
1582
1696
  });
1583
1697
  return {
1584
- session: {
1698
+ thread: {
1585
1699
  task_id: threadId,
1586
1700
  agent_id: 'codex',
1587
1701
  channel: 'local-agent',
@@ -1611,19 +1725,19 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
1611
1725
  ...(latestGoalContext ? { goal_context: latestGoalContext } : {}),
1612
1726
  };
1613
1727
  }
1614
- function serializeAdoptedMirroredThread(session) {
1728
+ function serializeAdoptedMirroredThread(thread) {
1615
1729
  return {
1616
- id: session.session_id,
1617
- taskId: session.session_id,
1618
- agentId: session.agent_id,
1619
- status: session.status || 'idle',
1620
- title: session.title,
1621
- createdAt: session.created_at,
1622
- updatedAt: session.updated_at,
1730
+ id: thread.thread_id,
1731
+ taskId: thread.thread_id,
1732
+ agentId: thread.agent_id,
1733
+ status: thread.status || 'idle',
1734
+ title: thread.title,
1735
+ createdAt: thread.created_at,
1736
+ updatedAt: thread.updated_at,
1623
1737
  supen: {
1624
- channel: session.channel,
1625
- sourceRef: session.source_ref,
1626
- taskWorkspaceFolder: session.task_workspace_folder,
1738
+ channel: thread.channel,
1739
+ sourceRef: thread.source_ref,
1740
+ taskWorkspaceFolder: thread.task_workspace_folder,
1627
1741
  },
1628
1742
  };
1629
1743
  }
@@ -1631,23 +1745,23 @@ function adoptMirroredThread(threadId, agentId) {
1631
1745
  const history = readCodexThreadHistory(threadId, 1);
1632
1746
  if (!history)
1633
1747
  return null;
1634
- const historySession = history.session;
1635
- const session = ensureSession({
1748
+ const historyThread = history.thread;
1749
+ const thread = ensureThread({
1636
1750
  agent_id: agentId,
1637
- session_id: threadId,
1751
+ thread_id: threadId,
1638
1752
  channel: 'http',
1639
1753
  agent_name: agentId,
1640
1754
  source_ref: `local-agent:${threadId}`,
1641
- title: typeof historySession.title === 'string' ? historySession.title : undefined,
1642
- status: historySession.status === 'running' ? 'running' : 'idle',
1755
+ title: typeof historyThread.title === 'string' ? historyThread.title : undefined,
1756
+ status: historyThread.status === 'running' ? 'running' : 'idle',
1643
1757
  backend_driver_id: 'codex-app-server',
1644
- task_workspace_folder: typeof historySession.task_workspace_folder === 'string'
1645
- ? historySession.task_workspace_folder
1758
+ task_workspace_folder: typeof historyThread.task_workspace_folder === 'string'
1759
+ ? historyThread.task_workspace_folder
1646
1760
  : null,
1647
1761
  });
1648
- updateSessionSdkId(agentId, threadId, threadId);
1649
- updateSessionBackendDriverId(agentId, threadId, 'codex-app-server');
1650
- return session;
1762
+ updateThreadSdkSessionId(agentId, threadId, threadId);
1763
+ updateThreadBackendDriverId(agentId, threadId, 'codex-app-server');
1764
+ return thread;
1651
1765
  }
1652
1766
  function coerceSingleQueryParam(value) {
1653
1767
  if (Array.isArray(value))
@@ -1670,24 +1784,24 @@ function collectSpaceLogEntries(filters = {}) {
1670
1784
  const agents = getAllAgents()
1671
1785
  // Single-Computer runtime: include all agents in this mounted volume.
1672
1786
  .sort((a, b) => (a.name || a.agent_id).localeCompare(b.name || b.agent_id));
1673
- const sessions = agents.flatMap((agent) => getSessionsForAgent(agent.agent_id)
1674
- .map((session) => ({
1787
+ const threads = agents.flatMap((agent) => getThreadsForAgent(agent.agent_id)
1788
+ .map((thread) => ({
1675
1789
  agent_id: agent.agent_id,
1676
1790
  agent_name: agent.name || null,
1677
- session_id: session.session_id,
1678
- status: session.status,
1679
- updated_at: session.updated_at,
1680
- title: session.title || null,
1791
+ thread_id: thread.thread_id,
1792
+ status: thread.status,
1793
+ updated_at: thread.updated_at,
1794
+ title: thread.title || null,
1681
1795
  })));
1682
- const filteredSessions = sessions.filter((session) => {
1683
- if (filters.agentId && session.agent_id !== filters.agentId)
1796
+ const filteredThreads = threads.filter((thread) => {
1797
+ if (filters.agentId && thread.agent_id !== filters.agentId)
1684
1798
  return false;
1685
- if (filters.sessionId && session.session_id !== filters.sessionId)
1799
+ if (filters.threadId && thread.thread_id !== filters.threadId)
1686
1800
  return false;
1687
1801
  return true;
1688
1802
  });
1689
- const entries = filteredSessions
1690
- .flatMap((session) => getSessionUiEvents(session.agent_id, session.session_id, limit).map((event) => {
1803
+ const entries = filteredThreads
1804
+ .flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, limit).map((event) => {
1691
1805
  const logSummary = event.chunk && typeof event.chunk === 'object'
1692
1806
  ? summarizeUiChunk(event.chunk)
1693
1807
  : null;
@@ -1695,10 +1809,10 @@ function collectSpaceLogEntries(filters = {}) {
1695
1809
  ? {
1696
1810
  id: event.id,
1697
1811
  timestamp: event.timestamp,
1698
- agent_id: session.agent_id,
1699
- agent_name: session.agent_name,
1700
- session_id: session.session_id,
1701
- session_title: session.title,
1812
+ agent_id: thread.agent_id,
1813
+ agent_name: thread.agent_name,
1814
+ thread_id: thread.thread_id,
1815
+ thread_title: thread.title,
1702
1816
  task_id: event.task_id || null,
1703
1817
  category: logSummary.category,
1704
1818
  summary: logSummary.summary,
@@ -1713,11 +1827,11 @@ function collectSpaceLogEntries(filters = {}) {
1713
1827
  generated_at: new Date().toISOString(),
1714
1828
  timezone: resolveHostTimeZone(),
1715
1829
  agents,
1716
- sessions,
1830
+ threads,
1717
1831
  entries,
1718
1832
  filters: {
1719
1833
  agent_id: filters.agentId || null,
1720
- session_id: filters.sessionId || null,
1834
+ thread_id: filters.threadId || null,
1721
1835
  },
1722
1836
  };
1723
1837
  }
@@ -2581,24 +2695,24 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
2581
2695
  }
2582
2696
  if (pathname === '/api/computers/{computer_id}/logs' && method === 'GET') {
2583
2697
  const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
2584
- const sessionId = coerceSingleQueryParam(url.searchParams.get('session_id'));
2698
+ const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
2585
2699
  const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
2586
2700
  const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
2587
2701
  writeJson(res, 200, collectSpaceLogEntries({
2588
2702
  agentId: agentId?.trim() || null,
2589
- sessionId: sessionId?.trim() || null,
2703
+ threadId: threadId?.trim() || null,
2590
2704
  limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
2591
2705
  }));
2592
2706
  return true;
2593
2707
  }
2594
2708
  if (pathname === '/api/computers/{computer_id}/logs/stream' && method === 'GET') {
2595
2709
  const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
2596
- const sessionId = coerceSingleQueryParam(url.searchParams.get('session_id'));
2710
+ const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
2597
2711
  const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
2598
2712
  const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
2599
2713
  const filters = {
2600
2714
  agentId: agentId?.trim() || null,
2601
- sessionId: sessionId?.trim() || null,
2715
+ threadId: threadId?.trim() || null,
2602
2716
  limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
2603
2717
  };
2604
2718
  res.writeHead(200, {
@@ -2881,7 +2995,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
2881
2995
  });
2882
2996
  return true;
2883
2997
  }
2884
- if (pathname === '/api/computers/{computer_id}/agents/codex/session' && method === 'DELETE') {
2998
+ if (pathname === '/api/computers/{computer_id}/agents/codex/thread' && method === 'DELETE') {
2885
2999
  const cmd = spawnSync('codex', ['logout'], {
2886
3000
  encoding: 'utf8',
2887
3001
  timeout: 15_000,
@@ -2976,10 +3090,10 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
2976
3090
  numericQueryParam(coerceSingleQueryParam(url.searchParams.get('after')));
2977
3091
  const replayAfter = afterSequence ?? readThreadEventLogHead(threadId);
2978
3092
  addThreadStreamClient(threadId, res);
2979
- const sessionPath = walkSessionFiles(path.join(localAgentHome(), 'sessions')).get(threadId);
2980
- let jsonlOffset = sessionPath && fs.existsSync(sessionPath) ? fs.statSync(sessionPath).size : 0;
3093
+ const threadPath = codexThreadFiles().get(threadId);
3094
+ let jsonlOffset = threadPath && fs.existsSync(threadPath) ? fs.statSync(threadPath).size : 0;
2981
3095
  let jsonlEventIndex = 0;
2982
- const workspacePath = readThreadWorkspacePath(sessionPath) || process.cwd();
3096
+ const workspacePath = readThreadWorkspacePath(threadPath) || process.cwd();
2983
3097
  const withThreadContext = (chunk) => attachThreadContextToChunk(chunk, readThreadContext({ threadId, workspace: workspacePath }));
2984
3098
  const stopCodexObserver = process.env.NODE_ENV === 'test'
2985
3099
  ? () => { }
@@ -2993,9 +3107,9 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
2993
3107
  res.flush?.();
2994
3108
  }, SPACE_LOG_STREAM_PING_MS);
2995
3109
  pingTimer.unref?.();
2996
- const jsonlPollTimer = sessionPath
3110
+ const jsonlPollTimer = threadPath
2997
3111
  ? setInterval(() => {
2998
- const read = readAppendedJsonlLines(sessionPath, jsonlOffset);
3112
+ const read = readAppendedJsonlLines(threadPath, jsonlOffset);
2999
3113
  jsonlOffset = read.offset;
3000
3114
  for (const line of read.lines) {
3001
3115
  if (!isCodexHistoryTranscriptLine(line))
@@ -3062,12 +3176,12 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
3062
3176
  : '';
3063
3177
  const fallbackAgentId = getAllAgents()[0]?.agent_id || 'codex';
3064
3178
  const agentId = requestedAgentId || fallbackAgentId;
3065
- const session = threadId ? adoptMirroredThread(threadId, agentId) : null;
3066
- if (!session) {
3179
+ const thread = threadId ? adoptMirroredThread(threadId, agentId) : null;
3180
+ if (!thread) {
3067
3181
  writeProtocolError(res, 404, 'not_found', 'mirrored_thread_not_found', 'Mirrored task history was not found.');
3068
3182
  return true;
3069
3183
  }
3070
- writeJson(res, 200, { thread: serializeAdoptedMirroredThread(session) });
3184
+ writeJson(res, 200, { thread: serializeAdoptedMirroredThread(thread) });
3071
3185
  return true;
3072
3186
  }
3073
3187
  if (codexProjectsOpenRoute(pathname) && method === 'POST') {