@sumicom/quicksave 0.6.0 → 0.6.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/dist/ai/asyncQueue.d.ts +7 -6
- package/dist/ai/asyncQueue.d.ts.map +1 -1
- package/dist/ai/asyncQueue.js +31 -21
- package/dist/ai/asyncQueue.js.map +1 -1
- package/dist/ai/cardBuilder.d.ts +22 -2
- package/dist/ai/cardBuilder.d.ts.map +1 -1
- package/dist/ai/cardBuilder.js +275 -30
- package/dist/ai/cardBuilder.js.map +1 -1
- package/dist/ai/claudeCliProvider.d.ts +21 -0
- package/dist/ai/claudeCliProvider.d.ts.map +1 -0
- package/dist/ai/claudeCliProvider.js +527 -0
- package/dist/ai/claudeCliProvider.js.map +1 -0
- package/dist/ai/claudeCodeProvider.d.ts +18 -0
- package/dist/ai/claudeCodeProvider.d.ts.map +1 -0
- package/dist/ai/claudeCodeProvider.js +30 -0
- package/dist/ai/claudeCodeProvider.js.map +1 -0
- package/dist/ai/claudeSdkProvider.d.ts +20 -0
- package/dist/ai/claudeSdkProvider.d.ts.map +1 -0
- package/dist/ai/claudeSdkProvider.js +428 -0
- package/dist/ai/claudeSdkProvider.js.map +1 -0
- package/dist/ai/codexMcpProvider.d.ts +15 -0
- package/dist/ai/codexMcpProvider.d.ts.map +1 -0
- package/dist/ai/codexMcpProvider.js +442 -0
- package/dist/ai/codexMcpProvider.js.map +1 -0
- package/dist/ai/codexSdkProvider.d.ts +35 -0
- package/dist/ai/codexSdkProvider.d.ts.map +1 -0
- package/dist/ai/codexSdkProvider.js +422 -0
- package/dist/ai/codexSdkProvider.js.map +1 -0
- package/dist/ai/commitSummary.d.ts +4 -0
- package/dist/ai/commitSummary.d.ts.map +1 -1
- package/dist/ai/commitSummary.js +80 -40
- package/dist/ai/commitSummary.js.map +1 -1
- package/dist/ai/debugLogger.d.ts +24 -0
- package/dist/ai/debugLogger.d.ts.map +1 -0
- package/dist/ai/debugLogger.js +55 -0
- package/dist/ai/debugLogger.js.map +1 -0
- package/dist/ai/provider.d.ts +57 -0
- package/dist/ai/provider.d.ts.map +1 -0
- package/dist/ai/provider.js +2 -0
- package/dist/ai/provider.js.map +1 -0
- package/dist/ai/sandboxMcp.d.ts +12 -0
- package/dist/ai/sandboxMcp.d.ts.map +1 -0
- package/dist/ai/sandboxMcp.js +12 -0
- package/dist/ai/sandboxMcp.js.map +1 -0
- package/dist/ai/sandboxMcpStdio.d.ts +16 -0
- package/dist/ai/sandboxMcpStdio.d.ts.map +1 -0
- package/dist/ai/sandboxMcpStdio.js +127 -0
- package/dist/ai/sandboxMcpStdio.js.map +1 -0
- package/dist/ai/sessionManager.d.ts +105 -0
- package/dist/ai/sessionManager.d.ts.map +1 -0
- package/dist/ai/sessionManager.js +716 -0
- package/dist/ai/sessionManager.js.map +1 -0
- package/dist/ai/sessionRegistry.d.ts +28 -0
- package/dist/ai/sessionRegistry.d.ts.map +1 -0
- package/dist/ai/sessionRegistry.js +148 -0
- package/dist/ai/sessionRegistry.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/connection/connection.d.ts +1 -0
- package/dist/connection/connection.d.ts.map +1 -1
- package/dist/connection/connection.js +19 -1
- package/dist/connection/connection.js.map +1 -1
- package/dist/git/operations.d.ts +26 -1
- package/dist/git/operations.d.ts.map +1 -1
- package/dist/git/operations.js +83 -2
- package/dist/git/operations.js.map +1 -1
- package/dist/handlers/messageHandler.d.ts +24 -3
- package/dist/handlers/messageHandler.d.ts.map +1 -1
- package/dist/handlers/messageHandler.js +378 -24
- package/dist/handlers/messageHandler.js.map +1 -1
- package/dist/index.js +23 -40
- package/dist/index.js.map +1 -1
- package/dist/service/debugHttpServer.d.ts +27 -0
- package/dist/service/debugHttpServer.d.ts.map +1 -0
- package/dist/service/debugHttpServer.js +189 -0
- package/dist/service/debugHttpServer.js.map +1 -0
- package/dist/service/ensureDaemon.d.ts.map +1 -1
- package/dist/service/ensureDaemon.js +15 -2
- package/dist/service/ensureDaemon.js.map +1 -1
- package/dist/service/ipcClient.js +1 -1
- package/dist/service/run.d.ts.map +1 -1
- package/dist/service/run.js +42 -15
- package/dist/service/run.js.map +1 -1
- package/dist/service/singleton.d.ts +4 -0
- package/dist/service/singleton.d.ts.map +1 -1
- package/dist/service/singleton.js +28 -2
- package/dist/service/singleton.js.map +1 -1
- package/package.json +7 -3
- package/dist/ai/asyncQueue.test.d.ts +0 -2
- package/dist/ai/asyncQueue.test.d.ts.map +0 -1
- package/dist/ai/asyncQueue.test.js +0 -56
- package/dist/ai/asyncQueue.test.js.map +0 -1
- package/dist/ai/claudeCodeService.d.ts +0 -122
- package/dist/ai/claudeCodeService.d.ts.map +0 -1
- package/dist/ai/claudeCodeService.js +0 -869
- package/dist/ai/claudeCodeService.js.map +0 -1
- package/dist/ai/sessionStore.test.d.ts +0 -2
- package/dist/ai/sessionStore.test.d.ts.map +0 -1
- package/dist/ai/sessionStore.test.js +0 -99
- package/dist/ai/sessionStore.test.js.map +0 -1
- package/dist/connection/pubsub.test.d.ts +0 -2
- package/dist/connection/pubsub.test.d.ts.map +0 -1
- package/dist/connection/pubsub.test.js +0 -96
- package/dist/connection/pubsub.test.js.map +0 -1
- package/dist/git/operations.test.d.ts +0 -2
- package/dist/git/operations.test.d.ts.map +0 -1
- package/dist/git/operations.test.js +0 -300
- package/dist/git/operations.test.js.map +0 -1
- package/dist/handlers/messageHandler.test.d.ts +0 -2
- package/dist/handlers/messageHandler.test.d.ts.map +0 -1
- package/dist/handlers/messageHandler.test.js +0 -361
- package/dist/handlers/messageHandler.test.js.map +0 -1
- package/dist/service/ipc.test.d.ts +0 -2
- package/dist/service/ipc.test.d.ts.map +0 -1
- package/dist/service/ipc.test.js +0 -140
- package/dist/service/ipc.test.js.map +0 -1
- package/dist/service/singleton.test.d.ts +0 -2
- package/dist/service/singleton.test.d.ts.map +0 -1
- package/dist/service/singleton.test.js +0 -39
- package/dist/service/singleton.test.js.map +0 -1
package/dist/ai/asyncQueue.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* A simple async iterable queue for multi-turn prompt delivery.
|
|
3
|
+
*
|
|
4
|
+
* Push values from the producer side; consumers iterate with `for await`.
|
|
5
|
+
* Calling `end()` signals the consumer that no more values will arrive.
|
|
5
6
|
*/
|
|
6
7
|
export declare class AsyncQueue<T> implements AsyncIterable<T> {
|
|
7
8
|
private queue;
|
|
8
|
-
private
|
|
9
|
+
private waiting;
|
|
9
10
|
private done;
|
|
10
|
-
/** Enqueue a value.
|
|
11
|
+
/** Enqueue a value. If a consumer is already waiting, deliver immediately. */
|
|
11
12
|
push(value: T): void;
|
|
12
13
|
/** Signal that no more values will be pushed. */
|
|
13
14
|
end(): void;
|
|
14
|
-
[Symbol.asyncIterator]():
|
|
15
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
15
16
|
}
|
|
16
17
|
//# sourceMappingURL=asyncQueue.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asyncQueue.d.ts","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"asyncQueue.d.ts","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,OAAO,CAAqD;IACpE,OAAO,CAAC,IAAI,CAAS;IAErB,8EAA8E;IAC9E,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAWpB,iDAAiD;IACjD,GAAG,IAAI,IAAI;IASX,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAe3C"}
|
package/dist/ai/asyncQueue.js
CHANGED
|
@@ -1,39 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* A simple async iterable queue for multi-turn prompt delivery.
|
|
3
|
+
*
|
|
4
|
+
* Push values from the producer side; consumers iterate with `for await`.
|
|
5
|
+
* Calling `end()` signals the consumer that no more values will arrive.
|
|
5
6
|
*/
|
|
6
7
|
export class AsyncQueue {
|
|
7
8
|
queue = [];
|
|
8
|
-
|
|
9
|
+
waiting = null;
|
|
9
10
|
done = false;
|
|
10
|
-
/** Enqueue a value.
|
|
11
|
+
/** Enqueue a value. If a consumer is already waiting, deliver immediately. */
|
|
11
12
|
push(value) {
|
|
12
13
|
if (this.done)
|
|
13
14
|
return;
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
this.
|
|
17
|
-
|
|
15
|
+
if (this.waiting) {
|
|
16
|
+
const resolve = this.waiting;
|
|
17
|
+
this.waiting = null;
|
|
18
|
+
resolve({ value, done: false });
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.queue.push(value);
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
/** Signal that no more values will be pushed. */
|
|
21
25
|
end() {
|
|
22
26
|
this.done = true;
|
|
23
|
-
if (this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
27
|
+
if (this.waiting) {
|
|
28
|
+
const resolve = this.waiting;
|
|
29
|
+
this.waiting = null;
|
|
30
|
+
resolve({ value: undefined, done: true });
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
[Symbol.asyncIterator]() {
|
|
34
|
+
return {
|
|
35
|
+
next: () => {
|
|
36
|
+
if (this.queue.length > 0) {
|
|
37
|
+
return Promise.resolve({ value: this.queue.shift(), done: false });
|
|
38
|
+
}
|
|
39
|
+
if (this.done) {
|
|
40
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
41
|
+
}
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
this.waiting = resolve;
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
37
47
|
}
|
|
38
48
|
}
|
|
39
49
|
//# sourceMappingURL=asyncQueue.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asyncQueue.js","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"asyncQueue.js","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACb,KAAK,GAAQ,EAAE,CAAC;IAChB,OAAO,GAAgD,IAAI,CAAC;IAC5D,IAAI,GAAG,KAAK,CAAC;IAErB,8EAA8E;IAC9E,IAAI,CAAC,KAAQ;QACX,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,GAAG;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,EAAE,KAAK,EAAE,SAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,OAAO;YACL,IAAI,EAAE,GAA+B,EAAE;gBACrC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC1B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBACtE,CAAC;gBACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBACd,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClE,CAAC;gBACD,OAAO,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,EAAE;oBAChD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;gBACzB,CAAC,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
|
package/dist/ai/cardBuilder.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment } from '@sumicom/quicksave-shared';
|
|
2
|
+
/**
|
|
3
|
+
* Load persisted card history for a memory-mode session.
|
|
4
|
+
* Returns cards in insertion order, or empty array if none exist.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadPersistedCards(sessionId: string): Promise<Card[]>;
|
|
2
7
|
export declare class StreamCardBuilder {
|
|
3
8
|
private sessionId;
|
|
4
9
|
private streamId;
|
|
10
|
+
private cwd;
|
|
5
11
|
private seq;
|
|
6
12
|
private cards;
|
|
7
13
|
/** tool_use_id → CardId, for pairing tool_result to ToolCallCard */
|
|
@@ -12,10 +18,24 @@ export declare class StreamCardBuilder {
|
|
|
12
18
|
private ephemeralCards;
|
|
13
19
|
/** Current streaming assistant_text card (for append_text events) */
|
|
14
20
|
private currentTextCardId;
|
|
15
|
-
|
|
21
|
+
/** JSONL file byte offset at the start of the current turn — history reads stop here. */
|
|
22
|
+
private _jsonlCutoff;
|
|
23
|
+
constructor(sessionId: string, streamId: string, cwd: string);
|
|
16
24
|
updateSessionId(sessionId: string): void;
|
|
17
25
|
/** Start a new turn: update streamId and reset per-turn state, keep accumulated cards. */
|
|
18
26
|
startNewTurn(streamId: string): void;
|
|
27
|
+
/** Clear all accumulated cards. Call after a turn completes and JSONL is flushed. */
|
|
28
|
+
clearCards(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Append current in-memory cards to the persisted card history file.
|
|
31
|
+
* Used by memory-mode providers (Codex) to survive reconnects.
|
|
32
|
+
* Call before clearCards() at the end of each turn.
|
|
33
|
+
*/
|
|
34
|
+
persistCards(): Promise<void>;
|
|
35
|
+
/** Snapshot current JSONL byte size so history reads stop before the active turn. */
|
|
36
|
+
snapshotCutoff(): Promise<void>;
|
|
37
|
+
get jsonlCutoff(): number | null;
|
|
38
|
+
set jsonlCutoff(value: number | null);
|
|
19
39
|
private nextId;
|
|
20
40
|
private addEvent;
|
|
21
41
|
private updateEvent;
|
|
@@ -60,5 +80,5 @@ export declare class StreamCardBuilder {
|
|
|
60
80
|
systemMessage(text: string, subtype?: 'compacted' | 'cost' | 'error' | 'info' | 'warning'): CardEvent;
|
|
61
81
|
errorMessage(text: string): CardEvent;
|
|
62
82
|
}
|
|
63
|
-
export declare function buildCardsFromHistory(sessionId: string, cwd: string, offset?: number, limit?: number): Promise<CardHistoryResponse>;
|
|
83
|
+
export declare function buildCardsFromHistory(sessionId: string, cwd: string, offset?: number, limit?: number, cutoffBytes?: number): Promise<CardHistoryResponse>;
|
|
64
84
|
//# sourceMappingURL=cardBuilder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,MAAM,EACN,SAAS,EAKT,mBAAmB,EAGnB,sBAAsB,EACvB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/cardBuilder.js
CHANGED
|
@@ -1,7 +1,143 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readFile } from 'fs/promises';
|
|
1
|
+
import { readFile, readdir, writeFile, mkdir, stat, open } from 'fs/promises';
|
|
3
2
|
import { join } from 'path';
|
|
3
|
+
import { createReadStream, existsSync } from 'fs';
|
|
4
|
+
import { createInterface } from 'readline';
|
|
5
|
+
import { homedir } from 'os';
|
|
4
6
|
const TOOL_RESULT_TRUNCATE_LENGTH = 500;
|
|
7
|
+
// ── Card history persistence (for memory-mode providers like Codex) ──
|
|
8
|
+
function cardHistoryDir() {
|
|
9
|
+
return join(homedir(), '.quicksave', 'state', 'card-history');
|
|
10
|
+
}
|
|
11
|
+
function cardHistoryPath(sessionId) {
|
|
12
|
+
return join(cardHistoryDir(), `${sessionId}.json`);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load persisted card history for a memory-mode session.
|
|
16
|
+
* Returns cards in insertion order, or empty array if none exist.
|
|
17
|
+
*/
|
|
18
|
+
export async function loadPersistedCards(sessionId) {
|
|
19
|
+
const p = cardHistoryPath(sessionId);
|
|
20
|
+
if (!existsSync(p))
|
|
21
|
+
return [];
|
|
22
|
+
try {
|
|
23
|
+
const raw = await readFile(p, 'utf-8');
|
|
24
|
+
const data = JSON.parse(raw);
|
|
25
|
+
return Array.isArray(data) ? data : [];
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// ── Direct JSONL file reading (replaces SDK getSessionMessages/listSubagents) ──
|
|
32
|
+
function encodeCwdPath(cwd) {
|
|
33
|
+
return cwd.replace(/\//g, '-');
|
|
34
|
+
}
|
|
35
|
+
function claudeProjectDir(cwd) {
|
|
36
|
+
return join(homedir(), '.claude', 'projects', encodeCwdPath(cwd));
|
|
37
|
+
}
|
|
38
|
+
function jsonlPath(sessionId, cwd) {
|
|
39
|
+
return join(claudeProjectDir(cwd), sessionId + '.jsonl');
|
|
40
|
+
}
|
|
41
|
+
function parseJSONLContent(content) {
|
|
42
|
+
const msgs = [];
|
|
43
|
+
for (const line of content.split('\n')) {
|
|
44
|
+
if (!line.trim())
|
|
45
|
+
continue;
|
|
46
|
+
try {
|
|
47
|
+
const m = JSON.parse(line);
|
|
48
|
+
if (m.type === 'user' || m.type === 'assistant' || m.type === 'system')
|
|
49
|
+
msgs.push(m);
|
|
50
|
+
}
|
|
51
|
+
catch { /* skip malformed lines */ }
|
|
52
|
+
}
|
|
53
|
+
return msgs;
|
|
54
|
+
}
|
|
55
|
+
/** Count valid non-sidechain messages by streaming the file (constant memory). */
|
|
56
|
+
async function countMessagesInJSONL(filePath) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
let count = 0;
|
|
59
|
+
const rl = createInterface({ input: createReadStream(filePath), crlfDelay: Infinity });
|
|
60
|
+
rl.on('line', (line) => {
|
|
61
|
+
if (!line || line[0] !== '{')
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
const m = JSON.parse(line);
|
|
65
|
+
if ((m.type === 'user' || m.type === 'assistant' || m.type === 'system') && !m.isSidechain)
|
|
66
|
+
count++;
|
|
67
|
+
}
|
|
68
|
+
catch { /* skip */ }
|
|
69
|
+
});
|
|
70
|
+
rl.on('close', () => resolve(count));
|
|
71
|
+
rl.on('error', reject);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/** Read the tail of a file, dropping the first partial line at the cut boundary. */
|
|
75
|
+
async function readTailContent(filePath, bytes) {
|
|
76
|
+
const { size } = await stat(filePath);
|
|
77
|
+
if (size <= bytes)
|
|
78
|
+
return await readFile(filePath, 'utf-8');
|
|
79
|
+
const fh = await open(filePath, 'r');
|
|
80
|
+
try {
|
|
81
|
+
const start = size - bytes;
|
|
82
|
+
const buf = Buffer.alloc(bytes);
|
|
83
|
+
const { bytesRead } = await fh.read(buf, 0, bytes, start);
|
|
84
|
+
let content = buf.toString('utf-8', 0, bytesRead);
|
|
85
|
+
// Drop leading partial line (may also be mid-UTF-8)
|
|
86
|
+
const firstNl = content.indexOf('\n');
|
|
87
|
+
if (firstNl >= 0)
|
|
88
|
+
content = content.slice(firstNl + 1);
|
|
89
|
+
return content;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await fh.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Read messages from a session JSONL file.
|
|
97
|
+
*
|
|
98
|
+
* Modes:
|
|
99
|
+
* - `headBytes` — read only the first N bytes (for active-turn cutoff).
|
|
100
|
+
* - `tailBytes` — read only the last N bytes (for large-file pagination).
|
|
101
|
+
* - neither — read the entire file.
|
|
102
|
+
*/
|
|
103
|
+
async function readMessagesFromJSONL(sessionId, cwd, opts) {
|
|
104
|
+
const p = jsonlPath(sessionId, cwd);
|
|
105
|
+
if (!existsSync(p))
|
|
106
|
+
return [];
|
|
107
|
+
if (opts?.headBytes != null) {
|
|
108
|
+
const fh = await open(p, 'r');
|
|
109
|
+
try {
|
|
110
|
+
const buf = Buffer.alloc(opts.headBytes);
|
|
111
|
+
const { bytesRead } = await fh.read(buf, 0, opts.headBytes, 0);
|
|
112
|
+
let raw = buf.toString('utf-8', 0, bytesRead);
|
|
113
|
+
const lastNewline = raw.lastIndexOf('\n');
|
|
114
|
+
if (lastNewline >= 0 && lastNewline < raw.length - 1) {
|
|
115
|
+
raw = raw.slice(0, lastNewline + 1);
|
|
116
|
+
}
|
|
117
|
+
return parseJSONLContent(raw);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
await fh.close();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (opts?.tailBytes != null) {
|
|
124
|
+
const content = await readTailContent(p, opts.tailBytes);
|
|
125
|
+
return parseJSONLContent(content);
|
|
126
|
+
}
|
|
127
|
+
const content = await readFile(p, 'utf-8');
|
|
128
|
+
return parseJSONLContent(content);
|
|
129
|
+
}
|
|
130
|
+
async function listSubagentIdsFromDisk(sessionId, cwd) {
|
|
131
|
+
const d = join(claudeProjectDir(cwd), sessionId, 'subagents');
|
|
132
|
+
if (!existsSync(d))
|
|
133
|
+
return [];
|
|
134
|
+
try {
|
|
135
|
+
return (await readdir(d)).filter(f => f.endsWith('.meta.json')).map(f => f.replace('.meta.json', ''));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
5
141
|
/** Extract readable text from tool_result content (string or array of blocks). */
|
|
6
142
|
function extractToolResultText(content) {
|
|
7
143
|
if (typeof content === 'string')
|
|
@@ -20,6 +156,7 @@ function extractToolResultText(content) {
|
|
|
20
156
|
export class StreamCardBuilder {
|
|
21
157
|
sessionId;
|
|
22
158
|
streamId;
|
|
159
|
+
cwd;
|
|
23
160
|
seq = 0;
|
|
24
161
|
cards = new Map();
|
|
25
162
|
/** tool_use_id → CardId, for pairing tool_result to ToolCallCard */
|
|
@@ -30,9 +167,12 @@ export class StreamCardBuilder {
|
|
|
30
167
|
ephemeralCards = new Set();
|
|
31
168
|
/** Current streaming assistant_text card (for append_text events) */
|
|
32
169
|
currentTextCardId = null;
|
|
33
|
-
|
|
170
|
+
/** JSONL file byte offset at the start of the current turn — history reads stop here. */
|
|
171
|
+
_jsonlCutoff = null;
|
|
172
|
+
constructor(sessionId, streamId, cwd) {
|
|
34
173
|
this.sessionId = sessionId;
|
|
35
174
|
this.streamId = streamId;
|
|
175
|
+
this.cwd = cwd;
|
|
36
176
|
}
|
|
37
177
|
updateSessionId(sessionId) {
|
|
38
178
|
this.sessionId = sessionId;
|
|
@@ -42,6 +182,65 @@ export class StreamCardBuilder {
|
|
|
42
182
|
this.streamId = streamId;
|
|
43
183
|
this.currentTextCardId = null;
|
|
44
184
|
}
|
|
185
|
+
/** Clear all accumulated cards. Call after a turn completes and JSONL is flushed. */
|
|
186
|
+
clearCards() {
|
|
187
|
+
this.cards.clear();
|
|
188
|
+
this.toolUseIdToCardId.clear();
|
|
189
|
+
this.agentIdToCardId.clear();
|
|
190
|
+
this.ephemeralCards.clear();
|
|
191
|
+
this.currentTextCardId = null;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Append current in-memory cards to the persisted card history file.
|
|
195
|
+
* Used by memory-mode providers (Codex) to survive reconnects.
|
|
196
|
+
* Call before clearCards() at the end of each turn.
|
|
197
|
+
*/
|
|
198
|
+
async persistCards() {
|
|
199
|
+
const cards = this.getCards();
|
|
200
|
+
if (cards.length === 0)
|
|
201
|
+
return;
|
|
202
|
+
// Strip transient fields before persisting
|
|
203
|
+
const cleaned = cards.map(c => {
|
|
204
|
+
const { pendingInput, ...rest } = c;
|
|
205
|
+
if (rest.type === 'assistant_text') {
|
|
206
|
+
return { ...rest, streaming: false };
|
|
207
|
+
}
|
|
208
|
+
return rest;
|
|
209
|
+
});
|
|
210
|
+
const dir = cardHistoryDir();
|
|
211
|
+
const p = cardHistoryPath(this.sessionId);
|
|
212
|
+
// Append to existing history
|
|
213
|
+
let existing = [];
|
|
214
|
+
try {
|
|
215
|
+
if (existsSync(p)) {
|
|
216
|
+
const raw = await readFile(p, 'utf-8');
|
|
217
|
+
const parsed = JSON.parse(raw);
|
|
218
|
+
if (Array.isArray(parsed))
|
|
219
|
+
existing = parsed;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch { /* start fresh */ }
|
|
223
|
+
const merged = [...existing, ...cleaned];
|
|
224
|
+
await mkdir(dir, { recursive: true });
|
|
225
|
+
await writeFile(p, JSON.stringify(merged) + '\n');
|
|
226
|
+
}
|
|
227
|
+
/** Snapshot current JSONL byte size so history reads stop before the active turn. */
|
|
228
|
+
async snapshotCutoff() {
|
|
229
|
+
const p = jsonlPath(this.sessionId, this.cwd);
|
|
230
|
+
try {
|
|
231
|
+
const { size } = await stat(p);
|
|
232
|
+
this._jsonlCutoff = size;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
this._jsonlCutoff = null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
get jsonlCutoff() {
|
|
239
|
+
return this._jsonlCutoff;
|
|
240
|
+
}
|
|
241
|
+
set jsonlCutoff(value) {
|
|
242
|
+
this._jsonlCutoff = value;
|
|
243
|
+
}
|
|
45
244
|
nextId() {
|
|
46
245
|
return `${this.sessionId}:${this.streamId}:${++this.seq}`;
|
|
47
246
|
}
|
|
@@ -240,13 +439,53 @@ export class StreamCardBuilder {
|
|
|
240
439
|
// ============================================================================
|
|
241
440
|
// buildCardsFromHistory — convert JSONL history into Card[]
|
|
242
441
|
// ============================================================================
|
|
243
|
-
export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit = 50) {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
442
|
+
export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit = 50, cutoffBytes) {
|
|
443
|
+
const p = jsonlPath(sessionId, cwd);
|
|
444
|
+
if (!existsSync(p))
|
|
445
|
+
return { cards: [], total: 0, hasMore: false };
|
|
446
|
+
let allMessages;
|
|
447
|
+
let total;
|
|
448
|
+
if (cutoffBytes != null) {
|
|
449
|
+
// Active turn: read head of file up to the snapshot byte offset.
|
|
450
|
+
allMessages = (await readMessagesFromJSONL(sessionId, cwd, { headBytes: cutoffBytes }))
|
|
451
|
+
.filter((m) => !m.isSidechain);
|
|
452
|
+
total = allMessages.length;
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const { size: fileSize } = await stat(p);
|
|
456
|
+
// Estimate: (offset + limit) messages × 3 margin × ~4 KB each
|
|
457
|
+
const tailBytes = (offset + limit) * 3 * 4096;
|
|
458
|
+
if (tailBytes < fileSize) {
|
|
459
|
+
// Large file: streaming count for stable total + tail read for cards
|
|
460
|
+
const [counted, tailMsgs] = await Promise.all([
|
|
461
|
+
countMessagesInJSONL(p),
|
|
462
|
+
readMessagesFromJSONL(sessionId, cwd, { tailBytes }),
|
|
463
|
+
]);
|
|
464
|
+
const filtered = tailMsgs.filter((m) => !m.isSidechain);
|
|
465
|
+
total = counted;
|
|
466
|
+
if (filtered.length >= offset + limit) {
|
|
467
|
+
allMessages = filtered;
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
// Tail wasn't enough — fall back to full read
|
|
471
|
+
allMessages = (await readMessagesFromJSONL(sessionId, cwd))
|
|
472
|
+
.filter((m) => !m.isSidechain);
|
|
473
|
+
total = allMessages.length;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
// Small file: read whole thing
|
|
478
|
+
allMessages = (await readMessagesFromJSONL(sessionId, cwd))
|
|
479
|
+
.filter((m) => !m.isSidechain);
|
|
480
|
+
total = allMessages.length;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// allMessages may be a tail subset — map page positions to local indices.
|
|
484
|
+
// Page wants global positions [total-offset-limit, total-offset].
|
|
485
|
+
// allMessages holds the last `allMessages.length` messages of the file.
|
|
486
|
+
const pageStart = Math.max(0, allMessages.length - offset - limit);
|
|
487
|
+
const pageEnd = Math.max(0, allMessages.length - offset);
|
|
488
|
+
const sliced = allMessages.slice(pageStart, pageEnd);
|
|
250
489
|
// ── Pass 1: Build indexes from ALL messages ────────────────────────────
|
|
251
490
|
// toolUseId → toolName (for tool_result lookup across pages)
|
|
252
491
|
const agentToolUseIds = new Set();
|
|
@@ -286,24 +525,11 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
286
525
|
}
|
|
287
526
|
const toolUseIdToAgentId = new Map();
|
|
288
527
|
try {
|
|
289
|
-
const agentIds = await
|
|
290
|
-
|
|
291
|
-
// SDK stores subagent meta at ~/.claude/projects/<cwd-hash>/<sessionId>/subagents/<agentId>.meta.json
|
|
292
|
-
const cwdHash = cwd.replace(/\//g, '-').replace(/^-/, '');
|
|
293
|
-
const subagentsDir = join(process.env.HOME ?? '', '.claude', 'projects', cwdHash, sessionId, 'subagents');
|
|
528
|
+
const agentIds = await listSubagentIdsFromDisk(sessionId, cwd);
|
|
529
|
+
const subagentsDir = join(claudeProjectDir(cwd), sessionId, 'subagents');
|
|
294
530
|
for (const agentId of agentIds) {
|
|
295
531
|
try {
|
|
296
|
-
const
|
|
297
|
-
join(subagentsDir, `${agentId}.meta.json`),
|
|
298
|
-
];
|
|
299
|
-
let meta = null;
|
|
300
|
-
for (const p of possiblePaths) {
|
|
301
|
-
try {
|
|
302
|
-
meta = JSON.parse(await readFile(p, 'utf-8'));
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
catch { /* try next */ }
|
|
306
|
-
}
|
|
532
|
+
const meta = JSON.parse(await readFile(join(subagentsDir, `${agentId}.meta.json`), 'utf-8'));
|
|
307
533
|
if (meta?.description && descToToolUseId.has(meta.description)) {
|
|
308
534
|
toolUseIdToAgentId.set(descToToolUseId.get(meta.description), agentId);
|
|
309
535
|
}
|
|
@@ -324,7 +550,7 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
324
550
|
}
|
|
325
551
|
}
|
|
326
552
|
// ── Pass 2: Build Cards from sliced messages ───────────────────────────
|
|
327
|
-
let seq =
|
|
553
|
+
let seq = Math.max(0, total - offset - limit);
|
|
328
554
|
const nextId = () => `${sessionId}:h:${++seq}`;
|
|
329
555
|
const cards = [];
|
|
330
556
|
for (const msg of sliced) {
|
|
@@ -445,6 +671,18 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
445
671
|
const toolInput = typeof block.input === 'object' && block.input !== null
|
|
446
672
|
? block.input
|
|
447
673
|
: {};
|
|
674
|
+
const result = block.id ? toolResults.get(block.id) : undefined;
|
|
675
|
+
let pairedResult;
|
|
676
|
+
if (result) {
|
|
677
|
+
const truncated = result.content.length > TOOL_RESULT_TRUNCATE_LENGTH;
|
|
678
|
+
pairedResult = {
|
|
679
|
+
content: truncated
|
|
680
|
+
? result.content.slice(0, TOOL_RESULT_TRUNCATE_LENGTH) + ' [truncated]'
|
|
681
|
+
: result.content,
|
|
682
|
+
isError: result.isError,
|
|
683
|
+
truncated,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
448
686
|
cards.push({
|
|
449
687
|
type: 'tool_call',
|
|
450
688
|
id: nextId(),
|
|
@@ -452,6 +690,7 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
452
690
|
toolName: block.name ?? block.type,
|
|
453
691
|
toolInput,
|
|
454
692
|
toolUseId: block.id ?? nextId(),
|
|
693
|
+
result: pairedResult,
|
|
455
694
|
});
|
|
456
695
|
break;
|
|
457
696
|
}
|
|
@@ -487,9 +726,15 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
487
726
|
case 'container_upload':
|
|
488
727
|
cards.push({ type: 'system', id: nextId(), timestamp: Date.now(), text: 'Container upload', subtype: 'info' });
|
|
489
728
|
break;
|
|
490
|
-
// Unknown block types —
|
|
491
|
-
default:
|
|
729
|
+
// Unknown block types — surface as info card so they're visible
|
|
730
|
+
default: {
|
|
731
|
+
const preview = block.text ?? block.content ?? '';
|
|
732
|
+
const previewStr = typeof preview === 'string' ? preview.slice(0, 200) : JSON.stringify(preview).slice(0, 200);
|
|
733
|
+
if (block.type && previewStr) {
|
|
734
|
+
cards.push({ type: 'system', id: nextId(), timestamp: Date.now(), text: `[${block.type}] ${previewStr}`, subtype: 'info' });
|
|
735
|
+
}
|
|
492
736
|
break;
|
|
737
|
+
}
|
|
493
738
|
}
|
|
494
739
|
}
|
|
495
740
|
}
|
|
@@ -497,7 +742,7 @@ export async function buildCardsFromHistory(sessionId, cwd, offset = 0, limit =
|
|
|
497
742
|
return {
|
|
498
743
|
cards,
|
|
499
744
|
total,
|
|
500
|
-
hasMore:
|
|
745
|
+
hasMore: total - offset - limit > 0,
|
|
501
746
|
};
|
|
502
747
|
}
|
|
503
748
|
//# sourceMappingURL=cardBuilder.js.map
|