@axhub/genie 0.2.10 → 0.2.12
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/dist/api-docs.html +2 -2
- package/dist/assets/App-Clb2COtW.js +274 -0
- package/dist/assets/ImagePlaygroundPage-DqhMSbM8.js +106 -0
- package/dist/assets/ImagePlaygroundPage-MEn3NN80.css +1 -0
- package/dist/assets/ReviewApp-CDcLYe-u.js +1 -0
- package/dist/assets/{_basePickBy-DVVb07UV.js → _basePickBy-jUZsM51q.js} +1 -1
- package/dist/assets/{_baseUniq-BtbziL5G.js → _baseUniq-BXglE6_v.js} +1 -1
- package/dist/assets/{arc-BsCC8yBD.js → arc-D-oFCFBv.js} +1 -1
- package/dist/assets/{architectureDiagram-2XIMDMQ5-woFp6eNI.js → architectureDiagram-2XIMDMQ5-DC8bAnQt.js} +1 -1
- package/dist/assets/{blockDiagram-WCTKOSBZ-ya8VAc2k.js → blockDiagram-WCTKOSBZ-C4semIRc.js} +1 -1
- package/dist/assets/{c4Diagram-IC4MRINW-CY1dZmIZ.js → c4Diagram-IC4MRINW-FHj1QO3y.js} +1 -1
- package/dist/assets/channel-BF4woPXX.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-CR1lAd74.js → chunk-4BX2VUAB-D-LjsQ_s.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CP98WcFC.js → chunk-55IACEB6-DI3j_d7A.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-D9c7ijAB.js → chunk-FMBD7UC4-BEVnaLFN.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-DQAGYOn-.js → chunk-JSJVCQXG-CSxpcErk.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-BbTXiDq7.js → chunk-KX2RTZJC-BbuhDN4h.js} +1 -1
- package/dist/assets/{chunk-NQ4KR5QH-BI6AX0dr.js → chunk-NQ4KR5QH-C3x61XQa.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-DB3V2Ifo.js → chunk-QZHKN3VN-DxWOFtPh.js} +1 -1
- package/dist/assets/{chunk-WL4C6EOR-DhzTthv6.js → chunk-WL4C6EOR-Bt2OauD2.js} +1 -1
- package/dist/assets/classDiagram-VBA2DB6C-D2kHlnQ7.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-D2kHlnQ7.js +1 -0
- package/dist/assets/clone-CqBvwCJW.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-BQ09ZE2j.js → cose-bilkent-S5V4N54A-Dexadrue.js} +1 -1
- package/dist/assets/{dagre-KLK3FWXG-Dc2ueD_R.js → dagre-KLK3FWXG-F9U4X2xC.js} +1 -1
- package/dist/assets/{diagram-E7M64L7V-DP-LsQoL.js → diagram-E7M64L7V-B3V17aH3.js} +1 -1
- package/dist/assets/{diagram-IFDJBPK2-Cg6r42cB.js → diagram-IFDJBPK2-CdHAmLL1.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-aHsfoUZE.js → diagram-P4PSJMXO-CrTNfk8K.js} +1 -1
- package/dist/assets/{erDiagram-INFDFZHY-qBXJ4aAz.js → erDiagram-INFDFZHY-vDh9SWK9.js} +1 -1
- package/dist/assets/{flowDiagram-PKNHOUZH-D_13emJM.js → flowDiagram-PKNHOUZH-DpltMg7L.js} +1 -1
- package/dist/assets/{ganttDiagram-A5KZAMGK-BvIcOLwz.js → ganttDiagram-A5KZAMGK-COTk2xur.js} +1 -1
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-ad0vvNcU.js → gitGraphDiagram-K3NZZRJ6-BNV7bvvj.js} +1 -1
- package/dist/assets/{graph-CeJCMjan.js → graph-Dkeg9oys.js} +1 -1
- package/dist/assets/{highlighted-body-TPN3WLV5-B_novwSz.js → highlighted-body-TPN3WLV5-DaiQEBwR.js} +1 -1
- package/dist/assets/index-DgGmiqsP.css +1 -0
- package/dist/assets/index-DvA901Vs.js +2 -0
- package/dist/assets/{infoDiagram-LFFYTUFH-lOxAqb3m.js → infoDiagram-LFFYTUFH-CZioW3Gt.js} +1 -1
- package/dist/assets/{ishikawaDiagram-PHBUUO56-DIr-51gj.js → ishikawaDiagram-PHBUUO56-BbqR3i1B.js} +1 -1
- package/dist/assets/{journeyDiagram-4ABVD52K-CYcIW0ZU.js → journeyDiagram-4ABVD52K-wfb-WHzl.js} +1 -1
- package/dist/assets/{kanban-definition-K7BYSVSG-C1ZK616a.js → kanban-definition-K7BYSVSG-B3c4y3VN.js} +1 -1
- package/dist/assets/{layout-CI2RM-v6.js → layout-Xr9Z2VGF.js} +1 -1
- package/dist/assets/{linear-DE7bISck.js → linear-JBmzAJtl.js} +1 -1
- package/dist/assets/{mermaid-O7DHMXV3-XxAJo8EK.js → mermaid-O7DHMXV3-fDuyNLKe.js} +238 -220
- package/dist/assets/{mindmap-definition-YRQLILUH-Dz6EFjmn.js → mindmap-definition-YRQLILUH-B5NTN_jD.js} +1 -1
- package/dist/assets/{pieDiagram-SKSYHLDU-DPpEzUed.js → pieDiagram-SKSYHLDU-CuO98GVu.js} +1 -1
- package/dist/assets/{quadrantDiagram-337W2JSQ-xdoXNet7.js → quadrantDiagram-337W2JSQ-LL3f4vLf.js} +1 -1
- package/dist/assets/{requirementDiagram-Z7DCOOCP-DUq8H3CL.js → requirementDiagram-Z7DCOOCP-Di-2O6LH.js} +1 -1
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-CmqEUxRu.js → sankeyDiagram-WA2Y5GQK-9lHqrXqR.js} +1 -1
- package/dist/assets/{sequenceDiagram-2WXFIKYE-DhtXRNiH.js → sequenceDiagram-2WXFIKYE-BQu-SoGr.js} +1 -1
- package/dist/assets/{stateDiagram-RAJIS63D-Dj0HOlbN.js → stateDiagram-RAJIS63D-BUxvd2BC.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-CDVexTiR.js +1 -0
- package/dist/assets/{timeline-definition-YZTLITO2-DUuJzZB5.js → timeline-definition-YZTLITO2-oP47UEU6.js} +1 -1
- package/dist/assets/{treemap-KZPCXAKY-DpYBQ0qr.js → treemap-KZPCXAKY-BRjDo2aE.js} +1 -1
- package/dist/assets/{vendor-codemirror-CMHSJ_9p.js → vendor-codemirror-BiCeS-y4.js} +1 -1
- package/dist/assets/{vendor-react-xmA_f8ig.js → vendor-react-DVlYPmi3.js} +1 -1
- package/dist/assets/{vennDiagram-LZ73GAT5-DpePUyOd.js → vennDiagram-LZ73GAT5-DrRqcDqo.js} +1 -1
- package/dist/assets/{xychartDiagram-JWTSCODW-Cfp1I4_U.js → xychartDiagram-JWTSCODW-DUXrymAi.js} +1 -1
- package/dist/index.html +4 -4
- package/package.json +25 -6
- package/scripts/refresh-acp-default-capabilities.mjs +160 -0
- package/server/acp-runtime/client.js +1137 -181
- package/server/acp-runtime/command-overrides.js +48 -0
- package/server/acp-runtime/index.js +576 -16
- package/server/acp-runtime/registry.js +6 -4
- package/server/acp-runtime/session-store.js +235 -92
- package/server/database/db.js +12 -3
- package/server/external-agent/ws.js +212 -11
- package/server/index.js +156 -53
- package/server/projects-watcher-config.js +4 -0
- package/server/projects.js +485 -125
- package/server/routes/cc-connect.js +5 -4
- package/server/routes/codex.js +24 -0
- package/server/routes/commands.js +166 -10
- package/server/routes/runs.js +641 -0
- package/server/routes/session-core.js +357 -109
- package/server/session-core/eventStore.js +0 -121
- package/server/session-core/providerAdapters.js +644 -163
- package/server/session-core/providerDiscovery.js +66 -38
- package/server/session-core/runRegistry.js +244 -0
- package/server/session-core/runtimeState.js +75 -3
- package/server/session-core/runtimeWriter.js +132 -10
- package/server/utils/codexImagePlayground.js +479 -0
- package/server/utils/localTerminal.js +56 -0
- package/server/utils/shellCommand.js +70 -0
- package/shared/acpCapabilities.js +393 -0
- package/shared/acpDefaultCapabilities.generated.json +141 -0
- package/shared/conversationEvents.js +425 -121
- package/dist/assets/App-CYCCsgwf.js +0 -264
- package/dist/assets/ReviewApp-0srHIXwb.js +0 -1
- package/dist/assets/channel-BMhScXFe.js +0 -1
- package/dist/assets/classDiagram-VBA2DB6C-CMIxlWcT.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-CMIxlWcT.js +0 -1
- package/dist/assets/clone-BPqOt4r3.js +0 -1
- package/dist/assets/index-C514cLyb.js +0 -2
- package/dist/assets/index-h1DBl_g3.css +0 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-C9utf5gv.js +0 -1
package/server/projects.js
CHANGED
|
@@ -51,10 +51,11 @@ import os from 'os';
|
|
|
51
51
|
import path from 'path';
|
|
52
52
|
import readline from 'readline';
|
|
53
53
|
import crypto from 'crypto';
|
|
54
|
+
import { spawn } from 'child_process';
|
|
54
55
|
import { fileURLToPath } from 'url';
|
|
55
56
|
import { parseCodexTokenCountInfo } from './utils/codexTokenUsage.js';
|
|
56
57
|
import { CODEX_MODELS } from '../shared/modelConstants.js';
|
|
57
|
-
import { listAcpSessions } from './acp-runtime/session-store.js';
|
|
58
|
+
import { deleteAcpSessionRecord, listAcpSessions } from './acp-runtime/session-store.js';
|
|
58
59
|
import { mergeSessionLists } from './session-core/sessionListMerge.js';
|
|
59
60
|
|
|
60
61
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -244,6 +245,7 @@ const projectListCache = {
|
|
|
244
245
|
expiresAt: 0
|
|
245
246
|
};
|
|
246
247
|
const codexSessionFileCache = new Map();
|
|
248
|
+
let codexAppServerClientOverride = null;
|
|
247
249
|
const providerSessionLookupCache = {
|
|
248
250
|
codex: { key: null, data: null, promise: null, expiresAt: 0 },
|
|
249
251
|
gemini: { key: null, data: null, promise: null, expiresAt: 0 },
|
|
@@ -332,6 +334,309 @@ function cacheCodexSessionFilePath(sessionId, filePath) {
|
|
|
332
334
|
codexSessionFileCache.set(sessionId, filePath);
|
|
333
335
|
}
|
|
334
336
|
|
|
337
|
+
class CodexAppServerClient {
|
|
338
|
+
constructor({ command = process.env.CODEX_APP_SERVER_COMMAND || 'codex' } = {}) {
|
|
339
|
+
this.command = command;
|
|
340
|
+
this.process = null;
|
|
341
|
+
this.readline = null;
|
|
342
|
+
this.nextId = 1;
|
|
343
|
+
this.pending = new Map();
|
|
344
|
+
this.initialized = false;
|
|
345
|
+
this.initializePromise = null;
|
|
346
|
+
this.stopped = false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
start() {
|
|
350
|
+
if (this.process) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this.stopped = false;
|
|
355
|
+
this.process = spawn(this.command, ['app-server', '--listen', 'stdio://'], {
|
|
356
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
this.readline = readline.createInterface({
|
|
360
|
+
input: this.process.stdout,
|
|
361
|
+
crlfDelay: Infinity
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
this.readline.on('line', (line) => this.handleLine(line));
|
|
365
|
+
this.process.stderr.on('data', (buffer) => {
|
|
366
|
+
const text = String(buffer || '').trim().split('\n')[0]?.slice(0, 500);
|
|
367
|
+
if (text) {
|
|
368
|
+
console.warn(`[codex app-server] ${text}`);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const rejectPending = (error) => {
|
|
373
|
+
for (const pending of this.pending.values()) {
|
|
374
|
+
pending.reject(error);
|
|
375
|
+
}
|
|
376
|
+
this.pending.clear();
|
|
377
|
+
this.process = null;
|
|
378
|
+
this.readline = null;
|
|
379
|
+
this.initialized = false;
|
|
380
|
+
this.initializePromise = null;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
this.process.on('error', (error) => rejectPending(error));
|
|
384
|
+
this.process.on('exit', (code, signal) => {
|
|
385
|
+
if (this.stopped) {
|
|
386
|
+
rejectPending(new Error('codex app-server stopped'));
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
rejectPending(new Error(`codex app-server exited unexpectedly: ${code ?? signal ?? 'unknown'}`));
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
handleLine(line) {
|
|
394
|
+
if (!line.trim()) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let message;
|
|
399
|
+
try {
|
|
400
|
+
message = JSON.parse(line);
|
|
401
|
+
} catch (_) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (typeof message.id === 'number' && this.pending.has(message.id)) {
|
|
406
|
+
const pending = this.pending.get(message.id);
|
|
407
|
+
this.pending.delete(message.id);
|
|
408
|
+
if (message.error) {
|
|
409
|
+
pending.reject(new Error(message.error.message || JSON.stringify(message.error)));
|
|
410
|
+
} else {
|
|
411
|
+
pending.resolve(message.result);
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (typeof message.id === 'number' && typeof message.method === 'string') {
|
|
417
|
+
this.send({
|
|
418
|
+
jsonrpc: '2.0',
|
|
419
|
+
id: message.id,
|
|
420
|
+
result: {}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
send(payload) {
|
|
426
|
+
this.start();
|
|
427
|
+
if (!this.process?.stdin?.writable) {
|
|
428
|
+
throw new Error('codex app-server is not writable');
|
|
429
|
+
}
|
|
430
|
+
this.process.stdin.write(`${JSON.stringify(payload)}\n`);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
call(method, params) {
|
|
434
|
+
this.start();
|
|
435
|
+
const id = this.nextId++;
|
|
436
|
+
return new Promise((resolve, reject) => {
|
|
437
|
+
this.pending.set(id, { resolve, reject });
|
|
438
|
+
this.send({
|
|
439
|
+
jsonrpc: '2.0',
|
|
440
|
+
id,
|
|
441
|
+
method,
|
|
442
|
+
params
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async ensureInitialized() {
|
|
448
|
+
if (this.initialized) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (this.initializePromise) {
|
|
452
|
+
await this.initializePromise;
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
this.initializePromise = this.call('initialize', {
|
|
457
|
+
clientInfo: {
|
|
458
|
+
name: 'axhub-genie',
|
|
459
|
+
version: '0.2.11'
|
|
460
|
+
},
|
|
461
|
+
capabilities: {
|
|
462
|
+
experimentalApi: true
|
|
463
|
+
}
|
|
464
|
+
}).then(() => {
|
|
465
|
+
this.send({
|
|
466
|
+
jsonrpc: '2.0',
|
|
467
|
+
method: 'initialized'
|
|
468
|
+
});
|
|
469
|
+
this.initialized = true;
|
|
470
|
+
}).finally(() => {
|
|
471
|
+
this.initializePromise = null;
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
await this.initializePromise;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async listThreads({ cwd, limit = 5, cursor = null } = {}) {
|
|
478
|
+
await this.ensureInitialized();
|
|
479
|
+
return this.call('thread/list', {
|
|
480
|
+
archived: false,
|
|
481
|
+
sortKey: 'updated_at',
|
|
482
|
+
modelProviders: [],
|
|
483
|
+
cwd,
|
|
484
|
+
limit,
|
|
485
|
+
cursor: cursor || null
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
stop() {
|
|
490
|
+
this.stopped = true;
|
|
491
|
+
this.readline?.close();
|
|
492
|
+
this.process?.kill();
|
|
493
|
+
this.process = null;
|
|
494
|
+
this.readline = null;
|
|
495
|
+
this.pending.clear();
|
|
496
|
+
this.initialized = false;
|
|
497
|
+
this.initializePromise = null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
let codexAppServerClient = null;
|
|
502
|
+
|
|
503
|
+
function getCodexAppServerClient() {
|
|
504
|
+
if (codexAppServerClientOverride) {
|
|
505
|
+
return codexAppServerClientOverride;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (!codexAppServerClient) {
|
|
509
|
+
codexAppServerClient = new CodexAppServerClient();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return codexAppServerClient;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function setCodexAppServerClientForTests(client) {
|
|
516
|
+
if (!client) {
|
|
517
|
+
codexAppServerClient?.stop?.();
|
|
518
|
+
codexAppServerClient = null;
|
|
519
|
+
}
|
|
520
|
+
codexAppServerClientOverride = client;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function normalizeCodexAppServerTimestamp(updatedAt) {
|
|
524
|
+
const timestamp = Number(updatedAt);
|
|
525
|
+
if (!Number.isFinite(timestamp) || timestamp <= 0) {
|
|
526
|
+
return new Date(0);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return new Date(timestamp * 1000);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function mapCodexAppServerThreadToSession(thread) {
|
|
533
|
+
if (!thread || typeof thread !== 'object' || typeof thread.id !== 'string' || !thread.id.trim()) {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const session = {
|
|
538
|
+
id: thread.id.trim(),
|
|
539
|
+
summary: typeof thread.preview === 'string' && thread.preview.trim() ? thread.preview.trim() : 'Codex Session',
|
|
540
|
+
messageCount: 0,
|
|
541
|
+
lastActivity: normalizeCodexAppServerTimestamp(thread.updatedAt),
|
|
542
|
+
cwd: typeof thread.cwd === 'string' ? thread.cwd : '',
|
|
543
|
+
model: null,
|
|
544
|
+
filePath: typeof thread.path === 'string' && thread.path.trim() ? thread.path.trim() : null,
|
|
545
|
+
provider: 'codex',
|
|
546
|
+
source: 'app-server'
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
if (session.filePath) {
|
|
550
|
+
cacheCodexSessionFilePath(session.id, session.filePath);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return session;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async function getCodexSessionsFromAppServer(projectPath, options = {}) {
|
|
557
|
+
const { limit = 5 } = options;
|
|
558
|
+
const normalizedProjectPath = normalizeComparableProjectPath(projectPath);
|
|
559
|
+
if (!normalizedProjectPath) {
|
|
560
|
+
return [];
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const payload = await getCodexAppServerClient().listThreads({
|
|
564
|
+
cwd: projectPath,
|
|
565
|
+
limit,
|
|
566
|
+
cursor: null
|
|
567
|
+
});
|
|
568
|
+
const threads = Array.isArray(payload?.data) ? payload.data : [];
|
|
569
|
+
|
|
570
|
+
return threads
|
|
571
|
+
.map(mapCodexAppServerThreadToSession)
|
|
572
|
+
.filter(Boolean)
|
|
573
|
+
.filter((session) => normalizeComparableProjectPath(session.cwd) === normalizedProjectPath)
|
|
574
|
+
.slice(0, limit > 0 ? limit : undefined);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
async function findCodexAppServerSessionById(sessionId, options = {}) {
|
|
578
|
+
const normalizedSessionId = trimText(sessionId);
|
|
579
|
+
if (!normalizedSessionId) {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const { limit = 50 } = options;
|
|
584
|
+
let cursor = null;
|
|
585
|
+
|
|
586
|
+
do {
|
|
587
|
+
const payload = await getCodexAppServerClient().listThreads({
|
|
588
|
+
cwd: undefined,
|
|
589
|
+
limit,
|
|
590
|
+
cursor
|
|
591
|
+
});
|
|
592
|
+
const threads = Array.isArray(payload?.data) ? payload.data : [];
|
|
593
|
+
const matchedThread = threads.find((thread) => trimText(thread?.id) === normalizedSessionId);
|
|
594
|
+
|
|
595
|
+
if (matchedThread) {
|
|
596
|
+
return mapCodexAppServerThreadToSession(matchedThread);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
cursor = payload?.nextCursor || null;
|
|
600
|
+
} while (cursor);
|
|
601
|
+
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async function getCodexSessionsFromJsonl(projectPath, options = {}) {
|
|
606
|
+
const { limit = 5 } = options;
|
|
607
|
+
const sessions = [];
|
|
608
|
+
const normalizedProjectPath = normalizeComparableProjectPath(projectPath);
|
|
609
|
+
const jsonlFiles = await findFilesRecursively(
|
|
610
|
+
getCodexSessionsDir(),
|
|
611
|
+
(entryName) => entryName.endsWith('.jsonl')
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
for (const filePath of jsonlFiles) {
|
|
615
|
+
try {
|
|
616
|
+
const sessionData = await parseCodexSessionFile(filePath);
|
|
617
|
+
|
|
618
|
+
if (sessionData && normalizeComparableProjectPath(sessionData.cwd) === normalizedProjectPath) {
|
|
619
|
+
cacheCodexSessionFilePath(sessionData.id, filePath);
|
|
620
|
+
sessions.push({
|
|
621
|
+
id: sessionData.id,
|
|
622
|
+
summary: sessionData.summary || 'Codex Session',
|
|
623
|
+
messageCount: sessionData.messageCount || 0,
|
|
624
|
+
lastActivity: sessionData.timestamp ? new Date(sessionData.timestamp) : new Date(),
|
|
625
|
+
cwd: sessionData.cwd,
|
|
626
|
+
model: sessionData.model,
|
|
627
|
+
filePath: filePath,
|
|
628
|
+
provider: 'codex'
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
} catch (error) {
|
|
632
|
+
console.warn(`Could not parse Codex session file ${filePath}:`, error.message);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
sessions.sort((a, b) => new Date(b.lastActivity) - new Date(a.lastActivity));
|
|
637
|
+
return limit > 0 ? sessions.slice(0, limit) : sessions;
|
|
638
|
+
}
|
|
639
|
+
|
|
335
640
|
function findCodexSessionFilePathBySessionIdHint(sessionId, filePaths = []) {
|
|
336
641
|
const normalizedSessionId = String(sessionId || '').trim();
|
|
337
642
|
if (!normalizedSessionId || !Array.isArray(filePaths) || filePaths.length === 0) {
|
|
@@ -799,8 +1104,7 @@ async function getProjectsList(progressCallback = null) {
|
|
|
799
1104
|
return cloneProjectList(await projectListCache.promise);
|
|
800
1105
|
}
|
|
801
1106
|
|
|
802
|
-
async function buildProjectFromDefinition(definition
|
|
803
|
-
const normalizedProjectPath = normalizeComparableProjectPath(definition.fullPath);
|
|
1107
|
+
async function buildProjectFromDefinition(definition) {
|
|
804
1108
|
const project = {
|
|
805
1109
|
name: definition.name,
|
|
806
1110
|
path: definition.path,
|
|
@@ -819,66 +1123,21 @@ async function buildProjectFromDefinition(definition, providerLookups = null) {
|
|
|
819
1123
|
};
|
|
820
1124
|
|
|
821
1125
|
try {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
);
|
|
838
|
-
project.opencodeSessions = mergeSessionLists(
|
|
839
|
-
providerLookups.opencodeSessionsByProjectPath?.get(normalizedProjectPath) || [],
|
|
840
|
-
opencodeAcpSessions,
|
|
841
|
-
{ fallbackProvider: 'opencode' }
|
|
842
|
-
);
|
|
843
|
-
project.geminiSessions = mergeSessionLists(
|
|
844
|
-
providerLookups.geminiSessionsByProjectPath?.get(normalizedProjectPath) || [],
|
|
845
|
-
geminiAcpSessions,
|
|
846
|
-
{ fallbackProvider: 'gemini' }
|
|
847
|
-
);
|
|
848
|
-
} else {
|
|
849
|
-
const [codexSessions, opencodeSessions, geminiSessions, acpSessions] = await Promise.all([
|
|
850
|
-
getCodexSessions(definition.fullPath, { limit: 5 }),
|
|
851
|
-
getOpencodeSessions(definition.fullPath, { limit: 5 }),
|
|
852
|
-
getGeminiSessions(definition.fullPath, { limit: 5 }),
|
|
853
|
-
listAcpSessions({ projectPath: definition.fullPath })
|
|
854
|
-
]);
|
|
855
|
-
claudeAcpSessions = acpSessions.filter((session) => session.provider === 'claude');
|
|
856
|
-
codexAcpSessions = acpSessions.filter((session) => session.provider === 'codex');
|
|
857
|
-
opencodeAcpSessions = acpSessions.filter((session) => session.provider === 'opencode');
|
|
858
|
-
geminiAcpSessions = acpSessions.filter((session) => session.provider === 'gemini');
|
|
859
|
-
project.codexSessions = mergeSessionLists(codexSessions, codexAcpSessions, { fallbackProvider: 'codex' });
|
|
860
|
-
project.opencodeSessions = mergeSessionLists(opencodeSessions, opencodeAcpSessions, { fallbackProvider: 'opencode' });
|
|
861
|
-
project.geminiSessions = mergeSessionLists(geminiSessions, geminiAcpSessions, { fallbackProvider: 'gemini' });
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
if (!definition.isManuallyAdded) {
|
|
865
|
-
const sessionResult = await getSessions(definition.name, 5, 0);
|
|
866
|
-
project.sessions = mergeSessionLists(
|
|
867
|
-
(sessionResult.sessions || []).map((session) => ({
|
|
868
|
-
...session,
|
|
869
|
-
provider: 'claude',
|
|
870
|
-
source: session?.source || 'legacy'
|
|
871
|
-
})),
|
|
872
|
-
claudeAcpSessions,
|
|
873
|
-
{ fallbackProvider: 'claude' }
|
|
874
|
-
);
|
|
875
|
-
project.sessionMeta = {
|
|
876
|
-
hasMore: sessionResult.hasMore,
|
|
877
|
-
total: sessionResult.total
|
|
878
|
-
};
|
|
879
|
-
} else {
|
|
880
|
-
project.sessions = mergeSessionLists([], claudeAcpSessions, { fallbackProvider: 'claude' });
|
|
881
|
-
}
|
|
1126
|
+
const [claudeSessions, codexSessions, opencodeSessions, geminiSessions] = await Promise.all([
|
|
1127
|
+
getLocalProviderSessions({ provider: 'claude', projectName: definition.name, projectPath: definition.fullPath, limit: 50 }),
|
|
1128
|
+
getLocalProviderSessions({ provider: 'codex', projectName: definition.name, projectPath: definition.fullPath, limit: 50 }),
|
|
1129
|
+
getLocalProviderSessions({ provider: 'opencode', projectName: definition.name, projectPath: definition.fullPath, limit: 50 }),
|
|
1130
|
+
getLocalProviderSessions({ provider: 'gemini', projectName: definition.name, projectPath: definition.fullPath, limit: 50 })
|
|
1131
|
+
]);
|
|
1132
|
+
|
|
1133
|
+
project.sessions = claudeSessions;
|
|
1134
|
+
project.codexSessions = codexSessions;
|
|
1135
|
+
project.opencodeSessions = opencodeSessions;
|
|
1136
|
+
project.geminiSessions = geminiSessions;
|
|
1137
|
+
project.sessionMeta = {
|
|
1138
|
+
hasMore: false,
|
|
1139
|
+
total: claudeSessions.length
|
|
1140
|
+
};
|
|
882
1141
|
} catch (error) {
|
|
883
1142
|
console.warn(`Could not load session details for project ${definition.name}:`, error.message);
|
|
884
1143
|
}
|
|
@@ -901,32 +1160,8 @@ async function getProjects(progressCallback = null) {
|
|
|
901
1160
|
const projects = [];
|
|
902
1161
|
const { projectDefinitions, totalProjects } = await collectProjectDefinitions(progressCallback);
|
|
903
1162
|
|
|
904
|
-
const uniqueProjectPaths = Array.from(new Set(
|
|
905
|
-
projectDefinitions
|
|
906
|
-
.map((definition) => normalizeComparableProjectPath(definition.fullPath))
|
|
907
|
-
.filter(Boolean)
|
|
908
|
-
));
|
|
909
|
-
|
|
910
|
-
const [codexSessionsByProjectPath, geminiSessionsByProjectPath, opencodeSessionsByProjectPath, claudeAcpSessionsByProjectPath, codexAcpSessionsByProjectPath, geminiAcpSessionsByProjectPath, opencodeAcpSessionsByProjectPath] = await Promise.all([
|
|
911
|
-
buildCodexSessionsLookup(uniqueProjectPaths, { limit: 5 }),
|
|
912
|
-
buildGeminiSessionsLookup(uniqueProjectPaths, { limit: 5 }),
|
|
913
|
-
buildOpencodeSessionsLookup(uniqueProjectPaths, { limit: 5 }),
|
|
914
|
-
buildAcpProviderSessionsLookup('claude', uniqueProjectPaths, { limit: 5 }),
|
|
915
|
-
buildAcpProviderSessionsLookup('codex', uniqueProjectPaths, { limit: 5 }),
|
|
916
|
-
buildAcpProviderSessionsLookup('gemini', uniqueProjectPaths, { limit: 5 }),
|
|
917
|
-
buildAcpProviderSessionsLookup('opencode', uniqueProjectPaths, { limit: 5 })
|
|
918
|
-
]);
|
|
919
|
-
|
|
920
1163
|
for (const definition of projectDefinitions) {
|
|
921
|
-
projects.push(await buildProjectFromDefinition(definition
|
|
922
|
-
claudeAcpSessionsByProjectPath,
|
|
923
|
-
codexSessionsByProjectPath,
|
|
924
|
-
codexAcpSessionsByProjectPath,
|
|
925
|
-
geminiSessionsByProjectPath,
|
|
926
|
-
geminiAcpSessionsByProjectPath,
|
|
927
|
-
opencodeSessionsByProjectPath,
|
|
928
|
-
opencodeAcpSessionsByProjectPath
|
|
929
|
-
}));
|
|
1164
|
+
projects.push(await buildProjectFromDefinition(definition));
|
|
930
1165
|
}
|
|
931
1166
|
|
|
932
1167
|
// Emit completion after all projects (including manual) are processed
|
|
@@ -1378,12 +1613,20 @@ async function deleteSession(projectName, sessionId) {
|
|
|
1378
1613
|
|
|
1379
1614
|
// Write back the filtered content
|
|
1380
1615
|
await fs.writeFile(jsonlFile, filteredLines.join('\n') + (filteredLines.length > 0 ? '\n' : ''));
|
|
1616
|
+
await deleteAcpSessionRecord('claude', sessionId).catch(() => {});
|
|
1381
1617
|
return true;
|
|
1382
1618
|
}
|
|
1383
1619
|
}
|
|
1384
|
-
|
|
1620
|
+
|
|
1621
|
+
if (await deleteAcpSessionRecord('claude', sessionId).catch(() => false)) {
|
|
1622
|
+
return true;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1385
1625
|
throw new Error(`Session ${sessionId} not found in any files`);
|
|
1386
1626
|
} catch (error) {
|
|
1627
|
+
if (await deleteAcpSessionRecord('claude', sessionId).catch(() => false)) {
|
|
1628
|
+
return true;
|
|
1629
|
+
}
|
|
1387
1630
|
console.error(`Error deleting session ${sessionId} from project ${projectName}:`, error);
|
|
1388
1631
|
throw error;
|
|
1389
1632
|
}
|
|
@@ -1533,47 +1776,27 @@ async function addProjectManually(projectPath, displayName = null) {
|
|
|
1533
1776
|
}
|
|
1534
1777
|
|
|
1535
1778
|
async function getCodexSessions(projectPath, options = {}) {
|
|
1536
|
-
const { limit = 5 } = options;
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
const jsonlFiles = await findFilesRecursively(
|
|
1541
|
-
getCodexSessionsDir(),
|
|
1542
|
-
(entryName) => entryName.endsWith('.jsonl')
|
|
1543
|
-
);
|
|
1544
|
-
|
|
1545
|
-
// Process each file to find sessions matching the project path
|
|
1546
|
-
for (const filePath of jsonlFiles) {
|
|
1547
|
-
try {
|
|
1548
|
-
const sessionData = await parseCodexSessionFile(filePath);
|
|
1779
|
+
const { limit = 5, localOnly = false } = options;
|
|
1780
|
+
if (localOnly) {
|
|
1781
|
+
return getCodexSessionsFromJsonl(projectPath, { limit });
|
|
1782
|
+
}
|
|
1549
1783
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
summary: sessionData.summary || 'Codex Session',
|
|
1555
|
-
messageCount: sessionData.messageCount || 0,
|
|
1556
|
-
lastActivity: sessionData.timestamp ? new Date(sessionData.timestamp) : new Date(),
|
|
1557
|
-
cwd: sessionData.cwd,
|
|
1558
|
-
model: sessionData.model,
|
|
1559
|
-
filePath: filePath,
|
|
1560
|
-
provider: 'codex'
|
|
1561
|
-
});
|
|
1562
|
-
}
|
|
1563
|
-
} catch (error) {
|
|
1564
|
-
console.warn(`Could not parse Codex session file ${filePath}:`, error.message);
|
|
1565
|
-
}
|
|
1784
|
+
try {
|
|
1785
|
+
const appServerSessions = await getCodexSessionsFromAppServer(projectPath, { limit });
|
|
1786
|
+
if (appServerSessions.length > 0) {
|
|
1787
|
+
return appServerSessions;
|
|
1566
1788
|
}
|
|
1567
1789
|
|
|
1568
|
-
|
|
1569
|
-
sessions.sort((a, b) => new Date(b.lastActivity) - new Date(a.lastActivity));
|
|
1570
|
-
|
|
1571
|
-
// Return limited sessions for performance (0 = unlimited for deletion)
|
|
1572
|
-
return limit > 0 ? sessions.slice(0, limit) : sessions;
|
|
1790
|
+
return await getCodexSessionsFromJsonl(projectPath, { limit });
|
|
1573
1791
|
|
|
1574
1792
|
} catch (error) {
|
|
1575
|
-
console.
|
|
1576
|
-
|
|
1793
|
+
console.warn(`Codex app-server thread/list failed, falling back to JSONL scan: ${error.message}`);
|
|
1794
|
+
try {
|
|
1795
|
+
return await getCodexSessionsFromJsonl(projectPath, { limit });
|
|
1796
|
+
} catch (fallbackError) {
|
|
1797
|
+
console.error('Error fetching Codex sessions:', fallbackError);
|
|
1798
|
+
return [];
|
|
1799
|
+
}
|
|
1577
1800
|
}
|
|
1578
1801
|
}
|
|
1579
1802
|
|
|
@@ -1999,10 +2222,14 @@ async function deleteOpencodeSession(sessionId) {
|
|
|
1999
2222
|
try {
|
|
2000
2223
|
const sessionFilePath = await findOpencodeSessionFileById(sessionId);
|
|
2001
2224
|
if (!sessionFilePath) {
|
|
2225
|
+
if (await deleteAcpSessionRecord('opencode', sessionId).catch(() => false)) {
|
|
2226
|
+
return true;
|
|
2227
|
+
}
|
|
2002
2228
|
throw new Error(`OpenCode session file not found for session ${sessionId}`);
|
|
2003
2229
|
}
|
|
2004
2230
|
|
|
2005
2231
|
await fs.unlink(sessionFilePath);
|
|
2232
|
+
await deleteAcpSessionRecord('opencode', sessionId).catch(() => {});
|
|
2006
2233
|
return true;
|
|
2007
2234
|
} catch (error) {
|
|
2008
2235
|
console.error(`Error deleting OpenCode session ${sessionId}:`, error);
|
|
@@ -2337,6 +2564,29 @@ async function getGeminiSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2337
2564
|
}
|
|
2338
2565
|
}
|
|
2339
2566
|
|
|
2567
|
+
async function deleteGeminiSession(sessionId) {
|
|
2568
|
+
try {
|
|
2569
|
+
const sessionMetadata = await getGeminiSessionMetadata(sessionId);
|
|
2570
|
+
const sessionFilePath = sessionMetadata?.filePath || null;
|
|
2571
|
+
|
|
2572
|
+
if (!sessionFilePath) {
|
|
2573
|
+
if (await deleteAcpSessionRecord('gemini', sessionId).catch(() => false)) {
|
|
2574
|
+
clearProviderSessionLookupCaches();
|
|
2575
|
+
return true;
|
|
2576
|
+
}
|
|
2577
|
+
throw new Error(`Gemini session file not found for session ${sessionId}`);
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
await fs.unlink(sessionFilePath);
|
|
2581
|
+
await deleteAcpSessionRecord('gemini', sessionId).catch(() => {});
|
|
2582
|
+
clearProviderSessionLookupCaches();
|
|
2583
|
+
return true;
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
console.error(`Error deleting Gemini session ${sessionId}:`, error);
|
|
2586
|
+
throw error;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2340
2590
|
// Parse a Codex session JSONL file to extract metadata
|
|
2341
2591
|
async function parseCodexSessionFile(filePath) {
|
|
2342
2592
|
try {
|
|
@@ -2669,15 +2919,120 @@ async function getCodexSessionMetadata(sessionId) {
|
|
|
2669
2919
|
return metadata;
|
|
2670
2920
|
}
|
|
2671
2921
|
|
|
2922
|
+
function normalizeLocalProviderSession(session, provider, projectPath = null) {
|
|
2923
|
+
if (!session || typeof session !== 'object') {
|
|
2924
|
+
return null;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
const sessionId = String(session.id || session.sessionId || '').trim();
|
|
2928
|
+
if (!sessionId) {
|
|
2929
|
+
return null;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
const lastActivity = session.lastActivity || session.updatedAt || session.timestamp || session.createdAt || null;
|
|
2933
|
+
const createdAt = session.createdAt || session.startTime || session.timestamp || lastActivity || null;
|
|
2934
|
+
const cwd = session.cwd || session.projectPath || projectPath || null;
|
|
2935
|
+
const summary = session.summary || session.title || session.name || `${provider} session`;
|
|
2936
|
+
|
|
2937
|
+
return {
|
|
2938
|
+
...session,
|
|
2939
|
+
id: sessionId,
|
|
2940
|
+
sessionId,
|
|
2941
|
+
provider,
|
|
2942
|
+
__provider: provider,
|
|
2943
|
+
source: session.source || 'native-history',
|
|
2944
|
+
runtime: 'native-history',
|
|
2945
|
+
title: session.title || summary || sessionId,
|
|
2946
|
+
summary: summary || null,
|
|
2947
|
+
projectPath: cwd,
|
|
2948
|
+
cwd,
|
|
2949
|
+
createdAt,
|
|
2950
|
+
updatedAt: session.updatedAt || lastActivity || createdAt,
|
|
2951
|
+
lastActivity: lastActivity || createdAt,
|
|
2952
|
+
model: session.model || null
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
async function getNativeProviderSessions({ provider, projectName = null, projectPath = null, limit = 50 } = {}) {
|
|
2957
|
+
switch (provider) {
|
|
2958
|
+
case 'claude': {
|
|
2959
|
+
if (!projectName) {
|
|
2960
|
+
return [];
|
|
2961
|
+
}
|
|
2962
|
+
try {
|
|
2963
|
+
await fs.access(path.join(os.homedir(), '.claude', 'projects', projectName));
|
|
2964
|
+
} catch (_) {
|
|
2965
|
+
return [];
|
|
2966
|
+
}
|
|
2967
|
+
const result = await getSessions(projectName, limit > 0 ? limit : Number.MAX_SAFE_INTEGER, 0);
|
|
2968
|
+
const sessions = Array.isArray(result?.sessions) ? result.sessions : [];
|
|
2969
|
+
return sessions.map((session) => normalizeLocalProviderSession(session, 'claude', projectPath)).filter(Boolean);
|
|
2970
|
+
}
|
|
2971
|
+
case 'codex':
|
|
2972
|
+
return (await getCodexSessions(projectPath, { limit, localOnly: true }))
|
|
2973
|
+
.map((session) => normalizeLocalProviderSession(session, 'codex', projectPath))
|
|
2974
|
+
.filter(Boolean);
|
|
2975
|
+
case 'gemini':
|
|
2976
|
+
return (await getGeminiSessions(projectPath, { limit }))
|
|
2977
|
+
.map((session) => normalizeLocalProviderSession(session, 'gemini', projectPath))
|
|
2978
|
+
.filter(Boolean);
|
|
2979
|
+
case 'opencode':
|
|
2980
|
+
return (await getOpencodeSessions(projectPath, { limit }))
|
|
2981
|
+
.map((session) => normalizeLocalProviderSession(session, 'opencode', projectPath))
|
|
2982
|
+
.filter(Boolean);
|
|
2983
|
+
default:
|
|
2984
|
+
return [];
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
async function getLocalProviderSessions({ provider, projectName = null, projectPath = null, limit = 50 } = {}) {
|
|
2989
|
+
const [nativeSessions, acpSessions] = await Promise.all([
|
|
2990
|
+
getNativeProviderSessions({ provider, projectName, projectPath, limit }),
|
|
2991
|
+
listAcpSessions({ provider, projectPath }).catch(() => [])
|
|
2992
|
+
]);
|
|
2993
|
+
|
|
2994
|
+
return mergeSessionLists(nativeSessions, acpSessions, { fallbackProvider: provider })
|
|
2995
|
+
.slice(0, limit > 0 ? limit : undefined);
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
async function getLocalProviderSessionMessages({
|
|
2999
|
+
provider,
|
|
3000
|
+
projectName = null,
|
|
3001
|
+
sessionId,
|
|
3002
|
+
limit = null,
|
|
3003
|
+
offset = 0
|
|
3004
|
+
} = {}) {
|
|
3005
|
+
switch (provider) {
|
|
3006
|
+
case 'claude':
|
|
3007
|
+
if (!projectName) {
|
|
3008
|
+
return { messages: [], total: 0, hasMore: false, offset, limit };
|
|
3009
|
+
}
|
|
3010
|
+
return getSessionMessages(projectName, sessionId, limit, offset);
|
|
3011
|
+
case 'codex':
|
|
3012
|
+
return getCodexSessionMessages(sessionId, limit, offset);
|
|
3013
|
+
case 'gemini':
|
|
3014
|
+
return getGeminiSessionMessages(sessionId, limit, offset);
|
|
3015
|
+
case 'opencode':
|
|
3016
|
+
return getOpencodeSessionMessages(sessionId, limit, offset);
|
|
3017
|
+
default:
|
|
3018
|
+
return { messages: [], total: 0, hasMore: false, offset, limit };
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
|
|
2672
3022
|
async function deleteCodexSession(sessionId) {
|
|
2673
3023
|
try {
|
|
2674
3024
|
const sessionFilePath = await resolveCodexSessionFile(sessionId);
|
|
2675
3025
|
|
|
2676
3026
|
if (!sessionFilePath) {
|
|
3027
|
+
if (await deleteAcpSessionRecord('codex', sessionId).catch(() => false)) {
|
|
3028
|
+
codexSessionFileCache.delete(sessionId);
|
|
3029
|
+
return true;
|
|
3030
|
+
}
|
|
2677
3031
|
throw new Error(`Codex session file not found for session ${sessionId}`);
|
|
2678
3032
|
}
|
|
2679
3033
|
|
|
2680
3034
|
await fs.unlink(sessionFilePath);
|
|
3035
|
+
await deleteAcpSessionRecord('codex', sessionId).catch(() => {});
|
|
2681
3036
|
codexSessionFileCache.delete(sessionId);
|
|
2682
3037
|
return true;
|
|
2683
3038
|
} catch (error) {
|
|
@@ -2704,16 +3059,21 @@ export {
|
|
|
2704
3059
|
extractProjectDirectory,
|
|
2705
3060
|
clearProjectDirectoryCache,
|
|
2706
3061
|
clearProviderSessionLookupCaches,
|
|
3062
|
+
setCodexAppServerClientForTests,
|
|
2707
3063
|
findCodexSessionFilePathBySessionIdHint,
|
|
3064
|
+
findCodexAppServerSessionById,
|
|
2708
3065
|
getCodexSessions,
|
|
2709
3066
|
getCodexSessionMessages,
|
|
2710
3067
|
getCodexSessionMetadata,
|
|
3068
|
+
getLocalProviderSessions,
|
|
3069
|
+
getLocalProviderSessionMessages,
|
|
2711
3070
|
getOpencodeSessions,
|
|
2712
3071
|
getOpencodeSessionMetadata,
|
|
2713
3072
|
getOpencodeSessionMessages,
|
|
2714
3073
|
getGeminiSessions,
|
|
2715
3074
|
getGeminiSessionMetadata,
|
|
2716
3075
|
getGeminiSessionMessages,
|
|
3076
|
+
deleteGeminiSession,
|
|
2717
3077
|
deleteCodexSession,
|
|
2718
3078
|
deleteOpencodeSession
|
|
2719
3079
|
};
|