@oh-my-pi/pi-coding-agent 17.2.9 → 17.2.11

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 (281) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/{CHANGELOG-8yy8h3bc.md → CHANGELOG-d9xenpn9.md} +83 -0
  3. package/dist/cli.js +10208 -12596
  4. package/dist/types/capability/mcp.d.ts +11 -0
  5. package/dist/types/capability/skill.d.ts +6 -0
  6. package/dist/types/cli/args.d.ts +1 -0
  7. package/dist/types/cli/command-help.d.ts +3 -0
  8. package/dist/types/commands/share.d.ts +25 -0
  9. package/dist/types/commit/agentic/index.d.ts +3 -1
  10. package/dist/types/commit/execute.d.ts +32 -0
  11. package/dist/types/commit/index.d.ts +2 -1
  12. package/dist/types/commit/pipeline.d.ts +7 -1
  13. package/dist/types/config/model-registry.d.ts +4 -0
  14. package/dist/types/config/model-resolver.d.ts +13 -0
  15. package/dist/types/config/settings-schema.d.ts +1 -34
  16. package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
  17. package/dist/types/discovery/agent-plugins.d.ts +1 -0
  18. package/dist/types/discovery/contained-path.d.ts +34 -0
  19. package/dist/types/discovery/index.d.ts +1 -0
  20. package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
  21. package/dist/types/extensibility/custom-tools/types.d.ts +3 -3
  22. package/dist/types/extensibility/extensions/loader.d.ts +9 -2
  23. package/dist/types/extensibility/extensions/types.d.ts +45 -8
  24. package/dist/types/extensibility/hooks/types.d.ts +5 -5
  25. package/dist/types/extensibility/shared-events.d.ts +3 -2
  26. package/dist/types/extensibility/skills.d.ts +5 -0
  27. package/dist/types/index.d.ts +2 -2
  28. package/dist/types/main.d.ts +1 -1
  29. package/dist/types/mcp/transports/header-policy.d.ts +45 -0
  30. package/dist/types/mcp/types.d.ts +15 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +1 -1
  32. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
  33. package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
  34. package/dist/types/modes/acp/acp-mode.d.ts +1 -1
  35. package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
  36. package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
  37. package/dist/types/modes/components/agent-hub.d.ts +14 -5
  38. package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
  39. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  40. package/dist/types/modes/components/custom-editor.d.ts +1 -2
  41. package/dist/types/modes/components/model-browser.d.ts +9 -1
  42. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  43. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  44. package/dist/types/modes/interactive-mode.d.ts +8 -1
  45. package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
  46. package/dist/types/registry/agent-registry.d.ts +47 -0
  47. package/dist/types/registry/persisted-agents.d.ts +3 -1
  48. package/dist/types/secrets/index.d.ts +23 -1
  49. package/dist/types/session/agent-session-events.d.ts +2 -2
  50. package/dist/types/session/agent-session.d.ts +4 -2
  51. package/dist/types/session/messages.d.ts +1 -0
  52. package/dist/types/session/prewalk.d.ts +3 -1
  53. package/dist/types/session/session-entries.d.ts +10 -0
  54. package/dist/types/session/session-loader.d.ts +11 -1
  55. package/dist/types/session/session-manager.d.ts +9 -1
  56. package/dist/types/session/session-tools.d.ts +18 -1
  57. package/dist/types/session/turn-recovery.d.ts +7 -2
  58. package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
  59. package/dist/types/slash-commands/available-commands.d.ts +1 -1
  60. package/dist/types/task/executor.d.ts +6 -0
  61. package/dist/types/task/index.d.ts +4 -3
  62. package/dist/types/task/read-only-policy.d.ts +3 -0
  63. package/dist/types/task/structured-subagent.d.ts +2 -0
  64. package/dist/types/task/types.d.ts +4 -0
  65. package/dist/types/tools/bash.d.ts +3 -3
  66. package/dist/types/tools/browser/launch.d.ts +2 -1
  67. package/dist/types/tools/browser/tab-worker.d.ts +2 -2
  68. package/dist/types/tools/path-utils.d.ts +1 -0
  69. package/dist/types/tools/read.d.ts +1 -5
  70. package/dist/types/tools/run-scope.d.ts +27 -1
  71. package/dist/types/tools/shell-tokenize.d.ts +16 -0
  72. package/dist/types/tools/terminal-output.d.ts +1 -1
  73. package/dist/types/utils/turndown.d.ts +1 -1
  74. package/dist/types/vibe/runtime.d.ts +2 -3
  75. package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
  76. package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
  77. package/examples/custom-tools/README.md +1 -1
  78. package/examples/extensions/README.md +1 -1
  79. package/examples/extensions/api-demo.ts +5 -6
  80. package/examples/extensions/chalk-logger.ts +1 -1
  81. package/examples/extensions/hello.ts +2 -2
  82. package/examples/extensions/reload-runtime.ts +1 -1
  83. package/examples/extensions/tools.ts +2 -2
  84. package/examples/extensions/with-deps/index.ts +1 -1
  85. package/examples/sdk/06-extensions.ts +1 -1
  86. package/package.json +15 -31
  87. package/scripts/bundle-dist.ts +1 -11
  88. package/scripts/legacy-pi-virtual-module.ts +4 -1
  89. package/src/advisor/runtime.ts +54 -0
  90. package/src/capability/mcp.ts +11 -0
  91. package/src/capability/skill.ts +6 -0
  92. package/src/cli/agents-cli.ts +1 -1
  93. package/src/cli/args.ts +24 -2
  94. package/src/cli/auth-broker-cli.ts +1 -1
  95. package/src/cli/auth-gateway-cli.ts +1 -1
  96. package/src/cli/bench-cli.ts +1 -1
  97. package/src/cli/claude-trace-cli.ts +1 -1
  98. package/src/cli/command-help.ts +4 -0
  99. package/src/cli/config-cli.ts +1 -1
  100. package/src/cli/dry-balance-cli.ts +1 -1
  101. package/src/cli/file-processor.ts +1 -1
  102. package/src/cli/flag-tables.ts +4 -0
  103. package/src/cli/gallery-cli.ts +1 -1
  104. package/src/cli/grep-cli.ts +1 -1
  105. package/src/cli/grievances-cli.ts +1 -1
  106. package/src/cli/help-extra.ts +1 -1
  107. package/src/cli/models-cli.ts +1 -1
  108. package/src/cli/plugin-cli.ts +1 -1
  109. package/src/cli/read-cli.ts +1 -1
  110. package/src/cli/setup-cli.ts +1 -1
  111. package/src/cli/shell-cli.ts +1 -1
  112. package/src/cli/ssh-cli.ts +1 -1
  113. package/src/cli/stats-cli.ts +1 -1
  114. package/src/cli/tiny-models-cli.ts +1 -1
  115. package/src/cli/ttsr-cli.ts +1 -1
  116. package/src/cli/update-cli.ts +1 -1
  117. package/src/cli/usage-cli.ts +1 -1
  118. package/src/cli/web-search-cli.ts +1 -1
  119. package/src/cli/worktree-cli.ts +1 -1
  120. package/src/cli-commands.ts +5 -0
  121. package/src/commands/commit.ts +16 -3
  122. package/src/commands/say.ts +1 -1
  123. package/src/commands/share.ts +71 -0
  124. package/src/commands/token.ts +1 -1
  125. package/src/commit/agentic/agent.ts +1 -1
  126. package/src/commit/agentic/index.ts +39 -21
  127. package/src/commit/cli.ts +1 -1
  128. package/src/commit/execute.ts +56 -0
  129. package/src/commit/index.ts +2 -1
  130. package/src/commit/pipeline.ts +20 -7
  131. package/src/config/model-discovery.ts +1 -1
  132. package/src/config/model-registry.ts +21 -1
  133. package/src/config/model-resolver.ts +54 -7
  134. package/src/config/settings-schema.ts +2 -33
  135. package/src/config/settings.ts +46 -0
  136. package/src/discovery/agent-plugin-format.ts +551 -0
  137. package/src/discovery/agent-plugins.ts +341 -0
  138. package/src/discovery/agents.ts +4 -2
  139. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
  140. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
  141. package/src/discovery/claude-plugins.ts +21 -5
  142. package/src/discovery/contained-path.ts +78 -0
  143. package/src/discovery/helpers.ts +23 -9
  144. package/src/discovery/index.ts +1 -0
  145. package/src/discovery/omp-plugins.ts +20 -7
  146. package/src/eval/py/runner.py +38 -1
  147. package/src/exec/non-interactive-env.ts +1 -0
  148. package/src/extensibility/custom-commands/loader.ts +4 -4
  149. package/src/extensibility/custom-commands/types.ts +5 -5
  150. package/src/extensibility/custom-tools/loader.ts +4 -4
  151. package/src/extensibility/custom-tools/types.ts +3 -3
  152. package/src/extensibility/extensions/loader.ts +85 -20
  153. package/src/extensibility/extensions/runner.ts +6 -0
  154. package/src/extensibility/extensions/types.ts +51 -8
  155. package/src/extensibility/hooks/loader.ts +4 -5
  156. package/src/extensibility/hooks/types.ts +5 -5
  157. package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
  158. package/src/extensibility/plugins/marketplace/manager.ts +2 -1
  159. package/src/extensibility/shared-events.ts +3 -2
  160. package/src/extensibility/skills.ts +9 -0
  161. package/src/index.ts +2 -2
  162. package/src/internal-urls/memory-protocol.ts +5 -1
  163. package/src/internal-urls/skill-protocol.ts +14 -0
  164. package/src/internal-urls/vault-protocol.ts +2 -2
  165. package/src/launch/client.ts +11 -1
  166. package/src/launch/protocol.ts +7 -2
  167. package/src/launch/terminal-output.ts +1 -1
  168. package/src/main.ts +88 -23
  169. package/src/markit/converters/docx.ts +1 -1
  170. package/src/markit/converters/epub.ts +1 -1
  171. package/src/markit/converters/pptx.ts +1 -1
  172. package/src/markit/converters/xlsx.ts +1 -1
  173. package/src/mcp/config.ts +3 -0
  174. package/src/mcp/manager.ts +12 -9
  175. package/src/mcp/transports/header-policy.ts +95 -0
  176. package/src/mcp/transports/http.ts +35 -52
  177. package/src/mcp/transports/sse.ts +20 -27
  178. package/src/mcp/types.ts +15 -0
  179. package/src/modes/acp/acp-agent.ts +20 -9
  180. package/src/modes/acp/acp-client-bridge.ts +1 -1
  181. package/src/modes/acp/acp-event-mapper.ts +1 -1
  182. package/src/modes/acp/acp-mode.ts +1 -1
  183. package/src/modes/components/agent-dashboard.ts +2 -0
  184. package/src/modes/components/agent-hub-projection.ts +248 -0
  185. package/src/modes/components/agent-hub-renderer.ts +194 -0
  186. package/src/modes/components/agent-hub.ts +674 -207
  187. package/src/modes/components/agent-transcript-viewer.ts +3 -0
  188. package/src/modes/components/assistant-message.ts +1 -1
  189. package/src/modes/components/chat-transcript-builder.ts +3 -0
  190. package/src/modes/components/custom-editor.ts +0 -9
  191. package/src/modes/components/extensions/inspector-panel.ts +11 -6
  192. package/src/modes/components/footer.ts +1 -3
  193. package/src/modes/components/model-browser.ts +11 -3
  194. package/src/modes/components/model-hub.ts +4 -1
  195. package/src/modes/components/status-line/component.ts +1 -0
  196. package/src/modes/components/status-line/segments.ts +10 -7
  197. package/src/modes/components/status-line/types.ts +2 -0
  198. package/src/modes/components/tool-execution.ts +13 -16
  199. package/src/modes/components/tree-selector.ts +62 -3
  200. package/src/modes/controllers/command-controller.ts +4 -1
  201. package/src/modes/controllers/event-controller.ts +44 -20
  202. package/src/modes/controllers/extension-ui-controller.ts +2 -2
  203. package/src/modes/controllers/input-controller.ts +20 -2
  204. package/src/modes/controllers/mcp-command-controller.ts +81 -20
  205. package/src/modes/controllers/selector-controller.ts +41 -49
  206. package/src/modes/controllers/streaming-reveal.ts +1 -1
  207. package/src/modes/interactive-mode.ts +13 -2
  208. package/src/modes/rpc/rpc-mode.ts +2 -2
  209. package/src/modes/runtime-init.ts +1 -1
  210. package/src/modes/theme/theme.ts +2 -2
  211. package/src/modes/utils/transcript-render-helpers.ts +4 -2
  212. package/src/modes/utils/ui-helpers.ts +1 -0
  213. package/src/prompts/tools/computer.md +1 -1
  214. package/src/registry/agent-lifecycle.ts +14 -7
  215. package/src/registry/agent-registry.ts +65 -2
  216. package/src/registry/persisted-agents.ts +341 -16
  217. package/src/sdk.ts +6 -49
  218. package/src/secrets/index.ts +52 -1
  219. package/src/session/agent-session-events.ts +2 -2
  220. package/src/session/agent-session.ts +54 -16
  221. package/src/session/messages.ts +1 -0
  222. package/src/session/prewalk.ts +57 -17
  223. package/src/session/session-context.ts +3 -5
  224. package/src/session/session-entries.ts +10 -0
  225. package/src/session/session-handoff.ts +5 -1
  226. package/src/session/session-listing.ts +1 -1
  227. package/src/session/session-loader.ts +74 -8
  228. package/src/session/session-manager.ts +22 -2
  229. package/src/session/session-paths.ts +57 -9
  230. package/src/session/session-tools.ts +65 -4
  231. package/src/session/todo-tracker.ts +11 -1
  232. package/src/session/turn-recovery.ts +96 -74
  233. package/src/slash-commands/acp-builtins.ts +1 -1
  234. package/src/slash-commands/available-commands.ts +1 -1
  235. package/src/slash-commands/builtin-registry.ts +12 -8
  236. package/src/task/executor.ts +70 -20
  237. package/src/task/index.ts +30 -34
  238. package/src/task/isolation-runner.ts +24 -11
  239. package/src/task/persisted-revive.ts +12 -5
  240. package/src/task/read-only-policy.ts +27 -0
  241. package/src/task/structured-subagent.ts +14 -4
  242. package/src/task/types.ts +4 -0
  243. package/src/tools/auto-generated-guard.ts +1 -1
  244. package/src/tools/bash-interactive.ts +4 -4
  245. package/src/tools/bash-skill-urls.ts +15 -0
  246. package/src/tools/bash.ts +16 -17
  247. package/src/tools/browser/cmux/cmux-tab.ts +66 -18
  248. package/src/tools/browser/launch.ts +41 -12
  249. package/src/tools/browser/readable.ts +9 -9
  250. package/src/tools/browser/tab-supervisor.ts +12 -3
  251. package/src/tools/browser/tab-worker-entry.ts +1 -1
  252. package/src/tools/browser/tab-worker.ts +101 -12
  253. package/src/tools/debug.ts +1 -1
  254. package/src/tools/fetch.ts +1 -1
  255. package/src/tools/jtd-to-json-schema.ts +123 -21
  256. package/src/tools/path-utils.ts +9 -2
  257. package/src/tools/read.ts +21 -9
  258. package/src/tools/run-scope.ts +290 -4
  259. package/src/tools/shell-tokenize.ts +98 -0
  260. package/src/tools/terminal-output.ts +1 -1
  261. package/src/tools/todo.ts +1 -1
  262. package/src/utils/clipboard.ts +10 -2
  263. package/src/utils/external-editor.ts +4 -2
  264. package/src/utils/jj.ts +1 -1
  265. package/src/utils/turndown.ts +1 -2
  266. package/src/vibe/runtime.ts +5 -5
  267. package/src/web/scrapers/arxiv.ts +1 -1
  268. package/src/web/scrapers/go-pkg.ts +1 -1
  269. package/src/web/scrapers/iacr.ts +1 -1
  270. package/src/web/scrapers/readthedocs.ts +2 -1
  271. package/src/web/scrapers/twitter.ts +2 -2
  272. package/src/web/scrapers/types.ts +1 -1
  273. package/src/web/scrapers/wikipedia.ts +1 -1
  274. package/src/web/search/providers/browser-headers.ts +12 -39
  275. package/src/web/search/providers/codex.ts +3 -26
  276. package/src/web/search/providers/ecosia.ts +1 -1
  277. package/src/web/search/providers/exa.ts +1 -1
  278. package/src/web/search/providers/google.ts +1 -1
  279. package/src/web/search/providers/mojeek.ts +1 -1
  280. package/src/web/search/providers/startpage.ts +1 -1
  281. package/src/markit/converters/mammoth.d.ts +0 -24
@@ -42,7 +42,28 @@ function encodeRelativeSessionDirName(prefix: string, relative: string): string
42
42
  return encoded ? (prefix.endsWith("-") ? `${prefix}${encoded}` : `${prefix}-${encoded}`) : prefix;
43
43
  }
44
44
 
45
- function getDefaultSessionDirName(cwd: string): { encodedDirName: string; resolvedCwd: string } {
45
+ /**
46
+ * Reconstruct the short-lived hashed session dir name used by 17.2.5-17.2.8
47
+ * (reverted PR #7397): `<scope>-<readable>-<sha256hex>` keyed by the canonical
48
+ * cwd. Kept only so {@link migrateHashedSessionDir} can recover sessions
49
+ * stranded when 17.2.9 restored the legacy names without a reverse migration.
50
+ */
51
+ function encodeHashedSessionDirName(canonicalCwd: string, scope: "home" | "tmp" | "abs"): string {
52
+ const normalized = canonicalCwd.replaceAll("\\", "/");
53
+ const readable = path
54
+ .basename(canonicalCwd)
55
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
56
+ .replace(/^-+|-+$/g, "")
57
+ .slice(-80);
58
+ const digest = Bun.SHA256.hash(normalized, "hex");
59
+ return `${scope}-${readable || "project"}-${digest}`;
60
+ }
61
+
62
+ function getDefaultSessionDirName(cwd: string): {
63
+ encodedDirName: string;
64
+ hashedDirName: string;
65
+ resolvedCwd: string;
66
+ } {
46
67
  const resolvedCwd = path.resolve(cwd);
47
68
  const canonicalCwd = resolveEquivalentPath(resolvedCwd);
48
69
  const home = os.homedir();
@@ -51,13 +72,19 @@ function getDefaultSessionDirName(cwd: string): { encodedDirName: string; resolv
51
72
  const canonicalTempRoot = resolveEquivalentPath(tempRoot);
52
73
  const homeRelative = path.relative(canonicalHome, canonicalCwd);
53
74
  const tempRelative = path.relative(canonicalTempRoot, canonicalCwd);
54
- const encodedDirName =
55
- homeRelative === "" || (!homeRelative.startsWith("..") && !path.isAbsolute(homeRelative))
56
- ? encodeRelativeSessionDirName("-", homeRelative)
57
- : tempRelative === "" || (!tempRelative.startsWith("..") && !path.isAbsolute(tempRelative))
58
- ? encodeRelativeSessionDirName("-tmp", tempRelative)
59
- : encodeLegacyAbsoluteSessionDirName(canonicalCwd);
60
- return { encodedDirName, resolvedCwd };
75
+ let encodedDirName: string;
76
+ let scope: "home" | "tmp" | "abs";
77
+ if (homeRelative === "" || (!homeRelative.startsWith("..") && !path.isAbsolute(homeRelative))) {
78
+ encodedDirName = encodeRelativeSessionDirName("-", homeRelative);
79
+ scope = "home";
80
+ } else if (tempRelative === "" || (!tempRelative.startsWith("..") && !path.isAbsolute(tempRelative))) {
81
+ encodedDirName = encodeRelativeSessionDirName("-tmp", tempRelative);
82
+ scope = "tmp";
83
+ } else {
84
+ encodedDirName = encodeLegacyAbsoluteSessionDirName(canonicalCwd);
85
+ scope = "abs";
86
+ }
87
+ return { encodedDirName, hashedDirName: encodeHashedSessionDirName(canonicalCwd, scope), resolvedCwd };
61
88
  }
62
89
 
63
90
  /**
@@ -121,6 +148,26 @@ function migrateLegacyAbsoluteSessionDir(cwd: string, sessionDir: string, sessio
121
148
  }
122
149
  }
123
150
 
151
+ /**
152
+ * Migrate a 17.2.5-17.2.8 hashed session dir back into its legacy path-based
153
+ * directory. The 17.2.9 revert restored the legacy names but dropped migration,
154
+ * stranding sessions written under the hashed scheme (issue #7677). Best-effort.
155
+ */
156
+ function migrateHashedSessionDir(hashedDirName: string, sessionDir: string, sessionsRoot: string): void {
157
+ const hashedDir = path.join(sessionsRoot, hashedDirName);
158
+ if (hashedDir === sessionDir || !fs.existsSync(hashedDir)) return;
159
+
160
+ try {
161
+ migrateSessionDirPath(hashedDir, sessionDir);
162
+ } catch (error) {
163
+ logger.warn("Failed to migrate hashed session directory", {
164
+ oldPath: hashedDir,
165
+ newPath: sessionDir,
166
+ error: String(error),
167
+ });
168
+ }
169
+ }
170
+
124
171
  export function resolveManagedSessionRoot(sessionDir: string, cwd: string): string | undefined {
125
172
  const currentDirName = path.basename(sessionDir);
126
173
  const { encodedDirName } = getDefaultSessionDirName(cwd);
@@ -140,10 +187,11 @@ export function computeDefaultSessionDir(
140
187
  storage: SessionStorage,
141
188
  sessionsRoot: string = getSessionsDir(),
142
189
  ): string {
143
- const { encodedDirName, resolvedCwd } = getDefaultSessionDirName(cwd);
190
+ const { encodedDirName, hashedDirName, resolvedCwd } = getDefaultSessionDirName(cwd);
144
191
  migrateHomeSessionDirs(sessionsRoot);
145
192
  const sessionDir = path.join(sessionsRoot, encodedDirName);
146
193
  migrateLegacyAbsoluteSessionDir(resolvedCwd, sessionDir, sessionsRoot);
194
+ migrateHashedSessionDir(hashedDirName, sessionDir, sessionsRoot);
147
195
  storage.ensureDirSync(sessionDir);
148
196
  return sessionDir;
149
197
  }
@@ -7,7 +7,7 @@ import { formatModelString } from "../config/model-resolver";
7
7
  import type { Settings, SkillsSettings } from "../config/settings";
8
8
  import type { CustomTool, CustomToolContext } from "../extensibility/custom-tools/types";
9
9
  import { CustomToolAdapter } from "../extensibility/custom-tools/wrapper";
10
- import type { ExtensionRunner } from "../extensibility/extensions";
10
+ import type { ExtensionRunner, SourceInfo, ToolInfo } from "../extensibility/extensions";
11
11
  import { ExtensionToolWrapper } from "../extensibility/extensions/wrapper";
12
12
  import { loadSkills, type Skill, type SkillWarning, setActiveSkills } from "../extensibility/skills";
13
13
  import { type LocalProtocolOptions, XD_URL_PREFIX } from "../internal-urls";
@@ -198,6 +198,15 @@ export class SessionTools {
198
198
  #presentationPinnedToolNames: ReadonlySet<string> | undefined;
199
199
  #runtimeSelectedToolNames: ReadonlySet<string> | undefined;
200
200
  #baseSystemPrompt: string[];
201
+ /**
202
+ * Per-turn system prompt returned by a `before_agent_start` extension hook
203
+ * ("replace the system prompt for this turn"). While set, base-prompt
204
+ * rebuilds keep this override on the agent instead of the rebuilt base, so a
205
+ * rebuild landing in the prompt window (compaction/promotion, memory
206
+ * promotion, MCP/RPC tool refresh, hindsight MM-TTL refresh) cannot silently
207
+ * drop it before the request. Cleared when the turn ends.
208
+ */
209
+ #turnSystemPromptOverride: string[] | undefined;
201
210
  #lastAppliedToolSignature: string | undefined;
202
211
  /**
203
212
  * `xd://` device names the current base system prompt renders in its catalog
@@ -262,6 +271,31 @@ export class SessionTools {
262
271
  this.#baseSystemPrompt = prompt;
263
272
  }
264
273
 
274
+ /**
275
+ * Pushes `base` to the agent as the effective system prompt, unless an active
276
+ * per-turn {@link #turnSystemPromptOverride} takes precedence. Every base
277
+ * rebuild applies its result through here so a mid-turn rebuild preserves the
278
+ * override.
279
+ */
280
+ #applyAgentSystemPrompt(base: string[]): void {
281
+ this.#host.agent.setSystemPrompt(this.#turnSystemPromptOverride ?? base);
282
+ }
283
+
284
+ /**
285
+ * Registers the per-turn `before_agent_start` system-prompt override and
286
+ * applies it to the agent. Base rebuilds during the turn preserve it until
287
+ * {@link clearTurnSystemPromptOverride}.
288
+ */
289
+ setTurnSystemPromptOverride(prompt: string[]): void {
290
+ this.#turnSystemPromptOverride = prompt;
291
+ this.#host.agent.setSystemPrompt(prompt);
292
+ }
293
+
294
+ /** Drops the active per-turn override; later rebuilds fall back to the base prompt. */
295
+ clearTurnSystemPromptOverride(): void {
296
+ this.#turnSystemPromptOverride = undefined;
297
+ }
298
+
265
299
  /** Skills currently rendered into the system prompt. */
266
300
  get skills(): Skill[] {
267
301
  return this.#skills;
@@ -333,6 +367,33 @@ export class SessionTools {
333
367
  return Array.from(this.#toolRegistry.keys());
334
368
  }
335
369
 
370
+ /**
371
+ * Full metadata for every registered tool, including source provenance.
372
+ *
373
+ * Backs the `getAllTools()` ExtensionAPI method. Returns {@link ToolInfo}
374
+ * objects (not bare names) so extensions authored against upstream
375
+ * `@earendil-works/pi-coding-agent` — which promises `ToolInfo[]` — can read
376
+ * `sourceInfo.source` unchanged.
377
+ */
378
+ getAllToolInfos(): ToolInfo[] {
379
+ return Array.from(this.#toolRegistry, ([name, tool]) => {
380
+ const source = this.#builtInToolNames.has(name)
381
+ ? "builtin"
382
+ : isMCPToolName(name)
383
+ ? "mcp"
384
+ : this.#rpcHostToolNames.has(name)
385
+ ? "sdk"
386
+ : "extension";
387
+ const sourceInfo: SourceInfo = {
388
+ path: `<${source}:${name}>`,
389
+ source,
390
+ scope: "temporary",
391
+ origin: "top-level",
392
+ };
393
+ return { name, description: tool.description, parameters: tool.parameters, sourceInfo };
394
+ });
395
+ }
396
+
336
397
  #wrapRuntimeTool(tool: AgentTool): AgentTool {
337
398
  const wrapped = wrapToolWithMetaNotice(tool);
338
399
  const extensionRunner = this.#host.extensionRunner();
@@ -663,7 +724,7 @@ export class SessionTools {
663
724
  if (this.#lastAppliedToolSignature !== undefined) this.#host.clearInheritedProviderPromptCacheKey();
664
725
  this.#baseSystemPrompt = rebuiltSystemPrompt;
665
726
  this.#host.clearMemoryPromotionSnapshot();
666
- this.#host.agent.setSystemPrompt(this.#baseSystemPrompt);
727
+ this.#applyAgentSystemPrompt(this.#baseSystemPrompt);
667
728
  this.#lastAppliedToolSignature = rebuiltSignature;
668
729
  this.#promptModelKey = this.#currentPromptModelKey();
669
730
  this.#basePromptXdevNames = new Set(rebuiltXdevCatalogNames);
@@ -1103,7 +1164,7 @@ export class SessionTools {
1103
1164
  ) {
1104
1165
  this.#host.clearInheritedProviderPromptCacheKey();
1105
1166
  }
1106
- this.#host.agent.setSystemPrompt(this.#baseSystemPrompt);
1167
+ this.#applyAgentSystemPrompt(this.#baseSystemPrompt);
1107
1168
  this.#promptModelKey = this.#currentPromptModelKey();
1108
1169
  // Refresh the cached signature so a subsequent `applyActiveToolsByName` with
1109
1170
  // the same tool set does not re-rebuild on top of the explicit refresh we
@@ -1143,7 +1204,7 @@ export class SessionTools {
1143
1204
  this.#host.captureMemoryPromotionSnapshot(previousBaseSystemPrompt);
1144
1205
  const stablePrompt = [...previousBaseSystemPrompt, injected];
1145
1206
  this.#baseSystemPrompt = stablePrompt;
1146
- this.#host.agent.setSystemPrompt(stablePrompt);
1207
+ this.#applyAgentSystemPrompt(stablePrompt);
1147
1208
  return stablePrompt;
1148
1209
  } catch (err) {
1149
1210
  logger.debug("Memory backend beforeAgentStartPrompt failed", {
@@ -27,6 +27,15 @@ const QUESTION_PROMPT_RE =
27
27
  const USER_DIRECTED_PROMPT_RE = /\b(?:you|your|we|our)\b/i;
28
28
  const USER_RESPONSE_CUE_RE =
29
29
  /^(?:please\s+)?(?:confirm|reply|choose|pick|decide|advise)\b|^(?:please\s+)?answer\b|^(?:please\s+)?(?:let\s+me\s+know|tell\s+me)\b/i;
30
+ /**
31
+ * A trailing question mark is the universal signal that a line is a question, but
32
+ * the English word/pronoun gates above exist to filter incidental "?" out of prose
33
+ * (e.g. a TypeScript `foo?: string` tail). Non-English text has no cheap word list,
34
+ * yet any non-ASCII character in a "?"/"?"-terminated line reliably marks it as
35
+ * genuine prose — CJK/Japanese/Korean, Spanish `¿…?`, accented Latin — so treat it
36
+ * as a real user-directed question. Fixes non-Latin prompts going undetected (#7803).
37
+ */
38
+ const NON_ASCII_TEXT_RE = /[^\x00-\x7F]/;
30
39
 
31
40
  interface PromptLine {
32
41
  text: string;
@@ -361,7 +370,8 @@ function isQuestionPromptLine(line: string): boolean {
361
370
  return (
362
371
  candidate.hadPromptLabel ||
363
372
  QUESTION_PROMPT_RE.test(candidate.text) ||
364
- USER_DIRECTED_PROMPT_RE.test(candidate.text)
373
+ USER_DIRECTED_PROMPT_RE.test(candidate.text) ||
374
+ NON_ASCII_TEXT_RE.test(candidate.text)
365
375
  );
366
376
  }
367
377
 
@@ -28,7 +28,7 @@ import type { ModelRegistry } from "../config/model-registry";
28
28
  import { formatModelStringWithRouting, resolveModelOverride } from "../config/model-resolver";
29
29
 
30
30
  import type { Settings } from "../config/settings";
31
- import type { RecoveredRetryError } from "../extensibility/shared-events";
31
+ import type { RetryErrorUpdate } from "../extensibility/shared-events";
32
32
  import emptyStopRetryTemplate from "../prompts/system/empty-stop-retry.md" with { type: "text" };
33
33
  import thinkingLoopRedirectTemplate from "../prompts/system/thinking-loop-redirect.md" with { type: "text" };
34
34
  import unexpectedStopRetryTemplate from "../prompts/system/unexpected-stop-retry.md" with { type: "text" };
@@ -161,7 +161,7 @@ export interface TurnRecoveryOptions {
161
161
  initialRetryFallback?: InitialRetryFallbackState;
162
162
  }
163
163
 
164
- type PendingRecoveredRetryError = {
164
+ type PendingRetryError = {
165
165
  entryId: string;
166
166
  persistenceKey: string;
167
167
  recovery: AssistantRetryRecoveryKind;
@@ -184,7 +184,7 @@ export class TurnRecovery {
184
184
  #retryResolve: (() => void) | undefined;
185
185
  #activeRetryFallback: ActiveRetryFallbackState | undefined;
186
186
  #usageReserveApprovedSelector: string | undefined;
187
- #pendingRecoveredRetryErrors: PendingRecoveredRetryError[] = [];
187
+ #pendingRetryErrors: PendingRetryError[] = [];
188
188
  #usageLimitOutcomes = new WeakMap<AssistantMessage, Promise<UsageLimitOutcome>>();
189
189
  #emptyStopRetryCount = 0;
190
190
  #unexpectedStopRetryCount = 0;
@@ -250,14 +250,17 @@ export class TurnRecovery {
250
250
  role: this.#activeRetryFallback.role,
251
251
  });
252
252
  }
253
- const recoveredErrors = await this.#markPendingRecoveredRetryErrors(message);
253
+ const retryErrors = await this.#markPendingRetryErrors({
254
+ status: "recovered",
255
+ supersedingMessage: message,
256
+ });
254
257
  await this.#host.emitSessionEvent({
255
258
  type: "auto_retry_end",
256
259
  success: true,
257
260
  attempt: this.#retryAttempt,
258
- recoveredErrors,
261
+ retryErrors,
259
262
  });
260
- this.#clearPendingRecoveredRetryErrors();
263
+ this.#clearPendingRetryErrors();
261
264
  this.#retryAttempt = 0;
262
265
  }
263
266
 
@@ -272,7 +275,7 @@ export class TurnRecovery {
272
275
  attempt,
273
276
  finalError: message.errorMessage,
274
277
  });
275
- this.#clearPendingRecoveredRetryErrors();
278
+ this.#clearPendingRetryErrors();
276
279
  }
277
280
 
278
281
  /** Persists an otherwise skipped terminal empty error turn. */
@@ -305,8 +308,13 @@ export class TurnRecovery {
305
308
  return this.#runRecoveryCompactionWithRollback(reason, message, allowDefer, options);
306
309
  }
307
310
 
308
- /** Restores the configured primary after fallback cooldown expiry. */
309
- maybeRestoreRetryFallbackPrimary(): Promise<void> {
311
+ /**
312
+ * Restores the configured primary after fallback cooldown expiry.
313
+ * @returns true when the active model was actually switched back to the
314
+ * primary, so callers can re-run the pre-send context-fit check against the
315
+ * reverted (possibly smaller) window before issuing the next request.
316
+ */
317
+ maybeRestoreRetryFallbackPrimary(): Promise<boolean> {
310
318
  return this.#maybeRestoreRetryFallbackPrimary();
311
319
  }
312
320
 
@@ -381,8 +389,8 @@ export class TurnRecovery {
381
389
  }
382
390
  }
383
391
 
384
- #clearPendingRecoveredRetryErrors(): void {
385
- this.#pendingRecoveredRetryErrors = [];
392
+ #clearPendingRetryErrors(): void {
393
+ this.#pendingRetryErrors = [];
386
394
  }
387
395
 
388
396
  /**
@@ -432,7 +440,7 @@ export class TurnRecovery {
432
440
  return parts.join("; ");
433
441
  }
434
442
 
435
- async #recordPendingRecoveredRetryError(
443
+ async #recordPendingRetryError(
436
444
  message: AssistantMessage,
437
445
  id: number,
438
446
  options: { switchedCredential: boolean; switchedModel: boolean; delayMs: number },
@@ -451,11 +459,11 @@ export class TurnRecovery {
451
459
  break;
452
460
  }
453
461
  if (!branchEntry) return;
454
- if (this.#pendingRecoveredRetryErrors.some(error => error.entryId === branchEntry.id)) return;
462
+ if (this.#pendingRetryErrors.some(error => error.entryId === branchEntry.id)) return;
455
463
  const rateLimited = AIError.is(id, AIError.Flag.UsageLimit);
456
464
  const recovery = this.#retryRecoveryKind(id, options.switchedCredential, options.switchedModel, options.delayMs);
457
465
  const note = this.#retryRecoveryNote(recovery, rateLimited);
458
- this.#pendingRecoveredRetryErrors.push({
466
+ this.#pendingRetryErrors.push({
459
467
  entryId: branchEntry.id,
460
468
  persistenceKey,
461
469
  recovery,
@@ -464,24 +472,17 @@ export class TurnRecovery {
464
472
  });
465
473
  }
466
474
 
467
- async #markPendingRecoveredRetryErrors(supersedingMessage: AssistantMessage): Promise<RecoveredRetryError[]> {
468
- if (this.#pendingRecoveredRetryErrors.length === 0) return [];
475
+ async #markPendingRetryErrors(
476
+ completion: { status: "recovered"; supersedingMessage: AssistantMessage } | { status: "superseded" },
477
+ ): Promise<RetryErrorUpdate[]> {
478
+ if (this.#pendingRetryErrors.length === 0) return [];
469
479
  const branch = this.#host.sessionManager.getBranch();
470
480
  const branchById = new Map<string, SessionEntry>();
471
481
  for (const entry of branch) {
472
482
  branchById.set(entry.id, entry);
473
483
  }
474
- const recoveredAt = new Date().toISOString();
475
- const supersededBy: AssistantRetryRecovery["supersededBy"] = {
476
- timestamp: supersedingMessage.timestamp,
477
- provider: supersedingMessage.provider,
478
- model: supersedingMessage.model,
479
- };
480
- if (supersedingMessage.responseId) {
481
- supersededBy.responseId = supersedingMessage.responseId;
482
- }
483
- const recoveredErrors: RecoveredRetryError[] = [];
484
- for (const pending of this.#pendingRecoveredRetryErrors) {
484
+ const retryErrors: RetryErrorUpdate[] = [];
485
+ for (const pending of this.#pendingRetryErrors) {
485
486
  let entry = branchById.get(pending.entryId);
486
487
  if (entry?.type !== "message" || entry.message.role !== "assistant") {
487
488
  entry = branch
@@ -495,27 +496,45 @@ export class TurnRecovery {
495
496
  );
496
497
  }
497
498
  if (entry?.type !== "message" || entry.message.role !== "assistant") continue;
498
- const retryRecovery: AssistantRetryRecovery = {
499
- kind: "auto-retry",
500
- status: "recovered",
501
- attempt: pending.attempt,
502
- recoveredAt,
503
- recovery: pending.recovery,
504
- note: pending.note,
505
- supersededBy,
506
- };
499
+ let retryRecovery: AssistantRetryRecovery;
500
+ if (completion.status === "recovered") {
501
+ retryRecovery = {
502
+ kind: "auto-retry",
503
+ status: "recovered",
504
+ attempt: pending.attempt,
505
+ recoveredAt: new Date().toISOString(),
506
+ recovery: pending.recovery,
507
+ note: pending.note,
508
+ supersededBy: {
509
+ timestamp: completion.supersedingMessage.timestamp,
510
+ ...(completion.supersedingMessage.responseId === undefined
511
+ ? {}
512
+ : { responseId: completion.supersedingMessage.responseId }),
513
+ provider: completion.supersedingMessage.provider,
514
+ model: completion.supersedingMessage.model,
515
+ },
516
+ };
517
+ } else {
518
+ retryRecovery = {
519
+ kind: "auto-retry",
520
+ status: "superseded",
521
+ attempt: pending.attempt,
522
+ recovery: pending.recovery,
523
+ note: pending.note,
524
+ };
525
+ }
507
526
  entry.message.retryRecovery = retryRecovery;
508
- recoveredErrors.push({
527
+ retryErrors.push({
509
528
  entryId: entry.id,
510
529
  persistenceKey: pending.persistenceKey,
511
530
  note: retryRecovery.note,
512
531
  retryRecovery,
513
532
  });
514
533
  }
515
- if (recoveredErrors.length > 0) {
534
+ if (retryErrors.length > 0) {
516
535
  await this.#host.sessionManager.rewriteEntries();
517
536
  }
518
- return recoveredErrors;
537
+ return retryErrors;
519
538
  }
520
539
 
521
540
  async #handleEmptyAssistantStop(assistantMessage: AssistantMessage): Promise<boolean> {
@@ -547,7 +566,7 @@ export class TurnRecovery {
547
566
  attempt: this.#retryAttempt > 0 ? this.#retryAttempt : attempts,
548
567
  finalError,
549
568
  });
550
- this.#clearPendingRecoveredRetryErrors();
569
+ this.#clearPendingRetryErrors();
551
570
  this.#retryAttempt = 0;
552
571
  this.resolveRetry();
553
572
  // A zero-content turn carries no transcript value, while its provider usage
@@ -1302,7 +1321,7 @@ export class TurnRecovery {
1302
1321
  return false;
1303
1322
  }
1304
1323
  if (this.#host.model() !== candidate) return false;
1305
- this.#host.sessionManager.appendModelChange(candidateSelector, EPHEMERAL_MODEL_CHANGE_ROLE);
1324
+ this.#host.sessionManager.appendModelChange(candidateSelector, EPHEMERAL_MODEL_CHANGE_ROLE, true);
1306
1325
  this.#host.settings.getStorage()?.recordModelUsage(candidateSelector);
1307
1326
  this.#host.setThinkingLevel(nextThinkingLevel);
1308
1327
  if (!this.#activeRetryFallback) {
@@ -1422,7 +1441,7 @@ export class TurnRecovery {
1422
1441
  if (!apiKey) return false;
1423
1442
  const baseSelector = formatModelStringWithRouting(baseModel);
1424
1443
  await this.#host.setModelWithProviderSessionReset(baseModel);
1425
- this.#host.sessionManager.appendModelChange(baseSelector, EPHEMERAL_MODEL_CHANGE_ROLE);
1444
+ this.#host.sessionManager.appendModelChange(baseSelector, EPHEMERAL_MODEL_CHANGE_ROLE, true);
1426
1445
  this.#host.settings.getStorage()?.recordModelUsage(baseSelector);
1427
1446
  await this.#host.emitSessionEvent({
1428
1447
  type: "retry_fallback_applied",
@@ -1433,10 +1452,10 @@ export class TurnRecovery {
1433
1452
  return true;
1434
1453
  }
1435
1454
 
1436
- async #maybeRestoreRetryFallbackPrimary(): Promise<void> {
1437
- if (!this.#activeRetryFallback) return;
1438
- if (this.#activeRetryFallback.pinned) return;
1439
- if (this.#getRetryFallbackRevertPolicy() !== "cooldown-expiry") return;
1455
+ async #maybeRestoreRetryFallbackPrimary(): Promise<boolean> {
1456
+ if (!this.#activeRetryFallback) return false;
1457
+ if (this.#activeRetryFallback.pinned) return false;
1458
+ if (this.#getRetryFallbackRevertPolicy() !== "cooldown-expiry") return false;
1440
1459
 
1441
1460
  const {
1442
1461
  originalSelector: originalSelectorRaw,
@@ -1446,19 +1465,19 @@ export class TurnRecovery {
1446
1465
  const originalSelector = parseRetryFallbackSelector(originalSelectorRaw, this.#host.modelRegistry);
1447
1466
  if (!originalSelector) {
1448
1467
  this.clearActiveRetryFallback();
1449
- return;
1468
+ return false;
1450
1469
  }
1451
1470
 
1452
1471
  const currentModel = this.#host.model();
1453
- if (!currentModel) return;
1472
+ if (!currentModel) return false;
1454
1473
  const currentSelector = formatRetryFallbackSelector(currentModel, this.#host.thinkingLevel());
1455
1474
  if (currentSelector === originalSelector.raw) {
1456
1475
  if (!this.isRetryFallbackSelectorSuppressed(originalSelector)) {
1457
1476
  this.clearActiveRetryFallback();
1458
1477
  }
1459
- return;
1478
+ return false;
1460
1479
  }
1461
- if (this.isRetryFallbackSelectorSuppressed(originalSelector)) return;
1480
+ if (this.isRetryFallbackSelectorSuppressed(originalSelector)) return false;
1462
1481
 
1463
1482
  const resolvedPrimary = resolveModelOverride(
1464
1483
  [originalSelector.raw],
@@ -1467,9 +1486,9 @@ export class TurnRecovery {
1467
1486
  );
1468
1487
  const primaryModel =
1469
1488
  resolvedPrimary.model ?? this.#host.modelRegistry.find(originalSelector.provider, originalSelector.id);
1470
- if (!primaryModel) return;
1489
+ if (!primaryModel) return false;
1471
1490
  const apiKey = await this.#host.modelRegistry.getApiKey(primaryModel, this.#host.sessionId());
1472
- if (!apiKey) return;
1491
+ if (!apiKey) return false;
1473
1492
 
1474
1493
  const currentThinkingLevel = this.#host.configuredThinkingLevel();
1475
1494
  const thinkingToApply =
@@ -1480,6 +1499,7 @@ export class TurnRecovery {
1480
1499
  this.#host.settings.getStorage()?.recordModelUsage(primarySelector);
1481
1500
  this.#host.setThinkingLevel(thinkingToApply);
1482
1501
  this.clearActiveRetryFallback();
1502
+ return true;
1483
1503
  }
1484
1504
 
1485
1505
  #parseRetryAfterMsFromError(errorMessage: string): number | undefined {
@@ -1581,27 +1601,27 @@ export class TurnRecovery {
1581
1601
 
1582
1602
  const errorMessage = message.errorMessage || "Unknown error";
1583
1603
  const id = this.#classifyRetryMessage(message);
1604
+ const rateLimitReason = parseRateLimitReason(errorMessage);
1584
1605
  const staleOpenAIResponsesReplayError = AIError.is(id, AIError.Flag.StaleResponsesItem);
1585
1606
  const recordedUsageLimitOutcome = await this.#usageLimitOutcomes.get(message);
1586
1607
  const parsedRetryAfterMs = this.#parseRetryAfterMsFromError(errorMessage);
1587
1608
  let delayMs = staleOpenAIResponsesReplayError
1588
1609
  ? 0
1589
1610
  : calculateRetryBackoffDelayMs(retrySettings.baseDelayMs, this.#retryAttempt);
1590
- // Concurrency caps shed-and-backoff (5s) rather than burning a sibling
1591
- // credential, so the usage-limit rotation branch below is deliberately
1592
- // skipped for them. Apply the reason-based backoff to the transient
1593
- // same-model retry path too otherwise the default exponential base
1594
- // (≈500ms) re-hits the cap immediately and burns the retry budget while
1595
- // the concurrency slot stays occupied. A categorical 402 billing cap whose
1596
- // body merely mentions concurrency is still a usage limit (handled below),
1597
- // so gate on the flag matching the rotation decision.
1611
+ // Transient rate/concurrency caps stay on the same credential, but must
1612
+ // honor their reason-specific windows. The default exponential base
1613
+ // (≈500ms, capped at 8s) otherwise re-hits the cap and burns the retry
1614
+ // budget before either window can clear. An explicit provider
1615
+ // retry-after is authoritative in both directions, so the heuristic
1616
+ // window only applies when the error carries no parsed timing.
1598
1617
  if (
1599
1618
  !staleOpenAIResponsesReplayError &&
1600
1619
  !AIError.is(id, AIError.Flag.UsageLimit) &&
1601
- parseRateLimitReason(errorMessage) === "CONCURRENT_LIMIT"
1620
+ parsedRetryAfterMs === undefined &&
1621
+ (rateLimitReason === "CONCURRENT_LIMIT" || rateLimitReason === "RATE_LIMIT_EXCEEDED")
1602
1622
  ) {
1603
- const concurrentBackoffMs = calculateRateLimitBackoffMs("CONCURRENT_LIMIT");
1604
- if (concurrentBackoffMs > delayMs) delayMs = concurrentBackoffMs;
1623
+ const reasonBackoffMs = calculateRateLimitBackoffMs(rateLimitReason);
1624
+ if (reasonBackoffMs > delayMs) delayMs = reasonBackoffMs;
1605
1625
  }
1606
1626
  let switchedCredential = false;
1607
1627
  let switchedModel = false;
@@ -1677,16 +1697,18 @@ export class TurnRecovery {
1677
1697
 
1678
1698
  if (retryBudgetExhausted) {
1679
1699
  if (!switchedModel) {
1700
+ const attempt = this.#retryAttempt - 1;
1701
+ message.errorMessage = `Retry budget exhausted after ${attempt} ${attempt === 1 ? "retry" : "retries"}: ${errorMessage}`;
1680
1702
  await this.persistTerminalEmptyErrorTurn(message);
1681
- // Max retries exceeded and no fallback model to switch to: emit
1682
- // final failure and reset.
1703
+ const retryErrors = await this.#markPendingRetryErrors({ status: "superseded" });
1683
1704
  await this.#host.emitSessionEvent({
1684
1705
  type: "auto_retry_end",
1685
1706
  success: false,
1686
- attempt: this.#retryAttempt - 1,
1687
- finalError: message.errorMessage,
1707
+ attempt,
1708
+ finalError: errorMessage,
1709
+ retryErrors,
1688
1710
  });
1689
- this.#clearPendingRecoveredRetryErrors();
1711
+ this.#clearPendingRetryErrors();
1690
1712
  this.#retryAttempt = 0;
1691
1713
  this.resolveRetry(); // Resolve so waitForRetry() completes
1692
1714
  return false;
@@ -1711,7 +1733,7 @@ export class TurnRecovery {
1711
1733
  attempt: this.#retryAttempt - 1,
1712
1734
  finalError: errorMessage,
1713
1735
  });
1714
- this.#clearPendingRecoveredRetryErrors();
1736
+ this.#clearPendingRetryErrors();
1715
1737
  }
1716
1738
  this.#retryAttempt = 0;
1717
1739
  this.resolveRetry();
@@ -1736,7 +1758,7 @@ export class TurnRecovery {
1736
1758
  attempt: this.#retryAttempt - 1,
1737
1759
  finalError: errorMessage,
1738
1760
  });
1739
- this.#clearPendingRecoveredRetryErrors();
1761
+ this.#clearPendingRetryErrors();
1740
1762
  }
1741
1763
  this.#retryAttempt = 0;
1742
1764
  this.resolveRetry();
@@ -1761,12 +1783,12 @@ export class TurnRecovery {
1761
1783
  attempt,
1762
1784
  finalError: `Provider requested ${delayMs}ms wait, exceeds retry.maxDelayMs (${maxDelayMs}ms). Original error: ${errorMessage}`,
1763
1785
  });
1764
- this.#clearPendingRecoveredRetryErrors();
1786
+ this.#clearPendingRetryErrors();
1765
1787
  this.resolveRetry();
1766
1788
  return false;
1767
1789
  }
1768
1790
 
1769
- await this.#recordPendingRecoveredRetryError(message, id, { switchedCredential, switchedModel, delayMs });
1791
+ await this.#recordPendingRetryError(message, id, { switchedCredential, switchedModel, delayMs });
1770
1792
 
1771
1793
  await this.#host.emitSessionEvent({
1772
1794
  type: "auto_retry_start",
@@ -1808,7 +1830,7 @@ export class TurnRecovery {
1808
1830
  attempt,
1809
1831
  finalError: "Retry cancelled",
1810
1832
  });
1811
- this.#clearPendingRecoveredRetryErrors();
1833
+ this.#clearPendingRetryErrors();
1812
1834
  this.resolveRetry();
1813
1835
  return false;
1814
1836
  }
@@ -1881,7 +1903,7 @@ export class TurnRecovery {
1881
1903
  attempt,
1882
1904
  finalError: `Retry continuation failed locally: ${localError}. Original error: ${message.errorMessage ?? "Unknown error"}`,
1883
1905
  });
1884
- this.#clearPendingRecoveredRetryErrors();
1906
+ this.#clearPendingRetryErrors();
1885
1907
  this.resolveRetry();
1886
1908
  }
1887
1909
 
@@ -1,4 +1,4 @@
1
- import type { AvailableCommand } from "@agentclientprotocol/sdk";
1
+ import type { AvailableCommand } from "@oh-my-pi/pi-utils/acp";
2
2
  import { BUILTIN_SLASH_COMMANDS_INTERNAL, lookupBuiltinSlashCommand } from "./builtin-registry";
3
3
  import { parseSlashCommand } from "./helpers/parse";
4
4
  import type { AcpBuiltinSlashCommandResult, SlashCommandRuntime } from "./types";
@@ -1,4 +1,4 @@
1
- import type { AvailableCommand } from "@agentclientprotocol/sdk";
1
+ import type { AvailableCommand } from "@oh-my-pi/pi-utils/acp";
2
2
  import type { SkillsSettings } from "../config/settings";
3
3
  import type { LoadedCustomCommand } from "../extensibility/custom-commands";
4
4
  import type { ExtensionRunner } from "../extensibility/extensions";