@earendil-works/pi-coding-agent 0.75.4 → 0.76.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 (175) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +1 -1
  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 +4 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/file-processor.d.ts.map +1 -1
  8. package/dist/cli/file-processor.js +2 -3
  9. package/dist/cli/file-processor.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +18 -12
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  14. package/dist/core/agent-session-runtime.js +2 -1
  15. package/dist/core/agent-session-runtime.js.map +1 -1
  16. package/dist/core/agent-session-services.d.ts.map +1 -1
  17. package/dist/core/agent-session-services.js +3 -2
  18. package/dist/core/agent-session-services.js.map +1 -1
  19. package/dist/core/agent-session.d.ts +1 -0
  20. package/dist/core/agent-session.d.ts.map +1 -1
  21. package/dist/core/agent-session.js +8 -2
  22. package/dist/core/agent-session.js.map +1 -1
  23. package/dist/core/auth-storage.d.ts.map +1 -1
  24. package/dist/core/auth-storage.js +2 -1
  25. package/dist/core/auth-storage.js.map +1 -1
  26. package/dist/core/compaction/compaction.d.ts.map +1 -1
  27. package/dist/core/compaction/compaction.js +18 -24
  28. package/dist/core/compaction/compaction.js.map +1 -1
  29. package/dist/core/export-html/index.d.ts.map +1 -1
  30. package/dist/core/export-html/index.js +8 -6
  31. package/dist/core/export-html/index.js.map +1 -1
  32. package/dist/core/export-html/template.js +6 -3
  33. package/dist/core/extensions/loader.d.ts.map +1 -1
  34. package/dist/core/extensions/loader.js +12 -29
  35. package/dist/core/extensions/loader.js.map +1 -1
  36. package/dist/core/model-registry.d.ts.map +1 -1
  37. package/dist/core/model-registry.js +5 -1
  38. package/dist/core/model-registry.js.map +1 -1
  39. package/dist/core/output-guard.d.ts +1 -0
  40. package/dist/core/output-guard.d.ts.map +1 -1
  41. package/dist/core/output-guard.js +52 -22
  42. package/dist/core/output-guard.js.map +1 -1
  43. package/dist/core/package-manager.d.ts +1 -0
  44. package/dist/core/package-manager.d.ts.map +1 -1
  45. package/dist/core/package-manager.js +63 -36
  46. package/dist/core/package-manager.js.map +1 -1
  47. package/dist/core/prompt-templates.d.ts.map +1 -1
  48. package/dist/core/prompt-templates.js +6 -20
  49. package/dist/core/prompt-templates.js.map +1 -1
  50. package/dist/core/resource-loader.d.ts.map +1 -1
  51. package/dist/core/resource-loader.js +38 -31
  52. package/dist/core/resource-loader.js.map +1 -1
  53. package/dist/core/sdk.d.ts.map +1 -1
  54. package/dist/core/sdk.js +15 -5
  55. package/dist/core/sdk.js.map +1 -1
  56. package/dist/core/session-manager.d.ts +3 -2
  57. package/dist/core/session-manager.d.ts.map +1 -1
  58. package/dist/core/session-manager.js +65 -35
  59. package/dist/core/session-manager.js.map +1 -1
  60. package/dist/core/settings-manager.d.ts +2 -0
  61. package/dist/core/settings-manager.d.ts.map +1 -1
  62. package/dist/core/settings-manager.js +20 -22
  63. package/dist/core/settings-manager.js.map +1 -1
  64. package/dist/core/skills.d.ts.map +1 -1
  65. package/dist/core/skills.js +8 -22
  66. package/dist/core/skills.js.map +1 -1
  67. package/dist/core/tools/bash.d.ts.map +1 -1
  68. package/dist/core/tools/bash.js +54 -53
  69. package/dist/core/tools/bash.js.map +1 -1
  70. package/dist/core/tools/edit-diff.d.ts +3 -1
  71. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  72. package/dist/core/tools/edit-diff.js +8 -1
  73. package/dist/core/tools/edit-diff.js.map +1 -1
  74. package/dist/core/tools/edit.d.ts +3 -1
  75. package/dist/core/tools/edit.d.ts.map +1 -1
  76. package/dist/core/tools/edit.js +44 -81
  77. package/dist/core/tools/edit.js.map +1 -1
  78. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  79. package/dist/core/tools/file-mutation-queue.js +27 -12
  80. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  81. package/dist/core/tools/find.d.ts.map +1 -1
  82. package/dist/core/tools/find.js +2 -3
  83. package/dist/core/tools/find.js.map +1 -1
  84. package/dist/core/tools/grep.d.ts.map +1 -1
  85. package/dist/core/tools/grep.js +3 -3
  86. package/dist/core/tools/grep.js.map +1 -1
  87. package/dist/core/tools/ls.d.ts.map +1 -1
  88. package/dist/core/tools/ls.js +5 -5
  89. package/dist/core/tools/ls.js.map +1 -1
  90. package/dist/core/tools/output-accumulator.d.ts +2 -0
  91. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  92. package/dist/core/tools/output-accumulator.js +9 -3
  93. package/dist/core/tools/output-accumulator.js.map +1 -1
  94. package/dist/core/tools/path-utils.d.ts +2 -0
  95. package/dist/core/tools/path-utils.d.ts.map +1 -1
  96. package/dist/core/tools/path-utils.js +39 -21
  97. package/dist/core/tools/path-utils.js.map +1 -1
  98. package/dist/core/tools/read.d.ts.map +1 -1
  99. package/dist/core/tools/read.js +9 -8
  100. package/dist/core/tools/read.js.map +1 -1
  101. package/dist/core/tools/truncate.d.ts.map +1 -1
  102. package/dist/core/tools/truncate.js +12 -2
  103. package/dist/core/tools/truncate.js.map +1 -1
  104. package/dist/core/tools/write.d.ts.map +1 -1
  105. package/dist/core/tools/write.js +20 -35
  106. package/dist/core/tools/write.js.map +1 -1
  107. package/dist/main.d.ts.map +1 -1
  108. package/dist/main.js +59 -20
  109. package/dist/main.js.map +1 -1
  110. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/config-selector.js +1 -1
  112. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  113. package/dist/modes/interactive/components/footer.d.ts +1 -0
  114. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/footer.js +14 -5
  116. package/dist/modes/interactive/components/footer.js.map +1 -1
  117. package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
  118. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/login-dialog.js +29 -4
  120. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  121. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/user-message.js +1 -1
  123. package/dist/modes/interactive/components/user-message.js.map +1 -1
  124. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  125. package/dist/modes/interactive/interactive-mode.js +4 -4
  126. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  127. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  128. package/dist/modes/interactive/theme/theme.js +37 -28
  129. package/dist/modes/interactive/theme/theme.js.map +1 -1
  130. package/dist/modes/rpc/rpc-client.d.ts +3 -0
  131. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  132. package/dist/modes/rpc/rpc-client.js +64 -7
  133. package/dist/modes/rpc/rpc-client.js.map +1 -1
  134. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  135. package/dist/modes/rpc/rpc-mode.js +18 -4
  136. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  137. package/dist/modes/rpc/rpc-types.d.ts +1 -0
  138. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  139. package/dist/modes/rpc/rpc-types.js.map +1 -1
  140. package/dist/utils/clipboard-native.d.ts +3 -1
  141. package/dist/utils/clipboard-native.d.ts.map +1 -1
  142. package/dist/utils/clipboard-native.js +14 -8
  143. package/dist/utils/clipboard-native.js.map +1 -1
  144. package/dist/utils/image-resize-core.d.ts +30 -0
  145. package/dist/utils/image-resize-core.d.ts.map +1 -0
  146. package/dist/utils/image-resize-core.js +124 -0
  147. package/dist/utils/image-resize-core.js.map +1 -0
  148. package/dist/utils/image-resize-worker.d.ts +2 -0
  149. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  150. package/dist/utils/image-resize-worker.js +31 -0
  151. package/dist/utils/image-resize-worker.js.map +1 -0
  152. package/dist/utils/image-resize.d.ts +7 -27
  153. package/dist/utils/image-resize.d.ts.map +1 -1
  154. package/dist/utils/image-resize.js +75 -115
  155. package/dist/utils/image-resize.js.map +1 -1
  156. package/dist/utils/paths.d.ts +15 -1
  157. package/dist/utils/paths.d.ts.map +1 -1
  158. package/dist/utils/paths.js +33 -7
  159. package/dist/utils/paths.js.map +1 -1
  160. package/docs/custom-provider.md +44 -12
  161. package/docs/models.md +8 -2
  162. package/docs/packages.md +5 -4
  163. package/docs/sdk.md +2 -0
  164. package/docs/settings.md +6 -2
  165. package/docs/terminal-setup.md +6 -0
  166. package/docs/usage.md +2 -2
  167. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  168. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  169. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  170. package/examples/extensions/sandbox/package-lock.json +2 -2
  171. package/examples/extensions/sandbox/package.json +1 -1
  172. package/examples/extensions/with-deps/package-lock.json +2 -2
  173. package/examples/extensions/with-deps/package.json +1 -1
  174. package/npm-shrinkwrap.json +13 -26
  175. package/package.json +5 -5
@@ -1,6 +1,9 @@
1
1
  import { realpathSync } from "node:fs";
2
- import { isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join, resolve as nodeResolvePath, relative, sep } from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { spawnProcessSync } from "./child-process.js";
6
+ const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
4
7
  /**
5
8
  * Resolve a path to its canonical (real) form, following symlinks.
6
9
  * Falls back to the raw path if resolution fails (e.g. the target does
@@ -17,12 +20,12 @@ export function canonicalizePath(path) {
17
20
  }
18
21
  /**
19
22
  * Returns true if the value is NOT a package source (npm:, git:, etc.)
20
- * or a URL protocol. Bare names and relative paths without ./ prefix
23
+ * or a remote URL protocol. Bare names, relative paths, and file: URLs
21
24
  * are considered local.
22
25
  */
23
26
  export function isLocalPath(value) {
24
27
  const trimmed = value.trim();
25
- // Known non-local prefixes
28
+ // Known non-local prefixes. file: URLs are local paths and are intentionally resolved by resolvePath().
26
29
  if (trimmed.startsWith("npm:") ||
27
30
  trimmed.startsWith("git:") ||
28
31
  trimmed.startsWith("github:") ||
@@ -33,19 +36,42 @@ export function isLocalPath(value) {
33
36
  }
34
37
  return true;
35
38
  }
36
- function resolveAgainstCwd(filePath, cwd) {
37
- return isAbsolute(filePath) ? resolvePath(filePath) : resolvePath(cwd, filePath);
39
+ export function normalizePath(input, options = {}) {
40
+ let normalized = options.trim ? input.trim() : input;
41
+ if (options.normalizeUnicodeSpaces) {
42
+ normalized = normalized.replace(UNICODE_SPACES, " ");
43
+ }
44
+ if (options.stripAtPrefix && normalized.startsWith("@")) {
45
+ normalized = normalized.slice(1);
46
+ }
47
+ if (options.expandTilde ?? true) {
48
+ const home = options.homeDir ?? homedir();
49
+ if (normalized === "~")
50
+ return home;
51
+ if (normalized.startsWith("~/") || (process.platform === "win32" && normalized.startsWith("~\\"))) {
52
+ return join(home, normalized.slice(2));
53
+ }
54
+ }
55
+ if (/^file:\/\//.test(normalized)) {
56
+ return fileURLToPath(normalized);
57
+ }
58
+ return normalized;
59
+ }
60
+ export function resolvePath(input, baseDir = process.cwd(), options = {}) {
61
+ const normalized = normalizePath(input, options);
62
+ const normalizedBaseDir = normalizePath(baseDir);
63
+ return isAbsolute(normalized) ? nodeResolvePath(normalized) : nodeResolvePath(normalizedBaseDir, normalized);
38
64
  }
39
65
  export function getCwdRelativePath(filePath, cwd) {
40
66
  const resolvedCwd = resolvePath(cwd);
41
- const resolvedPath = resolveAgainstCwd(filePath, resolvedCwd);
67
+ const resolvedPath = resolvePath(filePath, resolvedCwd);
42
68
  const relativePath = relative(resolvedCwd, resolvedPath);
43
69
  const isInsideCwd = relativePath === "" ||
44
70
  (relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));
45
71
  return isInsideCwd ? relativePath || "." : undefined;
46
72
  }
47
73
  export function formatPathRelativeToCwdOrAbsolute(filePath, cwd) {
48
- const absolutePath = resolveAgainstCwd(filePath, cwd);
74
+ const absolutePath = resolvePath(filePath, cwd);
49
75
  return (getCwdRelativePath(absolutePath, cwd) ?? absolutePath).split(sep).join("/");
50
76
  }
51
77
  export function markPathIgnoredByCloudSync(path) {
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpathSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve as resolvePath, sep } from \"node:path\";\nimport { spawnProcessSync } from \"./child-process.ts\";\n\n/**\n * Resolve a path to its canonical (real) form, following symlinks.\n * Falls back to the raw path if resolution fails (e.g. the target does\n * not exist yet), so that callers never crash on missing filesystem\n * entries.\n */\nexport function canonicalizePath(path: string): string {\n\ttry {\n\t\treturn realpathSync(path);\n\t} catch {\n\t\treturn path;\n\t}\n}\n\n/**\n * Returns true if the value is NOT a package source (npm:, git:, etc.)\n * or a URL protocol. Bare names and relative paths without ./ prefix\n * are considered local.\n */\nexport function isLocalPath(value: string): boolean {\n\tconst trimmed = value.trim();\n\t// Known non-local prefixes\n\tif (\n\t\ttrimmed.startsWith(\"npm:\") ||\n\t\ttrimmed.startsWith(\"git:\") ||\n\t\ttrimmed.startsWith(\"github:\") ||\n\t\ttrimmed.startsWith(\"http:\") ||\n\t\ttrimmed.startsWith(\"https:\") ||\n\t\ttrimmed.startsWith(\"ssh:\")\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction resolveAgainstCwd(filePath: string, cwd: string): string {\n\treturn isAbsolute(filePath) ? resolvePath(filePath) : resolvePath(cwd, filePath);\n}\n\nexport function getCwdRelativePath(filePath: string, cwd: string): string | undefined {\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedPath = resolveAgainstCwd(filePath, resolvedCwd);\n\tconst relativePath = relative(resolvedCwd, resolvedPath);\n\tconst isInsideCwd =\n\t\trelativePath === \"\" ||\n\t\t(relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));\n\n\treturn isInsideCwd ? relativePath || \".\" : undefined;\n}\n\nexport function formatPathRelativeToCwdOrAbsolute(filePath: string, cwd: string): string {\n\tconst absolutePath = resolveAgainstCwd(filePath, cwd);\n\treturn (getCwdRelativePath(absolutePath, cwd) ?? absolutePath).split(sep).join(\"/\");\n}\n\nexport function markPathIgnoredByCloudSync(path: string): void {\n\tconst attrs =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? [\"com.dropbox.ignored\", \"com.apple.fileprovider.ignore#P\"]\n\t\t\t: process.platform === \"linux\"\n\t\t\t\t? [\"user.com.dropbox.ignored\"]\n\t\t\t\t: [];\n\n\tfor (const attr of attrs) {\n\t\tif (process.platform === \"darwin\") {\n\t\t\tspawnProcessSync(\"xattr\", [\"-w\", attr, \"1\", path], { encoding: \"utf-8\", stdio: \"ignore\" });\n\t\t} else {\n\t\t\tspawnProcessSync(\"setfattr\", [\"-n\", attr, \"-v\", \"1\", path], { encoding: \"utf-8\", stdio: \"ignore\" });\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpathSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, resolve as nodeResolvePath, relative, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnProcessSync } from \"./child-process.ts\";\n\nconst UNICODE_SPACES = /[\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\n\nexport interface PathInputOptions {\n\t/** Trim leading/trailing whitespace before normalization. */\n\ttrim?: boolean;\n\t/** Expand leading `~` to a home directory. Defaults to true. */\n\texpandTilde?: boolean;\n\t/** Home directory used for `~` expansion. Defaults to `os.homedir()`. */\n\thomeDir?: string;\n\t/** Strip a leading `@`, used for CLI @file paths. */\n\tstripAtPrefix?: boolean;\n\t/** Normalize unicode space variants to regular spaces. */\n\tnormalizeUnicodeSpaces?: boolean;\n}\n\n/**\n * Resolve a path to its canonical (real) form, following symlinks.\n * Falls back to the raw path if resolution fails (e.g. the target does\n * not exist yet), so that callers never crash on missing filesystem\n * entries.\n */\nexport function canonicalizePath(path: string): string {\n\ttry {\n\t\treturn realpathSync(path);\n\t} catch {\n\t\treturn path;\n\t}\n}\n\n/**\n * Returns true if the value is NOT a package source (npm:, git:, etc.)\n * or a remote URL protocol. Bare names, relative paths, and file: URLs\n * are considered local.\n */\nexport function isLocalPath(value: string): boolean {\n\tconst trimmed = value.trim();\n\t// Known non-local prefixes. file: URLs are local paths and are intentionally resolved by resolvePath().\n\tif (\n\t\ttrimmed.startsWith(\"npm:\") ||\n\t\ttrimmed.startsWith(\"git:\") ||\n\t\ttrimmed.startsWith(\"github:\") ||\n\t\ttrimmed.startsWith(\"http:\") ||\n\t\ttrimmed.startsWith(\"https:\") ||\n\t\ttrimmed.startsWith(\"ssh:\")\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function normalizePath(input: string, options: PathInputOptions = {}): string {\n\tlet normalized = options.trim ? input.trim() : input;\n\tif (options.normalizeUnicodeSpaces) {\n\t\tnormalized = normalized.replace(UNICODE_SPACES, \" \");\n\t}\n\tif (options.stripAtPrefix && normalized.startsWith(\"@\")) {\n\t\tnormalized = normalized.slice(1);\n\t}\n\n\tif (options.expandTilde ?? true) {\n\t\tconst home = options.homeDir ?? homedir();\n\t\tif (normalized === \"~\") return home;\n\t\tif (normalized.startsWith(\"~/\") || (process.platform === \"win32\" && normalized.startsWith(\"~\\\\\"))) {\n\t\t\treturn join(home, normalized.slice(2));\n\t\t}\n\t}\n\n\tif (/^file:\\/\\//.test(normalized)) {\n\t\treturn fileURLToPath(normalized);\n\t}\n\n\treturn normalized;\n}\n\nexport function resolvePath(input: string, baseDir: string = process.cwd(), options: PathInputOptions = {}): string {\n\tconst normalized = normalizePath(input, options);\n\tconst normalizedBaseDir = normalizePath(baseDir);\n\treturn isAbsolute(normalized) ? nodeResolvePath(normalized) : nodeResolvePath(normalizedBaseDir, normalized);\n}\n\nexport function getCwdRelativePath(filePath: string, cwd: string): string | undefined {\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedPath = resolvePath(filePath, resolvedCwd);\n\tconst relativePath = relative(resolvedCwd, resolvedPath);\n\tconst isInsideCwd =\n\t\trelativePath === \"\" ||\n\t\t(relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));\n\n\treturn isInsideCwd ? relativePath || \".\" : undefined;\n}\n\nexport function formatPathRelativeToCwdOrAbsolute(filePath: string, cwd: string): string {\n\tconst absolutePath = resolvePath(filePath, cwd);\n\treturn (getCwdRelativePath(absolutePath, cwd) ?? absolutePath).split(sep).join(\"/\");\n}\n\nexport function markPathIgnoredByCloudSync(path: string): void {\n\tconst attrs =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? [\"com.dropbox.ignored\", \"com.apple.fileprovider.ignore#P\"]\n\t\t\t: process.platform === \"linux\"\n\t\t\t\t? [\"user.com.dropbox.ignored\"]\n\t\t\t\t: [];\n\n\tfor (const attr of attrs) {\n\t\tif (process.platform === \"darwin\") {\n\t\t\tspawnProcessSync(\"xattr\", [\"-w\", attr, \"1\", path], { encoding: \"utf-8\", stdio: \"ignore\" });\n\t\t} else {\n\t\t\tspawnProcessSync(\"setfattr\", [\"-n\", attr, \"-v\", \"1\", path], { encoding: \"utf-8\", stdio: \"ignore\" });\n\t\t}\n\t}\n}\n"]}
@@ -230,6 +230,8 @@ models: [{
230
230
  Use `openrouter` for OpenRouter-style `reasoning: { effort }` controls. Use `together` for Together-style `reasoning: { enabled }` controls; with `supportsReasoningEffort`, it also sends `reasoning_effort`. Use `qwen-chat-template` instead for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking`.
231
231
  Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user/assistant text content.
232
232
 
233
+ For Anthropic-compatible providers using `api: "anthropic-messages"`, set `compat.forceAdaptiveThinking: true` on models or providers whose upstream model requires adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`). Built-in adaptive Claude models set this automatically.
234
+
233
235
  > Migration note: Mistral moved from `openai-completions` to `mistral-conversations`.
234
236
  > Use `mistral-conversations` for native Mistral models.
235
237
  > If you intentionally route Mistral-compatible/custom endpoints through `openai-completions`, set `compat` flags explicitly as needed.
@@ -263,17 +265,28 @@ pi.registerProvider("corporate-ai", {
263
265
  name: "Corporate AI (SSO)",
264
266
 
265
267
  async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
266
- // Option 1: Browser-based OAuth
267
- callbacks.onAuth({ url: "https://sso.corp.com/authorize?..." });
268
-
269
- // Option 2: Device code flow
270
- callbacks.onDeviceCode({
271
- userCode: "ABCD-1234",
272
- verificationUri: "https://sso.corp.com/device"
268
+ const method = await callbacks.onSelect({
269
+ message: "Select login method:",
270
+ options: [
271
+ { id: "browser", label: "Browser OAuth" },
272
+ { id: "device", label: "Device code" }
273
+ ]
273
274
  });
274
-
275
- // Option 3: Prompt for token/code
276
- const code = await callbacks.onPrompt({ message: "Enter SSO code:" });
275
+ if (!method) throw new Error("Login cancelled");
276
+
277
+ let code: string;
278
+ if (method === "device") {
279
+ callbacks.onDeviceCode({
280
+ userCode: "ABCD-1234",
281
+ verificationUri: "https://sso.corp.com/device",
282
+ intervalSeconds: 5,
283
+ expiresInSeconds: 900
284
+ });
285
+ code = await pollDeviceCodeUntilComplete();
286
+ } else {
287
+ callbacks.onAuth({ url: "https://sso.corp.com/authorize?..." });
288
+ code = await callbacks.onPrompt({ message: "Enter SSO code:" });
289
+ }
277
290
 
278
291
  // Exchange for tokens (your implementation)
279
292
  const tokens = await exchangeCodeForTokens(code);
@@ -322,10 +335,21 @@ interface OAuthLoginCallbacks {
322
335
  onAuth(params: { url: string }): void;
323
336
 
324
337
  // Show device code (for device authorization flow)
325
- onDeviceCode(params: { userCode: string; verificationUri: string }): void;
338
+ onDeviceCode(params: {
339
+ userCode: string;
340
+ verificationUri: string;
341
+ intervalSeconds?: number;
342
+ expiresInSeconds?: number;
343
+ }): void;
326
344
 
327
345
  // Prompt user for input (for manual token entry)
328
346
  onPrompt(params: { message: string }): Promise<string>;
347
+
348
+ // Show an interactive selector, e.g. to choose browser OAuth vs device code
349
+ onSelect(params: {
350
+ message: string;
351
+ options: { id: string; label: string }[];
352
+ }): Promise<string | undefined>;
329
353
  }
330
354
  ```
331
355
 
@@ -680,8 +704,9 @@ interface ProviderModelConfig {
680
704
  /** Custom headers for this specific model. */
681
705
  headers?: Record<string, string>;
682
706
 
683
- /** OpenAI compatibility settings for openai-completions API. */
707
+ /** Compatibility settings for the selected API. */
684
708
  compat?: {
709
+ // openai-completions
685
710
  supportsStore?: boolean;
686
711
  supportsDeveloperRole?: boolean;
687
712
  supportsReasoningEffort?: boolean;
@@ -693,6 +718,13 @@ interface ProviderModelConfig {
693
718
  requiresReasoningContentOnAssistantMessages?: boolean;
694
719
  thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "zai" | "qwen" | "qwen-chat-template";
695
720
  cacheControlFormat?: "anthropic";
721
+
722
+ // anthropic-messages
723
+ supportsEagerToolInputStreaming?: boolean;
724
+ supportsLongCacheRetention?: boolean;
725
+ sendSessionAffinityHeaders?: boolean;
726
+ supportsCacheControlOnTools?: boolean;
727
+ forceAdaptiveThinking?: boolean;
696
728
  };
697
729
  }
698
730
  ```
package/docs/models.md CHANGED
@@ -315,10 +315,12 @@ Behavior notes:
315
315
 
316
316
  ## Anthropic Messages Compatibility
317
317
 
318
- For providers or proxies using `api: "anthropic-messages"`, use `compat.supportsEagerToolInputStreaming` to control Anthropic fine-grained tool streaming compatibility.
318
+ For providers or proxies using `api: "anthropic-messages"`, use `compat` to control Anthropic-specific request compatibility.
319
319
 
320
320
  By default pi sends per-tool `eager_input_streaming: true`. If a proxy or Anthropic-compatible backend rejects that field, set `supportsEagerToolInputStreaming` to `false`. Pi will omit `tools[].eager_input_streaming` and send the legacy `fine-grained-tool-streaming-2025-05-14` beta header for tool-enabled requests instead.
321
321
 
322
+ Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`) instead of the legacy budget-based thinking payload. Built-in models set this automatically. For custom providers or aliases that route to those models, set `forceAdaptiveThinking` to `true`.
323
+
322
324
  ```json
323
325
  {
324
326
  "providers": {
@@ -328,7 +330,8 @@ By default pi sends per-tool `eager_input_streaming: true`. If a proxy or Anthro
328
330
  "apiKey": "ANTHROPIC_PROXY_KEY",
329
331
  "compat": {
330
332
  "supportsEagerToolInputStreaming": false,
331
- "supportsLongCacheRetention": true
333
+ "supportsLongCacheRetention": true,
334
+ "forceAdaptiveThinking": true
332
335
  },
333
336
  "models": [
334
337
  {
@@ -346,6 +349,9 @@ By default pi sends per-tool `eager_input_streaming: true`. If a proxy or Anthro
346
349
  |-------|-------------|
347
350
  | `supportsEagerToolInputStreaming` | Whether the provider accepts per-tool `eager_input_streaming`. Default: `true`. Set to `false` to omit that field and use the legacy fine-grained tool streaming beta header on tool-enabled requests. |
348
351
  | `supportsLongCacheRetention` | Whether the provider accepts Anthropic long cache retention (`cache_control.ttl: "1h"`) when cache retention is `long`. Default: `true`. |
352
+ | `sendSessionAffinityHeaders` | Whether to send `x-session-affinity` from the session id when caching is enabled. Default: auto-detected for known providers. |
353
+ | `supportsCacheControlOnTools` | Whether the provider accepts Anthropic-style `cache_control` markers on tool definitions. Default: `true`. |
354
+ | `forceAdaptiveThinking` | Whether to send adaptive thinking (`thinking.type: "adaptive"` plus `output_config.effort`) for this model. Built-in adaptive models set this automatically. Default: `false`. |
349
355
 
350
356
  ## OpenAI Compatibility
351
357
 
package/docs/packages.md CHANGED
@@ -28,8 +28,8 @@ pi install ./relative/path/to/package
28
28
 
29
29
  pi remove npm:@foo/bar
30
30
  pi list # show installed packages from settings
31
- pi update # update pi and all non-pinned packages
32
- pi update --extensions # update all non-pinned packages only
31
+ pi update # update pi, update packages, and reconcile pinned git refs
32
+ pi update --extensions # update packages and reconcile pinned git refs only
33
33
  pi update --self # update pi only
34
34
  pi update --self --force # reinstall pi even if current
35
35
  pi update npm:@foo/bar # update one package
@@ -85,9 +85,10 @@ ssh://git@github.com/user/repo@v1
85
85
  - HTTPS and SSH URLs are both supported.
86
86
  - SSH URLs use your configured SSH keys automatically (respects `~/.ssh/config`).
87
87
  - For non-interactive runs (for example CI), you can set `GIT_TERMINAL_PROMPT=0` to disable credential prompts and set `GIT_SSH_COMMAND` (for example `ssh -o BatchMode=yes -o ConnectTimeout=5`) to fail fast.
88
- - Refs pin the package and skip package updates (`pi update`, `pi update --extensions`).
88
+ - Refs are pinned tags or commits. `pi update` and `pi update --extensions` do not move them to newer refs, but they do reconcile an existing clone to the configured ref.
89
+ - Use `pi install git:host/user/repo@new-ref` to update settings and move an existing package to a new pinned ref.
89
90
  - Cloned to `~/.pi/agent/git/<host>/<path>` (global) or `.pi/git/<host>/<path>` (project).
90
- - Runs `npm install` after clone or pull if `package.json` exists.
91
+ - When reconciliation changes the checkout, pi resets and cleans the clone, then runs `npm install` if `package.json` exists.
91
92
 
92
93
  **SSH examples:**
93
94
  ```bash
package/docs/sdk.md CHANGED
@@ -473,6 +473,8 @@ Specify which built-in tools to enable:
473
473
  - `noTools: "all"` disables all tools
474
474
  - `noTools: "builtin"` disables default built-ins while keeping extension and custom tools enabled
475
475
 
476
+ The `edit` tool returns `details.diff` for Pi's TUI display and `details.patch` as a standard unified patch for SDK consumers.
477
+
476
478
  ```typescript
477
479
  import { createAgentSession } from "@earendil-works/pi-coding-agent";
478
480
 
package/docs/settings.md CHANGED
@@ -101,11 +101,13 @@ Set `PI_SKIP_VERSION_CHECK=1` to disable the Pi version update check. Use `--off
101
101
  | `retry.maxRetries` | number | `3` | Maximum agent-level retry attempts |
102
102
  | `retry.baseDelayMs` | number | `2000` | Base delay for agent-level exponential backoff (2s, 4s, 8s) |
103
103
  | `retry.provider.timeoutMs` | number | SDK default | Provider/SDK request timeout in milliseconds |
104
- | `retry.provider.maxRetries` | number | SDK default | Provider/SDK retry attempts |
104
+ | `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
105
105
  | `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay before failing (60s) |
106
106
 
107
107
  When a provider requests a retry delay longer than `retry.provider.maxRetryDelayMs` (e.g., Google's "quota will reset after 5h"), the request fails immediately with an informative error instead of waiting silently. Set to `0` to disable the cap.
108
108
 
109
+ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explicitly needed. Setting it above `0` can make SDK/provider retries handle out-of-usage-limit errors before Pi sees them, which may block the agent until the provider quota resets in some circumstances.
110
+
109
111
  ```json
110
112
  {
111
113
  "retry": {
@@ -127,7 +129,9 @@ When a provider requests a retry delay longer than `retry.provider.maxRetryDelay
127
129
  |---------|------|---------|-------------|
128
130
  | `steeringMode` | string | `"one-at-a-time"` | How steering messages are sent: `"all"` or `"one-at-a-time"` |
129
131
  | `followUpMode` | string | `"one-at-a-time"` | How follow-up messages are sent: `"all"` or `"one-at-a-time"` |
130
- | `transport` | string | `"sse"` | Preferred transport for providers that support multiple transports: `"sse"`, `"websocket"`, or `"auto"` |
132
+ | `transport` | string | `"auto"` | Preferred transport for providers that support multiple transports: `"sse"`, `"websocket"`, `"websocket-cached"`, or `"auto"` |
133
+ | `httpIdleTimeoutMs` | number | `300000` | HTTP header/body idle timeout in milliseconds, also used by providers with explicit stream idle timeouts. Set to `0` to disable. |
134
+ | `websocketConnectTimeoutMs` | number | `15000` | WebSocket connect/open handshake timeout in milliseconds for providers that support WebSocket transports. Set to `0` to disable. |
131
135
 
132
136
  ### Terminal & Images
133
137
 
@@ -6,6 +6,12 @@ Pi uses the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-p
6
6
 
7
7
  Work out of the box.
8
8
 
9
+ ## Apple Terminal
10
+
11
+ Pi enables enhanced key reporting when available. If Terminal.app still sends plain Return for `Shift+Enter`, pi uses a local macOS modifier fallback to treat that Return as `Shift+Enter`.
12
+
13
+ This fallback only works when pi runs on the same Mac as Terminal.app. It cannot detect the local keyboard over remote SSH.
14
+
9
15
  ## Ghostty
10
16
 
11
17
  Add to your Ghostty config (`~/Library/Application Support/com.mitchellh.ghostty/config` on macOS, `~/.config/ghostty/config` on Linux):
package/docs/usage.md CHANGED
@@ -129,8 +129,8 @@ pi [options] [@files...] [messages...]
129
129
  pi install <source> [-l] # Install package, -l for project-local
130
130
  pi remove <source> [-l] # Remove package
131
131
  pi uninstall <source> [-l] # Alias for remove
132
- pi update [source|self|pi] # Update pi and packages; skips pinned packages
133
- pi update --extensions # Update packages only
132
+ pi update [source|self|pi] # Update pi and packages; reconcile pinned git refs
133
+ pi update --extensions # Update packages only; reconcile pinned git refs
134
134
  pi update --self # Update pi only
135
135
  pi update --extension <src> # Update one package
136
136
  pi list # List installed packages
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.75.4",
3
+ "version": "0.76.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.75.4",
9
+ "version": "0.76.0",
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.75.4",
4
+ "version": "0.76.0",
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.75.4",
4
+ "version": "0.76.0",
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-sandbox",
3
- "version": "1.5.4",
3
+ "version": "1.6.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.5.4",
9
+ "version": "1.6.0",
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.5.4",
4
+ "version": "1.6.0",
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-with-deps",
3
- "version": "0.75.4",
3
+ "version": "0.76.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.75.4",
9
+ "version": "0.76.0",
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.75.4",
4
+ "version": "0.76.0",
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.75.4",
3
+ "version": "0.76.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@earendil-works/pi-coding-agent",
9
- "version": "0.75.4",
9
+ "version": "0.76.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@earendil-works/pi-agent-core": "^0.75.4",
13
- "@earendil-works/pi-ai": "^0.75.4",
14
- "@earendil-works/pi-tui": "^0.75.4",
12
+ "@earendil-works/pi-agent-core": "^0.76.0",
13
+ "@earendil-works/pi-ai": "^0.76.0",
14
+ "@earendil-works/pi-tui": "^0.76.0",
15
15
  "@silvia-odwyer/photon-node": "0.3.4",
16
16
  "chalk": "5.6.2",
17
17
  "cross-spawn": "7.0.6",
@@ -473,11 +473,11 @@
473
473
  }
474
474
  },
475
475
  "node_modules/@earendil-works/pi-agent-core": {
476
- "version": "0.75.4",
477
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.75.4.tgz",
476
+ "version": "0.76.0",
477
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.76.0.tgz",
478
478
  "license": "MIT",
479
479
  "dependencies": {
480
- "@earendil-works/pi-ai": "^0.75.4",
480
+ "@earendil-works/pi-ai": "^0.76.0",
481
481
  "ignore": "7.0.5",
482
482
  "typebox": "1.1.38",
483
483
  "yaml": "2.9.0"
@@ -487,12 +487,13 @@
487
487
  }
488
488
  },
489
489
  "node_modules/@earendil-works/pi-ai": {
490
- "version": "0.75.4",
491
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.75.4.tgz",
490
+ "version": "0.76.0",
491
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.76.0.tgz",
492
492
  "license": "MIT",
493
493
  "dependencies": {
494
494
  "@anthropic-ai/sdk": "0.91.1",
495
495
  "@aws-sdk/client-bedrock-runtime": "3.1048.0",
496
+ "@smithy/node-http-handler": "4.7.3",
496
497
  "@google/genai": "1.52.0",
497
498
  "@mistralai/mistralai": "2.2.1",
498
499
  "http-proxy-agent": "7.0.2",
@@ -509,16 +510,13 @@
509
510
  }
510
511
  },
511
512
  "node_modules/@earendil-works/pi-tui": {
512
- "version": "0.75.4",
513
- "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.75.4.tgz",
513
+ "version": "0.76.0",
514
+ "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.76.0.tgz",
514
515
  "license": "MIT",
515
516
  "dependencies": {
516
517
  "get-east-asian-width": "1.6.0",
517
518
  "marked": "15.0.12"
518
519
  },
519
- "optionalDependencies": {
520
- "koffi": "2.16.2"
521
- },
522
520
  "engines": {
523
521
  "node": ">=22.19.0"
524
522
  }
@@ -1369,17 +1367,6 @@
1369
1367
  "safe-buffer": "^5.0.1"
1370
1368
  }
1371
1369
  },
1372
- "node_modules/koffi": {
1373
- "version": "2.16.2",
1374
- "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.16.2.tgz",
1375
- "integrity": "sha512-owU0MRwv6xkrVqCd+33uw6BaYppkTRXbO/rVdJNI2dvZG0gzyRhYwW25eWtc5pauwK8TGh3AbkFONSezdykfSA==",
1376
- "license": "MIT",
1377
- "optional": true,
1378
- "hasInstallScript": true,
1379
- "funding": {
1380
- "url": "https://liberapay.com/Koromix"
1381
- }
1382
- },
1383
1370
  "node_modules/long": {
1384
1371
  "version": "5.3.2",
1385
1372
  "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-coding-agent",
3
- "version": "0.75.4",
3
+ "version": "0.76.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -31,7 +31,7 @@
31
31
  "scripts": {
32
32
  "clean": "shx rm -rf dist",
33
33
  "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets",
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 --outfile dist/pi && npm run copy-binary-assets",
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",
35
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/",
36
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/",
37
37
  "test": "vitest --run",
@@ -39,9 +39,9 @@
39
39
  "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
40
40
  },
41
41
  "dependencies": {
42
- "@earendil-works/pi-agent-core": "^0.75.4",
43
- "@earendil-works/pi-ai": "^0.75.4",
44
- "@earendil-works/pi-tui": "^0.75.4",
42
+ "@earendil-works/pi-agent-core": "^0.76.0",
43
+ "@earendil-works/pi-ai": "^0.76.0",
44
+ "@earendil-works/pi-tui": "^0.76.0",
45
45
  "@silvia-odwyer/photon-node": "0.3.4",
46
46
  "chalk": "5.6.2",
47
47
  "cross-spawn": "7.0.6",