@averyyy/pi-coding-agent 0.80.10-piclient.1 → 0.81.1-piclient.1
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/CHANGELOG.md +60 -0
- package/README.md +6 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +13 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +22 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +86 -58
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +7 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +6 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +23 -4
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +75 -29
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +6 -17
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +13 -2
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +2 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +31 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +16 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +17 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +2 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +7 -2
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +41 -18
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +1 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +4 -4
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +26 -7
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +1 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +6 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -4
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +99 -23
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/usage-totals.d.ts +19 -0
- package/dist/core/usage-totals.d.ts.map +1 -0
- package/dist/core/usage-totals.js +52 -0
- package/dist/core/usage-totals.js.map +1 -0
- package/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +3 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/llama/client.d.ts +61 -0
- package/dist/extensions/llama/client.d.ts.map +1 -0
- package/dist/extensions/llama/client.js +302 -0
- package/dist/extensions/llama/client.js.map +1 -0
- package/dist/extensions/llama/huggingface.d.ts +23 -0
- package/dist/extensions/llama/huggingface.d.ts.map +1 -0
- package/dist/extensions/llama/huggingface.js +141 -0
- package/dist/extensions/llama/huggingface.js.map +1 -0
- package/dist/extensions/llama/index.d.ts +3 -0
- package/dist/extensions/llama/index.d.ts.map +1 -0
- package/dist/extensions/llama/index.js +208 -0
- package/dist/extensions/llama/index.js.map +1 -0
- package/dist/extensions/llama/provider.d.ts +10 -0
- package/dist/extensions/llama/provider.d.ts.map +1 -0
- package/dist/extensions/llama/provider.js +102 -0
- package/dist/extensions/llama/provider.js.map +1 -0
- package/dist/extensions/llama/ui.d.ts +42 -0
- package/dist/extensions/llama/ui.d.ts.map +1 -0
- package/dist/extensions/llama/ui.js +416 -0
- package/dist/extensions/llama/ui.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -4
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +24 -23
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +48 -26
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +4 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +4 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +11 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/compaction.md +8 -3
- package/docs/custom-provider.md +28 -0
- package/docs/extensions.md +51 -9
- package/docs/index.md +1 -0
- package/docs/json.md +5 -1
- package/docs/llama-cpp.md +99 -0
- package/docs/prompt-templates.md +1 -0
- package/docs/providers.md +11 -0
- package/docs/rpc.md +81 -2
- package/docs/sdk.md +3 -0
- package/docs/session-format.md +17 -3
- package/docs/tui.md +41 -26
- package/docs/usage.md +2 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/custom-compaction.ts +1 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { uuidv7 } from "@earendil-works/pi-
|
|
1
|
+
import { uuidv7 } from "@earendil-works/pi-ai";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
import { appendFileSync, closeSync, createReadStream, existsSync, mkdirSync, openSync, readdirSync, readSync, statSync, writeFileSync, } from "fs";
|
|
4
4
|
import { readdir, stat } from "fs/promises";
|
|
@@ -253,6 +253,15 @@ export function getDefaultSessionDir(cwd, agentDir = getDefaultAgentDir()) {
|
|
|
253
253
|
return sessionDir;
|
|
254
254
|
}
|
|
255
255
|
const SESSION_READ_BUFFER_SIZE = 1024 * 1024;
|
|
256
|
+
const SESSION_HEADER_READ_BUFFER_SIZE = 4096;
|
|
257
|
+
/** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */
|
|
258
|
+
const MAX_SESSION_HEADER_SCAN_BYTES = 1024 * 1024;
|
|
259
|
+
class SessionHeaderScanLimitError extends Error {
|
|
260
|
+
constructor(filePath) {
|
|
261
|
+
super(`Session header exceeds ${MAX_SESSION_HEADER_SCAN_BYTES}-byte scan limit: ${filePath}`);
|
|
262
|
+
this.name = "SessionHeaderScanLimitError";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
256
265
|
function parseSessionEntryLine(line) {
|
|
257
266
|
if (!line.trim())
|
|
258
267
|
return null;
|
|
@@ -308,22 +317,70 @@ export function loadEntriesFromFile(filePath) {
|
|
|
308
317
|
}
|
|
309
318
|
return entries;
|
|
310
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Inspect a physical line while searching for the first parsed session entry.
|
|
322
|
+
* Blank and malformed lines are skipped to match loadEntriesFromFile().
|
|
323
|
+
* Returns undefined to keep scanning, null for a parsed non-header entry, or the header.
|
|
324
|
+
*/
|
|
325
|
+
function parseSessionHeaderCandidate(line) {
|
|
326
|
+
if (!line.trim())
|
|
327
|
+
return undefined;
|
|
328
|
+
const entry = parseSessionEntryLine(line);
|
|
329
|
+
if (!entry)
|
|
330
|
+
return undefined;
|
|
331
|
+
if (entry.type !== "session" || typeof entry.id !== "string")
|
|
332
|
+
return null;
|
|
333
|
+
return entry;
|
|
334
|
+
}
|
|
311
335
|
function readSessionHeader(filePath) {
|
|
336
|
+
const fd = openSync(filePath, "r");
|
|
312
337
|
try {
|
|
313
|
-
const
|
|
314
|
-
const buffer = Buffer.
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
338
|
+
const decoder = new StringDecoder("utf8");
|
|
339
|
+
const buffer = Buffer.allocUnsafe(SESSION_HEADER_READ_BUFFER_SIZE);
|
|
340
|
+
const lineChunks = [];
|
|
341
|
+
let scannedBytes = 0;
|
|
342
|
+
while (scannedBytes < MAX_SESSION_HEADER_SCAN_BYTES) {
|
|
343
|
+
const readLength = Math.min(buffer.length, MAX_SESSION_HEADER_SCAN_BYTES - scannedBytes);
|
|
344
|
+
const bytesRead = readSync(fd, buffer, 0, readLength, null);
|
|
345
|
+
if (bytesRead === 0) {
|
|
346
|
+
lineChunks.push(decoder.end());
|
|
347
|
+
return parseSessionHeaderCandidate(lineChunks.join("")) ?? null;
|
|
348
|
+
}
|
|
349
|
+
scannedBytes += bytesRead;
|
|
350
|
+
const chunk = decoder.write(buffer.subarray(0, bytesRead));
|
|
351
|
+
let lineStart = 0;
|
|
352
|
+
let newlineIndex = chunk.indexOf("\n", lineStart);
|
|
353
|
+
while (newlineIndex !== -1) {
|
|
354
|
+
lineChunks.push(chunk.slice(lineStart, newlineIndex));
|
|
355
|
+
const header = parseSessionHeaderCandidate(lineChunks.join(""));
|
|
356
|
+
if (header !== undefined)
|
|
357
|
+
return header;
|
|
358
|
+
lineChunks.length = 0;
|
|
359
|
+
lineStart = newlineIndex + 1;
|
|
360
|
+
newlineIndex = chunk.indexOf("\n", lineStart);
|
|
361
|
+
}
|
|
362
|
+
lineChunks.push(chunk.slice(lineStart));
|
|
323
363
|
}
|
|
324
|
-
|
|
364
|
+
// Probe for EOF so a final header without a newline is allowed when it ends
|
|
365
|
+
// exactly at the scan limit. Any additional byte exceeds the bounded scan.
|
|
366
|
+
const probe = Buffer.allocUnsafe(1);
|
|
367
|
+
if (readSync(fd, probe, 0, probe.length, null) === 0) {
|
|
368
|
+
lineChunks.push(decoder.end());
|
|
369
|
+
return parseSessionHeaderCandidate(lineChunks.join("")) ?? null;
|
|
370
|
+
}
|
|
371
|
+
throw new SessionHeaderScanLimitError(filePath);
|
|
372
|
+
}
|
|
373
|
+
finally {
|
|
374
|
+
closeSync(fd);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function readSessionHeaderForDiscovery(filePath) {
|
|
378
|
+
try {
|
|
379
|
+
return readSessionHeader(filePath);
|
|
325
380
|
}
|
|
326
381
|
catch {
|
|
382
|
+
// Discovery is best-effort: unreadable or oversized files are not sessions,
|
|
383
|
+
// and one corrupt file must not prevent other sessions from being found.
|
|
327
384
|
return null;
|
|
328
385
|
}
|
|
329
386
|
}
|
|
@@ -342,7 +399,7 @@ export function findMostRecentSession(sessionDir, cwd) {
|
|
|
342
399
|
const files = readdirSync(resolvedSessionDir)
|
|
343
400
|
.filter((f) => f.endsWith(".jsonl"))
|
|
344
401
|
.map((f) => join(resolvedSessionDir, f))
|
|
345
|
-
.map((path) => ({ path, header:
|
|
402
|
+
.map((path) => ({ path, header: readSessionHeaderForDiscovery(path) }))
|
|
346
403
|
.filter((file) => file.header !== null &&
|
|
347
404
|
(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)))
|
|
348
405
|
.map(({ path }) => ({ path, mtime: statSync(path).mtime }))
|
|
@@ -350,6 +407,7 @@ export function findMostRecentSession(sessionDir, cwd) {
|
|
|
350
407
|
return files[0]?.path || null;
|
|
351
408
|
}
|
|
352
409
|
catch {
|
|
410
|
+
// Directory access and stat races make recent-session discovery unavailable.
|
|
353
411
|
return null;
|
|
354
412
|
}
|
|
355
413
|
}
|
|
@@ -535,7 +593,7 @@ export class SessionManager {
|
|
|
535
593
|
labelsById = new Map();
|
|
536
594
|
labelTimestampsById = new Map();
|
|
537
595
|
leafId = null;
|
|
538
|
-
constructor(cwd, sessionDir, sessionFile, persist, newSessionOptions) {
|
|
596
|
+
constructor(cwd, sessionDir, sessionFile, persist, newSessionOptions, preloadedFileEntries) {
|
|
539
597
|
this.cwd = resolvePath(cwd);
|
|
540
598
|
this.sessionDir = normalizePath(sessionDir);
|
|
541
599
|
this.persist = persist;
|
|
@@ -543,7 +601,7 @@ export class SessionManager {
|
|
|
543
601
|
mkdirSync(this.sessionDir, { recursive: true });
|
|
544
602
|
}
|
|
545
603
|
if (sessionFile) {
|
|
546
|
-
this.
|
|
604
|
+
this._setSessionFile(sessionFile, preloadedFileEntries);
|
|
547
605
|
}
|
|
548
606
|
else {
|
|
549
607
|
this.newSession(newSessionOptions);
|
|
@@ -551,9 +609,12 @@ export class SessionManager {
|
|
|
551
609
|
}
|
|
552
610
|
/** Switch to a different session file (used for resume and branching) */
|
|
553
611
|
setSessionFile(sessionFile) {
|
|
612
|
+
this._setSessionFile(sessionFile);
|
|
613
|
+
}
|
|
614
|
+
_setSessionFile(sessionFile, preloadedFileEntries) {
|
|
554
615
|
this.sessionFile = resolvePath(sessionFile);
|
|
555
616
|
if (existsSync(this.sessionFile)) {
|
|
556
|
-
this.fileEntries = loadEntriesFromFile(this.sessionFile);
|
|
617
|
+
this.fileEntries = preloadedFileEntries ?? loadEntriesFromFile(this.sessionFile);
|
|
557
618
|
// If file was empty, initialize it with a valid session header. If it was
|
|
558
619
|
// non-empty but did not parse as a pi session, fail without modifying it.
|
|
559
620
|
if (this.fileEntries.length === 0) {
|
|
@@ -790,7 +851,7 @@ export class SessionManager {
|
|
|
790
851
|
return entry.id;
|
|
791
852
|
}
|
|
792
853
|
/** Append a compaction summary as child of current leaf, then advance leaf. Returns entry id. */
|
|
793
|
-
appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromHook) {
|
|
854
|
+
appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromHook, usage) {
|
|
794
855
|
const entry = {
|
|
795
856
|
type: "compaction",
|
|
796
857
|
id: generateId(this.byId),
|
|
@@ -800,6 +861,7 @@ export class SessionManager {
|
|
|
800
861
|
firstKeptEntryId,
|
|
801
862
|
tokensBefore,
|
|
802
863
|
details,
|
|
864
|
+
usage,
|
|
803
865
|
fromHook,
|
|
804
866
|
};
|
|
805
867
|
this._appendEntry(entry);
|
|
@@ -1039,7 +1101,7 @@ export class SessionManager {
|
|
|
1039
1101
|
* Same as branch(), but also appends a branch_summary entry that captures
|
|
1040
1102
|
* context from the abandoned conversation path.
|
|
1041
1103
|
*/
|
|
1042
|
-
branchWithSummary(branchFromId, summary, details, fromHook) {
|
|
1104
|
+
branchWithSummary(branchFromId, summary, details, fromHook, usage) {
|
|
1043
1105
|
if (branchFromId !== null && !this.byId.has(branchFromId)) {
|
|
1044
1106
|
throw new Error(`Entry ${branchFromId} not found`);
|
|
1045
1107
|
}
|
|
@@ -1052,6 +1114,7 @@ export class SessionManager {
|
|
|
1052
1114
|
fromId: branchFromId ?? "root",
|
|
1053
1115
|
summary,
|
|
1054
1116
|
details,
|
|
1117
|
+
usage,
|
|
1055
1118
|
fromHook,
|
|
1056
1119
|
};
|
|
1057
1120
|
this._appendEntry(entry);
|
|
@@ -1173,13 +1236,26 @@ export class SessionManager {
|
|
|
1173
1236
|
*/
|
|
1174
1237
|
static open(path, sessionDir, cwdOverride) {
|
|
1175
1238
|
const resolvedPath = resolvePath(path);
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1239
|
+
let header = null;
|
|
1240
|
+
let preloadedFileEntries;
|
|
1241
|
+
if (cwdOverride === undefined && existsSync(resolvedPath)) {
|
|
1242
|
+
try {
|
|
1243
|
+
header = readSessionHeader(resolvedPath);
|
|
1244
|
+
}
|
|
1245
|
+
catch (error) {
|
|
1246
|
+
if (!(error instanceof SessionHeaderScanLimitError))
|
|
1247
|
+
throw error;
|
|
1248
|
+
// The bounded scan is only a discovery optimization. A full load remains
|
|
1249
|
+
// authoritative for legacy files with very large headers or prefixes.
|
|
1250
|
+
preloadedFileEntries = loadEntriesFromFile(resolvedPath);
|
|
1251
|
+
const firstEntry = preloadedFileEntries[0];
|
|
1252
|
+
header = firstEntry?.type === "session" ? firstEntry : null;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
const cwd = cwdOverride ?? (header ? getSessionHeaderCwd(header) : undefined) ?? process.cwd();
|
|
1180
1256
|
// If no sessionDir provided, derive from file's parent directory
|
|
1181
1257
|
const dir = sessionDir ? normalizePath(sessionDir) : resolve(resolvedPath, "..");
|
|
1182
|
-
return new SessionManager(cwd, dir, resolvedPath, true);
|
|
1258
|
+
return new SessionManager(cwd, dir, resolvedPath, true, undefined, preloadedFileEntries);
|
|
1183
1259
|
}
|
|
1184
1260
|
/**
|
|
1185
1261
|
* Continue the most recent session, or create new if none.
|