@earendil-works/pi-coding-agent 0.78.0 → 0.79.0

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 (203) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +26 -6
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +15 -2
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +9 -1
  9. package/dist/config.js.map +1 -1
  10. package/dist/core/agent-session-runtime.d.ts +3 -1
  11. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  12. package/dist/core/agent-session-runtime.js +1 -0
  13. package/dist/core/agent-session-runtime.js.map +1 -1
  14. package/dist/core/agent-session-services.d.ts +2 -1
  15. package/dist/core/agent-session-services.d.ts.map +1 -1
  16. package/dist/core/agent-session-services.js +2 -2
  17. package/dist/core/agent-session-services.js.map +1 -1
  18. package/dist/core/agent-session.d.ts +3 -1
  19. package/dist/core/agent-session.d.ts.map +1 -1
  20. package/dist/core/agent-session.js +7 -1
  21. package/dist/core/agent-session.js.map +1 -1
  22. package/dist/core/auth-storage.d.ts.map +1 -1
  23. package/dist/core/auth-storage.js +4 -3
  24. package/dist/core/auth-storage.js.map +1 -1
  25. package/dist/core/compaction/branch-summarization.d.ts +3 -1
  26. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  27. package/dist/core/compaction/branch-summarization.js +9 -3
  28. package/dist/core/compaction/branch-summarization.js.map +1 -1
  29. package/dist/core/compaction/utils.d.ts +1 -1
  30. package/dist/core/compaction/utils.d.ts.map +1 -1
  31. package/dist/core/compaction/utils.js +1 -1
  32. package/dist/core/compaction/utils.js.map +1 -1
  33. package/dist/core/export-html/template.js +19 -6
  34. package/dist/core/extensions/index.d.ts +1 -1
  35. package/dist/core/extensions/index.d.ts.map +1 -1
  36. package/dist/core/extensions/index.js.map +1 -1
  37. package/dist/core/extensions/loader.d.ts +1 -1
  38. package/dist/core/extensions/loader.d.ts.map +1 -1
  39. package/dist/core/extensions/loader.js +4 -4
  40. package/dist/core/extensions/loader.js.map +1 -1
  41. package/dist/core/extensions/runner.d.ts +9 -3
  42. package/dist/core/extensions/runner.d.ts.map +1 -1
  43. package/dist/core/extensions/runner.js +41 -1
  44. package/dist/core/extensions/runner.js.map +1 -1
  45. package/dist/core/extensions/types.d.ts +25 -2
  46. package/dist/core/extensions/types.d.ts.map +1 -1
  47. package/dist/core/extensions/types.js.map +1 -1
  48. package/dist/core/footer-data-provider.d.ts +2 -0
  49. package/dist/core/footer-data-provider.d.ts.map +1 -1
  50. package/dist/core/footer-data-provider.js +29 -1
  51. package/dist/core/footer-data-provider.js.map +1 -1
  52. package/dist/core/model-registry.d.ts.map +1 -1
  53. package/dist/core/model-registry.js +1 -0
  54. package/dist/core/model-registry.js.map +1 -1
  55. package/dist/core/model-resolver.d.ts.map +1 -1
  56. package/dist/core/model-resolver.js +3 -0
  57. package/dist/core/model-resolver.js.map +1 -1
  58. package/dist/core/package-manager.d.ts +3 -0
  59. package/dist/core/package-manager.d.ts.map +1 -1
  60. package/dist/core/package-manager.js +47 -13
  61. package/dist/core/package-manager.js.map +1 -1
  62. package/dist/core/provider-attribution.d.ts +4 -0
  63. package/dist/core/provider-attribution.d.ts.map +1 -0
  64. package/dist/core/provider-attribution.js +72 -0
  65. package/dist/core/provider-attribution.js.map +1 -0
  66. package/dist/core/provider-display-names.d.ts.map +1 -1
  67. package/dist/core/provider-display-names.js +3 -0
  68. package/dist/core/provider-display-names.js.map +1 -1
  69. package/dist/core/resource-loader.d.ts +13 -2
  70. package/dist/core/resource-loader.d.ts.map +1 -1
  71. package/dist/core/resource-loader.js +129 -54
  72. package/dist/core/resource-loader.js.map +1 -1
  73. package/dist/core/sdk.d.ts.map +1 -1
  74. package/dist/core/sdk.js +7 -33
  75. package/dist/core/sdk.js.map +1 -1
  76. package/dist/core/session-manager.d.ts.map +1 -1
  77. package/dist/core/session-manager.js +92 -68
  78. package/dist/core/session-manager.js.map +1 -1
  79. package/dist/core/settings-manager.d.ts +10 -2
  80. package/dist/core/settings-manager.d.ts.map +1 -1
  81. package/dist/core/settings-manager.js +71 -30
  82. package/dist/core/settings-manager.js.map +1 -1
  83. package/dist/core/slash-commands.d.ts.map +1 -1
  84. package/dist/core/slash-commands.js +1 -0
  85. package/dist/core/slash-commands.js.map +1 -1
  86. package/dist/core/tools/bash.d.ts.map +1 -1
  87. package/dist/core/tools/bash.js +1 -1
  88. package/dist/core/tools/bash.js.map +1 -1
  89. package/dist/core/tools/find.d.ts.map +1 -1
  90. package/dist/core/tools/find.js +1 -1
  91. package/dist/core/tools/find.js.map +1 -1
  92. package/dist/core/tools/grep.d.ts.map +1 -1
  93. package/dist/core/tools/grep.js +1 -1
  94. package/dist/core/tools/grep.js.map +1 -1
  95. package/dist/core/tools/ls.d.ts.map +1 -1
  96. package/dist/core/tools/ls.js +1 -1
  97. package/dist/core/tools/ls.js.map +1 -1
  98. package/dist/core/tools/read.d.ts.map +1 -1
  99. package/dist/core/tools/read.js +1 -1
  100. package/dist/core/tools/read.js.map +1 -1
  101. package/dist/core/tools/write.d.ts.map +1 -1
  102. package/dist/core/tools/write.js +1 -1
  103. package/dist/core/tools/write.js.map +1 -1
  104. package/dist/core/trust-manager.d.ts +10 -0
  105. package/dist/core/trust-manager.d.ts.map +1 -0
  106. package/dist/core/trust-manager.js +133 -0
  107. package/dist/core/trust-manager.js.map +1 -0
  108. package/dist/index.d.ts +5 -4
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +2 -1
  111. package/dist/index.js.map +1 -1
  112. package/dist/main.d.ts.map +1 -1
  113. package/dist/main.js +195 -6
  114. package/dist/main.js.map +1 -1
  115. package/dist/modes/index.d.ts +1 -1
  116. package/dist/modes/index.d.ts.map +1 -1
  117. package/dist/modes/index.js.map +1 -1
  118. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/bash-execution.js +2 -2
  120. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  121. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/footer.js +7 -0
  123. package/dist/modes/interactive/components/footer.js.map +1 -1
  124. package/dist/modes/interactive/components/index.d.ts +1 -0
  125. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  126. package/dist/modes/interactive/components/index.js +1 -0
  127. package/dist/modes/interactive/components/index.js.map +1 -1
  128. package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
  129. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/login-dialog.js +3 -12
  131. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  132. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  133. package/dist/modes/interactive/components/tool-execution.js +22 -0
  134. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  135. package/dist/modes/interactive/components/trust-selector.d.ts +20 -0
  136. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
  137. package/dist/modes/interactive/components/trust-selector.js +86 -0
  138. package/dist/modes/interactive/components/trust-selector.js.map +1 -0
  139. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  140. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  141. package/dist/modes/interactive/interactive-mode.js +87 -1
  142. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  143. package/dist/modes/print-mode.d.ts.map +1 -1
  144. package/dist/modes/print-mode.js +1 -0
  145. package/dist/modes/print-mode.js.map +1 -1
  146. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-mode.js +1 -0
  148. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  149. package/dist/package-manager-cli.d.ts.map +1 -1
  150. package/dist/package-manager-cli.js +55 -8
  151. package/dist/package-manager-cli.js.map +1 -1
  152. package/dist/utils/git.d.ts.map +1 -1
  153. package/dist/utils/git.js +54 -22
  154. package/dist/utils/git.js.map +1 -1
  155. package/dist/utils/open-browser.d.ts +9 -0
  156. package/dist/utils/open-browser.d.ts.map +1 -0
  157. package/dist/utils/open-browser.js +22 -0
  158. package/dist/utils/open-browser.js.map +1 -0
  159. package/docs/containerization.md +111 -0
  160. package/docs/docs.json +8 -0
  161. package/docs/extensions.md +58 -12
  162. package/docs/index.md +2 -0
  163. package/docs/packages.md +3 -1
  164. package/docs/prompt-templates.md +1 -1
  165. package/docs/providers.md +5 -0
  166. package/docs/rpc.md +1 -1
  167. package/docs/sdk.md +5 -0
  168. package/docs/security.md +57 -0
  169. package/docs/settings.md +10 -0
  170. package/docs/skills.md +1 -1
  171. package/docs/terminal-setup.md +36 -2
  172. package/docs/themes.md +1 -1
  173. package/docs/tmux.md +4 -2
  174. package/docs/tui.md +10 -1
  175. package/docs/usage.md +16 -4
  176. package/examples/extensions/README.md +2 -0
  177. package/examples/extensions/custom-header.ts +1 -1
  178. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  179. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  180. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  181. package/examples/extensions/doom-overlay/index.ts +1 -1
  182. package/examples/extensions/gondolin/index.ts +531 -0
  183. package/examples/extensions/gondolin/package-lock.json +185 -0
  184. package/examples/extensions/gondolin/package.json +19 -0
  185. package/examples/extensions/handoff.ts +1 -1
  186. package/examples/extensions/interactive-shell.ts +1 -1
  187. package/examples/extensions/overlay-qa-tests.ts +152 -81
  188. package/examples/extensions/project-trust.ts +64 -0
  189. package/examples/extensions/qna.ts +1 -1
  190. package/examples/extensions/question.ts +1 -1
  191. package/examples/extensions/questionnaire.ts +1 -1
  192. package/examples/extensions/sandbox/package-lock.json +2 -2
  193. package/examples/extensions/sandbox/package.json +1 -1
  194. package/examples/extensions/snake.ts +1 -1
  195. package/examples/extensions/space-invaders.ts +1 -1
  196. package/examples/extensions/summarize.ts +1 -1
  197. package/examples/extensions/tic-tac-toe.ts +1 -1
  198. package/examples/extensions/todo.ts +1 -1
  199. package/examples/extensions/tools.ts +5 -0
  200. package/examples/extensions/with-deps/package-lock.json +2 -2
  201. package/examples/extensions/with-deps/package.json +1 -1
  202. package/npm-shrinkwrap.json +12 -12
  203. package/package.json +5 -8
@@ -1,8 +1,10 @@
1
1
  import { uuidv7 } from "@earendil-works/pi-agent-core";
2
2
  import { randomUUID } from "crypto";
3
- import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, statSync, writeFileSync, } from "fs";
4
- import { readdir, readFile, stat } from "fs/promises";
3
+ import { appendFileSync, closeSync, createReadStream, existsSync, mkdirSync, openSync, readdirSync, readSync, statSync, writeFileSync, } from "fs";
4
+ import { readdir, stat } from "fs/promises";
5
5
  import { join, resolve } from "path";
6
+ import { createInterface } from "readline";
7
+ import { StringDecoder } from "string_decoder";
6
8
  import { getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.js";
7
9
  import { normalizePath, resolvePath } from "../utils/paths.js";
8
10
  import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage, } from "./messages.js";
@@ -228,24 +230,52 @@ export function getDefaultSessionDir(cwd, agentDir = getDefaultAgentDir()) {
228
230
  }
229
231
  return sessionDir;
230
232
  }
233
+ const SESSION_READ_BUFFER_SIZE = 1024 * 1024;
234
+ function parseSessionEntryLine(line) {
235
+ if (!line.trim())
236
+ return null;
237
+ try {
238
+ return JSON.parse(line);
239
+ }
240
+ catch {
241
+ // Skip malformed lines
242
+ return null;
243
+ }
244
+ }
231
245
  /** Exported for testing */
232
246
  export function loadEntriesFromFile(filePath) {
233
247
  const resolvedFilePath = normalizePath(filePath);
234
248
  if (!existsSync(resolvedFilePath))
235
249
  return [];
236
- const content = readFileSync(resolvedFilePath, "utf8");
237
250
  const entries = [];
238
- const lines = content.trim().split("\n");
239
- for (const line of lines) {
240
- if (!line.trim())
241
- continue;
242
- try {
243
- const entry = JSON.parse(line);
244
- entries.push(entry);
245
- }
246
- catch {
247
- // Skip malformed lines
251
+ const fd = openSync(resolvedFilePath, "r");
252
+ try {
253
+ const decoder = new StringDecoder("utf8");
254
+ const buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);
255
+ let pending = "";
256
+ while (true) {
257
+ const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
258
+ if (bytesRead === 0)
259
+ break;
260
+ pending += decoder.write(buffer.subarray(0, bytesRead));
261
+ let lineStart = 0;
262
+ let newlineIndex = pending.indexOf("\n", lineStart);
263
+ while (newlineIndex !== -1) {
264
+ const entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));
265
+ if (entry)
266
+ entries.push(entry);
267
+ lineStart = newlineIndex + 1;
268
+ newlineIndex = pending.indexOf("\n", lineStart);
269
+ }
270
+ pending = pending.slice(lineStart);
248
271
  }
272
+ pending += decoder.end();
273
+ const finalEntry = parseSessionEntryLine(pending);
274
+ if (finalEntry)
275
+ entries.push(finalEntry);
276
+ }
277
+ finally {
278
+ closeSync(fd);
249
279
  }
250
280
  // Validate session header
251
281
  if (entries.length === 0)
@@ -314,73 +344,53 @@ function extractTextContent(message) {
314
344
  .map((block) => block.text)
315
345
  .join(" ");
316
346
  }
317
- function getLastActivityTime(entries) {
318
- let lastActivityTime;
319
- for (const entry of entries) {
320
- if (entry.type !== "message")
321
- continue;
322
- const message = entry.message;
323
- if (!isMessageWithContent(message))
324
- continue;
325
- if (message.role !== "user" && message.role !== "assistant")
326
- continue;
327
- const msgTimestamp = message.timestamp;
328
- if (typeof msgTimestamp === "number") {
329
- lastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);
330
- continue;
331
- }
332
- const entryTimestamp = entry.timestamp;
333
- if (typeof entryTimestamp === "string") {
334
- const t = new Date(entryTimestamp).getTime();
335
- if (!Number.isNaN(t)) {
336
- lastActivityTime = Math.max(lastActivityTime ?? 0, t);
337
- }
338
- }
339
- }
340
- return lastActivityTime;
341
- }
342
- function getSessionModifiedDate(entries, header, statsMtime) {
343
- const lastActivityTime = getLastActivityTime(entries);
344
- if (typeof lastActivityTime === "number" && lastActivityTime > 0) {
345
- return new Date(lastActivityTime);
347
+ function getMessageActivityTime(entry) {
348
+ const message = entry.message;
349
+ if (!isMessageWithContent(message))
350
+ return undefined;
351
+ if (message.role !== "user" && message.role !== "assistant")
352
+ return undefined;
353
+ const msgTimestamp = message.timestamp;
354
+ if (typeof msgTimestamp === "number") {
355
+ return msgTimestamp;
346
356
  }
347
- const headerTime = typeof header.timestamp === "string" ? new Date(header.timestamp).getTime() : NaN;
348
- return !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;
357
+ const t = new Date(entry.timestamp).getTime();
358
+ return Number.isNaN(t) ? undefined : t;
349
359
  }
350
360
  async function buildSessionInfo(filePath) {
351
361
  try {
352
- const content = await readFile(filePath, "utf8");
353
- const entries = [];
354
- const lines = content.trim().split("\n");
355
- for (const line of lines) {
356
- if (!line.trim())
357
- continue;
358
- try {
359
- entries.push(JSON.parse(line));
360
- }
361
- catch {
362
- // Skip malformed lines
363
- }
364
- }
365
- if (entries.length === 0)
366
- return null;
367
- const header = entries[0];
368
- if (header.type !== "session")
369
- return null;
370
362
  const stats = await stat(filePath);
363
+ let header = null;
371
364
  let messageCount = 0;
372
365
  let firstMessage = "";
373
366
  const allMessages = [];
374
367
  let name;
375
- for (const entry of entries) {
368
+ let lastActivityTime;
369
+ const rl = createInterface({
370
+ input: createReadStream(filePath, { encoding: "utf8" }),
371
+ crlfDelay: Infinity,
372
+ });
373
+ for await (const line of rl) {
374
+ const entry = parseSessionEntryLine(line);
375
+ if (!entry)
376
+ continue;
377
+ if (!header) {
378
+ if (entry.type !== "session")
379
+ return null;
380
+ header = entry;
381
+ continue;
382
+ }
376
383
  // Extract session name (use latest, including explicit clears)
377
384
  if (entry.type === "session_info") {
378
- const infoEntry = entry;
379
- name = infoEntry.name?.trim() || undefined;
385
+ name = entry.name?.trim() || undefined;
380
386
  }
381
387
  if (entry.type !== "message")
382
388
  continue;
383
389
  messageCount++;
390
+ const activityTime = getMessageActivityTime(entry);
391
+ if (typeof activityTime === "number") {
392
+ lastActivityTime = Math.max(lastActivityTime ?? 0, activityTime);
393
+ }
384
394
  const message = entry.message;
385
395
  if (!isMessageWithContent(message))
386
396
  continue;
@@ -394,9 +404,16 @@ async function buildSessionInfo(filePath) {
394
404
  firstMessage = textContent;
395
405
  }
396
406
  }
407
+ if (!header)
408
+ return null;
397
409
  const cwd = typeof header.cwd === "string" ? header.cwd : "";
398
410
  const parentSessionPath = header.parentSession;
399
- const modified = getSessionModifiedDate(entries, header, stats.mtime);
411
+ const headerTime = typeof header.timestamp === "string" ? new Date(header.timestamp).getTime() : NaN;
412
+ const modified = typeof lastActivityTime === "number" && lastActivityTime > 0
413
+ ? new Date(lastActivityTime)
414
+ : !Number.isNaN(headerTime)
415
+ ? new Date(headerTime)
416
+ : stats.mtime;
400
417
  return {
401
418
  path: filePath,
402
419
  id: header.id,
@@ -589,8 +606,15 @@ export class SessionManager {
589
606
  _rewriteFile() {
590
607
  if (!this.persist || !this.sessionFile)
591
608
  return;
592
- const content = `${this.fileEntries.map((e) => JSON.stringify(e)).join("\n")}\n`;
593
- writeFileSync(this.sessionFile, content);
609
+ const fd = openSync(this.sessionFile, "w");
610
+ try {
611
+ for (const entry of this.fileEntries) {
612
+ writeFileSync(fd, `${JSON.stringify(entry)}\n`);
613
+ }
614
+ }
615
+ finally {
616
+ closeSync(fd);
617
+ }
594
618
  }
595
619
  isPersisted() {
596
620
  return this.persist;