@supen-ai/cli 1.4.0 → 1.4.2
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.
- package/README.md +1 -1
- package/daemon/dist/agent-sdk/app-server-stream.js +5 -5
- package/daemon/dist/agent-sdk/app-server-stream.js.map +1 -1
- package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.d.ts +3 -2
- package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.d.ts.map +1 -1
- package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.js +72 -49
- package/daemon/dist/agent-sdk/drivers/codex-app-server-driver.js.map +1 -1
- package/daemon/dist/agent-sdk/drivers/driver.d.ts +8 -8
- package/daemon/dist/agent-sdk/drivers/driver.d.ts.map +1 -1
- package/daemon/dist/agent-sdk/index.d.ts +4 -4
- package/daemon/dist/agent-sdk/index.d.ts.map +1 -1
- package/daemon/dist/agent-sdk/index.js +2 -2
- package/daemon/dist/agent-sdk/index.js.map +1 -1
- package/daemon/dist/agent-sdk/intelligence/contracts.d.ts +2 -2
- package/daemon/dist/agent-sdk/intelligence/contracts.d.ts.map +1 -1
- package/daemon/dist/agent-sdk/memory/subsystem.d.ts +1 -1
- package/daemon/dist/agent-sdk/memory/subsystem.d.ts.map +1 -1
- package/daemon/dist/agent-sdk/{session-events.d.ts → thread-events.d.ts} +18 -18
- package/daemon/dist/agent-sdk/thread-events.d.ts.map +1 -0
- package/daemon/dist/agent-sdk/{session-events.js → thread-events.js} +15 -15
- package/daemon/dist/agent-sdk/thread-events.js.map +1 -0
- package/daemon/dist/agent-sdk/{session-manager.d.ts → thread-manager.d.ts} +9 -9
- package/daemon/dist/agent-sdk/thread-manager.d.ts.map +1 -0
- package/daemon/dist/agent-sdk/{session-manager.js → thread-manager.js} +7 -7
- package/daemon/dist/agent-sdk/thread-manager.js.map +1 -0
- package/daemon/dist/agent-sdk/types.d.ts +18 -18
- package/daemon/dist/agent-sdk/types.d.ts.map +1 -1
- package/daemon/dist/automation-event-listener.js +6 -6
- package/daemon/dist/automation-event-listener.js.map +1 -1
- package/daemon/dist/automation-runner.d.ts +1 -1
- package/daemon/dist/automation-runner.d.ts.map +1 -1
- package/daemon/dist/automation-runner.js +24 -24
- package/daemon/dist/automation-runner.js.map +1 -1
- package/daemon/dist/autonomy/memory-rules.d.ts +2 -2
- package/daemon/dist/autonomy/memory-rules.d.ts.map +1 -1
- package/daemon/dist/autonomy/memory-rules.js +1 -1
- package/daemon/dist/autonomy/memory-rules.js.map +1 -1
- package/daemon/dist/autonomy/session-autonomy.d.ts +4 -4
- package/daemon/dist/autonomy/session-autonomy.d.ts.map +1 -1
- package/daemon/dist/autonomy/session-autonomy.js +5 -5
- package/daemon/dist/autonomy/session-autonomy.js.map +1 -1
- package/daemon/dist/bin/mcp-os.js +1 -1
- package/daemon/dist/bin/mcp-os.js.map +1 -1
- package/daemon/dist/bin/mcp-scheduler.js +1 -1
- package/daemon/dist/bin/mcp-scheduler.js.map +1 -1
- package/daemon/dist/bin/supen-sys.js +1 -1
- package/daemon/dist/bin/supen-sys.js.map +1 -1
- package/daemon/dist/bootstrap/hub-bootstrap.js +1 -1
- package/daemon/dist/bootstrap/hub-bootstrap.js.map +1 -1
- package/daemon/dist/channels/http-routes.d.ts.map +1 -1
- package/daemon/dist/channels/http-routes.js +54 -60
- package/daemon/dist/channels/http-routes.js.map +1 -1
- package/daemon/dist/channels/http.js +2 -2
- package/daemon/dist/channels/http.js.map +1 -1
- package/daemon/dist/channels/index.d.ts +0 -1
- package/daemon/dist/channels/index.d.ts.map +1 -1
- package/daemon/dist/channels/index.js +0 -2
- package/daemon/dist/channels/index.js.map +1 -1
- package/daemon/dist/channels/registry.d.ts +1 -1
- package/daemon/dist/channels/registry.d.ts.map +1 -1
- package/daemon/dist/commands/builtin.d.ts +1 -1
- package/daemon/dist/commands/builtin.d.ts.map +1 -1
- package/daemon/dist/commands/builtin.js +2 -2
- package/daemon/dist/commands/builtin.js.map +1 -1
- package/daemon/dist/commands/catalog.js +3 -3
- package/daemon/dist/commands/catalog.js.map +1 -1
- package/daemon/dist/core/config.d.ts +0 -2
- package/daemon/dist/core/config.d.ts.map +1 -1
- package/daemon/dist/core/config.js +0 -5
- package/daemon/dist/core/config.js.map +1 -1
- package/daemon/dist/core/control-commands.d.ts +3 -3
- package/daemon/dist/core/control-commands.d.ts.map +1 -1
- package/daemon/dist/core/control-commands.js +6 -6
- package/daemon/dist/core/control-commands.js.map +1 -1
- package/daemon/dist/core/control-log.d.ts +4 -4
- package/daemon/dist/core/control-log.d.ts.map +1 -1
- package/daemon/dist/core/control-log.js +12 -12
- package/daemon/dist/core/control-log.js.map +1 -1
- package/daemon/dist/core/cortex.d.ts +4 -4
- package/daemon/dist/core/cortex.d.ts.map +1 -1
- package/daemon/dist/core/cortex.js +98 -117
- package/daemon/dist/core/cortex.js.map +1 -1
- package/daemon/dist/core/dispatcher.d.ts +3 -3
- package/daemon/dist/core/dispatcher.js +18 -18
- package/daemon/dist/core/dispatcher.js.map +1 -1
- package/daemon/dist/core/gateway-protocol.d.ts +0 -1
- package/daemon/dist/core/gateway-protocol.d.ts.map +1 -1
- package/daemon/dist/core/gateway.d.ts +3 -3
- package/daemon/dist/core/gateway.d.ts.map +1 -1
- package/daemon/dist/core/gateway.js +26 -26
- package/daemon/dist/core/gateway.js.map +1 -1
- package/daemon/dist/core/hub-snapshot.d.ts +1 -1
- package/daemon/dist/core/hub-snapshot.d.ts.map +1 -1
- package/daemon/dist/core/hub-snapshot.js +1 -1
- package/daemon/dist/core/hub-snapshot.js.map +1 -1
- package/daemon/dist/core/pairing.d.ts +2 -2
- package/daemon/dist/core/pairing.js +3 -3
- package/daemon/dist/core/pairing.js.map +1 -1
- package/daemon/dist/core/store.d.ts +38 -38
- package/daemon/dist/core/store.d.ts.map +1 -1
- package/daemon/dist/core/store.js +285 -289
- package/daemon/dist/core/store.js.map +1 -1
- package/daemon/dist/core/task-artifacts.d.ts +4 -4
- package/daemon/dist/core/task-artifacts.d.ts.map +1 -1
- package/daemon/dist/core/task-artifacts.js +10 -10
- package/daemon/dist/core/task-artifacts.js.map +1 -1
- package/daemon/dist/core/thread-context.d.ts +76 -0
- package/daemon/dist/core/thread-context.d.ts.map +1 -0
- package/daemon/dist/core/thread-context.js +308 -0
- package/daemon/dist/core/thread-context.js.map +1 -0
- package/daemon/dist/core/types.d.ts +28 -28
- package/daemon/dist/core/types.d.ts.map +1 -1
- package/daemon/dist/core/utils.js +1 -1
- package/daemon/dist/core/utils.js.map +1 -1
- package/daemon/dist/http/router.d.ts +2 -2
- package/daemon/dist/http/router.d.ts.map +1 -1
- package/daemon/dist/http/router.js +5 -5
- package/daemon/dist/http/router.js.map +1 -1
- package/daemon/dist/http/routes/agents.js +3 -3
- package/daemon/dist/http/routes/agents.js.map +1 -1
- package/daemon/dist/http/routes/automations.d.ts +2 -2
- package/daemon/dist/http/routes/automations.d.ts.map +1 -1
- package/daemon/dist/http/routes/automations.js +23 -23
- package/daemon/dist/http/routes/automations.js.map +1 -1
- package/daemon/dist/http/routes/chat-input.d.ts +1 -1
- package/daemon/dist/http/routes/chat-input.d.ts.map +1 -1
- package/daemon/dist/http/routes/chat-input.js +2 -2
- package/daemon/dist/http/routes/chat-input.js.map +1 -1
- package/daemon/dist/http/routes/plugins.d.ts.map +1 -1
- package/daemon/dist/http/routes/plugins.js +6 -74
- package/daemon/dist/http/routes/plugins.js.map +1 -1
- package/daemon/dist/http/routes/rpc.d.ts +3 -3
- package/daemon/dist/http/routes/rpc.d.ts.map +1 -1
- package/daemon/dist/http/routes/rpc.js +93 -92
- package/daemon/dist/http/routes/rpc.js.map +1 -1
- package/daemon/dist/http/routes/system.d.ts +8 -7
- package/daemon/dist/http/routes/system.d.ts.map +1 -1
- package/daemon/dist/http/routes/system.js +225 -111
- package/daemon/dist/http/routes/system.js.map +1 -1
- package/daemon/dist/http/routes/threads.d.ts +11 -0
- package/daemon/dist/http/routes/threads.d.ts.map +1 -0
- package/daemon/dist/http/routes/{sessions.js → threads.js} +158 -158
- package/daemon/dist/http/routes/threads.js.map +1 -0
- package/daemon/dist/http/stream.d.ts +2 -2
- package/daemon/dist/http/stream.d.ts.map +1 -1
- package/daemon/dist/http/stream.js +3 -3
- package/daemon/dist/http/stream.js.map +1 -1
- package/daemon/dist/http/thread-title.d.ts +1 -1
- package/daemon/dist/http/thread-title.d.ts.map +1 -1
- package/daemon/dist/http/thread-title.js +6 -6
- package/daemon/dist/http/thread-title.js.map +1 -1
- package/daemon/dist/http/websocket.d.ts +2 -2
- package/daemon/dist/http/websocket.d.ts.map +1 -1
- package/daemon/dist/http/websocket.js +11 -11
- package/daemon/dist/http/websocket.js.map +1 -1
- package/daemon/dist/index.d.ts +3 -3
- package/daemon/dist/index.d.ts.map +1 -1
- package/daemon/dist/index.js +65 -81
- package/daemon/dist/index.js.map +1 -1
- package/daemon/dist/mcp/aggregate-config.d.ts +1 -1
- package/daemon/dist/mcp/index.js +1 -1
- package/daemon/dist/mcp/index.js.map +1 -1
- package/daemon/dist/mcp/tools.d.ts +1 -1
- package/daemon/dist/mcp/tools.js +1 -1
- package/daemon/dist/plugins/hub.d.ts +2 -8
- package/daemon/dist/plugins/hub.d.ts.map +1 -1
- package/daemon/dist/plugins/hub.js +63 -214
- package/daemon/dist/plugins/hub.js.map +1 -1
- package/daemon/dist/plugins/types.d.ts +10 -0
- package/daemon/dist/plugins/types.d.ts.map +1 -1
- package/daemon/dist/sub-agent.d.ts +3 -3
- package/daemon/dist/sub-agent.d.ts.map +1 -1
- package/daemon/dist/sub-agent.js +8 -8
- package/daemon/dist/sub-agent.js.map +1 -1
- package/daemon/dist/sync/supabase-sync.js +18 -18
- package/daemon/dist/sync/supabase-sync.js.map +1 -1
- package/daemon/dist/task-executor.js +1 -1
- package/daemon/dist/task-executor.js.map +1 -1
- package/daemon/dist/tools/shell.js +1 -1
- package/daemon/dist/tools/shell.js.map +1 -1
- package/daemon/dist/tools/types.d.ts +1 -1
- package/daemon/dist/tools/types.d.ts.map +1 -1
- package/daemon/package.json +1 -1
- package/dist/computer.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/daemon/dist/acp-client.d.ts +0 -42
- package/daemon/dist/acp-client.d.ts.map +0 -1
- package/daemon/dist/acp-client.js +0 -149
- package/daemon/dist/acp-client.js.map +0 -1
- package/daemon/dist/acp-types.d.ts +0 -98
- package/daemon/dist/acp-types.d.ts.map +0 -1
- package/daemon/dist/acp-types.js +0 -2
- package/daemon/dist/acp-types.js.map +0 -1
- package/daemon/dist/agent-sdk/session-events.d.ts.map +0 -1
- package/daemon/dist/agent-sdk/session-events.js.map +0 -1
- package/daemon/dist/agent-sdk/session-manager.d.ts.map +0 -1
- package/daemon/dist/agent-sdk/session-manager.js.map +0 -1
- package/daemon/dist/channels/acp.d.ts +0 -23
- package/daemon/dist/channels/acp.d.ts.map +0 -1
- package/daemon/dist/channels/acp.js +0 -915
- package/daemon/dist/channels/acp.js.map +0 -1
- package/daemon/dist/http/routes/sessions.d.ts +0 -11
- package/daemon/dist/http/routes/sessions.d.ts.map +0 -1
- 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
|
-
* -
|
|
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
|
|
297
|
-
return path.join(SUPEN_HOME, '
|
|
296
|
+
function threadsRootDir() {
|
|
297
|
+
return path.join(SUPEN_HOME, 'threads');
|
|
298
298
|
}
|
|
299
|
-
function
|
|
300
|
-
return path.join(SUPEN_HOME, '
|
|
299
|
+
function archivedThreadsRootDir() {
|
|
300
|
+
return path.join(SUPEN_HOME, 'archived_threads');
|
|
301
301
|
}
|
|
302
|
-
function
|
|
303
|
-
const
|
|
304
|
-
return resolveWithin(
|
|
302
|
+
function rootThreadDir(threadId) {
|
|
303
|
+
const safeThreadId = assertSafePathSegment(threadId, 'thread_id');
|
|
304
|
+
return resolveWithin(threadsRootDir(), safeThreadId);
|
|
305
305
|
}
|
|
306
|
-
function
|
|
307
|
-
const
|
|
308
|
-
return resolveWithin(
|
|
306
|
+
function rootArchivedThreadDir(threadId) {
|
|
307
|
+
const safeThreadId = assertSafePathSegment(threadId, 'thread_id');
|
|
308
|
+
return resolveWithin(archivedThreadsRootDir(), safeThreadId);
|
|
309
309
|
}
|
|
310
|
-
function
|
|
310
|
+
function threadDir(agentId, threadId) {
|
|
311
311
|
assertSafePathSegment(agentId, 'agent_id');
|
|
312
|
-
return
|
|
312
|
+
return rootThreadDir(threadId);
|
|
313
313
|
}
|
|
314
|
-
function
|
|
314
|
+
function resolveThreadDir(agentId, threadId) {
|
|
315
315
|
assertSafePathSegment(agentId, 'agent_id');
|
|
316
|
-
const livePath =
|
|
316
|
+
const livePath = rootThreadDir(threadId);
|
|
317
317
|
if (fs.existsSync(livePath)) {
|
|
318
318
|
return livePath;
|
|
319
319
|
}
|
|
320
|
-
const archivedPath =
|
|
320
|
+
const archivedPath = rootArchivedThreadDir(threadId);
|
|
321
321
|
if (fs.existsSync(archivedPath)) {
|
|
322
322
|
return archivedPath;
|
|
323
323
|
}
|
|
324
|
-
return
|
|
324
|
+
return threadDir(agentId, threadId);
|
|
325
325
|
}
|
|
326
|
-
function
|
|
327
|
-
return path.join(
|
|
326
|
+
function threadJsonPath(agentId, threadId) {
|
|
327
|
+
return path.join(resolveThreadDir(agentId, threadId), 'thread.json');
|
|
328
328
|
}
|
|
329
|
-
function messagesPath(agentId,
|
|
330
|
-
return path.join(
|
|
329
|
+
function messagesPath(agentId, threadId) {
|
|
330
|
+
return path.join(resolveThreadDir(agentId, threadId), 'messages.jsonl');
|
|
331
331
|
}
|
|
332
|
-
function taskMetadataDir(
|
|
332
|
+
function taskMetadataDir(threadId, taskId) {
|
|
333
333
|
if (!taskId || !taskId.trim())
|
|
334
334
|
return null;
|
|
335
|
-
return path.join(resolveTaskArtifactPaths(
|
|
335
|
+
return path.join(resolveTaskArtifactPaths(threadId, taskId).taskRoot, '.supen');
|
|
336
336
|
}
|
|
337
|
-
function taskUiEventsPath(
|
|
338
|
-
const dir = taskMetadataDir(
|
|
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(
|
|
342
|
-
const dir = taskMetadataDir(
|
|
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,
|
|
346
|
-
const metadataPath = taskMetadataPath(
|
|
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
|
-
|
|
351
|
+
thread_id: threadId,
|
|
352
352
|
task_id: taskId,
|
|
353
353
|
});
|
|
354
354
|
}
|
|
355
|
-
function
|
|
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.
|
|
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,
|
|
400
|
+
function automationChatJid(agentId, threadId) {
|
|
401
401
|
return agentId === AUTOMATION_TASK_AGENT_ID
|
|
402
|
-
? `task:${
|
|
403
|
-
: `web:${agentId}:${
|
|
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(
|
|
413
|
-
fs.mkdirSync(
|
|
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
|
-
//
|
|
518
|
+
// Threads
|
|
519
519
|
// ═══════════════════════════════════════════════════════════════
|
|
520
|
-
export function
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
588
|
+
thread_id: input.thread_id,
|
|
589
589
|
agent_id: input.agent_id,
|
|
590
590
|
space_id: resolvedSpaceId,
|
|
591
591
|
channel: input.channel,
|
|
592
|
-
|
|
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
|
|
609
|
-
return (readJson(path.join(
|
|
610
|
-
readJson(path.join(
|
|
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
|
|
613
|
-
const
|
|
614
|
-
return
|
|
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
|
|
621
|
+
export function getThreadsForAgent(agentId) {
|
|
622
622
|
ensureAutomationTasksForAgent(agentId);
|
|
623
|
-
const
|
|
624
|
-
const
|
|
625
|
-
if (fs.existsSync(
|
|
626
|
-
for (const sid of listDirs(
|
|
627
|
-
const
|
|
628
|
-
if (
|
|
629
|
-
|
|
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
|
|
632
|
+
return threads.sort((a, b) => (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || ''));
|
|
633
633
|
}
|
|
634
|
-
function
|
|
635
|
-
const jsonPath =
|
|
636
|
-
const
|
|
637
|
-
if (!
|
|
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 (
|
|
642
|
+
if (thread.updated_at && thread.updated_at > nextUpdatedAt)
|
|
643
643
|
return;
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
writeJson(jsonPath,
|
|
647
|
-
}
|
|
648
|
-
export function
|
|
649
|
-
const
|
|
650
|
-
const
|
|
651
|
-
if (fs.existsSync(
|
|
652
|
-
for (const sid of listDirs(
|
|
653
|
-
const jsonPath = path.join(
|
|
654
|
-
const
|
|
655
|
-
if (
|
|
656
|
-
|
|
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
|
|
660
|
+
return threads.sort((a, b) => (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || ''));
|
|
661
661
|
}
|
|
662
|
-
export function
|
|
663
|
-
const
|
|
664
|
-
for (const sid of listDirs(
|
|
665
|
-
const
|
|
666
|
-
if (
|
|
667
|
-
|
|
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
|
|
669
|
+
return threads.sort((a, b) => {
|
|
670
670
|
const cmp = a.agent_id.localeCompare(b.agent_id);
|
|
671
|
-
return cmp !== 0 ? cmp : a.
|
|
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
|
|
679
|
+
const thread = ensureThread({
|
|
680
680
|
agent_id: agentId,
|
|
681
|
-
|
|
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 (
|
|
687
|
+
if (thread.agent_id !== agentId) {
|
|
688
688
|
const updated = {
|
|
689
|
-
...
|
|
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(
|
|
696
|
+
writeJson(threadJsonPath(agentId, taskId), updated);
|
|
697
697
|
return updated;
|
|
698
698
|
}
|
|
699
|
-
return
|
|
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
|
-
|
|
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
|
|
836
|
-
const jsonPath =
|
|
837
|
-
const
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
writeJson(jsonPath,
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
export function
|
|
846
|
-
const jsonPath =
|
|
847
|
-
const
|
|
848
|
-
if (
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
writeJson(jsonPath,
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
export function
|
|
856
|
-
const jsonPath =
|
|
857
|
-
const
|
|
858
|
-
if (
|
|
859
|
-
|
|
860
|
-
delete
|
|
861
|
-
|
|
862
|
-
writeJson(jsonPath,
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
export function
|
|
866
|
-
const jsonPath =
|
|
867
|
-
const
|
|
868
|
-
if (
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
writeJson(jsonPath,
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
export function
|
|
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
|
|
878
|
-
const sessPath =
|
|
879
|
-
const
|
|
880
|
-
if (
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
writeJson(sessPath,
|
|
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,
|
|
896
|
+
appendGlobalUsage(agentId, threadId, tokensIn, tokensOut);
|
|
897
897
|
}
|
|
898
|
-
export function appendGlobalUsage(agentId,
|
|
898
|
+
export function appendGlobalUsage(agentId, threadId, tokensIn, tokensOut) {
|
|
899
899
|
const now = new Date().toISOString();
|
|
900
|
-
// This ensures deletion of agents/
|
|
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
|
-
|
|
938
|
+
threads: {},
|
|
939
939
|
};
|
|
940
940
|
}
|
|
941
941
|
const model = day.models[modelId];
|
|
942
|
-
if (!model.
|
|
943
|
-
model.
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
1027
|
-
* A "running"
|
|
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
|
|
1029
|
+
export function cleanupStaleThreads() {
|
|
1030
1030
|
let cleaned = 0;
|
|
1031
1031
|
for (const agentId of listDirs(AGENTS_DIR)) {
|
|
1032
|
-
for (const
|
|
1033
|
-
if (
|
|
1034
|
-
|
|
1035
|
-
|
|
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,
|
|
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
|
|
1044
|
-
const events =
|
|
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
|
-
|
|
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
|
|
1123
|
-
const jsonPath =
|
|
1124
|
-
const
|
|
1125
|
-
if (!
|
|
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
|
-
|
|
1127
|
+
thread.space_id = thread.space_id || DEFAULT_SPACE_ID;
|
|
1128
1128
|
if (updates.title !== undefined)
|
|
1129
|
-
|
|
1129
|
+
thread.title = updates.title;
|
|
1130
1130
|
if (updates.knowledge_id !== undefined)
|
|
1131
|
-
|
|
1131
|
+
thread.knowledge_id = updates.knowledge_id;
|
|
1132
1132
|
if (updates.knowledge_name !== undefined)
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
writeJson(jsonPath,
|
|
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
|
|
1139
|
-
const dir =
|
|
1138
|
+
export function archiveThreadForAgent(agentId, threadId) {
|
|
1139
|
+
const dir = threadDir(agentId, threadId);
|
|
1140
1140
|
if (!fs.existsSync(dir))
|
|
1141
1141
|
return false;
|
|
1142
|
-
const
|
|
1143
|
-
if (
|
|
1142
|
+
const thread = readJson(path.join(dir, 'thread.json'));
|
|
1143
|
+
if (thread && thread.agent_id !== agentId)
|
|
1144
1144
|
return false;
|
|
1145
|
-
const archiveDir =
|
|
1145
|
+
const archiveDir = archivedThreadsRootDir();
|
|
1146
1146
|
fs.mkdirSync(archiveDir, { recursive: true });
|
|
1147
|
-
const targetDir =
|
|
1147
|
+
const targetDir = rootArchivedThreadDir(threadId);
|
|
1148
1148
|
if (fs.existsSync(targetDir))
|
|
1149
1149
|
return false; // already archived
|
|
1150
1150
|
fs.renameSync(dir, targetDir);
|
|
1151
|
-
|
|
1151
|
+
deleteSchedulesForThread(agentId, threadId);
|
|
1152
1152
|
return true;
|
|
1153
1153
|
}
|
|
1154
|
-
function
|
|
1155
|
-
if (
|
|
1156
|
-
deleteSchedule(
|
|
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(':' +
|
|
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
|
|
1168
|
-
const archivePath =
|
|
1167
|
+
export function restoreThreadForAgent(agentId, threadId) {
|
|
1168
|
+
const archivePath = rootArchivedThreadDir(threadId);
|
|
1169
1169
|
if (!fs.existsSync(archivePath))
|
|
1170
1170
|
return false;
|
|
1171
|
-
const restoreDir =
|
|
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
|
|
1179
|
-
const liveDir =
|
|
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 =
|
|
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
|
|
1190
|
+
export function deleteThreadForAgent(agentId, threadId, fromArchived = false) {
|
|
1191
1191
|
const dir = fromArchived
|
|
1192
|
-
?
|
|
1193
|
-
:
|
|
1192
|
+
? rootArchivedThreadDir(threadId)
|
|
1193
|
+
: threadDir(agentId, threadId);
|
|
1194
1194
|
if (!fs.existsSync(dir))
|
|
1195
1195
|
return false;
|
|
1196
|
-
const
|
|
1197
|
-
if (
|
|
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
|
-
|
|
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+
|
|
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,
|
|
1232
|
+
return { agentId: AUTOMATION_TASK_AGENT_ID, threadId: taskMatch[1] };
|
|
1233
1233
|
}
|
|
1234
|
-
// web:agent_id:
|
|
1234
|
+
// web:agent_id:thread_id
|
|
1235
1235
|
const webMatch = chatJid.match(/^web:(.+?):(.+)$/);
|
|
1236
1236
|
if (webMatch)
|
|
1237
|
-
return { agentId: webMatch[1],
|
|
1238
|
-
//
|
|
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],
|
|
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],
|
|
1250
|
-
// For other channels, try to find the
|
|
1251
|
-
for (const sid of listDirs(
|
|
1252
|
-
const
|
|
1253
|
-
if (
|
|
1254
|
-
return { agentId:
|
|
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
|
|
1256
|
+
// Determine agent_id and thread_id
|
|
1261
1257
|
let agentId = msg.agent_id;
|
|
1262
|
-
let
|
|
1263
|
-
if (!agentId || !
|
|
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
|
-
|
|
1263
|
+
threadId = threadId || resolved.threadId;
|
|
1268
1264
|
}
|
|
1269
1265
|
}
|
|
1270
|
-
if (!agentId || !
|
|
1271
|
-
logger.warn({ chat_jid: msg.chat_jid }, 'Cannot store message: unable to resolve agent/
|
|
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
|
|
1270
|
+
const channelThread = typeof msg.metadata?.channel_thread === 'string'
|
|
1275
1271
|
? msg.metadata.channel_thread
|
|
1276
|
-
: typeof msg.metadata?.
|
|
1277
|
-
? msg.metadata.
|
|
1272
|
+
: typeof msg.metadata?.channel_thread === 'string'
|
|
1273
|
+
? msg.metadata.channel_thread
|
|
1278
1274
|
: undefined;
|
|
1279
|
-
if (
|
|
1280
|
-
const
|
|
1281
|
-
if (
|
|
1282
|
-
|
|
1275
|
+
if (channelThread) {
|
|
1276
|
+
const existingThread = getThreadForAgent(agentId, threadId);
|
|
1277
|
+
if (existingThread) {
|
|
1278
|
+
ensureThread({
|
|
1283
1279
|
agent_id: agentId,
|
|
1284
|
-
|
|
1285
|
-
channel:
|
|
1286
|
-
|
|
1287
|
-
source_ref:
|
|
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
|
|
1292
|
-
const msgPath = messagesPath(agentId,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
1964
|
+
const threadId = `sched-${id}`;
|
|
1969
1965
|
for (const agentId of listDirs(AGENTS_DIR)) {
|
|
1970
|
-
|
|
1971
|
-
|
|
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
|
-
|
|
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
|
|
2254
|
+
export function finalizeRunningAutomationRunForThread(agentId, threadId, input = {}) {
|
|
2259
2255
|
const normalizedAgentId = agentId.trim();
|
|
2260
|
-
const
|
|
2261
|
-
if (!normalizedAgentId || !
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
2810
|
+
// the thread. We ensure the thread exists.
|
|
2815
2811
|
const resolved = resolveFromChatJid(chatJid);
|
|
2816
2812
|
if (!resolved)
|
|
2817
2813
|
return;
|
|
2818
|
-
|
|
2814
|
+
ensureThread({
|
|
2819
2815
|
agent_id: resolved.agentId,
|
|
2820
|
-
|
|
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
|
|
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 =
|
|
2832
|
-
const
|
|
2833
|
-
if (
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
writeJson(jsonPath,
|
|
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
|
|
2837
|
+
// Reconstruct from threads
|
|
2842
2838
|
const chats = [];
|
|
2843
2839
|
for (const agentId of listDirs(AGENTS_DIR)) {
|
|
2844
|
-
const
|
|
2845
|
-
for (const
|
|
2840
|
+
const threads = getThreadsForAgent(agentId);
|
|
2841
|
+
for (const thread of threads) {
|
|
2846
2842
|
chats.push({
|
|
2847
|
-
jid:
|
|
2843
|
+
jid: thread.source_ref || `web:${agentId}:${thread.thread_id}`,
|
|
2848
2844
|
name: agentId,
|
|
2849
|
-
last_message_time:
|
|
2850
|
-
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
|
|
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(
|
|
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(
|
|
2873
|
+
const eventsPath = path.join(resolveThreadDir(resolved.agentId, resolved.threadId), 'interaction-events.jsonl');
|
|
2878
2874
|
return readJsonlTail(eventsPath, limit);
|
|
2879
2875
|
}
|
|
2880
|
-
export function
|
|
2881
|
-
const eventsPath = path.join(
|
|
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
|
|
2887
|
-
const taskEventPath = taskUiEventsPath(
|
|
2882
|
+
export function storeThreadUiEvent(agentId, threadId, event) {
|
|
2883
|
+
const taskEventPath = taskUiEventsPath(threadId, event.task_id);
|
|
2888
2884
|
if (taskEventPath) {
|
|
2889
|
-
writeTaskMetadata(agentId,
|
|
2885
|
+
writeTaskMetadata(agentId, threadId, event.task_id);
|
|
2890
2886
|
appendJsonl(taskEventPath, event);
|
|
2891
|
-
|
|
2887
|
+
touchThreadActivity(agentId, threadId, event.timestamp);
|
|
2892
2888
|
return;
|
|
2893
2889
|
}
|
|
2894
|
-
const eventsPath = path.join(
|
|
2890
|
+
const eventsPath = path.join(threadDir(agentId, threadId), 'events.jsonl');
|
|
2895
2891
|
appendJsonl(eventsPath, event);
|
|
2896
|
-
|
|
2892
|
+
touchThreadActivity(agentId, threadId, event.timestamp);
|
|
2897
2893
|
}
|
|
2898
|
-
export function
|
|
2894
|
+
export function getThreadUiEvents(agentId, threadId, limit = 200) {
|
|
2899
2895
|
const boundedLimit = limit > 0 ? limit : SESSION_UI_EVENT_RECOVERY_LIMIT;
|
|
2900
|
-
const baseDir =
|
|
2896
|
+
const baseDir = resolveThreadDir(agentId, threadId);
|
|
2901
2897
|
const eventsPath = path.join(baseDir, 'events.jsonl');
|
|
2902
|
-
const taskEvents =
|
|
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
|
|
2909
|
-
return [...
|
|
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
|
|
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(
|
|
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(
|
|
2930
|
+
const eventsPath = path.join(resolveThreadDir(agentId, threadId), 'events.jsonl');
|
|
2935
2931
|
const tasksWithStart = new Set();
|
|
2936
|
-
const
|
|
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, ...
|
|
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 = {
|