@oh-my-pi/pi-coding-agent 17.2.8 → 17.2.10

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 (246) hide show
  1. package/CHANGELOG.md +94 -1
  2. package/dist/{CHANGELOG-0b0w17se.md → CHANGELOG-rhwzpexa.md} +94 -1
  3. package/dist/cli.js +13216 -15614
  4. package/dist/types/async/job-manager.d.ts +12 -0
  5. package/dist/types/cli/args.d.ts +1 -0
  6. package/dist/types/config/model-resolver.d.ts +13 -0
  7. package/dist/types/discovery/agents.d.ts +11 -0
  8. package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
  9. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  10. package/dist/types/extensibility/extensions/loader.d.ts +2 -2
  11. package/dist/types/extensibility/extensions/types.d.ts +34 -8
  12. package/dist/types/extensibility/hooks/types.d.ts +5 -5
  13. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +10 -0
  14. package/dist/types/hindsight/state.d.ts +0 -1
  15. package/dist/types/index.d.ts +2 -2
  16. package/dist/types/lsp/utils.d.ts +8 -0
  17. package/dist/types/main.d.ts +1 -1
  18. package/dist/types/modes/acp/acp-agent.d.ts +1 -1
  19. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
  20. package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
  21. package/dist/types/modes/acp/acp-mode.d.ts +1 -1
  22. package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
  23. package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
  24. package/dist/types/modes/components/agent-hub.d.ts +12 -5
  25. package/dist/types/modes/components/model-browser.d.ts +9 -1
  26. package/dist/types/modes/interactive-mode.d.ts +8 -1
  27. package/dist/types/modes/session-observer-registry.d.ts +2 -0
  28. package/dist/types/registry/agent-lifecycle.d.ts +1 -1
  29. package/dist/types/registry/agent-registry.d.ts +47 -0
  30. package/dist/types/registry/persisted-agents.d.ts +3 -1
  31. package/dist/types/session/agent-session-types.d.ts +7 -0
  32. package/dist/types/session/agent-session.d.ts +8 -4
  33. package/dist/types/session/session-entries.d.ts +10 -0
  34. package/dist/types/session/session-loader.d.ts +11 -1
  35. package/dist/types/session/session-manager.d.ts +9 -1
  36. package/dist/types/session/session-tools.d.ts +10 -1
  37. package/dist/types/session/turn-recovery.d.ts +6 -2
  38. package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
  39. package/dist/types/slash-commands/available-commands.d.ts +1 -1
  40. package/dist/types/task/executor.d.ts +10 -0
  41. package/dist/types/task/index.d.ts +2 -3
  42. package/dist/types/task/read-only-policy.d.ts +3 -0
  43. package/dist/types/task/structured-subagent.d.ts +2 -0
  44. package/dist/types/task/types.d.ts +4 -0
  45. package/dist/types/tools/bash.d.ts +3 -3
  46. package/dist/types/tools/browser/launch.d.ts +3 -0
  47. package/dist/types/tools/browser/tab-worker.d.ts +2 -2
  48. package/dist/types/tools/path-utils.d.ts +8 -0
  49. package/dist/types/tools/read.d.ts +3 -5
  50. package/dist/types/tools/run-scope.d.ts +27 -1
  51. package/dist/types/tools/shell-tokenize.d.ts +21 -3
  52. package/dist/types/tools/terminal-output.d.ts +1 -1
  53. package/dist/types/utils/changelog.d.ts +3 -2
  54. package/dist/types/utils/late-cleanup.d.ts +2 -0
  55. package/dist/types/utils/turndown.d.ts +1 -1
  56. package/dist/types/vibe/runtime.d.ts +4 -3
  57. package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
  58. package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
  59. package/examples/custom-tools/README.md +1 -1
  60. package/examples/extensions/README.md +1 -1
  61. package/examples/extensions/api-demo.ts +5 -6
  62. package/examples/extensions/chalk-logger.ts +1 -1
  63. package/examples/extensions/hello.ts +2 -2
  64. package/examples/extensions/reload-runtime.ts +1 -1
  65. package/examples/extensions/tools.ts +2 -2
  66. package/examples/extensions/with-deps/index.ts +1 -1
  67. package/examples/sdk/06-extensions.ts +1 -1
  68. package/package.json +15 -31
  69. package/scripts/bundle-dist.ts +1 -11
  70. package/scripts/omp +11 -2
  71. package/src/advisor/runtime.ts +54 -0
  72. package/src/async/job-manager.ts +26 -0
  73. package/src/cli/agents-cli.ts +1 -1
  74. package/src/cli/args.ts +24 -2
  75. package/src/cli/auth-broker-cli.ts +1 -1
  76. package/src/cli/auth-gateway-cli.ts +1 -1
  77. package/src/cli/bench-cli.ts +1 -1
  78. package/src/cli/claude-trace-cli.ts +1 -1
  79. package/src/cli/config-cli.ts +1 -1
  80. package/src/cli/dry-balance-cli.ts +1 -1
  81. package/src/cli/file-processor.ts +1 -1
  82. package/src/cli/flag-tables.ts +4 -0
  83. package/src/cli/grep-cli.ts +1 -1
  84. package/src/cli/grievances-cli.ts +1 -1
  85. package/src/cli/help-extra.ts +1 -1
  86. package/src/cli/models-cli.ts +1 -1
  87. package/src/cli/plugin-cli.ts +1 -1
  88. package/src/cli/read-cli.ts +1 -1
  89. package/src/cli/setup-cli.ts +1 -1
  90. package/src/cli/shell-cli.ts +1 -1
  91. package/src/cli/ssh-cli.ts +1 -1
  92. package/src/cli/stats-cli.ts +4 -4
  93. package/src/cli/tiny-models-cli.ts +1 -1
  94. package/src/cli/ttsr-cli.ts +1 -1
  95. package/src/cli/update-cli.ts +3 -57
  96. package/src/cli/usage-cli.ts +1 -1
  97. package/src/cli/web-search-cli.ts +1 -1
  98. package/src/cli/worktree-cli.ts +1 -1
  99. package/src/commands/say.ts +1 -1
  100. package/src/commands/token.ts +1 -1
  101. package/src/commit/agentic/agent.ts +1 -1
  102. package/src/commit/cli.ts +1 -1
  103. package/src/config/config-file.ts +2 -2
  104. package/src/config/keybindings.ts +3 -3
  105. package/src/config/model-discovery.ts +36 -2
  106. package/src/config/model-registry.ts +4 -1
  107. package/src/config/model-resolver.ts +54 -7
  108. package/src/discovery/agents.ts +73 -3
  109. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
  110. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
  111. package/src/discovery/claude.ts +8 -5
  112. package/src/discovery/cursor.ts +8 -5
  113. package/src/discovery/gemini.ts +11 -8
  114. package/src/discovery/vscode.ts +1 -0
  115. package/src/discovery/windsurf.ts +4 -2
  116. package/src/edit/hashline/filesystem.ts +7 -7
  117. package/src/eval/agent-bridge.ts +1 -3
  118. package/src/eval/jl/prelude.jl +4 -4
  119. package/src/eval/js/shared/prelude.txt +2 -2
  120. package/src/eval/py/prelude.py +0 -3
  121. package/src/eval/py/runner.py +38 -1
  122. package/src/eval/rb/prelude.rb +1 -2
  123. package/src/extensibility/custom-commands/loader.ts +4 -4
  124. package/src/extensibility/custom-commands/types.ts +5 -5
  125. package/src/extensibility/custom-tools/loader.ts +4 -4
  126. package/src/extensibility/custom-tools/types.ts +1 -1
  127. package/src/extensibility/extensions/loader.ts +7 -6
  128. package/src/extensibility/extensions/types.ts +39 -8
  129. package/src/extensibility/hooks/loader.ts +4 -5
  130. package/src/extensibility/hooks/types.ts +5 -5
  131. package/src/extensibility/legacy-pi-coding-agent-shim.ts +23 -0
  132. package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
  133. package/src/hindsight/state.ts +2 -22
  134. package/src/index.ts +2 -2
  135. package/src/internal-urls/memory-protocol.ts +33 -0
  136. package/src/launch/terminal-output.ts +1 -1
  137. package/src/lsp/client.ts +2 -2
  138. package/src/lsp/utils.ts +29 -0
  139. package/src/main.ts +85 -21
  140. package/src/markit/converters/docx.ts +1 -1
  141. package/src/markit/converters/epub.ts +1 -1
  142. package/src/markit/converters/pptx.ts +1 -1
  143. package/src/markit/converters/xlsx.ts +1 -1
  144. package/src/modes/acp/acp-agent.ts +7 -6
  145. package/src/modes/acp/acp-client-bridge.ts +1 -1
  146. package/src/modes/acp/acp-event-mapper.ts +1 -1
  147. package/src/modes/acp/acp-mode.ts +1 -1
  148. package/src/modes/components/agent-hub-projection.ts +248 -0
  149. package/src/modes/components/agent-hub-renderer.ts +194 -0
  150. package/src/modes/components/agent-hub.ts +670 -192
  151. package/src/modes/components/agent-transcript-viewer.ts +1 -3
  152. package/src/modes/components/assistant-message.ts +1 -1
  153. package/src/modes/components/bordered-loader.ts +1 -1
  154. package/src/modes/components/custom-editor.ts +2 -2
  155. package/src/modes/components/extensions/inspector-panel.ts +11 -6
  156. package/src/modes/components/footer.ts +1 -3
  157. package/src/modes/components/hook-input.ts +1 -1
  158. package/src/modes/components/hook-selector.ts +2 -2
  159. package/src/modes/components/model-browser.ts +11 -3
  160. package/src/modes/components/model-hub.ts +4 -1
  161. package/src/modes/components/session-selector.ts +2 -2
  162. package/src/modes/components/status-line/segments.ts +4 -3
  163. package/src/modes/controllers/event-controller.ts +188 -7
  164. package/src/modes/controllers/extension-ui-controller.ts +4 -3
  165. package/src/modes/controllers/mcp-command-controller.ts +81 -20
  166. package/src/modes/controllers/selector-controller.ts +40 -49
  167. package/src/modes/controllers/streaming-reveal.ts +1 -1
  168. package/src/modes/interactive-mode.ts +13 -2
  169. package/src/modes/print-mode.ts +29 -12
  170. package/src/modes/rpc/rpc-mode.ts +2 -2
  171. package/src/modes/runtime-init.ts +1 -1
  172. package/src/modes/session-observer-registry.ts +5 -0
  173. package/src/modes/theme/theme.ts +2 -2
  174. package/src/prompts/tools/computer.md +1 -1
  175. package/src/registry/agent-lifecycle.ts +35 -10
  176. package/src/registry/agent-registry.ts +65 -2
  177. package/src/registry/persisted-agents.ts +341 -16
  178. package/src/sdk.ts +11 -5
  179. package/src/session/agent-session-types.ts +8 -0
  180. package/src/session/agent-session.ts +177 -188
  181. package/src/session/session-entries.ts +10 -0
  182. package/src/session/session-listing.ts +1 -1
  183. package/src/session/session-loader.ts +74 -8
  184. package/src/session/session-manager.ts +22 -2
  185. package/src/session/session-paths.ts +105 -110
  186. package/src/session/session-tools.ts +28 -1
  187. package/src/session/settings-stream-fn.ts +7 -3
  188. package/src/session/turn-recovery.ts +236 -18
  189. package/src/session/unexpected-stop-classifier.ts +12 -3
  190. package/src/slash-commands/acp-builtins.ts +1 -1
  191. package/src/slash-commands/available-commands.ts +1 -1
  192. package/src/slash-commands/helpers/stats-dashboard.ts +2 -1
  193. package/src/task/agents.ts +1 -1
  194. package/src/task/executor.ts +173 -47
  195. package/src/task/index.ts +9 -28
  196. package/src/task/isolation-runner.ts +43 -12
  197. package/src/task/persisted-revive.ts +12 -5
  198. package/src/task/read-only-policy.ts +27 -0
  199. package/src/task/structured-subagent.ts +31 -6
  200. package/src/task/types.ts +4 -0
  201. package/src/tiny/worker.ts +1 -1
  202. package/src/tools/auto-generated-guard.ts +1 -1
  203. package/src/tools/bash-interactive.ts +4 -4
  204. package/src/tools/bash-interceptor.ts +8 -4
  205. package/src/tools/bash.ts +6 -6
  206. package/src/tools/browser/cmux/cmux-tab.ts +66 -18
  207. package/src/tools/browser/launch.ts +76 -19
  208. package/src/tools/browser/readable.ts +9 -9
  209. package/src/tools/browser/tab-supervisor.ts +12 -3
  210. package/src/tools/browser/tab-worker-entry.ts +1 -1
  211. package/src/tools/browser/tab-worker.ts +101 -12
  212. package/src/tools/fetch.ts +1 -1
  213. package/src/tools/glob.ts +4 -2
  214. package/src/tools/path-utils.ts +22 -5
  215. package/src/tools/read.ts +22 -5
  216. package/src/tools/run-scope.ts +290 -4
  217. package/src/tools/shell-tokenize.ts +38 -9
  218. package/src/tools/terminal-output.ts +1 -1
  219. package/src/tools/todo.ts +1 -1
  220. package/src/utils/changelog.ts +5 -4
  221. package/src/utils/clipboard.ts +10 -2
  222. package/src/utils/external-editor.ts +4 -2
  223. package/src/utils/jj.ts +1 -1
  224. package/src/utils/late-cleanup.ts +17 -0
  225. package/src/utils/turndown.ts +1 -2
  226. package/src/vibe/runtime.ts +202 -58
  227. package/src/web/scrapers/arxiv.ts +1 -1
  228. package/src/web/scrapers/go-pkg.ts +1 -1
  229. package/src/web/scrapers/hackage.ts +1 -12
  230. package/src/web/scrapers/iacr.ts +1 -1
  231. package/src/web/scrapers/readthedocs.ts +2 -1
  232. package/src/web/scrapers/twitter.ts +2 -2
  233. package/src/web/scrapers/types.ts +1 -1
  234. package/src/web/scrapers/wikipedia.ts +1 -1
  235. package/src/web/search/providers/browser-headers.ts +12 -39
  236. package/src/web/search/providers/codex.ts +3 -26
  237. package/src/web/search/providers/duckduckgo.ts +1 -1
  238. package/src/web/search/providers/ecosia.ts +2 -2
  239. package/src/web/search/providers/exa.ts +1 -1
  240. package/src/web/search/providers/firecrawl.ts +1 -1
  241. package/src/web/search/providers/google.ts +1 -1
  242. package/src/web/search/providers/mojeek.ts +2 -2
  243. package/src/web/search/providers/perplexity.ts +1 -1
  244. package/src/web/search/providers/public.ts +1 -1
  245. package/src/web/search/providers/startpage.ts +2 -2
  246. package/src/markit/converters/mammoth.d.ts +0 -24
@@ -3,7 +3,7 @@
3
3
  * Tests that jiti can resolve npm modules correctly.
4
4
  */
5
5
  import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
6
- import chalk from "chalk";
6
+ import chalk from "@oh-my-pi/pi-utils/chalk";
7
7
 
8
8
  export default function (pi: ExtensionAPI) {
9
9
  // Log with colors using chalk
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Hello Tool - Minimal custom tool example
3
3
  *
4
- * Demonstrates using ExtensionAPI's logger, injected `pi.zod`, and pi module access.
4
+ * Demonstrates using ExtensionAPI's logger and injected schema builder.
5
5
  */
6
6
  import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
7
7
 
8
8
  export default function (pi: ExtensionAPI) {
9
- const { z } = pi.zod;
9
+ const z = pi.zod;
10
10
 
11
11
  pi.registerTool({
12
12
  name: "hello",
@@ -8,7 +8,7 @@
8
8
  import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
9
9
 
10
10
  export default function (pi: ExtensionAPI) {
11
- const { z } = pi.zod;
11
+ const z = pi.zod;
12
12
 
13
13
  // Command entrypoint for reload.
14
14
  // Treat reload as terminal for this handler.
@@ -36,7 +36,7 @@ export default function toolsExtension(pi: ExtensionAPI) {
36
36
 
37
37
  // Find the last tools-config entry in the current branch
38
38
  async function restoreFromBranch(ctx: ExtensionContext) {
39
- allTools = pi.getAllTools();
39
+ allTools = pi.getAllTools().map(t => t.name);
40
40
 
41
41
  // Get entries in current branch only
42
42
  const branchEntries = ctx.sessionManager.getBranch();
@@ -66,7 +66,7 @@ export default function toolsExtension(pi: ExtensionAPI) {
66
66
  description: "Enable/disable tools",
67
67
  handler: async (_args, ctx) => {
68
68
  // Refresh tool list
69
- allTools = pi.getAllTools();
69
+ allTools = pi.getAllTools().map(t => t.name);
70
70
 
71
71
  await ctx.ui.custom((tui, theme, _keybindings, done) => {
72
72
  // Build settings items for each tool
@@ -8,7 +8,7 @@ import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
8
8
  import ms from "ms";
9
9
 
10
10
  export default function (pi: ExtensionAPI) {
11
- const { z } = pi.zod;
11
+ const z = pi.zod;
12
12
 
13
13
  // Register a tool that uses ms
14
14
  pi.registerTool({
@@ -41,7 +41,7 @@ console.log();
41
41
  import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
42
42
 
43
43
  export default function (pi: ExtensionAPI) {
44
- const { z } = pi.zod;
44
+ const z = pi.zod;
45
45
 
46
46
  pi.on("agent_start", async () => {
47
47
  console.log("[Extension] Agent starting");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "17.2.8",
4
+ "version": "17.2.10",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -49,21 +49,19 @@
49
49
  "bench:guard": "bun scripts/bench-guard.ts"
50
50
  },
51
51
  "dependencies": {
52
- "@agentclientprotocol/sdk": "1.2.1",
53
52
  "@babel/parser": "^7.29.7",
54
- "@mozilla/readability": "^0.6.0",
55
- "@oh-my-pi/hashline": "17.2.8",
56
- "@oh-my-pi/omp-stats": "17.2.8",
57
- "@oh-my-pi/omptype": "17.2.8",
58
- "@oh-my-pi/pi-agent-core": "17.2.8",
59
- "@oh-my-pi/pi-ai": "17.2.8",
60
- "@oh-my-pi/pi-catalog": "17.2.8",
61
- "@oh-my-pi/pi-mnemopi": "17.2.8",
62
- "@oh-my-pi/pi-natives": "17.2.8",
63
- "@oh-my-pi/pi-tui": "17.2.8",
64
- "@oh-my-pi/pi-utils": "17.2.8",
65
- "@oh-my-pi/pi-wire": "17.2.8",
66
- "@oh-my-pi/snapcompact": "17.2.8",
53
+ "@oh-my-pi/hashline": "17.2.10",
54
+ "@oh-my-pi/omp-stats": "17.2.10",
55
+ "@oh-my-pi/omptype": "17.2.10",
56
+ "@oh-my-pi/pi-agent-core": "17.2.10",
57
+ "@oh-my-pi/pi-ai": "17.2.10",
58
+ "@oh-my-pi/pi-catalog": "17.2.10",
59
+ "@oh-my-pi/pi-mnemopi": "17.2.10",
60
+ "@oh-my-pi/pi-natives": "17.2.10",
61
+ "@oh-my-pi/pi-tui": "17.2.10",
62
+ "@oh-my-pi/pi-utils": "17.2.10",
63
+ "@oh-my-pi/pi-wire": "17.2.10",
64
+ "@oh-my-pi/snapcompact": "17.2.10",
67
65
  "@opentelemetry/api": "^1.9.1",
68
66
  "@opentelemetry/api-logs": "^0.220.0",
69
67
  "@opentelemetry/context-async-hooks": "^2.9.0",
@@ -75,29 +73,15 @@
75
73
  "@opentelemetry/sdk-metrics": "^2.9.0",
76
74
  "@opentelemetry/sdk-trace-base": "^2.9.0",
77
75
  "@opentelemetry/sdk-trace-node": "^2.9.0",
78
- "@puppeteer/browsers": "^3.0.6",
79
- "@types/turndown": "5.0.6",
80
- "@xterm/headless": "^6.0.0",
81
- "chalk": "^5.6.2",
82
- "fast-xml-parser": "^5.9.3",
83
- "handlebars": "^4.7.9",
84
- "header-generator": "^2.1.82",
85
- "linkedom": "^0.18.13",
86
- "lru-cache": "11.5.2",
87
- "mammoth": "^1.12.0",
88
76
  "mupdf": "^1.28.0",
89
- "puppeteer-core": "25.3.0",
90
- "turndown": "7.2.4",
91
- "turndown-plugin-gfm": "1.0.2",
92
- "zod": "^4"
77
+ "puppeteer-core": "25.3.0"
93
78
  },
94
79
  "optionalDependencies": {
95
80
  "@huggingface/transformers": "^4.2.0",
96
81
  "sherpa-onnx-node": "1.13.2"
97
82
  },
98
83
  "devDependencies": {
99
- "@types/bun": "^1.3.14",
100
- "marked": "^18.0.6"
84
+ "@types/bun": "^1.3.14"
101
85
  },
102
86
  "engines": {
103
87
  "bun": ">=1.3.14"
@@ -30,17 +30,7 @@ const ALWAYS_EXTERNAL = [
30
30
  // import would load the unpatched npm package in users' installs (currently
31
31
  // @ark/schema is patched, so it — and arktype, which pulls @ark/schema — stay
32
32
  // bundled).
33
- const RUNTIME_EXTERNAL = [
34
- "puppeteer-core",
35
- "@puppeteer/browsers",
36
- "@babel/parser",
37
- "@xterm/headless",
38
- "turndown",
39
- "turndown-plugin-gfm",
40
- "@mozilla/readability",
41
- "linkedom",
42
- "@agentclientprotocol/sdk",
43
- ];
33
+ const RUNTIME_EXTERNAL = ["puppeteer-core", "@babel/parser"];
44
34
 
45
35
  async function runCommand(command: string[]): Promise<void> {
46
36
  const proc = Bun.spawn(command, {
package/scripts/omp CHANGED
@@ -36,7 +36,16 @@ mkdir -p "$launch_dir"
36
36
  OMP_LAUNCH_CWD=$PWD
37
37
  export OMP_LAUNCH_CWD
38
38
  cd "$launch_dir"
39
+ # Herdr 0.7.5 validates the foreground process name. The macOS shell can
40
+ # preserve OMP's identity while still running the development CLI through Bun.
41
+ run_bun() {
42
+ if [ "${HERDR_ENV:-}" = 1 ] && [ "$(uname -s)" = Darwin ]; then
43
+ exec -a omp bun "$@"
44
+ fi
45
+ exec bun "$@"
46
+ }
47
+
39
48
  if [ -n "${PI_TIMING:-}" ]; then
40
- exec bun --preload "$preload" --preload "$timing_preload" "$cli" "$@"
49
+ run_bun --preload "$preload" --preload "$timing_preload" "$cli" "$@"
41
50
  fi
42
- exec bun --preload "$preload" "$cli" "$@"
51
+ run_bun --preload "$preload" "$cli" "$@"
@@ -296,6 +296,14 @@ export class AdvisorRuntime {
296
296
  #failureNotified = false;
297
297
  /** Consecutive quarantined turns since the last success/reset (issue #6661). */
298
298
  #consecutiveQuarantines = 0;
299
+ /**
300
+ * Model identities this refusal cascade has already tried. The cascade walks
301
+ * the fallback chain to exhaustion — that is what the chain is for — but
302
+ * visits each model at most once, so a chain whose keys point back at each
303
+ * other (A→B, B→A) cannot ping-pong forever. Cleared when a successful or
304
+ * terminal turn ends the cascade, or on reset, so a later refusal starts fresh.
305
+ */
306
+ readonly #refusalModelsTried = new Set<string>();
299
307
  /** Whether primary reasoning is included in advisor deltas for the current model. */
300
308
  #includeThinking = true;
301
309
  #modelIdentity: string | undefined;
@@ -541,6 +549,7 @@ export class AdvisorRuntime {
541
549
  this.#failing = false;
542
550
  this.#droppedBacklogs = 0;
543
551
  this.#consecutiveQuarantines = 0;
552
+ this.#refusalModelsTried.clear();
544
553
  this.#failureNotified = false;
545
554
  this.#resetAdvisorContext(true, true);
546
555
  }
@@ -936,6 +945,7 @@ export class AdvisorRuntime {
936
945
  this.#failureNotified = false;
937
946
  this.#droppedBacklogs = 0;
938
947
  this.#consecutiveQuarantines = 0;
948
+ this.#refusalModelsTried.clear();
939
949
  if (this.host.onTurnSuccess) {
940
950
  try {
941
951
  await raceWithSignal(Promise.resolve(this.host.onTurnSuccess()), iterationAbort.signal);
@@ -995,6 +1005,50 @@ export class AdvisorRuntime {
995
1005
  continue;
996
1006
  }
997
1007
  }
1008
+ // A refusal that outlives the strip is this model's policy call, not
1009
+ // a malformed request, so hand it to the host's model fallback before
1010
+ // declaring the advisor dead. The cascade walks the chain to
1011
+ // exhaustion (the host returns false once candidates run out); the
1012
+ // tried-set only stops a cyclic chain from revisiting a model. The
1013
+ // primary turn-recovery path already allows fallback on refusals.
1014
+ const refusalModel = this.host.getModelIdentity?.() ?? this.#modelIdentity ?? "";
1015
+ let refusalRecovered = false;
1016
+ try {
1017
+ if (!this.#refusalModelsTried.has(refusalModel)) {
1018
+ this.#refusalModelsTried.add(refusalModel);
1019
+ refusalRecovered =
1020
+ (await raceWithSignal(
1021
+ Promise.resolve(this.host.onTurnError?.(err, failedMessages, iterationAbort.signal)),
1022
+ iterationAbort.signal,
1023
+ )) === true;
1024
+ } else {
1025
+ logger.debug("advisor refusal chain exhausted", { model: refusalModel });
1026
+ }
1027
+ } catch (hookErr) {
1028
+ logger.debug("advisor onTurnError hook failed after refusal", { err: String(hookErr) });
1029
+ }
1030
+ if (this.#epoch !== epoch) continue;
1031
+ if (this.#sessionTransitionPaused) {
1032
+ this.#pending.unshift(...popped);
1033
+ continue;
1034
+ }
1035
+ if (refusalRecovered) {
1036
+ this.#consecutiveFailures = 0;
1037
+ this.#failureNotified = false;
1038
+ this.#pending.unshift({
1039
+ text: batch,
1040
+ rawMessages,
1041
+ renderRevision: this.#renderRevision,
1042
+ turns: finalTurns,
1043
+ wip,
1044
+ overflowRecovery: recoveringOverflow || undefined,
1045
+ });
1046
+ logger.debug("advisor refusal recovered by model fallback");
1047
+ continue;
1048
+ }
1049
+ // The batch is terminal, so the next primary update is a new
1050
+ // refusal cascade and must be allowed to try the chain again.
1051
+ this.#refusalModelsTried.clear();
998
1052
  this.#notifyFailureOnce(err);
999
1053
  this.#clearSeenContext();
1000
1054
  this.#backlog = Math.max(0, this.#backlog - finalTurns);
@@ -94,6 +94,12 @@ export interface AsyncJobDeliveryState {
94
94
  pendingJobIds: string[];
95
95
  }
96
96
 
97
+ export interface AsyncJobReapResult {
98
+ settled: boolean;
99
+ pendingJobIds: string[];
100
+ completion: Promise<void>;
101
+ }
102
+
97
103
  export interface AsyncJobRegisterOptions {
98
104
  id?: string;
99
105
  /** Registry id of the agent that owns this job; used to scope cancelAll. */
@@ -490,6 +496,26 @@ export class AsyncJobManager {
490
496
  }
491
497
  }
492
498
 
499
+ /**
500
+ * Cancel every job owned by `ownerId`, then wait only until `deadlineAt`.
501
+ * The returned completion keeps waiting for actual process settlement when
502
+ * the deadline expires, so callers can move that cleanup out of the
503
+ * user-visible Task wait without losing ownership of the live work.
504
+ */
505
+ async cancelAndReapOwnerJobs(ownerId: string, deadlineAt: number): Promise<AsyncJobReapResult> {
506
+ this.cancelAll({ ownerId });
507
+ const timeoutMs = Math.max(0, deadlineAt - Date.now());
508
+ const settled = await this.waitForOwnerJobs(ownerId, { timeoutMs });
509
+ if (settled) {
510
+ return { settled: true, pendingJobIds: [], completion: Promise.resolve() };
511
+ }
512
+ const pendingJobIds = this.getAllJobs({ ownerId })
513
+ .filter(job => job.status === "running" || job.status === "cancelled")
514
+ .map(job => job.id);
515
+ const completion = this.waitForOwnerJobs(ownerId).then(() => {});
516
+ return { settled: false, pendingJobIds, completion };
517
+ }
518
+
493
519
  async #waitForAllUntil(deadline: number): Promise<boolean> {
494
520
  const promises = Array.from(this.#jobs.values()).map(job => job.promise);
495
521
  if (promises.length === 0) return true;
@@ -6,8 +6,8 @@
6
6
  import * as fs from "node:fs/promises";
7
7
  import * as path from "node:path";
8
8
  import { getAgentDir, getProjectDir, isEnoent } from "@oh-my-pi/pi-utils";
9
+ import chalk from "@oh-my-pi/pi-utils/chalk";
9
10
  import { YAML } from "bun";
10
- import chalk from "chalk";
11
11
  import { theme } from "../modes/theme/theme";
12
12
  import { loadBundledAgents } from "../task/agents";
13
13
  import type { AgentDefinition } from "../task/types";
package/src/cli/args.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * CLI argument parsing and help display
3
3
  */
4
+ import * as path from "node:path";
4
5
  import { $env, APP_NAME, logger } from "@oh-my-pi/pi-utils";
5
- import chalk from "chalk";
6
+ import chalk from "@oh-my-pi/pi-utils/chalk";
6
7
  import type { ServiceTierOpenAISettingValue } from "../config/service-tier";
7
8
  import { CLI_THINKING_LEVELS, type ConfiguredThinkingLevel, parseCliThinkingLevel } from "../thinking";
8
9
  import { BUILTIN_TOOL_NAMES, HIDDEN_TOOL_NAMES, normalizeToolNames } from "../tools/builtin-names";
@@ -68,6 +69,7 @@ export interface Args {
68
69
  noPty?: boolean;
69
70
  hooks?: string[];
70
71
  extensions?: string[];
72
+ trustedExtensions?: string[];
71
73
  noExtensions?: boolean;
72
74
  pluginDirs?: string[];
73
75
  print?: boolean;
@@ -110,7 +112,7 @@ const PARSE_DEPS: ParseDeps = {
110
112
  thinkingEfforts: CLI_THINKING_LEVELS,
111
113
  };
112
114
 
113
- const WINDOWS_PATH_VALUE_FLAGS: ReadonlySet<string> = new Set(["--extension", "-e", "--hook"]);
115
+ const WINDOWS_PATH_VALUE_FLAGS = new Set(["--extension", "-e", "--hook", "--trusted-extension"]);
114
116
  const WINDOWS_PATH_START_RE =
115
117
  /^(?:[A-Za-z]:[\\/]|\\\\[?]\\(?:[A-Za-z]:[\\/]|UNC[\\/])|\\\\[^\\/]+[\\/][^\\/]+[\\/]|\/\/[?]\/(?:[A-Za-z]:\/|UNC\/)|\/\/[^/]+\/[^/]+\/)/;
116
118
  const WINDOWS_MODULE_PATH_SUFFIX_RE = /\.(?:[cm]?[jt]sx?)$/i;
@@ -156,6 +158,7 @@ export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { ty
156
158
  // `--` ends option parsing (POSIX end-of-options). Everything after it is
157
159
  // literal positional text, so flag-shaped messages are not parsed or rejected.
158
160
  let sawSeparator = false;
161
+ let trustedFlagCount = 0;
159
162
  for (let i = 0; i < args.length; i++) {
160
163
  let arg = args[i];
161
164
  if (sawSeparator) {
@@ -200,6 +203,7 @@ export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { ty
200
203
  }
201
204
  }
202
205
  } else if (STRING_VALUE_FLAGS.has(arg)) {
206
+ if (arg === "--trusted-extension") trustedFlagCount++;
203
207
  // Built-in string flags consume the next token even when it is flag-looking
204
208
  // (`--system-prompt --profile foo` ⇒ the prompt is the literal "--profile").
205
209
  // The one token they must never absorb is the profile bootstrap's internal
@@ -304,6 +308,24 @@ export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { ty
304
308
  }
305
309
  }
306
310
 
311
+ const swallowedTrustedFlag = [...(result.extensions ?? []), ...(result.hooks ?? [])].some(
312
+ value => value === "--trusted-extension" || value.startsWith("--trusted-extension="),
313
+ );
314
+ if ((result.trustedExtensions?.length ?? 0) !== trustedFlagCount || swallowedTrustedFlag) {
315
+ throw new CliUsageError("--trusted-extension requires a non-empty, non-flag value");
316
+ }
317
+ if (trustedFlagCount > 0 && ((result.extensions?.length ?? 0) > 0 || (result.hooks?.length ?? 0) > 0)) {
318
+ throw new CliUsageError("--trusted-extension cannot be combined with --extension, -e, or --hook");
319
+ }
320
+ for (const trustedPath of result.trustedExtensions ?? []) {
321
+ if (trustedPath.length === 0) {
322
+ throw new CliUsageError("--trusted-extension requires a non-empty, non-flag value");
323
+ }
324
+ if (!path.isAbsolute(trustedPath)) {
325
+ throw new CliUsageError(`--trusted-extension requires an absolute path: ${trustedPath}`);
326
+ }
327
+ }
328
+
307
329
  return result;
308
330
  }
309
331
 
@@ -34,9 +34,9 @@ import {
34
34
  } from "@oh-my-pi/pi-ai";
35
35
  import { AuthBrokerClient, DEFAULT_AUTH_BROKER_BIND, startAuthBroker } from "@oh-my-pi/pi-ai/auth-broker";
36
36
  import { $which, APP_NAME, getAgentDbPath, getConfigRootDir, isEnoent, logger, VERSION } from "@oh-my-pi/pi-utils";
37
+ import chalk from "@oh-my-pi/pi-utils/chalk";
37
38
  import { setTransports as setLoggerTransports } from "@oh-my-pi/pi-utils/logger";
38
39
  import { $ } from "bun";
39
- import chalk from "chalk";
40
40
  import { resolveAuthBrokerConfig } from "../session/auth-broker-config";
41
41
 
42
42
  export type AuthBrokerAction = "serve" | "token" | "login" | "logout" | "status" | "import" | "migrate" | "list";
@@ -33,7 +33,7 @@ import {
33
33
  import { DEFAULT_AUTH_GATEWAY_BIND, startAuthGateway } from "@oh-my-pi/pi-ai/auth-gateway";
34
34
  import { type GeneratedProvider, getBundledModels } from "@oh-my-pi/pi-catalog/models";
35
35
  import { getConfigRootDir, isEnoent, logger, VERSION } from "@oh-my-pi/pi-utils";
36
- import chalk from "chalk";
36
+ import chalk from "@oh-my-pi/pi-utils/chalk";
37
37
  import { ModelRegistry } from "../config/model-registry";
38
38
  import { type AuthBrokerClientConfig, resolveAuthBrokerConfig } from "../session/auth-broker-config";
39
39
 
@@ -17,7 +17,7 @@ import { resolveModelServiceTier, streamSimple } from "@oh-my-pi/pi-ai";
17
17
  import { buildModelProviderPriorityRank } from "@oh-my-pi/pi-catalog/identity";
18
18
  import { replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
19
19
  import { formatDuration, getProjectDir, prompt } from "@oh-my-pi/pi-utils";
20
- import chalk from "chalk";
20
+ import chalk from "@oh-my-pi/pi-utils/chalk";
21
21
  import type { ApiKeyResolverModel } from "../config/api-key-resolver";
22
22
  import { ModelRegistry } from "../config/model-registry";
23
23
  import {
@@ -10,7 +10,7 @@ import * as path from "node:path";
10
10
  import * as tls from "node:tls";
11
11
  import * as zlib from "node:zlib";
12
12
  import { PtySession } from "@oh-my-pi/pi-natives";
13
- import xterm from "@xterm/headless";
13
+ import xterm from "@oh-my-pi/pi-utils/vterm";
14
14
 
15
15
  const DEFAULT_PROXY_HOST = "127.0.0.1";
16
16
  const DEFAULT_PROXY_PORT = 8080;
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { APP_NAME, getAgentDir } from "@oh-my-pi/pi-utils";
9
- import chalk from "chalk";
9
+ import chalk from "@oh-my-pi/pi-utils/chalk";
10
10
  import {
11
11
  getDefault,
12
12
  getEnumValues,
@@ -14,7 +14,7 @@ import type {
14
14
  import { streamSimple } from "@oh-my-pi/pi-ai";
15
15
  import { replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
16
16
  import { formatDuration, getProjectDir } from "@oh-my-pi/pi-utils";
17
- import chalk from "chalk";
17
+ import chalk from "@oh-my-pi/pi-utils/chalk";
18
18
  import { ModelRegistry } from "../config/model-registry";
19
19
  import {
20
20
  formatModelString,
@@ -5,7 +5,7 @@ import * as fs from "node:fs";
5
5
  import * as path from "node:path";
6
6
  import type { ImageContent } from "@oh-my-pi/pi-ai";
7
7
  import { getProjectDir, isEnoent, readImageMetadata } from "@oh-my-pi/pi-utils";
8
- import chalk from "chalk";
8
+ import chalk from "@oh-my-pi/pi-utils/chalk";
9
9
  import { resolveReadPath } from "../tools/path-utils";
10
10
  import { formatBytes } from "../tools/render-utils";
11
11
  import { formatDimensionNote, resizeImage } from "../utils/image-resize";
@@ -220,6 +220,10 @@ export const STRING_SETTERS: Record<string, StringSetter> = {
220
220
  },
221
221
  "--extension": setExtension,
222
222
  "-e": setExtension,
223
+ "--trusted-extension": (result, value) => {
224
+ result.trustedExtensions = result.trustedExtensions ?? [];
225
+ result.trustedExtensions.push(value);
226
+ },
223
227
  "--plugin-dir": (result, value) => {
224
228
  result.pluginDirs = result.pluginDirs ?? [];
225
229
  result.pluginDirs.push(value);
@@ -6,7 +6,7 @@
6
6
  import * as path from "node:path";
7
7
  import { GrepOutputMode, grep } from "@oh-my-pi/pi-natives";
8
8
  import { APP_NAME } from "@oh-my-pi/pi-utils";
9
- import chalk from "chalk";
9
+ import chalk from "@oh-my-pi/pi-utils/chalk";
10
10
  import { expandPath } from "../tools/path-utils";
11
11
 
12
12
  export interface GrepCommandArgs {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * CLI handler for `omp grievances` — view, clean, and manually push reported tool issues.
3
3
  */
4
- import chalk from "chalk";
4
+ import chalk from "@oh-my-pi/pi-utils/chalk";
5
5
  import { Settings } from "../config/settings";
6
6
  import { flushGrievances, openAutoQaDb } from "../tools/report-tool-issue";
7
7
 
@@ -1,6 +1,6 @@
1
1
  import "@oh-my-pi/pi-utils/env";
2
+ import chalk from "@oh-my-pi/pi-utils/chalk";
2
3
  import { APP_NAME, CONFIG_DIR_NAME } from "@oh-my-pi/pi-utils/dirs";
3
- import chalk from "chalk";
4
4
 
5
5
  export function getExtraHelpText(): string {
6
6
  return `${chalk.bold("Environment Variables:")}
@@ -14,7 +14,7 @@
14
14
  import type { Api, Effort, Model } from "@oh-my-pi/pi-ai";
15
15
  import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
16
16
  import { formatNumber, getProjectDir } from "@oh-my-pi/pi-utils";
17
- import chalk from "chalk";
17
+ import chalk from "@oh-my-pi/pi-utils/chalk";
18
18
  import { ModelRegistry } from "../config/model-registry";
19
19
  import { Settings } from "../config/settings";
20
20
  import { discoverAndLoadExtensions, ExtensionRunner, emitSessionShutdownEvent } from "../extensibility/extensions";
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { APP_NAME, getProjectDir } from "@oh-my-pi/pi-utils";
8
- import chalk from "chalk";
8
+ import chalk from "@oh-my-pi/pi-utils/chalk";
9
9
  import { resolveOrDefaultProjectRegistryPath } from "../discovery/helpers";
10
10
  import { PluginManager, parseSettingValue, validateSetting } from "../extensibility/plugins";
11
11
  import {
@@ -6,7 +6,7 @@
6
6
  * (including truncation/limit notices appended by the meta-notice wrapper).
7
7
  */
8
8
  import { getProjectDir } from "@oh-my-pi/pi-utils";
9
- import chalk from "chalk";
9
+ import chalk from "@oh-my-pi/pi-utils/chalk";
10
10
  import { Settings } from "../config/settings";
11
11
  import { extractUriScheme } from "../internal-urls/parse";
12
12
  import { InternalUrlRouter } from "../internal-urls/router";
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import * as path from "node:path";
7
7
  import { APP_NAME, getProjectDir, getPythonEnvDir } from "@oh-my-pi/pi-utils";
8
- import chalk from "chalk";
8
+ import chalk from "@oh-my-pi/pi-utils/chalk";
9
9
  import { Settings, settings } from "../config/settings";
10
10
  import { checkPythonKernelAvailability } from "../eval/py/kernel";
11
11
  import { theme } from "../modes/theme/theme";
@@ -7,7 +7,7 @@ import * as path from "node:path";
7
7
  import { createInterface } from "node:readline/promises";
8
8
  import { Shell } from "@oh-my-pi/pi-natives";
9
9
  import { APP_NAME, getProjectDir } from "@oh-my-pi/pi-utils";
10
- import chalk from "chalk";
10
+ import chalk from "@oh-my-pi/pi-utils/chalk";
11
11
  import { Settings } from "../config/settings";
12
12
  import { buildMinimizerOptions } from "../exec/bash-executor";
13
13
  import { getOrCreateSnapshot } from "../utils/shell-snapshot";
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { getSSHConfigPath } from "@oh-my-pi/pi-utils";
8
- import chalk from "chalk";
8
+ import chalk from "@oh-my-pi/pi-utils/chalk";
9
9
  import { addSSHHost, readSSHConfigFile, removeSSHHost, type SSHHostConfig } from "../ssh/config-writer";
10
10
 
11
11
  // =============================================================================
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { truncateToWidth } from "@oh-my-pi/pi-tui/utils";
8
8
  import { APP_NAME, formatDuration, formatNumber, formatPercent } from "@oh-my-pi/pi-utils";
9
- import chalk from "chalk";
9
+ import chalk from "@oh-my-pi/pi-utils/chalk";
10
10
  import { openPath } from "../utils/open";
11
11
 
12
12
  /**
@@ -136,11 +136,11 @@ export async function runStatsCommand(cmd: StatsCommandArgs): Promise<void> {
136
136
  }
137
137
 
138
138
  // Start the dashboard server
139
- const { port } = await startServer(cmd.port);
140
- console.log(chalk.green(`Dashboard available at: http://localhost:${port}`));
139
+ const { hostname, port } = await startServer(cmd.port);
140
+ const url = `http://${hostname}:${port}`;
141
+ console.log(chalk.green(`Dashboard available at: ${url}`));
141
142
 
142
143
  // Open browser
143
- const url = `http://localhost:${port}`;
144
144
  openPath(url);
145
145
 
146
146
  console.log("Press Ctrl+C to stop\n");
@@ -1,5 +1,5 @@
1
1
  import { formatBytes } from "@oh-my-pi/pi-utils";
2
- import chalk from "chalk";
2
+ import chalk from "@oh-my-pi/pi-utils/chalk";
3
3
  import {
4
4
  DEFAULT_TINY_TITLE_LOCAL_MODEL_KEY,
5
5
  getTinyLocalModelSpec,
@@ -13,8 +13,8 @@
13
13
  import * as fs from "node:fs";
14
14
  import * as path from "node:path";
15
15
  import { AstMatchStrictness, astMatch, FileType, type GlobMatch, glob } from "@oh-my-pi/pi-natives";
16
+ import chalk from "@oh-my-pi/pi-utils/chalk";
16
17
  import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
17
- import chalk from "chalk";
18
18
  import { BUILTIN_DEFAULTS_PROVIDER_ID, compileRuleCondition, type Rule, ruleCapability } from "../capability/rule";
19
19
  import { bucketRules } from "../capability/rule-buckets";
20
20
  import { Settings } from "../config/settings";