@code-yeongyu/senpi 2026.7.13 → 2026.7.14-3

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 (196) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/core/agent-session.js +1 -1
  3. package/dist/core/agent-session.js.map +1 -1
  4. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  5. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  6. package/dist/core/compaction/branch-summarization.js.map +1 -1
  7. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  8. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +14 -5
  9. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  10. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +1 -1
  11. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
  12. package/dist/core/extensions/builtin/terminal/runtime-session.js +3 -2
  13. package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
  14. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts +1 -1
  15. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/websearch/websearch/native.js +83 -33
  17. package/dist/core/extensions/builtin/websearch/websearch/native.js.map +1 -1
  18. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.d.ts.map +1 -1
  19. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js +2 -1
  20. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js.map +1 -1
  21. package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts.map +1 -1
  22. package/dist/core/extensions/builtin/websearch/websearch/tool.js +3 -3
  23. package/dist/core/extensions/builtin/websearch/websearch/tool.js.map +1 -1
  24. package/dist/core/model-registry.d.ts.map +1 -1
  25. package/dist/core/model-registry.js +18 -3
  26. package/dist/core/model-registry.js.map +1 -1
  27. package/dist/core/model-resolver.d.ts.map +1 -1
  28. package/dist/core/model-resolver.js +1 -0
  29. package/dist/core/model-resolver.js.map +1 -1
  30. package/dist/core/package-manager.d.ts.map +1 -1
  31. package/dist/core/package-manager.js +7 -2
  32. package/dist/core/package-manager.js.map +1 -1
  33. package/dist/core/provider-display-names.d.ts.map +1 -1
  34. package/dist/core/provider-display-names.js +1 -0
  35. package/dist/core/provider-display-names.js.map +1 -1
  36. package/dist/core/radius.d.ts +21 -0
  37. package/dist/core/radius.d.ts.map +1 -0
  38. package/dist/core/radius.js +30 -0
  39. package/dist/core/radius.js.map +1 -0
  40. package/dist/core/system-prompt.d.ts.map +1 -1
  41. package/dist/core/system-prompt.js +1 -11
  42. package/dist/core/system-prompt.js.map +1 -1
  43. package/dist/modes/interactive/interactive-mode.js +2 -2
  44. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  45. package/docs/custom-provider.md +2 -0
  46. package/docs/models.md +2 -0
  47. package/examples/AGENTS.md +34 -0
  48. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  49. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  50. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  51. package/examples/extensions/gondolin/package-lock.json +2 -2
  52. package/examples/extensions/gondolin/package.json +1 -1
  53. package/examples/extensions/sandbox/package-lock.json +2 -2
  54. package/examples/extensions/sandbox/package.json +1 -1
  55. package/examples/extensions/with-deps/package-lock.json +2 -2
  56. package/examples/extensions/with-deps/package.json +1 -1
  57. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +26 -0
  58. package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
  59. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +16 -23
  60. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  61. package/node_modules/@earendil-works/pi-ai/README.md +3 -2
  62. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  63. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +23 -42
  64. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  65. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  66. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +10 -6
  67. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  68. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts +1 -0
  69. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  70. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +1 -1
  71. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  72. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  73. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +12 -3
  74. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  75. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
  76. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +23 -0
  77. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
  78. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts +1 -0
  79. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  80. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +22 -6
  81. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  82. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.d.ts +97 -0
  83. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.d.ts.map +1 -0
  84. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js +307 -0
  85. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js.map +1 -0
  86. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.lazy.d.ts +3 -0
  87. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.lazy.d.ts.map +1 -0
  88. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.lazy.js +3 -0
  89. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.lazy.js.map +1 -0
  90. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts +2 -0
  91. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
  92. package/node_modules/@earendil-works/pi-ai/dist/compat.js +3 -0
  93. package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
  94. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  95. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +1 -0
  96. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
  97. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  98. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  99. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  100. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +233 -164
  101. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  102. package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts +9 -5
  103. package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
  104. package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
  105. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts +60 -0
  106. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  107. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +54 -0
  108. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js.map +1 -1
  109. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +1 -1
  110. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js.map +1 -1
  111. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts +92 -0
  112. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  113. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +72 -0
  114. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  115. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts +1 -6
  116. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts.map +1 -1
  117. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +1 -2
  118. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
  119. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts +57 -0
  120. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts.map +1 -1
  121. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +19 -0
  122. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js.map +1 -1
  123. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +23 -22
  124. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
  125. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +54 -54
  126. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
  127. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +0 -136
  128. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  129. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +2 -138
  130. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  131. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/context-transformer.d.ts.map +1 -1
  132. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/context-transformer.js +9 -0
  133. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/context-transformer.js.map +1 -1
  134. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.d.ts +1 -1
  135. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.d.ts.map +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js +6 -2
  137. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js.map +1 -1
  138. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/coerce-parameters.d.ts +8 -0
  139. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/coerce-parameters.d.ts.map +1 -0
  140. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/coerce-parameters.js +92 -0
  141. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/coerce-parameters.js.map +1 -0
  142. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/format.d.ts +6 -0
  143. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/format.d.ts.map +1 -0
  144. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/format.js +54 -0
  145. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/format.js.map +1 -0
  146. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/index.d.ts +4 -0
  147. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/index.d.ts.map +1 -0
  148. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/index.js +4 -0
  149. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/index.js.map +1 -0
  150. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-match.d.ts +7 -0
  151. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-match.d.ts.map +1 -0
  152. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-match.js +16 -0
  153. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-match.js.map +1 -0
  154. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-tag-scanner.d.ts +19 -0
  155. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-tag-scanner.d.ts.map +1 -0
  156. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-tag-scanner.js +217 -0
  157. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/invoke-tag-scanner.js.map +1 -0
  158. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/parse.d.ts +4 -0
  159. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/parse.d.ts.map +1 -0
  160. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/parse.js +46 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/parse.js.map +1 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream-boundary.d.ts +13 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream-boundary.d.ts.map +1 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream-boundary.js +144 -0
  165. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream-boundary.js.map +1 -0
  166. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream.d.ts +5 -0
  167. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream.d.ts.map +1 -0
  168. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream.js +208 -0
  169. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/stream.js.map +1 -0
  170. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/tool-resolver.d.ts +4 -0
  171. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/tool-resolver.d.ts.map +1 -0
  172. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/tool-resolver.js +17 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/tool-resolver.js.map +1 -0
  174. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/xml-entities.d.ts +5 -0
  175. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/xml-entities.d.ts.map +1 -0
  176. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/xml-entities.js +34 -0
  177. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/anthropic-xml/xml-entities.js.map +1 -0
  178. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/types.d.ts +3 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/types.d.ts.map +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/types.js.map +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +10 -5
  182. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  184. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts +1 -0
  185. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
  186. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js +9 -0
  187. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js.map +1 -1
  188. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/radius.d.ts +39 -0
  189. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/radius.d.ts.map +1 -0
  190. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/radius.js +413 -0
  191. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/radius.js.map +1 -0
  192. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  193. package/node_modules/@earendil-works/pi-pty/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
  194. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  195. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  196. package/package.json +6 -6
@@ -722,6 +722,8 @@ interface ProviderModelConfig {
722
722
  thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
723
723
  chatTemplateKwargs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort"; omitWhenOff?: boolean }>;
724
724
  cacheControlFormat?: "anthropic";
725
+ sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter";
726
+ sendSessionAffinityHeaders?: boolean;
725
727
 
726
728
  // anthropic-messages
727
729
  supportsEagerToolInputStreaming?: boolean;
package/docs/models.md CHANGED
@@ -447,6 +447,8 @@ For providers with partial OpenAI compatibility, use the `compat` field.
447
447
  | `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
448
448
  | `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
449
449
  | `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user/assistant text content. Currently only `anthropic` is supported. |
450
+ | `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
451
+ | `sessionAffinityFormat` | For `openai-completions` and `openai-responses`, the session-affinity header format: `openai` sends `session_id`/`x-client-request-id` (completions also `x-session-affinity`), `openai-nosession` omits the underscore-containing `session_id` header, `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param. Default: auto-detected. |
450
452
  | `supportsStrictMode` | Include the `strict` field in tool definitions |
451
453
  | `supportsLongCacheRetention` | Whether the provider accepts long cache retention when cache retention is `long`: `prompt_cache_retention: "24h"` for OpenAI prompt caching, or `cache_control.ttl: "1h"` when `cacheControlFormat` is `anthropic`. Default: `true`. |
452
454
  | `openRouterRouting` | OpenRouter provider routing preferences. This object is sent as-is in the `provider` field of the [OpenRouter API request](https://openrouter.ai/docs/guides/routing/provider-selection). |
@@ -0,0 +1,34 @@
1
+ # packages/coding-agent/examples
2
+
3
+ Runnable examples for the public Senpi SDK and extension API. Examples are documentation-quality code and must reflect shipped APIs, not private core internals. Unqualified paths below are relative to this directory; `packages/...` paths are repository-relative.
4
+
5
+ ## STRUCTURE
6
+
7
+ ```text
8
+ extensions/ Tools, commands, UI, providers, hooks, resources
9
+ extensions/*/ Multi-file examples and nested private workspaces
10
+ sdk/ Programmatic SDK usage
11
+ rpc-extension-ui.ts RPC-compatible extension UI example
12
+ ```
13
+
14
+ ## CONVENTIONS
15
+
16
+ - Import public package surfaces such as `@code-yeongyu/senpi` and `@earendil-works/pi-ai`; do not reach into `packages/coding-agent/src/core/` internals.
17
+ - Keep examples small enough to teach one pattern, while preserving real error, cleanup, cancellation, and persistence behavior where relevant.
18
+ - Extension factories have no top-level runtime side effects. Register work through the public `pi.*` API and lifecycle events.
19
+ - New interactive examples should use configurable keybindings and themed TUI helpers. Existing demos may keep fixed controls when the control scheme is part of the example. Direct terminal writes belong only in examples explicitly teaching a terminal protocol; ordinary SDK examples may use normal stdout.
20
+ - Tool string enums use the shared `StringEnum` helper for provider compatibility.
21
+ - Stateful examples persist reconstructable state in session entries or tool-result details so fork/resume behavior remains valid.
22
+ - Nested example packages are private workspaces with exact-pinned dependencies. Treat their manifests and lock impact as production dependency changes.
23
+
24
+ ## DOCUMENTATION CONTRACT
25
+
26
+ - Keep `extensions/README.md`, `packages/coding-agent/docs/extensions.md`, and `packages/coding-agent/docs/sdk.md` aligned with public API changes.
27
+ - New public extension capabilities should include a focused example when usage is not obvious from types alone.
28
+ - Do not present experimental or internal behavior as stable API.
29
+
30
+ ## VALIDATION
31
+
32
+ - Run the focused tests for the public API demonstrated by the example.
33
+ - Typecheck examples through root `npm run check`.
34
+ - Interactive examples require real CLI or visual QA when their behavior changes.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.80.6",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.80.6",
9
+ "version": "0.80.7",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.80.6",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.80.6",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
- "version": "0.80.6",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-gondolin",
9
- "version": "0.80.6",
9
+ "version": "0.80.7",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-gondolin",
3
3
  "private": true,
4
- "version": "0.80.6",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "1.10.6",
3
+ "version": "1.10.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "1.10.6",
9
+ "version": "1.10.7",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "1.10.6",
4
+ "version": "1.10.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.80.6",
3
+ "version": "0.80.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.80.6",
9
+ "version": "0.80.7",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.80.6",
4
+ "version": "0.80.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [2026.7.14-3] - 2026-07-14
4
+
5
+ ### Added
6
+
7
+ ### Changed
8
+
9
+ ### Fixed
10
+
11
+ ## [2026.7.14-2] - 2026-07-14
12
+
13
+ ### Added
14
+
15
+ ### Changed
16
+
17
+ ### Fixed
18
+
19
+ ## [2026.7.14] - 2026-07-14
20
+
21
+ ### Added
22
+
23
+ ### Changed
24
+
25
+ - Improved the `eval` prompt instructions and reuse-chain examples to teach persistent-state reuse, batch file processing, and parallel session-tool fan-out within a single cell.
26
+
27
+ ### Fixed
28
+
3
29
  ## [2026.7.13] - 2026-07-13
4
30
 
5
31
  ### Added
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.7.13",
3
+ "version": "2026.7.14-3",
4
4
  "description": "Source-only senpi extension package for codemode evaluation tools",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -30,14 +30,14 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@babel/parser": "7.29.7",
33
- "@earendil-works/pi-ai": "^2026.7.13",
33
+ "@earendil-works/pi-ai": "^2026.7.14-3",
34
34
  "typebox": "1.1.38"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@code-yeongyu/senpi": "*"
38
38
  },
39
39
  "devDependencies": {
40
- "@code-yeongyu/senpi": "2026.7.13"
40
+ "@code-yeongyu/senpi": "2026.7.14-3"
41
41
  },
42
42
  "keywords": [
43
43
  "senpi",
@@ -25,37 +25,28 @@ type EvalPromptExample = {
25
25
  readonly code: string;
26
26
  };
27
27
 
28
- // senpi ToolDefinition has no examples field, so description embeds parity examples.
28
+ // senpi ToolDefinition has no examples field, so description embeds the examples.
29
+ // ADAPTATION: payloads diverge from omp's json-config chain to teach batch read,
30
+ // comprehension filtering, and parallel tool.<name> fan-out while keeping the
31
+ // three-cell reuse narrative.
29
32
  const REUSE_CHAIN_EXAMPLES = [
30
33
  {
31
34
  caption: "First call — set up once",
32
35
  language: "py",
33
- title: "imports",
34
- code: "import json\nfrom pathlib import Path",
36
+ title: "collect targets",
37
+ code: "from pathlib import Path\nfrom collections import Counter\nfiles = [p for p in Path('src').rglob('*.ts') if 'test' not in p.parts]\nprint(len(files))",
35
38
  },
36
39
  {
37
- caption: "Second call — reuse, do NOT re-import",
40
+ caption: "Second call — reuse `files`, batch-read in one cell",
38
41
  language: "py",
39
- title: "load config",
40
- code: "data = json.loads(read('package.json'))\ndisplay(data)",
42
+ title: "scan usages",
43
+ code: "hits = Counter()\nfor p in files:\n hits[p.name] = read(p).count('legacyClient')\ndisplay({k: v for k, v in hits.items() if v})",
41
44
  },
42
45
  {
43
- caption: "Third call — reuse the loaded config",
46
+ caption: "Third call — reuse results, fan out session tools in parallel",
44
47
  language: "py",
45
- title: "scan deps",
46
- code: "display(sorted(data['dependencies']))",
47
- },
48
- {
49
- caption: "Ruby first call — set up once",
50
- language: "rb",
51
- title: "setup",
52
- code: "require 'json'\npkg_path = 'package.json'",
53
- },
54
- {
55
- caption: "Ruby second call — reuse, do NOT re-require",
56
- language: "rb",
57
- title: "load config",
58
- code: "pkg = JSON.parse(read(pkg_path))\ndisplay(pkg.keys.sort)",
48
+ title: "confirm callsites",
49
+ code: "dirs = ['src/core', 'src/tools']\ndisplay(parallel([lambda d=d: tool.grep({'pattern': 'legacyClient', 'path': d}) for d in dirs]))",
59
50
  },
60
51
  ] as const satisfies readonly EvalPromptExample[];
61
52
 
@@ -64,7 +55,9 @@ const EVAL_PROMPT_TEMPLATE = `Run one step of code in a persistent kernel.
64
55
  <instruction>
65
56
  **One eval call = one cell = one logical step.** State persists per language across separate eval calls and tool calls{{#if spawns}}, and \`task\` subagents{{/if}} — define helpers, datasets, and clients in one call, then later calls reuse them directly.
66
57
 
67
- Work incrementally: imports in one call, define in the next, test, then use — each its own eval call. Re-run setup ONLY after \`reset\`, a kernel crash, or a \`NameError\`/\`ReferenceError\` proving the state is gone. Parallelize work *within* a cell with the \`parallel(thunks)\` helper, not by batching steps.
58
+ Work incrementally: imports in one call, define in the next, test, then use — each its own eval call. Re-run setup ONLY after \`reset\`, a kernel crash, or a \`NameError\`/\`ReferenceError\` proving the state is gone.
59
+
60
+ **Batch through eval.** One cell replaces many one-shot tool calls: loop or comprehend over file sets with \`read()\`/stdlib instead of reading files one call at a time, post-process \`tool.<name>()\` results programmatically, and fan independent calls through \`parallel(thunks)\` within the cell — never one call per turn.
68
61
 
69
62
  Fields:
70
63
 
@@ -125,7 +118,7 @@ Pipe handles through stage helpers to build a dependency graph — acyclic waves
125
118
  {{/if}}
126
119
 
127
120
  <critical>
128
- Prior top-level names (\`data\`, \`sessions\`, helpers, imports) survive into the next eval call — reuse them; NEVER re-import, re-require, or re-declare a helper. Re-read a file only if it may have changed since the last read. Re-run setup only after \`reset\`, a crash, or a \`NameError\`/\`ReferenceError\`.
121
+ Prior top-level names (\`data\`, \`sessions\`, helpers, imports) survive into the next eval call — reuse them; NEVER re-import, re-require, or re-declare a helper. Re-read a file only if it may have changed since the last read.
129
122
  </critical>`;
130
123
 
131
124
  export function buildEvalPrompt(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-agent-core",
3
3
  "private": true,
4
- "version": "2026.7.13",
4
+ "version": "2026.7.14-3",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "prepublishOnly": "npm run clean && npm run build"
31
31
  },
32
32
  "dependencies": {
33
- "@earendil-works/pi-ai": "^2026.7.13",
33
+ "@earendil-works/pi-ai": "^2026.7.14-3",
34
34
  "ignore": "7.0.5",
35
35
  "typebox": "1.1.38",
36
36
  "yaml": "2.9.0"
@@ -1070,7 +1070,8 @@ interface OpenAICompletionsCompat {
1070
1070
  supportsReasoningEffort?: boolean; // Whether provider supports `reasoning_effort` (default: true)
1071
1071
  supportsUsageInStreaming?: boolean; // Whether provider supports `stream_options: { include_usage: true }` (default: true)
1072
1072
  supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: true)
1073
- sendSessionAffinityHeaders?: boolean; // Whether to send `session_id`, `x-client-request-id`, and `x-session-affinity` from `sessionId` when caching is enabled (default: false)
1073
+ sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: false)
1074
+ sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Format for session affinity: 'openai' uses `prompt_cache_key`, `session_id`, `x-client-request-id`, and `x-session-affinity`; 'openai-nosession' uses `prompt_cache_key`, `x-client-request-id`, and `x-session-affinity`; 'openrouter' uses `x-session-id` (default: auto-detected)
1074
1075
  maxTokensField?: 'max_completion_tokens' | 'max_tokens'; // Which field name to use (default: max_completion_tokens)
1075
1076
  requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
1076
1077
  requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false)
@@ -1085,7 +1086,7 @@ interface OpenAICompletionsCompat {
1085
1086
 
1086
1087
  interface OpenAIResponsesCompat {
1087
1088
  supportsDeveloperRole?: boolean; // Whether provider supports `developer` role vs `system` (default: true)
1088
- sendSessionIdHeader?: boolean; // Whether to send `session_id` from `sessionId` when caching is enabled (default: true)
1089
+ sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Session-affinity header format: 'openai' sends `session_id` and `x-client-request-id`; 'openai-nosession' sends `x-client-request-id`; 'openrouter' sends `x-session-id`. Does not affect the `prompt_cache_key` body param (default: auto-detected)
1089
1090
  supportsLongCacheRetention?: boolean; // Whether provider supports `prompt_cache_retention: "24h"` (default: true)
1090
1091
  }
1091
1092
  ```
@@ -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;AAU1C,OAAO,KAAK,EAWX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAgKrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AAuDhE,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;;;;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;AAkiBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAoTzE,CAAC;AAoEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA2ClF,CAAC"}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAU1C,OAAO,KAAK,EAWX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAgKrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AAuDhE,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;;;;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;AAghBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAsTzE,CAAC;AAoEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA2ClF,CAAC"}
@@ -200,24 +200,6 @@ const REPLAYABLE_ANTHROPIC_PROVIDER_NATIVE_TYPES = new Set([
200
200
  function isReplayableAnthropicProviderNativeBlock(raw) {
201
201
  return isRecord(raw) && typeof raw.type === "string" && REPLAYABLE_ANTHROPIC_PROVIDER_NATIVE_TYPES.has(raw.type);
202
202
  }
203
- function stripEncryptedContentFromWebSearchResult(raw) {
204
- const { encrypted_content: _encryptedContent, ...rest } = raw;
205
- return rest;
206
- }
207
- function sanitizeReplayableAnthropicProviderNativeBlock(raw) {
208
- if (!isReplayableAnthropicProviderNativeBlock(raw))
209
- return undefined;
210
- if (!isRecord(raw) || raw.type !== "web_search_tool_result" || !Array.isArray(raw.content))
211
- return raw;
212
- return {
213
- ...raw,
214
- content: raw.content.map((item) => {
215
- if (!isRecord(item) || item.type !== "web_search_result")
216
- return item;
217
- return stripEncryptedContentFromWebSearchResult(item);
218
- }),
219
- };
220
- }
221
203
  function isSameAnthropicModel(message, model) {
222
204
  return message.provider === model.provider && message.api === model.api && message.model === model.id;
223
205
  }
@@ -837,25 +819,27 @@ export const stream = (model, context, options) => {
837
819
  }
838
820
  // Only update usage fields if present (not null).
839
821
  // Preserves input_tokens from message_start when proxies omit it in message_delta.
840
- if (event.usage.input_tokens != null) {
841
- output.usage.input = event.usage.input_tokens;
842
- }
843
- if (event.usage.output_tokens != null) {
844
- output.usage.output = event.usage.output_tokens;
845
- }
846
- if (event.usage.cache_read_input_tokens != null) {
847
- output.usage.cacheRead = event.usage.cache_read_input_tokens;
848
- }
849
- if (event.usage.cache_creation_input_tokens != null) {
850
- output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
851
- }
852
- // Anthropic reports reasoning tokens in `output_tokens_details.thinking_tokens` on the
853
- // final message_delta usage (a subset of output_tokens). SDK 0.91.1 omits the field from
854
- // its Usage type, so read it through a narrow cast. Verified against the live API.
855
- const thinkingTokens = event.usage
856
- .output_tokens_details?.thinking_tokens;
857
- if (thinkingTokens != null) {
858
- output.usage.reasoning = thinkingTokens;
822
+ if (event.usage) {
823
+ if (event.usage.input_tokens != null) {
824
+ output.usage.input = event.usage.input_tokens;
825
+ }
826
+ if (event.usage.output_tokens != null) {
827
+ output.usage.output = event.usage.output_tokens;
828
+ }
829
+ if (event.usage.cache_read_input_tokens != null) {
830
+ output.usage.cacheRead = event.usage.cache_read_input_tokens;
831
+ }
832
+ if (event.usage.cache_creation_input_tokens != null) {
833
+ output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
834
+ }
835
+ // Anthropic reports reasoning tokens in `output_tokens_details.thinking_tokens` on the
836
+ // final message_delta usage (a subset of output_tokens). SDK 0.91.1 omits the field from
837
+ // its Usage type, so read it through a narrow cast. Verified against the live API.
838
+ const thinkingTokens = event.usage
839
+ .output_tokens_details?.thinking_tokens;
840
+ if (thinkingTokens != null) {
841
+ output.usage.reasoning = thinkingTokens;
842
+ }
859
843
  }
860
844
  // Anthropic doesn't provide total_tokens, compute from components
861
845
  output.usage.totalTokens =
@@ -1336,11 +1320,8 @@ function convertMessages(transformedMessages, model, isOAuthToken, cacheControl,
1336
1320
  });
1337
1321
  }
1338
1322
  else if (block.type === "providerNative") {
1339
- if (isSameModel) {
1340
- const replayableBlock = sanitizeReplayableAnthropicProviderNativeBlock(block.raw);
1341
- if (replayableBlock)
1342
- blocks.push(replayableBlock);
1343
- }
1323
+ if (isSameModel && isReplayableAnthropicProviderNativeBlock(block.raw))
1324
+ blocks.push(block.raw);
1344
1325
  }
1345
1326
  }
1346
1327
  if (blocks.length === 0)