@supen-ai/cli 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +222 -108
- 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;
|
|
463
|
+
}
|
|
464
|
+
cached.accessedAt = Date.now();
|
|
465
|
+
return cached.text;
|
|
466
|
+
}
|
|
467
|
+
function setCachedFileText(key, stat, text) {
|
|
468
|
+
if (mirroredFileTextCache.size >= MIRRORED_THREAD_JSONL_CACHE_LIMIT && !mirroredFileTextCache.has(key)) {
|
|
469
|
+
let oldestKey = null;
|
|
470
|
+
let oldestAccess = Number.POSITIVE_INFINITY;
|
|
471
|
+
for (const [candidateKey, candidate] of mirroredFileTextCache) {
|
|
472
|
+
if (candidate.accessedAt < oldestAccess) {
|
|
473
|
+
oldestAccess = candidate.accessedAt;
|
|
474
|
+
oldestKey = candidateKey;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (oldestKey)
|
|
478
|
+
mirroredFileTextCache.delete(oldestKey);
|
|
422
479
|
}
|
|
480
|
+
mirroredFileTextCache.set(key, {
|
|
481
|
+
size: stat.size,
|
|
482
|
+
mtimeMs: stat.mtimeMs,
|
|
483
|
+
text,
|
|
484
|
+
accessedAt: Date.now(),
|
|
485
|
+
});
|
|
486
|
+
return text;
|
|
487
|
+
}
|
|
488
|
+
export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES) {
|
|
489
|
+
const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
|
|
490
|
+
const stat = fs.statSync(filePath);
|
|
491
|
+
const cachedKey = jsonlCacheKey(['recent', filePath, safeMaxBytes]);
|
|
492
|
+
const cachedLines = getCachedJsonlLines(recentJsonlLineCache, cachedKey, stat);
|
|
493
|
+
if (cachedLines)
|
|
494
|
+
return cachedLines;
|
|
495
|
+
const size = stat.size;
|
|
423
496
|
const readLength = Math.min(size, safeMaxBytes);
|
|
424
497
|
const start = size - readLength;
|
|
425
498
|
const fd = fs.openSync(filePath, 'r');
|
|
426
499
|
try {
|
|
427
|
-
const buffer = Buffer.
|
|
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,28 @@ 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
|
|
654
|
+
const agentHome = localAgentHome();
|
|
655
|
+
const sessionIndexPath = path.join(agentHome, 'session_index.jsonl');
|
|
656
|
+
const threadIndexPath = path.join(agentHome, 'thread_index.jsonl');
|
|
657
|
+
const indexPath = fs.existsSync(sessionIndexPath) ? sessionIndexPath : threadIndexPath;
|
|
559
658
|
if (!fs.existsSync(indexPath))
|
|
560
659
|
return [];
|
|
561
|
-
const lines =
|
|
660
|
+
const lines = readRecentJsonlLines(indexPath);
|
|
562
661
|
const byId = new Map();
|
|
563
662
|
for (const line of lines) {
|
|
564
663
|
const parsed = parseJsonLine(line);
|
|
@@ -651,8 +750,8 @@ function readThreadStateEntries(limit) {
|
|
|
651
750
|
return [];
|
|
652
751
|
}
|
|
653
752
|
}
|
|
654
|
-
let
|
|
655
|
-
function
|
|
753
|
+
let mirroredThreadFileIndexCache = new Map();
|
|
754
|
+
function walkThreadFilesUncached(dir, out = new Map()) {
|
|
656
755
|
if (!fs.existsSync(dir))
|
|
657
756
|
return out;
|
|
658
757
|
let entries;
|
|
@@ -665,7 +764,7 @@ function walkSessionFilesUncached(dir, out = new Map()) {
|
|
|
665
764
|
for (const entry of entries) {
|
|
666
765
|
const fullPath = path.join(dir, entry.name);
|
|
667
766
|
if (entry.isDirectory()) {
|
|
668
|
-
|
|
767
|
+
walkThreadFilesUncached(fullPath, out);
|
|
669
768
|
continue;
|
|
670
769
|
}
|
|
671
770
|
if (!entry.isFile() || !entry.name.endsWith('.jsonl'))
|
|
@@ -676,40 +775,54 @@ function walkSessionFilesUncached(dir, out = new Map()) {
|
|
|
676
775
|
}
|
|
677
776
|
return out;
|
|
678
777
|
}
|
|
679
|
-
function
|
|
778
|
+
function walkThreadFiles(dir) {
|
|
680
779
|
const now = Date.now();
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
mirroredSessionFileIndexCache = {
|
|
688
|
-
root: dir,
|
|
780
|
+
const cached = mirroredThreadFileIndexCache.get(dir);
|
|
781
|
+
if (cached && now - cached.indexedAt <= MIRRORED_THREAD_SESSION_FILE_INDEX_TTL_MS) {
|
|
782
|
+
return new Map(cached.files);
|
|
783
|
+
}
|
|
784
|
+
const files = walkThreadFilesUncached(dir);
|
|
785
|
+
mirroredThreadFileIndexCache.set(dir, {
|
|
689
786
|
indexedAt: now,
|
|
690
787
|
files: new Map(files),
|
|
691
|
-
};
|
|
788
|
+
});
|
|
692
789
|
return files;
|
|
693
790
|
}
|
|
791
|
+
function codexThreadFiles() {
|
|
792
|
+
const agentHome = localAgentHome();
|
|
793
|
+
return new Map([
|
|
794
|
+
...walkThreadFiles(path.join(agentHome, 'threads')),
|
|
795
|
+
...walkThreadFiles(path.join(agentHome, 'sessions')),
|
|
796
|
+
]);
|
|
797
|
+
}
|
|
694
798
|
function readFileHead(filePath, maxBytes = 64 * 1024) {
|
|
799
|
+
const stat = fs.statSync(filePath);
|
|
800
|
+
const cacheKey = jsonlCacheKey(['head', filePath, maxBytes]);
|
|
801
|
+
const cached = getCachedFileText(cacheKey, stat);
|
|
802
|
+
if (cached !== null)
|
|
803
|
+
return cached;
|
|
695
804
|
const fd = fs.openSync(filePath, 'r');
|
|
696
805
|
try {
|
|
697
806
|
const buffer = Buffer.alloc(maxBytes);
|
|
698
807
|
const bytesRead = fs.readSync(fd, buffer, 0, maxBytes, 0);
|
|
699
|
-
return buffer.subarray(0, bytesRead).toString('utf-8');
|
|
808
|
+
return setCachedFileText(cacheKey, stat, buffer.subarray(0, bytesRead).toString('utf-8'));
|
|
700
809
|
}
|
|
701
810
|
finally {
|
|
702
811
|
fs.closeSync(fd);
|
|
703
812
|
}
|
|
704
813
|
}
|
|
705
814
|
function readFileTail(filePath, maxBytes = 256 * 1024) {
|
|
815
|
+
const stat = fs.statSync(filePath);
|
|
816
|
+
const cacheKey = jsonlCacheKey(['tail', filePath, maxBytes]);
|
|
817
|
+
const cached = getCachedFileText(cacheKey, stat);
|
|
818
|
+
if (cached !== null)
|
|
819
|
+
return cached;
|
|
706
820
|
const fd = fs.openSync(filePath, 'r');
|
|
707
821
|
try {
|
|
708
|
-
const stat = fs.fstatSync(fd);
|
|
709
822
|
const length = Math.min(maxBytes, stat.size);
|
|
710
823
|
const buffer = Buffer.alloc(length);
|
|
711
824
|
fs.readSync(fd, buffer, 0, length, Math.max(0, stat.size - length));
|
|
712
|
-
return buffer.toString('utf-8');
|
|
825
|
+
return setCachedFileText(cacheKey, stat, buffer.toString('utf-8'));
|
|
713
826
|
}
|
|
714
827
|
finally {
|
|
715
828
|
fs.closeSync(fd);
|
|
@@ -752,7 +865,7 @@ function readThreadWorkspacePath(filePath) {
|
|
|
752
865
|
if (!line.trim())
|
|
753
866
|
continue;
|
|
754
867
|
const parsed = parseJsonLine(line);
|
|
755
|
-
if (parsed?.type !== 'session_meta')
|
|
868
|
+
if (parsed?.type !== 'thread_meta' && parsed?.type !== 'session_meta')
|
|
756
869
|
continue;
|
|
757
870
|
const payload = parsed.payload && typeof parsed.payload === 'object'
|
|
758
871
|
? parsed.payload
|
|
@@ -1009,7 +1122,7 @@ function readMirroredThreadStatus(threadId, filePath) {
|
|
|
1009
1122
|
return 'running';
|
|
1010
1123
|
}
|
|
1011
1124
|
catch {
|
|
1012
|
-
// Fall back to the Codex
|
|
1125
|
+
// Fall back to the Codex thread file status below.
|
|
1013
1126
|
}
|
|
1014
1127
|
return readThreadStatus(filePath);
|
|
1015
1128
|
}
|
|
@@ -1106,18 +1219,19 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1106
1219
|
const safeLimit = Math.max(1, Math.min(limit, 200));
|
|
1107
1220
|
const stateEntries = readThreadStateEntries(safeLimit);
|
|
1108
1221
|
const indexEntries = readThreadIndex(safeLimit);
|
|
1109
|
-
const
|
|
1222
|
+
const indexEntriesById = new Map(indexEntries.map((entry) => [entry.id, entry]));
|
|
1223
|
+
const threadFiles = codexThreadFiles();
|
|
1110
1224
|
const projects = new Map();
|
|
1111
1225
|
if (stateEntries.length > 0) {
|
|
1112
1226
|
for (const entry of stateEntries) {
|
|
1113
|
-
const
|
|
1114
|
-
if (!
|
|
1227
|
+
const threadPath = threadFiles.get(entry.id) || null;
|
|
1228
|
+
if (!threadPath)
|
|
1115
1229
|
continue;
|
|
1116
|
-
const workspacePath = entry.cwd || readThreadWorkspacePath(
|
|
1230
|
+
const workspacePath = entry.cwd || readThreadWorkspacePath(threadPath);
|
|
1117
1231
|
if (!isMirrorableCodexWorkspace(workspacePath))
|
|
1118
1232
|
continue;
|
|
1119
1233
|
const projectId = workspacePath || 'no-workspace';
|
|
1120
|
-
const indexEntry =
|
|
1234
|
+
const indexEntry = indexEntriesById.get(entry.id) || null;
|
|
1121
1235
|
const title = indexEntry?.thread_name || entry.title;
|
|
1122
1236
|
if (isAutomationRunThreadTitle(title))
|
|
1123
1237
|
continue;
|
|
@@ -1131,8 +1245,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1131
1245
|
id: entry.id,
|
|
1132
1246
|
title,
|
|
1133
1247
|
updated_at: entry.updated_at,
|
|
1134
|
-
status: readMirroredThreadStatus(entry.id,
|
|
1135
|
-
|
|
1248
|
+
status: readMirroredThreadStatus(entry.id, threadPath),
|
|
1249
|
+
thread_path: threadPath,
|
|
1136
1250
|
});
|
|
1137
1251
|
projects.set(projectId, project);
|
|
1138
1252
|
}
|
|
@@ -1145,8 +1259,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1145
1259
|
};
|
|
1146
1260
|
}
|
|
1147
1261
|
for (const entry of indexEntries) {
|
|
1148
|
-
const
|
|
1149
|
-
const workspacePath = readThreadWorkspacePath(
|
|
1262
|
+
const threadPath = threadFiles.get(entry.id) || null;
|
|
1263
|
+
const workspacePath = readThreadWorkspacePath(threadPath);
|
|
1150
1264
|
if (!isMirrorableCodexWorkspace(workspacePath))
|
|
1151
1265
|
continue;
|
|
1152
1266
|
if (isAutomationRunThreadTitle(entry.thread_name))
|
|
@@ -1162,8 +1276,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1162
1276
|
id: entry.id,
|
|
1163
1277
|
title: entry.thread_name,
|
|
1164
1278
|
updated_at: entry.updated_at,
|
|
1165
|
-
status: readMirroredThreadStatus(entry.id,
|
|
1166
|
-
|
|
1279
|
+
status: readMirroredThreadStatus(entry.id, threadPath),
|
|
1280
|
+
thread_path: threadPath,
|
|
1167
1281
|
});
|
|
1168
1282
|
projects.set(projectId, project);
|
|
1169
1283
|
}
|
|
@@ -1298,10 +1412,10 @@ function isMirroredGoalContextMessage(text) {
|
|
|
1298
1412
|
const trimmed = text.trim();
|
|
1299
1413
|
return trimmed.startsWith('<goal_context>') && trimmed.endsWith('</goal_context>');
|
|
1300
1414
|
}
|
|
1301
|
-
function mirroredThreadWorkspacePath(
|
|
1415
|
+
function mirroredThreadWorkspacePath(threadPath, stateEntry) {
|
|
1302
1416
|
const stateCwd = typeof stateEntry?.cwd === 'string' ? stateEntry.cwd : null;
|
|
1303
|
-
const
|
|
1304
|
-
for (const candidate of [stateCwd,
|
|
1417
|
+
const threadWorkspace = readThreadWorkspacePath(threadPath);
|
|
1418
|
+
for (const candidate of [stateCwd, threadWorkspace]) {
|
|
1305
1419
|
if (candidate && isMirrorableCodexWorkspace(candidate))
|
|
1306
1420
|
return candidate;
|
|
1307
1421
|
}
|
|
@@ -1356,16 +1470,16 @@ function collapseCompletedAssistantProgressMessages(input) {
|
|
|
1356
1470
|
}
|
|
1357
1471
|
export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY_LIMIT, options = {}) {
|
|
1358
1472
|
const safeLimit = Math.max(1, Math.min(limit, 1000));
|
|
1359
|
-
const
|
|
1360
|
-
if (!
|
|
1473
|
+
const threadPath = codexThreadFiles().get(threadId);
|
|
1474
|
+
if (!threadPath)
|
|
1361
1475
|
return null;
|
|
1362
1476
|
const eventLogThreadId = options.eventLogThreadId || threadId;
|
|
1363
1477
|
const stateEntry = readThreadStateEntry(threadId);
|
|
1364
|
-
const workspacePath = mirroredThreadWorkspacePath(
|
|
1478
|
+
const workspacePath = mirroredThreadWorkspacePath(threadPath, stateEntry);
|
|
1365
1479
|
const indexEntry = readThreadIndexEntry(threadId);
|
|
1366
1480
|
const eventLogHead = readThreadEventLogHead(eventLogThreadId);
|
|
1367
1481
|
const historyLineTarget = Math.min(250, Math.max(3, Math.ceil(safeLimit / 4)));
|
|
1368
|
-
const lines = readRecentCodexHistoryLines(
|
|
1482
|
+
const lines = readRecentCodexHistoryLines(threadPath, historyLineTarget);
|
|
1369
1483
|
const messages = [];
|
|
1370
1484
|
const events = [];
|
|
1371
1485
|
let messageIndex = 0;
|
|
@@ -1573,7 +1687,7 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1573
1687
|
const collapsedMessages = collapseCompletedAssistantProgressMessages(messages);
|
|
1574
1688
|
const slicedMessages = collapsedMessages.slice(-safeLimit);
|
|
1575
1689
|
const slicedEvents = events.slice(-safeLimit * 8);
|
|
1576
|
-
const status = readMirroredThreadStatus(eventLogThreadId,
|
|
1690
|
+
const status = readMirroredThreadStatus(eventLogThreadId, threadPath);
|
|
1577
1691
|
const latestGoalContext = recentGoalContext;
|
|
1578
1692
|
const context = readThreadContext({
|
|
1579
1693
|
threadId,
|
|
@@ -1581,7 +1695,7 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1581
1695
|
workspace: workspacePath,
|
|
1582
1696
|
});
|
|
1583
1697
|
return {
|
|
1584
|
-
|
|
1698
|
+
thread: {
|
|
1585
1699
|
task_id: threadId,
|
|
1586
1700
|
agent_id: 'codex',
|
|
1587
1701
|
channel: 'local-agent',
|
|
@@ -1611,19 +1725,19 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1611
1725
|
...(latestGoalContext ? { goal_context: latestGoalContext } : {}),
|
|
1612
1726
|
};
|
|
1613
1727
|
}
|
|
1614
|
-
function serializeAdoptedMirroredThread(
|
|
1728
|
+
function serializeAdoptedMirroredThread(thread) {
|
|
1615
1729
|
return {
|
|
1616
|
-
id:
|
|
1617
|
-
taskId:
|
|
1618
|
-
agentId:
|
|
1619
|
-
status:
|
|
1620
|
-
title:
|
|
1621
|
-
createdAt:
|
|
1622
|
-
updatedAt:
|
|
1730
|
+
id: thread.thread_id,
|
|
1731
|
+
taskId: thread.thread_id,
|
|
1732
|
+
agentId: thread.agent_id,
|
|
1733
|
+
status: thread.status || 'idle',
|
|
1734
|
+
title: thread.title,
|
|
1735
|
+
createdAt: thread.created_at,
|
|
1736
|
+
updatedAt: thread.updated_at,
|
|
1623
1737
|
supen: {
|
|
1624
|
-
channel:
|
|
1625
|
-
sourceRef:
|
|
1626
|
-
taskWorkspaceFolder:
|
|
1738
|
+
channel: thread.channel,
|
|
1739
|
+
sourceRef: thread.source_ref,
|
|
1740
|
+
taskWorkspaceFolder: thread.task_workspace_folder,
|
|
1627
1741
|
},
|
|
1628
1742
|
};
|
|
1629
1743
|
}
|
|
@@ -1631,23 +1745,23 @@ function adoptMirroredThread(threadId, agentId) {
|
|
|
1631
1745
|
const history = readCodexThreadHistory(threadId, 1);
|
|
1632
1746
|
if (!history)
|
|
1633
1747
|
return null;
|
|
1634
|
-
const
|
|
1635
|
-
const
|
|
1748
|
+
const historyThread = history.thread;
|
|
1749
|
+
const thread = ensureThread({
|
|
1636
1750
|
agent_id: agentId,
|
|
1637
|
-
|
|
1751
|
+
thread_id: threadId,
|
|
1638
1752
|
channel: 'http',
|
|
1639
1753
|
agent_name: agentId,
|
|
1640
1754
|
source_ref: `local-agent:${threadId}`,
|
|
1641
|
-
title: typeof
|
|
1642
|
-
status:
|
|
1755
|
+
title: typeof historyThread.title === 'string' ? historyThread.title : undefined,
|
|
1756
|
+
status: historyThread.status === 'running' ? 'running' : 'idle',
|
|
1643
1757
|
backend_driver_id: 'codex-app-server',
|
|
1644
|
-
task_workspace_folder: typeof
|
|
1645
|
-
?
|
|
1758
|
+
task_workspace_folder: typeof historyThread.task_workspace_folder === 'string'
|
|
1759
|
+
? historyThread.task_workspace_folder
|
|
1646
1760
|
: null,
|
|
1647
1761
|
});
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
return
|
|
1762
|
+
updateThreadSdkSessionId(agentId, threadId, threadId);
|
|
1763
|
+
updateThreadBackendDriverId(agentId, threadId, 'codex-app-server');
|
|
1764
|
+
return thread;
|
|
1651
1765
|
}
|
|
1652
1766
|
function coerceSingleQueryParam(value) {
|
|
1653
1767
|
if (Array.isArray(value))
|
|
@@ -1670,24 +1784,24 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1670
1784
|
const agents = getAllAgents()
|
|
1671
1785
|
// Single-Computer runtime: include all agents in this mounted volume.
|
|
1672
1786
|
.sort((a, b) => (a.name || a.agent_id).localeCompare(b.name || b.agent_id));
|
|
1673
|
-
const
|
|
1674
|
-
.map((
|
|
1787
|
+
const threads = agents.flatMap((agent) => getThreadsForAgent(agent.agent_id)
|
|
1788
|
+
.map((thread) => ({
|
|
1675
1789
|
agent_id: agent.agent_id,
|
|
1676
1790
|
agent_name: agent.name || null,
|
|
1677
|
-
|
|
1678
|
-
status:
|
|
1679
|
-
updated_at:
|
|
1680
|
-
title:
|
|
1791
|
+
thread_id: thread.thread_id,
|
|
1792
|
+
status: thread.status,
|
|
1793
|
+
updated_at: thread.updated_at,
|
|
1794
|
+
title: thread.title || null,
|
|
1681
1795
|
})));
|
|
1682
|
-
const
|
|
1683
|
-
if (filters.agentId &&
|
|
1796
|
+
const filteredThreads = threads.filter((thread) => {
|
|
1797
|
+
if (filters.agentId && thread.agent_id !== filters.agentId)
|
|
1684
1798
|
return false;
|
|
1685
|
-
if (filters.
|
|
1799
|
+
if (filters.threadId && thread.thread_id !== filters.threadId)
|
|
1686
1800
|
return false;
|
|
1687
1801
|
return true;
|
|
1688
1802
|
});
|
|
1689
|
-
const entries =
|
|
1690
|
-
.flatMap((
|
|
1803
|
+
const entries = filteredThreads
|
|
1804
|
+
.flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, limit).map((event) => {
|
|
1691
1805
|
const logSummary = event.chunk && typeof event.chunk === 'object'
|
|
1692
1806
|
? summarizeUiChunk(event.chunk)
|
|
1693
1807
|
: null;
|
|
@@ -1695,10 +1809,10 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1695
1809
|
? {
|
|
1696
1810
|
id: event.id,
|
|
1697
1811
|
timestamp: event.timestamp,
|
|
1698
|
-
agent_id:
|
|
1699
|
-
agent_name:
|
|
1700
|
-
|
|
1701
|
-
|
|
1812
|
+
agent_id: thread.agent_id,
|
|
1813
|
+
agent_name: thread.agent_name,
|
|
1814
|
+
thread_id: thread.thread_id,
|
|
1815
|
+
thread_title: thread.title,
|
|
1702
1816
|
task_id: event.task_id || null,
|
|
1703
1817
|
category: logSummary.category,
|
|
1704
1818
|
summary: logSummary.summary,
|
|
@@ -1713,11 +1827,11 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1713
1827
|
generated_at: new Date().toISOString(),
|
|
1714
1828
|
timezone: resolveHostTimeZone(),
|
|
1715
1829
|
agents,
|
|
1716
|
-
|
|
1830
|
+
threads,
|
|
1717
1831
|
entries,
|
|
1718
1832
|
filters: {
|
|
1719
1833
|
agent_id: filters.agentId || null,
|
|
1720
|
-
|
|
1834
|
+
thread_id: filters.threadId || null,
|
|
1721
1835
|
},
|
|
1722
1836
|
};
|
|
1723
1837
|
}
|
|
@@ -2581,24 +2695,24 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2581
2695
|
}
|
|
2582
2696
|
if (pathname === '/api/computers/{computer_id}/logs' && method === 'GET') {
|
|
2583
2697
|
const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
|
|
2584
|
-
const
|
|
2698
|
+
const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
|
|
2585
2699
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2586
2700
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
|
|
2587
2701
|
writeJson(res, 200, collectSpaceLogEntries({
|
|
2588
2702
|
agentId: agentId?.trim() || null,
|
|
2589
|
-
|
|
2703
|
+
threadId: threadId?.trim() || null,
|
|
2590
2704
|
limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
|
|
2591
2705
|
}));
|
|
2592
2706
|
return true;
|
|
2593
2707
|
}
|
|
2594
2708
|
if (pathname === '/api/computers/{computer_id}/logs/stream' && method === 'GET') {
|
|
2595
2709
|
const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
|
|
2596
|
-
const
|
|
2710
|
+
const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
|
|
2597
2711
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2598
2712
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
|
|
2599
2713
|
const filters = {
|
|
2600
2714
|
agentId: agentId?.trim() || null,
|
|
2601
|
-
|
|
2715
|
+
threadId: threadId?.trim() || null,
|
|
2602
2716
|
limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
|
|
2603
2717
|
};
|
|
2604
2718
|
res.writeHead(200, {
|
|
@@ -2881,7 +2995,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2881
2995
|
});
|
|
2882
2996
|
return true;
|
|
2883
2997
|
}
|
|
2884
|
-
if (pathname === '/api/computers/{computer_id}/agents/codex/
|
|
2998
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/thread' && method === 'DELETE') {
|
|
2885
2999
|
const cmd = spawnSync('codex', ['logout'], {
|
|
2886
3000
|
encoding: 'utf8',
|
|
2887
3001
|
timeout: 15_000,
|
|
@@ -2976,10 +3090,10 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2976
3090
|
numericQueryParam(coerceSingleQueryParam(url.searchParams.get('after')));
|
|
2977
3091
|
const replayAfter = afterSequence ?? readThreadEventLogHead(threadId);
|
|
2978
3092
|
addThreadStreamClient(threadId, res);
|
|
2979
|
-
const
|
|
2980
|
-
let jsonlOffset =
|
|
3093
|
+
const threadPath = codexThreadFiles().get(threadId);
|
|
3094
|
+
let jsonlOffset = threadPath && fs.existsSync(threadPath) ? fs.statSync(threadPath).size : 0;
|
|
2981
3095
|
let jsonlEventIndex = 0;
|
|
2982
|
-
const workspacePath = readThreadWorkspacePath(
|
|
3096
|
+
const workspacePath = readThreadWorkspacePath(threadPath) || process.cwd();
|
|
2983
3097
|
const withThreadContext = (chunk) => attachThreadContextToChunk(chunk, readThreadContext({ threadId, workspace: workspacePath }));
|
|
2984
3098
|
const stopCodexObserver = process.env.NODE_ENV === 'test'
|
|
2985
3099
|
? () => { }
|
|
@@ -2993,9 +3107,9 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2993
3107
|
res.flush?.();
|
|
2994
3108
|
}, SPACE_LOG_STREAM_PING_MS);
|
|
2995
3109
|
pingTimer.unref?.();
|
|
2996
|
-
const jsonlPollTimer =
|
|
3110
|
+
const jsonlPollTimer = threadPath
|
|
2997
3111
|
? setInterval(() => {
|
|
2998
|
-
const read = readAppendedJsonlLines(
|
|
3112
|
+
const read = readAppendedJsonlLines(threadPath, jsonlOffset);
|
|
2999
3113
|
jsonlOffset = read.offset;
|
|
3000
3114
|
for (const line of read.lines) {
|
|
3001
3115
|
if (!isCodexHistoryTranscriptLine(line))
|
|
@@ -3062,12 +3176,12 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3062
3176
|
: '';
|
|
3063
3177
|
const fallbackAgentId = getAllAgents()[0]?.agent_id || 'codex';
|
|
3064
3178
|
const agentId = requestedAgentId || fallbackAgentId;
|
|
3065
|
-
const
|
|
3066
|
-
if (!
|
|
3179
|
+
const thread = threadId ? adoptMirroredThread(threadId, agentId) : null;
|
|
3180
|
+
if (!thread) {
|
|
3067
3181
|
writeProtocolError(res, 404, 'not_found', 'mirrored_thread_not_found', 'Mirrored task history was not found.');
|
|
3068
3182
|
return true;
|
|
3069
3183
|
}
|
|
3070
|
-
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(
|
|
3184
|
+
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(thread) });
|
|
3071
3185
|
return true;
|
|
3072
3186
|
}
|
|
3073
3187
|
if (codexProjectsOpenRoute(pathname) && method === 'POST') {
|