@oh-my-pi/pi-coding-agent 15.12.4 → 15.13.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 (291) hide show
  1. package/CHANGELOG.md +304 -6
  2. package/dist/cli.js +1015 -881
  3. package/dist/types/async/job-manager.d.ts +15 -0
  4. package/dist/types/autolearn/controller.d.ts +25 -0
  5. package/dist/types/autolearn/managed-skills.d.ts +45 -0
  6. package/dist/types/autoresearch/state.d.ts +1 -1
  7. package/dist/types/autoresearch/types.d.ts +1 -1
  8. package/dist/types/cli/args.d.ts +19 -1
  9. package/dist/types/cli/session-picker.d.ts +1 -1
  10. package/dist/types/cli/setup-cli.d.ts +1 -1
  11. package/dist/types/cli/setup-model-picker.d.ts +14 -0
  12. package/dist/types/collab/protocol.d.ts +1 -1
  13. package/dist/types/commands/say.d.ts +24 -0
  14. package/dist/types/config/keybindings.d.ts +3 -3
  15. package/dist/types/config/model-registry.d.ts +10 -0
  16. package/dist/types/config/models-config-schema.d.ts +12 -0
  17. package/dist/types/config/models-config.d.ts +8 -2
  18. package/dist/types/config/settings-schema.d.ts +261 -58
  19. package/dist/types/export/html/index.d.ts +2 -1
  20. package/dist/types/extensibility/extensions/model-api.d.ts +17 -0
  21. package/dist/types/extensibility/extensions/runner.d.ts +3 -1
  22. package/dist/types/extensibility/extensions/types.d.ts +47 -1
  23. package/dist/types/extensibility/hooks/index.d.ts +2 -1
  24. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +9 -0
  25. package/dist/types/extensibility/plugins/loader.d.ts +11 -0
  26. package/dist/types/extensibility/shared-events.d.ts +1 -1
  27. package/dist/types/extensibility/skills.d.ts +10 -0
  28. package/dist/types/goals/guided-setup.d.ts +18 -0
  29. package/dist/types/goals/state.d.ts +1 -1
  30. package/dist/types/hindsight/transcript.d.ts +1 -1
  31. package/dist/types/index.d.ts +5 -0
  32. package/dist/types/internal-urls/local-protocol.d.ts +4 -2
  33. package/dist/types/main.d.ts +4 -3
  34. package/dist/types/mcp/startup-events.d.ts +11 -0
  35. package/dist/types/memories/index.d.ts +7 -0
  36. package/dist/types/memory-backend/local-backend.d.ts +4 -3
  37. package/dist/types/mnemopi/config.d.ts +4 -4
  38. package/dist/types/modes/components/agent-hub.d.ts +6 -0
  39. package/dist/types/modes/components/assistant-message.d.ts +1 -2
  40. package/dist/types/modes/components/compaction-summary-message.d.ts +15 -1
  41. package/dist/types/modes/components/custom-editor.d.ts +39 -1
  42. package/dist/types/modes/components/custom-editor.test.d.ts +1 -0
  43. package/dist/types/modes/components/session-selector.d.ts +1 -1
  44. package/dist/types/modes/components/tool-execution.d.ts +26 -16
  45. package/dist/types/modes/components/transcript-container.d.ts +23 -2
  46. package/dist/types/modes/components/tree-selector.d.ts +1 -1
  47. package/dist/types/modes/components/usage-row.d.ts +3 -0
  48. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  49. package/dist/types/modes/controllers/input-controller.d.ts +14 -0
  50. package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
  51. package/dist/types/modes/gradient-highlight.d.ts +9 -4
  52. package/dist/types/modes/image-references.d.ts +6 -0
  53. package/dist/types/modes/interactive-mode.d.ts +27 -3
  54. package/dist/types/modes/magic-keywords.d.ts +13 -1
  55. package/dist/types/modes/rpc/rpc-mode.d.ts +35 -1
  56. package/dist/types/modes/rpc/rpc-types.d.ts +9 -1
  57. package/dist/types/modes/runtime-init.d.ts +4 -0
  58. package/dist/types/modes/theme/theme.d.ts +13 -2
  59. package/dist/types/modes/types.d.ts +8 -2
  60. package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
  61. package/dist/types/registry/agent-registry.d.ts +17 -0
  62. package/dist/types/secrets/obfuscator.d.ts +1 -1
  63. package/dist/types/session/agent-session.d.ts +14 -2
  64. package/dist/types/session/indexed-session-storage.d.ts +3 -4
  65. package/dist/types/session/session-context.d.ts +39 -0
  66. package/dist/types/session/session-entries.d.ts +159 -0
  67. package/dist/types/session/session-listing.d.ts +69 -0
  68. package/dist/types/session/session-loader.d.ts +16 -0
  69. package/dist/types/session/session-manager.d.ts +82 -474
  70. package/dist/types/session/session-migrations.d.ts +12 -0
  71. package/dist/types/session/session-paths.d.ts +25 -0
  72. package/dist/types/session/session-persistence.d.ts +8 -0
  73. package/dist/types/session/session-storage.d.ts +11 -12
  74. package/dist/types/session/snapcompact-inline.d.ts +12 -1
  75. package/dist/types/session/snapcompact-savings-journal.d.ts +46 -0
  76. package/dist/types/session/tool-choice-queue.d.ts +6 -6
  77. package/dist/types/stt/asr-client.d.ts +90 -0
  78. package/dist/types/stt/asr-protocol.d.ts +97 -0
  79. package/dist/types/stt/asr-worker.d.ts +2 -0
  80. package/dist/types/stt/downloader.d.ts +38 -0
  81. package/dist/types/stt/endpointer.d.ts +59 -0
  82. package/dist/types/stt/index.d.ts +5 -1
  83. package/dist/types/stt/models.d.ts +120 -0
  84. package/dist/types/stt/recorder.d.ts +17 -0
  85. package/dist/types/stt/stt-controller.d.ts +6 -0
  86. package/dist/types/stt/transcriber.d.ts +5 -7
  87. package/dist/types/stt/wav.d.ts +29 -0
  88. package/dist/types/system-prompt.d.ts +4 -0
  89. package/dist/types/task/executor.d.ts +2 -0
  90. package/dist/types/task/index.d.ts +9 -1
  91. package/dist/types/task/types.d.ts +36 -0
  92. package/dist/types/tools/bash.d.ts +2 -2
  93. package/dist/types/tools/eval-render.d.ts +1 -1
  94. package/dist/types/tools/index.d.ts +11 -1
  95. package/dist/types/tools/irc.d.ts +1 -0
  96. package/dist/types/tools/learn.d.ts +51 -0
  97. package/dist/types/tools/manage-skill.d.ts +40 -0
  98. package/dist/types/tools/plan-mode-guard.d.ts +10 -0
  99. package/dist/types/tools/renderers.d.ts +7 -11
  100. package/dist/types/tools/ssh.d.ts +1 -1
  101. package/dist/types/tools/todo.d.ts +1 -1
  102. package/dist/types/tools/tts.d.ts +25 -0
  103. package/dist/types/tools/write.d.ts +1 -1
  104. package/dist/types/tts/downloader.d.ts +20 -0
  105. package/dist/types/tts/index.d.ts +8 -0
  106. package/dist/types/tts/models.d.ts +82 -0
  107. package/dist/types/tts/player.d.ts +32 -0
  108. package/dist/types/tts/runtime.d.ts +6 -0
  109. package/dist/types/tts/streaming-player.d.ts +41 -0
  110. package/dist/types/tts/tts-client.d.ts +93 -0
  111. package/dist/types/tts/tts-protocol.d.ts +95 -0
  112. package/dist/types/tts/tts-worker.d.ts +2 -0
  113. package/dist/types/tts/vocalizer.d.ts +41 -0
  114. package/dist/types/tts/wav.d.ts +8 -0
  115. package/dist/types/utils/tool-choice.d.ts +8 -0
  116. package/dist/types/utils/tools-manager.d.ts +2 -1
  117. package/dist/types/utils/tools-manager.test.d.ts +1 -0
  118. package/dist/types/web/scrapers/github.d.ts +1 -1
  119. package/package.json +15 -14
  120. package/src/async/job-manager.ts +49 -0
  121. package/src/autolearn/controller.ts +139 -0
  122. package/src/autolearn/managed-skills.ts +257 -0
  123. package/src/autoresearch/state.ts +1 -1
  124. package/src/autoresearch/types.ts +1 -1
  125. package/src/cli/args.ts +56 -2
  126. package/src/cli/session-picker.ts +2 -1
  127. package/src/cli/setup-cli.ts +148 -47
  128. package/src/cli/setup-model-picker.ts +43 -0
  129. package/src/cli-commands.ts +1 -0
  130. package/src/cli.ts +45 -13
  131. package/src/collab/host.ts +1 -1
  132. package/src/collab/protocol.ts +1 -1
  133. package/src/commands/say.ts +102 -0
  134. package/src/commands/setup.ts +1 -1
  135. package/src/commit/agentic/tools/analyze-file.ts +3 -0
  136. package/src/config/keybindings.ts +2 -2
  137. package/src/config/model-discovery.ts +11 -5
  138. package/src/config/model-registry.ts +64 -9
  139. package/src/config/models-config-schema.ts +4 -1
  140. package/src/config/models-config.ts +2 -1
  141. package/src/config/settings-schema.ts +248 -32
  142. package/src/config/settings.ts +10 -0
  143. package/src/discovery/builtin.ts +23 -1
  144. package/src/discovery/claude-plugins.ts +44 -5
  145. package/src/discovery/helpers.ts +41 -1
  146. package/src/eval/__tests__/budget-bridge.test.ts +1 -1
  147. package/src/eval/js/shared/prelude.txt +69 -17
  148. package/src/export/html/index.ts +3 -6
  149. package/src/extensibility/extensions/model-api.ts +41 -0
  150. package/src/extensibility/extensions/runner.ts +4 -0
  151. package/src/extensibility/extensions/types.ts +52 -1
  152. package/src/extensibility/extensions/wrapper.ts +41 -5
  153. package/src/extensibility/hooks/index.ts +2 -1
  154. package/src/extensibility/plugins/legacy-pi-compat.ts +43 -13
  155. package/src/extensibility/plugins/loader.ts +30 -19
  156. package/src/extensibility/plugins/manager.ts +221 -90
  157. package/src/extensibility/shared-events.ts +1 -1
  158. package/src/extensibility/skills.ts +96 -15
  159. package/src/goals/guided-setup.ts +133 -0
  160. package/src/goals/state.ts +1 -1
  161. package/src/hindsight/transcript.ts +1 -1
  162. package/src/index.ts +5 -0
  163. package/src/internal-urls/docs-index.generated.ts +10 -10
  164. package/src/internal-urls/history-protocol.ts +1 -1
  165. package/src/internal-urls/local-protocol.ts +29 -7
  166. package/src/main.ts +27 -7
  167. package/src/mcp/startup-events.ts +21 -0
  168. package/src/mcp/transports/stdio.ts +2 -1
  169. package/src/memories/index.ts +146 -11
  170. package/src/memory-backend/local-backend.ts +11 -5
  171. package/src/mnemopi/backend.ts +1 -0
  172. package/src/mnemopi/config.ts +26 -10
  173. package/src/modes/acp/acp-agent.ts +3 -5
  174. package/src/modes/components/agent-hub.ts +49 -4
  175. package/src/modes/components/assistant-message.ts +4 -37
  176. package/src/modes/components/compaction-summary-message.ts +125 -26
  177. package/src/modes/components/custom-editor.test.ts +96 -0
  178. package/src/modes/components/custom-editor.ts +164 -8
  179. package/src/modes/components/session-selector.ts +1 -1
  180. package/src/modes/components/settings-defs.ts +7 -0
  181. package/src/modes/components/tool-execution.ts +82 -43
  182. package/src/modes/components/transcript-container.ts +70 -1
  183. package/src/modes/components/tree-selector.ts +1 -1
  184. package/src/modes/components/usage-row.ts +18 -0
  185. package/src/modes/components/user-message.ts +4 -2
  186. package/src/modes/controllers/command-controller.ts +14 -4
  187. package/src/modes/controllers/event-controller.ts +78 -11
  188. package/src/modes/controllers/extension-ui-controller.ts +6 -0
  189. package/src/modes/controllers/input-controller.ts +258 -27
  190. package/src/modes/controllers/selector-controller.ts +12 -2
  191. package/src/modes/gradient-highlight.ts +21 -9
  192. package/src/modes/image-references.ts +20 -0
  193. package/src/modes/interactive-mode.ts +286 -40
  194. package/src/modes/magic-keywords.ts +27 -5
  195. package/src/modes/rpc/rpc-mode.ts +146 -14
  196. package/src/modes/rpc/rpc-subagents.ts +2 -2
  197. package/src/modes/rpc/rpc-types.ts +8 -2
  198. package/src/modes/runtime-init.ts +28 -3
  199. package/src/modes/theme/theme.ts +98 -50
  200. package/src/modes/types.ts +6 -2
  201. package/src/modes/utils/hotkeys-markdown.ts +1 -1
  202. package/src/modes/utils/ui-helpers.ts +34 -6
  203. package/src/priority.json +5 -1
  204. package/src/prompts/agents/task.md +1 -0
  205. package/src/prompts/goals/guided-goal-interview.md +8 -0
  206. package/src/prompts/goals/guided-goal-system.md +12 -0
  207. package/src/prompts/memories/read-path.md +6 -0
  208. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  209. package/src/prompts/system/autolearn-guidance.md +7 -0
  210. package/src/prompts/system/autolearn-nudge.md +3 -0
  211. package/src/prompts/system/eager-task.md +7 -0
  212. package/src/prompts/system/eager-todo.md +11 -6
  213. package/src/prompts/system/subagent-system-prompt.md +4 -0
  214. package/src/prompts/system/system-prompt.md +10 -5
  215. package/src/prompts/system/title-marker-instruction.md +1 -0
  216. package/src/prompts/system/title-system-marker.md +16 -0
  217. package/src/prompts/tools/job.md +1 -0
  218. package/src/prompts/tools/learn.md +7 -0
  219. package/src/prompts/tools/manage-skill.md +9 -0
  220. package/src/prompts/tools/task.md +3 -0
  221. package/src/registry/agent-registry.ts +30 -0
  222. package/src/sdk.ts +88 -24
  223. package/src/secrets/obfuscator.ts +1 -1
  224. package/src/session/agent-session.ts +209 -87
  225. package/src/session/history-storage.ts +2 -2
  226. package/src/session/indexed-session-storage.ts +7 -17
  227. package/src/session/session-context.ts +352 -0
  228. package/src/session/session-entries.ts +194 -0
  229. package/src/session/session-listing.ts +588 -0
  230. package/src/session/session-loader.ts +106 -0
  231. package/src/session/session-manager.ts +933 -3145
  232. package/src/session/session-migrations.ts +78 -0
  233. package/src/session/session-paths.ts +193 -0
  234. package/src/session/session-persistence.ts +131 -0
  235. package/src/session/session-storage.ts +91 -50
  236. package/src/session/snapcompact-inline.ts +21 -1
  237. package/src/session/snapcompact-savings-journal.ts +113 -0
  238. package/src/session/tool-choice-queue.ts +23 -11
  239. package/src/slash-commands/builtin-registry.ts +25 -3
  240. package/src/stt/asr-client.ts +520 -0
  241. package/src/stt/asr-protocol.ts +65 -0
  242. package/src/stt/asr-worker.ts +790 -0
  243. package/src/stt/downloader.ts +107 -47
  244. package/src/stt/endpointer.ts +259 -0
  245. package/src/stt/index.ts +5 -1
  246. package/src/stt/models.ts +150 -0
  247. package/src/stt/recorder.ts +247 -60
  248. package/src/stt/stt-controller.ts +201 -22
  249. package/src/stt/transcriber.ts +37 -68
  250. package/src/stt/wav.ts +173 -0
  251. package/src/system-prompt.ts +8 -0
  252. package/src/task/agents.ts +1 -2
  253. package/src/task/executor.ts +49 -15
  254. package/src/task/index.ts +60 -6
  255. package/src/task/render.ts +83 -8
  256. package/src/task/types.ts +53 -0
  257. package/src/tools/ask.ts +8 -0
  258. package/src/tools/bash.ts +4 -3
  259. package/src/tools/eval-render.ts +4 -3
  260. package/src/tools/index.ts +40 -4
  261. package/src/tools/irc.ts +10 -2
  262. package/src/tools/job.ts +14 -2
  263. package/src/tools/learn.ts +144 -0
  264. package/src/tools/manage-skill.ts +104 -0
  265. package/src/tools/plan-mode-guard.ts +53 -19
  266. package/src/tools/renderers.ts +7 -11
  267. package/src/tools/ssh.ts +4 -3
  268. package/src/tools/todo.ts +1 -1
  269. package/src/tools/tts.ts +203 -92
  270. package/src/tools/write.ts +18 -2
  271. package/src/tts/downloader.ts +64 -0
  272. package/src/tts/index.ts +8 -0
  273. package/src/tts/models.ts +137 -0
  274. package/src/tts/player.ts +137 -0
  275. package/src/tts/runtime.ts +21 -0
  276. package/src/tts/streaming-player.ts +266 -0
  277. package/src/tts/tts-client.ts +647 -0
  278. package/src/tts/tts-protocol.ts +60 -0
  279. package/src/tts/tts-worker.ts +497 -0
  280. package/src/tts/vocalizer.ts +162 -0
  281. package/src/tts/wav.ts +58 -0
  282. package/src/utils/title-generator.ts +48 -5
  283. package/src/utils/tool-choice.ts +16 -0
  284. package/src/utils/tools-manager.test.ts +25 -0
  285. package/src/utils/tools-manager.ts +19 -1
  286. package/src/web/scrapers/github.ts +96 -0
  287. package/src/web/search/index.ts +13 -0
  288. package/src/web/search/providers/searxng.ts +13 -1
  289. package/dist/types/stt/setup.d.ts +0 -18
  290. package/src/stt/setup.ts +0 -52
  291. package/src/stt/transcribe.py +0 -70
@@ -2,6 +2,7 @@ import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
2
2
  import { DEFAULT_SHARE_URL } from "@oh-my-pi/pi-wire";
3
3
  import { SHAPE_VARIANT_NAMES } from "@oh-my-pi/snapcompact";
4
4
  import { DEFAULT_RELAY_URL } from "../collab/protocol";
5
+ import { DEFAULT_STT_MODEL_KEY, STT_MODEL_OPTIONS, STT_MODEL_VALUES } from "../stt/models";
5
6
  import { AUTO_THINKING, getConfiguredThinkingLevelMetadata, getThinkingLevelMetadata } from "../thinking";
6
7
  import {
7
8
  TINY_MODEL_DEVICE_DEFAULT,
@@ -24,6 +25,14 @@ import {
24
25
  TINY_TITLE_MODEL_OPTIONS,
25
26
  TINY_TITLE_MODEL_VALUES,
26
27
  } from "../tiny/models";
28
+ import {
29
+ DEFAULT_TTS_LOCAL_MODEL_KEY,
30
+ DEFAULT_TTS_VOICE,
31
+ TTS_LOCAL_MODEL_OPTIONS,
32
+ TTS_LOCAL_MODEL_VALUES,
33
+ TTS_LOCAL_VOICE_OPTIONS,
34
+ TTS_LOCAL_VOICE_VALUES,
35
+ } from "../tts/models";
27
36
  import { EDIT_MODES } from "../utils/edit-mode";
28
37
  import { SEARCH_PROVIDER_OPTIONS, SEARCH_PROVIDER_PREFERENCES } from "../web/search/types";
29
38
 
@@ -109,7 +118,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
109
118
  "Power (macOS)",
110
119
  ],
111
120
  context: ["General", "Compaction", "Rules (TTSR)", "Experimental"],
112
- memory: ["General", "Mnemopi", "Hindsight"],
121
+ memory: ["General", "Auto-Learn", "Mnemopi", "Hindsight"],
113
122
  files: ["Editing", "Reading", "Read Summaries", "LSP"],
114
123
  shell: ["Bash", "Eval & Python"],
115
124
  tools: [
@@ -998,6 +1007,32 @@ export const SETTINGS_SCHEMA = {
998
1007
  },
999
1008
  },
1000
1009
 
1010
+ fastModeScope: {
1011
+ type: "enum",
1012
+ values: ["both", "openai", "claude"] as const,
1013
+ default: "both",
1014
+ ui: {
1015
+ tab: "model",
1016
+ group: "Sampling",
1017
+ label: "Fast Mode Scope",
1018
+ description:
1019
+ 'Which providers `/fast on` (and the fast-mode toggle) target. "both" = priority on every supported provider; "openai"/"claude" scope it to one family (mirrors serviceTier openai-only/claude-only).',
1020
+ options: [
1021
+ { value: "both", label: "Both", description: "Priority on every supported provider" },
1022
+ {
1023
+ value: "openai",
1024
+ label: "OpenAI only",
1025
+ description: "Priority on OpenAI/OpenAI-Codex requests; ignored elsewhere",
1026
+ },
1027
+ {
1028
+ value: "claude",
1029
+ label: "Claude only",
1030
+ description: "Anthropic fast mode on direct Claude requests; ignored elsewhere",
1031
+ },
1032
+ ],
1033
+ },
1034
+ },
1035
+
1001
1036
  // Retries
1002
1037
  "retry.enabled": { type: "boolean", default: true },
1003
1038
 
@@ -1183,6 +1218,25 @@ export const SETTINGS_SCHEMA = {
1183
1218
  },
1184
1219
  },
1185
1220
 
1221
+ "paste.largeMenuThreshold": {
1222
+ type: "number",
1223
+ default: 100,
1224
+ ui: {
1225
+ tab: "interaction",
1226
+ group: "Input",
1227
+ label: "Large Paste Menu",
1228
+ description:
1229
+ "When a paste reaches this many lines, offer a menu to wrap it in a code block, wrap it in XML tags, or save it to a file. 0 disables the menu (large pastes still collapse to a [Paste] marker).",
1230
+ options: [
1231
+ { value: "0", label: "Off" },
1232
+ { value: "100", label: "100 lines" },
1233
+ { value: "250", label: "250 lines" },
1234
+ { value: "500", label: "500 lines" },
1235
+ { value: "1000", label: "1000 lines" },
1236
+ ],
1237
+ },
1238
+ },
1239
+
1186
1240
  "startup.quiet": {
1187
1241
  type: "boolean",
1188
1242
  default: false,
@@ -1396,24 +1450,15 @@ export const SETTINGS_SCHEMA = {
1396
1450
 
1397
1451
  "stt.modelName": {
1398
1452
  type: "enum",
1399
- values: ["tiny", "tiny.en", "base", "base.en", "small", "small.en", "medium", "medium.en", "large"] as const,
1400
- default: "base.en",
1453
+ values: STT_MODEL_VALUES,
1454
+ default: DEFAULT_STT_MODEL_KEY,
1401
1455
  ui: {
1402
1456
  tab: "interaction",
1403
1457
  group: "Speech",
1404
1458
  label: "Speech Model",
1405
- description: "Whisper model size (larger = more accurate but slower)",
1406
- options: [
1407
- { value: "tiny", label: "tiny", description: "Multilingual; fastest, lowest accuracy" },
1408
- { value: "tiny.en", label: "tiny.en", description: "English-only; fastest" },
1409
- { value: "base", label: "base", description: "Multilingual; small and fast" },
1410
- { value: "base.en", label: "base.en", description: "English-only; default" },
1411
- { value: "small", label: "small", description: "Multilingual; balanced" },
1412
- { value: "small.en", label: "small.en", description: "English-only; balanced" },
1413
- { value: "medium", label: "medium", description: "Multilingual; accurate but slower" },
1414
- { value: "medium.en", label: "medium.en", description: "English-only; accurate but slower" },
1415
- { value: "large", label: "large", description: "Multilingual; most accurate" },
1416
- ],
1459
+ description:
1460
+ "Local on-device speech model. Parakeet TDT v3 (sherpa-onnx) is the SoTA default; Whisper base/small/large-v3-turbo tiers (transformers.js) trade size for multilingual coverage. Downloaded on first use.",
1461
+ options: STT_MODEL_OPTIONS,
1417
1462
  },
1418
1463
  },
1419
1464
 
@@ -1744,6 +1789,18 @@ export const SETTINGS_SCHEMA = {
1744
1789
  label: "8x13 on 16px pitch, black",
1745
1790
  description: "8x13 glyphs on an 8x16 cell (extra leading), black ink.",
1746
1791
  },
1792
+ {
1793
+ value: "8on22-bw",
1794
+ label: "8x13 on 22px pitch (leading), black",
1795
+ description:
1796
+ "8x13 glyphs on an 8x22 cell — extra line spacing so rows don't crowd. Default for OpenAI/Google.",
1797
+ },
1798
+ {
1799
+ value: "11on16-bw",
1800
+ label: "8x13 on 11px advance (tracking), black",
1801
+ description:
1802
+ "8x13 glyphs on an 11x16 cell — extra letter spacing so characters don't merge. Default for Anthropic.",
1803
+ },
1747
1804
  {
1748
1805
  value: "doc-8on16-bw",
1749
1806
  label: "Doc 8on16, black",
@@ -1841,6 +1898,35 @@ export const SETTINGS_SCHEMA = {
1841
1898
  },
1842
1899
  },
1843
1900
 
1901
+ // Auto-Learn (experimental): post-stop nudge to capture lessons to memory
1902
+ // and mint/enhance isolated managed skills under ~/.omp/agent/managed-skills.
1903
+ // Master flag is default-off → zero footprint; sub-flags gate behaviour.
1904
+ "autolearn.enabled": {
1905
+ type: "boolean",
1906
+ default: false,
1907
+ ui: {
1908
+ tab: "memory",
1909
+ group: "Auto-Learn",
1910
+ label: "Auto-Learn (experimental)",
1911
+ description:
1912
+ "After the agent stops, nudge it to capture lessons to memory and create/enhance isolated managed skills",
1913
+ },
1914
+ },
1915
+ "autolearn.autoContinue": {
1916
+ type: "boolean",
1917
+ default: false,
1918
+ ui: {
1919
+ tab: "memory",
1920
+ group: "Auto-Learn",
1921
+ label: "Auto-run capture at stop",
1922
+ description:
1923
+ "When on, auto-run one capture turn at stop (uses extra tokens). Off = passive reminder on your next turn.",
1924
+ condition: "autolearnActive",
1925
+ },
1926
+ },
1927
+ // Config-file-only knob (numbers without `options` are hidden from the UI).
1928
+ "autolearn.minToolCalls": { type: "number", default: 5 },
1929
+
1844
1930
  // Mnemopi local SQLite memory backend.
1845
1931
  "mnemopi.dbPath": {
1846
1932
  type: "string",
@@ -1894,6 +1980,31 @@ export const SETTINGS_SCHEMA = {
1894
1980
  condition: "mnemopiActive",
1895
1981
  },
1896
1982
  },
1983
+ "mnemopi.embeddingVariant": {
1984
+ type: "enum",
1985
+ values: ["en", "multilingual"] as const,
1986
+ default: "en",
1987
+ ui: {
1988
+ tab: "memory",
1989
+ group: "Mnemopi",
1990
+ label: "Embedding variant",
1991
+ description:
1992
+ "Local embedding model family. en = stronger English model; multilingual = cross-language model. Changing this rebuilds existing memory embeddings on next start.",
1993
+ options: [
1994
+ {
1995
+ value: "en",
1996
+ label: "English (bge-base-en-v1.5)",
1997
+ description: "BAAI/bge-base-en-v1.5 (768d), English-only",
1998
+ },
1999
+ {
2000
+ value: "multilingual",
2001
+ label: "Multilingual (multilingual-e5-large)",
2002
+ description: "intfloat/multilingual-e5-large (1024d), cross-language recall",
2003
+ },
2004
+ ],
2005
+ condition: "mnemopiActive",
2006
+ },
2007
+ },
1897
2008
  "mnemopi.autoRecall": {
1898
2009
  type: "boolean",
1899
2010
  default: true,
@@ -1956,7 +2067,8 @@ export const SETTINGS_SCHEMA = {
1956
2067
  tab: "memory",
1957
2068
  group: "Mnemopi",
1958
2069
  label: "Mnemopi Embedding Model",
1959
- description: "Optional embedding model override passed to Mnemopi",
2070
+ description:
2071
+ "Advanced: explicit embedding model id that overrides the variant. Leave empty to use mnemopi.embeddingVariant.",
1960
2072
  condition: "mnemopiActive",
1961
2073
  },
1962
2074
  },
@@ -2772,13 +2884,23 @@ export const SETTINGS_SCHEMA = {
2772
2884
  },
2773
2885
 
2774
2886
  "todo.eager": {
2775
- type: "boolean",
2776
- default: false,
2887
+ type: "enum",
2888
+ values: ["default", "preferred", "always"] as const,
2889
+ default: "default",
2777
2890
  ui: {
2778
2891
  tab: "tools",
2779
2892
  group: "Todos",
2780
2893
  label: "Create Todos Automatically",
2781
- description: "Automatically create a comprehensive todo list after the first message",
2894
+ description: "How strongly to push automatic todo-list creation after the first message",
2895
+ options: [
2896
+ { value: "default", label: "Default", description: "Model decides; no automatic todo list" },
2897
+ {
2898
+ value: "preferred",
2899
+ label: "Preferred",
2900
+ description: "Suggests a todo list on the first message (reminder, not forced)",
2901
+ },
2902
+ { value: "always", label: "Always", description: "Forces a comprehensive todo list on the first message" },
2903
+ ],
2782
2904
  },
2783
2905
  },
2784
2906
 
@@ -2888,14 +3010,14 @@ export const SETTINGS_SCHEMA = {
2888
3010
  },
2889
3011
  },
2890
3012
 
2891
- "tts.enabled": {
3013
+ "speechgen.enabled": {
2892
3014
  type: "boolean",
2893
3015
  default: false,
2894
3016
  ui: {
2895
3017
  tab: "tools",
2896
3018
  group: "Available Tools",
2897
- label: "Text-to-Speech",
2898
- description: "Enable the tts tool for xAI Grok Voice speech synthesis",
3019
+ label: "Speech Generation",
3020
+ description: "Enable the tts tool for on-device (Kokoro) or xAI Grok Voice speech-file synthesis",
2899
3021
  },
2900
3022
  },
2901
3023
 
@@ -3098,19 +3220,21 @@ export const SETTINGS_SCHEMA = {
3098
3220
 
3099
3221
  "async.pollWaitDuration": {
3100
3222
  type: "enum",
3101
- values: ["5s", "10s", "30s", "1m", "5m"] as const,
3102
- default: "30s",
3223
+ values: ["5s", "10s", "30s", "1m", "5m", "smart"] as const,
3224
+ default: "smart",
3103
3225
  ui: {
3104
3226
  tab: "tools",
3105
3227
  group: "Execution",
3106
- label: "Poll Wait Duration",
3107
- description: "How long the poll tool waits for background job updates before returning the current state",
3228
+ label: "Max Poll Time",
3229
+ description:
3230
+ "How long the poll tool waits for background job updates before returning the current state. A fixed value waits that exact duration every time. `smart` adapts: it starts at 5s and lengthens with each back-to-back poll (up to 5m), then resets to 5s after about a minute without polling.",
3108
3231
  options: [
3109
3232
  { value: "5s", label: "5 seconds" },
3110
3233
  { value: "10s", label: "10 seconds" },
3111
- { value: "30s", label: "30 seconds", description: "Default" },
3234
+ { value: "30s", label: "30 seconds" },
3112
3235
  { value: "1m", label: "1 minute" },
3113
3236
  { value: "5m", label: "5 minutes" },
3237
+ { value: "smart", label: "Smart", description: "Default — adaptive 5s→5m, resets when you stop polling" },
3114
3238
  ],
3115
3239
  },
3116
3240
  },
@@ -3352,13 +3476,19 @@ export const SETTINGS_SCHEMA = {
3352
3476
  },
3353
3477
 
3354
3478
  "task.eager": {
3355
- type: "boolean",
3356
- default: false,
3479
+ type: "enum",
3480
+ values: ["default", "preferred", "always"] as const,
3481
+ default: "default",
3357
3482
  ui: {
3358
3483
  tab: "tasks",
3359
3484
  group: "Subagents",
3360
3485
  label: "Prefer Task Delegation",
3361
- description: "Encourage the agent to delegate work to subagents unless changes are trivial",
3486
+ description: "How strongly to push delegating work to subagents",
3487
+ options: [
3488
+ { value: "default", label: "Default", description: "Model decides when to delegate" },
3489
+ { value: "preferred", label: "Preferred", description: "Adds delegation guidance to the system prompt" },
3490
+ { value: "always", label: "Always", description: "Prompt guidance plus a first-turn delegation reminder" },
3491
+ ],
3362
3492
  },
3363
3493
  },
3364
3494
 
@@ -3654,6 +3784,93 @@ export const SETTINGS_SCHEMA = {
3654
3784
  ],
3655
3785
  },
3656
3786
  },
3787
+ "providers.tts": {
3788
+ type: "enum",
3789
+ values: ["auto", "local", "xai"] as const,
3790
+ default: "auto",
3791
+ ui: {
3792
+ tab: "providers",
3793
+ group: "Services",
3794
+ label: "Text-to-Speech Provider",
3795
+ description: "Backend for the tts tool: local on-device neural TTS (Kokoro-82M) or xAI Grok Voice",
3796
+ options: [
3797
+ {
3798
+ value: "auto",
3799
+ label: "Auto",
3800
+ description: "Prefer local on-device TTS; route .mp3 output to xAI when credentials exist",
3801
+ },
3802
+ { value: "local", label: "Local", description: "On-device neural TTS (Kokoro-82M); output is WAV/PCM16" },
3803
+ {
3804
+ value: "xai",
3805
+ label: "xAI Grok Voice",
3806
+ description: "Requires xAI Grok OAuth or XAI_API_KEY; MP3 or WAV",
3807
+ },
3808
+ ],
3809
+ },
3810
+ },
3811
+ "tts.localModel": {
3812
+ type: "enum",
3813
+ values: TTS_LOCAL_MODEL_VALUES,
3814
+ default: DEFAULT_TTS_LOCAL_MODEL_KEY,
3815
+ ui: {
3816
+ tab: "providers",
3817
+ group: "Services",
3818
+ label: "Local TTS Model",
3819
+ description: "On-device neural TTS model (Kokoro-82M) used by the local TTS backend",
3820
+ options: TTS_LOCAL_MODEL_OPTIONS,
3821
+ },
3822
+ },
3823
+ "tts.localVoice": {
3824
+ type: "enum",
3825
+ values: TTS_LOCAL_VOICE_VALUES,
3826
+ default: DEFAULT_TTS_VOICE,
3827
+ ui: {
3828
+ tab: "providers",
3829
+ group: "Services",
3830
+ label: "Local TTS Voice",
3831
+ description: "Kokoro voice used by the local TTS backend (American/British, female/male)",
3832
+ options: TTS_LOCAL_VOICE_OPTIONS,
3833
+ },
3834
+ },
3835
+ "speech.enabled": {
3836
+ type: "boolean",
3837
+ default: false,
3838
+ ui: {
3839
+ tab: "providers",
3840
+ group: "Services",
3841
+ label: "Speech Vocalization",
3842
+ description: "Speak the assistant's output aloud through the speakers as it streams",
3843
+ },
3844
+ },
3845
+ "speech.mode": {
3846
+ type: "enum",
3847
+ values: ["all", "assistant", "yield"] as const,
3848
+ default: "assistant",
3849
+ ui: {
3850
+ tab: "providers",
3851
+ group: "Services",
3852
+ label: "Speech Vocalization Mode",
3853
+ description:
3854
+ "What to speak: all = assistant messages + thinking; assistant = messages only; yield = only the final message at turn end",
3855
+ options: [
3856
+ { value: "all", label: "All (messages + thinking)" },
3857
+ { value: "assistant", label: "Assistant messages" },
3858
+ { value: "yield", label: "Final message only" },
3859
+ ],
3860
+ },
3861
+ },
3862
+ "speech.voice": {
3863
+ type: "enum",
3864
+ values: TTS_LOCAL_VOICE_VALUES,
3865
+ default: DEFAULT_TTS_VOICE,
3866
+ ui: {
3867
+ tab: "providers",
3868
+ group: "Services",
3869
+ label: "Speech Vocalization Voice",
3870
+ description: "Kokoro voice used when speaking the assistant's output aloud",
3871
+ options: TTS_LOCAL_VOICE_OPTIONS,
3872
+ },
3873
+ },
3657
3874
  "providers.tinyModel": {
3658
3875
  type: "enum",
3659
3876
  values: TINY_TITLE_MODEL_VALUES,
@@ -4211,8 +4428,7 @@ export interface SttSettings {
4211
4428
  enabled: boolean;
4212
4429
  language: string | undefined;
4213
4430
  modelName: string;
4214
- whisperPath: string | undefined;
4215
- modelPath: string | undefined;
4431
+ streaming: boolean;
4216
4432
  }
4217
4433
 
4218
4434
  export interface BashInterceptorRule {
@@ -752,6 +752,16 @@ export class Settings {
752
752
  delete taskObj.simple;
753
753
  }
754
754
 
755
+ // task.eager / todo.eager: boolean -> enum (default | preferred | always).
756
+ // `true` reproduced the previous "on" behavior, which is now `always`.
757
+ if (taskObj && typeof taskObj.eager === "boolean") {
758
+ taskObj.eager = taskObj.eager ? "always" : "default";
759
+ }
760
+ const todoObj = raw.todo as Record<string, unknown> | undefined;
761
+ if (todoObj && typeof todoObj.eager === "boolean") {
762
+ todoObj.eager = todoObj.eager ? "always" : "default";
763
+ }
764
+
755
765
  // task.isolation.mode: legacy values from before the pi-iso PAL refactor.
756
766
  // `worktree` was git worktree → now lives under `rcopy`. `fuse-overlay`
757
767
  // and `fuse-projfs` are now the platform-named `overlayfs` / `projfs`
@@ -6,6 +6,7 @@
6
6
  import * as path from "node:path";
7
7
  import { logger, parseFrontmatter, tryParseJson } from "@oh-my-pi/pi-utils";
8
8
  import { YAML } from "bun";
9
+ import { getManagedSkillsDir, MANAGED_SKILLS_PROVIDER_ID } from "../autolearn/managed-skills";
9
10
  import { registerProvider } from "../capability";
10
11
  import { type ContextFile, contextFileCapability } from "../capability/context-file";
11
12
  import { type Extension, type ExtensionManifest, extensionCapability } from "../capability/extension";
@@ -283,13 +284,26 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
283
284
  });
284
285
 
285
286
  const results = await Promise.all([...projectScans, userScan]);
286
-
287
287
  return {
288
288
  items: results.flatMap(r => r.items),
289
289
  warnings: results.flatMap(r => r.warnings ?? []),
290
290
  };
291
291
  }
292
292
 
293
+ // Managed skills (auto-learn) are a SEPARATE provider at the lowest skill
294
+ // priority, so an authored skill of the same name from ANY other provider wins
295
+ // the capability-level priority dedup. Discovery is unconditional (an empty
296
+ // managed dir is a no-op); only writing/nudging is gated by `autolearn.enabled`.
297
+ const MANAGED_SKILLS_PRIORITY = 5;
298
+ async function loadManagedSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
299
+ return scanSkillsFromDir(ctx, {
300
+ dir: getManagedSkillsDir(ctx.home),
301
+ providerId: MANAGED_SKILLS_PROVIDER_ID,
302
+ level: "user",
303
+ requireDescription: true,
304
+ });
305
+ }
306
+
293
307
  registerProvider<Skill>(skillCapability.id, {
294
308
  id: PROVIDER_ID,
295
309
  displayName: DISPLAY_NAME,
@@ -298,6 +312,14 @@ registerProvider<Skill>(skillCapability.id, {
298
312
  load: loadSkills,
299
313
  });
300
314
 
315
+ registerProvider<Skill>(skillCapability.id, {
316
+ id: MANAGED_SKILLS_PROVIDER_ID,
317
+ displayName: "Managed Skills (auto-learn)",
318
+ description: "Auto-generated managed skills from ~/.omp/agent/managed-skills",
319
+ priority: MANAGED_SKILLS_PRIORITY,
320
+ load: loadManagedSkills,
321
+ });
322
+
301
323
  // Slash Commands
302
324
  async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashCommand>> {
303
325
  const items: SlashCommand[] = [];
@@ -124,6 +124,43 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
124
124
  }
125
125
  return { items, warnings };
126
126
  }
127
+ async function loadSkillSlashCommands(ctx: LoadContext, root: ClaudePluginRoot): Promise<LoadResult<SlashCommand>> {
128
+ const { dir: skillsDir, warning } = await resolvePluginDir(root, ["skills"], "skills");
129
+ const warnings: string[] = warning ? [warning] : [];
130
+ const skillsResult = await scanSkillsFromDir(ctx, {
131
+ dir: skillsDir,
132
+ providerId: PROVIDER_ID,
133
+ level: root.scope,
134
+ });
135
+ warnings.push(...(skillsResult.warnings ?? []));
136
+
137
+ const commands = await Promise.all(
138
+ skillsResult.items.map(async skill => {
139
+ const content = await readFile(skill.path);
140
+ if (content === null) {
141
+ warnings.push(`Failed to read skill slash command: ${skill.path}`);
142
+ return null;
143
+ }
144
+ // Slash command name MUST come from the skill directory basename, not
145
+ // frontmatter `name`: `expandSlashCommand` splits the command at the first
146
+ // whitespace, so a display name like "Understand Anything" would never match
147
+ // `/understand`. The documented layout is `skills/<name>/SKILL.md` → `/<name>`.
148
+ const command: SlashCommand = {
149
+ name: path.basename(path.dirname(skill.path)),
150
+ path: skill.path,
151
+ content,
152
+ level: skill.level,
153
+ _source: skill._source,
154
+ };
155
+ return command;
156
+ }),
157
+ );
158
+
159
+ return {
160
+ items: commands.filter((command): command is SlashCommand => command !== null),
161
+ warnings,
162
+ };
163
+ }
127
164
 
128
165
  // =============================================================================
129
166
  // Slash Commands
@@ -139,7 +176,7 @@ async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashComm
139
176
  const results = await Promise.all(
140
177
  roots.map(async root => {
141
178
  const { dir: commandsDir, warning } = await resolvePluginDir(root, ["commands", "slash-commands"], "commands");
142
- const result = await loadFilesFromDir<SlashCommand>(ctx, commandsDir, PROVIDER_ID, root.scope, {
179
+ const commandResult = await loadFilesFromDir<SlashCommand>(ctx, commandsDir, PROVIDER_ID, root.scope, {
143
180
  extensions: ["md"],
144
181
  transform: (name, content, filePath, source) => {
145
182
  const cmdName = name.replace(/\.md$/, "");
@@ -152,14 +189,16 @@ async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashComm
152
189
  };
153
190
  },
154
191
  });
155
- return { result, warning };
192
+ const skillCommandResult = await loadSkillSlashCommands(ctx, root);
193
+ return { commandResult, skillCommandResult, warning };
156
194
  }),
157
195
  );
158
196
 
159
- for (const { result, warning } of results) {
197
+ for (const { commandResult, skillCommandResult, warning } of results) {
160
198
  if (warning) warnings.push(warning);
161
- items.push(...result.items);
162
- if (result.warnings) warnings.push(...result.warnings);
199
+ items.push(...commandResult.items, ...skillCommandResult.items);
200
+ if (commandResult.warnings) warnings.push(...commandResult.warnings);
201
+ if (skillCommandResult.warnings) warnings.push(...skillCommandResult.warnings);
163
202
  }
164
203
 
165
204
  return { items, warnings };
@@ -491,6 +491,42 @@ interface ExtensionModuleManifest {
491
491
  extensions?: string[];
492
492
  }
493
493
 
494
+ async function discoverLinkedExtensionModuleFiles(dir: string): Promise<{
495
+ indexFiles: Array<{ path: string }>;
496
+ packageJsonFiles: Array<{ path: string }>;
497
+ }> {
498
+ const entries = await readDirEntries(dir);
499
+ const indexFiles: Array<{ path: string }> = [];
500
+ const packageJsonFiles: Array<{ path: string }> = [];
501
+
502
+ await Promise.all(
503
+ entries.map(async entry => {
504
+ if (entry.name.startsWith(".") || entry.isDirectory()) return;
505
+
506
+ const entryPath = path.join(dir, entry.name);
507
+ const stat = await fs.promises.stat(entryPath).catch(() => null);
508
+ if (!stat?.isDirectory()) return;
509
+
510
+ const [packageJsonContent, indexTsContent, indexJsContent] = await Promise.all([
511
+ readFile(path.join(entryPath, "package.json")),
512
+ readFile(path.join(entryPath, "index.ts")),
513
+ readFile(path.join(entryPath, "index.js")),
514
+ ]);
515
+
516
+ if (packageJsonContent !== null) {
517
+ packageJsonFiles.push({ path: `${entry.name}/package.json` });
518
+ }
519
+ if (indexTsContent !== null) {
520
+ indexFiles.push({ path: `${entry.name}/index.ts` });
521
+ } else if (indexJsContent !== null) {
522
+ indexFiles.push({ path: `${entry.name}/index.js` });
523
+ }
524
+ }),
525
+ );
526
+
527
+ return { indexFiles, packageJsonFiles };
528
+ }
529
+
494
530
  async function readExtensionModuleManifest(
495
531
  _ctx: LoadContext,
496
532
  packageJsonPath: string,
@@ -520,14 +556,18 @@ async function readExtensionModuleManifest(
520
556
  export async function discoverExtensionModulePaths(_ctx: LoadContext, dir: string): Promise<string[]> {
521
557
  const discovered = new Set<string>();
522
558
  // Find all candidate files in parallel using glob
523
- const [directFiles, indexFiles, packageJsonFiles] = await Promise.all([
559
+ const [directFiles, globIndexFiles, globPackageJsonFiles, linkedFiles] = await Promise.all([
524
560
  // 1. Direct *.ts or *.js files
525
561
  globIf(dir, "*.{ts,js}", FileType.File, false),
526
562
  // 2. Subdirectory index files
527
563
  globIf(dir, "*/index.{ts,js}", FileType.File, false),
528
564
  // 3. Subdirectory package.json files
529
565
  globIf(dir, "*/package.json", FileType.File, false),
566
+ // Native glob does not follow linked extension directories.
567
+ discoverLinkedExtensionModuleFiles(dir),
530
568
  ]);
569
+ const indexFiles = [...globIndexFiles, ...linkedFiles.indexFiles];
570
+ const packageJsonFiles = [...globPackageJsonFiles, ...linkedFiles.packageJsonFiles];
531
571
 
532
572
  // Process direct files
533
573
  for (const match of directFiles) {
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from "bun:test";
2
2
  import type { GoalModeState } from "../../goals/state";
3
- import type { UsageStatistics } from "../../session/session-manager";
3
+ import type { UsageStatistics } from "../../session/session-entries";
4
4
  import type { ToolSession } from "../../tools";
5
5
  import { runEvalBudget } from "../budget-bridge";
6
6