@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.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 (349) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +4685 -4614
  4. package/dist/types/async/job-manager.d.ts +37 -1
  5. package/dist/types/cli/args.d.ts +2 -0
  6. package/dist/types/cli/bench-cli.d.ts +41 -0
  7. package/dist/types/commands/bench.d.ts +15 -3
  8. package/dist/types/commands/launch.d.ts +4 -0
  9. package/dist/types/config/model-registry.d.ts +3 -1
  10. package/dist/types/config/models-config-schema.d.ts +18 -6
  11. package/dist/types/config/models-config.d.ts +14 -5
  12. package/dist/types/config/provider-globals.d.ts +2 -2
  13. package/dist/types/config/service-tier.d.ts +5 -1
  14. package/dist/types/config/settings-schema.d.ts +374 -47
  15. package/dist/types/config/settings.d.ts +7 -0
  16. package/dist/types/dap/client.d.ts +20 -0
  17. package/dist/types/exec/bash-executor.d.ts +38 -0
  18. package/dist/types/exec/direnv.d.ts +33 -0
  19. package/dist/types/export/html/args.d.ts +15 -0
  20. package/dist/types/export/html/index.d.ts +11 -26
  21. package/dist/types/export/html/web-palette.d.ts +55 -116
  22. package/dist/types/extensibility/extensions/loader.d.ts +3 -0
  23. package/dist/types/extensibility/extensions/types.d.ts +17 -2
  24. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
  25. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
  26. package/dist/types/live/attestation.d.ts +2 -0
  27. package/dist/types/live/controller.d.ts +48 -0
  28. package/dist/types/live/protocol.d.ts +85 -0
  29. package/dist/types/live/protocol.test.d.ts +1 -0
  30. package/dist/types/live/transport.d.ts +35 -0
  31. package/dist/types/live/visualizer.d.ts +30 -0
  32. package/dist/types/lsp/client.d.ts +16 -1
  33. package/dist/types/mcp/config-writer.test.d.ts +1 -0
  34. package/dist/types/mcp/oauth-discovery.d.ts +2 -0
  35. package/dist/types/mcp/smithery-auth.d.ts +1 -1
  36. package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
  37. package/dist/types/memory-backend/tool-names.d.ts +2 -0
  38. package/dist/types/modes/components/assistant-message.d.ts +1 -0
  39. package/dist/types/modes/components/custom-message.d.ts +1 -1
  40. package/dist/types/modes/components/login-dialog.d.ts +2 -2
  41. package/dist/types/modes/components/message-frame.d.ts +8 -4
  42. package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
  43. package/dist/types/modes/components/session-account-selector.d.ts +11 -0
  44. package/dist/types/modes/components/settings-defs.d.ts +7 -1
  45. package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
  46. package/dist/types/modes/components/tool-execution.d.ts +9 -1
  47. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  48. package/dist/types/modes/controllers/event-controller.d.ts +6 -1
  49. package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
  50. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  51. package/dist/types/modes/interactive-mode.d.ts +6 -0
  52. package/dist/types/modes/theme/theme.d.ts +1 -1
  53. package/dist/types/modes/types.d.ts +4 -1
  54. package/dist/types/modes/utils/context-usage.d.ts +12 -2
  55. package/dist/types/plan-mode/plan-files.d.ts +10 -0
  56. package/dist/types/registry/agent-lifecycle.d.ts +21 -9
  57. package/dist/types/registry/agent-registry.d.ts +11 -4
  58. package/dist/types/sdk.d.ts +15 -1
  59. package/dist/types/secrets/index.d.ts +15 -2
  60. package/dist/types/secrets/obfuscator.d.ts +113 -15
  61. package/dist/types/session/acp-permission-gate.d.ts +19 -0
  62. package/dist/types/session/agent-session-events.d.ts +84 -0
  63. package/dist/types/session/agent-session-types.d.ts +321 -0
  64. package/dist/types/session/agent-session.d.ts +135 -753
  65. package/dist/types/session/async-job-delivery.d.ts +28 -0
  66. package/dist/types/session/auth-storage.d.ts +1 -1
  67. package/dist/types/session/bash-runner.d.ts +75 -0
  68. package/dist/types/session/blob-store.d.ts +11 -1
  69. package/dist/types/session/blob-store.test.d.ts +1 -0
  70. package/dist/types/session/checkpoint-entries.d.ts +28 -0
  71. package/dist/types/session/eval-runner.d.ts +51 -0
  72. package/dist/types/session/irc-bridge.d.ts +46 -0
  73. package/dist/types/session/messages.d.ts +24 -0
  74. package/dist/types/session/model-controls.d.ts +180 -0
  75. package/dist/types/session/prewalk.d.ts +46 -0
  76. package/dist/types/session/queued-messages.d.ts +22 -0
  77. package/dist/types/session/redis-session-storage.d.ts +1 -0
  78. package/dist/types/session/retry-fallback-chains.d.ts +74 -0
  79. package/dist/types/session/role-models.d.ts +15 -0
  80. package/dist/types/session/session-advisors.d.ts +228 -0
  81. package/dist/types/session/session-entries.d.ts +8 -0
  82. package/dist/types/session/session-handoff.d.ts +70 -0
  83. package/dist/types/session/session-history-format.d.ts +6 -1
  84. package/dist/types/session/session-maintenance.d.ts +246 -0
  85. package/dist/types/session/session-manager.d.ts +41 -0
  86. package/dist/types/session/session-memory.d.ts +56 -0
  87. package/dist/types/session/session-provider-boundary.d.ts +62 -0
  88. package/dist/types/session/session-stats.d.ts +48 -0
  89. package/dist/types/session/session-tools.d.ts +158 -0
  90. package/dist/types/session/session-workspace.d.ts +32 -0
  91. package/dist/types/session/stream-guards.d.ts +44 -0
  92. package/dist/types/session/streaming-output.d.ts +8 -0
  93. package/dist/types/session/todo-tracker.d.ts +56 -0
  94. package/dist/types/session/ttsr-coordinator.d.ts +51 -0
  95. package/dist/types/session/turn-recovery.d.ts +225 -0
  96. package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
  97. package/dist/types/stt/index.d.ts +0 -2
  98. package/dist/types/stt/stt-controller.d.ts +7 -0
  99. package/dist/types/system-prompt.d.ts +2 -0
  100. package/dist/types/task/executor.d.ts +4 -1
  101. package/dist/types/task/output-manager.d.ts +3 -1
  102. package/dist/types/tiny/title-client.d.ts +10 -0
  103. package/dist/types/tools/approval.d.ts +1 -0
  104. package/dist/types/tools/browser/launch.d.ts +5 -0
  105. package/dist/types/tools/builtin-names.d.ts +1 -1
  106. package/dist/types/tools/computer/protocol.d.ts +43 -0
  107. package/dist/types/tools/computer/supervisor.d.ts +32 -0
  108. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  109. package/dist/types/tools/computer/worker.d.ts +15 -0
  110. package/dist/types/tools/computer-renderer.d.ts +22 -0
  111. package/dist/types/tools/computer.d.ts +71 -0
  112. package/dist/types/tools/context.d.ts +2 -0
  113. package/dist/types/tools/default-renderer.d.ts +21 -0
  114. package/dist/types/tools/image-gen.d.ts +4 -4
  115. package/dist/types/tools/image-providers.d.ts +38 -0
  116. package/dist/types/tools/index.d.ts +7 -0
  117. package/dist/types/tools/path-utils.d.ts +8 -0
  118. package/dist/types/tools/todo.d.ts +10 -4
  119. package/dist/types/tools/xdev.d.ts +5 -1
  120. package/dist/types/tts/streaming-player.d.ts +10 -29
  121. package/dist/types/tts/vocalizer.d.ts +5 -0
  122. package/dist/types/utils/jj.d.ts +29 -0
  123. package/dist/types/utils/lang-from-path.d.ts +1 -0
  124. package/dist/types/utils/title-generator.d.ts +30 -0
  125. package/dist/types/utils/tools-manager.d.ts +1 -2
  126. package/dist/types/vibe/runtime.d.ts +36 -8
  127. package/dist/types/web/search/index.d.ts +1 -1
  128. package/dist/types/web/search/provider.d.ts +15 -5
  129. package/dist/types/web/search/types.d.ts +94 -0
  130. package/package.json +12 -12
  131. package/scripts/generate-share-viewer.ts +4 -6
  132. package/src/advisor/__tests__/advisor.test.ts +643 -0
  133. package/src/advisor/runtime.ts +144 -43
  134. package/src/async/job-manager.ts +90 -2
  135. package/src/cli/args.ts +3 -0
  136. package/src/cli/auth-gateway-cli.ts +18 -3
  137. package/src/cli/bench-cli.ts +395 -29
  138. package/src/cli/flag-tables.ts +3 -0
  139. package/src/cli/gc-cli.ts +2 -2
  140. package/src/cli/setup-cli.ts +2 -14
  141. package/src/cli/usage-cli.ts +10 -1
  142. package/src/cli.ts +8 -0
  143. package/src/collab/host.ts +3 -2
  144. package/src/commands/bench.ts +18 -4
  145. package/src/commands/launch.ts +4 -0
  146. package/src/config/model-registry.ts +16 -2
  147. package/src/config/models-config-schema.ts +15 -4
  148. package/src/config/provider-globals.ts +9 -9
  149. package/src/config/service-tier.ts +26 -1
  150. package/src/config/settings-schema.ts +270 -51
  151. package/src/config/settings.ts +81 -2
  152. package/src/dap/client.ts +37 -4
  153. package/src/edit/index.ts +49 -12
  154. package/src/eval/executor-base.ts +1 -0
  155. package/src/eval/js/executor.ts +2 -0
  156. package/src/eval/py/executor.ts +103 -33
  157. package/src/exec/bash-executor.ts +90 -6
  158. package/src/exec/direnv.ts +145 -0
  159. package/src/export/html/args.ts +20 -0
  160. package/src/export/html/index.ts +45 -47
  161. package/src/export/html/template.css +19 -1
  162. package/src/export/html/template.html +6 -0
  163. package/src/export/html/template.js +21 -0
  164. package/src/export/html/tool-views.generated.js +31 -31
  165. package/src/export/html/web-palette.ts +116 -132
  166. package/src/export/share.ts +249 -49
  167. package/src/extensibility/extensions/loader.ts +29 -1
  168. package/src/extensibility/extensions/runner.ts +6 -0
  169. package/src/extensibility/extensions/types.ts +33 -2
  170. package/src/extensibility/extensions/wrapper.ts +68 -12
  171. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
  172. package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
  173. package/src/hindsight/state.ts +64 -6
  174. package/src/internal-urls/local-protocol.ts +2 -1
  175. package/src/internal-urls/memory-protocol.ts +2 -2
  176. package/src/internal-urls/skill-protocol.ts +2 -2
  177. package/src/internal-urls/ssh-protocol.ts +2 -1
  178. package/src/internal-urls/vault-protocol.ts +2 -1
  179. package/src/live/attestation.ts +91 -0
  180. package/src/live/controller.ts +517 -0
  181. package/src/live/prompts/agent-final-message.md +3 -0
  182. package/src/live/prompts/live-instructions.md +23 -0
  183. package/src/live/protocol.test.ts +140 -0
  184. package/src/live/protocol.ts +233 -0
  185. package/src/live/transport.ts +422 -0
  186. package/src/live/visualizer.ts +214 -0
  187. package/src/lsp/client.ts +29 -9
  188. package/src/lsp/index.ts +19 -4
  189. package/src/main.ts +8 -1
  190. package/src/mcp/config-writer.test.ts +43 -0
  191. package/src/mcp/config-writer.ts +109 -82
  192. package/src/mcp/oauth-discovery.ts +10 -2
  193. package/src/mcp/smithery-auth.test.ts +29 -0
  194. package/src/mcp/smithery-auth.ts +3 -2
  195. package/src/memories/index.ts +40 -20
  196. package/src/memory-backend/tool-names.ts +2 -0
  197. package/src/mnemopi/backend.ts +24 -7
  198. package/src/modes/acp/acp-agent.ts +56 -10
  199. package/src/modes/acp/acp-event-mapper.ts +8 -1
  200. package/src/modes/components/agent-hub.ts +1 -1
  201. package/src/modes/components/assistant-message.ts +7 -2
  202. package/src/modes/components/custom-message.ts +4 -1
  203. package/src/modes/components/login-dialog.ts +14 -4
  204. package/src/modes/components/message-frame.ts +14 -8
  205. package/src/modes/components/plan-review-overlay.ts +350 -35
  206. package/src/modes/components/session-account-selector.ts +62 -0
  207. package/src/modes/components/settings-defs.ts +23 -1
  208. package/src/modes/components/settings-selector.ts +169 -1
  209. package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
  210. package/src/modes/components/status-line/component.ts +123 -2
  211. package/src/modes/components/tool-execution.ts +28 -112
  212. package/src/modes/controllers/command-controller.ts +56 -15
  213. package/src/modes/controllers/event-controller.ts +111 -16
  214. package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
  215. package/src/modes/controllers/extension-ui-controller.ts +20 -4
  216. package/src/modes/controllers/input-controller.ts +47 -39
  217. package/src/modes/controllers/live-command-controller.ts +255 -0
  218. package/src/modes/controllers/mcp-command-controller.ts +10 -1
  219. package/src/modes/controllers/omfg-controller.ts +44 -40
  220. package/src/modes/controllers/selector-controller.ts +80 -10
  221. package/src/modes/controllers/session-focus-controller.ts +6 -1
  222. package/src/modes/interactive-mode.ts +176 -38
  223. package/src/modes/runtime-init.ts +2 -0
  224. package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
  225. package/src/modes/theme/theme.ts +1 -1
  226. package/src/modes/types.ts +4 -1
  227. package/src/modes/utils/context-usage.ts +23 -7
  228. package/src/modes/utils/ui-helpers.ts +10 -5
  229. package/src/plan-mode/plan-files.ts +40 -0
  230. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  231. package/src/prompts/bench/cache-prefix.md +3 -0
  232. package/src/prompts/bench/cache-suffix.md +1 -0
  233. package/src/prompts/system/computer-safety.md +14 -0
  234. package/src/prompts/system/custom-system-prompt.md +1 -1
  235. package/src/prompts/system/project-prompt.md +8 -1
  236. package/src/prompts/system/subagent-async-pending.md +6 -0
  237. package/src/prompts/system/system-prompt.md +1 -1
  238. package/src/prompts/system/workflow-notice.md +48 -40
  239. package/src/prompts/system/xdev-mount-notice.md +4 -0
  240. package/src/prompts/tools/computer.md +26 -0
  241. package/src/prompts/tools/todo.md +3 -1
  242. package/src/registry/agent-lifecycle.ts +116 -46
  243. package/src/registry/agent-registry.ts +35 -8
  244. package/src/registry/persisted-agents.ts +26 -2
  245. package/src/sdk.ts +215 -136
  246. package/src/secrets/index.ts +127 -4
  247. package/src/secrets/obfuscator.ts +2328 -105
  248. package/src/session/acp-permission-gate.ts +165 -0
  249. package/src/session/agent-session-error-log.test.ts +1 -1
  250. package/src/session/agent-session-events.ts +66 -0
  251. package/src/session/agent-session-types.ts +343 -0
  252. package/src/session/agent-session.ts +1940 -12218
  253. package/src/session/artifacts.ts +6 -5
  254. package/src/session/async-job-delivery.ts +74 -0
  255. package/src/session/auth-storage.ts +1 -0
  256. package/src/session/bash-runner.ts +326 -0
  257. package/src/session/blob-store.test.ts +56 -0
  258. package/src/session/blob-store.ts +18 -2
  259. package/src/session/checkpoint-entries.ts +81 -0
  260. package/src/session/eval-runner.ts +217 -0
  261. package/src/session/indexed-session-storage.ts +9 -2
  262. package/src/session/irc-bridge.ts +203 -0
  263. package/src/session/messages.ts +266 -1
  264. package/src/session/model-controls.ts +714 -0
  265. package/src/session/prewalk.ts +252 -0
  266. package/src/session/queued-messages.ts +93 -0
  267. package/src/session/redis-session-storage.ts +51 -11
  268. package/src/session/retry-fallback-chains.ts +455 -0
  269. package/src/session/role-models.ts +85 -0
  270. package/src/session/session-advisors.ts +1679 -0
  271. package/src/session/session-context.ts +6 -3
  272. package/src/session/session-entries.ts +8 -0
  273. package/src/session/session-handoff.ts +308 -0
  274. package/src/session/session-history-format.ts +20 -9
  275. package/src/session/session-loader.ts +0 -46
  276. package/src/session/session-maintenance.ts +2983 -0
  277. package/src/session/session-manager.ts +390 -30
  278. package/src/session/session-memory.ts +222 -0
  279. package/src/session/session-provider-boundary.ts +307 -0
  280. package/src/session/session-stats.ts +293 -0
  281. package/src/session/session-tools.ts +980 -0
  282. package/src/session/session-workspace.ts +53 -0
  283. package/src/session/stream-guards.ts +417 -0
  284. package/src/session/streaming-output.ts +52 -5
  285. package/src/session/todo-tracker.ts +380 -0
  286. package/src/session/ttsr-coordinator.ts +496 -0
  287. package/src/session/turn-recovery.ts +1629 -0
  288. package/src/slash-commands/builtin-registry.ts +257 -20
  289. package/src/slash-commands/helpers/session-pin.ts +44 -0
  290. package/src/slash-commands/helpers/usage-report.ts +22 -3
  291. package/src/stt/downloader.ts +0 -2
  292. package/src/stt/index.ts +0 -2
  293. package/src/stt/stt-controller.ts +57 -146
  294. package/src/system-prompt.ts +45 -17
  295. package/src/task/executor.ts +285 -70
  296. package/src/task/index.ts +98 -105
  297. package/src/task/output-manager.ts +25 -3
  298. package/src/task/persisted-revive.ts +4 -3
  299. package/src/task/structured-subagent.ts +1 -0
  300. package/src/tiny/title-client.ts +22 -0
  301. package/src/tools/approval.ts +55 -10
  302. package/src/tools/bash-interactive.ts +116 -86
  303. package/src/tools/bash-skill-urls.ts +28 -2
  304. package/src/tools/bash.ts +339 -123
  305. package/src/tools/browser/launch.ts +9 -1
  306. package/src/tools/browser/tab-supervisor.ts +8 -5
  307. package/src/tools/browser.ts +94 -47
  308. package/src/tools/builtin-names.ts +1 -0
  309. package/src/tools/computer/protocol.ts +28 -0
  310. package/src/tools/computer/supervisor.ts +258 -0
  311. package/src/tools/computer/worker-entry.ts +25 -0
  312. package/src/tools/computer/worker.ts +135 -0
  313. package/src/tools/computer-renderer.ts +108 -0
  314. package/src/tools/computer.ts +433 -0
  315. package/src/tools/context.ts +2 -0
  316. package/src/tools/default-renderer.ts +139 -0
  317. package/src/tools/essential-tools.ts +1 -0
  318. package/src/tools/image-gen.ts +20 -28
  319. package/src/tools/image-providers.ts +50 -0
  320. package/src/tools/index.ts +14 -0
  321. package/src/tools/path-utils.ts +49 -2
  322. package/src/tools/read.ts +156 -88
  323. package/src/tools/renderers.ts +9 -1
  324. package/src/tools/todo.ts +101 -11
  325. package/src/tools/vibe.ts +1 -2
  326. package/src/tools/write.ts +51 -1
  327. package/src/tools/xdev.ts +121 -29
  328. package/src/tts/streaming-player.ts +82 -283
  329. package/src/tts/vocalizer.ts +18 -1
  330. package/src/utils/clipboard.ts +1 -30
  331. package/src/utils/jj.ts +125 -4
  332. package/src/utils/lang-from-path.ts +7 -0
  333. package/src/utils/mac-file-urls.applescript +37 -0
  334. package/src/utils/title-generator.ts +137 -1
  335. package/src/utils/tool-choice.ts +14 -0
  336. package/src/utils/tools-manager.ts +1 -19
  337. package/src/vibe/runtime.ts +852 -72
  338. package/src/web/search/index.ts +3 -6
  339. package/src/web/search/provider.ts +31 -16
  340. package/src/web/search/providers/xai.ts +7 -1
  341. package/src/web/search/types.ts +3 -0
  342. package/dist/types/stt/recorder.d.ts +0 -30
  343. package/dist/types/stt/transcriber.d.ts +0 -14
  344. package/dist/types/stt/wav.d.ts +0 -29
  345. package/dist/types/tts/player.d.ts +0 -32
  346. package/src/stt/recorder.ts +0 -551
  347. package/src/stt/transcriber.ts +0 -60
  348. package/src/stt/wav.ts +0 -173
  349. package/src/tts/player.ts +0 -137
@@ -26,6 +26,7 @@ import {
26
26
  TINY_TITLE_MODEL_OPTIONS,
27
27
  TINY_TITLE_MODEL_VALUES,
28
28
  } from "../tiny/models";
29
+ import { IMAGE_PROVIDER_CHOICES, type ImageProvider } from "../tools/image-providers";
29
30
  import {
30
31
  DEFAULT_TTS_LOCAL_MODEL_KEY,
31
32
  DEFAULT_TTS_VOICE,
@@ -35,7 +36,7 @@ import {
35
36
  TTS_LOCAL_VOICE_VALUES,
36
37
  } from "../tts/models";
37
38
  import { EDIT_MODES } from "../utils/edit-mode";
38
- import { SEARCH_PROVIDER_OPTIONS, SEARCH_PROVIDER_PREFERENCES, type SearchProviderId } from "../web/search/types";
39
+ import { SEARCH_PROVIDER_CHOICES, type SearchProviderId } from "../web/search/types";
39
40
  import {
40
41
  SERVICE_TIER_ANTHROPIC_OPTIONS,
41
42
  SERVICE_TIER_ANTHROPIC_VALUES,
@@ -140,6 +141,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
140
141
  "Available Tools",
141
142
  "Todos",
142
143
  "Grep & Browser",
144
+ "Computer",
143
145
  "GitHub",
144
146
  "Output Limits",
145
147
  "Execution",
@@ -218,10 +220,18 @@ interface UiString extends UiBase {
218
220
  options?: ReadonlyArray<SubmenuOption> | "runtime";
219
221
  }
220
222
 
223
+ interface UiArray extends UiBase {
224
+ /** Membership choices. Without options, an array setting has no UI representation (config-file only). */
225
+ options?: ReadonlyArray<SubmenuOption>;
226
+ /** Selection order is meaningful; the editor renders positions and supports reordering. */
227
+ ordered?: boolean;
228
+ }
229
+
221
230
  /** Wide ui shape exposed to consumers that walk the schema generically. */
222
231
  export type AnyUiMetadata = UiBase & {
223
232
  options?: ReadonlyArray<SubmenuOption> | "runtime";
224
233
  secret?: boolean;
234
+ ordered?: boolean;
225
235
  };
226
236
 
227
237
  interface BooleanDef {
@@ -252,7 +262,7 @@ interface EnumDef<T extends readonly string[]> {
252
262
  interface ArrayDef<T> {
253
263
  type: "array";
254
264
  default: T[];
255
- ui?: UiBase;
265
+ ui?: UiArray;
256
266
  }
257
267
 
258
268
  interface RecordDef<T> {
@@ -904,6 +914,18 @@ export const SETTINGS_SCHEMA = {
904
914
  },
905
915
  },
906
916
 
917
+ "tui.titleState": {
918
+ type: "boolean",
919
+ default: true,
920
+ ui: {
921
+ tab: "appearance",
922
+ group: "Display",
923
+ label: "Terminal Title Run State",
924
+ description:
925
+ "Show the agent run state in the terminal title's separator — an animated spinner while working, '>' when it's your turn, '!' when the agent is waiting on you",
926
+ },
927
+ },
928
+
907
929
  "tui.hyperlinks": {
908
930
  type: "enum",
909
931
  values: ["off", "auto", "always"] as const,
@@ -1188,6 +1210,18 @@ export const SETTINGS_SCHEMA = {
1188
1210
  },
1189
1211
  },
1190
1212
 
1213
+ "workspace.additionalDirectories": {
1214
+ type: "array",
1215
+ default: [] as string[],
1216
+ ui: {
1217
+ tab: "context",
1218
+ group: "General",
1219
+ label: "Additional Workspace Dirs",
1220
+ description:
1221
+ "Extra workspace directories added to every session as additional roots (multi-root workspace). Managed live via /add-dir and /remove-dir. Paths resolve relative to cwd; absolute paths recommended. The agent is told these roots exist and can read/grep/glob them.",
1222
+ },
1223
+ },
1224
+
1191
1225
  personality: {
1192
1226
  type: "enum",
1193
1227
  values: ["default", "friendly", "pragmatic", "none"] as const,
@@ -1460,6 +1494,65 @@ export const SETTINGS_SCHEMA = {
1460
1494
  description: "Allow retry recovery to switch to configured fallback models",
1461
1495
  },
1462
1496
  },
1497
+ "retry.usageAwareFallback": {
1498
+ type: "boolean",
1499
+ default: false,
1500
+ ui: {
1501
+ tab: "model",
1502
+ group: "Retry & Fallback",
1503
+ label: "Usage-Aware Fallback",
1504
+ description:
1505
+ "Use reliable coding-plan quota reports to prefer same-provider accounts, then configured fallback models, before a hard usage limit. Ordinary configured API keys are excluded.",
1506
+ },
1507
+ },
1508
+ "retry.usageReservePct": {
1509
+ type: "number",
1510
+ default: 10,
1511
+ ui: {
1512
+ tab: "model",
1513
+ group: "Retry & Fallback",
1514
+ label: "Reserve Margin",
1515
+ description:
1516
+ "Treat a coding-plan model as near its limit below this remaining percentage. Unknown or unmapped usage keeps the primary model.",
1517
+ condition: "usageAwareFallbackEnabled",
1518
+ options: [
1519
+ { value: "5", label: "5%", description: "Act only when nearly exhausted" },
1520
+ { value: "10", label: "10%", description: "Balanced safety margin" },
1521
+ { value: "15", label: "15%", description: "Conservative" },
1522
+ { value: "20", label: "20%", description: "Early protection" },
1523
+ { value: "25", label: "25%", description: "Very conservative" },
1524
+ ],
1525
+ },
1526
+ },
1527
+ "retry.usageReservePolicy": {
1528
+ type: "enum",
1529
+ values: ["confirm", "auto", "fail-closed"] as const,
1530
+ default: "confirm",
1531
+ ui: {
1532
+ tab: "model",
1533
+ group: "Retry & Fallback",
1534
+ label: "Reserve Policy",
1535
+ description: "What to do when every same-provider coding-plan account is inside the reserve margin.",
1536
+ condition: "usageAwareFallbackEnabled",
1537
+ options: [
1538
+ {
1539
+ value: "confirm",
1540
+ label: "Confirm interactively",
1541
+ description: "Keep interactive sessions on the primary until confirmed; background agents auto-fallback",
1542
+ },
1543
+ {
1544
+ value: "auto",
1545
+ label: "Auto-fallback",
1546
+ description: "Always select the next eligible configured fallback",
1547
+ },
1548
+ {
1549
+ value: "fail-closed",
1550
+ label: "Fail closed",
1551
+ description: "Do not spend reserve quota or select a fallback",
1552
+ },
1553
+ ],
1554
+ },
1555
+ },
1463
1556
  "retry.fallbackChains": {
1464
1557
  type: "record",
1465
1558
  default: {} as Record<string, string[]>,
@@ -1773,6 +1866,18 @@ export const SETTINGS_SCHEMA = {
1773
1866
  },
1774
1867
  },
1775
1868
 
1869
+ "error.notify": {
1870
+ type: "enum",
1871
+ values: ["on", "off"] as const,
1872
+ default: "off",
1873
+ ui: {
1874
+ tab: "interaction",
1875
+ group: "Notifications",
1876
+ label: "Error Notification",
1877
+ description: "Notify when the agent stops with an error",
1878
+ },
1879
+ },
1880
+
1776
1881
  "ask.timeout": {
1777
1882
  type: "number",
1778
1883
  default: 0,
@@ -2448,9 +2553,8 @@ export const SETTINGS_SCHEMA = {
2448
2553
  "memories.summaryInjectionTokenLimit": { type: "number", default: 5000 },
2449
2554
 
2450
2555
  // Memory backend selector — picks between local memories pipeline,
2451
- // Mnemopi local SQLite, Hindsight remote memory, or off. Legacy
2452
- // `memories.enabled` keeps gating the local backend; see config/settings.ts
2453
- // migration for details.
2556
+ // Mnemopi local SQLite, Hindsight remote memory, or off. The legacy
2557
+ // `memories.enabled` flag is migration input only; see config/settings.ts.
2454
2558
  "memory.backend": {
2455
2559
  type: "enum",
2456
2560
  values: ["off", "local", "hindsight", "mnemopi"] as const,
@@ -3108,6 +3212,17 @@ export const SETTINGS_SCHEMA = {
3108
3212
  },
3109
3213
  },
3110
3214
 
3215
+ "read.renderMarkdown": {
3216
+ type: "boolean",
3217
+ default: false,
3218
+ ui: {
3219
+ tab: "files",
3220
+ group: "Reading",
3221
+ label: "Markdown Previews",
3222
+ description: "Render Markdown read results as formatted terminal Markdown previews instead of raw source",
3223
+ },
3224
+ },
3225
+
3111
3226
  "read.summarize.enabled": {
3112
3227
  type: "boolean",
3113
3228
  default: true,
@@ -3287,6 +3402,17 @@ export const SETTINGS_SCHEMA = {
3287
3402
  description: "Automatically background long-running bash commands and deliver the result later",
3288
3403
  },
3289
3404
  },
3405
+ "bash.patterns": {
3406
+ type: "array",
3407
+ default: [],
3408
+ ui: {
3409
+ tab: "shell",
3410
+ group: "Bash",
3411
+ label: "Bash Approval Patterns",
3412
+ description:
3413
+ "Ordered bash command approval rules. Each item has match and approval fields; only '*' wildcards are supported.",
3414
+ },
3415
+ },
3290
3416
 
3291
3417
  // Bash interceptor
3292
3418
  "bashInterceptor.enabled": {
@@ -3301,6 +3427,29 @@ export const SETTINGS_SCHEMA = {
3301
3427
  },
3302
3428
  "bashInterceptor.patterns": { type: "array", default: DEFAULT_BASH_INTERCEPTOR_RULES },
3303
3429
 
3430
+ "bash.direnv": {
3431
+ type: "enum",
3432
+ values: ["auto", "off"] as const,
3433
+ default: "auto",
3434
+ ui: {
3435
+ tab: "shell",
3436
+ group: "Bash",
3437
+ label: "direnv Auto-Load",
3438
+ description:
3439
+ "Auto-load a repo's direnv/devenv `.envrc` into the bash session so devenv tools and env vars are present without manual `direnv exec`. Honors direnv's allow list: an `.envrc` you haven't `direnv allow`ed is never executed",
3440
+ },
3441
+ },
3442
+ "bash.direnvLoadTimeoutMs": {
3443
+ type: "number",
3444
+ default: 30_000,
3445
+ ui: {
3446
+ tab: "shell",
3447
+ group: "Bash",
3448
+ label: "direnv Load Timeout (ms)",
3449
+ description:
3450
+ "Max wait for the first `direnv export` (a cold devenv shell can be slow); on timeout the session runs without the direnv env",
3451
+ },
3452
+ },
3304
3453
  // Shell output minimizer
3305
3454
  "shellMinimizer.enabled": {
3306
3455
  type: "boolean",
@@ -3682,6 +3831,66 @@ export const SETTINGS_SCHEMA = {
3682
3831
  },
3683
3832
  },
3684
3833
 
3834
+ "computer.enabled": {
3835
+ type: "boolean",
3836
+ default: false,
3837
+ ui: {
3838
+ tab: "tools",
3839
+ group: "Available Tools",
3840
+ label: "Computer",
3841
+ description: "Enable native host-desktop screenshots and input for OpenAI computer use",
3842
+ },
3843
+ },
3844
+
3845
+ "computer.backend": {
3846
+ type: "enum",
3847
+ values: ["auto", "native"] as const,
3848
+ default: "auto",
3849
+ ui: {
3850
+ tab: "tools",
3851
+ group: "Computer",
3852
+ label: "Computer Backend",
3853
+ description: "Select automatic or explicit platform-native desktop capture and input",
3854
+ options: [
3855
+ { value: "auto", label: "Auto" },
3856
+ { value: "native", label: "Native" },
3857
+ ],
3858
+ },
3859
+ },
3860
+
3861
+ "computer.display": {
3862
+ type: "string",
3863
+ default: "all",
3864
+ ui: {
3865
+ tab: "tools",
3866
+ group: "Computer",
3867
+ label: "Computer Display",
3868
+ description: "Composite all displays or select a native display id",
3869
+ },
3870
+ },
3871
+
3872
+ "computer.maxWidth": {
3873
+ type: "number",
3874
+ default: 1920,
3875
+ ui: {
3876
+ tab: "tools",
3877
+ group: "Computer",
3878
+ label: "Computer Screenshot Width",
3879
+ description: "Maximum composite screenshot width in pixels",
3880
+ },
3881
+ },
3882
+
3883
+ "computer.maxHeight": {
3884
+ type: "number",
3885
+ default: 1200,
3886
+ ui: {
3887
+ tab: "tools",
3888
+ group: "Computer",
3889
+ label: "Computer Screenshot Height",
3890
+ description: "Maximum composite screenshot height in pixels",
3891
+ },
3892
+ },
3893
+
3685
3894
  "checkpoint.enabled": {
3686
3895
  type: "boolean",
3687
3896
  default: false,
@@ -3947,6 +4156,40 @@ export const SETTINGS_SCHEMA = {
3947
4156
  },
3948
4157
  },
3949
4158
 
4159
+ "tools.xdevDocs": {
4160
+ type: "enum",
4161
+ values: ["inline", "builtins", "catalog"] as const,
4162
+ default: "builtins",
4163
+ ui: {
4164
+ tab: "tools",
4165
+ group: "Discovery & MCP",
4166
+ label: "xd:// Prompt Docs",
4167
+ description:
4168
+ "Choose which mounted-device docs and schemas are inlined in the system prompt. Built-ins keeps core tools inline while MCP and extension tools stay on-demand.",
4169
+ options: [
4170
+ { value: "inline", label: "All Devices", description: "Inline docs and schemas for every mounted device." },
4171
+ {
4172
+ value: "builtins",
4173
+ label: "Built-ins Only",
4174
+ description: "Inline built-in docs; fetch MCP and extension docs on demand.",
4175
+ },
4176
+ { value: "catalog", label: "Catalog Only", description: "List every device; fetch all docs on demand." },
4177
+ ],
4178
+ },
4179
+ },
4180
+
4181
+ "tools.xdevInlineDevices": {
4182
+ type: "array",
4183
+ default: EMPTY_STRING_ARRAY,
4184
+ ui: {
4185
+ tab: "tools",
4186
+ group: "Discovery & MCP",
4187
+ label: "xd:// Inline Devices",
4188
+ description:
4189
+ "When xd:// Prompt Docs is Built-ins Only, inline dynamic devices whose names match these glob patterns (for example mcp__context_mode_*). Catalog Only ignores this setting.",
4190
+ },
4191
+ },
4192
+
3950
4193
  // MCP
3951
4194
  "mcp.enableProjectConfig": {
3952
4195
  type: "boolean",
@@ -4195,7 +4438,7 @@ export const SETTINGS_SCHEMA = {
4195
4438
  group: "Subagents",
4196
4439
  label: "Batch Task Calls",
4197
4440
  description:
4198
- "Switch the task tool to its batch shape: one call carries { agent, context, tasks[] } — one subagent per item (with per-item isolation) and a required shared context prepended to every assignment. With async.enabled=true, each spawn runs as an independent background agent with the normal idle/parked lifecycle; otherwise the call blocks for merged results. Disable to restore the flat single-spawn schema.",
4441
+ "Switch the task tool to its batch shape: one call carries { context, tasks[] } — one subagent per item, with an optional per-item agent (defaulting to the session spawn-policy agent), per-item isolation, and a required shared context prepended to every assignment. With async.enabled=true, each spawn runs as an independent background agent with the normal idle/parked lifecycle; otherwise the call blocks for merged results. Disable to restore the flat single-spawn schema.",
4199
4442
  },
4200
4443
  },
4201
4444
 
@@ -4458,7 +4701,7 @@ export const SETTINGS_SCHEMA = {
4458
4701
  tab: "providers",
4459
4702
  group: "Privacy",
4460
4703
  label: "Hide Secrets",
4461
- description: "Obfuscate secrets before sending to AI providers",
4704
+ description: "Obfuscate configured secrets and redact credential-shaped tokens before sending to AI providers",
4462
4705
  },
4463
4706
  },
4464
4707
 
@@ -4474,16 +4717,17 @@ export const SETTINGS_SCHEMA = {
4474
4717
  "Maximum concurrent Ollama Cloud subagent runs per process; 0 disables the provider-specific limit",
4475
4718
  },
4476
4719
  },
4477
- "providers.webSearch": {
4478
- type: "enum",
4479
- values: SEARCH_PROVIDER_PREFERENCES,
4480
- default: "auto",
4720
+ "providers.webSearchOrder": {
4721
+ type: "array",
4722
+ default: [] as SearchProviderId[],
4481
4723
  ui: {
4482
4724
  tab: "providers",
4483
4725
  group: "Services",
4484
- label: "Web Search Provider",
4485
- description: "Preferred provider for the web_search tool",
4486
- options: SEARCH_PROVIDER_OPTIONS,
4726
+ label: "Web Search Provider Order",
4727
+ description:
4728
+ "Prioritized providers for the web_search tool; unlisted providers retain their default order afterward",
4729
+ options: SEARCH_PROVIDER_CHOICES,
4730
+ ordered: true,
4487
4731
  },
4488
4732
  },
4489
4733
  "providers.webSearchExclude": {
@@ -4494,6 +4738,7 @@ export const SETTINGS_SCHEMA = {
4494
4738
  group: "Services",
4495
4739
  label: "Excluded Web Search Providers",
4496
4740
  description: "Providers that web_search should never use, even as fallbacks",
4741
+ options: SEARCH_PROVIDER_CHOICES,
4497
4742
  },
4498
4743
  },
4499
4744
  "providers.webSearchGeminiModel": {
@@ -4534,46 +4779,17 @@ export const SETTINGS_SCHEMA = {
4534
4779
  ],
4535
4780
  },
4536
4781
  },
4537
- "providers.image": {
4538
- type: "enum",
4539
- values: ["auto", "openai", "openai-codex", "antigravity", "xai", "gemini", "openrouter"] as const,
4540
- default: "auto",
4782
+ "providers.imageOrder": {
4783
+ type: "array",
4784
+ default: [] as ImageProvider[],
4541
4785
  ui: {
4542
4786
  tab: "providers",
4543
4787
  group: "Services",
4544
- label: "Image Provider",
4545
- description: "Preferred provider for image generation",
4546
- options: [
4547
- {
4548
- value: "auto",
4549
- label: "Auto",
4550
- description:
4551
- "Priority: per-request provider > configured provider > active session provider > GPT model image tool > Codex subscription > Antigravity > xAI > OpenRouter > Gemini",
4552
- },
4553
- {
4554
- value: "openai",
4555
- label: "OpenAI",
4556
- description:
4557
- "OPENAI_API_KEY (gpt-image-2) or active GPT model; falls back to a connected Codex subscription",
4558
- },
4559
- {
4560
- value: "openai-codex",
4561
- label: "OpenAI Codex (ChatGPT)",
4562
- description: "Uses a connected Codex / ChatGPT subscription — no OPENAI_API_KEY needed",
4563
- },
4564
- {
4565
- value: "antigravity",
4566
- label: "Antigravity",
4567
- description: "Requires google-antigravity OAuth",
4568
- },
4569
- {
4570
- value: "xai",
4571
- label: "xAI Grok Imagine",
4572
- description: "Requires xAI Grok OAuth or XAI_API_KEY",
4573
- },
4574
- { value: "gemini", label: "Gemini", description: "Requires GEMINI_API_KEY" },
4575
- { value: "openrouter", label: "OpenRouter", description: "Requires OPENROUTER_API_KEY" },
4576
- ],
4788
+ label: "Image Provider Order",
4789
+ description:
4790
+ "Prioritized providers for image generation; unlisted providers follow the active session provider and the built-in order",
4791
+ options: IMAGE_PROVIDER_CHOICES,
4792
+ ordered: true,
4577
4793
  },
4578
4794
  },
4579
4795
  "providers.fireworksTier": {
@@ -5263,6 +5479,9 @@ export interface RetrySettings {
5263
5479
  baseDelayMs: number;
5264
5480
  maxDelayMs: number;
5265
5481
  modelFallback: boolean;
5482
+ usageAwareFallback: boolean;
5483
+ usageReservePct: number;
5484
+ usageReservePolicy: "confirm" | "auto" | "fail-closed";
5266
5485
  }
5267
5486
 
5268
5487
  export interface MemoriesSettings {
@@ -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 { configureCredentialRedaction } from "@oh-my-pi/pi-ai/providers/transform-messages";
17
18
  import { configureProviderMaxInFlightRequests } from "@oh-my-pi/pi-ai/stream";
18
19
  import {
19
20
  getAgentDbPath,
@@ -33,7 +34,9 @@ import type { ModelRole } from "../config/model-roles";
33
34
  import { loadCapability } from "../discovery";
34
35
  import { isLightTheme, setAutoThemeMapping, setColorBlindMode, setSymbolPreset } from "../modes/theme/theme";
35
36
  import { AgentStorage } from "../session/agent-storage";
37
+ import { AUTO_IMAGE_PROVIDER_ORDER, isImageProviderId } from "../tools/image-providers";
36
38
  import { type EditMode, normalizeEditMode } from "../utils/edit-mode";
39
+ import { isSearchProviderId, SEARCH_PROVIDER_ORDER } from "../web/search/types";
37
40
  import { withFileLock } from "./file-lock";
38
41
  import {
39
42
  type BashInterceptorRule,
@@ -361,6 +364,7 @@ export class Settings {
361
364
  if (options.configFiles) configFiles.push(...options.configFiles);
362
365
  this.#configFiles = configFiles.map(file => path.resolve(this.#cwd, expandTilde(file)));
363
366
  this.#persist = !options.inMemory && options.readOnly !== true;
367
+ liveSettingsInstances.add(new WeakRef(this));
364
368
 
365
369
  if (options.overrides) {
366
370
  for (const [key, value] of Object.entries(options.overrides)) {
@@ -534,6 +538,23 @@ export class Settings {
534
538
  }
535
539
  }
536
540
 
541
+ /** Set once this instance is discarded; background saves become no-ops. */
542
+ #savesCancelled = false;
543
+
544
+ /**
545
+ * Drop pending debounced saves and refuse any further background writes.
546
+ * Used when an instance is being discarded (test teardown): an armed timer
547
+ * or a chained in-flight save on a dropped instance would otherwise fire
548
+ * later and race the successor's file locks.
549
+ */
550
+ cancelPendingSaves(): void {
551
+ this.#savesCancelled = true;
552
+ clearTimeout(this.#saveTimer);
553
+ this.#saveTimer = undefined;
554
+ clearTimeout(this.#projectSaveTimer);
555
+ this.#projectSaveTimer = undefined;
556
+ }
557
+
537
558
  /**
538
559
  * Flush any pending saves to disk.
539
560
  * Call before exit to ensure all changes are persisted.
@@ -1595,6 +1616,45 @@ export class Settings {
1595
1616
  delete raw["mcp.discoveryMode"];
1596
1617
  delete raw["mcp.discoveryDefaultServers"];
1597
1618
 
1619
+ // providers.webSearch / providers.image (single preferred provider) →
1620
+ // providers.webSearchOrder / providers.imageOrder (priority lists). A
1621
+ // concrete legacy choice becomes the head of the new list with every
1622
+ // remaining provider appended in its built-in order, so the old
1623
+ // preference stays #1 and the fallback chain is written out explicitly.
1624
+ // "auto" (or an unknown id) just drops the key — the default chain.
1625
+ const providerPrefsObj = raw.providers as Record<string, unknown> | undefined;
1626
+ const migrateProviderPreference = (
1627
+ legacyKey: string,
1628
+ orderKey: string,
1629
+ expand: (value: string) => string[] | undefined,
1630
+ ): void => {
1631
+ const flatLegacyKey = `providers.${legacyKey}`;
1632
+ const legacy = providerPrefsObj?.[legacyKey] ?? raw[flatLegacyKey];
1633
+ if (legacy === undefined) return;
1634
+ const existingOrder = providerPrefsObj?.[orderKey] ?? raw[`providers.${orderKey}`];
1635
+ const orderAlreadySet = Array.isArray(existingOrder) && existingOrder.length > 0;
1636
+ if (!orderAlreadySet && typeof legacy === "string") {
1637
+ const expanded = expand(legacy);
1638
+ if (expanded) {
1639
+ const root = providerPrefsObj ?? {};
1640
+ root[orderKey] = expanded;
1641
+ raw.providers = root;
1642
+ }
1643
+ }
1644
+ if (providerPrefsObj) delete providerPrefsObj[legacyKey];
1645
+ delete raw[flatLegacyKey];
1646
+ };
1647
+ migrateProviderPreference("webSearch", "webSearchOrder", value =>
1648
+ value !== "auto" && isSearchProviderId(value)
1649
+ ? [value, ...SEARCH_PROVIDER_ORDER.filter(id => id !== value)]
1650
+ : undefined,
1651
+ );
1652
+ migrateProviderPreference("image", "imageOrder", value =>
1653
+ value !== "auto" && isImageProviderId(value)
1654
+ ? [value, ...AUTO_IMAGE_PROVIDER_ORDER.filter(id => id !== value)]
1655
+ : undefined,
1656
+ );
1657
+
1598
1658
  return raw;
1599
1659
  }
1600
1660
 
@@ -1646,7 +1706,7 @@ export class Settings {
1646
1706
  }
1647
1707
 
1648
1708
  async #saveNow(): Promise<void> {
1649
- if (!this.#persist || !this.#configPath) return;
1709
+ if (this.#savesCancelled || !this.#persist || !this.#configPath) return;
1650
1710
  if (this.#modified.size === 0 && this.#modifiedGlobalModelRoles.size === 0) return;
1651
1711
 
1652
1712
  const configPath = this.#configPath;
@@ -1750,7 +1810,7 @@ export class Settings {
1750
1810
  }
1751
1811
 
1752
1812
  async #saveProjectNow(): Promise<void> {
1753
- if (!this.#persist || this.#modifiedProjectModelRoles.size === 0) return;
1813
+ if (this.#savesCancelled || !this.#persist || this.#modifiedProjectModelRoles.size === 0) return;
1754
1814
 
1755
1815
  const projectConfigPath = path.join(this.#cwd, ".omp", "config.yml");
1756
1816
  const modifiedModelRoles = [...this.#modifiedProjectModelRoles];
@@ -1919,6 +1979,9 @@ const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook<any>>> = {
1919
1979
  "providers.maxInFlightRequests": value => {
1920
1980
  configureProviderMaxInFlightRequests(validateProviderMaxInFlightRequests(value));
1921
1981
  },
1982
+ "secrets.enabled": value => {
1983
+ configureCredentialRedaction(value === true);
1984
+ },
1922
1985
  "hindsight.bankId": () => hindsightScopeSignal.fire(),
1923
1986
  "hindsight.bankIdPrefix": () => hindsightScopeSignal.fire(),
1924
1987
  "hindsight.scoping": () => hindsightScopeSignal.fire(),
@@ -1978,6 +2041,13 @@ export const onHindsightScopeChanged = (cb: () => void) => hindsightScopeSignal.
1978
2041
  // Global Singleton
1979
2042
  // ═══════════════════════════════════════════════════════════════════════════
1980
2043
 
2044
+ /**
2045
+ * Weak registry of every constructed instance so `resetSettingsForTest` can
2046
+ * disarm stray background saves on isolated instances too. WeakRefs never
2047
+ * retain instances; the set is cleared on every test reset.
2048
+ */
2049
+ const liveSettingsInstances = new Set<WeakRef<Settings>>();
2050
+
1981
2051
  let globalInstance: Settings | null = null;
1982
2052
  let globalInstancePromise: Promise<Settings> | null = null;
1983
2053
  let boundSettingsInstance: Settings | null = null;
@@ -1997,10 +2067,19 @@ export function isSettingsInitialized(): boolean {
1997
2067
  * @internal
1998
2068
  */
1999
2069
  export function resetSettingsForTest(): void {
2070
+ // Disarm every constructed instance's debounced saves — including isolated
2071
+ // (non-singleton) instances: an armed timer or chained in-flight save on a
2072
+ // dropped instance fires mid-way through the NEXT test and races its file
2073
+ // locks/spies (cross-file pollution).
2074
+ for (const ref of liveSettingsInstances) {
2075
+ ref.deref()?.cancelPendingSaves();
2076
+ }
2077
+ liveSettingsInstances.clear();
2000
2078
  globalInstance = null;
2001
2079
  globalInstancePromise = null;
2002
2080
  clearBoundSettingsMethods();
2003
2081
  configureProviderMaxInFlightRequests(undefined);
2082
+ configureCredentialRedaction(false);
2004
2083
  }
2005
2084
 
2006
2085
  /**