@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
|
@@ -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';
|
|
@@ -23,14 +23,18 @@ import { buildDaemonOpenApiSpec } from '../../channels/http-routes.js';
|
|
|
23
23
|
import { writeJson, writeProtocolError, readJsonBody } from '../response.js';
|
|
24
24
|
import { listRecentThreadEventsAfter, readThreadEventLogHead, } from '../../core/thread-event-log.js';
|
|
25
25
|
import { projectThreadRuntimeState } from '../../core/thread-runtime-state.js';
|
|
26
|
+
import { attachThreadContextToChunk, readThreadContext, } from '../../core/thread-context.js';
|
|
26
27
|
import { addThreadStreamClient, removeThreadStreamClient, writeThreadStreamEvent } from '../thread-stream.js';
|
|
27
28
|
const DEFAULT_TOKEN_TTL_MINUTES = 20;
|
|
28
29
|
const SPACE_LOG_EVENT_LIMIT = 200;
|
|
29
30
|
const SPACE_LOG_STREAM_POLL_MS = 1000;
|
|
30
31
|
const SPACE_LOG_STREAM_PING_MS = 15000;
|
|
31
32
|
const MIRRORED_THREAD_LIMIT = 80;
|
|
32
|
-
const MIRRORED_THREAD_HISTORY_LIMIT =
|
|
33
|
+
const MIRRORED_THREAD_HISTORY_LIMIT = 200;
|
|
33
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;
|
|
34
38
|
const MIRRORED_THREAD_PROJECTION_VERSION = 2;
|
|
35
39
|
const MIRRORED_THREAD_TEXT_LIMIT = 48_000;
|
|
36
40
|
const MIRRORED_THREAD_INLINE_DATA_URL_LIMIT = 120_000;
|
|
@@ -113,6 +117,9 @@ function truncateStreamDelta(value, max = 160) {
|
|
|
113
117
|
function currentSpaceId() {
|
|
114
118
|
return (process.env.SUPEN_SPACE_ID || '').trim() || 'local';
|
|
115
119
|
}
|
|
120
|
+
const recentJsonlLineCache = new Map();
|
|
121
|
+
const recentCodexHistoryLineCache = new Map();
|
|
122
|
+
const mirroredFileTextCache = new Map();
|
|
116
123
|
function summarizeStreamEvent(raw) {
|
|
117
124
|
const inner = raw.event && typeof raw.event === 'object'
|
|
118
125
|
? raw.event
|
|
@@ -383,13 +390,13 @@ function extractQuotaEvent(event) {
|
|
|
383
390
|
};
|
|
384
391
|
}
|
|
385
392
|
function readLatestSpaceQuotaStatus() {
|
|
386
|
-
const
|
|
387
|
-
.flatMap((agent) =>
|
|
393
|
+
const threads = getAllAgents()
|
|
394
|
+
.flatMap((agent) => getThreadsForAgent(agent.agent_id).map((thread) => ({
|
|
388
395
|
agent_id: agent.agent_id,
|
|
389
|
-
|
|
396
|
+
thread_id: thread.thread_id,
|
|
390
397
|
})));
|
|
391
|
-
const latest =
|
|
392
|
-
.flatMap((
|
|
398
|
+
const latest = threads
|
|
399
|
+
.flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, 200)
|
|
393
400
|
.map((event) => extractQuotaEvent(event)))
|
|
394
401
|
.filter((entry) => Boolean(entry))
|
|
395
402
|
.sort((a, b) => a.updated_at.localeCompare(b.updated_at))
|
|
@@ -413,17 +420,84 @@ function parseJsonLine(line) {
|
|
|
413
420
|
return null;
|
|
414
421
|
}
|
|
415
422
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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);
|
|
421
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);
|
|
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;
|
|
422
496
|
const readLength = Math.min(size, safeMaxBytes);
|
|
423
497
|
const start = size - readLength;
|
|
424
498
|
const fd = fs.openSync(filePath, 'r');
|
|
425
499
|
try {
|
|
426
|
-
const buffer = Buffer.
|
|
500
|
+
const buffer = Buffer.allocUnsafe(readLength);
|
|
427
501
|
const bytesRead = fs.readSync(fd, buffer, 0, readLength, start);
|
|
428
502
|
let text = buffer.subarray(0, bytesRead).toString('utf-8');
|
|
429
503
|
if (start > 0) {
|
|
@@ -432,7 +506,14 @@ export function readRecentJsonlLines(filePath, maxBytes = MIRRORED_THREAD_HISTOR
|
|
|
432
506
|
return [];
|
|
433
507
|
text = text.slice(firstLineEnd + 1);
|
|
434
508
|
}
|
|
435
|
-
|
|
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
|
+
});
|
|
436
517
|
}
|
|
437
518
|
finally {
|
|
438
519
|
fs.closeSync(fd);
|
|
@@ -519,10 +600,15 @@ function codexAppServerEventChunk(method, params = {}) {
|
|
|
519
600
|
},
|
|
520
601
|
};
|
|
521
602
|
}
|
|
522
|
-
function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes =
|
|
603
|
+
function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRRORED_THREAD_HISTORY_FAST_MAX_BYTES) {
|
|
523
604
|
const safeTargetMessages = Math.max(1, Math.min(Math.floor(targetMessages), 5_000));
|
|
524
605
|
const safeMaxBytes = Math.max(1, Math.min(Math.floor(maxBytes), MIRRORED_THREAD_HISTORY_MAX_BYTES));
|
|
525
|
-
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;
|
|
526
612
|
const fd = fs.openSync(filePath, 'r');
|
|
527
613
|
try {
|
|
528
614
|
let position = size;
|
|
@@ -530,7 +616,10 @@ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRROR
|
|
|
530
616
|
let leadingPartial = '';
|
|
531
617
|
let messageCount = 0;
|
|
532
618
|
let collected = [];
|
|
533
|
-
while (position > 0 &&
|
|
619
|
+
while (position > 0 &&
|
|
620
|
+
scannedBytes < safeMaxBytes &&
|
|
621
|
+
messageCount < safeTargetMessages &&
|
|
622
|
+
collected.length < MIRRORED_THREAD_HISTORY_FAST_MAX_LINES) {
|
|
534
623
|
const readLength = Math.min(MIRRORED_THREAD_HISTORY_CHUNK_BYTES, position, safeMaxBytes - scannedBytes);
|
|
535
624
|
position -= readLength;
|
|
536
625
|
scannedBytes += readLength;
|
|
@@ -547,17 +636,25 @@ function readRecentCodexHistoryLines(filePath, targetMessages, maxBytes = MIRROR
|
|
|
547
636
|
messageCount += historyLines.filter(isCodexVisibleUserHistoryLine).length;
|
|
548
637
|
collected = [...historyLines, ...collected];
|
|
549
638
|
}
|
|
550
|
-
|
|
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
|
+
});
|
|
551
648
|
}
|
|
552
649
|
finally {
|
|
553
650
|
fs.closeSync(fd);
|
|
554
651
|
}
|
|
555
652
|
}
|
|
556
653
|
function readThreadIndex(limit) {
|
|
557
|
-
const indexPath = path.join(localAgentHome(), '
|
|
654
|
+
const indexPath = path.join(localAgentHome(), 'thread_index.jsonl');
|
|
558
655
|
if (!fs.existsSync(indexPath))
|
|
559
656
|
return [];
|
|
560
|
-
const lines =
|
|
657
|
+
const lines = readRecentJsonlLines(indexPath);
|
|
561
658
|
const byId = new Map();
|
|
562
659
|
for (const line of lines) {
|
|
563
660
|
const parsed = parseJsonLine(line);
|
|
@@ -650,8 +747,8 @@ function readThreadStateEntries(limit) {
|
|
|
650
747
|
return [];
|
|
651
748
|
}
|
|
652
749
|
}
|
|
653
|
-
let
|
|
654
|
-
function
|
|
750
|
+
let mirroredThreadFileIndexCache = null;
|
|
751
|
+
function walkThreadFilesUncached(dir, out = new Map()) {
|
|
655
752
|
if (!fs.existsSync(dir))
|
|
656
753
|
return out;
|
|
657
754
|
let entries;
|
|
@@ -664,7 +761,7 @@ function walkSessionFilesUncached(dir, out = new Map()) {
|
|
|
664
761
|
for (const entry of entries) {
|
|
665
762
|
const fullPath = path.join(dir, entry.name);
|
|
666
763
|
if (entry.isDirectory()) {
|
|
667
|
-
|
|
764
|
+
walkThreadFilesUncached(fullPath, out);
|
|
668
765
|
continue;
|
|
669
766
|
}
|
|
670
767
|
if (!entry.isFile() || !entry.name.endsWith('.jsonl'))
|
|
@@ -675,15 +772,15 @@ function walkSessionFilesUncached(dir, out = new Map()) {
|
|
|
675
772
|
}
|
|
676
773
|
return out;
|
|
677
774
|
}
|
|
678
|
-
function
|
|
775
|
+
function walkThreadFiles(dir) {
|
|
679
776
|
const now = Date.now();
|
|
680
|
-
if (
|
|
681
|
-
|
|
682
|
-
now -
|
|
683
|
-
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);
|
|
684
781
|
}
|
|
685
|
-
const files =
|
|
686
|
-
|
|
782
|
+
const files = walkThreadFilesUncached(dir);
|
|
783
|
+
mirroredThreadFileIndexCache = {
|
|
687
784
|
root: dir,
|
|
688
785
|
indexedAt: now,
|
|
689
786
|
files: new Map(files),
|
|
@@ -691,24 +788,33 @@ function walkSessionFiles(dir) {
|
|
|
691
788
|
return files;
|
|
692
789
|
}
|
|
693
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;
|
|
694
796
|
const fd = fs.openSync(filePath, 'r');
|
|
695
797
|
try {
|
|
696
798
|
const buffer = Buffer.alloc(maxBytes);
|
|
697
799
|
const bytesRead = fs.readSync(fd, buffer, 0, maxBytes, 0);
|
|
698
|
-
return buffer.subarray(0, bytesRead).toString('utf-8');
|
|
800
|
+
return setCachedFileText(cacheKey, stat, buffer.subarray(0, bytesRead).toString('utf-8'));
|
|
699
801
|
}
|
|
700
802
|
finally {
|
|
701
803
|
fs.closeSync(fd);
|
|
702
804
|
}
|
|
703
805
|
}
|
|
704
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;
|
|
705
812
|
const fd = fs.openSync(filePath, 'r');
|
|
706
813
|
try {
|
|
707
|
-
const stat = fs.fstatSync(fd);
|
|
708
814
|
const length = Math.min(maxBytes, stat.size);
|
|
709
815
|
const buffer = Buffer.alloc(length);
|
|
710
816
|
fs.readSync(fd, buffer, 0, length, Math.max(0, stat.size - length));
|
|
711
|
-
return buffer.toString('utf-8');
|
|
817
|
+
return setCachedFileText(cacheKey, stat, buffer.toString('utf-8'));
|
|
712
818
|
}
|
|
713
819
|
finally {
|
|
714
820
|
fs.closeSync(fd);
|
|
@@ -751,7 +857,7 @@ function readThreadWorkspacePath(filePath) {
|
|
|
751
857
|
if (!line.trim())
|
|
752
858
|
continue;
|
|
753
859
|
const parsed = parseJsonLine(line);
|
|
754
|
-
if (parsed?.type !== '
|
|
860
|
+
if (parsed?.type !== 'thread_meta')
|
|
755
861
|
continue;
|
|
756
862
|
const payload = parsed.payload && typeof parsed.payload === 'object'
|
|
757
863
|
? parsed.payload
|
|
@@ -1008,7 +1114,7 @@ function readMirroredThreadStatus(threadId, filePath) {
|
|
|
1008
1114
|
return 'running';
|
|
1009
1115
|
}
|
|
1010
1116
|
catch {
|
|
1011
|
-
// Fall back to the Codex
|
|
1117
|
+
// Fall back to the Codex thread file status below.
|
|
1012
1118
|
}
|
|
1013
1119
|
return readThreadStatus(filePath);
|
|
1014
1120
|
}
|
|
@@ -1105,18 +1211,19 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1105
1211
|
const safeLimit = Math.max(1, Math.min(limit, 200));
|
|
1106
1212
|
const stateEntries = readThreadStateEntries(safeLimit);
|
|
1107
1213
|
const indexEntries = readThreadIndex(safeLimit);
|
|
1108
|
-
const
|
|
1214
|
+
const indexEntriesById = new Map(indexEntries.map((entry) => [entry.id, entry]));
|
|
1215
|
+
const threadFiles = walkThreadFiles(path.join(localAgentHome(), 'threads'));
|
|
1109
1216
|
const projects = new Map();
|
|
1110
1217
|
if (stateEntries.length > 0) {
|
|
1111
1218
|
for (const entry of stateEntries) {
|
|
1112
|
-
const
|
|
1113
|
-
if (!
|
|
1219
|
+
const threadPath = threadFiles.get(entry.id) || null;
|
|
1220
|
+
if (!threadPath)
|
|
1114
1221
|
continue;
|
|
1115
|
-
const workspacePath = entry.cwd || readThreadWorkspacePath(
|
|
1222
|
+
const workspacePath = entry.cwd || readThreadWorkspacePath(threadPath);
|
|
1116
1223
|
if (!isMirrorableCodexWorkspace(workspacePath))
|
|
1117
1224
|
continue;
|
|
1118
1225
|
const projectId = workspacePath || 'no-workspace';
|
|
1119
|
-
const indexEntry =
|
|
1226
|
+
const indexEntry = indexEntriesById.get(entry.id) || null;
|
|
1120
1227
|
const title = indexEntry?.thread_name || entry.title;
|
|
1121
1228
|
if (isAutomationRunThreadTitle(title))
|
|
1122
1229
|
continue;
|
|
@@ -1130,8 +1237,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1130
1237
|
id: entry.id,
|
|
1131
1238
|
title,
|
|
1132
1239
|
updated_at: entry.updated_at,
|
|
1133
|
-
status: readMirroredThreadStatus(entry.id,
|
|
1134
|
-
|
|
1240
|
+
status: readMirroredThreadStatus(entry.id, threadPath),
|
|
1241
|
+
thread_path: threadPath,
|
|
1135
1242
|
});
|
|
1136
1243
|
projects.set(projectId, project);
|
|
1137
1244
|
}
|
|
@@ -1144,8 +1251,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1144
1251
|
};
|
|
1145
1252
|
}
|
|
1146
1253
|
for (const entry of indexEntries) {
|
|
1147
|
-
const
|
|
1148
|
-
const workspacePath = readThreadWorkspacePath(
|
|
1254
|
+
const threadPath = threadFiles.get(entry.id) || null;
|
|
1255
|
+
const workspacePath = readThreadWorkspacePath(threadPath);
|
|
1149
1256
|
if (!isMirrorableCodexWorkspace(workspacePath))
|
|
1150
1257
|
continue;
|
|
1151
1258
|
if (isAutomationRunThreadTitle(entry.thread_name))
|
|
@@ -1161,8 +1268,8 @@ function readMirroredTaskProjects(limit = MIRRORED_THREAD_LIMIT) {
|
|
|
1161
1268
|
id: entry.id,
|
|
1162
1269
|
title: entry.thread_name,
|
|
1163
1270
|
updated_at: entry.updated_at,
|
|
1164
|
-
status: readMirroredThreadStatus(entry.id,
|
|
1165
|
-
|
|
1271
|
+
status: readMirroredThreadStatus(entry.id, threadPath),
|
|
1272
|
+
thread_path: threadPath,
|
|
1166
1273
|
});
|
|
1167
1274
|
projects.set(projectId, project);
|
|
1168
1275
|
}
|
|
@@ -1297,10 +1404,10 @@ function isMirroredGoalContextMessage(text) {
|
|
|
1297
1404
|
const trimmed = text.trim();
|
|
1298
1405
|
return trimmed.startsWith('<goal_context>') && trimmed.endsWith('</goal_context>');
|
|
1299
1406
|
}
|
|
1300
|
-
function mirroredThreadWorkspacePath(
|
|
1407
|
+
function mirroredThreadWorkspacePath(threadPath, stateEntry) {
|
|
1301
1408
|
const stateCwd = typeof stateEntry?.cwd === 'string' ? stateEntry.cwd : null;
|
|
1302
|
-
const
|
|
1303
|
-
for (const candidate of [stateCwd,
|
|
1409
|
+
const threadWorkspace = readThreadWorkspacePath(threadPath);
|
|
1410
|
+
for (const candidate of [stateCwd, threadWorkspace]) {
|
|
1304
1411
|
if (candidate && isMirrorableCodexWorkspace(candidate))
|
|
1305
1412
|
return candidate;
|
|
1306
1413
|
}
|
|
@@ -1354,17 +1461,17 @@ function collapseCompletedAssistantProgressMessages(input) {
|
|
|
1354
1461
|
.map(({ phase: _phase, internal: _internal, ...message }) => message);
|
|
1355
1462
|
}
|
|
1356
1463
|
export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY_LIMIT, options = {}) {
|
|
1357
|
-
const safeLimit = Math.max(1, Math.min(limit,
|
|
1358
|
-
const
|
|
1359
|
-
if (!
|
|
1464
|
+
const safeLimit = Math.max(1, Math.min(limit, 1000));
|
|
1465
|
+
const threadPath = walkThreadFiles(path.join(localAgentHome(), 'threads')).get(threadId);
|
|
1466
|
+
if (!threadPath)
|
|
1360
1467
|
return null;
|
|
1361
1468
|
const eventLogThreadId = options.eventLogThreadId || threadId;
|
|
1362
1469
|
const stateEntry = readThreadStateEntry(threadId);
|
|
1363
|
-
const workspacePath = mirroredThreadWorkspacePath(
|
|
1470
|
+
const workspacePath = mirroredThreadWorkspacePath(threadPath, stateEntry);
|
|
1364
1471
|
const indexEntry = readThreadIndexEntry(threadId);
|
|
1365
1472
|
const eventLogHead = readThreadEventLogHead(eventLogThreadId);
|
|
1366
|
-
const historyLineTarget = Math.min(
|
|
1367
|
-
const lines = readRecentCodexHistoryLines(
|
|
1473
|
+
const historyLineTarget = Math.min(250, Math.max(3, Math.ceil(safeLimit / 4)));
|
|
1474
|
+
const lines = readRecentCodexHistoryLines(threadPath, historyLineTarget);
|
|
1368
1475
|
const messages = [];
|
|
1369
1476
|
const events = [];
|
|
1370
1477
|
let messageIndex = 0;
|
|
@@ -1572,10 +1679,15 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1572
1679
|
const collapsedMessages = collapseCompletedAssistantProgressMessages(messages);
|
|
1573
1680
|
const slicedMessages = collapsedMessages.slice(-safeLimit);
|
|
1574
1681
|
const slicedEvents = events.slice(-safeLimit * 8);
|
|
1575
|
-
const status = readMirroredThreadStatus(eventLogThreadId,
|
|
1682
|
+
const status = readMirroredThreadStatus(eventLogThreadId, threadPath);
|
|
1576
1683
|
const latestGoalContext = recentGoalContext;
|
|
1684
|
+
const context = readThreadContext({
|
|
1685
|
+
threadId,
|
|
1686
|
+
eventLogThreadId,
|
|
1687
|
+
workspace: workspacePath,
|
|
1688
|
+
});
|
|
1577
1689
|
return {
|
|
1578
|
-
|
|
1690
|
+
thread: {
|
|
1579
1691
|
task_id: threadId,
|
|
1580
1692
|
agent_id: 'codex',
|
|
1581
1693
|
channel: 'local-agent',
|
|
@@ -1600,23 +1712,24 @@ export function readCodexThreadHistory(threadId, limit = MIRRORED_THREAD_HISTORY
|
|
|
1600
1712
|
updated_at: latestTimestamp || undefined,
|
|
1601
1713
|
},
|
|
1602
1714
|
},
|
|
1715
|
+
context,
|
|
1603
1716
|
has_more: collapsedMessages.length > safeLimit || lines.length >= historyLineTarget,
|
|
1604
1717
|
...(latestGoalContext ? { goal_context: latestGoalContext } : {}),
|
|
1605
1718
|
};
|
|
1606
1719
|
}
|
|
1607
|
-
function serializeAdoptedMirroredThread(
|
|
1720
|
+
function serializeAdoptedMirroredThread(thread) {
|
|
1608
1721
|
return {
|
|
1609
|
-
id:
|
|
1610
|
-
taskId:
|
|
1611
|
-
agentId:
|
|
1612
|
-
status:
|
|
1613
|
-
title:
|
|
1614
|
-
createdAt:
|
|
1615
|
-
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,
|
|
1616
1729
|
supen: {
|
|
1617
|
-
channel:
|
|
1618
|
-
sourceRef:
|
|
1619
|
-
taskWorkspaceFolder:
|
|
1730
|
+
channel: thread.channel,
|
|
1731
|
+
sourceRef: thread.source_ref,
|
|
1732
|
+
taskWorkspaceFolder: thread.task_workspace_folder,
|
|
1620
1733
|
},
|
|
1621
1734
|
};
|
|
1622
1735
|
}
|
|
@@ -1624,23 +1737,23 @@ function adoptMirroredThread(threadId, agentId) {
|
|
|
1624
1737
|
const history = readCodexThreadHistory(threadId, 1);
|
|
1625
1738
|
if (!history)
|
|
1626
1739
|
return null;
|
|
1627
|
-
const
|
|
1628
|
-
const
|
|
1740
|
+
const historyThread = history.thread;
|
|
1741
|
+
const thread = ensureThread({
|
|
1629
1742
|
agent_id: agentId,
|
|
1630
|
-
|
|
1743
|
+
thread_id: threadId,
|
|
1631
1744
|
channel: 'http',
|
|
1632
1745
|
agent_name: agentId,
|
|
1633
1746
|
source_ref: `local-agent:${threadId}`,
|
|
1634
|
-
title: typeof
|
|
1635
|
-
status:
|
|
1747
|
+
title: typeof historyThread.title === 'string' ? historyThread.title : undefined,
|
|
1748
|
+
status: historyThread.status === 'running' ? 'running' : 'idle',
|
|
1636
1749
|
backend_driver_id: 'codex-app-server',
|
|
1637
|
-
task_workspace_folder: typeof
|
|
1638
|
-
?
|
|
1750
|
+
task_workspace_folder: typeof historyThread.task_workspace_folder === 'string'
|
|
1751
|
+
? historyThread.task_workspace_folder
|
|
1639
1752
|
: null,
|
|
1640
1753
|
});
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
return
|
|
1754
|
+
updateThreadSdkSessionId(agentId, threadId, threadId);
|
|
1755
|
+
updateThreadBackendDriverId(agentId, threadId, 'codex-app-server');
|
|
1756
|
+
return thread;
|
|
1644
1757
|
}
|
|
1645
1758
|
function coerceSingleQueryParam(value) {
|
|
1646
1759
|
if (Array.isArray(value))
|
|
@@ -1663,24 +1776,24 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1663
1776
|
const agents = getAllAgents()
|
|
1664
1777
|
// Single-Computer runtime: include all agents in this mounted volume.
|
|
1665
1778
|
.sort((a, b) => (a.name || a.agent_id).localeCompare(b.name || b.agent_id));
|
|
1666
|
-
const
|
|
1667
|
-
.map((
|
|
1779
|
+
const threads = agents.flatMap((agent) => getThreadsForAgent(agent.agent_id)
|
|
1780
|
+
.map((thread) => ({
|
|
1668
1781
|
agent_id: agent.agent_id,
|
|
1669
1782
|
agent_name: agent.name || null,
|
|
1670
|
-
|
|
1671
|
-
status:
|
|
1672
|
-
updated_at:
|
|
1673
|
-
title:
|
|
1783
|
+
thread_id: thread.thread_id,
|
|
1784
|
+
status: thread.status,
|
|
1785
|
+
updated_at: thread.updated_at,
|
|
1786
|
+
title: thread.title || null,
|
|
1674
1787
|
})));
|
|
1675
|
-
const
|
|
1676
|
-
if (filters.agentId &&
|
|
1788
|
+
const filteredThreads = threads.filter((thread) => {
|
|
1789
|
+
if (filters.agentId && thread.agent_id !== filters.agentId)
|
|
1677
1790
|
return false;
|
|
1678
|
-
if (filters.
|
|
1791
|
+
if (filters.threadId && thread.thread_id !== filters.threadId)
|
|
1679
1792
|
return false;
|
|
1680
1793
|
return true;
|
|
1681
1794
|
});
|
|
1682
|
-
const entries =
|
|
1683
|
-
.flatMap((
|
|
1795
|
+
const entries = filteredThreads
|
|
1796
|
+
.flatMap((thread) => getThreadUiEvents(thread.agent_id, thread.thread_id, limit).map((event) => {
|
|
1684
1797
|
const logSummary = event.chunk && typeof event.chunk === 'object'
|
|
1685
1798
|
? summarizeUiChunk(event.chunk)
|
|
1686
1799
|
: null;
|
|
@@ -1688,10 +1801,10 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1688
1801
|
? {
|
|
1689
1802
|
id: event.id,
|
|
1690
1803
|
timestamp: event.timestamp,
|
|
1691
|
-
agent_id:
|
|
1692
|
-
agent_name:
|
|
1693
|
-
|
|
1694
|
-
|
|
1804
|
+
agent_id: thread.agent_id,
|
|
1805
|
+
agent_name: thread.agent_name,
|
|
1806
|
+
thread_id: thread.thread_id,
|
|
1807
|
+
thread_title: thread.title,
|
|
1695
1808
|
task_id: event.task_id || null,
|
|
1696
1809
|
category: logSummary.category,
|
|
1697
1810
|
summary: logSummary.summary,
|
|
@@ -1706,11 +1819,11 @@ function collectSpaceLogEntries(filters = {}) {
|
|
|
1706
1819
|
generated_at: new Date().toISOString(),
|
|
1707
1820
|
timezone: resolveHostTimeZone(),
|
|
1708
1821
|
agents,
|
|
1709
|
-
|
|
1822
|
+
threads,
|
|
1710
1823
|
entries,
|
|
1711
1824
|
filters: {
|
|
1712
1825
|
agent_id: filters.agentId || null,
|
|
1713
|
-
|
|
1826
|
+
thread_id: filters.threadId || null,
|
|
1714
1827
|
},
|
|
1715
1828
|
};
|
|
1716
1829
|
}
|
|
@@ -2574,24 +2687,24 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2574
2687
|
}
|
|
2575
2688
|
if (pathname === '/api/computers/{computer_id}/logs' && method === 'GET') {
|
|
2576
2689
|
const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
|
|
2577
|
-
const
|
|
2690
|
+
const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
|
|
2578
2691
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2579
2692
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
|
|
2580
2693
|
writeJson(res, 200, collectSpaceLogEntries({
|
|
2581
2694
|
agentId: agentId?.trim() || null,
|
|
2582
|
-
|
|
2695
|
+
threadId: threadId?.trim() || null,
|
|
2583
2696
|
limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
|
|
2584
2697
|
}));
|
|
2585
2698
|
return true;
|
|
2586
2699
|
}
|
|
2587
2700
|
if (pathname === '/api/computers/{computer_id}/logs/stream' && method === 'GET') {
|
|
2588
2701
|
const agentId = coerceSingleQueryParam(url.searchParams.get('agent_id'));
|
|
2589
|
-
const
|
|
2702
|
+
const threadId = coerceSingleQueryParam(url.searchParams.get('thread_id'));
|
|
2590
2703
|
const limitRaw = coerceSingleQueryParam(url.searchParams.get('limit'));
|
|
2591
2704
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : SPACE_LOG_EVENT_LIMIT;
|
|
2592
2705
|
const filters = {
|
|
2593
2706
|
agentId: agentId?.trim() || null,
|
|
2594
|
-
|
|
2707
|
+
threadId: threadId?.trim() || null,
|
|
2595
2708
|
limit: Number.isFinite(limit) ? limit : SPACE_LOG_EVENT_LIMIT,
|
|
2596
2709
|
};
|
|
2597
2710
|
res.writeHead(200, {
|
|
@@ -2874,7 +2987,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2874
2987
|
});
|
|
2875
2988
|
return true;
|
|
2876
2989
|
}
|
|
2877
|
-
if (pathname === '/api/computers/{computer_id}/agents/codex/
|
|
2990
|
+
if (pathname === '/api/computers/{computer_id}/agents/codex/thread' && method === 'DELETE') {
|
|
2878
2991
|
const cmd = spawnSync('codex', ['logout'], {
|
|
2879
2992
|
encoding: 'utf8',
|
|
2880
2993
|
timeout: 15_000,
|
|
@@ -2969,25 +3082,26 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2969
3082
|
numericQueryParam(coerceSingleQueryParam(url.searchParams.get('after')));
|
|
2970
3083
|
const replayAfter = afterSequence ?? readThreadEventLogHead(threadId);
|
|
2971
3084
|
addThreadStreamClient(threadId, res);
|
|
2972
|
-
const
|
|
2973
|
-
let jsonlOffset =
|
|
3085
|
+
const threadPath = walkThreadFiles(path.join(localAgentHome(), 'threads')).get(threadId);
|
|
3086
|
+
let jsonlOffset = threadPath && fs.existsSync(threadPath) ? fs.statSync(threadPath).size : 0;
|
|
2974
3087
|
let jsonlEventIndex = 0;
|
|
2975
|
-
const workspacePath = readThreadWorkspacePath(
|
|
3088
|
+
const workspacePath = readThreadWorkspacePath(threadPath) || process.cwd();
|
|
3089
|
+
const withThreadContext = (chunk) => attachThreadContextToChunk(chunk, readThreadContext({ threadId, workspace: workspacePath }));
|
|
2976
3090
|
const stopCodexObserver = process.env.NODE_ENV === 'test'
|
|
2977
3091
|
? () => { }
|
|
2978
3092
|
: startCodexThreadObserver({
|
|
2979
3093
|
threadId,
|
|
2980
3094
|
cwd: workspacePath,
|
|
2981
|
-
write: (chunk, id) => writeThreadStreamEvent(res, chunk, id ? { id } : {}),
|
|
3095
|
+
write: (chunk, id) => writeThreadStreamEvent(res, withThreadContext(chunk), id ? { id } : {}),
|
|
2982
3096
|
});
|
|
2983
3097
|
const pingTimer = setInterval(() => {
|
|
2984
3098
|
writeSse(res);
|
|
2985
3099
|
res.flush?.();
|
|
2986
3100
|
}, SPACE_LOG_STREAM_PING_MS);
|
|
2987
3101
|
pingTimer.unref?.();
|
|
2988
|
-
const jsonlPollTimer =
|
|
3102
|
+
const jsonlPollTimer = threadPath
|
|
2989
3103
|
? setInterval(() => {
|
|
2990
|
-
const read = readAppendedJsonlLines(
|
|
3104
|
+
const read = readAppendedJsonlLines(threadPath, jsonlOffset);
|
|
2991
3105
|
jsonlOffset = read.offset;
|
|
2992
3106
|
for (const line of read.lines) {
|
|
2993
3107
|
if (!isCodexHistoryTranscriptLine(line))
|
|
@@ -2998,7 +3112,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
2998
3112
|
if (isCodexJsonlMessageRecord(parsed))
|
|
2999
3113
|
continue;
|
|
3000
3114
|
jsonlEventIndex += 1;
|
|
3001
|
-
writeThreadStreamEvent(res, codexJsonlEventChunk(parsed), {
|
|
3115
|
+
writeThreadStreamEvent(res, withThreadContext(codexJsonlEventChunk(parsed)), {
|
|
3002
3116
|
id: `jsonl-${jsonlOffset}-${jsonlEventIndex}`,
|
|
3003
3117
|
});
|
|
3004
3118
|
}
|
|
@@ -3025,7 +3139,7 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3025
3139
|
const chunk = threadStreamChunkForEvent(event);
|
|
3026
3140
|
if (!chunk)
|
|
3027
3141
|
continue;
|
|
3028
|
-
writeThreadStreamEvent(res, chunk, { id: event.sequence });
|
|
3142
|
+
writeThreadStreamEvent(res, withThreadContext(chunk), { id: event.sequence });
|
|
3029
3143
|
}
|
|
3030
3144
|
res.flush?.();
|
|
3031
3145
|
return true;
|
|
@@ -3054,12 +3168,12 @@ export async function handleSystemRoutes(req, res, url, pathname, method) {
|
|
|
3054
3168
|
: '';
|
|
3055
3169
|
const fallbackAgentId = getAllAgents()[0]?.agent_id || 'codex';
|
|
3056
3170
|
const agentId = requestedAgentId || fallbackAgentId;
|
|
3057
|
-
const
|
|
3058
|
-
if (!
|
|
3171
|
+
const thread = threadId ? adoptMirroredThread(threadId, agentId) : null;
|
|
3172
|
+
if (!thread) {
|
|
3059
3173
|
writeProtocolError(res, 404, 'not_found', 'mirrored_thread_not_found', 'Mirrored task history was not found.');
|
|
3060
3174
|
return true;
|
|
3061
3175
|
}
|
|
3062
|
-
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(
|
|
3176
|
+
writeJson(res, 200, { thread: serializeAdoptedMirroredThread(thread) });
|
|
3063
3177
|
return true;
|
|
3064
3178
|
}
|
|
3065
3179
|
if (codexProjectsOpenRoute(pathname) && method === 'POST') {
|