@oh-my-pi/pi-coding-agent 16.1.22 → 16.2.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 (276) hide show
  1. package/CHANGELOG.md +99 -1
  2. package/dist/cli.js +4387 -4164
  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 +2 -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/cli/gc-cli.d.ts +58 -0
  11. package/dist/types/collab/display-name.d.ts +3 -0
  12. package/dist/types/collab/host.d.ts +0 -2
  13. package/dist/types/commands/gc.d.ts +37 -0
  14. package/dist/types/commands/worktree.d.ts +0 -3
  15. package/dist/types/config/inline-tool-descriptors-mode.d.ts +13 -0
  16. package/dist/types/config/model-discovery.d.ts +6 -1
  17. package/dist/types/config/model-registry.d.ts +6 -2
  18. package/dist/types/config/model-resolver.d.ts +12 -0
  19. package/dist/types/config/models-config-schema.d.ts +29 -2
  20. package/dist/types/config/models-config.d.ts +22 -1
  21. package/dist/types/config/settings-schema.d.ts +145 -21
  22. package/dist/types/config/settings.d.ts +13 -0
  23. package/dist/types/dap/config.d.ts +1 -1
  24. package/dist/types/edit/hashline/filesystem.d.ts +4 -2
  25. package/dist/types/edit/renderer.d.ts +4 -0
  26. package/dist/types/extensibility/extensions/types.d.ts +17 -17
  27. package/dist/types/extensibility/hooks/types.d.ts +10 -10
  28. package/dist/types/extensibility/plugins/marketplace/types.d.ts +1 -0
  29. package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +1 -0
  30. package/dist/types/internal-urls/index.d.ts +1 -0
  31. package/dist/types/internal-urls/registry-helpers.d.ts +2 -0
  32. package/dist/types/internal-urls/router.d.ts +1 -1
  33. package/dist/types/internal-urls/ssh-protocol.d.ts +10 -0
  34. package/dist/types/internal-urls/types.d.ts +19 -2
  35. package/dist/types/irc/bus.d.ts +6 -0
  36. package/dist/types/mcp/transports/stdio.d.ts +25 -1
  37. package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +1 -0
  38. package/dist/types/modes/components/custom-editor.d.ts +7 -0
  39. package/dist/types/modes/components/move-overlay.d.ts +23 -0
  40. package/dist/types/modes/components/plugin-selector.d.ts +2 -1
  41. package/dist/types/modes/components/queue-mode-selector.d.ts +2 -1
  42. package/dist/types/modes/components/select-list-mouse-routing.d.ts +2 -0
  43. package/dist/types/modes/components/settings-defs.d.ts +4 -1
  44. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  45. package/dist/types/modes/components/show-images-selector.d.ts +2 -1
  46. package/dist/types/modes/components/status-line/component.d.ts +2 -0
  47. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  48. package/dist/types/modes/components/theme-selector.d.ts +2 -1
  49. package/dist/types/modes/components/thinking-selector.d.ts +2 -1
  50. package/dist/types/modes/controllers/command-controller.d.ts +10 -1
  51. package/dist/types/modes/controllers/streaming-reveal.d.ts +6 -0
  52. package/dist/types/modes/interactive-mode.d.ts +10 -1
  53. package/dist/types/modes/internal-url-autocomplete.d.ts +1 -1
  54. package/dist/types/modes/running-subagent-badge.d.ts +6 -0
  55. package/dist/types/modes/theme/mermaid-rendering.test.d.ts +1 -0
  56. package/dist/types/modes/theme/theme.d.ts +2 -1
  57. package/dist/types/modes/types.d.ts +9 -1
  58. package/dist/types/sdk.d.ts +2 -2
  59. package/dist/types/session/agent-session.d.ts +2 -1
  60. package/dist/types/session/session-listing.d.ts +10 -1
  61. package/dist/types/session/session-manager.d.ts +13 -0
  62. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  63. package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +1 -0
  64. package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +1 -0
  65. package/dist/types/ssh/connection-manager.d.ts +2 -0
  66. package/dist/types/ssh/file-transfer.d.ts +79 -0
  67. package/dist/types/ssh/utils.d.ts +6 -0
  68. package/dist/types/system-prompt.d.ts +5 -0
  69. package/dist/types/task/executor.d.ts +16 -0
  70. package/dist/types/tiny/text.d.ts +1 -1
  71. package/dist/types/tools/builtin-names.d.ts +5 -1
  72. package/dist/types/tools/{find.d.ts → glob.d.ts} +15 -15
  73. package/dist/types/tools/{search.d.ts → grep.d.ts} +14 -14
  74. package/dist/types/tools/index.d.ts +3 -3
  75. package/dist/types/tools/path-utils.d.ts +29 -0
  76. package/dist/types/tools/plan-mode-guard.d.ts +7 -0
  77. package/dist/types/tools/read.d.ts +2 -2
  78. package/dist/types/tools/render-utils.d.ts +8 -0
  79. package/dist/types/tools/renderers.d.ts +11 -0
  80. package/dist/types/tools/ssh.d.ts +2 -0
  81. package/dist/types/tools/todo.d.ts +0 -16
  82. package/dist/types/tools/write.d.ts +2 -2
  83. package/dist/types/utils/active-repo-context.d.ts +8 -0
  84. package/dist/types/utils/image-resize.d.ts +1 -0
  85. package/dist/types/utils/markit-cache.d.ts +23 -0
  86. package/dist/types/utils/markit.d.ts +5 -1
  87. package/dist/types/utils/prompt-path.d.ts +1 -0
  88. package/dist/types/web/search/providers/duckduckgo.d.ts +14 -0
  89. package/dist/types/web/search/providers/firecrawl.d.ts +28 -0
  90. package/dist/types/web/search/providers/tinyfish.d.ts +29 -0
  91. package/dist/types/web/search/providers/xai.d.ts +13 -0
  92. package/dist/types/web/search/types.d.ts +18 -2
  93. package/package.json +30 -15
  94. package/scripts/bench-guard.ts +1 -1
  95. package/scripts/build-binary.ts +9 -9
  96. package/scripts/bundle-dist.ts +2 -2
  97. package/src/advisor/__tests__/advisor.test.ts +40 -4
  98. package/src/advisor/__tests__/emission-guard.test.ts +147 -0
  99. package/src/advisor/advise-tool.ts +1 -1
  100. package/src/advisor/emission-guard.ts +172 -0
  101. package/src/advisor/index.ts +1 -0
  102. package/src/advisor/runtime.ts +11 -0
  103. package/src/advisor/watchdog.ts +12 -1
  104. package/src/cli/args.ts +5 -2
  105. package/src/cli/auth-broker-cli.ts +17 -0
  106. package/src/cli/config-cli.ts +4 -0
  107. package/src/cli/flag-tables.ts +7 -4
  108. package/src/cli/gallery-cli.ts +14 -2
  109. package/src/cli/gallery-fixtures/edit.ts +60 -0
  110. package/src/cli/gallery-fixtures/fs.ts +17 -17
  111. package/src/cli/gallery-fixtures/search.ts +4 -4
  112. package/src/cli/gc-cli.ts +939 -0
  113. package/src/cli/usage-cli.ts +20 -1
  114. package/src/cli/web-search-cli.ts +1 -1
  115. package/src/cli-commands.ts +1 -0
  116. package/src/collab/display-name.ts +13 -0
  117. package/src/collab/guest.ts +5 -1
  118. package/src/collab/host.ts +2 -13
  119. package/src/commands/gc.ts +46 -0
  120. package/src/commands/worktree.ts +6 -0
  121. package/src/config/inline-tool-descriptors-mode.ts +27 -0
  122. package/src/config/model-discovery.ts +77 -8
  123. package/src/config/model-registry.ts +89 -11
  124. package/src/config/model-resolver.ts +39 -1
  125. package/src/config/models-config-schema.ts +41 -5
  126. package/src/config/models-config.ts +4 -1
  127. package/src/config/settings-schema.ts +130 -27
  128. package/src/config/settings.ts +216 -7
  129. package/src/cursor.ts +1 -1
  130. package/src/dap/config.ts +152 -8
  131. package/src/dap/session.ts +1 -1
  132. package/src/discovery/helpers.ts +3 -1
  133. package/src/edit/hashline/diff.ts +14 -3
  134. package/src/edit/hashline/execute.ts +42 -6
  135. package/src/edit/hashline/filesystem.ts +49 -8
  136. package/src/edit/index.ts +1 -0
  137. package/src/edit/modes/patch.ts +11 -1
  138. package/src/edit/renderer.ts +140 -13
  139. package/src/eval/__tests__/agent-bridge.test.ts +101 -0
  140. package/src/eval/__tests__/julia-prelude.test.ts +18 -0
  141. package/src/eval/agent-bridge.ts +26 -3
  142. package/src/eval/jl/runner.jl +7 -1
  143. package/src/eval/js/tool-bridge.ts +2 -2
  144. package/src/export/html/index.ts +1 -1
  145. package/src/export/html/template.js +3 -1
  146. package/src/export/html/tool-views.generated.js +20 -20
  147. package/src/extensibility/extensions/types.ts +22 -22
  148. package/src/extensibility/hooks/types.ts +11 -11
  149. package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +10 -3
  150. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +31 -10
  151. package/src/extensibility/plugins/marketplace/manager.ts +22 -0
  152. package/src/extensibility/plugins/marketplace/types.ts +1 -0
  153. package/src/internal-urls/__tests__/ssh-protocol.test.ts +331 -0
  154. package/src/internal-urls/docs-index.generated.txt +2 -2
  155. package/src/internal-urls/docs-index.ts +2 -3
  156. package/src/internal-urls/index.ts +1 -0
  157. package/src/internal-urls/registry-helpers.ts +19 -4
  158. package/src/internal-urls/router.ts +5 -3
  159. package/src/internal-urls/ssh-protocol.ts +367 -0
  160. package/src/internal-urls/types.ts +19 -2
  161. package/src/irc/bus.ts +11 -3
  162. package/src/lsp/index.ts +8 -1
  163. package/src/mcp/oauth-discovery.ts +20 -20
  164. package/src/mcp/tool-bridge.ts +32 -2
  165. package/src/mcp/transports/stdio.test.ts +20 -3
  166. package/src/mcp/transports/stdio.ts +45 -14
  167. package/src/memories/index.ts +1 -1
  168. package/src/modes/acp/acp-event-mapper.ts +2 -2
  169. package/src/modes/components/__tests__/move-overlay.test.ts +166 -0
  170. package/src/modes/components/agent-hub.ts +3 -0
  171. package/src/modes/components/agent-transcript-viewer.ts +9 -7
  172. package/src/modes/components/custom-editor.ts +90 -5
  173. package/src/modes/components/move-overlay.ts +282 -0
  174. package/src/modes/components/plan-review-overlay.ts +35 -35
  175. package/src/modes/components/plugin-selector.ts +6 -1
  176. package/src/modes/components/queue-mode-selector.ts +6 -1
  177. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  178. package/src/modes/components/session-selector.ts +11 -10
  179. package/src/modes/components/settings-defs.ts +14 -1
  180. package/src/modes/components/settings-selector.ts +196 -29
  181. package/src/modes/components/show-images-selector.ts +6 -1
  182. package/src/modes/components/status-line/component.ts +108 -28
  183. package/src/modes/components/status-line/segments.ts +5 -1
  184. package/src/modes/components/status-line/types.ts +2 -0
  185. package/src/modes/components/theme-selector.ts +6 -1
  186. package/src/modes/components/thinking-selector.ts +6 -1
  187. package/src/modes/components/tool-execution.ts +25 -9
  188. package/src/modes/components/tree-selector.ts +5 -5
  189. package/src/modes/controllers/command-controller.ts +140 -47
  190. package/src/modes/controllers/event-controller.ts +59 -3
  191. package/src/modes/controllers/input-controller.ts +70 -4
  192. package/src/modes/controllers/selector-controller.ts +15 -2
  193. package/src/modes/controllers/streaming-reveal.ts +17 -0
  194. package/src/modes/controllers/tool-args-reveal.ts +1 -1
  195. package/src/modes/interactive-mode.ts +142 -59
  196. package/src/modes/internal-url-autocomplete.ts +17 -2
  197. package/src/modes/prompt-action-autocomplete.ts +3 -1
  198. package/src/modes/running-subagent-badge.ts +13 -0
  199. package/src/modes/setup-wizard/scenes/glyph.ts +2 -13
  200. package/src/modes/setup-wizard/scenes/providers.ts +2 -1
  201. package/src/modes/setup-wizard/scenes/theme.ts +6 -12
  202. package/src/modes/setup-wizard/scenes/web-search.ts +8 -13
  203. package/src/modes/setup-wizard/wizard-overlay.ts +6 -5
  204. package/src/modes/theme/mermaid-rendering.test.ts +53 -0
  205. package/src/modes/theme/theme.ts +42 -15
  206. package/src/modes/types.ts +9 -1
  207. package/src/modes/utils/interactive-context-helpers.ts +1 -1
  208. package/src/priority.json +15 -0
  209. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  210. package/src/prompts/advisor/system.md +21 -7
  211. package/src/prompts/agents/designer.md +1 -1
  212. package/src/prompts/agents/explore.md +1 -1
  213. package/src/prompts/agents/librarian.md +2 -2
  214. package/src/prompts/agents/plan.md +2 -2
  215. package/src/prompts/agents/reviewer.md +1 -1
  216. package/src/prompts/agents/task.md +2 -2
  217. package/src/prompts/system/active-repo-context.md +4 -0
  218. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  219. package/src/prompts/system/plan-mode-active.md +12 -3
  220. package/src/prompts/system/project-prompt.md +2 -2
  221. package/src/prompts/system/system-prompt.md +8 -6
  222. package/src/prompts/tools/bash.md +2 -2
  223. package/src/prompts/tools/checkpoint.md +1 -1
  224. package/src/prompts/tools/{find.md → glob.md} +1 -1
  225. package/src/prompts/tools/{search.md → grep.md} +3 -3
  226. package/src/prompts/tools/read.md +4 -2
  227. package/src/sdk.ts +98 -29
  228. package/src/session/agent-session.ts +828 -191
  229. package/src/session/session-history-format.ts +2 -2
  230. package/src/session/session-listing.ts +35 -2
  231. package/src/session/session-manager.ts +46 -0
  232. package/src/slash-commands/builtin-registry.ts +158 -22
  233. package/src/slash-commands/helpers/usage-report.ts +23 -2
  234. package/src/ssh/__tests__/connection-manager-args.test.ts +69 -0
  235. package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +68 -0
  236. package/src/ssh/connection-manager.ts +12 -15
  237. package/src/ssh/file-transfer.ts +209 -0
  238. package/src/ssh/utils.ts +24 -0
  239. package/src/system-prompt.ts +60 -28
  240. package/src/task/executor.ts +96 -36
  241. package/src/task/index.ts +3 -3
  242. package/src/task/render.ts +14 -13
  243. package/src/task/worktree.ts +38 -1
  244. package/src/tiny/text.ts +49 -4
  245. package/src/tiny/worker.ts +3 -3
  246. package/src/tools/acp-bridge.ts +6 -1
  247. package/src/tools/bash.ts +2 -2
  248. package/src/tools/builtin-names.ts +26 -2
  249. package/src/tools/{find.ts → glob.ts} +48 -42
  250. package/src/tools/{search.ts → grep.ts} +311 -129
  251. package/src/tools/index.ts +13 -14
  252. package/src/tools/irc.ts +6 -2
  253. package/src/tools/path-utils.ts +64 -1
  254. package/src/tools/plan-mode-guard.ts +26 -13
  255. package/src/tools/read.ts +35 -6
  256. package/src/tools/render-utils.ts +14 -0
  257. package/src/tools/renderers.ts +15 -4
  258. package/src/tools/ssh.ts +17 -2
  259. package/src/tools/todo.ts +4 -26
  260. package/src/tools/write.ts +25 -9
  261. package/src/utils/active-repo-context.ts +143 -0
  262. package/src/utils/edit-mode.ts +19 -2
  263. package/src/utils/image-resize.ts +88 -7
  264. package/src/utils/lang-from-path.ts +3 -3
  265. package/src/utils/markit-cache.ts +166 -0
  266. package/src/utils/markit.ts +86 -18
  267. package/src/utils/prompt-path.ts +3 -0
  268. package/src/utils/shell-snapshot.ts +1 -1
  269. package/src/utils/title-generator.ts +1 -1
  270. package/src/web/search/provider.ts +54 -34
  271. package/src/web/search/providers/duckduckgo.ts +140 -0
  272. package/src/web/search/providers/firecrawl.ts +144 -0
  273. package/src/web/search/providers/tinyfish.ts +159 -0
  274. package/src/web/search/providers/xai.ts +292 -0
  275. package/src/web/search/providers/zai.ts +142 -56
  276. package/src/web/search/types.ts +6 -2
@@ -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,
@@ -790,6 +814,17 @@ export const SETTINGS_SCHEMA = {
790
814
  },
791
815
  },
792
816
 
817
+ "tui.renderMermaid": {
818
+ type: "boolean",
819
+ default: true,
820
+ ui: {
821
+ tab: "appearance",
822
+ group: "Display",
823
+ label: "Render Mermaid Diagrams",
824
+ description: "Render Mermaid fenced code blocks as ASCII diagrams",
825
+ },
826
+ },
827
+
793
828
  "tui.hyperlinks": {
794
829
  type: "enum",
795
830
  values: ["off", "auto", "always"] as const,
@@ -950,15 +985,37 @@ export const SETTINGS_SCHEMA = {
950
985
  },
951
986
  },
952
987
 
953
- inlineToolDescriptors: {
988
+ "model.loopGuard.toolCallReminder": {
954
989
  type: "boolean",
955
- 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",
956
1004
  ui: {
957
1005
  tab: "model",
958
1006
  group: "Prompt",
959
1007
  label: "Inline Tool Descriptors",
960
1008
  description:
961
- "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
+ ],
962
1019
  },
963
1020
  },
964
1021
 
@@ -1700,6 +1757,17 @@ export const SETTINGS_SCHEMA = {
1700
1757
  },
1701
1758
  },
1702
1759
 
1760
+ "compaction.midTurnEnabled": {
1761
+ type: "boolean",
1762
+ default: true,
1763
+ ui: {
1764
+ tab: "context",
1765
+ group: "Compaction",
1766
+ label: "Mid-Turn Compaction",
1767
+ description: "Check thresholds at safe mid-turn tool-loop boundaries before the next provider request",
1768
+ },
1769
+ },
1770
+
1703
1771
  "compaction.strategy": {
1704
1772
  type: "enum",
1705
1773
  values: ["context-full", "handoff", "shake", "snapcompact", "off"] as const,
@@ -3206,37 +3274,37 @@ export const SETTINGS_SCHEMA = {
3206
3274
  },
3207
3275
  },
3208
3276
 
3209
- // Search and AST tools
3210
- "find.enabled": {
3277
+ // Grep, glob, and AST tools
3278
+ "glob.enabled": {
3211
3279
  type: "boolean",
3212
3280
  default: true,
3213
3281
  ui: {
3214
3282
  tab: "tools",
3215
3283
  group: "Available Tools",
3216
- label: "Find",
3217
- description: "Enable the find tool for glob-based file lookup",
3284
+ label: "Glob",
3285
+ description: "Enable the glob tool for glob-based file lookup",
3218
3286
  },
3219
3287
  },
3220
3288
 
3221
- "search.enabled": {
3289
+ "grep.enabled": {
3222
3290
  type: "boolean",
3223
3291
  default: true,
3224
3292
  ui: {
3225
3293
  tab: "tools",
3226
3294
  group: "Available Tools",
3227
- label: "Search",
3228
- description: "Enable the search tool for regex content search",
3295
+ label: "Grep",
3296
+ description: "Enable the grep tool for regex content search",
3229
3297
  },
3230
3298
  },
3231
3299
 
3232
- "search.contextBefore": {
3300
+ "grep.contextBefore": {
3233
3301
  type: "number",
3234
3302
  default: 1,
3235
3303
  ui: {
3236
3304
  tab: "tools",
3237
- group: "Search & Browser",
3238
- label: "Search Context Before",
3239
- 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",
3240
3308
  options: [
3241
3309
  { value: "0", label: "0 lines" },
3242
3310
  { value: "1", label: "1 line" },
@@ -3247,14 +3315,14 @@ export const SETTINGS_SCHEMA = {
3247
3315
  },
3248
3316
  },
3249
3317
 
3250
- "search.contextAfter": {
3318
+ "grep.contextAfter": {
3251
3319
  type: "number",
3252
3320
  default: 3,
3253
3321
  ui: {
3254
3322
  tab: "tools",
3255
- group: "Search & Browser",
3256
- label: "Search Context After",
3257
- 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",
3258
3326
  options: [
3259
3327
  { value: "0", label: "0 lines" },
3260
3328
  { value: "1", label: "1 line" },
@@ -3432,7 +3500,7 @@ export const SETTINGS_SCHEMA = {
3432
3500
  default: true,
3433
3501
  ui: {
3434
3502
  tab: "tools",
3435
- group: "Search & Browser",
3503
+ group: "Grep & Browser",
3436
3504
  label: "Headless Browser",
3437
3505
  description: "Launch browser in headless mode (disable to show browser UI)",
3438
3506
  },
@@ -3443,7 +3511,7 @@ export const SETTINGS_SCHEMA = {
3443
3511
  default: true,
3444
3512
  ui: {
3445
3513
  tab: "tools",
3446
- group: "Search & Browser",
3514
+ group: "Grep & Browser",
3447
3515
  label: "cmux Browser",
3448
3516
  description:
3449
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.",
@@ -3454,7 +3522,7 @@ export const SETTINGS_SCHEMA = {
3454
3522
  default: undefined,
3455
3523
  ui: {
3456
3524
  tab: "tools",
3457
- group: "Search & Browser",
3525
+ group: "Grep & Browser",
3458
3526
  label: "Screenshot Directory",
3459
3527
  description:
3460
3528
  "Directory to save screenshots. If unset, screenshots go to a temp file. Supports ~. Examples: ~/Downloads, ~/Desktop, /sdcard/Download (Android)",
@@ -3586,7 +3654,7 @@ export const SETTINGS_SCHEMA = {
3586
3654
  group: "Discovery & MCP",
3587
3655
  label: "Essential Tools Override",
3588
3656
  description:
3589
- "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.",
3590
3658
  },
3591
3659
  },
3592
3660
 
@@ -3789,6 +3857,18 @@ export const SETTINGS_SCHEMA = {
3789
3857
  },
3790
3858
  },
3791
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
+
3792
3872
  "task.eager": {
3793
3873
  type: "enum",
3794
3874
  values: ["default", "preferred", "always"] as const,
@@ -4630,6 +4710,18 @@ export const SETTINGS_SCHEMA = {
4630
4710
  default: "unset" as const,
4631
4711
  },
4632
4712
 
4713
+ "gc.blobs": { type: "boolean", default: true },
4714
+
4715
+ "gc.archive": { type: "boolean", default: true },
4716
+
4717
+ "gc.wal": { type: "boolean", default: true },
4718
+
4719
+ "gc.coldArchiveAfterDays": { type: "number", default: 30 },
4720
+
4721
+ "gc.retainNewestGlobal": { type: "number", default: 20 },
4722
+
4723
+ "gc.retainNewestPerCwd": { type: "number", default: 10 },
4724
+
4633
4725
  "thinkingBudgets.minimal": { type: "number", default: 1024 },
4634
4726
 
4635
4727
  "thinkingBudgets.low": { type: "number", default: 2048 },
@@ -4731,6 +4823,7 @@ export interface CompactionSettings {
4731
4823
  thresholdTokens: number;
4732
4824
  reserveTokens: number;
4733
4825
  keepRecentTokens: number;
4826
+ midTurnEnabled: boolean;
4734
4827
  handoffSaveToDisk: boolean;
4735
4828
  autoContinue: boolean;
4736
4829
  remoteEnabled: boolean;
@@ -4875,6 +4968,15 @@ export interface CodexResetsSettings {
4875
4968
  keepCredits: number;
4876
4969
  }
4877
4970
 
4971
+ export interface GcSettings {
4972
+ blobs: boolean;
4973
+ archive: boolean;
4974
+ wal: boolean;
4975
+ coldArchiveAfterDays: number;
4976
+ retainNewestGlobal: number;
4977
+ retainNewestPerCwd: number;
4978
+ }
4979
+
4878
4980
  /** Map group prefix -> typed settings interface */
4879
4981
  export interface GroupTypeMap {
4880
4982
  compaction: CompactionSettings;
@@ -4894,6 +4996,7 @@ export interface GroupTypeMap {
4894
4996
  cycleOrder: string[];
4895
4997
  shellMinimizer: ShellMinimizerSettings;
4896
4998
  codexResets: CodexResetsSettings;
4999
+ gc: GcSettings;
4897
5000
  }
4898
5001
 
4899
5002
  export type GroupPrefix = keyof GroupTypeMap;
@@ -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 {
@@ -61,6 +64,8 @@ export interface SettingsOptions {
61
64
  agentDir?: string;
62
65
  /** Don't persist to disk (for tests) */
63
66
  inMemory?: boolean;
67
+ /** Read config sources without opening storage or writing migrations */
68
+ readOnly?: boolean;
64
69
  /** Initial overrides */
65
70
  overrides?: Partial<Record<SettingPath, unknown>>;
66
71
  /** Extra config.yml-style overlays loaded after global/project settings */
@@ -105,6 +110,33 @@ function setByPath(obj: RawSettings, segments: string[], value: unknown): void {
105
110
  current[segments[segments.length - 1]] = value;
106
111
  }
107
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
+
108
140
  const PATH_SCOPED_ARRAY_SETTINGS = new Set<SettingPath>(["enabledModels", "disabledProviders"]);
109
141
  type PathScopedStringArrayEntry = {
110
142
  path?: unknown;
@@ -136,11 +168,17 @@ function stringArrayFromUnknown(value: unknown): string[] {
136
168
  return [];
137
169
  }
138
170
 
171
+ function isRecord(value: unknown): value is Record<string, unknown> {
172
+ return !!value && typeof value === "object" && !Array.isArray(value);
173
+ }
174
+
139
175
  function shallowStringRecord(value: unknown): Record<string, string> {
140
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
176
+ if (!isRecord(value)) return {};
141
177
 
142
178
  const result: Record<string, string> = {};
143
- for (const [key, item] of Object.entries(value)) {
179
+ for (const key in value) {
180
+ if (!Object.hasOwn(value, key)) continue;
181
+ const item = value[key];
144
182
  if (typeof item === "string") {
145
183
  result[key] = item;
146
184
  }
@@ -228,7 +266,7 @@ export class Settings {
228
266
  this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
229
267
  this.#configPath = options.inMemory ? null : path.join(this.#agentDir, "config.yml");
230
268
  this.#configFiles = options.configFiles?.map(file => path.resolve(this.#cwd, expandTilde(file))) ?? [];
231
- this.#persist = !options.inMemory;
269
+ this.#persist = !options.inMemory && options.readOnly !== true;
232
270
 
233
271
  if (options.overrides) {
234
272
  for (const [key, value] of Object.entries(options.overrides)) {
@@ -270,6 +308,23 @@ export class Settings {
270
308
  );
271
309
  }
272
310
 
311
+ /**
312
+ * Load effective settings from config.yml and project providers without
313
+ * opening agent.db, migrating legacy settings, or writing marker files.
314
+ */
315
+ static loadReadOnly(options: SettingsOptions = {}): Promise<Settings> {
316
+ const instance = new Settings({ ...options, readOnly: true });
317
+ return instance.#loadReadOnly();
318
+ }
319
+
320
+ /**
321
+ * Load a persisted settings instance without touching the global singleton.
322
+ */
323
+ static loadIsolated(options: SettingsOptions = {}): Promise<Settings> {
324
+ const instance = new Settings(options);
325
+ return instance.#load();
326
+ }
327
+
273
328
  /**
274
329
  * Create an isolated instance for testing.
275
330
  * Does not affect the global singleton.
@@ -336,7 +391,7 @@ export class Settings {
336
391
  // Trigger hook if exists
337
392
  const hook = SETTING_HOOKS[path];
338
393
  if (hook) {
339
- hook(value, prev);
394
+ hook(next, prev);
340
395
  }
341
396
  this.#fireEffectiveSettingChanged(path, next, prev);
342
397
  }
@@ -481,10 +536,10 @@ export class Settings {
481
536
  */
482
537
  getEditVariantForModel(model: string | undefined): EditMode | null {
483
538
  if (!model) return null;
484
- const variants = (this.#merged.edit as { modelVariants?: Record<string, string> })?.modelVariants;
485
- if (!variants) return null;
539
+ const variants = shallowStringRecord(getByPath(this.#merged, ["edit", "modelVariants"]));
540
+ const modelLower = model.toLowerCase();
486
541
  for (const pattern in variants) {
487
- if (model.includes(pattern)) {
542
+ if (modelLower.includes(pattern.toLowerCase())) {
488
543
  const value = normalizeEditMode(variants[pattern]);
489
544
  if (value) {
490
545
  return value;
@@ -583,6 +638,19 @@ export class Settings {
583
638
  return this;
584
639
  }
585
640
 
641
+ async #loadReadOnly(): Promise<Settings> {
642
+ const projectPromise = this.#loadProjectSettings();
643
+
644
+ if (this.#configPath) {
645
+ this.#global = await this.#loadYaml(this.#configPath);
646
+ }
647
+
648
+ this.#project = await projectPromise;
649
+ this.#configOverlay = await this.#loadConfigOverlays();
650
+ this.#rebuildMerged();
651
+ return this;
652
+ }
653
+
586
654
  async #loadYaml(filePath: string): Promise<RawSettings> {
587
655
  try {
588
656
  const content = await Bun.file(filePath).text();
@@ -816,6 +884,14 @@ export class Settings {
816
884
  raw["snapcompact.systemPrompt"] = raw["snapcompact.systemPrompt"] ? "all" : "none";
817
885
  }
818
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
+
819
895
  // statusLine: rename "plan_mode" segment to "mode"
820
896
  const statusLineObj = raw.statusLine as Record<string, unknown> | undefined;
821
897
  if (statusLineObj) {
@@ -949,6 +1025,124 @@ export class Settings {
949
1025
  delete raw["power.preventDisplaySleep"];
950
1026
  }
951
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
+ }
952
1146
  // readHashLines: removed. Hashline anchors are now driven solely by
953
1147
  // edit.mode === "hashline"; the separate read toggle only ever produced
954
1148
  // the incoherent "hashline edits without addressable anchors" state.
@@ -1152,9 +1346,23 @@ const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook<any>>> = {
1152
1346
  appendOnlyModeSignal.fire(value);
1153
1347
  }
1154
1348
  },
1349
+ "providers.maxInFlightRequests": value => {
1350
+ configureProviderMaxInFlightRequests(validateProviderMaxInFlightRequests(value));
1351
+ },
1155
1352
  "hindsight.bankId": () => hindsightScopeSignal.fire(),
1156
1353
  "hindsight.bankIdPrefix": () => hindsightScopeSignal.fire(),
1157
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
+ },
1158
1366
  };
1159
1367
  /** Fires when `provider.appendOnlyContext` changes at runtime. */
1160
1368
  const appendOnlyModeSignal = new SettingSignal<[value: string]>("provider.appendOnlyContext");
@@ -1216,6 +1424,7 @@ export function resetSettingsForTest(): void {
1216
1424
  globalInstance = null;
1217
1425
  globalInstancePromise = null;
1218
1426
  clearBoundSettingsMethods();
1427
+ configureProviderMaxInFlightRequests(undefined);
1219
1428
  }
1220
1429
 
1221
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,