@earendil-works/pi-coding-agent 0.80.2 → 0.80.3

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 (118) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +2 -1
  3. package/dist/cli/file-processor.d.ts.map +1 -1
  4. package/dist/cli/file-processor.js +13 -25
  5. package/dist/cli/file-processor.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +2 -1
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +38 -28
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/extensions/index.d.ts +1 -1
  11. package/dist/core/extensions/index.d.ts.map +1 -1
  12. package/dist/core/extensions/index.js.map +1 -1
  13. package/dist/core/extensions/loader.d.ts.map +1 -1
  14. package/dist/core/extensions/loader.js +4 -0
  15. package/dist/core/extensions/loader.js.map +1 -1
  16. package/dist/core/extensions/types.d.ts +8 -1
  17. package/dist/core/extensions/types.d.ts.map +1 -1
  18. package/dist/core/extensions/types.js.map +1 -1
  19. package/dist/core/http-dispatcher.d.ts.map +1 -1
  20. package/dist/core/http-dispatcher.js +28 -1
  21. package/dist/core/http-dispatcher.js.map +1 -1
  22. package/dist/core/model-resolver.d.ts.map +1 -1
  23. package/dist/core/model-resolver.js +1 -1
  24. package/dist/core/model-resolver.js.map +1 -1
  25. package/dist/core/resource-loader.d.ts.map +1 -1
  26. package/dist/core/resource-loader.js +2 -0
  27. package/dist/core/resource-loader.js.map +1 -1
  28. package/dist/core/session-manager.d.ts +1 -1
  29. package/dist/core/session-manager.d.ts.map +1 -1
  30. package/dist/core/session-manager.js +7 -4
  31. package/dist/core/session-manager.js.map +1 -1
  32. package/dist/core/settings-manager.d.ts +5 -0
  33. package/dist/core/settings-manager.d.ts.map +1 -1
  34. package/dist/core/settings-manager.js +19 -0
  35. package/dist/core/settings-manager.js.map +1 -1
  36. package/dist/core/timings.d.ts +4 -2
  37. package/dist/core/timings.d.ts.map +1 -1
  38. package/dist/core/timings.js +24 -14
  39. package/dist/core/timings.js.map +1 -1
  40. package/dist/core/tools/read.d.ts.map +1 -1
  41. package/dist/core/tools/read.js +12 -25
  42. package/dist/core/tools/read.js.map +1 -1
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/main.d.ts.map +1 -1
  47. package/dist/main.js +16 -4
  48. package/dist/main.js.map +1 -1
  49. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  50. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  51. package/dist/modes/interactive/components/assistant-message.js +23 -15
  52. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  53. package/dist/modes/interactive/components/extension-editor.d.ts +3 -1
  54. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  55. package/dist/modes/interactive/components/extension-editor.js +12 -3
  56. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  57. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  58. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/settings-selector.js +13 -1
  60. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  62. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
  63. package/dist/modes/interactive/components/status-indicator.js +60 -0
  64. package/dist/modes/interactive/components/status-indicator.js.map +1 -0
  65. package/dist/modes/interactive/components/user-message.d.ts +6 -2
  66. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  67. package/dist/modes/interactive/components/user-message.js +19 -6
  68. package/dist/modes/interactive/components/user-message.js.map +1 -1
  69. package/dist/modes/interactive/interactive-mode.d.ts +6 -7
  70. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  71. package/dist/modes/interactive/interactive-mode.js +115 -135
  72. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  73. package/dist/modes/rpc/rpc-client.d.ts +15 -0
  74. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  75. package/dist/modes/rpc/rpc-client.js +14 -0
  76. package/dist/modes/rpc/rpc-client.js.map +1 -1
  77. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  78. package/dist/modes/rpc/rpc-mode.js +16 -0
  79. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  80. package/dist/modes/rpc/rpc-types.d.ts +26 -0
  81. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  82. package/dist/modes/rpc/rpc-types.js.map +1 -1
  83. package/dist/rpc-entry.d.ts +3 -0
  84. package/dist/rpc-entry.d.ts.map +1 -0
  85. package/dist/rpc-entry.js +10 -0
  86. package/dist/rpc-entry.js.map +1 -0
  87. package/dist/utils/image-convert.d.ts +1 -0
  88. package/dist/utils/image-convert.d.ts.map +1 -1
  89. package/dist/utils/image-convert.js +21 -15
  90. package/dist/utils/image-convert.js.map +1 -1
  91. package/dist/utils/image-process.d.ts +18 -0
  92. package/dist/utils/image-process.d.ts.map +1 -0
  93. package/dist/utils/image-process.js +83 -0
  94. package/dist/utils/image-process.js.map +1 -0
  95. package/dist/utils/mime.d.ts.map +1 -1
  96. package/dist/utils/mime.js +41 -0
  97. package/dist/utils/mime.js.map +1 -1
  98. package/docs/extensions.md +14 -0
  99. package/docs/keybindings.md +1 -1
  100. package/docs/providers.md +2 -1
  101. package/docs/rpc.md +58 -0
  102. package/docs/settings.md +10 -0
  103. package/docs/usage.md +2 -2
  104. package/examples/extensions/custom-compaction.ts +1 -0
  105. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  106. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  107. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  108. package/examples/extensions/gondolin/package-lock.json +2 -2
  109. package/examples/extensions/gondolin/package.json +1 -1
  110. package/examples/extensions/handoff.ts +1 -1
  111. package/examples/extensions/qna.ts +1 -1
  112. package/examples/extensions/sandbox/package-lock.json +2 -2
  113. package/examples/extensions/sandbox/package.json +1 -1
  114. package/examples/extensions/summarize.ts +1 -0
  115. package/examples/extensions/with-deps/package-lock.json +2 -2
  116. package/examples/extensions/with-deps/package.json +1 -1
  117. package/npm-shrinkwrap.json +12 -12
  118. package/package.json +8 -5
@@ -0,0 +1,83 @@
1
+ import { convertImageBytesToPng } from "./image-convert.js";
2
+ import { formatDimensionNote, resizeImage } from "./image-resize.js";
3
+ function baseMimeType(mimeType) {
4
+ return mimeType.split(";")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();
5
+ }
6
+ function normalizeSupportedImageMimeType(mimeType) {
7
+ switch (baseMimeType(mimeType)) {
8
+ case "image/png":
9
+ return "image/png";
10
+ case "image/jpeg":
11
+ case "image/jpg":
12
+ return "image/jpeg";
13
+ case "image/gif":
14
+ return "image/gif";
15
+ case "image/webp":
16
+ return "image/webp";
17
+ default:
18
+ return null;
19
+ }
20
+ }
21
+ async function normalizeImage(bytes, mimeType) {
22
+ const normalizedMimeType = normalizeSupportedImageMimeType(mimeType);
23
+ if (normalizedMimeType) {
24
+ return { bytes, mimeType: normalizedMimeType };
25
+ }
26
+ const pngBytes = await convertImageBytesToPng(bytes);
27
+ if (!pngBytes) {
28
+ return null;
29
+ }
30
+ return {
31
+ bytes: pngBytes,
32
+ mimeType: "image/png",
33
+ convertedFrom: baseMimeType(mimeType),
34
+ };
35
+ }
36
+ function conversionHint(from, to) {
37
+ if (!from || from === to)
38
+ return undefined;
39
+ return `[Image converted from ${from} to ${to}.]`;
40
+ }
41
+ export async function processImage(bytes, mimeType, options) {
42
+ const autoResizeImages = options?.autoResizeImages ?? true;
43
+ const normalized = await normalizeImage(bytes, mimeType);
44
+ if (!normalized) {
45
+ return {
46
+ ok: false,
47
+ message: "[Image omitted: could not be converted to a supported inline image format.]",
48
+ };
49
+ }
50
+ if (autoResizeImages) {
51
+ const resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);
52
+ if (!resized) {
53
+ return {
54
+ ok: false,
55
+ message: "[Image omitted: could not be resized below the inline image size limit.]",
56
+ };
57
+ }
58
+ const hints = [];
59
+ const convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);
60
+ if (convertedHint)
61
+ hints.push(convertedHint);
62
+ const dimensionNote = formatDimensionNote(resized);
63
+ if (dimensionNote)
64
+ hints.push(dimensionNote);
65
+ return {
66
+ ok: true,
67
+ data: resized.data,
68
+ mimeType: resized.mimeType,
69
+ hints,
70
+ };
71
+ }
72
+ const hints = [];
73
+ const convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);
74
+ if (convertedHint)
75
+ hints.push(convertedHint);
76
+ return {
77
+ ok: true,
78
+ data: Buffer.from(normalized.bytes).toString("base64"),
79
+ mimeType: normalized.mimeType,
80
+ hints,
81
+ };
82
+ }
83
+ //# sourceMappingURL=image-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-process.js","sourceRoot":"","sources":["../../src/utils/image-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAA2B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA2B9F,SAAS,YAAY,CAAC,QAAgB,EAAU;IAC/C,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;AAAA,CAC9E;AAED,SAAS,+BAA+B,CAAC,QAAgB,EAAiB;IACzE,QAAQ,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,KAAK,WAAW;YACf,OAAO,WAAW,CAAC;QACpB,KAAK,YAAY,CAAC;QAClB,KAAK,WAAW;YACf,OAAO,YAAY,CAAC;QACrB,KAAK,WAAW;YACf,OAAO,WAAW,CAAC;QACpB,KAAK,YAAY;YAChB,OAAO,YAAY,CAAC;QACrB;YACC,OAAO,IAAI,CAAC;IACd,CAAC;AAAA,CACD;AAED,KAAK,UAAU,cAAc,CAAC,KAAiB,EAAE,QAAgB,EAAmC;IACnG,MAAM,kBAAkB,GAAG,+BAA+B,CAAC,QAAQ,CAAC,CAAC;IACrE,IAAI,kBAAkB,EAAE,CAAC;QACxB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO;QACN,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,WAAW;QACrB,aAAa,EAAE,YAAY,CAAC,QAAQ,CAAC;KACrC,CAAC;AAAA,CACF;AAED,SAAS,cAAc,CAAC,IAAwB,EAAE,EAAU,EAAsB;IACjF,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,yBAAyB,IAAI,OAAO,EAAE,IAAI,CAAC;AAAA,CAClD;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,KAAiB,EACjB,QAAgB,EAChB,OAA6B,EACC;IAC9B,MAAM,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,IAAI,CAAC;IAC3D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,6EAA6E;SACtF,CAAC;IACH,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QACjG,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,OAAO,EAAE,0EAA0E;aACnF,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,aAAa,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjF,IAAI,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAE7C,OAAO;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK;SACL,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IACpF,IAAI,aAAa;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAE7C,OAAO;QACN,EAAE,EAAE,IAAI;QACR,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACtD,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,KAAK;KACL,CAAC;AAAA,CACF","sourcesContent":["import { convertImageBytesToPng } from \"./image-convert.ts\";\nimport { formatDimensionNote, type ImageResizeOptions, resizeImage } from \"./image-resize.ts\";\n\nexport interface ProcessImageOptions {\n\t/** Whether to resize images to inline provider limits. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Optional resize overrides. Uses resizeImage defaults when omitted. */\n\tresizeOptions?: ImageResizeOptions;\n}\n\nexport type ProcessImageResult =\n\t| {\n\t\t\tok: true;\n\t\t\tdata: string;\n\t\t\tmimeType: string;\n\t\t\thints: string[];\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\tmessage: string;\n\t };\n\ninterface NormalizedImage {\n\tbytes: Uint8Array;\n\tmimeType: string;\n\tconvertedFrom?: string;\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nfunction normalizeSupportedImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"image/png\";\n\t\tcase \"image/jpeg\":\n\t\tcase \"image/jpg\":\n\t\t\treturn \"image/jpeg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"image/gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"image/webp\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nasync function normalizeImage(bytes: Uint8Array, mimeType: string): Promise<NormalizedImage | null> {\n\tconst normalizedMimeType = normalizeSupportedImageMimeType(mimeType);\n\tif (normalizedMimeType) {\n\t\treturn { bytes, mimeType: normalizedMimeType };\n\t}\n\n\tconst pngBytes = await convertImageBytesToPng(bytes);\n\tif (!pngBytes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tbytes: pngBytes,\n\t\tmimeType: \"image/png\",\n\t\tconvertedFrom: baseMimeType(mimeType),\n\t};\n}\n\nfunction conversionHint(from: string | undefined, to: string): string | undefined {\n\tif (!from || from === to) return undefined;\n\treturn `[Image converted from ${from} to ${to}.]`;\n}\n\nexport async function processImage(\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ProcessImageOptions,\n): Promise<ProcessImageResult> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst normalized = await normalizeImage(bytes, mimeType);\n\tif (!normalized) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: \"[Image omitted: could not be converted to a supported inline image format.]\",\n\t\t};\n\t}\n\n\tif (autoResizeImages) {\n\t\tconst resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);\n\t\tif (!resized) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: \"[Image omitted: could not be resized below the inline image size limit.]\",\n\t\t\t};\n\t\t}\n\n\t\tconst hints: string[] = [];\n\t\tconst convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);\n\t\tif (convertedHint) hints.push(convertedHint);\n\t\tconst dimensionNote = formatDimensionNote(resized);\n\t\tif (dimensionNote) hints.push(dimensionNote);\n\n\t\treturn {\n\t\t\tok: true,\n\t\t\tdata: resized.data,\n\t\t\tmimeType: resized.mimeType,\n\t\t\thints,\n\t\t};\n\t}\n\n\tconst hints: string[] = [];\n\tconst convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);\n\tif (convertedHint) hints.push(convertedHint);\n\n\treturn {\n\t\tok: true,\n\t\tdata: Buffer.from(normalized.bytes).toString(\"base64\"),\n\t\tmimeType: normalized.mimeType,\n\t\thints,\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAc9E;AAED,wBAAsB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASnG","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\treturn null;\n}\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\treturn detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAED,wBAAsB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASnG","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"BM\") && isBmp(buffer)) {\n\t\treturn \"image/bmp\";\n\t}\n\treturn null;\n}\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\treturn detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction isBmp(buffer: Uint8Array): boolean {\n\tif (buffer.length < 26) return false;\n\n\tconst declaredFileSize = readUint32LE(buffer, 2);\n\tconst pixelDataOffset = readUint32LE(buffer, 10);\n\tconst dibHeaderSize = readUint32LE(buffer, 14);\n\tif (declaredFileSize !== 0 && declaredFileSize < 26) return false;\n\tif (pixelDataOffset < 14 + dibHeaderSize) return false;\n\tif (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize) return false;\n\n\tlet colorPlanes: number;\n\tlet bitsPerPixel: number;\n\tif (dibHeaderSize === 12) {\n\t\tcolorPlanes = readUint16LE(buffer, 22);\n\t\tbitsPerPixel = readUint16LE(buffer, 24);\n\t} else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {\n\t\tif (buffer.length < 30) return false;\n\t\tcolorPlanes = readUint16LE(buffer, 26);\n\t\tbitsPerPixel = readUint16LE(buffer, 28);\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);\n}\n\nfunction readUint16LE(buffer: Uint8Array, offset: number): number {\n\treturn (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction readUint32LE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) +\n\t\t((buffer[offset + 1] ?? 0) << 8) +\n\t\t((buffer[offset + 2] ?? 0) << 16) +\n\t\t(buffer[offset + 3] ?? 0) * 0x1000000\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
@@ -14,6 +14,9 @@ export function detectSupportedImageMimeType(buffer) {
14
14
  if (startsWithAscii(buffer, 0, "RIFF") && startsWithAscii(buffer, 8, "WEBP")) {
15
15
  return "image/webp";
16
16
  }
17
+ if (startsWithAscii(buffer, 0, "BM") && isBmp(buffer)) {
18
+ return "image/bmp";
19
+ }
17
20
  return null;
18
21
  }
19
22
  export async function detectSupportedImageMimeTypeFromFile(filePath) {
@@ -46,12 +49,50 @@ function isAnimatedPng(buffer) {
46
49
  }
47
50
  return false;
48
51
  }
52
+ function isBmp(buffer) {
53
+ if (buffer.length < 26)
54
+ return false;
55
+ const declaredFileSize = readUint32LE(buffer, 2);
56
+ const pixelDataOffset = readUint32LE(buffer, 10);
57
+ const dibHeaderSize = readUint32LE(buffer, 14);
58
+ if (declaredFileSize !== 0 && declaredFileSize < 26)
59
+ return false;
60
+ if (pixelDataOffset < 14 + dibHeaderSize)
61
+ return false;
62
+ if (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize)
63
+ return false;
64
+ let colorPlanes;
65
+ let bitsPerPixel;
66
+ if (dibHeaderSize === 12) {
67
+ colorPlanes = readUint16LE(buffer, 22);
68
+ bitsPerPixel = readUint16LE(buffer, 24);
69
+ }
70
+ else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {
71
+ if (buffer.length < 30)
72
+ return false;
73
+ colorPlanes = readUint16LE(buffer, 26);
74
+ bitsPerPixel = readUint16LE(buffer, 28);
75
+ }
76
+ else {
77
+ return false;
78
+ }
79
+ return colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);
80
+ }
81
+ function readUint16LE(buffer, offset) {
82
+ return (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);
83
+ }
49
84
  function readUint32BE(buffer, offset) {
50
85
  return ((buffer[offset] ?? 0) * 0x1000000 +
51
86
  ((buffer[offset + 1] ?? 0) << 16) +
52
87
  ((buffer[offset + 2] ?? 0) << 8) +
53
88
  (buffer[offset + 3] ?? 0));
54
89
  }
90
+ function readUint32LE(buffer, offset) {
91
+ return ((buffer[offset] ?? 0) +
92
+ ((buffer[offset + 1] ?? 0) << 8) +
93
+ ((buffer[offset + 2] ?? 0) << 16) +
94
+ (buffer[offset + 3] ?? 0) * 0x1000000);
95
+ }
55
96
  function startsWith(buffer, bytes) {
56
97
  if (buffer.length < bytes.length)
57
98
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"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","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\treturn null;\n}\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\treturn detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"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","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"BM\") && isBmp(buffer)) {\n\t\treturn \"image/bmp\";\n\t}\n\treturn null;\n}\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\treturn detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction isBmp(buffer: Uint8Array): boolean {\n\tif (buffer.length < 26) return false;\n\n\tconst declaredFileSize = readUint32LE(buffer, 2);\n\tconst pixelDataOffset = readUint32LE(buffer, 10);\n\tconst dibHeaderSize = readUint32LE(buffer, 14);\n\tif (declaredFileSize !== 0 && declaredFileSize < 26) return false;\n\tif (pixelDataOffset < 14 + dibHeaderSize) return false;\n\tif (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize) return false;\n\n\tlet colorPlanes: number;\n\tlet bitsPerPixel: number;\n\tif (dibHeaderSize === 12) {\n\t\tcolorPlanes = readUint16LE(buffer, 22);\n\t\tbitsPerPixel = readUint16LE(buffer, 24);\n\t} else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {\n\t\tif (buffer.length < 30) return false;\n\t\tcolorPlanes = readUint16LE(buffer, 26);\n\t\tbitsPerPixel = readUint16LE(buffer, 28);\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);\n}\n\nfunction readUint16LE(buffer: Uint8Array, offset: number): number {\n\treturn (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction readUint32LE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) +\n\t\t((buffer[offset + 1] ?? 0) << 8) +\n\t\t((buffer[offset + 2] ?? 0) << 16) +\n\t\t(buffer[offset + 3] ?? 0) * 0x1000000\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
@@ -322,6 +322,9 @@ user sends another prompt ◄─────────────────
322
322
  ├─► session_start { reason: "fork", previousSessionFile }
323
323
  └─► resources_discover { reason: "startup" }
324
324
 
325
+ /name or pi.setSessionName()
326
+ └─► session_info_changed
327
+
325
328
  /compact or auto-compaction
326
329
  ├─► session_before_compact (can cancel or customize)
327
330
  └─► session_compact
@@ -395,6 +398,17 @@ pi.on("session_start", async (event, ctx) => {
395
398
  });
396
399
  ```
397
400
 
401
+ #### session_info_changed
402
+
403
+ Fired when the current session display name is set via `/name`, RPC, or `pi.setSessionName()`.
404
+
405
+ ```typescript
406
+ pi.on("session_info_changed", async (event, ctx) => {
407
+ // event.name - current normalized name, or undefined if cleared
408
+ ctx.ui.notify(`Session renamed: ${event.name ?? "(none)"}`, "info");
409
+ });
410
+ ```
411
+
398
412
  #### session_before_switch
399
413
 
400
414
  Fired before starting a new session (`/new`) or switching sessions (`/resume`).
@@ -86,7 +86,7 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
86
86
  | `app.clear` | `ctrl+c` | Clear editor |
87
87
  | `app.exit` | `ctrl+d` | Exit (when editor empty) |
88
88
  | `app.suspend` | `ctrl+z` (none on Windows) | Suspend to background |
89
- | `app.editor.external` | `ctrl+g` | Open in external editor (`$VISUAL` or `$EDITOR`) |
89
+ | `app.editor.external` | `ctrl+g` | Open in external editor (`externalEditor`, `$VISUAL`, `$EDITOR`, Notepad on Windows, or `nano` elsewhere) |
90
90
  | `app.clipboard.pasteImage` | `ctrl+v` (`alt+v` on Windows) | Paste image from clipboard |
91
91
 
92
92
  ### Sessions
package/docs/providers.md CHANGED
@@ -156,8 +156,9 @@ OAuth credentials are also stored here after `/login` and managed automatically.
156
156
 
157
157
  ```bash
158
158
  export AZURE_OPENAI_API_KEY=...
159
- export AZURE_OPENAI_BASE_URL=https://your-resource.openai.azure.com
159
+ export AZURE_OPENAI_BASE_URL=https://your-resource.ai.azure.com
160
160
  # also supported: https://your-resource.cognitiveservices.azure.com
161
+ # also supported: https://your-resource.openai.azure.com
161
162
  # root endpoints are auto-normalized to /openai/v1
162
163
  # or use resource name instead of base URL
163
164
  export AZURE_OPENAI_RESOURCE_NAME=your-resource
package/docs/rpc.md CHANGED
@@ -661,6 +661,64 @@ Response:
661
661
  }
662
662
  ```
663
663
 
664
+ #### get_entries
665
+
666
+ Get all session entries in append order (excluding the session header). The session is an append-only tree of entries with stable ids, so an entry id works as a durable cursor: pass the last entry id you have seen as `since` to get only entries strictly after it, even across client restarts. Unlike `get_messages`, this includes pre-compaction history and abandoned branches.
667
+
668
+ ```json
669
+ {"type": "get_entries"}
670
+ ```
671
+
672
+ With a cursor:
673
+ ```json
674
+ {"type": "get_entries", "since": "abc123"}
675
+ ```
676
+
677
+ Response:
678
+ ```json
679
+ {
680
+ "type": "response",
681
+ "command": "get_entries",
682
+ "success": true,
683
+ "data": {
684
+ "entries": [
685
+ {"type": "message", "id": "def456", "parentId": "abc123", "timestamp": "...", "message": {"role": "user", "...": "..."}}
686
+ ],
687
+ "leafId": "def456"
688
+ }
689
+ }
690
+ ```
691
+
692
+ `leafId` is the id of the current leaf entry (`null` for an empty session), so a client can tell in one round trip whether the active branch moved. If `since` does not match any entry id, the response is `success: false`.
693
+
694
+ #### get_tree
695
+
696
+ Get the session as a tree of entries. Each node is `{entry, children, label?, labelTimestamp?}`. A well-formed session has a single root; orphaned entries (broken parent chain) also appear as roots.
697
+
698
+ ```json
699
+ {"type": "get_tree"}
700
+ ```
701
+
702
+ Response:
703
+ ```json
704
+ {
705
+ "type": "response",
706
+ "command": "get_tree",
707
+ "success": true,
708
+ "data": {
709
+ "tree": [
710
+ {
711
+ "entry": {"type": "message", "id": "abc123", "parentId": null, "...": "..."},
712
+ "children": [
713
+ {"entry": {"type": "message", "id": "def456", "parentId": "abc123", "...": "..."}, "children": []}
714
+ ]
715
+ }
716
+ ],
717
+ "leafId": "def456"
718
+ }
719
+ }
720
+ ```
721
+
664
722
  #### get_last_assistant_text
665
723
 
666
724
  Get the text content of the last assistant message.
package/docs/settings.md CHANGED
@@ -51,6 +51,7 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
51
51
  | Setting | Type | Default | Description |
52
52
  |---------|------|---------|-------------|
53
53
  | `theme` | string | `"dark"` | Theme name (`"dark"`, `"light"`, or custom) |
54
+ | `externalEditor` | string | `$VISUAL`, then `$EDITOR`, then Notepad on Windows or `nano` elsewhere | Command for Ctrl+G external editor; takes precedence over environment variables |
54
55
  | `quietStartup` | boolean | `false` | Hide startup header |
55
56
  | `defaultProjectTrust` | string | `"ask"` | Fallback project trust behavior: `"ask"`, `"always"`, or `"never"`. Global setting only |
56
57
  | `collapseChangelog` | boolean | `false` | Show condensed changelog after updates |
@@ -60,9 +61,18 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
60
61
  | `doubleEscapeAction` | string | `"tree"` | Action for double-escape: `"tree"`, `"fork"`, or `"none"` |
61
62
  | `treeFilterMode` | string | `"default"` | Default filter for `/tree`: `"default"`, `"no-tools"`, `"user-only"`, `"labeled-only"`, `"all"` |
62
63
  | `editorPaddingX` | number | `0` | Horizontal padding for input editor (0-3) |
64
+ | `outputPad` | number | `1` | Horizontal padding for user messages, assistant messages, and thinking (0 or 1) |
63
65
  | `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
64
66
  | `showHardwareCursor` | boolean | `false` | Show the terminal cursor while TUI positions it for IME support |
65
67
 
68
+ For VS Code, include `--wait` so pi resumes after the editor exits:
69
+
70
+ ```json
71
+ {
72
+ "externalEditor": "code --wait"
73
+ }
74
+ ```
75
+
66
76
  ### Telemetry and update checks
67
77
 
68
78
  `enableInstallTelemetry` only controls the anonymous install/update ping to `https://pi.dev/api/report-install`. Opting out of telemetry does not disable update checks; Pi can still fetch `https://pi.dev/api/latest-version` to look for the latest version.
package/docs/usage.md CHANGED
@@ -25,7 +25,7 @@ The editor can be replaced temporarily by built-in UI such as `/settings` or by
25
25
  | Images | Paste with Ctrl+V, Alt+V on Windows, or drag into the terminal |
26
26
  | Shell command | `!command` runs and sends output to the model |
27
27
  | Hidden shell command | `!!command` runs without sending output to the model |
28
- | External editor | Ctrl+G opens `$VISUAL` or `$EDITOR` |
28
+ | External editor | Ctrl+G opens `externalEditor`, `$VISUAL`, `$EDITOR`, Notepad on Windows, or `nano` elsewhere |
29
29
 
30
30
  See [Keybindings](keybindings.md) for all shortcuts and customization.
31
31
 
@@ -297,7 +297,7 @@ pi --exclude-tools ask_question
297
297
  | `PI_SKIP_VERSION_CHECK` | Skip the Pi version update check at startup. This prevents the `pi.dev` latest-version request |
298
298
  | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no`. This does not disable update checks |
299
299
  | `PI_CACHE_RETENTION` | Set to `long` for extended prompt cache where supported |
300
- | `VISUAL`, `EDITOR` | External editor for Ctrl+G |
300
+ | `VISUAL`, `EDITOR` | Fallback external editor for Ctrl+G when `externalEditor` is unset; defaults to Notepad on Windows and `nano` elsewhere |
301
301
 
302
302
  ## Design Principles
303
303
 
@@ -93,6 +93,7 @@ ${conversationText}
93
93
  {
94
94
  apiKey: auth.apiKey,
95
95
  headers: auth.headers,
96
+ env: auth.env,
96
97
  maxTokens: 8192,
97
98
  signal,
98
99
  },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.80.2",
3
+ "version": "0.80.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.80.2",
9
+ "version": "0.80.3",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.80.2",
4
+ "version": "0.80.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.80.2",
4
+ "version": "0.80.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
- "version": "0.80.2",
3
+ "version": "0.80.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "0.80.2",
9
+ "version": "0.80.3",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
3
  "private": true,
4
- "version": "0.80.2",
4
+ "version": "0.80.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -136,7 +136,7 @@ export default function (pi: ExtensionAPI) {
136
136
  const response = await complete(
137
137
  ctx.model!,
138
138
  { systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
139
- { apiKey: auth.apiKey, headers: auth.headers, signal: loader.signal },
139
+ { apiKey: auth.apiKey, headers: auth.headers, env: auth.env, signal: loader.signal },
140
140
  );
141
141
 
142
142
  if (response.stopReason === "aborted") {
@@ -90,7 +90,7 @@ export default function (pi: ExtensionAPI) {
90
90
  const response = await complete(
91
91
  ctx.model!,
92
92
  { systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
93
- { apiKey: auth.apiKey, headers: auth.headers, signal: loader.signal },
93
+ { apiKey: auth.apiKey, headers: auth.headers, env: auth.env, signal: loader.signal },
94
94
  );
95
95
 
96
96
  if (response.stopReason === "aborted") {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.10.2",
9
+ "version": "1.10.3",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "1.10.2",
4
+ "version": "1.10.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -191,6 +191,7 @@ export default function (pi: ExtensionAPI) {
191
191
  {
192
192
  apiKey: auth.apiKey,
193
193
  headers: auth.headers,
194
+ env: auth.env,
194
195
  reasoningEffort: "high",
195
196
  },
196
197
  );
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.80.2",
3
+ "version": "0.80.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.80.2",
9
+ "version": "0.80.3",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.80.2",
4
+ "version": "0.80.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-coding-agent",
3
- "version": "0.80.2",
3
+ "version": "0.80.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@earendil-works/pi-coding-agent",
9
- "version": "0.80.2",
9
+ "version": "0.80.3",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@earendil-works/pi-agent-core": "^0.80.2",
13
- "@earendil-works/pi-ai": "^0.80.2",
14
- "@earendil-works/pi-tui": "^0.80.2",
12
+ "@earendil-works/pi-agent-core": "^0.80.3",
13
+ "@earendil-works/pi-ai": "^0.80.3",
14
+ "@earendil-works/pi-tui": "^0.80.3",
15
15
  "@silvia-odwyer/photon-node": "0.3.4",
16
16
  "chalk": "5.6.2",
17
17
  "cross-spawn": "7.0.6",
@@ -474,11 +474,11 @@
474
474
  }
475
475
  },
476
476
  "node_modules/@earendil-works/pi-agent-core": {
477
- "version": "0.80.2",
478
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.2.tgz",
477
+ "version": "0.80.3",
478
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.3.tgz",
479
479
  "license": "MIT",
480
480
  "dependencies": {
481
- "@earendil-works/pi-ai": "^0.80.2",
481
+ "@earendil-works/pi-ai": "^0.80.3",
482
482
  "ignore": "7.0.5",
483
483
  "typebox": "1.1.38",
484
484
  "yaml": "2.9.0"
@@ -488,8 +488,8 @@
488
488
  }
489
489
  },
490
490
  "node_modules/@earendil-works/pi-ai": {
491
- "version": "0.80.2",
492
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.2.tgz",
491
+ "version": "0.80.3",
492
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.3.tgz",
493
493
  "license": "MIT",
494
494
  "dependencies": {
495
495
  "@anthropic-ai/sdk": "0.91.1",
@@ -512,8 +512,8 @@
512
512
  }
513
513
  },
514
514
  "node_modules/@earendil-works/pi-tui": {
515
- "version": "0.80.2",
516
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.2.tgz",
515
+ "version": "0.80.3",
516
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.3.tgz",
517
517
  "license": "MIT",
518
518
  "dependencies": {
519
519
  "get-east-asian-width": "1.6.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-coding-agent",
3
- "version": "0.80.2",
3
+ "version": "0.80.3",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -15,6 +15,9 @@
15
15
  ".": {
16
16
  "types": "./dist/index.d.ts",
17
17
  "import": "./dist/index.js"
18
+ },
19
+ "./rpc-entry": {
20
+ "import": "./dist/rpc-entry.js"
18
21
  }
19
22
  },
20
23
  "files": [
@@ -27,7 +30,7 @@
27
30
  ],
28
31
  "scripts": {
29
32
  "clean": "shx rm -rf dist",
30
- "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets",
33
+ "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets",
31
34
  "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/bun/cli.js ./src/utils/image-resize-worker.ts --outfile dist/pi && npm run copy-binary-assets",
32
35
  "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
33
36
  "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
@@ -36,9 +39,9 @@
36
39
  "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
37
40
  },
38
41
  "dependencies": {
39
- "@earendil-works/pi-agent-core": "^0.80.2",
40
- "@earendil-works/pi-ai": "^0.80.2",
41
- "@earendil-works/pi-tui": "^0.80.2",
42
+ "@earendil-works/pi-agent-core": "^0.80.3",
43
+ "@earendil-works/pi-ai": "^0.80.3",
44
+ "@earendil-works/pi-tui": "^0.80.3",
42
45
  "@silvia-odwyer/photon-node": "0.3.4",
43
46
  "chalk": "5.6.2",
44
47
  "cross-spawn": "7.0.6",