@supen-ai/cli 1.4.1 → 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.js +1 -1
- package/daemon/dist/core/thread-context.js.map +1 -1
- 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 +91 -92
- package/daemon/dist/http/routes/rpc.js.map +1 -1
- package/daemon/dist/http/routes/system.d.ts +7 -7
- package/daemon/dist/http/routes/system.d.ts.map +1 -1
- package/daemon/dist/http/routes/system.js +211 -105
- 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} +157 -157
- 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 +64 -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
|
@@ -14,7 +14,7 @@ import { collectOsTelemetryFields } from '../../core/os-info.js';
|
|
|
14
14
|
import { getCodingCliStatusResponse, startCodingCliStatusCache, } from '../../core/coding-cli-status-cache.js';
|
|
15
15
|
import { getGatewayInstance, getLlmToken } from '../../core/gateway.js';
|
|
16
16
|
import { normalizeGatewayUplinkUrl, readGatewayConfig, writeGatewayConfig, } from '../../core/gateway-config.js';
|
|
17
|
-
import {
|
|
17
|
+
import { ensureThread, getAllAgents, getDailyUsage, getGlobalUsage, getThreadsForAgent, getThreadUiEvents, updateThreadBackendDriverId, updateThreadSdkSessionId, } from '../../core/store.js';
|
|
18
18
|
import { listMcpEnvKeys, listMcpServers } from '../../mcp/default-servers.js';
|
|
19
19
|
import { getMcpEnvOverrides, updateMcpEnvOverrides } from '../../mcp/settings.js';
|
|
20
20
|
import { getMcpManager } from '../../mcp/index.js';
|
|
@@ -32,6 +32,9 @@ const SPACE_LOG_STREAM_PING_MS = 15000;
|
|
|
32
32
|
const MIRRORED_THREAD_LIMIT = 80;
|
|
33
33
|
const MIRRORED_THREAD_HISTORY_LIMIT = 200;
|
|
34
34
|
const MIRRORED_THREAD_HISTORY_MAX_BYTES = 256 * 1024 * 1024;
|
|
35
|
+
const MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES = 8 * 1024 * 1024;
|
|
36
|
+
const MIRRORED_THREAD_HISTORY_FAST_MAX_LINES = 2_000;
|
|
37
|
+
const MIRRORED_THREAD_JSONL_CACHE_LIMIT = 64;
|
|
35
38
|
const MIRRORED_THREAD_PROJECTION_VERSION = 2;
|
|
36
39
|
const MIRRORED_THREAD_TEXT_LIMIT = 48_000;
|
|
37
40
|
const MIRRORED_THREAD_INLINE_DATA_URL_LIMIT = 120_000;
|
|
@@ -114,6 +117,9 @@ function truncateStreamDelta(value, max = 160) {
|
|
|
114
117
|
function currentSpaceId() {
|
|
115
118
|
return (process.env.SUPEN_SPACE_ID || '').trim() || 'local';
|
|
116
119
|
}
|
|
120
|
+
const recentJsonlLineCache = new Map();
|
|
121
|
+
const recentCodexHistoryLineCache = new Map();
|
|
122
|
+
const mirroredFileTextCache = new Map();
|
|
117
123
|
function summarizeStreamEvent(raw) {
|
|
118
124
|
const inner = raw.event && typeof raw.event === 'object'
|
|
119
125
|
? raw.event
|
|
@@ -384,13 +390,13 @@ function extractQuotaEvent(event) {
|
|
|
384
390
|
};
|
|
385
391
|
}
|
|
386
392
|
function readLatestSpaceQuotaStatus() {
|
|
387
|
-
const
|
|
388
|
-
.flatMap((agent) =>
|
|
393
|
+
const threads = getAllAgents()
|
|
394
|
+
.flatMap((agent) => getThreadsForAgent(agent.agent_id).map((thread) => ({
|
|
389
395
|
agent_id: agent.agent_id,
|
|
390
|
-
|
|
396
|
+
thread_id: thread.thread_id,
|
|
391
397
|
})));
|
|
392
|
-
const latest =
|
|
393
|
-
.flatMap((
|
|
398
|
+
const latest = threads
|
|
399
|
+
.flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, 200)
|
|
394
400
|
.map((event) => extractQuotaEvent(event)))
|
|
395
401
|
.filter((entry) => Boolean(entry))
|
|
396
402
|
.sort((a, b) => a.updated_at.localeCompare(b.updated_at))
|
|
@@ -414,17 +420,84 @@ function parseJsonLine(line) {
|
|
|
414
420
|
return null;
|
|
415
421
|
}
|
|
416
422
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
423
|
+
function jsonlCacheKey(parts) {
|
|
424
|
+
return parts.map((part) => part === undefined ? '' : String(part)).join('\u0000');
|
|
425
|
+
}
|
|
426
|
+
function getCachedJsonlLines(cache, key, stat) {
|
|
427
|
+
const cached = cache.get(key);
|
|
428
|
+
if (!cached)
|
|
429
|
+
return null;
|
|
430
|
+
if (cached.size !== stat.size || cached.mtimeMs !== stat.mtimeMs) {
|
|
431
|
+
cache.delete(key);
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
cached.accessedAt = Date.now();
|
|
435
|
+
return cached.lines;
|
|
436
|
+
}
|
|
437
|
+
function setCachedJsonlLines(cache, key, entry) {
|
|
438
|
+
if (cache.size >= MIRRORED_THREAD_JSONL_CACHE_LIMIT && !cache.has(key)) {
|
|
439
|
+
let oldestKey = null;
|
|
440
|
+
let oldestAccess = Number.POSITIVE_INFINITY;
|
|
441
|
+
for (const [candidateKey, candidate] of cache) {
|
|
442
|
+
if (candidate.accessedAt < oldestAccess) {
|
|
443
|
+
oldestAccess = candidate.accessedAt;
|
|
444
|
+
oldestKey = candidateKey;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (oldestKey)
|
|
448
|
+
cache.delete(oldestKey);
|
|
449
|
+
}
|
|
450
|
+
cache.set(key, {
|
|
451
|
+
...entry,
|
|
452
|
+
accessedAt: Date.now(),
|
|
453
|
+
});
|
|
454
|
+
return entry.lines;
|
|
455
|
+
}
|
|
456
|
+
function getCachedFileText(key, stat) {
|
|
457
|
+
const cached = mirroredFileTextCache.get(key);
|
|
458
|
+
if (!cached)
|
|
459
|
+
return null;
|
|
460
|
+
if (cached.size !== stat.size || cached.mtimeMs !== stat.mtimeMs) {
|
|
461
|
+
mirroredFileTextCache.delete(key);
|
|
462
|
+
return null;
|
|
422
463
|
}
|
|
464
|
+
cached.accessedAt = Date.now();
|
|
465
|
+
return cached.text;
|
|
466
|
+
}
|
|
467
|
+
function setCachedFileText(key, stat, text) {
|
|
468
|
+
if (mirroredFileTextCache.size >= MIRRORED_THREAD_JSONL_CACHE_LIMIT && !mirroredFileTextCache.has(key)) {
|
|
469
|
+
let oldestKey = null;
|
|
470
|
+
let oldestAccess = Number.POSITIVE_INFINITY;
|
|
471
|
+
for (const [candidateKey, candidate] of mirroredFileTextCache) {
|
|
472
|
+
if (candidate.accessedAt < oldestAccess) {
|
|
473
|
+
oldestAccess = candidate.accessedAt;
|
|
474
|
+
oldestKey = candidateKey;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (oldestKey)
|
|
478
|
+
mirroredFileTextCache.delete(oldestKey);
|
|
479
|
+
}
|
|
480
|
+
mirroredFileTextCache.set(key, {
|
|
481
|
+
size: stat.size,
|
|
482
|
+
mtimeMs: stat.mtimeMs,
|
|
483
|
+
text,
|
|
484
|
+
accessedAt: Date.now(),
|
|
485
|
+
});
|
|
486
|
+
return text;
|
|
487
|
+
}
|
|
488
|
+
export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES) {
|
|
489
|
+
const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
|
|
490
|
+
const stat = fs.statSync(filePath);
|
|
491
|
+
const cachedKey = jsonlCacheKey(['recent', filePath, safeMaxBytes]);
|
|
492
|
+
const cachedLines = getCachedJsonlLines(recentJsonlLineCache, cachedKey, stat);
|
|
493
|
+
if (cachedLines)
|
|
494
|
+
return cachedLines;
|
|
495
|
+
const size = stat.size;
|
|
423
496
|
const readLength = Math.min(size, safeMaxBytes);
|
|
424
497
|
const start = size - readLength;
|
|
425
498
|
const fd = fs.openSync(filePath, 'r');
|
|
426
499
|
try {
|
|
427
|
-
const buffer = Buffer.
|
|
500
|
+
const buffer = Buffer.allocUnsafe(readLength);
|
|
428
501
|
const bytesRead = fs.readSync(fd, buffer, 0, readLength, start);
|
|
429
502
|
let text = buffer.subarray(0, bytesRead).toString('utf-8');
|
|
430
503
|
if (start > 0) {
|
|
@@ -433,7 +506,14 @@ export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTOR
|
|
|
433
506
|
return [];
|
|
434
507
|
text = text.slice(firstLineEnd + 1);
|
|
435
508
|
}
|
|
436
|
-
|
|
509
|
+
const lines = text.split(/\r?\n/).filter(Boolean);
|
|
510
|
+
return setCachedJsonlLines(recentJsonlLineCache, cachedKey, {
|
|
511
|
+
filePath,
|
|
512
|
+
size: stat.size,
|
|
513
|
+
mtimeMs: stat.mtimeMs,
|
|
514
|
+
maxBytes: safeMaxBytes,
|
|
515
|
+
lines,
|
|
516
|
+
});
|
|
437
517
|
}
|
|
438
518
|
finally {
|
|
439
519
|
fs.closeSync(fd);
|
|
@@ -520,10 +600,15 @@ function codexAppServerEventChunk(method, params = {}) {
|
|
|
520
600
|
},
|
|
521
601
|
};
|
|
522
602
|
}
|
|
523
|
-
function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes =
|
|
603
|
+
function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES) {
|
|
524
604
|
const safeTargetMessages = Math.max(1, Math.min(Math.floor(targetMessages), 5_000));
|
|
525
605
|
const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
|
|
526
|
-
const
|
|
606
|
+
const stat = fs.statSync(filePath);
|
|
607
|
+
const cachedKey = jsonlCacheKey(['codex-history', filePath, safeTargetMessages, safeMaxBytes]);
|
|
608
|
+
const cachedLines = getCachedJsonlLines(recentCodexHistoryLineCache, cachedKey, stat);
|
|
609
|
+
if (cachedLines)
|
|
610
|
+
return cachedLines;
|
|
611
|
+
const size = stat.size;
|
|
527
612
|
const fd = fs.openSync(filePath, 'r');
|
|
528
613
|
try {
|
|
529
614
|
let position = size;
|
|
@@ -531,7 +616,10 @@ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRROR
|
|
|
531
616
|
let leadingPartial = '';
|
|
532
617
|
let messageCount = 0;
|
|
533
618
|
let collected = [];
|
|
534
|
-
while (position > 0 &&
|
|
619
|
+
while (position > 0 &&
|
|
620
|
+
scannedBytes < safeMaxBytes &&
|
|
621
|
+
messageCount < safeTargetMessages &&
|
|
622
|
+
collected.length < MIRRORED_THREAD_HISTORY_FAST_MAX_LINES) {
|
|
535
623
|
const readLength = Math.min(MIRRORED_THREAD_HISTORY_CHUNK_BYTES, position, safeMaxBytes - scannedBytes);
|
|
536
624
|
position -= readLength;
|
|
537
625
|
scannedBytes += readLength;
|
|
@@ -548,17 +636,25 @@ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRROR
|
|
|
548
636
|
messageCount += historyLines.filter(isCodexVisibleUserHistoryLine).length;
|
|
549
637
|
collected = [...historyLines, ...collected];
|
|
550
638
|
}
|
|
551
|
-
|
|
639
|
+
const lines = collected.filter(Boolean);
|
|
640
|
+
return setCachedJsonlLines(recentCodexHistoryLineCache, cachedKey, {
|
|
641
|
+
filePath,
|
|
642
|
+
size: stat.size,
|
|
643
|
+
mtimeMs: stat.mtimeMs,
|
|
644
|
+
maxBytes: safeMaxBytes,
|
|
645
|
+
targetMessages: safeTargetMessages,
|
|
646
|
+
lines,
|
|
647
|
+
});
|
|
552
648
|
}
|
|
553
649
|
finally {
|
|
554
650
|
fs.closeSync(fd);
|
|
555
651
|
}
|
|
556
652
|
}
|
|
557
653
|
function readThreadIndex(limit) {
|
|
558
|
-
const indexPath = path.join(localAgentHome(), '
|
|
654
|
+
const indexPath = path.join(localAgentHome(), 'thread_index.jsonl');
|
|
559
655
|
if (!fs.existsSync(indexPath))
|
|
560
656
|
return [];
|
|
561
|
-
const lines =
|
|
657
|
+
const lines = readRecentJsonlLines(indexPath);
|
|
562
658
|
const byId = new Map();
|
|
563
659
|
for (const line of lines) {
|
|
564
660
|
const parsed = parseJsonLine(line);
|
|
@@ -651,8 +747,8 @@ function readThreadStateEntries(limit) {
|
|
|
651
747
|
return [];
|
|
652
748
|
}
|
|
653
749
|
}
|
|
654
|
-
let
|
|
655
|
-
function
|
|
750
|
+
let mirroredThreadFileIndexCache = null;
|
|
751
|
+
function walkThreadFilesUncached(dir, out = new Map()) {
|
|
656
752
|
if (!fs.existsSync(dir))
|
|
657
753
|
return out;
|
|
658
754
|
let entries;
|
|
@@ -665,7 +761,7 @@ function walkSessionFilesUncached(dir, out = new Map()) {
|
|
|
665
761
|
for (const entry of entries) {
|
|
666
762
|
const fullPath = path.join(dir, entry.name);
|
|
667
763
|
if (entry.isDirectory()) {
|
|
668
|
-
|
|
764
|
+
walkThreadFilesUncached(fullPath, out);
|
|
669
765
|
continue;
|
|
670
766
|
}
|
|
671
767
|
if (!entry.isFile() || !entry.name.endsWith('.jsonl'))
|
|
@@ -676,15 +772,15 @@ function walkSessionFilesUncached(dir, out = new Map()) {
|
|
|
676
772
|
}
|
|
677
773
|
return out;
|
|
678
774
|
}
|
|
679
|
-
function
|
|
775
|
+
function walkThreadFiles(dir) {
|
|
680
776
|
const now = Date.now();
|
|
681
|
-
if (
|
|
682
|
-
|
|
683
|
-
now -
|
|
684
|
-
return new Map(
|
|
777
|
+
if (mirroredThreadFileIndexCache &&
|
|
778
|
+
mirroredThreadFileIndexCache.root === dir &&
|
|
779
|
+
now - mirroredThreadFileIndexCache.indexedAt <= MIRRORED_THREAD_SESSION_FILE_INDEX_TTL_MS) {
|
|
780
|
+
return new Map(mirroredThreadFileIndexCache.files);
|
|
685
781
|
}
|
|
686
|
-
const files =
|
|
687
|
-
|
|
782
|
+
const files = walkThreadFilesUncached(dir);
|
|
783
|
+
mirroredThreadFileIndexCache = {
|
|
688
784
|
root: dir,
|
|
689
785
|
indexedAt: now,
|
|
690
786
|
files: new Map(files),
|
|
@@ -692,24 +788,33 @@ function walkSessionFiles(dir) {
|
|
|
692
788
|
return files;
|
|
693
789
|
}
|
|
694
790
|
function readFileHead(filePath, maxBytes = 64 * 1024) {
|
|
791
|
+
const stat = fs.statSync(filePath);
|
|
792
|
+
const cacheKey = jsonlCacheKey(['head', filePath, maxBytes]);
|
|
793
|
+
const cached = getCachedFileText(cacheKey, stat);
|
|
794
|
+
if (cached !== null)
|
|
795
|
+
return cached;
|
|
695
796
|
const fd = fs.openSync(filePath, 'r');
|
|
696
797
|
try {
|
|
697
798
|
const buffer = Buffer.alloc(maxBytes);
|
|
698
799
|
const bytesRead = fs.readSync(fd, buffer, 0, maxBytes, 0);
|
|
699
|
-
return buffer.subarray(0, bytesRead).toString('utf-8');
|
|
800
|
+
return setCachedFileText(cacheKey, stat, buffer.subarray(0, bytesRead).toString('utf-8'));
|
|
700
801
|
}
|
|
701
802
|
finally {
|
|
702
803
|
fs.closeSync(fd);
|
|
703
804
|
}
|
|
704
805
|
}
|
|
705
806
|
function readFileTail(filePath, maxBytes = 256 * 1024) {
|
|
807
|
+
const stat = fs.statSync(filePath);
|
|
808
|
+
const cacheKey = jsonlCacheKey(['tail', filePath, maxBytes]);
|
|
809
|
+
const cached = getCachedFileText(cacheKey, stat);
|
|
810
|
+
if (cached !== null)
|
|
811
|
+
return cached;
|
|
706
812
|
const fd = fs.openSync(filePath, 'r');
|
|
707
813
|
try {
|
|
708
|
-
const stat = fs.fstatSync(fd);
|
|
709
814
|
const length = Math.min(maxBytes, stat.size);
|
|
710
815
|
const buffer = Buffer.alloc(length);
|
|
711
816
|
fs.readSync(fd, buffer, 0, length, Math.max(0, stat.size - length));
|
|
712
|
-
return buffer.toString('utf-8');
|
|
817
|
+
return setCachedFileText(cacheKey, stat, buffer.toString('utf-8'));
|
|
713
818
|
}
|
|
714
819
|
finally {
|
|
715
820
|
fs.closeSync(fd);
|
|
@@ -752,7 +857,7 @@ function readThreadWorkspacePath(filePath) {
|
|
|
752
857
|
if (!line.trim())
|
|
753
858
|
continue;
|
|
754
859
|
const parsed = parseJsonLine(line);
|
|
755
|
-
if (parsed?.type !== '
|
|
860
|
+
if (parsed?.type !== 'thread_meta')
|
|
756
861
|
continue;
|
|
757
862
|
const payload = parsed.payload && typeof parsed.payload === 'object'
|
|
758
863
|
? parsed.payload
|
|
@@ -1009,7 +1114,7 @@ function readMirroredThreadStatus(threadId, filePath) {
|
|
|
1009
1114
|
return 'running';
|
|
1010
1115
|
}
|
|
1011
1116
|
catch {
|
|
1012
|
-
// Fall back to the Codex
|
|
1117
|
+
// Fall back to the Codex thread file status below.
|
|
1013
1118
|
}
|
|
1014
1119
|
return readThreadStatus(filePath);
|
|
1015
1120
|
}
|
|
@@ -1106,18 +1211,19 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1106
1211
|
const safeLimit = Math.max(1, Math.min(limit, 200));
|
|
1107
1212
|
const stateEntries = readThreadStateEntries(safeLimit);
|
|
1108
1213
|
const indexEntries = readThreadIndex(safeLimit);
|
|
1109
|
-
const
|
|
1214
|
+
const indexEntriesById = new Map(indexEntries.map((entry) => [entry.id, entry]));
|
|
1215
|
+
const threadFiles = walkThreadFiles(path.join(localAgentHome(), 'threads'));
|
|
1110
1216
|
const projects = new Map();
|
|
1111
1217
|
if (stateEntries.length > 0) {
|
|
1112
1218
|
for (const entry of stateEntries) {
|
|
1113
|
-
const
|
|
1114
|
-
if (!
|
|
1219
|
+
const threadPath = threadFiles.get(entry.id) || null;
|
|
1220
|
+
if (!threadPath)
|
|
1115
1221
|
continue;
|
|
1116
|
-
const workspacePath = entry.cwd || readThreadWorkspacePath(
|
|
1222
|
+
const workspacePath = entry.cwd || readThreadWorkspacePath(threadPath);
|
|
1117
1223
|
if (!isMirrorableCodexWorkspace(workspacePath))
|
|
1118
1224
|
continue;
|
|
1119
1225
|
const projectId = workspacePath || 'no-workspace';
|
|
1120
|
-
const indexEntry =
|
|
1226
|
+
const indexEntry = indexEntriesById.get(entry.id) || null;
|
|
1121
1227
|
const title = indexEntry?.thread_name || entry.title;
|
|
1122
1228
|
if (isAutomationRunThreadTitle(title))
|
|
1123
1229
|
continue;
|
|
@@ -1131,8 +1237,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1131
1237
|
id: entry.id,
|
|
1132
1238
|
title,
|
|
1133
1239
|
updated_at: entry.updated_at,
|
|
1134
|
-
status: readMirroredThreadStatus(entry.id,
|
|
1135
|
-
|
|
1240
|
+
status: readMirroredThreadStatus(entry.id, threadPath),
|
|
1241
|
+
thread_path: threadPath,
|
|
1136
1242
|
});
|
|
1137
1243
|
projects.set(projectId, project);
|
|
1138
1244
|
}
|
|
@@ -1145,8 +1251,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1145
1251
|
};
|
|
1146
1252
|
}
|
|
1147
1253
|
for (const entry of indexEntries) {
|
|
1148
|
-
const
|
|
1149
|
-
const workspacePath = readThreadWorkspacePath(
|
|
1254
|
+
const threadPath = threadFiles.get(entry.id) || null;
|
|
1255
|
+
const workspacePath = readThreadWorkspacePath(threadPath);
|
|
1150
1256
|
if (!isMirrorableCodexWorkspace(workspacePath))
|
|
1151
1257
|
continue;
|
|
1152
1258
|
if (isAutomationRunThreadTitle(entry.thread_name))
|
|
@@ -1162,8 +1268,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1162
1268
|
id: entry.id,
|
|
1163
1269
|
title: entry.thread_name,
|
|
1164
1270
|
updated_at: entry.updated_at,
|
|
1165
|
-
status: readMirroredThreadStatus(entry.id,
|
|
1166
|
-
|
|
1271
|
+
status: readMirroredThreadStatus(entry.id, threadPath),
|
|
1272
|
+
thread_path: threadPath,
|
|
1167
1273
|
});
|
|
1168
1274
|
projects.set(projectId, project);
|
|
1169
1275
|
}
|
|
@@ -1298,10 +1404,10 @@ function isMirroredGoalContextMessage(text) {
|
|
|
1298
1404
|
const trimmed = text.trim();
|
|
1299
1405
|
return trimmed.startsWith('<goal_context>') && trimmed.endsWith('</goal_context>');
|
|
1300
1406
|
}
|
|
1301
|
-
function mirroredThreadWorkspacePath(
|
|
1407
|
+
function mirroredThreadWorkspacePath(threadPath, stateEntry) {
|
|
1302
1408
|
const stateCwd = typeof stateEntry?.cwd === 'string' ? stateEntry.cwd : null;
|
|
1303
|
-
const
|
|
1304
|
-
for (const candidate of [stateCwd,
|
|
1409
|
+
const threadWorkspace = readThreadWorkspacePath(threadPath);
|
|
1410
|
+
for (const candidate of [stateCwd, threadWorkspace]) {
|
|
1305
1411
|
if (candidate && isMirrorableCodexWorkspace(candidate))
|
|
1306
1412
|
return candidate;
|
|
1307
1413
|
}
|
|
@@ -1356,16 +1462,16 @@ function collapseCompletedAssistantProgressMessages(input) {
|
|
|
1356
1462
|
}
|
|
1357
1463
|
export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY_LIMIT, options = {}) {
|
|
1358
1464
|
const safeLimit = Math.max(1, Math.min(limit, 1000));
|
|
1359
|
-
const
|
|
1360
|
-
if (!
|
|
1465
|
+
const threadPath = walkThreadFiles(path.join(localAgentHome(), 'threads')).get(threadId);
|
|
1466
|
+
if (!threadPath)
|
|
1361
1467
|
return null;
|
|
1362
1468
|
const eventLogThreadId = options.eventLogThreadId || threadId;
|
|
1363
1469
|
const stateEntry = readThreadStateEntry(threadId);
|
|
1364
|
-
const workspacePath = mirroredThreadWorkspacePath(
|
|
1470
|
+
const workspacePath = mirroredThreadWorkspacePath(threadPath, stateEntry);
|
|
1365
1471
|
const indexEntry = readThreadIndexEntry(threadId);
|
|
1366
1472
|
const eventLogHead = readThreadEventLogHead(eventLogThreadId);
|
|
1367
1473
|
const historyLineTarget = Math.min(250, Math.max(3, Math.ceil(safeLimit / 4)));
|
|
1368
|
-
const lines = readRecentCodexHistoryLines(
|
|
1474
|
+
const lines = readRecentCodexHistoryLines(threadPath, historyLineTarget);
|
|
1369
1475
|
const messages = [];
|
|
1370
1476
|
const events = [];
|
|
1371
1477
|
let messageIndex = 0;
|
|
@@ -1573,7 +1679,7 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1573
1679
|
const collapsedMessages = collapseCompletedAssistantProgressMessages(messages);
|
|
1574
1680
|
const slicedMessages = collapsedMessages.slice(-safeLimit);
|
|
1575
1681
|
const slicedEvents = events.slice(-safeLimit * 8);
|
|
1576
|
-
const status = readMirroredThreadStatus(eventLogThreadId,
|
|
1682
|
+
const status = readMirroredThreadStatus(eventLogThreadId, threadPath);
|
|
1577
1683
|
const latestGoalContext = recentGoalContext;
|
|
1578
1684
|
const context = readThreadContext({
|
|
1579
1685
|
threadId,
|
|
@@ -1581,7 +1687,7 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1581
1687
|
workspace: workspacePath,
|
|
1582
1688
|
});
|
|
1583
1689
|
return {
|
|
1584
|
-
|
|
1690
|
+
thread: {
|
|
1585
1691
|
task_id: threadId,
|
|
1586
1692
|
agent_id: 'codex',
|
|
1587
1693
|
channel: 'local-agent',
|
|
@@ -1611,19 +1717,19 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1611
1717
|
...(latestGoalContext ? { goal_context: latestGoalContext } : {}),
|
|
1612
1718
|
};
|
|
1613
1719
|
}
|
|
1614
|
-
function serializeAdoptedMirroredThread(
|
|
1720
|
+
function serializeAdoptedMirroredThread(thread) {
|
|
1615
1721
|
return {
|
|
1616
|
-
id:
|
|
1617
|
-
taskId:
|
|
1618
|
-
agentId:
|
|
1619
|
-
status:
|
|
1620
|
-
title:
|
|
1621
|
-
createdAt:
|
|
1622
|
-
updatedAt:
|
|
1722
|
+
id: thread.thread_id,
|
|
1723
|
+
taskId: thread.thread_id,
|
|
1724
|
+
agentId: thread.agent_id,
|
|
1725
|
+
status: thread.status || 'idle',
|
|
1726
|
+
title: thread.title,
|
|
1727
|
+
createdAt: thread.created_at,
|
|
1728
|
+
updatedAt: thread.updated_at,
|
|
1623
1729
|
supen: {
|
|
1624
|
-
channel:
|
|
1625
|
-
sourceRef:
|
|
1626
|
-
taskWorkspaceFolder:
|
|
1730
|
+
channel: thread.channel,
|
|
1731
|
+
sourceRef: thread.source_ref,
|
|
1732
|
+
taskWorkspaceFolder: thread.task_workspace_folder,
|
|
1627
1733
|
},
|
|
1628
1734
|
};
|
|
1629
1735
|
}
|
|
@@ -1631,23 +1737,23 @@ function adoptMirroredThread(threadId, agentId) {
|
|
|
1631
1737
|
const history = readCodexThreadHistory(threadId, 1);
|
|
1632
1738
|
if (!history)
|
|
1633
1739
|
return null;
|
|
1634
|
-
const
|
|
1635
|
-
const
|
|
1740
|
+
const historyThread = history.thread;
|
|
1741
|
+
const thread = ensureThread({
|
|
1636
1742
|
agent_id: agentId,
|
|
1637
|
-
|
|
1743
|
+
thread_id: threadId,
|
|
1638
1744
|
channel: 'http',
|
|
1639
1745
|
agent_name: agentId,
|
|
1640
1746
|
source_ref: `local-agent:${threadId}`,
|
|
1641
|
-
title: typeof
|
|
1642
|
-
status:
|
|
1747
|
+
title: typeof historyThread.title === 'string' ? historyThread.title : undefined,
|
|
1748
|
+
status: historyThread.status === 'running' ? 'running' : 'idle',
|
|
1643
1749
|
backend_driver_id: 'codex-app-server',
|
|
1644
|
-
task_workspace_folder: typeof
|
|
1645
|
-
?
|
|
1750
|
+
task_workspace_folder: typeof historyThread.task_workspace_folder === 'string'
|
|
1751
|
+
? historyThread.task_workspace_folder
|
|
1646
1752
|
: null,
|
|
1647
1753
|
});
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
return
|
|
1754
|
+
updateThreadSdkSessionId(agentId, threadId, threadId);
|
|
1755
|
+
updateThreadBackendDriverId(agentId, threadId, 'codex-app-server');
|
|
1756
|
+
return thread;
|
|
1651
1757
|
}
|
|
1652
1758
|
function coerceSingleQueryParam(value) {
|
|
1653
1759
|
if (Array.isArray(value))
|
|
@@ -1670,24 +1776,24 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1670
1776
|
const agents = getAllAgents()
|
|
1671
1777
|
// Single-Computer runtime: include all agents in this mounted volume.
|
|
1672
1778
|
.sort((a, b) => (a.name || a.agent_id).localeCompare(b.name || b.agent_id));
|
|
1673
|
-
const
|
|
1674
|
-
.map((
|
|
1779
|
+
const threads = agents.flatMap((agent) => getThreadsForAgent(agent.agent_id)
|
|
1780
|
+
.map((thread) => ({
|
|
1675
1781
|
agent_id: agent.agent_id,
|
|
1676
1782
|
agent_name: agent.name || null,
|
|
1677
|
-
|
|
1678
|
-
status:
|
|
1679
|
-
updated_at:
|
|
1680
|
-
title:
|
|
1783
|
+
thread_id: thread.thread_id,
|
|
1784
|
+
status: thread.status,
|
|
1785
|
+
updated_at: thread.updated_at,
|
|
1786
|
+
title: thread.title || null,
|
|
1681
1787
|
})));
|
|
1682
|
-
const
|
|
1683
|
-
if (filters.agentId &&
|
|
1788
|
+
const filteredThreads = threads.filter((thread) => {
|
|
1789
|
+
if (filters.agentId && thread.agent_id !== filters.agentId)
|
|
1684
1790
|
return false;
|
|
1685
|
-
if (filters.
|
|
1791
|
+
if (filters.threadId && thread.thread_id !== filters.threadId)
|
|
1686
1792
|
return false;
|
|
1687
1793
|
return true;
|
|
1688
1794
|
});
|
|
1689
|
-
const entries =
|
|
1690
|
-
.flatMap((
|
|
1795
|
+
const entries = filteredThreads
|
|
1796
|
+
.flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, limit).map((event) => {
|
|
1691
1797
|
const logSummary = event.chunk && typeof event.chunk === 'object'
|
|
1692
1798
|
? summarizeUiChunk(event.chunk)
|
|
1693
1799
|
: null;
|
|
@@ -1695,10 +1801,10 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1695
1801
|
? {
|
|
1696
1802
|
id: event.id,
|
|
1697
1803
|
timestamp: event.timestamp,
|
|
1698
|
-
agent_id:
|
|
1699
|
-
agent_name:
|
|
1700
|
-
|
|
1701
|
-
|
|
1804
|
+
agent_id: thread.agent_id,
|
|
1805
|
+
agent_name: thread.agent_name,
|
|
1806
|
+
thread_id: thread.thread_id,
|
|
1807
|
+
thread_title: thread.title,
|
|
1702
1808
|
task_id: event.task_id || null,
|
|
1703
1809
|
category: logSummary.category,
|
|
1704
1810
|
summary: logSummary.summary,
|
|
@@ -1713,11 +1819,11 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1713
1819
|
generated_at: new Date().toISOString(),
|
|
1714
1820
|
timezone: resolveHostTimeZone(),
|
|
1715
1821
|
agents,
|
|
1716
|
-
|
|
1822
|
+
threads,
|
|
1717
1823
|
entries,
|
|
1718
1824
|
filters: {
|
|
1719
1825
|
agent_id: filters.agentId || null,
|
|
1720
|
-
|
|
1826
|
+
thread_id: filters.threadId || null,
|
|
1721
1827
|
},
|
|
1722
1828
|
};
|
|
1723
1829
|
}
|
|
@@ -2581,24 +2687,24 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2581
2687
|
}
|
|
2582
2688
|
if (pathname === '/api/computers/{computer_id}/logs' && method === 'GET') {
|
|
2583
2689
|
const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
|
|
2584
|
-
const
|
|
2690
|
+
const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
|
|
2585
2691
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2586
2692
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
|
|
2587
2693
|
writeJson(res, 200, collectSpaceLogEntries({
|
|
2588
2694
|
agentId: agentId?.trim() || null,
|
|
2589
|
-
|
|
2695
|
+
threadId: threadId?.trim() || null,
|
|
2590
2696
|
limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
|
|
2591
2697
|
}));
|
|
2592
2698
|
return true;
|
|
2593
2699
|
}
|
|
2594
2700
|
if (pathname === '/api/computers/{computer_id}/logs/stream' && method === 'GET') {
|
|
2595
2701
|
const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
|
|
2596
|
-
const
|
|
2702
|
+
const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
|
|
2597
2703
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2598
2704
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
|
|
2599
2705
|
const filters = {
|
|
2600
2706
|
agentId: agentId?.trim() || null,
|
|
2601
|
-
|
|
2707
|
+
threadId: threadId?.trim() || null,
|
|
2602
2708
|
limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
|
|
2603
2709
|
};
|
|
2604
2710
|
res.writeHead(200, {
|
|
@@ -2881,7 +2987,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2881
2987
|
});
|
|
2882
2988
|
return true;
|
|
2883
2989
|
}
|
|
2884
|
-
if (pathname === '/api/computers/{computer_id}/agents/codex/
|
|
2990
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/thread' && method === 'DELETE') {
|
|
2885
2991
|
const cmd = spawnSync('codex', ['logout'], {
|
|
2886
2992
|
encoding: 'utf8',
|
|
2887
2993
|
timeout: 15_000,
|
|
@@ -2976,10 +3082,10 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2976
3082
|
numericQueryParam(coerceSingleQueryParam(url.searchParams.get('after')));
|
|
2977
3083
|
const replayAfter = afterSequence ?? readThreadEventLogHead(threadId);
|
|
2978
3084
|
addThreadStreamClient(threadId, res);
|
|
2979
|
-
const
|
|
2980
|
-
let jsonlOffset =
|
|
3085
|
+
const threadPath = walkThreadFiles(path.join(localAgentHome(), 'threads')).get(threadId);
|
|
3086
|
+
let jsonlOffset = threadPath && fs.existsSync(threadPath) ? fs.statSync(threadPath).size : 0;
|
|
2981
3087
|
let jsonlEventIndex = 0;
|
|
2982
|
-
const workspacePath = readThreadWorkspacePath(
|
|
3088
|
+
const workspacePath = readThreadWorkspacePath(threadPath) || process.cwd();
|
|
2983
3089
|
const withThreadContext = (chunk) => attachThreadContextToChunk(chunk, readThreadContext({ threadId, workspace: workspacePath }));
|
|
2984
3090
|
const stopCodexObserver = process.env.NODE_ENV === 'test'
|
|
2985
3091
|
? () => { }
|
|
@@ -2993,9 +3099,9 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2993
3099
|
res.flush?.();
|
|
2994
3100
|
}, SPACE_LOG_STREAM_PING_MS);
|
|
2995
3101
|
pingTimer.unref?.();
|
|
2996
|
-
const jsonlPollTimer =
|
|
3102
|
+
const jsonlPollTimer = threadPath
|
|
2997
3103
|
? setInterval(() => {
|
|
2998
|
-
const read = readAppendedJsonlLines(
|
|
3104
|
+
const read = readAppendedJsonlLines(threadPath, jsonlOffset);
|
|
2999
3105
|
jsonlOffset = read.offset;
|
|
3000
3106
|
for (const line of read.lines) {
|
|
3001
3107
|
if (!isCodexHistoryTranscriptLine(line))
|
|
@@ -3062,12 +3168,12 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3062
3168
|
: '';
|
|
3063
3169
|
const fallbackAgentId = getAllAgents()[0]?.agent_id || 'codex';
|
|
3064
3170
|
const agentId = requestedAgentId || fallbackAgentId;
|
|
3065
|
-
const
|
|
3066
|
-
if (!
|
|
3171
|
+
const thread = threadId ? adoptMirroredThread(threadId, agentId) : null;
|
|
3172
|
+
if (!thread) {
|
|
3067
3173
|
writeProtocolError(res, 404, 'not_found', 'mirrored_thread_not_found', 'Mirrored task history was not found.');
|
|
3068
3174
|
return true;
|
|
3069
3175
|
}
|
|
3070
|
-
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(
|
|
3176
|
+
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(thread) });
|
|
3071
3177
|
return true;
|
|
3072
3178
|
}
|
|
3073
3179
|
if (codexProjectsOpenRoute(pathname) && method === 'POST') {
|