@code-yeongyu/senpi 2026.9.7-2 → 2026.9.8

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 (199) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/core/agent-session.d.ts +1 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +41 -18
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/dynamic-prompt/working-task.js +1 -1
  7. package/dist/core/dynamic-prompt/working-task.js.map +1 -1
  8. package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -1
  9. package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -2
  10. package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -1
  11. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.d.ts +2 -0
  12. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.d.ts.map +1 -1
  13. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.js +10 -25
  14. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.js.map +1 -1
  15. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -0
  16. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  17. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +110 -28
  18. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  19. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/compaction/index.js +52 -9
  21. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  22. package/dist/core/extensions/builtin/goal/agent-end-continuation.d.ts.map +1 -1
  23. package/dist/core/extensions/builtin/goal/agent-end-continuation.js +11 -0
  24. package/dist/core/extensions/builtin/goal/agent-end-continuation.js.map +1 -1
  25. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +17 -11
  26. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/goal/cache-warm.js +17 -11
  28. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  29. package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
  30. package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/goal/continuation.js +17 -2
  32. package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
  33. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +1 -0
  35. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
  36. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/goal/monitor-continuation.js +6 -4
  38. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  39. package/dist/core/extensions/builtin/gpt-account.d.ts +5 -1
  40. package/dist/core/extensions/builtin/gpt-account.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/gpt-account.js +9 -26
  42. package/dist/core/extensions/builtin/gpt-account.js.map +1 -1
  43. package/dist/core/extensions/builtin/monitor-state-event.d.ts +18 -0
  44. package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/monitor-state-event.js +23 -0
  46. package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
  47. package/dist/core/extensions/builtin/oauth-login-interaction.d.ts +24 -0
  48. package/dist/core/extensions/builtin/oauth-login-interaction.d.ts.map +1 -0
  49. package/dist/core/extensions/builtin/oauth-login-interaction.js +77 -0
  50. package/dist/core/extensions/builtin/oauth-login-interaction.js.map +1 -0
  51. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +1 -1
  52. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js.map +1 -1
  53. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +1 -1
  54. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js.map +1 -1
  55. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts +14 -7
  56. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js +34 -13
  58. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js.map +1 -1
  59. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +6 -10
  60. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +14 -9
  62. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  63. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +15 -15
  64. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +49 -21
  66. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
  67. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +1 -1
  68. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js.map +1 -1
  69. package/dist/core/extensions/builtin/service-tier.d.ts +2 -0
  70. package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
  71. package/dist/core/extensions/builtin/service-tier.js +17 -1
  72. package/dist/core/extensions/builtin/service-tier.js.map +1 -1
  73. package/dist/core/extensions/builtin/terminal/durable-command.d.ts.map +1 -1
  74. package/dist/core/extensions/builtin/terminal/durable-command.js +3 -0
  75. package/dist/core/extensions/builtin/terminal/durable-command.js.map +1 -1
  76. package/dist/core/extensions/builtin/terminal/durable-file.d.ts.map +1 -1
  77. package/dist/core/extensions/builtin/terminal/durable-file.js +2 -0
  78. package/dist/core/extensions/builtin/terminal/durable-file.js.map +1 -1
  79. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  80. package/dist/core/extensions/builtin/terminal/extension.js +14 -2
  81. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  82. package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts.map +1 -1
  83. package/dist/core/extensions/builtin/terminal/monitor-notify.js +27 -5
  84. package/dist/core/extensions/builtin/terminal/monitor-notify.js.map +1 -1
  85. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +17 -0
  86. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  87. package/dist/core/extensions/builtin/terminal/monitor-registry.js +62 -7
  88. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  89. package/dist/core/extensions/builtin/terminal/notify.d.ts +2 -0
  90. package/dist/core/extensions/builtin/terminal/notify.d.ts.map +1 -1
  91. package/dist/core/extensions/builtin/terminal/notify.js +6 -1
  92. package/dist/core/extensions/builtin/terminal/notify.js.map +1 -1
  93. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +3 -1
  94. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  95. package/dist/core/extensions/builtin/terminal/session-bundle.js +13 -0
  96. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  97. package/dist/core/extensions/builtin/terminal/shared.d.ts +13 -2
  98. package/dist/core/extensions/builtin/terminal/shared.d.ts.map +1 -1
  99. package/dist/core/extensions/builtin/terminal/shared.js +15 -2
  100. package/dist/core/extensions/builtin/terminal/shared.js.map +1 -1
  101. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/terminal/tools/monitor.js +8 -1
  103. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  104. package/dist/core/extensions/builtin/tool-search/index.d.ts.map +1 -1
  105. package/dist/core/extensions/builtin/tool-search/index.js +1 -0
  106. package/dist/core/extensions/builtin/tool-search/index.js.map +1 -1
  107. package/dist/core/extensions/builtin/tool-search/service.d.ts +4 -0
  108. package/dist/core/extensions/builtin/tool-search/service.d.ts.map +1 -1
  109. package/dist/core/extensions/builtin/tool-search/service.js +11 -0
  110. package/dist/core/extensions/builtin/tool-search/service.js.map +1 -1
  111. package/dist/core/extensions/runner.d.ts +1 -0
  112. package/dist/core/extensions/runner.d.ts.map +1 -1
  113. package/dist/core/extensions/runner.js +8 -1
  114. package/dist/core/extensions/runner.js.map +1 -1
  115. package/dist/core/extensions/types.d.ts +9 -0
  116. package/dist/core/extensions/types.d.ts.map +1 -1
  117. package/dist/core/extensions/types.js.map +1 -1
  118. package/dist/core/high-reasoning-warning.d.ts.map +1 -1
  119. package/dist/core/high-reasoning-warning.js +4 -6
  120. package/dist/core/high-reasoning-warning.js.map +1 -1
  121. package/dist/core/sdk.d.ts +1 -1
  122. package/dist/core/sdk.d.ts.map +1 -1
  123. package/dist/core/sdk.js +25 -6
  124. package/dist/core/sdk.js.map +1 -1
  125. package/dist/core/settings-manager.js +1 -1
  126. package/dist/core/settings-manager.js.map +1 -1
  127. package/dist/core/settings-shapes.js.map +1 -1
  128. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  129. package/dist/modes/interactive/interactive-mode.js +1 -0
  130. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  131. package/docs/providers.md +5 -1
  132. package/docs/settings.md +1 -0
  133. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +14 -0
  134. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  135. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +17 -24
  136. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  137. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -2
  138. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  139. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts +7 -0
  140. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts.map +1 -1
  141. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js +19 -0
  142. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js.map +1 -1
  143. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
  144. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +24 -16
  145. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
  146. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +1 -1
  147. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
  148. package/node_modules/@earendil-works/pi-agent-core/dist/index.js +1 -1
  149. package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
  150. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  151. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  152. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +47 -178
  153. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  154. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.d.ts +3 -0
  155. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.d.ts.map +1 -0
  156. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.js +260 -0
  157. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.js.map +1 -0
  158. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  159. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +1 -0
  160. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  161. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  162. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +1 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  164. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.d.ts +40 -0
  165. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.d.ts.map +1 -0
  166. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.js +138 -0
  167. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.js.map +1 -0
  168. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.d.ts +2 -3
  169. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.d.ts.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js +42 -139
  171. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.d.ts +6 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.d.ts.map +1 -0
  174. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.js +29 -0
  175. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.js.map +1 -0
  176. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.d.ts +3 -0
  177. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.d.ts.map +1 -0
  178. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.js +20 -0
  179. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.js.map +1 -0
  180. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  182. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  184. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  185. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/recovery-stream-terminal.d.ts.map +1 -1
  186. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/recovery-stream-terminal.js +16 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/recovery-stream-terminal.js.map +1 -1
  188. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +7 -0
  189. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  190. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  191. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts +2 -1
  192. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts.map +1 -1
  193. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js +16 -0
  194. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js.map +1 -1
  195. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  196. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  197. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  198. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  199. package/package.json +7 -7
@@ -7,6 +7,13 @@ export declare class ProviderRetryWatchdogAbortError extends Error {
7
7
  readonly providerCause: string;
8
8
  constructor(providerCause: string);
9
9
  }
10
+ /**
11
+ * Marks a terminal message whose `toolUse` stop reason was demoted because the provider sent no
12
+ * tool call. Demotion rewrites the stop reason, so this diagnostic is the only surviving evidence
13
+ * that the turn was malformed rather than a clean stop; downstream recovery keys on it.
14
+ */
15
+ export declare const EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC = "empty_tool_use_terminal_state";
16
+ export declare function demoteToolUseWithoutToolCalls(message: AssistantMessage): AssistantMessage;
10
17
  export declare function promoteStopWithPendingToolCalls(message: AssistantMessage): AssistantMessage;
11
18
  export declare function shouldTerminateAssistantTurn(message: AssistantMessage): boolean;
12
19
  export declare function isStreamIdleTimeoutError(error: unknown): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-terminal-state.d.ts","sourceRoot":"","sources":["../src/assistant-terminal-state.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAI1B,KAAK,iBAAiB,EACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAWlD,KAAK,6BAA6B,GAAG,OAAO,CAAC,qBAAqB,EAAE;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,CAAC;AAEhG,qBAAa,+BAAgC,SAAQ,KAAK;IACzD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,aAAa,EAAE,MAAM;CAKjC;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAI3F;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE/E;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEhE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACvC,cAAc,EAAE,gBAAgB,GAAG,IAAI,EACvC,mBAAmB,EAAE,SAAS,iBAAiB,EAAE,GAC/C,OAAO,CAMT;AAED,wBAAgB,qCAAqC,CACpD,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAC/B,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,EACpE,KAAK,EAAE,OAAO,EACd,cAAc,EAAE,gBAAgB,GAAG,IAAI,GACrC,gBAAgB,CAiBlB;AAED,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,6BAA6B,GAClC,gBAAgB,CAKlB"}
1
+ {"version":3,"file":"assistant-terminal-state.d.ts","sourceRoot":"","sources":["../src/assistant-terminal-state.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAI1B,KAAK,iBAAiB,EACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAWlD,KAAK,6BAA6B,GAAG,OAAO,CAAC,qBAAqB,EAAE;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,CAAC;AAEhG,qBAAa,+BAAgC,SAAQ,KAAK;IACzD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,aAAa,EAAE,MAAM;CAKjC;AAED;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,kCAAkC,CAAC;AAElF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAWzF;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAI3F;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE/E;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEhE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACvC,cAAc,EAAE,gBAAgB,GAAG,IAAI,EACvC,mBAAmB,EAAE,SAAS,iBAAiB,EAAE,GAC/C,OAAO,CAMT;AAED,wBAAgB,qCAAqC,CACpD,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAC/B,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,EACpE,KAAK,EAAE,OAAO,EACd,cAAc,EAAE,gBAAgB,GAAG,IAAI,GACrC,gBAAgB,CAiBlB;AAED,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,6BAA6B,GAClC,gBAAgB,CAKlB"}
@@ -14,6 +14,25 @@ export class ProviderRetryWatchdogAbortError extends Error {
14
14
  this.name = "ProviderRetryWatchdogAbortError";
15
15
  }
16
16
  }
17
+ /**
18
+ * Marks a terminal message whose `toolUse` stop reason was demoted because the provider sent no
19
+ * tool call. Demotion rewrites the stop reason, so this diagnostic is the only surviving evidence
20
+ * that the turn was malformed rather than a clean stop; downstream recovery keys on it.
21
+ */
22
+ export const EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC = "empty_tool_use_terminal_state";
23
+ export function demoteToolUseWithoutToolCalls(message) {
24
+ // Count raw blocks: cursor-resolved calls are legitimate completed tool calls and must not be demoted.
25
+ if (message.stopReason !== "toolUse" || message.content.some((block) => block.type === "toolCall"))
26
+ return message;
27
+ return {
28
+ ...message,
29
+ stopReason: "stop",
30
+ diagnostics: [
31
+ ...(message.diagnostics ?? []),
32
+ { type: EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC, timestamp: Date.now(), details: {} },
33
+ ],
34
+ };
35
+ }
17
36
  export function promoteStopWithPendingToolCalls(message) {
18
37
  if (message.stopReason !== "stop")
19
38
  return message;
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-terminal-state.js","sourceRoot":"","sources":["../src/assistant-terminal-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,mBAAmB,EACnB,oBAAoB,GAEpB,MAAM,uBAAuB,CAAC;AAG/B,MAAM,WAAW,GAAG;IACnB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;CAChC,CAAC;AAItC,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IAGzD,YAAY,aAAqB;QAChC,KAAK,CAAC,aAAa,CAAC,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAC/C,CAAC;CACD;AAED,MAAM,UAAU,+BAA+B,CAAC,OAAyB;IACxE,IAAI,OAAO,CAAC,UAAU,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IAClD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC;QAAE,OAAO,OAAO,CAAC;IAChF,OAAO,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,OAAyB;IACrE,OAAO,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC3G,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACtD,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,CAAC;AAC1E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACvC,cAAuC,EACvC,mBAAiD;IAEjD,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAClC,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACtF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,KAAkC,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,qCAAqC,CACpD,KAA+B,EAC/B,MAAoE,EACpE,KAAc,EACd,cAAuC;IAEvC,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5E,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAChE,GAAG,EAAE,cAAc,EAAE,GAAG,IAAI,KAAK,CAAC,GAAG;QACrC,QAAQ,EAAE,cAAc,EAAE,QAAQ,IAAI,KAAK,CAAC,QAAQ;QACpD,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,KAAK,CAAC,EAAE;QACxC,aAAa,EAAE,cAAc,EAAE,aAAa;QAC5C,UAAU,EAAE,cAAc,EAAE,UAAU;QACtC,WAAW,EAAE,cAAc,EAAE,WAAW;QACxC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,WAAW;QAC3C,UAAU,EAAE,MAAM;QAClB,YAAY,EAAE,YAAY,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,OAAO,CAAC;QACtF,GAAG,CAAC,KAAK,YAAY,+BAA+B,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,SAAS,EAAE,cAAc,EAAE,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE;KAClD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iCAAiC,CAChD,OAAyB,EACzB,KAAoC;IAEpC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC5G,IAAI,OAAO,CAAC,UAAU,KAAK,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,YAAY,KAAK,YAAY;QAAE,OAAO,OAAO,CAAC;IACjG,OAAO,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC;AAC/D,CAAC","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype CursorExecResolvedCarrier,\n\tisClassifierRefusal,\n\tisCursorExecResolved,\n\ttype ToolResultMessage,\n} from \"@earendil-works/pi-ai\";\nimport type { AgentLoopConfig } from \"./types.ts\";\n\nconst EMPTY_USAGE = {\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n} satisfies AssistantMessage[\"usage\"];\n\ntype TerminalAssistantMessageEvent = Extract<AssistantMessageEvent, { type: \"done\" | \"error\" }>;\n\nexport class ProviderRetryWatchdogAbortError extends Error {\n\treadonly providerCause: string;\n\n\tconstructor(providerCause: string) {\n\t\tsuper(providerCause);\n\t\tthis.providerCause = providerCause;\n\t\tthis.name = \"ProviderRetryWatchdogAbortError\";\n\t}\n}\n\nexport function promoteStopWithPendingToolCalls(message: AssistantMessage): AssistantMessage {\n\tif (message.stopReason !== \"stop\") return message;\n\tif (!message.content.some((block) => block.type === \"toolCall\")) return message;\n\treturn { ...message, stopReason: \"toolUse\" };\n}\n\nexport function shouldTerminateAssistantTurn(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"error\" || message.stopReason === \"aborted\" || isClassifierRefusal(message);\n}\n\nexport function isStreamIdleTimeoutError(error: unknown): boolean {\n\treturn error instanceof Error && error.name === \"StreamIdleTimeoutError\";\n}\n\n/**\n * After tools already finished (Cursor exec-resolved or buffered results),\n * a silent provider is a finished turn, not a failed one.\n */\nexport function shouldFinalizeIdleAsStop(\n\tpartialMessage: AssistantMessage | null,\n\tproviderToolResults: readonly ToolResultMessage[],\n): boolean {\n\tif (!partialMessage) return false;\n\tconst toolCalls = partialMessage.content.filter((block) => block.type === \"toolCall\");\n\tif (toolCalls.length === 0) return false;\n\tif (providerToolResults.length > 0) return true;\n\treturn toolCalls.every((block) => isCursorExecResolved(block as CursorExecResolvedCarrier));\n}\n\nexport function createTerminalFailureAssistantMessage(\n\tmodel: AgentLoopConfig[\"model\"],\n\treason: Extract<AssistantMessage[\"stopReason\"], \"aborted\" | \"error\">,\n\terror: unknown,\n\tpartialMessage: AssistantMessage | null,\n): AssistantMessage {\n\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: partialMessage?.content ?? [{ type: \"text\", text: \"\" }],\n\t\tapi: partialMessage?.api ?? model.api,\n\t\tprovider: partialMessage?.provider ?? model.provider,\n\t\tmodel: partialMessage?.model ?? model.id,\n\t\tresponseModel: partialMessage?.responseModel,\n\t\tresponseId: partialMessage?.responseId,\n\t\tdiagnostics: partialMessage?.diagnostics,\n\t\tusage: partialMessage?.usage ?? EMPTY_USAGE,\n\t\tstopReason: reason,\n\t\terrorMessage: errorMessage || (reason === \"aborted\" ? \"Request was aborted\" : \"Error\"),\n\t\t...(error instanceof ProviderRetryWatchdogAbortError ? { abortSource: \"provider\" as const } : {}),\n\t\ttimestamp: partialMessage?.timestamp ?? Date.now(),\n\t};\n}\n\nexport function normalizeTerminalAssistantMessage(\n\tmessage: AssistantMessage,\n\tevent: TerminalAssistantMessageEvent,\n): AssistantMessage {\n\tif (event.type === \"done\") return message;\n\tconst errorMessage = message.errorMessage ?? (event.reason === \"aborted\" ? \"Request was aborted\" : \"Error\");\n\tif (message.stopReason === event.reason && message.errorMessage === errorMessage) return message;\n\treturn { ...message, stopReason: event.reason, errorMessage };\n}\n"]}
1
+ {"version":3,"file":"assistant-terminal-state.js","sourceRoot":"","sources":["../src/assistant-terminal-state.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype CursorExecResolvedCarrier,\n\tisClassifierRefusal,\n\tisCursorExecResolved,\n\ttype ToolResultMessage,\n} from \"@earendil-works/pi-ai\";\nimport type { AgentLoopConfig } from \"./types.ts\";\n\nconst EMPTY_USAGE = {\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n} satisfies AssistantMessage[\"usage\"];\n\ntype TerminalAssistantMessageEvent = Extract<AssistantMessageEvent, { type: \"done\" | \"error\" }>;\n\nexport class ProviderRetryWatchdogAbortError extends Error {\n\treadonly providerCause: string;\n\n\tconstructor(providerCause: string) {\n\t\tsuper(providerCause);\n\t\tthis.providerCause = providerCause;\n\t\tthis.name = \"ProviderRetryWatchdogAbortError\";\n\t}\n}\n\n/**\n * Marks a terminal message whose `toolUse` stop reason was demoted because the provider sent no\n * tool call. Demotion rewrites the stop reason, so this diagnostic is the only surviving evidence\n * that the turn was malformed rather than a clean stop; downstream recovery keys on it.\n */\nexport const EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC = \"empty_tool_use_terminal_state\";\n\nexport function demoteToolUseWithoutToolCalls(message: AssistantMessage): AssistantMessage {\n\t// Count raw blocks: cursor-resolved calls are legitimate completed tool calls and must not be demoted.\n\tif (message.stopReason !== \"toolUse\" || message.content.some((block) => block.type === \"toolCall\")) return message;\n\treturn {\n\t\t...message,\n\t\tstopReason: \"stop\",\n\t\tdiagnostics: [\n\t\t\t...(message.diagnostics ?? []),\n\t\t\t{ type: EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC, timestamp: Date.now(), details: {} },\n\t\t],\n\t};\n}\n\nexport function promoteStopWithPendingToolCalls(message: AssistantMessage): AssistantMessage {\n\tif (message.stopReason !== \"stop\") return message;\n\tif (!message.content.some((block) => block.type === \"toolCall\")) return message;\n\treturn { ...message, stopReason: \"toolUse\" };\n}\n\nexport function shouldTerminateAssistantTurn(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"error\" || message.stopReason === \"aborted\" || isClassifierRefusal(message);\n}\n\nexport function isStreamIdleTimeoutError(error: unknown): boolean {\n\treturn error instanceof Error && error.name === \"StreamIdleTimeoutError\";\n}\n\n/**\n * After tools already finished (Cursor exec-resolved or buffered results),\n * a silent provider is a finished turn, not a failed one.\n */\nexport function shouldFinalizeIdleAsStop(\n\tpartialMessage: AssistantMessage | null,\n\tproviderToolResults: readonly ToolResultMessage[],\n): boolean {\n\tif (!partialMessage) return false;\n\tconst toolCalls = partialMessage.content.filter((block) => block.type === \"toolCall\");\n\tif (toolCalls.length === 0) return false;\n\tif (providerToolResults.length > 0) return true;\n\treturn toolCalls.every((block) => isCursorExecResolved(block as CursorExecResolvedCarrier));\n}\n\nexport function createTerminalFailureAssistantMessage(\n\tmodel: AgentLoopConfig[\"model\"],\n\treason: Extract<AssistantMessage[\"stopReason\"], \"aborted\" | \"error\">,\n\terror: unknown,\n\tpartialMessage: AssistantMessage | null,\n): AssistantMessage {\n\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: partialMessage?.content ?? [{ type: \"text\", text: \"\" }],\n\t\tapi: partialMessage?.api ?? model.api,\n\t\tprovider: partialMessage?.provider ?? model.provider,\n\t\tmodel: partialMessage?.model ?? model.id,\n\t\tresponseModel: partialMessage?.responseModel,\n\t\tresponseId: partialMessage?.responseId,\n\t\tdiagnostics: partialMessage?.diagnostics,\n\t\tusage: partialMessage?.usage ?? EMPTY_USAGE,\n\t\tstopReason: reason,\n\t\terrorMessage: errorMessage || (reason === \"aborted\" ? \"Request was aborted\" : \"Error\"),\n\t\t...(error instanceof ProviderRetryWatchdogAbortError ? { abortSource: \"provider\" as const } : {}),\n\t\ttimestamp: partialMessage?.timestamp ?? Date.now(),\n\t};\n}\n\nexport function normalizeTerminalAssistantMessage(\n\tmessage: AssistantMessage,\n\tevent: TerminalAssistantMessageEvent,\n): AssistantMessage {\n\tif (event.type === \"done\") return message;\n\tconst errorMessage = message.errorMessage ?? (event.reason === \"aborted\" ? \"Request was aborted\" : \"Error\");\n\tif (message.stopReason === event.reason && message.errorMessage === errorMessage) return message;\n\treturn { ...message, stopReason: event.reason, errorMessage };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"empty-assistant-recovery.d.ts","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAQR,KAAK,KAAK,EAEV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAqG3C,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAMnH"}
1
+ {"version":3,"file":"empty-assistant-recovery.d.ts","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAQR,KAAK,KAAK,EAEV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoH3C,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAMnH"}
@@ -1,30 +1,28 @@
1
1
  import { createAssistantMessageEventStream, getToolCallFormat, hasVisibleAssistantContent, hasVisibleText, shouldRecoverTextToolCalls, } from "@earendil-works/pi-ai";
2
2
  const EMPTY_RESPONSE_ERROR = "Model returned an empty response twice";
3
+ const EMPTY_TOOL_USE_ERROR = "Model returned tool_use without a tool call twice";
3
4
  function isEmptyStop(message) {
4
5
  return message.stopReason === "stop" && !hasVisibleAssistantContent(message);
5
6
  }
7
+ function isEmptyToolUse(message) {
8
+ return message.stopReason === "toolUse" && !message.content.some((block) => block.type === "toolCall");
9
+ }
6
10
  function eventStartsVisibleContent(event) {
7
11
  return event.type === "toolcall_start" || (event.type === "text_delta" && hasVisibleText(event.delta));
8
12
  }
9
- function appendRetryDiagnostic(message) {
13
+ function appendRetryDiagnostic(message, type = "empty_assistant_response_recovery") {
10
14
  return {
11
15
  ...message,
12
- diagnostics: [
13
- ...(message.diagnostics ?? []),
14
- {
15
- type: "empty_assistant_response_recovery",
16
- timestamp: Date.now(),
17
- details: { retries: 1 },
18
- },
19
- ],
16
+ diagnostics: [...(message.diagnostics ?? []), { type, timestamp: Date.now(), details: { retries: 1 } }],
20
17
  };
21
18
  }
22
- function createEmptyResponseFailure(message) {
19
+ function createEmptyResponseFailure(message, toolUse = false) {
20
+ const errorMessage = toolUse ? EMPTY_TOOL_USE_ERROR : EMPTY_RESPONSE_ERROR;
23
21
  return {
24
- ...appendRetryDiagnostic(message),
25
- content: [{ type: "text", text: EMPTY_RESPONSE_ERROR }],
22
+ ...appendRetryDiagnostic(message, toolUse ? "empty_tool_use_response_recovery" : undefined),
23
+ content: [{ type: "text", text: errorMessage }],
26
24
  stopReason: "error",
27
- errorMessage: EMPTY_RESPONSE_ERROR,
25
+ errorMessage,
28
26
  };
29
27
  }
30
28
  function createRetryingStream(firstStream, createStream) {
@@ -39,17 +37,23 @@ function createRetryingStream(firstStream, createStream) {
39
37
  let retry = false;
40
38
  for await (const event of stream) {
41
39
  if (event.type === "done") {
42
- if (isEmptyStop(event.message)) {
40
+ const emptyToolUse = isEmptyToolUse(event.message);
41
+ if (isEmptyStop(event.message) || emptyToolUse) {
43
42
  if (!retrying) {
44
43
  retry = true;
45
44
  break;
46
45
  }
47
- const error = createEmptyResponseFailure(event.message);
46
+ const error = createEmptyResponseFailure(event.message, emptyToolUse);
48
47
  outerStream.push({ type: "error", reason: "error", error });
49
48
  outerStream.end();
50
49
  return;
51
50
  }
52
- const terminal = retrying ? { ...event, message: appendRetryDiagnostic(event.message) } : event;
51
+ const terminal = retrying
52
+ ? {
53
+ ...event,
54
+ message: appendRetryDiagnostic(event.message, isEmptyToolUse(event.message) ? "empty_tool_use_response_recovery" : undefined),
55
+ }
56
+ : event;
53
57
  if (!forwarding) {
54
58
  for (const pending of buffered)
55
59
  outerStream.push(pending);
@@ -92,6 +96,10 @@ function createRetryingStream(firstStream, createStream) {
92
96
  })();
93
97
  return outerStream;
94
98
  }
99
+ // Wrapping replaces the provider stream with a buffering proxy, which does not carry the
100
+ // underlying stream's liveness surface (trackLocalWork/hasPendingLocalWork) that the loop's
101
+ // idle watchdog reads. Only wrap models that actually need stream-level recovery; the
102
+ // empty-tool_use contradiction is normalized for every model by the agent loop instead.
95
103
  export function withEmptyAssistantRecovery(model, streamFunction) {
96
104
  if (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined)
97
105
  return streamFunction;
@@ -1 +1 @@
1
- {"version":3,"file":"empty-assistant-recovery.js","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\tgetToolCallFormat,\n\thasVisibleAssistantContent,\n\thasVisibleText,\n\ttype Model,\n\tshouldRecoverTextToolCalls,\n} from \"@earendil-works/pi-ai\";\nimport type { StreamFn } from \"./types.ts\";\n\ntype StreamFactory = () => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;\n\nconst EMPTY_RESPONSE_ERROR = \"Model returned an empty response twice\";\n\nfunction isEmptyStop(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"stop\" && !hasVisibleAssistantContent(message);\n}\n\nfunction eventStartsVisibleContent(event: AssistantMessageEvent): boolean {\n\treturn event.type === \"toolcall_start\" || (event.type === \"text_delta\" && hasVisibleText(event.delta));\n}\n\nfunction appendRetryDiagnostic(message: AssistantMessage): AssistantMessage {\n\treturn {\n\t\t...message,\n\t\tdiagnostics: [\n\t\t\t...(message.diagnostics ?? []),\n\t\t\t{\n\t\t\t\ttype: \"empty_assistant_response_recovery\",\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdetails: { retries: 1 },\n\t\t\t},\n\t\t],\n\t};\n}\n\nfunction createEmptyResponseFailure(message: AssistantMessage): AssistantMessage {\n\treturn {\n\t\t...appendRetryDiagnostic(message),\n\t\tcontent: [{ type: \"text\", text: EMPTY_RESPONSE_ERROR }],\n\t\tstopReason: \"error\",\n\t\terrorMessage: EMPTY_RESPONSE_ERROR,\n\t};\n}\n\nfunction createRetryingStream(firstStream: AssistantMessageEventStream, createStream: StreamFactory) {\n\tconst outerStream = createAssistantMessageEventStream();\n\n\tvoid (async (): Promise<void> => {\n\t\ttry {\n\t\t\tlet stream = firstStream;\n\t\t\tlet retrying = false;\n\t\t\tfor (;;) {\n\t\t\t\tconst buffered: AssistantMessageEvent[] = [];\n\t\t\t\tlet forwarding = false;\n\t\t\t\tlet retry = false;\n\t\t\t\tfor await (const event of stream) {\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tif (isEmptyStop(event.message)) {\n\t\t\t\t\t\t\tif (!retrying) {\n\t\t\t\t\t\t\t\tretry = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = createEmptyResponseFailure(event.message);\n\t\t\t\t\t\t\touterStream.push({ type: \"error\", reason: \"error\", error });\n\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst terminal = retrying ? { ...event, message: appendRetryDiagnostic(event.message) } : event;\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(terminal);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"error\") {\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffered.push(event);\n\t\t\t\t\tif (eventStartsVisibleContent(event)) {\n\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\tforwarding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!retry) {\n\t\t\t\t\touterStream.end(retrying ? appendRetryDiagnostic(await stream.result()) : await stream.result());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tretrying = true;\n\t\t\t\tstream = await createStream();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\touterStream.fail(error);\n\t\t}\n\t})();\n\n\treturn outerStream;\n}\n\nexport function withEmptyAssistantRecovery<TApi extends Api>(model: Model<TApi>, streamFunction: StreamFn): StreamFn {\n\tif (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined) return streamFunction;\n\treturn async (requestedModel, context, options) => {\n\t\tconst createStream = (): ReturnType<StreamFn> => streamFunction(requestedModel, context, options);\n\t\treturn createRetryingStream(await createStream(), createStream);\n\t};\n}\n"]}
1
+ {"version":3,"file":"empty-assistant-recovery.js","sourceRoot":"","sources":["../src/empty-assistant-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype AssistantMessageEventStream,\n\tcreateAssistantMessageEventStream,\n\tgetToolCallFormat,\n\thasVisibleAssistantContent,\n\thasVisibleText,\n\ttype Model,\n\tshouldRecoverTextToolCalls,\n} from \"@earendil-works/pi-ai\";\nimport type { StreamFn } from \"./types.ts\";\n\ntype StreamFactory = () => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;\n\nconst EMPTY_RESPONSE_ERROR = \"Model returned an empty response twice\";\nconst EMPTY_TOOL_USE_ERROR = \"Model returned tool_use without a tool call twice\";\n\nfunction isEmptyStop(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"stop\" && !hasVisibleAssistantContent(message);\n}\n\nfunction isEmptyToolUse(message: AssistantMessage): boolean {\n\treturn message.stopReason === \"toolUse\" && !message.content.some((block) => block.type === \"toolCall\");\n}\n\nfunction eventStartsVisibleContent(event: AssistantMessageEvent): boolean {\n\treturn event.type === \"toolcall_start\" || (event.type === \"text_delta\" && hasVisibleText(event.delta));\n}\n\nfunction appendRetryDiagnostic(\n\tmessage: AssistantMessage,\n\ttype = \"empty_assistant_response_recovery\",\n): AssistantMessage {\n\treturn {\n\t\t...message,\n\t\tdiagnostics: [...(message.diagnostics ?? []), { type, timestamp: Date.now(), details: { retries: 1 } }],\n\t};\n}\n\nfunction createEmptyResponseFailure(message: AssistantMessage, toolUse = false): AssistantMessage {\n\tconst errorMessage = toolUse ? EMPTY_TOOL_USE_ERROR : EMPTY_RESPONSE_ERROR;\n\treturn {\n\t\t...appendRetryDiagnostic(message, toolUse ? \"empty_tool_use_response_recovery\" : undefined),\n\t\tcontent: [{ type: \"text\", text: errorMessage }],\n\t\tstopReason: \"error\",\n\t\terrorMessage,\n\t};\n}\n\nfunction createRetryingStream(firstStream: AssistantMessageEventStream, createStream: StreamFactory) {\n\tconst outerStream = createAssistantMessageEventStream();\n\n\tvoid (async (): Promise<void> => {\n\t\ttry {\n\t\t\tlet stream = firstStream;\n\t\t\tlet retrying = false;\n\t\t\tfor (;;) {\n\t\t\t\tconst buffered: AssistantMessageEvent[] = [];\n\t\t\t\tlet forwarding = false;\n\t\t\t\tlet retry = false;\n\t\t\t\tfor await (const event of stream) {\n\t\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\t\tconst emptyToolUse = isEmptyToolUse(event.message);\n\t\t\t\t\t\tif (isEmptyStop(event.message) || emptyToolUse) {\n\t\t\t\t\t\t\tif (!retrying) {\n\t\t\t\t\t\t\t\tretry = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst error = createEmptyResponseFailure(event.message, emptyToolUse);\n\t\t\t\t\t\t\touterStream.push({ type: \"error\", reason: \"error\", error });\n\t\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst terminal = retrying\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...event,\n\t\t\t\t\t\t\t\t\tmessage: appendRetryDiagnostic(\n\t\t\t\t\t\t\t\t\t\tevent.message,\n\t\t\t\t\t\t\t\t\t\tisEmptyToolUse(event.message) ? \"empty_tool_use_response_recovery\" : undefined,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: event;\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(terminal);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.type === \"error\") {\n\t\t\t\t\t\tif (!forwarding) {\n\t\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\t}\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\touterStream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (forwarding) {\n\t\t\t\t\t\touterStream.push(event);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffered.push(event);\n\t\t\t\t\tif (eventStartsVisibleContent(event)) {\n\t\t\t\t\t\tfor (const pending of buffered) outerStream.push(pending);\n\t\t\t\t\t\tforwarding = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!retry) {\n\t\t\t\t\touterStream.end(retrying ? appendRetryDiagnostic(await stream.result()) : await stream.result());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tretrying = true;\n\t\t\t\tstream = await createStream();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\touterStream.fail(error);\n\t\t}\n\t})();\n\n\treturn outerStream;\n}\n\n// Wrapping replaces the provider stream with a buffering proxy, which does not carry the\n// underlying stream's liveness surface (trackLocalWork/hasPendingLocalWork) that the loop's\n// idle watchdog reads. Only wrap models that actually need stream-level recovery; the\n// empty-tool_use contradiction is normalized for every model by the agent loop instead.\nexport function withEmptyAssistantRecovery<TApi extends Api>(model: Model<TApi>, streamFunction: StreamFn): StreamFn {\n\tif (!shouldRecoverTextToolCalls(model) && getToolCallFormat(model) === undefined) return streamFunction;\n\treturn async (requestedModel, context, options) => {\n\t\tconst createStream = (): ReturnType<StreamFn> => streamFunction(requestedModel, context, options);\n\t\treturn createRetryingStream(await createStream(), createStream);\n\t};\n}\n"]}
@@ -3,7 +3,7 @@ export type { AttributeValue, ExactTelemetryAttributes, InferEventAttributes, In
3
3
  export { createTypedSpanStarter, defineTelemetrySchema, InMemoryTelemetryContext, NOOP_TELEMETRY_CONTEXT, } from "@earendil-works/pi-telemetry";
4
4
  export * from "./agent.ts";
5
5
  export * from "./agent-loop.ts";
6
- export { ProviderRetryWatchdogAbortError } from "./assistant-terminal-state.ts";
6
+ export { EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC, ProviderRetryWatchdogAbortError } from "./assistant-terminal-state.ts";
7
7
  export * from "./harness/agent-harness.ts";
8
8
  export { type BranchPreparation, type BranchSummaryDetails, type BranchSummaryResult, type CollectEntriesResult, collectEntriesForBranchSummary, type FileOperations, type GenerateBranchSummaryOptions, generateBranchSummary, prepareBranchEntries, } from "./harness/compaction/branch-summarization.ts";
9
9
  export { type CompactionPreparation, type CompactionSettings, type CompactResult, calculateContextTokens, compact, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, estimateTokens, findCutPoint, findTurnStartIndex, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareCompaction, serializeConversation, shouldCompact, } from "./harness/compaction/compaction.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,YAAY,EACX,cAAc,EACd,wBAAwB,EACxB,oBAAoB,EACpB,uBAAuB,EACvB,kCAAkC,EAClC,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,cAAc,IAAI,uBAAuB,EACzC,WAAW,EACX,UAAU,EACV,4BAA4B,EAC5B,0BAA0B,EAC1B,sBAAsB,EACtB,gBAAgB,EAChB,iCAAiC,EACjC,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,gCAAgC,EAChC,kCAAkC,EAClC,4BAA4B,EAC5B,uBAAuB,EACvB,kCAAkC,EAClC,wBAAwB,EACxB,aAAa,EACb,uBAAuB,EACvB,iCAAiC,EACjC,gBAAgB,GAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,GACtB,MAAM,8BAA8B,CAAC;AACtC,cAAc,YAAY,CAAC;AAE3B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,kCAAkC,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AACpH,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,8BAA8B,EAC9B,KAAK,cAAc,EACnB,KAAK,4BAA4B,EACjC,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAE9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,YAAY,EACX,MAAM,EACN,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,oBAAoB,EACpB,eAAe,EACf,0BAA0B,EAC1B,oBAAoB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GAChB,MAAM,wBAAwB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,8BAA8B,EACnC,KAAK,gBAAgB,EACrB,KAAK,6BAA6B,EAClC,kBAAkB,EAClB,KAAK,sBAAsB,EAC3B,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,cAAc,EACd,KAAK,kBAAkB,EACvB,GAAG,EACH,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,UAAU,EACV,cAAc,EACd,EAAE,EACF,KAAK,cAAc,EACnB,KAAK,KAAK,EACV,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACV,OAAO,GACP,MAAM,oBAAoB,CAAC;AAC5B,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAE5C,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,cAAc,YAAY,CAAC"}
@@ -4,7 +4,7 @@ export { createTypedSpanStarter, defineTelemetrySchema, InMemoryTelemetryContext
4
4
  export * from "./agent.js";
5
5
  // Loop functions
6
6
  export * from "./agent-loop.js";
7
- export { ProviderRetryWatchdogAbortError } from "./assistant-terminal-state.js";
7
+ export { EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC, ProviderRetryWatchdogAbortError } from "./assistant-terminal-state.js";
8
8
  export * from "./harness/agent-harness.js";
9
9
  export { collectEntriesForBranchSummary, generateBranchSummary, prepareBranchEntries, } from "./harness/compaction/branch-summarization.js";
10
10
  export { calculateContextTokens, compact, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, estimateTokens, findCutPoint, findTurnStartIndex, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareCompaction, serializeConversation, shouldCompact, } from "./harness/compaction/compaction.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAkC/C,OAAO,EACN,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,GACtB,MAAM,8BAA8B,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,iBAAiB;AACjB,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAKN,8BAA8B,EAG9B,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAIN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,UAAU;AACV,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAmB3C,OAAO,EACN,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GAChB,MAAM,wBAAwB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAMN,kBAAkB,EAElB,eAAe,EAGf,cAAc,EAEd,GAAG,EACH,SAAS,EAKT,UAAU,EACV,cAAc,EACd,EAAE,EAKF,OAAO,GACP,MAAM,oBAAoB,CAAC;AAC5B,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,kBAAkB;AAClB,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,kBAAkB;AAClB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,QAAQ;AACR,cAAc,YAAY,CAAC","sourcesContent":["// Core Agent\n\nexport { uuidv7 } from \"@earendil-works/pi-ai\";\nexport type {\n\tAttributeValue,\n\tExactTelemetryAttributes,\n\tInferEventAttributes,\n\tInferOptionalAttributes,\n\tInferRequiredAndOptionalAttributes,\n\tInferStartAttributes,\n\tRecordedTelemetryEvent,\n\tRecordedTelemetrySpan,\n\tSchemaTelemetrySpan,\n\tSpanAttributes,\n\tSpanAttributes as TelemetrySpanAttributes,\n\tSpanOptions,\n\tSpanStatus,\n\tTelemetryAttributeDefinition,\n\tTelemetryAttributeMetadata,\n\tTelemetryAttributeType,\n\tTelemetryContext,\n\tTelemetryEventAttributeDefinition,\n\tTelemetryEventDefinition,\n\tTelemetryParentDefinition,\n\tTelemetrySchemaDefinition,\n\tTelemetrySchemaSpanEndAttributes,\n\tTelemetrySchemaSpanEventAttributes,\n\tTelemetrySchemaSpanEventName,\n\tTelemetrySchemaSpanName,\n\tTelemetrySchemaSpanStartAttributes,\n\tTelemetrySchemaSpanUnion,\n\tTelemetrySpan,\n\tTelemetrySpanDefinition,\n\tTelemetryStartAttributeDefinition,\n\tTypedSpanStarter,\n} from \"@earendil-works/pi-telemetry\";\nexport {\n\tcreateTypedSpanStarter,\n\tdefineTelemetrySchema,\n\tInMemoryTelemetryContext,\n\tNOOP_TELEMETRY_CONTEXT,\n} from \"@earendil-works/pi-telemetry\";\nexport * from \"./agent.ts\";\n// Loop functions\nexport * from \"./agent-loop.ts\";\nexport { ProviderRetryWatchdogAbortError } from \"./assistant-terminal-state.ts\";\nexport * from \"./harness/agent-harness.ts\";\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryDetails,\n\ttype BranchSummaryResult,\n\ttype CollectEntriesResult,\n\tcollectEntriesForBranchSummary,\n\ttype FileOperations,\n\ttype GenerateBranchSummaryOptions,\n\tgenerateBranchSummary,\n\tprepareBranchEntries,\n} from \"./harness/compaction/branch-summarization.ts\";\nexport {\n\ttype CompactionPreparation,\n\ttype CompactionSettings,\n\ttype CompactResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgenerateSummaryWithUsage,\n\tgetLastAssistantUsage,\n\tprepareCompaction,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./harness/compaction/compaction.ts\";\nexport * from \"./harness/messages.ts\";\nexport * from \"./harness/prompt-templates.ts\";\n// Harness\nexport * from \"./harness/result.ts\";\nexport * from \"./harness/session/index.ts\";\nexport * from \"./harness/skills.ts\";\nexport * from \"./harness/system-prompt.ts\";\nexport type {\n\tAiSpan,\n\tAiSpanAttributes,\n\tAiSpanEndAttributes,\n\tAiSpanEventAttributes,\n\tAiSpanEventName,\n\tAiSpanName,\n\tAiSpanStartAttributes,\n\tAiTelemetrySpan,\n\tHarnessSpan,\n\tHarnessSpanAttributes,\n\tHarnessSpanEndAttributes,\n\tHarnessSpanEventAttributes,\n\tHarnessSpanEventName,\n\tHarnessSpanName,\n\tHarnessSpanStartAttributes,\n\tHarnessTelemetrySpan,\n} from \"./harness/telemetry.ts\";\nexport {\n\tAGENT_TELEMETRY_SCHEMAS,\n\tAI_TELEMETRY_SCHEMA,\n\tHARNESS_TELEMETRY_SCHEMA,\n\tstartAiSpan,\n\tstartHarnessSpan,\n} from \"./harness/telemetry.ts\";\nexport * from \"./harness/tools/index.ts\";\nexport {\n\ttype AgentHarnessResources,\n\ttype AgentHarnessStreamOptions,\n\ttype AgentHarnessStreamOptionsPatch,\n\ttype AgentHarnessTool,\n\ttype AgentHarnessToolContextSource,\n\tBranchSummaryError,\n\ttype BranchSummaryErrorCode,\n\tCompactionError,\n\ttype CompactionErrorCode,\n\ttype ExecutionEnv,\n\tExecutionError,\n\ttype ExecutionErrorCode,\n\terr,\n\tFileError,\n\ttype FileErrorCode,\n\ttype FileInfo,\n\ttype FileKind,\n\ttype FileSystem,\n\tgetOrThrow,\n\tgetOrUndefined,\n\tok,\n\ttype PromptTemplate,\n\ttype Shell,\n\ttype ShellExecOptions,\n\ttype Skill,\n\ttoError,\n} from \"./harness/types.ts\";\nexport * from \"./harness/utils/shell-output.ts\";\nexport * from \"./harness/utils/truncate.ts\";\n// Proxy utilities\nexport * from \"./proxy.ts\";\nexport * from \"./search/index.ts\";\n// Stream defaults\nexport { setDefaultStreamFn } from \"./stream-fn.ts\";\n// Types\nexport * from \"./types.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAkC/C,OAAO,EACN,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,GACtB,MAAM,8BAA8B,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,iBAAiB;AACjB,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,kCAAkC,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AACpH,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAKN,8BAA8B,EAG9B,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAIN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,UAAU;AACV,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAmB3C,OAAO,EACN,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GAChB,MAAM,wBAAwB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAMN,kBAAkB,EAElB,eAAe,EAGf,cAAc,EAEd,GAAG,EACH,SAAS,EAKT,UAAU,EACV,cAAc,EACd,EAAE,EAKF,OAAO,GACP,MAAM,oBAAoB,CAAC;AAC5B,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,kBAAkB;AAClB,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,kBAAkB;AAClB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,QAAQ;AACR,cAAc,YAAY,CAAC","sourcesContent":["// Core Agent\n\nexport { uuidv7 } from \"@earendil-works/pi-ai\";\nexport type {\n\tAttributeValue,\n\tExactTelemetryAttributes,\n\tInferEventAttributes,\n\tInferOptionalAttributes,\n\tInferRequiredAndOptionalAttributes,\n\tInferStartAttributes,\n\tRecordedTelemetryEvent,\n\tRecordedTelemetrySpan,\n\tSchemaTelemetrySpan,\n\tSpanAttributes,\n\tSpanAttributes as TelemetrySpanAttributes,\n\tSpanOptions,\n\tSpanStatus,\n\tTelemetryAttributeDefinition,\n\tTelemetryAttributeMetadata,\n\tTelemetryAttributeType,\n\tTelemetryContext,\n\tTelemetryEventAttributeDefinition,\n\tTelemetryEventDefinition,\n\tTelemetryParentDefinition,\n\tTelemetrySchemaDefinition,\n\tTelemetrySchemaSpanEndAttributes,\n\tTelemetrySchemaSpanEventAttributes,\n\tTelemetrySchemaSpanEventName,\n\tTelemetrySchemaSpanName,\n\tTelemetrySchemaSpanStartAttributes,\n\tTelemetrySchemaSpanUnion,\n\tTelemetrySpan,\n\tTelemetrySpanDefinition,\n\tTelemetryStartAttributeDefinition,\n\tTypedSpanStarter,\n} from \"@earendil-works/pi-telemetry\";\nexport {\n\tcreateTypedSpanStarter,\n\tdefineTelemetrySchema,\n\tInMemoryTelemetryContext,\n\tNOOP_TELEMETRY_CONTEXT,\n} from \"@earendil-works/pi-telemetry\";\nexport * from \"./agent.ts\";\n// Loop functions\nexport * from \"./agent-loop.ts\";\nexport { EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC, ProviderRetryWatchdogAbortError } from \"./assistant-terminal-state.ts\";\nexport * from \"./harness/agent-harness.ts\";\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryDetails,\n\ttype BranchSummaryResult,\n\ttype CollectEntriesResult,\n\tcollectEntriesForBranchSummary,\n\ttype FileOperations,\n\ttype GenerateBranchSummaryOptions,\n\tgenerateBranchSummary,\n\tprepareBranchEntries,\n} from \"./harness/compaction/branch-summarization.ts\";\nexport {\n\ttype CompactionPreparation,\n\ttype CompactionSettings,\n\ttype CompactResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgenerateSummaryWithUsage,\n\tgetLastAssistantUsage,\n\tprepareCompaction,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./harness/compaction/compaction.ts\";\nexport * from \"./harness/messages.ts\";\nexport * from \"./harness/prompt-templates.ts\";\n// Harness\nexport * from \"./harness/result.ts\";\nexport * from \"./harness/session/index.ts\";\nexport * from \"./harness/skills.ts\";\nexport * from \"./harness/system-prompt.ts\";\nexport type {\n\tAiSpan,\n\tAiSpanAttributes,\n\tAiSpanEndAttributes,\n\tAiSpanEventAttributes,\n\tAiSpanEventName,\n\tAiSpanName,\n\tAiSpanStartAttributes,\n\tAiTelemetrySpan,\n\tHarnessSpan,\n\tHarnessSpanAttributes,\n\tHarnessSpanEndAttributes,\n\tHarnessSpanEventAttributes,\n\tHarnessSpanEventName,\n\tHarnessSpanName,\n\tHarnessSpanStartAttributes,\n\tHarnessTelemetrySpan,\n} from \"./harness/telemetry.ts\";\nexport {\n\tAGENT_TELEMETRY_SCHEMAS,\n\tAI_TELEMETRY_SCHEMA,\n\tHARNESS_TELEMETRY_SCHEMA,\n\tstartAiSpan,\n\tstartHarnessSpan,\n} from \"./harness/telemetry.ts\";\nexport * from \"./harness/tools/index.ts\";\nexport {\n\ttype AgentHarnessResources,\n\ttype AgentHarnessStreamOptions,\n\ttype AgentHarnessStreamOptionsPatch,\n\ttype AgentHarnessTool,\n\ttype AgentHarnessToolContextSource,\n\tBranchSummaryError,\n\ttype BranchSummaryErrorCode,\n\tCompactionError,\n\ttype CompactionErrorCode,\n\ttype ExecutionEnv,\n\tExecutionError,\n\ttype ExecutionErrorCode,\n\terr,\n\tFileError,\n\ttype FileErrorCode,\n\ttype FileInfo,\n\ttype FileKind,\n\ttype FileSystem,\n\tgetOrThrow,\n\tgetOrUndefined,\n\tok,\n\ttype PromptTemplate,\n\ttype Shell,\n\ttype ShellExecOptions,\n\ttype Skill,\n\ttoError,\n} from \"./harness/types.ts\";\nexport * from \"./harness/utils/shell-output.ts\";\nexport * from \"./harness/utils/truncate.ts\";\n// Proxy utilities\nexport * from \"./proxy.ts\";\nexport * from \"./search/index.ts\";\n// Stream defaults\nexport { setDefaultStreamFn } from \"./stream-fn.ts\";\n// Types\nexport * from \"./types.ts\";\n"]}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-agent-core",
3
- "version": "2026.9.7-2",
3
+ "version": "2026.9.8",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,8 +35,8 @@
35
35
  "prepublishOnly": "npm run build"
36
36
  },
37
37
  "dependencies": {
38
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.7-2",
39
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.7-2",
38
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.8",
39
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.8",
40
40
  "diff": "9.0.0",
41
41
  "ignore": "7.0.6",
42
42
  "typebox": "1.3.18",
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAMX,+BAA+B,EAK/B,MAAM,uDAAuD,CAAC;AAG/D,OAAO,KAAK,EACX,wBAAwB,EAKxB,OAAO,EAGP,KAAK,EAGL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAOb,MAAM,aAAa,CAAC;AAqCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA6JlE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA+FhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAo4BD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA2czE,CAAC;AAyFF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA+ClF,CAAC;AA6IF,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACxB,+BAA+B,CAkBjC"}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAMX,+BAA+B,EAK/B,MAAM,uDAAuD,CAAC;AAG/D,OAAO,KAAK,EACX,wBAAwB,EAKxB,OAAO,EAGP,KAAK,EAGL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAOb,MAAM,aAAa,CAAC;AAsCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA6JlE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA+FhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAmvBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAudzE,CAAC;AAyFF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA+ClF,CAAC;AA6IF,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACxB,+BAA+B,CAkBjC"}
@@ -15,12 +15,12 @@ import { retryProviderRequest } from "../utils/provider-retry.js";
15
15
  import { appendRetryAfterMsMarker, extract429RetryAfterMs } from "../utils/retry-hint.js";
16
16
  import { normalizeAnthropicRetryFailure } from "../utils/retry-profile/failure.js";
17
17
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
18
- import { applyServerFallbackAbort, parseServerFallbackReceipt, parseStickyFallbackReceipt, } from "../utils/server-fallback-receipt.js";
18
+ import { applyServerFallbackAbort, applyServerFallbackContinuation, parseServerFallbackReceipt, parseStickyFallbackReceipt, } from "../utils/server-fallback-receipt.js";
19
19
  import { normalizeToolCallId } from "../utils/tool-call-id.js";
20
20
  import { isForcedToolChoiceUnsupportedError, omitToolChoiceParam } from "../utils/tool-choice-fallback.js";
21
21
  import { resolveRootObjectSchema } from "../utils/tool-schema-compat.js";
22
- import { demotedToolCallText, demotedToolResultText } from "../utils/unavailable-tool-text.js";
23
22
  import { sanitizeAnthropicToolPairs } from "./anthropic-tool-pairs.js";
23
+ import { demoteUnavailableToolReferences } from "./anthropic-tool-references.js";
24
24
  import { resolveCloudflareBaseUrl } from "./cloudflare.js";
25
25
  import { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling } from "./constrained-sampling.js";
26
26
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
@@ -571,157 +571,12 @@ function sanitizeUnsupportedNativeTools(model, params) {
571
571
  }
572
572
  return changed ? sanitized : params;
573
573
  }
574
- /**
575
- * Anthropic validates that every tool referenced by the message history is
576
- * available in the same request — defined in `tools` or discovered through a
577
- * `tool_reference` block — and rejects the whole request otherwise
578
- * ("Tool reference '<name>' not found in available tools"). Sessions outlive
579
- * their tools: an MCP server can be absent after a resume, an extension can
580
- * stop registering a tool, or a payload hook can strip a definition while the
581
- * history still carries the call. Demote those references to plain text so
582
- * the turn can proceed; the matching tool_result is demoted in lockstep so no
583
- * orphan pairing error replaces the original one.
584
- */
585
- function demoteUnavailableToolReferences(params) {
586
- const messages = params.messages;
587
- if (!Array.isArray(messages) || messages.length === 0)
588
- return params;
589
- const definedNames = new Set();
590
- if (Array.isArray(params.tools)) {
591
- for (const tool of params.tools) {
592
- if (isRecord(tool) && typeof tool.name === "string")
593
- definedNames.add(tool.name);
594
- }
595
- }
596
- // `tool_reference` blocks — emitted for deferred tools or replayed from a
597
- // server-side tool search — make their targets available without a
598
- // non-deferred definition.
599
- const discoveredNames = new Set();
600
- collectToolReferenceNames(messages, discoveredNames);
601
- const demotedCallNames = new Map();
602
- for (const message of messages) {
603
- if (message.role !== "assistant" || !Array.isArray(message.content))
604
- continue;
605
- for (const block of message.content) {
606
- if (isRecord(block) &&
607
- block.type === "tool_use" &&
608
- typeof block.name === "string" &&
609
- !definedNames.has(block.name) &&
610
- !discoveredNames.has(block.name)) {
611
- demotedCallNames.set(block.id, block.name);
612
- }
613
- }
614
- }
615
- // A `tool_reference` without its definition 400s the same way.
616
- const danglingReferenceNames = new Set();
617
- for (const name of discoveredNames) {
618
- if (!definedNames.has(name))
619
- danglingReferenceNames.add(name);
620
- }
621
- if (demotedCallNames.size === 0 && danglingReferenceNames.size === 0)
622
- return params;
623
- let changed = false;
624
- const availableToolNames = [...definedNames];
625
- const seenDemotedCallNames = new Set();
626
- const rewrittenMessages = [];
627
- for (const message of messages) {
628
- if (!Array.isArray(message.content)) {
629
- rewrittenMessages.push(message);
630
- continue;
631
- }
632
- let messageChanged = false;
633
- const content = [];
634
- for (const block of message.content) {
635
- if (message.role === "assistant" && isRecord(block) && block.type === "tool_use") {
636
- const demotedName = demotedCallNames.get(block.id);
637
- if (demotedName !== undefined) {
638
- messageChanged = true;
639
- const firstOccurrence = !seenDemotedCallNames.has(demotedName);
640
- seenDemotedCallNames.add(demotedName);
641
- content.push({
642
- type: "text",
643
- text: demotedToolCallText(demotedName, availableToolNames, firstOccurrence),
644
- });
645
- continue;
646
- }
647
- }
648
- if (isRecord(block) && block.type === "tool_result") {
649
- const demotedName = demotedCallNames.get(block.tool_use_id);
650
- if (demotedName !== undefined) {
651
- messageChanged = true;
652
- content.push({ type: "text", text: demotedToolResultText(demotedName, toolResultText(block.content)) });
653
- continue;
654
- }
655
- if (danglingReferenceNames.size > 0 && Array.isArray(block.content)) {
656
- const kept = [];
657
- const omitted = [];
658
- for (const item of block.content) {
659
- if (isRecord(item) &&
660
- item.type === "tool_reference" &&
661
- typeof item.tool_name === "string" &&
662
- danglingReferenceNames.has(item.tool_name)) {
663
- omitted.push(item.tool_name);
664
- continue;
665
- }
666
- kept.push(item);
667
- }
668
- if (omitted.length > 0) {
669
- messageChanged = true;
670
- const nextContent = kept.length > 0
671
- ? kept
672
- : [{ type: "text", text: `Tool reference unavailable: ${[...new Set(omitted)].join(", ")}` }];
673
- content.push({ ...block, content: nextContent });
674
- continue;
675
- }
676
- }
677
- }
678
- content.push(block);
679
- }
680
- if (content.length === 0) {
681
- changed = true;
682
- continue;
683
- }
684
- if (messageChanged) {
685
- changed = true;
686
- rewrittenMessages.push({ ...message, content });
687
- continue;
688
- }
689
- rewrittenMessages.push(message);
690
- }
691
- if (!changed)
692
- return params;
693
- return { ...params, messages: rewrittenMessages };
694
- }
695
- function collectToolReferenceNames(value, names) {
696
- if (Array.isArray(value)) {
697
- for (const item of value)
698
- collectToolReferenceNames(item, names);
699
- return;
700
- }
701
- if (!isRecord(value))
702
- return;
703
- if (value.type === "tool_reference" && typeof value.tool_name === "string")
704
- names.add(value.tool_name);
705
- for (const nested of Object.values(value))
706
- collectToolReferenceNames(nested, names);
707
- }
708
- function toolResultText(content) {
709
- if (typeof content === "string")
710
- return content;
711
- if (Array.isArray(content)) {
712
- const parts = [];
713
- for (const item of content) {
714
- if (!isRecord(item))
715
- continue;
716
- if (item.type === "text" && typeof item.text === "string")
717
- parts.push(item.text);
718
- else if (typeof item.type === "string")
719
- parts.push(`[${item.type}]`);
720
- }
721
- if (parts.length > 0)
722
- return parts.join("\n");
723
- }
724
- return "Tool output unavailable.";
574
+ /** Numeric HTTP status carried by an SDK error (Anthropic APIError.status), if any. */
575
+ function httpStatusOfError(error) {
576
+ if (!isRecord(error))
577
+ return undefined;
578
+ const status = error.status;
579
+ return typeof status === "number" && Number.isInteger(status) && status >= 100 && status < 600 ? status : undefined;
725
580
  }
726
581
  function sanitizeAdaptiveThinkingPayload(model, params, options) {
727
582
  if (!supportsAdaptiveThinking(model)) {
@@ -1046,24 +901,40 @@ export const stream = (model, context, options) => {
1046
901
  throw error;
1047
902
  }
1048
903
  };
1049
- const { params: sentParams, response } = await retryProviderRequest(async () => {
1050
- try {
1051
- return await createRequest();
1052
- }
1053
- catch (error) {
1054
- if (unsignedThinkingReplay !== "text" && isInvalidUnsignedThinkingSignatureError(error)) {
1055
- unsignedThinkingReplay = "text";
1056
- if (fallbackKey)
1057
- unsignedThinkingTextReplayFallbacks.add(fallbackKey);
1058
- return createRequest();
904
+ let requestOutcome;
905
+ try {
906
+ requestOutcome = await retryProviderRequest(async () => {
907
+ try {
908
+ return await createRequest();
1059
909
  }
1060
- throw error;
910
+ catch (error) {
911
+ if (unsignedThinkingReplay !== "text" && isInvalidUnsignedThinkingSignatureError(error)) {
912
+ unsignedThinkingReplay = "text";
913
+ if (fallbackKey)
914
+ unsignedThinkingTextReplayFallbacks.add(fallbackKey);
915
+ return createRequest();
916
+ }
917
+ throw error;
918
+ }
919
+ }, {
920
+ maxRetries: options?.maxRetries,
921
+ maxRetryDelayMs: options?.maxRetryDelayMs,
922
+ signal: requestSignal,
923
+ });
924
+ }
925
+ catch (error) {
926
+ // The SDK rejects HTTP failures instead of returning a Response, so a
927
+ // rejected request never reached onResponse. Deliver the numeric status
928
+ // once, after every internal retry, before the caller handles the error;
929
+ // errors without a status (network, aborts) report nothing rather than
930
+ // a fabricated code.
931
+ const status = httpStatusOfError(error);
932
+ if (status !== undefined) {
933
+ await options?.onResponse?.({ status, headers: {} }, model);
1061
934
  }
1062
- }, {
1063
- maxRetries: options?.maxRetries,
1064
- maxRetryDelayMs: options?.maxRetryDelayMs,
1065
- signal: requestSignal,
1066
- });
935
+ throw error;
936
+ }
937
+ const { params: sentParams, response } = requestOutcome;
1067
938
  await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
1068
939
  stream.push({ type: "start", partial: output });
1069
940
  const blocks = output.content;
@@ -1102,16 +973,14 @@ export const stream = (model, context, options) => {
1102
973
  }
1103
974
  }
1104
975
  else if (event.type === "content_block_start") {
1105
- if (event.content_block.type === "fallback" && output.content.length > 0) {
1106
- throw new Error("Anthropic performed an unsupported mid-output model fallback");
1107
- }
1108
- const receipt = options?.abortServerSideFallback === true
1109
- ? parseServerFallbackReceipt(event.content_block)
1110
- : undefined;
976
+ const receipt = parseServerFallbackReceipt(event.content_block);
1111
977
  if (receipt !== undefined) {
1112
- serverFallbackReceipt = receipt;
1113
- serverFallbackAbort.abort();
1114
- break;
978
+ if (options?.abortServerSideFallback === true) {
979
+ serverFallbackReceipt = receipt;
980
+ serverFallbackAbort.abort();
981
+ break;
982
+ }
983
+ usageModel = applyServerFallbackContinuation(output, model, receipt);
1115
984
  }
1116
985
  if (event.content_block.type === "text") {
1117
986
  const block = {