@averyyy/pi-coding-agent 0.80.10-piclient.2 → 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.
Files changed (143) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +6 -3
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +2 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +13 -0
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +22 -3
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +86 -58
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/compaction/branch-summarization.d.ts +7 -1
  14. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  15. package/dist/core/compaction/branch-summarization.js +6 -2
  16. package/dist/core/compaction/branch-summarization.js.map +1 -1
  17. package/dist/core/compaction/compaction.d.ts +23 -4
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +75 -29
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/compaction/utils.d.ts +1 -1
  22. package/dist/core/compaction/utils.d.ts.map +1 -1
  23. package/dist/core/compaction/utils.js +6 -17
  24. package/dist/core/compaction/utils.js.map +1 -1
  25. package/dist/core/extensions/loader.d.ts.map +1 -1
  26. package/dist/core/extensions/loader.js +13 -2
  27. package/dist/core/extensions/loader.js.map +1 -1
  28. package/dist/core/extensions/runner.d.ts +2 -1
  29. package/dist/core/extensions/runner.d.ts.map +1 -1
  30. package/dist/core/extensions/runner.js +31 -0
  31. package/dist/core/extensions/runner.js.map +1 -1
  32. package/dist/core/extensions/types.d.ts +16 -2
  33. package/dist/core/extensions/types.d.ts.map +1 -1
  34. package/dist/core/extensions/types.js.map +1 -1
  35. package/dist/core/model-registry.d.ts +5 -1
  36. package/dist/core/model-registry.d.ts.map +1 -1
  37. package/dist/core/model-registry.js +17 -2
  38. package/dist/core/model-registry.js.map +1 -1
  39. package/dist/core/model-resolver.d.ts.map +1 -1
  40. package/dist/core/model-resolver.js +2 -0
  41. package/dist/core/model-resolver.js.map +1 -1
  42. package/dist/core/model-runtime.d.ts +7 -2
  43. package/dist/core/model-runtime.d.ts.map +1 -1
  44. package/dist/core/model-runtime.js +41 -18
  45. package/dist/core/model-runtime.js.map +1 -1
  46. package/dist/core/prompt-templates.d.ts +1 -0
  47. package/dist/core/prompt-templates.d.ts.map +1 -1
  48. package/dist/core/prompt-templates.js +4 -4
  49. package/dist/core/prompt-templates.js.map +1 -1
  50. package/dist/core/remote-catalog-provider.d.ts +1 -1
  51. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  52. package/dist/core/remote-catalog-provider.js +26 -7
  53. package/dist/core/remote-catalog-provider.js.map +1 -1
  54. package/dist/core/resource-loader.d.ts.map +1 -1
  55. package/dist/core/resource-loader.js +1 -0
  56. package/dist/core/resource-loader.js.map +1 -1
  57. package/dist/core/sdk.d.ts.map +1 -1
  58. package/dist/core/sdk.js +6 -2
  59. package/dist/core/sdk.js.map +1 -1
  60. package/dist/core/session-manager.d.ts +9 -4
  61. package/dist/core/session-manager.d.ts.map +1 -1
  62. package/dist/core/session-manager.js +99 -23
  63. package/dist/core/session-manager.js.map +1 -1
  64. package/dist/core/tools/read.d.ts.map +1 -1
  65. package/dist/core/tools/read.js +1 -1
  66. package/dist/core/tools/read.js.map +1 -1
  67. package/dist/core/usage-totals.d.ts +19 -0
  68. package/dist/core/usage-totals.d.ts.map +1 -0
  69. package/dist/core/usage-totals.js +52 -0
  70. package/dist/core/usage-totals.js.map +1 -0
  71. package/dist/extensions/index.d.ts +3 -0
  72. package/dist/extensions/index.d.ts.map +1 -0
  73. package/dist/extensions/index.js +3 -0
  74. package/dist/extensions/index.js.map +1 -0
  75. package/dist/extensions/llama/client.d.ts +61 -0
  76. package/dist/extensions/llama/client.d.ts.map +1 -0
  77. package/dist/extensions/llama/client.js +302 -0
  78. package/dist/extensions/llama/client.js.map +1 -0
  79. package/dist/extensions/llama/huggingface.d.ts +23 -0
  80. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  81. package/dist/extensions/llama/huggingface.js +141 -0
  82. package/dist/extensions/llama/huggingface.js.map +1 -0
  83. package/dist/extensions/llama/index.d.ts +3 -0
  84. package/dist/extensions/llama/index.d.ts.map +1 -0
  85. package/dist/extensions/llama/index.js +208 -0
  86. package/dist/extensions/llama/index.js.map +1 -0
  87. package/dist/extensions/llama/provider.d.ts +10 -0
  88. package/dist/extensions/llama/provider.d.ts.map +1 -0
  89. package/dist/extensions/llama/provider.js +102 -0
  90. package/dist/extensions/llama/provider.js.map +1 -0
  91. package/dist/extensions/llama/ui.d.ts +42 -0
  92. package/dist/extensions/llama/ui.d.ts.map +1 -0
  93. package/dist/extensions/llama/ui.js +416 -0
  94. package/dist/extensions/llama/ui.js.map +1 -0
  95. package/dist/index.d.ts +2 -2
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/main.d.ts.map +1 -1
  100. package/dist/main.js +10 -4
  101. package/dist/main.js.map +1 -1
  102. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/footer.js +24 -23
  104. package/dist/modes/interactive/components/footer.js.map +1 -1
  105. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  106. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  107. package/dist/modes/interactive/interactive-mode.js +48 -26
  108. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  109. package/dist/modes/rpc/rpc-client.d.ts +4 -0
  110. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  111. package/dist/modes/rpc/rpc-client.js +7 -0
  112. package/dist/modes/rpc/rpc-client.js.map +1 -1
  113. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  114. package/dist/modes/rpc/rpc-mode.js +4 -0
  115. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  116. package/dist/modes/rpc/rpc-types.d.ts +11 -0
  117. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  118. package/dist/modes/rpc/rpc-types.js.map +1 -1
  119. package/docs/compaction.md +8 -3
  120. package/docs/custom-provider.md +28 -0
  121. package/docs/extensions.md +51 -9
  122. package/docs/index.md +1 -0
  123. package/docs/json.md +5 -1
  124. package/docs/llama-cpp.md +99 -0
  125. package/docs/prompt-templates.md +1 -0
  126. package/docs/providers.md +11 -0
  127. package/docs/rpc.md +81 -2
  128. package/docs/sdk.md +3 -0
  129. package/docs/session-format.md +17 -3
  130. package/docs/tui.md +41 -26
  131. package/docs/usage.md +2 -1
  132. package/examples/extensions/README.md +1 -1
  133. package/examples/extensions/custom-compaction.ts +1 -0
  134. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  135. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  136. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  137. package/examples/extensions/gondolin/package-lock.json +2 -2
  138. package/examples/extensions/gondolin/package.json +1 -1
  139. package/examples/extensions/sandbox/package-lock.json +2 -2
  140. package/examples/extensions/sandbox/package.json +1 -1
  141. package/examples/extensions/with-deps/package-lock.json +2 -2
  142. package/examples/extensions/with-deps/package.json +1 -1
  143. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import { uuidv7 } from "@earendil-works/pi-agent-core";
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 fd = openSync(filePath, "r");
314
- const buffer = Buffer.alloc(512);
315
- const bytesRead = readSync(fd, buffer, 0, 512, 0);
316
- closeSync(fd);
317
- const firstLine = buffer.toString("utf8", 0, bytesRead).split("\n")[0];
318
- if (!firstLine)
319
- return null;
320
- const header = JSON.parse(firstLine);
321
- if (header.type !== "session" || typeof header.id !== "string") {
322
- return null;
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
- return header;
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: readSessionHeader(path) }))
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.setSessionFile(sessionFile);
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
- // Extract cwd from session header if possible, otherwise use process.cwd()
1177
- const entries = loadEntriesFromFile(resolvedPath);
1178
- const header = entries.find((e) => e.type === "session");
1179
- const cwd = cwdOverride ?? header?.cwd ?? process.cwd();
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.