@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
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Replaces SQLite (db.ts) with plain files:
5
5
  * - agents/{id}/agent.json → AgentRecord
6
- * - sessions/{sid}/ → session.json + messages.jsonl
6
+ * - threads/{sid}/ → thread.json + messages.jsonl
7
7
  * - router-state.json → key-value pairs
8
8
  * - registered groups → agent.json `sources` field (future)
9
9
  *
@@ -293,73 +293,73 @@ function agentJsonPath(agentId) {
293
293
  return resolved.configPath;
294
294
  return agentHomePaths(agentId).config;
295
295
  }
296
- function sessionsRootDir() {
297
- return path.join(SUPEN_HOME, 'sessions');
296
+ function threadsRootDir() {
297
+ return path.join(SUPEN_HOME, 'threads');
298
298
  }
299
- function archivedSessionsRootDir() {
300
- return path.join(SUPEN_HOME, 'archived_sessions');
299
+ function archivedThreadsRootDir() {
300
+ return path.join(SUPEN_HOME, 'archived_threads');
301
301
  }
302
- function rootSessionDir(sessionId) {
303
- const safeSessionId = assertSafePathSegment(sessionId, 'session_id');
304
- return resolveWithin(sessionsRootDir(), safeSessionId);
302
+ function rootThreadDir(threadId) {
303
+ const safeThreadId = assertSafePathSegment(threadId, 'thread_id');
304
+ return resolveWithin(threadsRootDir(), safeThreadId);
305
305
  }
306
- function rootArchivedSessionDir(sessionId) {
307
- const safeSessionId = assertSafePathSegment(sessionId, 'session_id');
308
- return resolveWithin(archivedSessionsRootDir(), safeSessionId);
306
+ function rootArchivedThreadDir(threadId) {
307
+ const safeThreadId = assertSafePathSegment(threadId, 'thread_id');
308
+ return resolveWithin(archivedThreadsRootDir(), safeThreadId);
309
309
  }
310
- function sessionDir(agentId, sessionId) {
310
+ function threadDir(agentId, threadId) {
311
311
  assertSafePathSegment(agentId, 'agent_id');
312
- return rootSessionDir(sessionId);
312
+ return rootThreadDir(threadId);
313
313
  }
314
- function resolveSessionDir(agentId, sessionId) {
314
+ function resolveThreadDir(agentId, threadId) {
315
315
  assertSafePathSegment(agentId, 'agent_id');
316
- const livePath = rootSessionDir(sessionId);
316
+ const livePath = rootThreadDir(threadId);
317
317
  if (fs.existsSync(livePath)) {
318
318
  return livePath;
319
319
  }
320
- const archivedPath = rootArchivedSessionDir(sessionId);
320
+ const archivedPath = rootArchivedThreadDir(threadId);
321
321
  if (fs.existsSync(archivedPath)) {
322
322
  return archivedPath;
323
323
  }
324
- return sessionDir(agentId, sessionId);
324
+ return threadDir(agentId, threadId);
325
325
  }
326
- function sessionJsonPath(agentId, sessionId) {
327
- return path.join(resolveSessionDir(agentId, sessionId), 'session.json');
326
+ function threadJsonPath(agentId, threadId) {
327
+ return path.join(resolveThreadDir(agentId, threadId), 'thread.json');
328
328
  }
329
- function messagesPath(agentId, sessionId) {
330
- return path.join(resolveSessionDir(agentId, sessionId), 'messages.jsonl');
329
+ function messagesPath(agentId, threadId) {
330
+ return path.join(resolveThreadDir(agentId, threadId), 'messages.jsonl');
331
331
  }
332
- function taskMetadataDir(sessionId, taskId) {
332
+ function taskMetadataDir(threadId, taskId) {
333
333
  if (!taskId || !taskId.trim())
334
334
  return null;
335
- return path.join(resolveTaskArtifactPaths(sessionId, taskId).taskRoot, '.supen');
335
+ return path.join(resolveTaskArtifactPaths(threadId, taskId).taskRoot, '.supen');
336
336
  }
337
- function taskUiEventsPath(sessionId, taskId) {
338
- const dir = taskMetadataDir(sessionId, taskId);
337
+ function taskUiEventsPath(threadId, taskId) {
338
+ const dir = taskMetadataDir(threadId, taskId);
339
339
  return dir ? path.join(dir, 'events.jsonl') : null;
340
340
  }
341
- function taskMetadataPath(sessionId, taskId) {
342
- const dir = taskMetadataDir(sessionId, taskId);
341
+ function taskMetadataPath(threadId, taskId) {
342
+ const dir = taskMetadataDir(threadId, taskId);
343
343
  return dir ? path.join(dir, 'task.json') : null;
344
344
  }
345
- function writeTaskMetadata(agentId, sessionId, taskId) {
346
- const metadataPath = taskMetadataPath(sessionId, taskId);
345
+ function writeTaskMetadata(agentId, threadId, taskId) {
346
+ const metadataPath = taskMetadataPath(threadId, taskId);
347
347
  if (!metadataPath || fs.existsSync(metadataPath))
348
348
  return;
349
349
  writeJson(metadataPath, {
350
350
  agent_id: agentId,
351
- session_id: sessionId,
351
+ thread_id: threadId,
352
352
  task_id: taskId,
353
353
  });
354
354
  }
355
- function listTaskUiEventsForSession(agentId, sessionId, limit = TASK_UI_EVENT_HISTORY_LIMIT) {
355
+ function listTaskUiEventsForThread(agentId, threadId, limit = TASK_UI_EVENT_HISTORY_LIMIT) {
356
356
  const tasksRoot = spacePaths('local').tasks;
357
357
  const events = [];
358
358
  const boundedLimit = Math.max(1, limit);
359
359
  for (const taskFolder of listDirs(tasksRoot)) {
360
360
  const metadataDir = path.join(tasksRoot, taskFolder, '.supen');
361
361
  const metadata = readJson(path.join(metadataDir, 'task.json'));
362
- if (metadata?.agent_id !== agentId || metadata.session_id !== sessionId)
362
+ if (metadata?.agent_id !== agentId || metadata.thread_id !== threadId)
363
363
  continue;
364
364
  events.push(...readJsonlTail(path.join(metadataDir, 'events.jsonl'), boundedLimit));
365
365
  }
@@ -397,10 +397,10 @@ function automationEventJsonPath(automationId, eventId) {
397
397
  function automationTaskId(automationId) {
398
398
  return `task-automation-${automationId}`;
399
399
  }
400
- function automationChatJid(agentId, sessionId) {
400
+ function automationChatJid(agentId, threadId) {
401
401
  return agentId === AUTOMATION_TASK_AGENT_ID
402
- ? `task:${sessionId}`
403
- : `web:${agentId}:${sessionId}`;
402
+ ? `task:${threadId}`
403
+ : `web:${agentId}:${threadId}`;
404
404
  }
405
405
  const getRouterStatePath = () => path.join(SUPEN_HOME, 'router-state.json');
406
406
  const getRegisteredGroupsPath = () => path.join(SUPEN_HOME, 'registered-groups.json');
@@ -409,8 +409,8 @@ const getRegisteredGroupsPath = () => path.join(SUPEN_HOME, 'registered-groups.j
409
409
  // ═══════════════════════════════════════════════════════════════
410
410
  export function initStore() {
411
411
  fs.mkdirSync(AGENTS_DIR, { recursive: true });
412
- fs.mkdirSync(sessionsRootDir(), { recursive: true });
413
- fs.mkdirSync(archivedSessionsRootDir(), { recursive: true });
412
+ fs.mkdirSync(threadsRootDir(), { recursive: true });
413
+ fs.mkdirSync(archivedThreadsRootDir(), { recursive: true });
414
414
  fs.mkdirSync(spacesDir(), { recursive: true });
415
415
  logger.info({ dir: AGENTS_DIR }, 'Store initialized (filesystem)');
416
416
  }
@@ -515,9 +515,9 @@ export function getAllAgents() {
515
515
  return agents.sort((a, b) => (b.updated_at || '').localeCompare(a.updated_at || ''));
516
516
  }
517
517
  // ═══════════════════════════════════════════════════════════════
518
- // Sessions
518
+ // Threads
519
519
  // ═══════════════════════════════════════════════════════════════
520
- export function ensureSession(input) {
520
+ export function ensureThread(input) {
521
521
  const existingAgent = findAgentRecord(input.agent_id);
522
522
  const resolvedSpaceId = input.space_id || existingAgent?.space_id || defaultSpaceId();
523
523
  ensureAgent({
@@ -525,7 +525,7 @@ export function ensureSession(input) {
525
525
  name: input.agent_name,
526
526
  space_id: resolvedSpaceId,
527
527
  });
528
- const jsonPath = sessionJsonPath(input.agent_id, input.session_id);
528
+ const jsonPath = threadJsonPath(input.agent_id, input.thread_id);
529
529
  const existing = readJson(jsonPath);
530
530
  const now = new Date().toISOString();
531
531
  const normalizedCreatedAt = typeof input.created_at === 'string' && input.created_at.trim()
@@ -541,7 +541,7 @@ export function ensureSession(input) {
541
541
  ? resolvedSpaceId
542
542
  : existingSpaceId);
543
543
  const nextChannel = input.channel;
544
- const nextChannelSession = input.channel_session ?? existing.channel_session;
544
+ const nextChannelThread = input.channel_thread ?? existing.channel_thread;
545
545
  const nextBackendDriverId = input.backend_driver_id || existing.backend_driver_id;
546
546
  const nextSourceRef = input.source_ref || existing.source_ref;
547
547
  const nextKnowledgeId = input.knowledge_id || existing.knowledge_id;
@@ -553,7 +553,7 @@ export function ensureSession(input) {
553
553
  const nextStatus = input.status || existing.status;
554
554
  const changed = nextSpaceId !== existing.space_id ||
555
555
  nextChannel !== existing.channel ||
556
- nextChannelSession !== existing.channel_session ||
556
+ nextChannelThread !== existing.channel_thread ||
557
557
  nextBackendDriverId !== existing.backend_driver_id ||
558
558
  nextSourceRef !== existing.source_ref ||
559
559
  nextKnowledgeId !== existing.knowledge_id ||
@@ -567,7 +567,7 @@ export function ensureSession(input) {
567
567
  ...existing,
568
568
  space_id: nextSpaceId,
569
569
  channel: nextChannel,
570
- channel_session: nextChannelSession,
570
+ channel_thread: nextChannelThread,
571
571
  backend_driver_id: nextBackendDriverId,
572
572
  source_ref: nextSourceRef,
573
573
  knowledge_id: nextKnowledgeId,
@@ -585,11 +585,11 @@ export function ensureSession(input) {
585
585
  return updated;
586
586
  }
587
587
  const record = {
588
- session_id: input.session_id,
588
+ thread_id: input.thread_id,
589
589
  agent_id: input.agent_id,
590
590
  space_id: resolvedSpaceId,
591
591
  channel: input.channel,
592
- channel_session: input.channel_session,
592
+ channel_thread: input.channel_thread,
593
593
  backend_driver_id: input.backend_driver_id,
594
594
  source_ref: input.source_ref,
595
595
  knowledge_id: input.knowledge_id,
@@ -605,70 +605,70 @@ export function ensureSession(input) {
605
605
  writeJson(jsonPath, record);
606
606
  return record;
607
607
  }
608
- export function getSession(sessionId) {
609
- return (readJson(path.join(rootSessionDir(sessionId), 'session.json')) ||
610
- readJson(path.join(rootArchivedSessionDir(sessionId), 'session.json')));
608
+ export function getThread(threadId) {
609
+ return (readJson(path.join(rootThreadDir(threadId), 'thread.json')) ||
610
+ readJson(path.join(rootArchivedThreadDir(threadId), 'thread.json')));
611
611
  }
612
- export function getSessionForAgent(agentId, sessionId) {
613
- const session = readJson(sessionJsonPath(agentId, sessionId));
614
- return session?.agent_id === agentId ? session : undefined;
612
+ export function getThreadForAgent(agentId, threadId) {
613
+ const thread = readJson(threadJsonPath(agentId, threadId));
614
+ return thread?.agent_id === agentId ? thread : undefined;
615
615
  }
616
616
  function ensureAutomationTasksForAgent(agentId) {
617
617
  for (const automation of listAutomations(agentId)) {
618
618
  ensureAutomationTask(automation);
619
619
  }
620
620
  }
621
- export function getSessionsForAgent(agentId) {
621
+ export function getThreadsForAgent(agentId) {
622
622
  ensureAutomationTasksForAgent(agentId);
623
- const sessions = [];
624
- const sessionsBase = sessionsRootDir();
625
- if (fs.existsSync(sessionsBase)) {
626
- for (const sid of listDirs(sessionsBase)) {
627
- const session = readJson(path.join(sessionsBase, sid, 'session.json'));
628
- if (session?.agent_id === agentId)
629
- sessions.push(session);
623
+ const threads = [];
624
+ const threadsBase = threadsRootDir();
625
+ if (fs.existsSync(threadsBase)) {
626
+ for (const sid of listDirs(threadsBase)) {
627
+ const thread = readJson(path.join(threadsBase, sid, 'thread.json'));
628
+ if (thread?.agent_id === agentId)
629
+ threads.push(thread);
630
630
  }
631
631
  }
632
- return sessions.sort((a, b) => (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || ''));
632
+ return threads.sort((a, b) => (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || ''));
633
633
  }
634
- function touchSessionActivity(agentId, sessionId, timestamp = new Date().toISOString()) {
635
- const jsonPath = sessionJsonPath(agentId, sessionId);
636
- const session = readJson(jsonPath);
637
- if (!session)
634
+ function touchThreadActivity(agentId, threadId, timestamp = new Date().toISOString()) {
635
+ const jsonPath = threadJsonPath(agentId, threadId);
636
+ const thread = readJson(jsonPath);
637
+ if (!thread)
638
638
  return;
639
639
  const nextUpdatedAt = Number.isNaN(new Date(timestamp).getTime())
640
640
  ? new Date().toISOString()
641
641
  : timestamp;
642
- if (session.updated_at && session.updated_at > nextUpdatedAt)
642
+ if (thread.updated_at && thread.updated_at > nextUpdatedAt)
643
643
  return;
644
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
645
- session.updated_at = nextUpdatedAt;
646
- writeJson(jsonPath, session);
647
- }
648
- export function getArchivedSessionsForAgent(agentId) {
649
- const sessionsBase = archivedSessionsRootDir();
650
- const sessions = [];
651
- if (fs.existsSync(sessionsBase)) {
652
- for (const sid of listDirs(sessionsBase)) {
653
- const jsonPath = path.join(sessionsBase, sid, 'session.json');
654
- const session = readJson(jsonPath);
655
- if (session?.agent_id === agentId) {
656
- sessions.push({ ...session, status: 'archived' });
644
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
645
+ thread.updated_at = nextUpdatedAt;
646
+ writeJson(jsonPath, thread);
647
+ }
648
+ export function getArchivedThreadsForAgent(agentId) {
649
+ const threadsBase = archivedThreadsRootDir();
650
+ const threads = [];
651
+ if (fs.existsSync(threadsBase)) {
652
+ for (const sid of listDirs(threadsBase)) {
653
+ const jsonPath = path.join(threadsBase, sid, 'thread.json');
654
+ const thread = readJson(jsonPath);
655
+ if (thread?.agent_id === agentId) {
656
+ threads.push({ ...thread, status: 'archived' });
657
657
  }
658
658
  }
659
659
  }
660
- return sessions.sort((a, b) => (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || ''));
660
+ return threads.sort((a, b) => (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || ''));
661
661
  }
662
- export function getAllSessions() {
663
- const sessions = [];
664
- for (const sid of listDirs(sessionsRootDir())) {
665
- const session = readJson(path.join(sessionsRootDir(), sid, 'session.json'));
666
- if (session)
667
- sessions.push(session);
662
+ export function getAllThreads() {
663
+ const threads = [];
664
+ for (const sid of listDirs(threadsRootDir())) {
665
+ const thread = readJson(path.join(threadsRootDir(), sid, 'thread.json'));
666
+ if (thread)
667
+ threads.push(thread);
668
668
  }
669
- return sessions.sort((a, b) => {
669
+ return threads.sort((a, b) => {
670
670
  const cmp = a.agent_id.localeCompare(b.agent_id);
671
- return cmp !== 0 ? cmp : a.session_id.localeCompare(b.session_id);
671
+ return cmp !== 0 ? cmp : a.thread_id.localeCompare(b.thread_id);
672
672
  });
673
673
  }
674
674
  export function ensureAutomationTask(automation) {
@@ -676,27 +676,27 @@ export function ensureAutomationTask(automation) {
676
676
  const agentId = automation.kind === 'event'
677
677
  ? AUTOMATION_TASK_AGENT_ID
678
678
  : automation.agent_id;
679
- const session = ensureSession({
679
+ const thread = ensureThread({
680
680
  agent_id: agentId,
681
- session_id: taskId,
681
+ thread_id: taskId,
682
682
  channel: 'automation',
683
683
  source_ref: `automation:${automation.id}`,
684
684
  title: automation.name,
685
685
  task_workspace_folder: automation.cwds?.[0] ?? null,
686
686
  });
687
- if (session.agent_id !== agentId) {
687
+ if (thread.agent_id !== agentId) {
688
688
  const updated = {
689
- ...session,
689
+ ...thread,
690
690
  agent_id: agentId,
691
691
  channel: 'automation',
692
692
  source_ref: `automation:${automation.id}`,
693
693
  updated_at: new Date().toISOString(),
694
694
  };
695
695
  ensureAgent({ agent_id: agentId, name: agentId === AUTOMATION_TASK_AGENT_ID ? 'Automation Task' : undefined });
696
- writeJson(sessionJsonPath(agentId, taskId), updated);
696
+ writeJson(threadJsonPath(agentId, taskId), updated);
697
697
  return updated;
698
698
  }
699
- return session;
699
+ return thread;
700
700
  }
701
701
  export function automationExecutionIdentity(automationId) {
702
702
  return `supen-automation:${automationId}`;
@@ -809,7 +809,7 @@ export function buildAutomationTriggerMessage(input) {
809
809
  timestamp: input.nowIso,
810
810
  is_from_me: false,
811
811
  agent_id: input.automation.agent_id,
812
- session_id: taskId,
812
+ thread_id: taskId,
813
813
  task_id: taskId,
814
814
  automation_id: input.automation.id,
815
815
  automation_run_id: runId,
@@ -832,57 +832,57 @@ export function buildAutomationTriggerMessage(input) {
832
832
  },
833
833
  };
834
834
  }
835
- export function updateSessionStatus(agentId, sessionId, status) {
836
- const jsonPath = sessionJsonPath(agentId, sessionId);
837
- const session = readJson(jsonPath);
838
- if (session) {
839
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
840
- session.status = status;
841
- session.updated_at = new Date().toISOString();
842
- writeJson(jsonPath, session);
843
- }
844
- }
845
- export function updateSessionSdkId(agentId, sessionId, sdkSessionId) {
846
- const jsonPath = sessionJsonPath(agentId, sessionId);
847
- const session = readJson(jsonPath);
848
- if (session) {
849
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
850
- session.sdk_session_id = sdkSessionId;
851
- session.updated_at = new Date().toISOString();
852
- writeJson(jsonPath, session);
853
- }
854
- }
855
- export function clearSessionSdkId(agentId, sessionId) {
856
- const jsonPath = sessionJsonPath(agentId, sessionId);
857
- const session = readJson(jsonPath);
858
- if (session) {
859
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
860
- delete session.sdk_session_id;
861
- session.updated_at = new Date().toISOString();
862
- writeJson(jsonPath, session);
863
- }
864
- }
865
- export function updateSessionBackendDriverId(agentId, sessionId, backendDriverId) {
866
- const jsonPath = sessionJsonPath(agentId, sessionId);
867
- const session = readJson(jsonPath);
868
- if (session) {
869
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
870
- session.backend_driver_id = backendDriverId;
871
- session.updated_at = new Date().toISOString();
872
- writeJson(jsonPath, session);
873
- }
874
- }
875
- export function updateSessionUsage(agentId, sessionId, tokensIn, tokensOut) {
835
+ export function updateThreadStatus(agentId, threadId, status) {
836
+ const jsonPath = threadJsonPath(agentId, threadId);
837
+ const thread = readJson(jsonPath);
838
+ if (thread) {
839
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
840
+ thread.status = status;
841
+ thread.updated_at = new Date().toISOString();
842
+ writeJson(jsonPath, thread);
843
+ }
844
+ }
845
+ export function updateThreadSdkSessionId(agentId, threadId, sdkSessionId) {
846
+ const jsonPath = threadJsonPath(agentId, threadId);
847
+ const thread = readJson(jsonPath);
848
+ if (thread) {
849
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
850
+ thread.sdk_session_id = sdkSessionId;
851
+ thread.updated_at = new Date().toISOString();
852
+ writeJson(jsonPath, thread);
853
+ }
854
+ }
855
+ export function clearThreadSdkSessionId(agentId, threadId) {
856
+ const jsonPath = threadJsonPath(agentId, threadId);
857
+ const thread = readJson(jsonPath);
858
+ if (thread) {
859
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
860
+ delete thread.sdk_session_id;
861
+ thread.updated_at = new Date().toISOString();
862
+ writeJson(jsonPath, thread);
863
+ }
864
+ }
865
+ export function updateThreadBackendDriverId(agentId, threadId, backendDriverId) {
866
+ const jsonPath = threadJsonPath(agentId, threadId);
867
+ const thread = readJson(jsonPath);
868
+ if (thread) {
869
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
870
+ thread.backend_driver_id = backendDriverId;
871
+ thread.updated_at = new Date().toISOString();
872
+ writeJson(jsonPath, thread);
873
+ }
874
+ }
875
+ export function updateThreadUsage(agentId, threadId, tokensIn, tokensOut) {
876
876
  const now = new Date().toISOString();
877
- // 1. Update Session Usage
878
- const sessPath = sessionJsonPath(agentId, sessionId);
879
- const session = readJson(sessPath);
880
- if (session) {
881
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
882
- session.tokens_in = (session.tokens_in || 0) + tokensIn;
883
- session.tokens_out = (session.tokens_out || 0) + tokensOut;
884
- session.updated_at = now;
885
- writeJson(sessPath, session);
877
+ // 1. Update Thread Usage
878
+ const sessPath = threadJsonPath(agentId, threadId);
879
+ const thread = readJson(sessPath);
880
+ if (thread) {
881
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
882
+ thread.tokens_in = (thread.tokens_in || 0) + tokensIn;
883
+ thread.tokens_out = (thread.tokens_out || 0) + tokensOut;
884
+ thread.updated_at = now;
885
+ writeJson(sessPath, thread);
886
886
  }
887
887
  // 2. Update Agent Usage (Aggregate)
888
888
  const aPath = agentJsonPath(agentId);
@@ -893,11 +893,11 @@ export function updateSessionUsage(agentId, sessionId, tokensIn, tokensOut) {
893
893
  agent.updated_at = now;
894
894
  writeJson(aPath, agent);
895
895
  }
896
- appendGlobalUsage(agentId, sessionId, tokensIn, tokensOut);
896
+ appendGlobalUsage(agentId, threadId, tokensIn, tokensOut);
897
897
  }
898
- export function appendGlobalUsage(agentId, sessionId, tokensIn, tokensOut) {
898
+ export function appendGlobalUsage(agentId, threadId, tokensIn, tokensOut) {
899
899
  const now = new Date().toISOString();
900
- // This ensures deletion of agents/sessions does not impact the historical total.
900
+ // This ensures deletion of agents/threads does not impact the historical total.
901
901
  try {
902
902
  const today = now.split('T')[0];
903
903
  const models = getAgentModelConfig(agentId, true);
@@ -935,19 +935,19 @@ export function appendGlobalUsage(agentId, sessionId, tokensIn, tokensOut) {
935
935
  if (!day.models[modelId]) {
936
936
  day.models[modelId] = {
937
937
  total: { tokens_in: 0, tokens_out: 0, estimated_cost_usd: 0 },
938
- sessions: {},
938
+ threads: {},
939
939
  };
940
940
  }
941
941
  const model = day.models[modelId];
942
- if (!model.sessions[sessionId]) {
943
- model.sessions[sessionId] = {
942
+ if (!model.threads[threadId]) {
943
+ model.threads[threadId] = {
944
944
  agent_id: agentId,
945
945
  tokens_in: 0,
946
946
  tokens_out: 0,
947
947
  estimated_cost_usd: 0,
948
948
  };
949
949
  }
950
- const sessionEntry = model.sessions[sessionId];
950
+ const threadEntry = model.threads[threadId];
951
951
  const applyIncrement = (target) => {
952
952
  target.tokens_in += tokensIn;
953
953
  target.tokens_out += tokensOut;
@@ -958,7 +958,7 @@ export function appendGlobalUsage(agentId, sessionId, tokensIn, tokensOut) {
958
958
  ledger.total.updated_at = now;
959
959
  applyIncrement(day.total);
960
960
  applyIncrement(model.total);
961
- applyIncrement(sessionEntry);
961
+ applyIncrement(threadEntry);
962
962
  writeJson(getGlobalUsagePath(), ledger);
963
963
  }
964
964
  catch (e) {
@@ -989,7 +989,7 @@ export function getUsageStats(record) {
989
989
  estimated_cost_usd: Number(estimated_cost_usd.toFixed(6)),
990
990
  };
991
991
  }
992
- /** Get global usage across all agents and sessions. */
992
+ /** Get global usage across all agents and threads. */
993
993
  export function getGlobalUsage() {
994
994
  const ledger = readJson(getGlobalUsagePath());
995
995
  if (ledger && ledger.total) {
@@ -1023,25 +1023,25 @@ export function getDailyUsage() {
1023
1023
  return ledger?.daily || {};
1024
1024
  }
1025
1025
  /**
1026
- * Reset sessions stuck as "running" — called at startup to recover from crashes.
1027
- * A "running" session cannot survive a process restart, so these are stale.
1026
+ * Reset threads stuck as "running" — called at startup to recover from crashes.
1027
+ * A "running" thread cannot survive a process restart, so these are stale.
1028
1028
  */
1029
- export function cleanupStaleSessions() {
1029
+ export function cleanupStaleThreads() {
1030
1030
  let cleaned = 0;
1031
1031
  for (const agentId of listDirs(AGENTS_DIR)) {
1032
- for (const session of getSessionsForAgent(agentId)) {
1033
- if (session.status === 'running') {
1034
- finalizeInterruptedSessionUiStream(agentId, session.session_id);
1035
- updateSessionStatus(agentId, session.session_id, 'idle');
1032
+ for (const thread of getThreadsForAgent(agentId)) {
1033
+ if (thread.status === 'running') {
1034
+ finalizeInterruptedThreadUiStream(agentId, thread.thread_id);
1035
+ updateThreadStatus(agentId, thread.thread_id, 'idle');
1036
1036
  cleaned++;
1037
- logger.info({ agentId, sessionId: session.session_id }, 'Reset stale running session to idle');
1037
+ logger.info({ agentId, threadId: thread.thread_id }, 'Reset stale running thread to idle');
1038
1038
  }
1039
1039
  }
1040
1040
  }
1041
1041
  return cleaned;
1042
1042
  }
1043
- function finalizeInterruptedSessionUiStream(agentId, sessionId) {
1044
- const events = getSessionUiEvents(agentId, sessionId, SESSION_UI_EVENT_RECOVERY_LIMIT);
1043
+ function finalizeInterruptedThreadUiStream(agentId, threadId) {
1044
+ const events = getThreadUiEvents(agentId, threadId, SESSION_UI_EVENT_RECOVERY_LIMIT);
1045
1045
  if (events.length === 0)
1046
1046
  return;
1047
1047
  let openTaskId;
@@ -1111,7 +1111,7 @@ function finalizeInterruptedSessionUiStream(agentId, sessionId) {
1111
1111
  { type: 'finish', finishReason: 'error' },
1112
1112
  ];
1113
1113
  chunks.forEach((chunk, index) => {
1114
- storeSessionUiEvent(agentId, sessionId, {
1114
+ storeThreadUiEvent(agentId, threadId, {
1115
1115
  id: `${taskId}:stale-interrupted:${timestamp}:${index}`,
1116
1116
  timestamp,
1117
1117
  task_id: taskId,
@@ -1119,85 +1119,85 @@ function finalizeInterruptedSessionUiStream(agentId, sessionId) {
1119
1119
  });
1120
1120
  });
1121
1121
  }
1122
- export function updateSessionMetadata(agentId, sessionId, updates) {
1123
- const jsonPath = sessionJsonPath(agentId, sessionId);
1124
- const session = readJson(jsonPath);
1125
- if (!session)
1122
+ export function updateThreadMetadata(agentId, threadId, updates) {
1123
+ const jsonPath = threadJsonPath(agentId, threadId);
1124
+ const thread = readJson(jsonPath);
1125
+ if (!thread)
1126
1126
  return false;
1127
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
1127
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
1128
1128
  if (updates.title !== undefined)
1129
- session.title = updates.title;
1129
+ thread.title = updates.title;
1130
1130
  if (updates.knowledge_id !== undefined)
1131
- session.knowledge_id = updates.knowledge_id;
1131
+ thread.knowledge_id = updates.knowledge_id;
1132
1132
  if (updates.knowledge_name !== undefined)
1133
- session.knowledge_name = updates.knowledge_name;
1134
- session.updated_at = new Date().toISOString();
1135
- writeJson(jsonPath, session);
1133
+ thread.knowledge_name = updates.knowledge_name;
1134
+ thread.updated_at = new Date().toISOString();
1135
+ writeJson(jsonPath, thread);
1136
1136
  return true;
1137
1137
  }
1138
- export function archiveSessionForAgent(agentId, sessionId) {
1139
- const dir = sessionDir(agentId, sessionId);
1138
+ export function archiveThreadForAgent(agentId, threadId) {
1139
+ const dir = threadDir(agentId, threadId);
1140
1140
  if (!fs.existsSync(dir))
1141
1141
  return false;
1142
- const session = readJson(path.join(dir, 'session.json'));
1143
- if (session && session.agent_id !== agentId)
1142
+ const thread = readJson(path.join(dir, 'thread.json'));
1143
+ if (thread && thread.agent_id !== agentId)
1144
1144
  return false;
1145
- const archiveDir = archivedSessionsRootDir();
1145
+ const archiveDir = archivedThreadsRootDir();
1146
1146
  fs.mkdirSync(archiveDir, { recursive: true });
1147
- const targetDir = rootArchivedSessionDir(sessionId);
1147
+ const targetDir = rootArchivedThreadDir(threadId);
1148
1148
  if (fs.existsSync(targetDir))
1149
1149
  return false; // already archived
1150
1150
  fs.renameSync(dir, targetDir);
1151
- deleteSchedulesForSession(agentId, sessionId);
1151
+ deleteSchedulesForThread(agentId, threadId);
1152
1152
  return true;
1153
1153
  }
1154
- function deleteSchedulesForSession(agentId, sessionId) {
1155
- if (sessionId.startsWith('sched-')) {
1156
- deleteSchedule(sessionId.slice(6));
1154
+ function deleteSchedulesForThread(agentId, threadId) {
1155
+ if (threadId.startsWith('sched-')) {
1156
+ deleteSchedule(threadId.slice(6));
1157
1157
  }
1158
1158
  else {
1159
1159
  const schedules = getSchedulesForAgent(agentId);
1160
1160
  for (const s of schedules) {
1161
- if (s.target_jid && s.target_jid.endsWith(':' + sessionId)) {
1161
+ if (s.target_jid && s.target_jid.endsWith(':' + threadId)) {
1162
1162
  deleteSchedule(s.id);
1163
1163
  }
1164
1164
  }
1165
1165
  }
1166
1166
  }
1167
- export function restoreSessionForAgent(agentId, sessionId) {
1168
- const archivePath = rootArchivedSessionDir(sessionId);
1167
+ export function restoreThreadForAgent(agentId, threadId) {
1168
+ const archivePath = rootArchivedThreadDir(threadId);
1169
1169
  if (!fs.existsSync(archivePath))
1170
1170
  return false;
1171
- const restoreDir = sessionDir(agentId, sessionId);
1171
+ const restoreDir = threadDir(agentId, threadId);
1172
1172
  fs.mkdirSync(path.dirname(restoreDir), { recursive: true });
1173
1173
  if (fs.existsSync(restoreDir))
1174
1174
  return false;
1175
1175
  fs.renameSync(archivePath, restoreDir);
1176
1176
  return true;
1177
1177
  }
1178
- export function deleteSession(sessionId) {
1179
- const liveDir = rootSessionDir(sessionId);
1178
+ export function deleteThread(threadId) {
1179
+ const liveDir = rootThreadDir(threadId);
1180
1180
  if (fs.existsSync(liveDir)) {
1181
1181
  fs.rmSync(liveDir, { recursive: true, force: true });
1182
1182
  return;
1183
1183
  }
1184
- const archivedDir = rootArchivedSessionDir(sessionId);
1184
+ const archivedDir = rootArchivedThreadDir(threadId);
1185
1185
  if (fs.existsSync(archivedDir)) {
1186
1186
  fs.rmSync(archivedDir, { recursive: true, force: true });
1187
1187
  return;
1188
1188
  }
1189
1189
  }
1190
- export function deleteSessionForAgent(agentId, sessionId, fromArchived = false) {
1190
+ export function deleteThreadForAgent(agentId, threadId, fromArchived = false) {
1191
1191
  const dir = fromArchived
1192
- ? rootArchivedSessionDir(sessionId)
1193
- : sessionDir(agentId, sessionId);
1192
+ ? rootArchivedThreadDir(threadId)
1193
+ : threadDir(agentId, threadId);
1194
1194
  if (!fs.existsSync(dir))
1195
1195
  return false;
1196
- const session = readJson(path.join(dir, 'session.json'));
1197
- if (session && session.agent_id !== agentId)
1196
+ const thread = readJson(path.join(dir, 'thread.json'));
1197
+ if (thread && thread.agent_id !== agentId)
1198
1198
  return false;
1199
1199
  fs.rmSync(dir, { recursive: true, force: true });
1200
- deleteSchedulesForSession(agentId, sessionId);
1200
+ deleteSchedulesForThread(agentId, threadId);
1201
1201
  return true;
1202
1202
  }
1203
1203
  /** Convert internal JSONL format → NewMessage for API compatibility */
@@ -1225,71 +1225,67 @@ function storedToNewMessage(m, chatJid) {
1225
1225
  schedule_notify_policy: m.schedule_notify_policy,
1226
1226
  };
1227
1227
  }
1228
- /** Resolve agent+session from chat_jid (format: "web:agent_id:session_id" or similar). */
1228
+ /** Resolve agent+thread from chat_jid (format: "web:agent_id:thread_id" or similar). */
1229
1229
  export function resolveFromChatJid(chatJid) {
1230
1230
  const taskMatch = chatJid.match(/^task:(.+)$/);
1231
1231
  if (taskMatch) {
1232
- return { agentId: AUTOMATION_TASK_AGENT_ID, sessionId: taskMatch[1] };
1232
+ return { agentId: AUTOMATION_TASK_AGENT_ID, threadId: taskMatch[1] };
1233
1233
  }
1234
- // web:agent_id:session_id
1234
+ // web:agent_id:thread_id
1235
1235
  const webMatch = chatJid.match(/^web:(.+?):(.+)$/);
1236
1236
  if (webMatch)
1237
- return { agentId: webMatch[1], sessionId: webMatch[2] };
1238
- // acp:agent_id:session_id
1239
- const acpMatch = chatJid.match(/^acp:(.+?):(.+)$/);
1240
- if (acpMatch)
1241
- return { agentId: acpMatch[1], sessionId: acpMatch[2] };
1242
- // gateway:agent_id:session_id
1237
+ return { agentId: webMatch[1], threadId: webMatch[2] };
1238
+ // gateway:agent_id:thread_id
1243
1239
  const gatewayMatch = chatJid.match(/^gateway:(.+?):(.+)$/);
1244
1240
  if (gatewayMatch)
1245
- return { agentId: gatewayMatch[1], sessionId: gatewayMatch[2] };
1241
+ return { agentId: gatewayMatch[1], threadId: gatewayMatch[2] };
1246
1242
  // feishu:app_id:chat_id
1247
1243
  const feishuMatch = chatJid.match(/^feishu:(.+?):(.+)$/);
1248
1244
  if (feishuMatch)
1249
- return { agentId: feishuMatch[1], sessionId: feishuMatch[2] };
1250
- // For other channels, try to find the session across agents
1251
- for (const sid of listDirs(sessionsRootDir())) {
1252
- const session = readJson(path.join(sessionsRootDir(), sid, 'session.json'));
1253
- if (session?.source_ref === chatJid) {
1254
- return { agentId: session.agent_id, sessionId: session.session_id };
1245
+ return { agentId: feishuMatch[1], threadId: feishuMatch[2] };
1246
+ // For other channels, try to find the thread across agents
1247
+ for (const sid of listDirs(threadsRootDir())) {
1248
+ const thread = readJson(path.join(threadsRootDir(), sid, 'thread.json'));
1249
+ if (thread?.source_ref === chatJid) {
1250
+ return { agentId: thread.agent_id, threadId: thread.thread_id };
1255
1251
  }
1256
1252
  }
1257
1253
  return null;
1258
1254
  }
1259
1255
  export function storeMessage(msg) {
1260
- // Determine agent_id and session_id
1256
+ // Determine agent_id and thread_id
1261
1257
  let agentId = msg.agent_id;
1262
- let sessionId = msg.session_id;
1263
- if (!agentId || !sessionId) {
1258
+ let threadId = msg.thread_id;
1259
+ if (!agentId || !threadId) {
1264
1260
  const resolved = resolveFromChatJid(msg.chat_jid);
1265
1261
  if (resolved) {
1266
1262
  agentId = agentId || resolved.agentId;
1267
- sessionId = sessionId || resolved.sessionId;
1263
+ threadId = threadId || resolved.threadId;
1268
1264
  }
1269
1265
  }
1270
- if (!agentId || !sessionId) {
1271
- logger.warn({ chat_jid: msg.chat_jid }, 'Cannot store message: unable to resolve agent/session from chat_jid');
1266
+ if (!agentId || !threadId) {
1267
+ logger.warn({ chat_jid: msg.chat_jid }, 'Cannot store message: unable to resolve agent/thread from chat_jid');
1272
1268
  return;
1273
1269
  }
1274
- const channelSession = typeof msg.metadata?.channel_thread === 'string'
1270
+ const channelThread = typeof msg.metadata?.channel_thread === 'string'
1275
1271
  ? msg.metadata.channel_thread
1276
- : typeof msg.metadata?.channel_session === 'string'
1277
- ? msg.metadata.channel_session
1272
+ : typeof msg.metadata?.channel_thread === 'string'
1273
+ ? msg.metadata.channel_thread
1278
1274
  : undefined;
1279
- if (channelSession) {
1280
- const existingSession = getSessionForAgent(agentId, sessionId);
1281
- if (existingSession) {
1282
- ensureSession({
1275
+ if (channelThread) {
1276
+ const existingThread = getThreadForAgent(agentId, threadId);
1277
+ if (existingThread) {
1278
+ ensureThread({
1283
1279
  agent_id: agentId,
1284
- session_id: sessionId,
1285
- channel: existingSession.channel,
1286
- channel_session: channelSession,
1287
- source_ref: existingSession.source_ref || msg.chat_jid,
1280
+ thread_id: threadId,
1281
+ channel: existingThread.channel,
1282
+ channel_thread: channelThread,
1283
+ source_ref: existingThread.source_ref || msg.chat_jid,
1288
1284
  });
1289
1285
  }
1290
1286
  }
1291
- // Ensure session directory exists
1292
- const msgPath = messagesPath(agentId, sessionId);
1287
+ // Ensure thread directory exists
1288
+ const msgPath = messagesPath(agentId, threadId);
1293
1289
  const stored = {
1294
1290
  id: msg.id,
1295
1291
  ts: msg.timestamp,
@@ -1312,7 +1308,7 @@ export function storeMessage(msg) {
1312
1308
  schedule_notify_policy: msg.schedule_notify_policy,
1313
1309
  };
1314
1310
  appendJsonl(msgPath, stored);
1315
- touchSessionActivity(agentId, sessionId, msg.timestamp);
1311
+ touchThreadActivity(agentId, threadId, msg.timestamp);
1316
1312
  }
1317
1313
  /** Alias for storeMessage — both had identical implementations in db.ts */
1318
1314
  export const storeMessageDirect = storeMessage;
@@ -1320,14 +1316,14 @@ export function getRecentMessages(chatJid, limit = 10) {
1320
1316
  const resolved = resolveFromChatJid(chatJid);
1321
1317
  if (!resolved)
1322
1318
  return [];
1323
- const messages = readJsonlTail(messagesPath(resolved.agentId, resolved.sessionId), limit);
1319
+ const messages = readJsonlTail(messagesPath(resolved.agentId, resolved.threadId), limit);
1324
1320
  return messages.map((m) => storedToNewMessage(m, chatJid));
1325
1321
  }
1326
1322
  export function getMessagesSince(chatJid, sinceTimestamp, _botPrefix) {
1327
1323
  const resolved = resolveFromChatJid(chatJid);
1328
1324
  if (!resolved)
1329
1325
  return [];
1330
- const all = readJsonl(messagesPath(resolved.agentId, resolved.sessionId));
1326
+ const all = readJsonl(messagesPath(resolved.agentId, resolved.threadId));
1331
1327
  return all
1332
1328
  .filter((m) => {
1333
1329
  const isNew = m.ts > sinceTimestamp;
@@ -1692,7 +1688,7 @@ export function createAutomation(input) {
1692
1688
  rrule: input.rrule.trim(),
1693
1689
  timezone: input.timezone.trim(),
1694
1690
  exdate: input.exdate?.filter(Boolean),
1695
- session_mode: input.session_mode,
1691
+ thread_mode: input.thread_mode,
1696
1692
  model: input.model,
1697
1693
  reasoning_effort: input.reasoning_effort,
1698
1694
  permission_mode: input.permission_mode,
@@ -1810,7 +1806,7 @@ export function updateAutomation(id, updates) {
1810
1806
  ...(updates.timezone !== undefined ? { timezone: updates.timezone.trim() } : {}),
1811
1807
  ...(updates.exdate !== undefined ? { exdate: updates.exdate.filter(Boolean) } : {}),
1812
1808
  ...(updates.status !== undefined ? { status: updates.status } : {}),
1813
- ...(updates.session_mode !== undefined ? { session_mode: updates.session_mode } : {}),
1809
+ ...(updates.thread_mode !== undefined ? { thread_mode: updates.thread_mode } : {}),
1814
1810
  ...(updates.model !== undefined ? { model: updates.model } : {}),
1815
1811
  ...(updates.reasoning_effort !== undefined
1816
1812
  ? { reasoning_effort: updates.reasoning_effort }
@@ -1965,10 +1961,10 @@ export function deleteAutomation(id) {
1965
1961
  if (fs.existsSync(dir)) {
1966
1962
  fs.rmSync(dir, { recursive: true, force: true });
1967
1963
  }
1968
- const sessionId = `sched-${id}`;
1964
+ const threadId = `sched-${id}`;
1969
1965
  for (const agentId of listDirs(AGENTS_DIR)) {
1970
- deleteSessionForAgent(agentId, sessionId);
1971
- deleteSessionForAgent(agentId, sessionId, true);
1966
+ deleteThreadForAgent(agentId, threadId);
1967
+ deleteThreadForAgent(agentId, threadId, true);
1972
1968
  }
1973
1969
  }
1974
1970
  export function createAutomationRun(automationId, input = {}) {
@@ -1995,7 +1991,7 @@ export function createAutomationRun(automationId, input = {}) {
1995
1991
  claimed_at: claimedAt,
1996
1992
  started_at: startedAt,
1997
1993
  finished_at: null,
1998
- session_id: input.session_id,
1994
+ thread_id: input.thread_id,
1999
1995
  task_id: input.task_id,
2000
1996
  prompt: bundle.automation.prompt,
2001
1997
  model: bundle.automation.model,
@@ -2255,16 +2251,16 @@ export function listAutomationRuns(input = {}) {
2255
2251
  })
2256
2252
  .slice(0, limit);
2257
2253
  }
2258
- export function finalizeRunningAutomationRunForSession(agentId, sessionId, input = {}) {
2254
+ export function finalizeRunningAutomationRunForThread(agentId, threadId, input = {}) {
2259
2255
  const normalizedAgentId = agentId.trim();
2260
- const normalizedSessionId = sessionId.trim();
2261
- if (!normalizedAgentId || !normalizedSessionId)
2256
+ const normalizedThreadId = threadId.trim();
2257
+ if (!normalizedAgentId || !normalizedThreadId)
2262
2258
  return undefined;
2263
2259
  const runningRun = listAutomationRuns({
2264
2260
  agent_id: normalizedAgentId,
2265
2261
  status: 'running',
2266
2262
  limit: 100,
2267
- }).find((run) => run.session_id === normalizedSessionId);
2263
+ }).find((run) => run.thread_id === normalizedThreadId);
2268
2264
  if (!runningRun)
2269
2265
  return undefined;
2270
2266
  const actor = input.actor?.trim() || 'user';
@@ -2283,7 +2279,7 @@ function automationToScheduleRecord(automation, state) {
2283
2279
  prompt: automation.prompt,
2284
2280
  cron: automation.rrule,
2285
2281
  type: 'cron',
2286
- session: automation.session_mode ?? 'isolated',
2282
+ thread: automation.thread_mode ?? 'isolated',
2287
2283
  status: automation.status === 'ACTIVE' ? 'active' : 'paused',
2288
2284
  target_jid: undefined,
2289
2285
  created_by: undefined,
@@ -2310,7 +2306,7 @@ function automationRunToScheduleRun(run) {
2310
2306
  agent_id: run.agent_id,
2311
2307
  prompt: run.prompt,
2312
2308
  status: run.status,
2313
- session_id: run.session_id,
2309
+ thread_id: run.thread_id,
2314
2310
  task_id: run.task_id,
2315
2311
  result_preview: run.result_preview,
2316
2312
  error: run.error,
@@ -2324,7 +2320,7 @@ export function createScheduleRun(scheduleId, input) {
2324
2320
  const run = createAutomationRun(scheduleId, {
2325
2321
  triggered_at: input.started_at,
2326
2322
  started_at: input.started_at,
2327
- session_id: input.session_id,
2323
+ thread_id: input.thread_id,
2328
2324
  task_id: input.task_id,
2329
2325
  });
2330
2326
  return automationRunToScheduleRun(run);
@@ -2390,7 +2386,7 @@ export function createSchedule(input) {
2390
2386
  timezone: TIMEZONE,
2391
2387
  status: 'ACTIVE',
2392
2388
  next_run: nextRun,
2393
- session_mode: input.session ?? 'isolated',
2389
+ thread_mode: input.thread ?? 'isolated',
2394
2390
  });
2395
2391
  const state = getAutomationState(automation.id);
2396
2392
  if (!state) {
@@ -2633,7 +2629,7 @@ export function updateSchedule(id, updates) {
2633
2629
  ? { status: updates.status === 'active' ? 'ACTIVE' : 'PAUSED' }
2634
2630
  : {}),
2635
2631
  next_run: derivedNextRun,
2636
- ...(updates.session !== undefined ? { session_mode: updates.session } : {}),
2632
+ ...(updates.thread !== undefined ? { thread_mode: updates.thread } : {}),
2637
2633
  });
2638
2634
  if (!automation)
2639
2635
  return undefined;
@@ -2807,47 +2803,47 @@ export function getAllRegisteredProjects() {
2807
2803
  return result;
2808
2804
  }
2809
2805
  // ═══════════════════════════════════════════════════════════════
2810
- // Chat metadata (lightweight — stored in session.json)
2806
+ // Chat metadata (lightweight — stored in thread.json)
2811
2807
  // ═══════════════════════════════════════════════════════════════
2812
2808
  export function storeChatMetadata(chatJid, timestamp, name, channel, _isGroup) {
2813
2809
  // For the filesystem approach, chat metadata is implicitly part of
2814
- // the session. We ensure the session exists.
2810
+ // the thread. We ensure the thread exists.
2815
2811
  const resolved = resolveFromChatJid(chatJid);
2816
2812
  if (!resolved)
2817
2813
  return;
2818
- ensureSession({
2814
+ ensureThread({
2819
2815
  agent_id: resolved.agentId,
2820
- session_id: resolved.sessionId,
2816
+ thread_id: resolved.threadId,
2821
2817
  channel: channel || 'unknown',
2822
2818
  source_ref: chatJid,
2823
2819
  agent_name: name,
2824
2820
  });
2825
2821
  }
2826
2822
  export function updateChatName(chatJid, name) {
2827
- // Chat metadata is stored in session.json — update the agent name
2823
+ // Chat metadata is stored in thread.json — update the agent name
2828
2824
  const resolved = resolveFromChatJid(chatJid);
2829
2825
  if (!resolved)
2830
2826
  return;
2831
- const jsonPath = sessionJsonPath(resolved.agentId, resolved.sessionId);
2832
- const session = readJson(jsonPath);
2833
- if (session) {
2834
- session.name = name;
2835
- session.space_id = session.space_id || DEFAULT_SPACE_ID;
2836
- session.updated_at = new Date().toISOString();
2837
- writeJson(jsonPath, session);
2827
+ const jsonPath = threadJsonPath(resolved.agentId, resolved.threadId);
2828
+ const thread = readJson(jsonPath);
2829
+ if (thread) {
2830
+ thread.name = name;
2831
+ thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
2832
+ thread.updated_at = new Date().toISOString();
2833
+ writeJson(jsonPath, thread);
2838
2834
  }
2839
2835
  }
2840
2836
  export function getAllChats() {
2841
- // Reconstruct from sessions
2837
+ // Reconstruct from threads
2842
2838
  const chats = [];
2843
2839
  for (const agentId of listDirs(AGENTS_DIR)) {
2844
- const sessions = getSessionsForAgent(agentId);
2845
- for (const session of sessions) {
2840
+ const threads = getThreadsForAgent(agentId);
2841
+ for (const thread of threads) {
2846
2842
  chats.push({
2847
- jid: session.source_ref || `web:${agentId}:${session.session_id}`,
2843
+ jid: thread.source_ref || `web:${agentId}:${thread.thread_id}`,
2848
2844
  name: agentId,
2849
- last_message_time: session.updated_at,
2850
- channel: session.channel,
2845
+ last_message_time: thread.updated_at,
2846
+ channel: thread.channel,
2851
2847
  is_group: 0,
2852
2848
  });
2853
2849
  }
@@ -2861,56 +2857,56 @@ export function setLastGroupSync() {
2861
2857
  setRouterState('__group_sync__', new Date().toISOString());
2862
2858
  }
2863
2859
  // ═══════════════════════════════════════════════════════════════
2864
- // Interaction events (JSONL per session)
2860
+ // Interaction events (JSONL per thread)
2865
2861
  // ═══════════════════════════════════════════════════════════════
2866
2862
  export function storeInteractionEvent(event) {
2867
2863
  const resolved = resolveFromChatJid(event.chat_jid);
2868
2864
  if (!resolved)
2869
2865
  return;
2870
- const eventsPath = path.join(sessionDir(resolved.agentId, resolved.sessionId), 'interaction-events.jsonl');
2866
+ const eventsPath = path.join(threadDir(resolved.agentId, resolved.threadId), 'interaction-events.jsonl');
2871
2867
  appendJsonl(eventsPath, event);
2872
2868
  }
2873
2869
  export function getRecentInteractionEvents(chatJid, limit = 20) {
2874
2870
  const resolved = resolveFromChatJid(chatJid);
2875
2871
  if (!resolved)
2876
2872
  return [];
2877
- const eventsPath = path.join(resolveSessionDir(resolved.agentId, resolved.sessionId), 'interaction-events.jsonl');
2873
+ const eventsPath = path.join(resolveThreadDir(resolved.agentId, resolved.threadId), 'interaction-events.jsonl');
2878
2874
  return readJsonlTail(eventsPath, limit);
2879
2875
  }
2880
- export function getSessionInteractionEvents(agentId, sessionId, limit = 20) {
2881
- const eventsPath = path.join(resolveSessionDir(agentId, sessionId), 'interaction-events.jsonl');
2876
+ export function getThreadInteractionEvents(agentId, threadId, limit = 20) {
2877
+ const eventsPath = path.join(resolveThreadDir(agentId, threadId), 'interaction-events.jsonl');
2882
2878
  return limit > 0
2883
2879
  ? readJsonlTail(eventsPath, limit)
2884
2880
  : readJsonl(eventsPath);
2885
2881
  }
2886
- export function storeSessionUiEvent(agentId, sessionId, event) {
2887
- const taskEventPath = taskUiEventsPath(sessionId, event.task_id);
2882
+ export function storeThreadUiEvent(agentId, threadId, event) {
2883
+ const taskEventPath = taskUiEventsPath(threadId, event.task_id);
2888
2884
  if (taskEventPath) {
2889
- writeTaskMetadata(agentId, sessionId, event.task_id);
2885
+ writeTaskMetadata(agentId, threadId, event.task_id);
2890
2886
  appendJsonl(taskEventPath, event);
2891
- touchSessionActivity(agentId, sessionId, event.timestamp);
2887
+ touchThreadActivity(agentId, threadId, event.timestamp);
2892
2888
  return;
2893
2889
  }
2894
- const eventsPath = path.join(sessionDir(agentId, sessionId), 'events.jsonl');
2890
+ const eventsPath = path.join(threadDir(agentId, threadId), 'events.jsonl');
2895
2891
  appendJsonl(eventsPath, event);
2896
- touchSessionActivity(agentId, sessionId, event.timestamp);
2892
+ touchThreadActivity(agentId, threadId, event.timestamp);
2897
2893
  }
2898
- export function getSessionUiEvents(agentId, sessionId, limit = 200) {
2894
+ export function getThreadUiEvents(agentId, threadId, limit = 200) {
2899
2895
  const boundedLimit = limit > 0 ? limit : SESSION_UI_EVENT_RECOVERY_LIMIT;
2900
- const baseDir = resolveSessionDir(agentId, sessionId);
2896
+ const baseDir = resolveThreadDir(agentId, threadId);
2901
2897
  const eventsPath = path.join(baseDir, 'events.jsonl');
2902
- const taskEvents = listTaskUiEventsForSession(agentId, sessionId, boundedLimit);
2898
+ const taskEvents = listTaskUiEventsForThread(agentId, threadId, boundedLimit);
2903
2899
  if (!fs.existsSync(eventsPath) && taskEvents.length > 0) {
2904
2900
  return taskEvents
2905
2901
  .sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')))
2906
2902
  .slice(-boundedLimit);
2907
2903
  }
2908
- const sessionEvents = readJsonlTail(eventsPath, boundedLimit);
2909
- return [...sessionEvents, ...taskEvents]
2904
+ const threadEvents = readJsonlTail(eventsPath, boundedLimit);
2905
+ return [...threadEvents, ...taskEvents]
2910
2906
  .sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')))
2911
2907
  .slice(-boundedLimit);
2912
2908
  }
2913
- export function getSessionUiEventsForTaskIds(agentId, sessionId, taskIds) {
2909
+ export function getThreadUiEventsForTaskIds(agentId, threadId, taskIds) {
2914
2910
  const wanted = new Set(Array.from(taskIds)
2915
2911
  .map((taskId) => String(taskId || '').trim())
2916
2912
  .filter(Boolean));
@@ -2919,7 +2915,7 @@ export function getSessionUiEventsForTaskIds(agentId, sessionId, taskIds) {
2919
2915
  const taskScopedEvents = [];
2920
2916
  const missingTaskIds = new Set();
2921
2917
  for (const taskId of wanted) {
2922
- const taskEventsPathValue = taskUiEventsPath(sessionId, taskId);
2918
+ const taskEventsPathValue = taskUiEventsPath(threadId, taskId);
2923
2919
  if (taskEventsPathValue && fs.existsSync(taskEventsPathValue)) {
2924
2920
  const taskEvents = readJsonlTail(taskEventsPathValue, TASK_UI_EVENT_HISTORY_LIMIT);
2925
2921
  taskScopedEvents.push(...taskEvents);
@@ -2931,16 +2927,16 @@ export function getSessionUiEventsForTaskIds(agentId, sessionId, taskIds) {
2931
2927
  if (missingTaskIds.size === 0) {
2932
2928
  return taskScopedEvents.sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')));
2933
2929
  }
2934
- const eventsPath = path.join(resolveSessionDir(agentId, sessionId), 'events.jsonl');
2930
+ const eventsPath = path.join(resolveThreadDir(agentId, threadId), 'events.jsonl');
2935
2931
  const tasksWithStart = new Set();
2936
- const sessionEvents = readJsonlTailMatchingUntil(eventsPath, (event) => typeof event.task_id === 'string' && missingTaskIds.has(event.task_id), (event) => {
2932
+ const threadEvents = readJsonlTailMatchingUntil(eventsPath, (event) => typeof event.task_id === 'string' && missingTaskIds.has(event.task_id), (event) => {
2937
2933
  if (event.chunk?.type !== 'start' || typeof event.task_id !== 'string') {
2938
2934
  return false;
2939
2935
  }
2940
2936
  tasksWithStart.add(event.task_id);
2941
2937
  return tasksWithStart.size >= missingTaskIds.size;
2942
2938
  });
2943
- return [...taskScopedEvents, ...sessionEvents].sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')));
2939
+ return [...taskScopedEvents, ...threadEvents].sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')));
2944
2940
  }
2945
2941
  // Test-only exports for deterministic helper validation.
2946
2942
  export const __testOnly = {