@oh-my-pi/pi-coding-agent 16.1.23 → 16.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli.js +3785 -3695
  3. package/dist/types/advisor/__tests__/emission-guard.test.d.ts +1 -0
  4. package/dist/types/advisor/emission-guard.d.ts +73 -0
  5. package/dist/types/advisor/index.d.ts +1 -0
  6. package/dist/types/advisor/runtime.d.ts +7 -0
  7. package/dist/types/advisor/watchdog.d.ts +13 -0
  8. package/dist/types/cli/flag-tables.d.ts +1 -0
  9. package/dist/types/cli/gallery-fixtures/search.d.ts +1 -1
  10. package/dist/types/collab/display-name.d.ts +3 -0
  11. package/dist/types/collab/host.d.ts +0 -2
  12. package/dist/types/commands/worktree.d.ts +0 -3
  13. package/dist/types/config/inline-tool-descriptors-mode.d.ts +13 -0
  14. package/dist/types/config/model-discovery.d.ts +6 -1
  15. package/dist/types/config/model-registry.d.ts +6 -2
  16. package/dist/types/config/model-resolver.d.ts +12 -0
  17. package/dist/types/config/models-config-schema.d.ts +29 -2
  18. package/dist/types/config/models-config.d.ts +22 -1
  19. package/dist/types/config/settings-schema.d.ts +91 -21
  20. package/dist/types/config/settings.d.ts +2 -0
  21. package/dist/types/dap/config.d.ts +1 -1
  22. package/dist/types/edit/hashline/filesystem.d.ts +4 -2
  23. package/dist/types/edit/renderer.d.ts +4 -0
  24. package/dist/types/extensibility/extensions/types.d.ts +17 -17
  25. package/dist/types/extensibility/hooks/types.d.ts +10 -10
  26. package/dist/types/extensibility/plugins/marketplace/types.d.ts +1 -0
  27. package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +1 -0
  28. package/dist/types/internal-urls/index.d.ts +1 -0
  29. package/dist/types/internal-urls/registry-helpers.d.ts +2 -0
  30. package/dist/types/internal-urls/router.d.ts +1 -1
  31. package/dist/types/internal-urls/ssh-protocol.d.ts +10 -0
  32. package/dist/types/internal-urls/types.d.ts +19 -2
  33. package/dist/types/irc/bus.d.ts +6 -0
  34. package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -0
  35. package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +1 -0
  36. package/dist/types/modes/components/custom-editor.d.ts +7 -0
  37. package/dist/types/modes/components/move-overlay.d.ts +23 -0
  38. package/dist/types/modes/components/plugin-selector.d.ts +2 -1
  39. package/dist/types/modes/components/queue-mode-selector.d.ts +2 -1
  40. package/dist/types/modes/components/select-list-mouse-routing.d.ts +2 -0
  41. package/dist/types/modes/components/settings-defs.d.ts +4 -1
  42. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  43. package/dist/types/modes/components/show-images-selector.d.ts +2 -1
  44. package/dist/types/modes/components/status-line/component.d.ts +2 -0
  45. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  46. package/dist/types/modes/components/theme-selector.d.ts +2 -1
  47. package/dist/types/modes/components/thinking-selector.d.ts +2 -1
  48. package/dist/types/modes/controllers/command-controller.d.ts +10 -1
  49. package/dist/types/modes/controllers/streaming-reveal.d.ts +6 -0
  50. package/dist/types/modes/interactive-mode.d.ts +10 -1
  51. package/dist/types/modes/internal-url-autocomplete.d.ts +1 -1
  52. package/dist/types/modes/running-subagent-badge.d.ts +6 -0
  53. package/dist/types/modes/theme/theme.d.ts +1 -1
  54. package/dist/types/modes/types.d.ts +9 -1
  55. package/dist/types/sdk.d.ts +2 -2
  56. package/dist/types/session/agent-session.d.ts +8 -1
  57. package/dist/types/session/blob-store.d.ts +4 -0
  58. package/dist/types/session/session-manager.d.ts +13 -0
  59. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  60. package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +1 -0
  61. package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +1 -0
  62. package/dist/types/ssh/connection-manager.d.ts +2 -0
  63. package/dist/types/ssh/file-transfer.d.ts +79 -0
  64. package/dist/types/ssh/utils.d.ts +6 -0
  65. package/dist/types/system-prompt.d.ts +3 -0
  66. package/dist/types/task/executor.d.ts +16 -0
  67. package/dist/types/tiny/text.d.ts +1 -1
  68. package/dist/types/tools/builtin-names.d.ts +5 -1
  69. package/dist/types/tools/{find.d.ts → glob.d.ts} +15 -15
  70. package/dist/types/tools/{search.d.ts → grep.d.ts} +14 -14
  71. package/dist/types/tools/index.d.ts +3 -3
  72. package/dist/types/tools/path-utils.d.ts +29 -0
  73. package/dist/types/tools/read.d.ts +2 -2
  74. package/dist/types/tools/render-utils.d.ts +8 -0
  75. package/dist/types/tools/renderers.d.ts +11 -0
  76. package/dist/types/tools/ssh.d.ts +2 -0
  77. package/dist/types/tools/todo.d.ts +0 -16
  78. package/dist/types/tools/write.d.ts +2 -2
  79. package/dist/types/utils/active-repo-context.d.ts +8 -0
  80. package/dist/types/utils/image-resize.d.ts +1 -0
  81. package/dist/types/utils/markit-cache.d.ts +23 -0
  82. package/dist/types/utils/markit.d.ts +5 -1
  83. package/dist/types/utils/prompt-path.d.ts +1 -0
  84. package/dist/types/web/search/providers/duckduckgo.d.ts +14 -0
  85. package/dist/types/web/search/providers/firecrawl.d.ts +28 -0
  86. package/dist/types/web/search/providers/tinyfish.d.ts +29 -0
  87. package/dist/types/web/search/providers/xai.d.ts +13 -0
  88. package/dist/types/web/search/types.d.ts +18 -2
  89. package/package.json +30 -15
  90. package/scripts/build-binary.ts +9 -9
  91. package/scripts/bundle-dist.ts +2 -2
  92. package/src/advisor/__tests__/advisor.test.ts +61 -4
  93. package/src/advisor/__tests__/emission-guard.test.ts +147 -0
  94. package/src/advisor/advise-tool.ts +1 -1
  95. package/src/advisor/emission-guard.ts +172 -0
  96. package/src/advisor/index.ts +1 -0
  97. package/src/advisor/runtime.ts +11 -0
  98. package/src/advisor/watchdog.ts +27 -1
  99. package/src/cli/args.ts +5 -2
  100. package/src/cli/auth-broker-cli.ts +17 -0
  101. package/src/cli/config-cli.ts +4 -0
  102. package/src/cli/flag-tables.ts +7 -4
  103. package/src/cli/gallery-cli.ts +14 -2
  104. package/src/cli/gallery-fixtures/edit.ts +60 -0
  105. package/src/cli/gallery-fixtures/fs.ts +17 -17
  106. package/src/cli/gallery-fixtures/search.ts +4 -4
  107. package/src/cli/usage-cli.ts +20 -1
  108. package/src/cli/web-search-cli.ts +1 -1
  109. package/src/collab/display-name.ts +13 -0
  110. package/src/collab/guest.ts +5 -1
  111. package/src/collab/host.ts +2 -13
  112. package/src/commands/worktree.ts +6 -0
  113. package/src/config/inline-tool-descriptors-mode.ts +27 -0
  114. package/src/config/model-discovery.ts +77 -8
  115. package/src/config/model-registry.ts +89 -11
  116. package/src/config/model-resolver.ts +39 -1
  117. package/src/config/models-config-schema.ts +41 -5
  118. package/src/config/models-config.ts +4 -1
  119. package/src/config/settings-schema.ts +85 -27
  120. package/src/config/settings.ts +172 -1
  121. package/src/cursor.ts +1 -1
  122. package/src/dap/config.ts +152 -8
  123. package/src/dap/session.ts +1 -1
  124. package/src/discovery/claude-plugins.ts +3 -2
  125. package/src/discovery/helpers.ts +3 -1
  126. package/src/edit/hashline/diff.ts +14 -3
  127. package/src/edit/hashline/execute.ts +42 -6
  128. package/src/edit/hashline/filesystem.ts +38 -2
  129. package/src/edit/index.ts +1 -0
  130. package/src/edit/modes/patch.ts +11 -1
  131. package/src/edit/renderer.ts +140 -13
  132. package/src/eval/__tests__/agent-bridge.test.ts +101 -0
  133. package/src/eval/agent-bridge.ts +26 -3
  134. package/src/eval/js/tool-bridge.ts +2 -2
  135. package/src/export/html/index.ts +1 -1
  136. package/src/export/html/template.js +3 -1
  137. package/src/export/html/tool-views.generated.js +20 -20
  138. package/src/extensibility/extensions/types.ts +22 -22
  139. package/src/extensibility/hooks/types.ts +11 -11
  140. package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +8 -3
  141. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +25 -10
  142. package/src/extensibility/plugins/marketplace/manager.ts +22 -0
  143. package/src/extensibility/plugins/marketplace/types.ts +1 -0
  144. package/src/internal-urls/__tests__/ssh-protocol.test.ts +331 -0
  145. package/src/internal-urls/docs-index.generated.txt +2 -2
  146. package/src/internal-urls/docs-index.ts +2 -3
  147. package/src/internal-urls/index.ts +1 -0
  148. package/src/internal-urls/registry-helpers.ts +19 -4
  149. package/src/internal-urls/router.ts +5 -3
  150. package/src/internal-urls/ssh-protocol.ts +367 -0
  151. package/src/internal-urls/types.ts +19 -2
  152. package/src/irc/bus.ts +11 -3
  153. package/src/mcp/tool-bridge.ts +32 -2
  154. package/src/memories/index.ts +1 -1
  155. package/src/modes/acp/acp-agent.ts +14 -1
  156. package/src/modes/acp/acp-event-mapper.ts +91 -27
  157. package/src/modes/components/__tests__/move-overlay.test.ts +166 -0
  158. package/src/modes/components/agent-hub.ts +3 -0
  159. package/src/modes/components/agent-transcript-viewer.ts +9 -7
  160. package/src/modes/components/custom-editor.ts +90 -5
  161. package/src/modes/components/move-overlay.ts +282 -0
  162. package/src/modes/components/plan-review-overlay.ts +35 -35
  163. package/src/modes/components/plugin-selector.ts +6 -1
  164. package/src/modes/components/queue-mode-selector.ts +6 -1
  165. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  166. package/src/modes/components/session-selector.ts +11 -10
  167. package/src/modes/components/settings-defs.ts +14 -1
  168. package/src/modes/components/settings-selector.ts +196 -29
  169. package/src/modes/components/show-images-selector.ts +6 -1
  170. package/src/modes/components/status-line/component.ts +108 -28
  171. package/src/modes/components/status-line/segments.ts +5 -1
  172. package/src/modes/components/status-line/types.ts +2 -0
  173. package/src/modes/components/theme-selector.ts +6 -1
  174. package/src/modes/components/thinking-selector.ts +6 -1
  175. package/src/modes/components/tool-execution.ts +25 -9
  176. package/src/modes/components/tree-selector.ts +5 -5
  177. package/src/modes/controllers/command-controller.ts +140 -47
  178. package/src/modes/controllers/event-controller.ts +59 -3
  179. package/src/modes/controllers/input-controller.ts +68 -2
  180. package/src/modes/controllers/selector-controller.ts +5 -2
  181. package/src/modes/controllers/streaming-reveal.ts +17 -0
  182. package/src/modes/controllers/tool-args-reveal.ts +1 -1
  183. package/src/modes/interactive-mode.ts +116 -50
  184. package/src/modes/internal-url-autocomplete.ts +17 -2
  185. package/src/modes/prompt-action-autocomplete.ts +3 -1
  186. package/src/modes/running-subagent-badge.ts +13 -0
  187. package/src/modes/setup-wizard/scenes/glyph.ts +2 -13
  188. package/src/modes/setup-wizard/scenes/providers.ts +2 -1
  189. package/src/modes/setup-wizard/scenes/theme.ts +6 -12
  190. package/src/modes/setup-wizard/scenes/web-search.ts +8 -13
  191. package/src/modes/setup-wizard/wizard-overlay.ts +6 -5
  192. package/src/modes/theme/theme.ts +9 -1
  193. package/src/modes/types.ts +9 -1
  194. package/src/modes/utils/interactive-context-helpers.ts +1 -1
  195. package/src/priority.json +15 -0
  196. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  197. package/src/prompts/advisor/context-files.md +8 -0
  198. package/src/prompts/advisor/system.md +21 -7
  199. package/src/prompts/agents/designer.md +1 -1
  200. package/src/prompts/agents/explore.md +1 -1
  201. package/src/prompts/agents/librarian.md +2 -2
  202. package/src/prompts/agents/plan.md +2 -2
  203. package/src/prompts/agents/reviewer.md +1 -1
  204. package/src/prompts/agents/task.md +2 -2
  205. package/src/prompts/system/active-repo-context.md +4 -0
  206. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  207. package/src/prompts/system/plan-mode-active.md +3 -3
  208. package/src/prompts/system/project-prompt.md +2 -2
  209. package/src/prompts/system/system-prompt.md +6 -6
  210. package/src/prompts/tools/bash.md +2 -2
  211. package/src/prompts/tools/checkpoint.md +1 -1
  212. package/src/prompts/tools/{find.md → glob.md} +1 -1
  213. package/src/prompts/tools/{search.md → grep.md} +3 -3
  214. package/src/prompts/tools/read.md +4 -2
  215. package/src/sdk.ts +65 -25
  216. package/src/session/agent-session.ts +519 -132
  217. package/src/session/blob-store.ts +24 -0
  218. package/src/session/session-history-format.ts +2 -2
  219. package/src/session/session-manager.ts +46 -0
  220. package/src/slash-commands/builtin-registry.ts +138 -20
  221. package/src/slash-commands/helpers/usage-report.ts +23 -2
  222. package/src/ssh/__tests__/connection-manager-args.test.ts +69 -0
  223. package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +68 -0
  224. package/src/ssh/connection-manager.ts +12 -15
  225. package/src/ssh/file-transfer.ts +209 -0
  226. package/src/ssh/utils.ts +24 -0
  227. package/src/system-prompt.ts +56 -28
  228. package/src/task/executor.ts +96 -36
  229. package/src/task/index.ts +3 -3
  230. package/src/task/render.ts +14 -13
  231. package/src/task/worktree.ts +38 -1
  232. package/src/tiny/text.ts +49 -4
  233. package/src/tiny/worker.ts +3 -3
  234. package/src/tools/bash.ts +2 -2
  235. package/src/tools/builtin-names.ts +26 -2
  236. package/src/tools/{find.ts → glob.ts} +48 -42
  237. package/src/tools/{search.ts → grep.ts} +311 -129
  238. package/src/tools/index.ts +13 -14
  239. package/src/tools/irc.ts +6 -2
  240. package/src/tools/path-utils.ts +64 -1
  241. package/src/tools/read.ts +35 -6
  242. package/src/tools/render-utils.ts +14 -0
  243. package/src/tools/renderers.ts +15 -4
  244. package/src/tools/ssh.ts +17 -2
  245. package/src/tools/todo.ts +4 -26
  246. package/src/tools/write.ts +25 -9
  247. package/src/utils/active-repo-context.ts +143 -0
  248. package/src/utils/image-resize.ts +88 -7
  249. package/src/utils/lang-from-path.ts +3 -3
  250. package/src/utils/markit-cache.ts +166 -0
  251. package/src/utils/markit.ts +86 -18
  252. package/src/utils/prompt-path.ts +3 -0
  253. package/src/utils/title-generator.ts +1 -1
  254. package/src/web/search/provider.ts +54 -34
  255. package/src/web/search/providers/duckduckgo.ts +140 -0
  256. package/src/web/search/providers/firecrawl.ts +144 -0
  257. package/src/web/search/providers/tinyfish.ts +159 -0
  258. package/src/web/search/providers/xai.ts +292 -0
  259. package/src/web/search/providers/zai.ts +142 -56
  260. package/src/web/search/types.ts +6 -2
@@ -70,6 +70,10 @@ export const OpenAICompatSchema = type({
70
70
  "whenThinking?": OpenAICompatFieldsSchema,
71
71
  });
72
72
 
73
+ const ApiSchema = type(
74
+ '"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
75
+ );
76
+
73
77
  const EffortSchema = type('"minimal" | "low" | "medium" | "high" | "xhigh"');
74
78
 
75
79
  const ThinkingControlModeSchema = type(
@@ -118,11 +122,25 @@ const ModelThinkingSchema = type({
118
122
  };
119
123
  });
120
124
 
125
+ const RemoteCompactionSchema = type({
126
+ "enabled?": "boolean",
127
+ "api?": ApiSchema,
128
+ "endpoint?": "string",
129
+ "model?": "string",
130
+ }).narrow((value, ctx) => {
131
+ if (value.endpoint !== undefined && typeof value.endpoint === "string" && value.endpoint.length === 0) {
132
+ return ctx.mustBe("remoteCompaction.endpoint a non-empty string");
133
+ }
134
+ if (value.model !== undefined && typeof value.model === "string" && value.model.length === 0) {
135
+ return ctx.mustBe("remoteCompaction.model a non-empty string");
136
+ }
137
+ return true;
138
+ });
139
+
121
140
  const ModelDefinitionSchema = type({
122
141
  id: "string",
123
142
  "name?": "string",
124
- "api?":
125
- '"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
143
+ "api?": ApiSchema,
126
144
  "baseUrl?": "string",
127
145
  "reasoning?": "boolean",
128
146
  "thinking?": ModelThinkingSchema,
@@ -141,6 +159,8 @@ const ModelDefinitionSchema = type({
141
159
  "headers?": { "[string]": "string" },
142
160
  "compat?": OpenAICompatSchema,
143
161
  "contextPromotionTarget?": "string",
162
+ "compactionModel?": "string",
163
+ "remoteCompaction?": RemoteCompactionSchema,
144
164
  }).narrow((value, ctx) => {
145
165
  // Enforce id non-empty
146
166
  if (typeof value.id === "string" && value.id.length === 0) {
@@ -159,6 +179,13 @@ const ModelDefinitionSchema = type({
159
179
  ) {
160
180
  return ctx.mustBe("contextPromotionTarget a non-empty string");
161
181
  }
182
+ if (
183
+ value.compactionModel !== undefined &&
184
+ typeof value.compactionModel === "string" &&
185
+ value.compactionModel.length === 0
186
+ ) {
187
+ return ctx.mustBe("compactionModel a non-empty string");
188
+ }
162
189
  return true;
163
190
  });
164
191
 
@@ -181,6 +208,8 @@ export const ModelOverrideSchema = type({
181
208
  "headers?": { "[string]": "string" },
182
209
  "compat?": OpenAICompatSchema,
183
210
  "contextPromotionTarget?": "string",
211
+ "compactionModel?": "string",
212
+ "remoteCompaction?": RemoteCompactionSchema,
184
213
  }).narrow((value, ctx) => {
185
214
  if (value.name !== undefined && typeof value.name === "string" && value.name.length === 0) {
186
215
  return ctx.mustBe("name a non-empty string");
@@ -192,13 +221,20 @@ export const ModelOverrideSchema = type({
192
221
  ) {
193
222
  return ctx.mustBe("contextPromotionTarget a non-empty string");
194
223
  }
224
+ if (
225
+ value.compactionModel !== undefined &&
226
+ typeof value.compactionModel === "string" &&
227
+ value.compactionModel.length === 0
228
+ ) {
229
+ return ctx.mustBe("compactionModel a non-empty string");
230
+ }
195
231
  return true;
196
232
  });
197
233
 
198
234
  export type ModelOverride = typeof ModelOverrideSchema.infer;
199
235
 
200
236
  export const ProviderDiscoverySchema = type({
201
- type: '"ollama" | "llama.cpp" | "lm-studio" | "openai-models-list" | "proxy"',
237
+ type: '"ollama" | "llama.cpp" | "lm-studio" | "openai-models-list" | "proxy" | "litellm"',
202
238
  });
203
239
 
204
240
  export const ProviderAuthSchema = type('"apiKey" | "none" | "oauth"');
@@ -209,10 +245,10 @@ export type ProviderDiscovery = typeof ProviderDiscoverySchema.infer;
209
245
  const ProviderConfigSchema = type({
210
246
  "baseUrl?": "string",
211
247
  "apiKey?": "string",
212
- "api?":
213
- '"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
248
+ "api?": ApiSchema,
214
249
  "headers?": { "[string]": "string" },
215
250
  "compat?": OpenAICompatSchema,
251
+ "remoteCompaction?": RemoteCompactionSchema,
216
252
  "authHeader?": "boolean",
217
253
  "auth?": ProviderAuthSchema,
218
254
  "discovery?": ProviderDiscoverySchema,
@@ -30,6 +30,7 @@ export interface ProviderValidationConfig {
30
30
  oauthConfigured?: boolean;
31
31
  discovery?: ProviderDiscovery;
32
32
  compat?: ModelSpec<Api>["compat"];
33
+ remoteCompaction?: unknown;
33
34
  disableStrictTools?: boolean;
34
35
  modelOverrides?: Record<string, unknown>;
35
36
  models: ProviderValidationModel[];
@@ -53,11 +54,12 @@ export function validateProviderConfiguration(
53
54
  !config.apiKey &&
54
55
  config.auth !== "none" &&
55
56
  !config.disableStrictTools &&
57
+ !config.remoteCompaction &&
56
58
  !hasModelOverrides &&
57
59
  !config.discovery
58
60
  ) {
59
61
  throw new Error(
60
- `Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "auth: none", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
62
+ `Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "auth: none", "compat", "disableStrictTools", "remoteCompaction", "modelOverrides", "discovery", or "models"`,
61
63
  );
62
64
  }
63
65
  }
@@ -120,6 +122,7 @@ export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsCon
120
122
  auth: (providerConfig.auth ?? "apiKey") as ProviderAuthMode,
121
123
  discovery: providerConfig.discovery as ProviderDiscovery | undefined,
122
124
  compat: providerConfig.compat,
125
+ remoteCompaction: providerConfig.remoteCompaction,
123
126
  disableStrictTools: providerConfig.disableStrictTools,
124
127
  modelOverrides: providerConfig.modelOverrides,
125
128
  models: (providerConfig.models ?? []) as ProviderValidationModel[],
@@ -132,7 +132,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
132
132
  tools: [
133
133
  "Available Tools",
134
134
  "Todos",
135
- "Search & Browser",
135
+ "Grep & Browser",
136
136
  "GitHub",
137
137
  "Output Limits",
138
138
  "Execution",
@@ -278,6 +278,7 @@ export interface ModelTagsSettings {
278
278
  // under `as const` while still letting SettingValue infer the correct element type.
279
279
  const EMPTY_STRING_ARRAY: string[] = [];
280
280
  const EMPTY_STRING_RECORD: Record<string, string> = {};
281
+ const EMPTY_NUMBER_RECORD: Record<string, number> = {};
281
282
  const DEFAULT_CYCLE_ORDER: string[] = ["smol", "default", "slow"];
282
283
  const EMPTY_MODEL_TAGS_RECORD: ModelTagsSettings = {};
283
284
  const HINDSIGHT_RECALL_TYPES_DEFAULT: string[] = ["world", "experience"];
@@ -289,13 +290,13 @@ export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
289
290
  },
290
291
  {
291
292
  pattern: "^\\s*(grep|rg|ripgrep|ag|ack)\\s+",
292
- tool: "search",
293
- message: "Use the `search` tool instead of grep/rg. It respects .gitignore and provides structured output.",
293
+ tool: "grep",
294
+ message: "Use the `grep` tool instead of grep/rg. It respects .gitignore and provides structured output.",
294
295
  },
295
296
  {
296
297
  pattern: "^\\s*(find|fd|locate)\\s+.*(-name|-iname|-type|--type|-glob)",
297
- tool: "find",
298
- message: "Use the `find` tool instead of find/fd. It respects .gitignore and is faster for glob patterns.",
298
+ tool: "glob",
299
+ message: "Use the `glob` tool instead of find/fd. It respects .gitignore and is faster for glob patterns.",
299
300
  },
300
301
  {
301
302
  pattern: "^\\s*sed\\s+(-i|--in-place)",
@@ -455,6 +456,18 @@ export const SETTINGS_SCHEMA = {
455
456
 
456
457
  disabledProviders: { type: "array", default: EMPTY_STRING_ARRAY },
457
458
 
459
+ "providers.maxInFlightRequests": {
460
+ type: "record",
461
+ default: EMPTY_NUMBER_RECORD,
462
+ ui: {
463
+ tab: "providers",
464
+ group: "Services",
465
+ label: "Max In-Flight Requests",
466
+ description:
467
+ 'Maximum concurrent LLM requests per provider id (for example "openai" or "anthropic"), shared across local OMP processes with this config root. Omitted providers are unlimited.',
468
+ },
469
+ },
470
+
458
471
  disabledExtensions: { type: "array", default: EMPTY_STRING_ARRAY },
459
472
 
460
473
  modelRoles: { type: "record", default: EMPTY_STRING_RECORD },
@@ -778,6 +791,17 @@ export const SETTINGS_SCHEMA = {
778
791
  "Maximum number of inline images kept as live terminal graphics (default 8). Older images fall back to a text placeholder via a full redraw once the limit is exceeded. Set to 0 to keep every image (no limit).",
779
792
  },
780
793
 
794
+ "terminal.showProgress": {
795
+ type: "boolean",
796
+ default: false,
797
+ ui: {
798
+ tab: "appearance",
799
+ group: "Display",
800
+ label: "Native Terminal Progress",
801
+ description: "Emit OSC 9;4 indeterminate progress while the agent or context maintenance is running",
802
+ },
803
+ },
804
+
781
805
  "tui.textSizing": {
782
806
  type: "boolean",
783
807
  default: false,
@@ -961,15 +985,37 @@ export const SETTINGS_SCHEMA = {
961
985
  },
962
986
  },
963
987
 
964
- inlineToolDescriptors: {
988
+ "model.loopGuard.toolCallReminder": {
965
989
  type: "boolean",
966
- default: false,
990
+ default: true,
991
+ ui: {
992
+ tab: "model",
993
+ group: "Thinking",
994
+ label: "Loop Guard Tool-Call Reminder",
995
+ description:
996
+ "When a Gemini reasoning stream emits many consecutive planning headers without calling a tool, interrupt it and inject a reminder to issue a tool call (requires Loop Guard)",
997
+ },
998
+ },
999
+
1000
+ inlineToolDescriptors: {
1001
+ type: "enum",
1002
+ values: ["auto", "on", "off"] as const,
1003
+ default: "auto",
967
1004
  ui: {
968
1005
  tab: "model",
969
1006
  group: "Prompt",
970
1007
  label: "Inline Tool Descriptors",
971
1008
  description:
972
- "Render full tool descriptors in the system prompt and strip top-level/nested descriptions from provider tool schemas so descriptor text is sent once",
1009
+ "Render full tool descriptors in the system prompt and strip top-level/nested descriptions from provider tool schemas so descriptor text is sent once. Auto enables this for Gemini models and disables it otherwise",
1010
+ options: [
1011
+ {
1012
+ value: "auto",
1013
+ label: "Auto",
1014
+ description: "Inline descriptors for Gemini models; keep them in tool schemas otherwise",
1015
+ },
1016
+ { value: "on", label: "On", description: "Always inline descriptors in the system prompt" },
1017
+ { value: "off", label: "Off", description: "Keep descriptors in provider tool schemas only" },
1018
+ ],
973
1019
  },
974
1020
  },
975
1021
 
@@ -3228,37 +3274,37 @@ export const SETTINGS_SCHEMA = {
3228
3274
  },
3229
3275
  },
3230
3276
 
3231
- // Search and AST tools
3232
- "find.enabled": {
3277
+ // Grep, glob, and AST tools
3278
+ "glob.enabled": {
3233
3279
  type: "boolean",
3234
3280
  default: true,
3235
3281
  ui: {
3236
3282
  tab: "tools",
3237
3283
  group: "Available Tools",
3238
- label: "Find",
3239
- description: "Enable the find tool for glob-based file lookup",
3284
+ label: "Glob",
3285
+ description: "Enable the glob tool for glob-based file lookup",
3240
3286
  },
3241
3287
  },
3242
3288
 
3243
- "search.enabled": {
3289
+ "grep.enabled": {
3244
3290
  type: "boolean",
3245
3291
  default: true,
3246
3292
  ui: {
3247
3293
  tab: "tools",
3248
3294
  group: "Available Tools",
3249
- label: "Search",
3250
- description: "Enable the search tool for regex content search",
3295
+ label: "Grep",
3296
+ description: "Enable the grep tool for regex content search",
3251
3297
  },
3252
3298
  },
3253
3299
 
3254
- "search.contextBefore": {
3300
+ "grep.contextBefore": {
3255
3301
  type: "number",
3256
3302
  default: 1,
3257
3303
  ui: {
3258
3304
  tab: "tools",
3259
- group: "Search & Browser",
3260
- label: "Search Context Before",
3261
- description: "Lines of context before each search match",
3305
+ group: "Grep & Browser",
3306
+ label: "Grep Context Before",
3307
+ description: "Lines of context before each grep match",
3262
3308
  options: [
3263
3309
  { value: "0", label: "0 lines" },
3264
3310
  { value: "1", label: "1 line" },
@@ -3269,14 +3315,14 @@ export const SETTINGS_SCHEMA = {
3269
3315
  },
3270
3316
  },
3271
3317
 
3272
- "search.contextAfter": {
3318
+ "grep.contextAfter": {
3273
3319
  type: "number",
3274
3320
  default: 3,
3275
3321
  ui: {
3276
3322
  tab: "tools",
3277
- group: "Search & Browser",
3278
- label: "Search Context After",
3279
- description: "Lines of context after each search match",
3323
+ group: "Grep & Browser",
3324
+ label: "Grep Context After",
3325
+ description: "Lines of context after each grep match",
3280
3326
  options: [
3281
3327
  { value: "0", label: "0 lines" },
3282
3328
  { value: "1", label: "1 line" },
@@ -3454,7 +3500,7 @@ export const SETTINGS_SCHEMA = {
3454
3500
  default: true,
3455
3501
  ui: {
3456
3502
  tab: "tools",
3457
- group: "Search & Browser",
3503
+ group: "Grep & Browser",
3458
3504
  label: "Headless Browser",
3459
3505
  description: "Launch browser in headless mode (disable to show browser UI)",
3460
3506
  },
@@ -3465,7 +3511,7 @@ export const SETTINGS_SCHEMA = {
3465
3511
  default: true,
3466
3512
  ui: {
3467
3513
  tab: "tools",
3468
- group: "Search & Browser",
3514
+ group: "Grep & Browser",
3469
3515
  label: "cmux Browser",
3470
3516
  description:
3471
3517
  "Use cmux WKWebView surfaces for browser automation when a cmux socket is available. Set PI_BROWSER_CMUX=0 or PI_BROWSER_CMUX=1 to override.",
@@ -3476,7 +3522,7 @@ export const SETTINGS_SCHEMA = {
3476
3522
  default: undefined,
3477
3523
  ui: {
3478
3524
  tab: "tools",
3479
- group: "Search & Browser",
3525
+ group: "Grep & Browser",
3480
3526
  label: "Screenshot Directory",
3481
3527
  description:
3482
3528
  "Directory to save screenshots. If unset, screenshots go to a temp file. Supports ~. Examples: ~/Downloads, ~/Desktop, /sdcard/Download (Android)",
@@ -3608,7 +3654,7 @@ export const SETTINGS_SCHEMA = {
3608
3654
  group: "Discovery & MCP",
3609
3655
  label: "Essential Tools Override",
3610
3656
  description:
3611
- "Override the always-loaded built-in tools (default: read, bash, edit, write, find, eval). Leave empty to use defaults.",
3657
+ "Override the always-loaded built-in tools (default: read, bash, edit, write, glob, eval). Leave empty to use defaults.",
3612
3658
  },
3613
3659
  },
3614
3660
 
@@ -3811,6 +3857,18 @@ export const SETTINGS_SCHEMA = {
3811
3857
  },
3812
3858
  },
3813
3859
 
3860
+ "worktree.base": {
3861
+ type: "string",
3862
+ default: undefined,
3863
+ ui: {
3864
+ tab: "tasks",
3865
+ group: "Isolation",
3866
+ label: "Worktree Base Directory",
3867
+ description:
3868
+ "Base directory for agent-managed worktrees — task-isolation copies, `github` PR checkouts, and `omp worktree` cleanup all live here. Unset uses ~/.omp/wt. Must be an absolute or ~-relative path; relative paths are ignored. The OMP_WORKTREE_DIR env var overrides this.",
3869
+ },
3870
+ },
3871
+
3814
3872
  "task.eager": {
3815
3873
  type: "enum",
3816
3874
  values: ["default", "preferred", "always"] as const,
@@ -14,6 +14,7 @@
14
14
  import * as fs from "node:fs";
15
15
  import * as os from "node:os";
16
16
  import * as path from "node:path";
17
+ import { configureProviderMaxInFlightRequests } from "@oh-my-pi/pi-ai/stream";
17
18
  import {
18
19
  getAgentDbPath,
19
20
  getAgentDir,
@@ -22,6 +23,7 @@ import {
22
23
  isEnoent,
23
24
  logger,
24
25
  procmgr,
26
+ setWorktreesDir,
25
27
  } from "@oh-my-pi/pi-utils";
26
28
  import { JSONC, YAML } from "bun";
27
29
  import { type Settings as SettingsCapabilityItem, settingsCapability } from "../capability/settings";
@@ -29,6 +31,7 @@ import type { ModelRole } from "../config/model-roles";
29
31
  import { loadCapability } from "../discovery";
30
32
  import { isLightTheme, setAutoThemeMapping, setColorBlindMode, setSymbolPreset } from "../modes/theme/theme";
31
33
  import { AgentStorage } from "../session/agent-storage";
34
+ import { normalizeToolName } from "../tools/builtin-names";
32
35
  import { type EditMode, normalizeEditMode } from "../utils/edit-mode";
33
36
  import { withFileLock } from "./file-lock";
34
37
  import {
@@ -107,6 +110,33 @@ function setByPath(obj: RawSettings, segments: string[], value: unknown): void {
107
110
  current[segments[segments.length - 1]] = value;
108
111
  }
109
112
 
113
+ export function normalizeProviderMaxInFlightRequests(value: unknown): Record<string, number> {
114
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
115
+ const normalized: Record<string, number> = {};
116
+ for (const [provider, rawLimit] of Object.entries(value)) {
117
+ if (typeof rawLimit !== "number" || !Number.isFinite(rawLimit) || rawLimit <= 0) continue;
118
+ normalized[provider] = Math.max(1, Math.floor(rawLimit));
119
+ }
120
+ return normalized;
121
+ }
122
+
123
+ export function validateProviderMaxInFlightRequests(value: unknown): Record<string, number> {
124
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
125
+ const invalidProviders: string[] = [];
126
+ const normalized: Record<string, number> = {};
127
+ for (const [provider, rawLimit] of Object.entries(value)) {
128
+ if (typeof rawLimit !== "number" || !Number.isFinite(rawLimit) || rawLimit <= 0) {
129
+ invalidProviders.push(provider);
130
+ continue;
131
+ }
132
+ normalized[provider] = Math.max(1, Math.floor(rawLimit));
133
+ }
134
+ if (invalidProviders.length > 0) {
135
+ throw new Error(`Provider request limits must be positive numbers: ${invalidProviders.join(", ")}`);
136
+ }
137
+ return normalized;
138
+ }
139
+
110
140
  const PATH_SCOPED_ARRAY_SETTINGS = new Set<SettingPath>(["enabledModels", "disabledProviders"]);
111
141
  type PathScopedStringArrayEntry = {
112
142
  path?: unknown;
@@ -361,7 +391,7 @@ export class Settings {
361
391
  // Trigger hook if exists
362
392
  const hook = SETTING_HOOKS[path];
363
393
  if (hook) {
364
- hook(value, prev);
394
+ hook(next, prev);
365
395
  }
366
396
  this.#fireEffectiveSettingChanged(path, next, prev);
367
397
  }
@@ -854,6 +884,14 @@ export class Settings {
854
884
  raw["snapcompact.systemPrompt"] = raw["snapcompact.systemPrompt"] ? "all" : "none";
855
885
  }
856
886
 
887
+ // inlineToolDescriptors: boolean -> enum (auto | on | off). The old
888
+ // `true`/`false` mapped directly onto inline-on/inline-off, so preserve
889
+ // the user's explicit choice; new installs get the `auto` default that
890
+ // turns it on only for Gemini models.
891
+ if (typeof raw.inlineToolDescriptors === "boolean") {
892
+ raw.inlineToolDescriptors = raw.inlineToolDescriptors ? "on" : "off";
893
+ }
894
+
857
895
  // statusLine: rename "plan_mode" segment to "mode"
858
896
  const statusLineObj = raw.statusLine as Record<string, unknown> | undefined;
859
897
  if (statusLineObj) {
@@ -987,6 +1025,124 @@ export class Settings {
987
1025
  delete raw["power.preventDisplaySleep"];
988
1026
  }
989
1027
 
1028
+ // Migration for renamed settings grep.* and glob.* from search.* and find.*:
1029
+ // 1. Nested settings: find -> glob, search -> grep (per-property merge to avoid clobbering)
1030
+ const ensureRawObject = (key: "glob" | "grep"): Record<string, unknown> => {
1031
+ const current = raw[key];
1032
+ if (isRecord(current)) {
1033
+ return current;
1034
+ }
1035
+ const created: Record<string, unknown> = {};
1036
+ raw[key] = created;
1037
+ return created;
1038
+ };
1039
+
1040
+ if ("find" in raw) {
1041
+ const findObj = raw.find;
1042
+ if (isRecord(findObj)) {
1043
+ const globObj = ensureRawObject("glob");
1044
+ const findKeys: Array<"enabled"> = ["enabled"];
1045
+ for (const key of findKeys) {
1046
+ if (key in findObj && !(key in globObj)) {
1047
+ globObj[key] = findObj[key];
1048
+ }
1049
+ }
1050
+ }
1051
+ delete raw.find;
1052
+ }
1053
+
1054
+ if ("search" in raw) {
1055
+ const searchObj = raw.search;
1056
+ if (isRecord(searchObj)) {
1057
+ const grepObj = ensureRawObject("grep");
1058
+ const searchKeys: Array<"enabled" | "contextBefore" | "contextAfter"> = [
1059
+ "enabled",
1060
+ "contextBefore",
1061
+ "contextAfter",
1062
+ ];
1063
+ for (const key of searchKeys) {
1064
+ if (key in searchObj && !(key in grepObj)) {
1065
+ grepObj[key] = searchObj[key];
1066
+ }
1067
+ }
1068
+ }
1069
+ delete raw.search;
1070
+ }
1071
+
1072
+ // 2. Flat settings keys: map them to the proper nested target so get/set resolves them correctly
1073
+ if ("find.enabled" in raw) {
1074
+ const globObj = ensureRawObject("glob");
1075
+ if (!("enabled" in globObj)) {
1076
+ globObj.enabled = raw["find.enabled"];
1077
+ }
1078
+ delete raw["find.enabled"];
1079
+ }
1080
+ if ("search.enabled" in raw) {
1081
+ const grepObj = ensureRawObject("grep");
1082
+ if (!("enabled" in grepObj)) {
1083
+ grepObj.enabled = raw["search.enabled"];
1084
+ }
1085
+ delete raw["search.enabled"];
1086
+ }
1087
+ if ("search.contextBefore" in raw) {
1088
+ const grepObj = ensureRawObject("grep");
1089
+ if (!("contextBefore" in grepObj)) {
1090
+ grepObj.contextBefore = raw["search.contextBefore"];
1091
+ }
1092
+ delete raw["search.contextBefore"];
1093
+ }
1094
+ if ("search.contextAfter" in raw) {
1095
+ const grepObj = ensureRawObject("grep");
1096
+ if (!("contextAfter" in grepObj)) {
1097
+ grepObj.contextAfter = raw["search.contextAfter"];
1098
+ }
1099
+ delete raw["search.contextAfter"];
1100
+ }
1101
+
1102
+ // 3. Tool-name arrays use wire IDs too. Preserve user overrides across
1103
+ // the rename without duplicating entries if they already added grep/glob.
1104
+ const migrateToolNameList = (names: unknown): unknown => {
1105
+ if (!Array.isArray(names)) return names;
1106
+ const out: unknown[] = [];
1107
+ const seen = new Set<string>();
1108
+ for (const name of names) {
1109
+ const migrated = typeof name === "string" ? normalizeToolName(name) : name;
1110
+ if (typeof migrated === "string") {
1111
+ if (seen.has(migrated)) continue;
1112
+ seen.add(migrated);
1113
+ }
1114
+ out.push(migrated);
1115
+ }
1116
+ return out;
1117
+ };
1118
+ const ensureToolsObject = (): Record<string, unknown> => {
1119
+ const current = raw.tools;
1120
+ if (current && typeof current === "object" && !Array.isArray(current)) {
1121
+ return current as Record<string, unknown>;
1122
+ }
1123
+ const created: Record<string, unknown> = {};
1124
+ raw.tools = created;
1125
+ return created;
1126
+ };
1127
+ const toolsObj = raw.tools as Record<string, unknown> | undefined;
1128
+ if (toolsObj && "essentialOverride" in toolsObj) {
1129
+ toolsObj.essentialOverride = migrateToolNameList(toolsObj.essentialOverride);
1130
+ }
1131
+ if ("tools.essentialOverride" in raw) {
1132
+ const nestedToolsObj = ensureToolsObject();
1133
+ if (!("essentialOverride" in nestedToolsObj)) {
1134
+ nestedToolsObj.essentialOverride = migrateToolNameList(raw["tools.essentialOverride"]);
1135
+ }
1136
+ delete raw["tools.essentialOverride"];
1137
+ }
1138
+
1139
+ // Also clean up any empty nested objects we might have created or left behind
1140
+ if (raw.glob && typeof raw.glob === "object" && Object.keys(raw.glob).length === 0) {
1141
+ delete raw.glob;
1142
+ }
1143
+ if (raw.grep && typeof raw.grep === "object" && Object.keys(raw.grep).length === 0) {
1144
+ delete raw.grep;
1145
+ }
990
1146
  // readHashLines: removed. Hashline anchors are now driven solely by
991
1147
  // edit.mode === "hashline"; the separate read toggle only ever produced
992
1148
  // the incoherent "hashline edits without addressable anchors" state.
@@ -1190,9 +1346,23 @@ const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook<any>>> = {
1190
1346
  appendOnlyModeSignal.fire(value);
1191
1347
  }
1192
1348
  },
1349
+ "providers.maxInFlightRequests": value => {
1350
+ configureProviderMaxInFlightRequests(validateProviderMaxInFlightRequests(value));
1351
+ },
1193
1352
  "hindsight.bankId": () => hindsightScopeSignal.fire(),
1194
1353
  "hindsight.bankIdPrefix": () => hindsightScopeSignal.fire(),
1195
1354
  "hindsight.scoping": () => hindsightScopeSignal.fire(),
1355
+ "worktree.base": value => {
1356
+ const dir = typeof value === "string" && value.trim() ? value : undefined;
1357
+ // Always call so an unset/empty value clears a previously-applied override.
1358
+ // setWorktreesDir expands `~`, rejects relative paths, and returns the
1359
+ // applied absolute path (or undefined when cleared/rejected).
1360
+ if (dir && !setWorktreesDir(dir)) {
1361
+ logger.warn("Settings: worktree.base must be an absolute or ~-relative path; ignoring", { value: dir });
1362
+ } else if (!dir) {
1363
+ setWorktreesDir(undefined);
1364
+ }
1365
+ },
1196
1366
  };
1197
1367
  /** Fires when `provider.appendOnlyContext` changes at runtime. */
1198
1368
  const appendOnlyModeSignal = new SettingSignal<[value: string]>("provider.appendOnlyContext");
@@ -1254,6 +1424,7 @@ export function resetSettingsForTest(): void {
1254
1424
  globalInstance = null;
1255
1425
  globalInstancePromise = null;
1256
1426
  clearBoundSettingsMethods();
1427
+ configureProviderMaxInFlightRequests(undefined);
1257
1428
  }
1258
1429
 
1259
1430
  /**
package/src/cursor.ts CHANGED
@@ -178,7 +178,7 @@ export class CursorExecHandlers implements ICursorExecHandlers {
178
178
  async grep(args: Parameters<NonNullable<ICursorExecHandlers["grep"]>>[0]) {
179
179
  const toolCallId = decodeToolCallId(args.toolCallId);
180
180
  const searchPath = args.glob ? `${args.path || "."}/${args.glob}` : args.path || ".";
181
- const toolResultMessage = await executeTool(this.options, "search", toolCallId, {
181
+ const toolResultMessage = await executeTool(this.options, "grep", toolCallId, {
182
182
  pattern: args.pattern,
183
183
  paths: [searchPath],
184
184
  case: args.caseInsensitive === true ? false : undefined,