pi-python-core 0.10.1__tar.gz → 0.11.0__tar.gz

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 (371) hide show
  1. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/PKG-INFO +2 -2
  2. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/README.md +1 -1
  3. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/README.zh-CN.md +1 -1
  4. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/COVERAGE.md +24 -1
  5. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/verification.json +38 -25
  6. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/API.md +34 -3
  7. pi_python_core-0.11.0/docs/APPLICATION_TOOLS_IMPLEMENTATION.md +76 -0
  8. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/CODING_AGENTS.md +2 -0
  9. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/PROVIDERS.md +37 -1
  10. pi_python_core-0.11.0/docs/releases/0.10.2.md +52 -0
  11. pi_python_core-0.11.0/docs/releases/0.11.0.md +40 -0
  12. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/API.md +34 -3
  13. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/CODING_AGENTS.md +2 -0
  14. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/PROVIDERS.md +37 -1
  15. pi_python_core-0.11.0/examples/capability_composition.py +149 -0
  16. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/pyproject.toml +1 -1
  17. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/__init__.py +2 -0
  18. pi_python_core-0.11.0/src/pi_python/_version.py +1 -0
  19. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/agent.py +88 -4
  20. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/events.py +3 -4
  21. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/features.py +2 -0
  22. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/loop.py +44 -6
  23. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/messages.py +57 -0
  24. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/provider.py +18 -0
  25. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/openai.py +23 -11
  26. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/run.py +91 -11
  27. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/tools.py +39 -2
  28. pi_python_core-0.11.0/tests/test_application_compatibility.py +207 -0
  29. pi_python_core-0.11.0/tests/test_application_tools.py +511 -0
  30. pi_python_core-0.11.0/tests/test_cache_sessions.py +310 -0
  31. pi_python_core-0.11.0/tests/test_capability_composition.py +59 -0
  32. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_examples.py +1 -0
  33. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_features.py +8 -1
  34. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_websocket_continuation.py +49 -3
  35. pi_python_core-0.10.1/src/pi_python/_version.py +0 -1
  36. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/.gitignore +0 -0
  37. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/LICENSE +0 -0
  38. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/NOTICE +0 -0
  39. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/__init__.py +0 -0
  40. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/baseline.json +0 -0
  41. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/contracts/release-candidate.example.json +0 -0
  42. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/contracts/release-candidate.schema.json +0 -0
  43. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/contracts/upstream-baseline.json +0 -0
  44. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C01-text.json +0 -0
  45. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C02-tool.json +0 -0
  46. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C03-sequential.json +0 -0
  47. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C04-parallel.json +0 -0
  48. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C05-one-sequential.json +0 -0
  49. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C06-one-failure.json +0 -0
  50. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C07-unknown.json +0 -0
  51. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C08-prepare.json +0 -0
  52. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C09-before-error.json +0 -0
  53. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C09-block.json +0 -0
  54. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C09-replace.json +0 -0
  55. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C10-length.json +0 -0
  56. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C11-transform.json +0 -0
  57. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C12-system.json +0 -0
  58. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C13-steering.json +0 -0
  59. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C14-followup-all.json +0 -0
  60. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C14-followup-one.json +0 -0
  61. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C15-prepare.json +0 -0
  62. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C16-continue.json +0 -0
  63. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C16-end.json +0 -0
  64. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C16-mixed.json +0 -0
  65. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C16-terminate.json +0 -0
  66. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C19-abort-stream.json +0 -0
  67. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C19-abort-tools.json +0 -0
  68. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/fixtures/C24-provider-error.json +0 -0
  69. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-cases.json +0 -0
  70. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/prompts/notes.txt +0 -0
  71. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/prompts/plan.md +0 -0
  72. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/prompts/review.md +0 -0
  73. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/prompts/sub/nested.md +0 -0
  74. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/prompts/summarize.md +0 -0
  75. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/.hidden/SKILL.md +0 -0
  76. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/alpha/SKILL.md +0 -0
  77. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/alpha/references/notes.md +0 -0
  78. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/beta/SKILL.md +0 -0
  79. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/broken/SKILL.md +0 -0
  80. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/delta/SKILL.md +0 -0
  81. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/gamma/SKILL.md +0 -0
  82. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/longdesc/SKILL.md +0 -0
  83. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/loose.md +0 -0
  84. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/nested/group/epsilon/SKILL.md +0 -0
  85. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/noname/SKILL.md +0 -0
  86. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/plain.md +0 -0
  87. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/zeta/SKILL.md +0 -0
  88. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/plugin-fixtures/skills/zeta/sub/eta/SKILL.md +0 -0
  89. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-oauth.json +0 -0
  90. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-request-cache-long.json +0 -0
  91. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-request-cache-none.json +0 -0
  92. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-request-replay.json +0 -0
  93. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-request-tools-image.json +0 -0
  94. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-text.json +0 -0
  95. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-thinking.json +0 -0
  96. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/anthropic-tool.json +0 -0
  97. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/codex-request-tools-image.json +0 -0
  98. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/codex-text.json +0 -0
  99. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-compat-strict.json +0 -0
  100. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-deepseek.json +0 -0
  101. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-format-ant-ling.json +0 -0
  102. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-format-baseten.json +0 -0
  103. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-format-string-thinking.json +0 -0
  104. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-format-together.json +0 -0
  105. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-format-zai.json +0 -0
  106. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-midconvo-tools.json +0 -0
  107. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-moonshot-usage.json +0 -0
  108. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-openai-off.json +0 -0
  109. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-openrouter.json +0 -0
  110. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-qwen-length.json +0 -0
  111. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-reasoning-content.json +0 -0
  112. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-request-replay.json +0 -0
  113. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-text.json +0 -0
  114. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/completions-tool-stream.json +0 -0
  115. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-incomplete.json +0 -0
  116. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-request-cache-long.json +0 -0
  117. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-request-cache-none.json +0 -0
  118. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-request-reasoning.json +0 -0
  119. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-request-replay.json +0 -0
  120. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-request-tools-image.json +0 -0
  121. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-text.json +0 -0
  122. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-thinking-backfill.json +0 -0
  123. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-thinking.json +0 -0
  124. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/openai-tool.json +0 -0
  125. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/proxy-text.json +0 -0
  126. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/proxy-thinking-tool.json +0 -0
  127. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-anthropic-collapse-budget.json +0 -0
  128. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-anthropic-native-tools.json +0 -0
  129. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-anthropic-oauth-effort.json +0 -0
  130. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-anthropic-redefinition.json +0 -0
  131. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-anthropic-server-fallback.json +0 -0
  132. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-anthropic-thinking-off.json +0 -0
  133. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-codex-tool-search.json +0 -0
  134. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-openai-additional-tools.json +0 -0
  135. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-openai-collapse.json +0 -0
  136. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-openai-explicit-cache-long.json +0 -0
  137. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-openai-explicit-cache-none.json +0 -0
  138. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/provider-fixtures/session-openai-removal.json +0 -0
  139. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/recovery-cases.json +0 -0
  140. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/release.py +0 -0
  141. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C01-text.python.json +0 -0
  142. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C01-text.upstream.json +0 -0
  143. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C02-tool.python.json +0 -0
  144. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C02-tool.upstream.json +0 -0
  145. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C03-sequential.python.json +0 -0
  146. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C03-sequential.upstream.json +0 -0
  147. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C04-parallel.python.json +0 -0
  148. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C04-parallel.upstream.json +0 -0
  149. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C05-one-sequential.python.json +0 -0
  150. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C05-one-sequential.upstream.json +0 -0
  151. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C06-one-failure.python.json +0 -0
  152. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C06-one-failure.upstream.json +0 -0
  153. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C07-unknown.python.json +0 -0
  154. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C07-unknown.upstream.json +0 -0
  155. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C08-prepare.python.json +0 -0
  156. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C08-prepare.upstream.json +0 -0
  157. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C09-before-error.python.json +0 -0
  158. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C09-before-error.upstream.json +0 -0
  159. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C09-block.python.json +0 -0
  160. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C09-block.upstream.json +0 -0
  161. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C09-replace.python.json +0 -0
  162. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C09-replace.upstream.json +0 -0
  163. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C10-length.python.json +0 -0
  164. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C10-length.upstream.json +0 -0
  165. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C11-transform.python.json +0 -0
  166. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C11-transform.upstream.json +0 -0
  167. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C12-system.python.json +0 -0
  168. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C12-system.upstream.json +0 -0
  169. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C13-steering.python.json +0 -0
  170. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C13-steering.upstream.json +0 -0
  171. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C14-followup-all.python.json +0 -0
  172. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C14-followup-all.upstream.json +0 -0
  173. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C14-followup-one.python.json +0 -0
  174. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C14-followup-one.upstream.json +0 -0
  175. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C15-prepare.python.json +0 -0
  176. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C15-prepare.upstream.json +0 -0
  177. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-continue.python.json +0 -0
  178. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-continue.upstream.json +0 -0
  179. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-end.python.json +0 -0
  180. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-end.upstream.json +0 -0
  181. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-mixed.python.json +0 -0
  182. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-mixed.upstream.json +0 -0
  183. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-terminate.python.json +0 -0
  184. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C16-terminate.upstream.json +0 -0
  185. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C19-abort-stream.python.json +0 -0
  186. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C19-abort-stream.upstream.json +0 -0
  187. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C19-abort-tools.python.json +0 -0
  188. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C19-abort-tools.upstream.json +0 -0
  189. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C24-provider-error.python.json +0 -0
  190. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/C24-provider-error.upstream.json +0 -0
  191. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/artifacts.json +0 -0
  192. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/benchmark.json +0 -0
  193. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/bootstrap.json +0 -0
  194. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/candidate-live-noop.json +0 -0
  195. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/conformance.json +0 -0
  196. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/linux-3.11.json +0 -0
  197. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/linux-3.12.json +0 -0
  198. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/linux-3.13.json +0 -0
  199. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/linux-3.14.json +0 -0
  200. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/linux-install-hash-repair.json +0 -0
  201. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-0.5-regression.json +0 -0
  202. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-0.5-session-claude.json +0 -0
  203. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-0.5-session-codex.json +0 -0
  204. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-claude-reasoning-replay.json +0 -0
  205. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-final-matrix.json +0 -0
  206. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-initial.json +0 -0
  207. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-loop-initial.json +0 -0
  208. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-providers.json +0 -0
  209. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-reasoning-replay.json +0 -0
  210. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-refresh.json +0 -0
  211. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-regression-0.4-deepseek.json +0 -0
  212. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-regression-0.4-first-run.json +0 -0
  213. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-regression-0.4.json +0 -0
  214. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-session-claude.json +0 -0
  215. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-session-codex.json +0 -0
  216. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-websocket-cached-recheck.json +0 -0
  217. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-websocket-cached.json +0 -0
  218. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/live-websocket-final.json +0 -0
  219. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/plugins.upstream.json +0 -0
  220. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/provider-conformance.json +0 -0
  221. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/recovery-conformance.upstream.json +0 -0
  222. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/runtime-dependencies.json +0 -0
  223. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/source-verification.json +0 -0
  224. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.1.0/artifacts.json +0 -0
  225. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.1.0/linux-3.11.json +0 -0
  226. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.1.0/linux-3.12.json +0 -0
  227. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.1.0/linux-3.13.json +0 -0
  228. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.1.0/verification.json +0 -0
  229. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.2.0/artifacts.json +0 -0
  230. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.2.0/linux-3.11.json +0 -0
  231. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.2.0/linux-3.12.json +0 -0
  232. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.2.0/linux-3.13.json +0 -0
  233. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.2.0/verification.json +0 -0
  234. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/against-0.4-fixtures.json +0 -0
  235. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/artifacts.json +0 -0
  236. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/linux-3.11.json +0 -0
  237. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/linux-3.12.json +0 -0
  238. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/linux-3.13.json +0 -0
  239. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/provider-conformance.json +0 -0
  240. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.3.0/verification.json +0 -0
  241. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.4.0/artifacts.json +0 -0
  242. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.4.0/linux-3.11.json +0 -0
  243. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.4.0/linux-3.12.json +0 -0
  244. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.4.0/linux-3.13.json +0 -0
  245. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.4.0/verification.json +0 -0
  246. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.5.0/artifacts.json +0 -0
  247. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.5.0/linux-3.11.json +0 -0
  248. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.5.0/linux-3.12.json +0 -0
  249. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.5.0/linux-3.13.json +0 -0
  250. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.5.0/verification.json +0 -0
  251. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.6.0/artifacts.json +0 -0
  252. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.6.0/linux-3.11.json +0 -0
  253. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.6.0/linux-3.12.json +0 -0
  254. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.6.0/linux-3.13.json +0 -0
  255. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.6.0/verification.json +0 -0
  256. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.7.0/artifacts.json +0 -0
  257. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.7.0/linux-3.11.json +0 -0
  258. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.7.0/linux-3.12.json +0 -0
  259. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.7.0/linux-3.13.json +0 -0
  260. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.7.0/linux-3.14.json +0 -0
  261. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.7.0/verification.json +0 -0
  262. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.8.0/artifacts.json +0 -0
  263. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.8.0/linux-3.11.json +0 -0
  264. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.8.0/linux-3.12.json +0 -0
  265. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.8.0/linux-3.13.json +0 -0
  266. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.8.0/linux-3.14.json +0 -0
  267. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/v0.8.0/verification.json +0 -0
  268. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/results/websocket-conformance.json +0 -0
  269. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/runner.py +0 -0
  270. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/websocket-fixtures/codex-continuation-lost-state.json +0 -0
  271. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/compat/websocket-fixtures/codex-continuation-tool-loop.json +0 -0
  272. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/CONCEPTS.md +0 -0
  273. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/IMPLEMENTATION.md +0 -0
  274. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/MCP_INTERACTION.md +0 -0
  275. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/PLUGINS.md +0 -0
  276. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/PLUGIN_DEVELOPMENT.md +0 -0
  277. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/history/IMPLEMENTATION-0.3.0.md +0 -0
  278. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/history/IMPLEMENTATION-0.4.0.md +0 -0
  279. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/history/IMPLEMENTATION-0.5.0.md +0 -0
  280. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/history/IMPLEMENTATION-0.6.0.md +0 -0
  281. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/history/IMPLEMENTATION-0.7.0.md +0 -0
  282. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/history/IMPLEMENTATION-0.8.0.md +0 -0
  283. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/CONCEPTS.md +0 -0
  284. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/MCP_HOST_VERIFICATION.md +0 -0
  285. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/MCP_INTERACTION.md +0 -0
  286. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/PLUGINS.md +0 -0
  287. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/docs/zh/PLUGIN_DEVELOPMENT.md +0 -0
  288. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/in_memory.py +0 -0
  289. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/local_model.py +0 -0
  290. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/mcp_interactive.py +0 -0
  291. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/mcp_interactive_server.py +0 -0
  292. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/mcp_tools.py +0 -0
  293. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/memory_demo.py +0 -0
  294. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugin_demo.py +0 -0
  295. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugins/lab_tools/agents/reviewer.md +0 -0
  296. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugins/lab_tools/ops.py +0 -0
  297. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugins/lab_tools/plugin.py +0 -0
  298. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugins/lab_tools/prompts/dedup.md +0 -0
  299. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugins/lab_tools/skills/dedup-window/SKILL.md +0 -0
  300. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/plugins/memory/plugin.py +0 -0
  301. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/provider_chat.py +0 -0
  302. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/quickstart.py +0 -0
  303. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/recovery.py +0 -0
  304. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/save_restore.py +0 -0
  305. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/examples/subagent.py +0 -0
  306. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/_mcp_host.py +0 -0
  307. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/_mcp_interaction.py +0 -0
  308. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/_mcp_process.py +0 -0
  309. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/_mcp_sampling.py +0 -0
  310. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/cancellation.py +0 -0
  311. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/data/models.json +0 -0
  312. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/errors.py +0 -0
  313. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/estimate.py +0 -0
  314. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/function_tools.py +0 -0
  315. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/hooks.py +0 -0
  316. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/limits.py +0 -0
  317. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/lowlevel.py +0 -0
  318. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/mcp.py +0 -0
  319. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/models.py +0 -0
  320. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/plugins/__init__.py +0 -0
  321. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/plugins/_compose.py +0 -0
  322. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/plugins/_frontmatter.py +0 -0
  323. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/plugins/_resources.py +0 -0
  324. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/plugins/_subagents.py +0 -0
  325. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/__init__.py +0 -0
  326. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/anthropic.py +0 -0
  327. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/common.py +0 -0
  328. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/completions.py +0 -0
  329. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/oauth.py +0 -0
  330. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/providers/transport.py +0 -0
  331. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/proxy.py +0 -0
  332. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/py.typed +0 -0
  333. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/queues.py +0 -0
  334. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/recovery.py +0 -0
  335. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/stream.py +0 -0
  336. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/sync.py +0 -0
  337. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/tasks.py +0 -0
  338. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/testing.py +0 -0
  339. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/src/pi_python/transcript.py +0 -0
  340. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/servers/mcp_interactive_server.py +0 -0
  341. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/servers/mcp_process_server.py +0 -0
  342. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/servers/mcp_server.py +0 -0
  343. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_completions.py +0 -0
  344. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_conformance.py +0 -0
  345. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_contract_v3.py +0 -0
  346. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_control.py +0 -0
  347. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_extended_core.py +0 -0
  348. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_function_tools.py +0 -0
  349. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_lifecycle_contracts.py +0 -0
  350. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_loop.py +0 -0
  351. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_mcp.py +0 -0
  352. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_mcp_delta.py +0 -0
  353. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_mcp_host.py +0 -0
  354. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_mcp_interaction.py +0 -0
  355. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_mcp_process.py +0 -0
  356. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_messages.py +0 -0
  357. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_models.py +0 -0
  358. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_nested_agents.py +0 -0
  359. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_oauth.py +0 -0
  360. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_plugin_conformance.py +0 -0
  361. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_plugins.py +0 -0
  362. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_providers.py +0 -0
  363. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_recovery.py +0 -0
  364. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_recovery_conformance.py +0 -0
  365. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_recovery_helpers.py +0 -0
  366. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_release.py +0 -0
  367. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_session_changes.py +0 -0
  368. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_sync.py +0 -0
  369. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_tasks.py +0 -0
  370. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_tools.py +0 -0
  371. {pi_python_core-0.10.1 → pi_python_core-0.11.0}/tests/test_typeddict_annotations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pi-python-core
3
- Version: 0.10.1
3
+ Version: 0.11.0
4
4
  Summary: An embeddable asyncio agent core with opt-in Claude and OpenAI providers
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -111,7 +111,7 @@ Apart from `provider_chat`, which needs real credentials, every example runs off
111
111
 
112
112
  The run loop, event order, hooks, queues, and the handling of errors and cancellation all match Pi, and this is checked differentially: the same inputs go to the pinned upstream code and to this library, and the model requests, tool calls, events and final transcript are compared item by item. All cases currently agree: 25 for the core loop, 50 for model connectors, 2 for multi-turn WebSocket, and 44 error-classification samples. The plugin rules for skills, prompt templates and frontmatter are compared the same way against Pi's coding-agent code.
113
113
 
114
- A few differences are deliberate, such as strict tool-argument validation without type coercion, and returning copies of state to callers. Others are additions for Python users, such as `@tool`, blocking calls, and calling `continue_run()` directly after a failure. Each one is recorded in the [coverage map](compat/COVERAGE.md) (Chinese). Features Pi keeps in its application layer (terminal UI, session file format, context compaction) are not in this core; compaction can be built with hooks, and an example shows the full approach. Plugins are the one piece taken from that layer: an optional module that reads Pi's package format and builds an ordinary Agent from it. This project uses its own version numbers and is not an official Pi release.
114
+ A few differences are deliberate, such as strict tool-argument validation without type coercion, and returning copies of state to callers. Others are additions for Python users, such as `@tool`, blocking calls, and calling `continue_run()` directly after a failure. Each one is recorded in the [coverage map](compat/COVERAGE.md) (Chinese). Features Pi keeps in its application layer (terminal UI, session file format, context compaction) are not in this core; compaction can be built with hooks, and an example shows the full approach. The Agent also supplies cache-only session IDs for Codex, a responsibility upstream assigns to its coding-agent layer; see [cache sessions and the opt-out](docs/PROVIDERS.md#automatic-codex-cache-sessions). Plugins are another piece taken from that layer: an optional module that reads Pi's package format and builds an ordinary Agent from it. This project uses its own version numbers and is not an official Pi release.
115
115
 
116
116
  ## Documentation
117
117
 
@@ -77,7 +77,7 @@ Apart from `provider_chat`, which needs real credentials, every example runs off
77
77
 
78
78
  The run loop, event order, hooks, queues, and the handling of errors and cancellation all match Pi, and this is checked differentially: the same inputs go to the pinned upstream code and to this library, and the model requests, tool calls, events and final transcript are compared item by item. All cases currently agree: 25 for the core loop, 50 for model connectors, 2 for multi-turn WebSocket, and 44 error-classification samples. The plugin rules for skills, prompt templates and frontmatter are compared the same way against Pi's coding-agent code.
79
79
 
80
- A few differences are deliberate, such as strict tool-argument validation without type coercion, and returning copies of state to callers. Others are additions for Python users, such as `@tool`, blocking calls, and calling `continue_run()` directly after a failure. Each one is recorded in the [coverage map](compat/COVERAGE.md) (Chinese). Features Pi keeps in its application layer (terminal UI, session file format, context compaction) are not in this core; compaction can be built with hooks, and an example shows the full approach. Plugins are the one piece taken from that layer: an optional module that reads Pi's package format and builds an ordinary Agent from it. This project uses its own version numbers and is not an official Pi release.
80
+ A few differences are deliberate, such as strict tool-argument validation without type coercion, and returning copies of state to callers. Others are additions for Python users, such as `@tool`, blocking calls, and calling `continue_run()` directly after a failure. Each one is recorded in the [coverage map](compat/COVERAGE.md) (Chinese). Features Pi keeps in its application layer (terminal UI, session file format, context compaction) are not in this core; compaction can be built with hooks, and an example shows the full approach. The Agent also supplies cache-only session IDs for Codex, a responsibility upstream assigns to its coding-agent layer; see [cache sessions and the opt-out](docs/PROVIDERS.md#automatic-codex-cache-sessions). Plugins are another piece taken from that layer: an optional module that reads Pi's package format and builds an ordinary Agent from it. This project uses its own version numbers and is not an official Pi release.
81
81
 
82
82
  ## Documentation
83
83
 
@@ -77,7 +77,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
77
77
 
78
78
  执行循环、事件顺序、钩子、队列、出错与取消的处理都与 Pi 一致,并用差分验证:同一组输入分别交给固定版本的上游代码和本库运行,逐项比较模型请求、工具调用、事件和最终记录。目前核心循环 25 组、模型接入 50 组、WebSocket 多轮 2 组、出错判断 44 条样例全部一致。插件里技能、提示模板和文件头元数据的规则,也用同样的方法和 Pi 的 coding-agent 代码对照。
79
79
 
80
- 有几处是有意的差异,例如工具参数严格校验、不自动转换类型,返回给调用者的状态是副本;另一些是为 Python 用户加的,例如 `@tool`、同步调用、失败后直接 `continue_run()`。逐项记录见[验收映射](compat/COVERAGE.md)。Pi 放在应用层的功能(终端界面、会话文件格式、上下文压缩)不在本库核心里;压缩可以用钩子实现,示例里有完整做法。插件是从应用层取来的唯一一块:一个可选模块,读取 Pi 的 package 格式,再据此构造普通的 Agent。本项目使用自己的版本号,并非 Pi 官方发行版。
80
+ 有几处是有意的差异,例如工具参数严格校验、不自动转换类型,返回给调用者的状态是副本;另一些是为 Python 用户加的,例如 `@tool`、同步调用、失败后直接 `continue_run()`。逐项记录见[验收映射](compat/COVERAGE.md)。Pi 放在应用层的功能(终端界面、会话文件格式、上下文压缩)不在本库核心里;压缩可以用钩子实现,示例里有完整做法。Agent 还为 Codex 自动提供缓存专用会话标识,补齐上游 coding-agent 层承担的职责;见[缓存会话与关闭方式](docs/zh/PROVIDERS.md#codex-自动缓存会话)。插件也是从应用层取来的功能:一个可选模块,读取 Pi 的 package 格式,再据此构造普通的 Agent。本项目使用自己的版本号,并非 Pi 官方发行版。
81
81
 
82
82
  ## 文档
83
83
 
@@ -1,6 +1,6 @@
1
1
  # 验收映射
2
2
 
3
- 首期测试证明的是本文列出的文本和工具行为,不是完整 Pi 或模型智能。27 行是设计中的用例组,不是 27 个单独测试。具体测试总数与命令见 [验证记录](results/verification.json),逐 fixture 结果见 [差分记录](results/conformance.json)。
3
+ 首期测试证明的是本文列出的文本和工具行为,不是完整 Pi 或模型智能。C01–C29 是设计中的用例组,不是 29 个单独测试。具体测试总数与命令见 [验证记录](results/verification.json),逐 fixture 结果见 [差分记录](results/conformance.json)。
4
4
 
5
5
  | 用例组 | Python 行为证据 | 上游证据 / 分类 |
6
6
  |---|---|---|
@@ -32,6 +32,29 @@
32
32
  | C26 release 候选 | test_C26_noop_duplicate_and_synthetic_upgrade 及参数化拒绝测试 | 合成接口演练;独立远端校验脚本另存实际记录 |
33
33
  | C27 wheel 安装 | Linux 容器内断网安装、示例、全部 pytest | 实际组合见 linux-3.11/3.12/3.13.json |
34
34
 
35
+ ## D9:应用工具批次与能力包最小接入
36
+
37
+ 2026-10-16 设计修订的新增合同由 `tests/test_application_tools.py`(C28)和 `tests/test_capability_composition.py`(C29)覆盖。它们是 Python 新增合同,不算作与上游直接一致。
38
+
39
+ | 用例组 | Python 行为证据 | 分类 |
40
+ |---|---|---|
41
+ | C28 应用工具批次 | 零模型请求;来源与空轮次事件编解码;共享准备、验证、钩子、预算、取消和超时;忙碌互斥;订阅故障保留结果;配置快照;可见性与历史无伪造调用 | intentional_difference,D9 |
42
+ | C29 普通 Python 能力组合 | 同一工具的两种调用方式;名称冲突拒绝;状态与配置隔离;否决不可覆盖;运行完成不掩盖领域验证失败 | intentional_difference,D9;`examples/capability_composition.py` 无网络示例 |
43
+
44
+ 兼容性修订:按用户要求,工具事件的 `origin/source` 改为显式 `Agent(include_tool_origin=True)` 才输出,默认不新增键;两种调用入口都遵守此选项。工具上下文和执行结果通过只读属性提供来源,不改变原有 dataclass 字段及 `asdict` 输出。应用批次历史仍保存来源。`tests/test_application_compatibility.py` 对照 v0.10.1 的字段集合,检查旧解析器、整数轮次及自定义消息转换器;新接口接入时可能的订阅失败和消息被过滤也有明确断言。这是对设计草案中“所有工具事件都携带来源”的兼容性调整,不是将缺失来源默认为已记录。
45
+
46
+ 本次沿用现行版本的默认无预算上限、已知取消与显式未知结果的区分,以及可选扩展能力,不把它们按早期草案回退。跨 run 预算、领域效果、科研判断内化和新的包加载器不在本次实现范围内。上述旧验证文件记录的是各自运行时的版本,不自动成为 D9 的验证证据。
47
+
48
+ ## D10:Agent 管理 Codex 缓存会话标识
49
+
50
+ 0.11.0 在 Agent 入口补齐缓存会话生命周期。官方 Pi v1.0.0 的底层 Agent 只转发 `sessionId`,由 coding-agent 的 `createAgentSession()` 从 `SessionManager` 取值;这一分工在核查的 v1.3.0 中也相同。Python 没有对应的完整上层应用,因而默认在 Agent 提供此便利能力。它是经用户确认的有意差异,不是上游 core 行为完全一致的声明,也不升级固定参照。
51
+
52
+ core 只识别可选的 `supports_agent_cache_session=True` 能力,不绑定模型名或 Provider 类。内置 Codex Provider 使用 `request.cache_session_id` 作为缓存键与会话头的后备值;其他内置 Provider、自定义流和未声明支持的 Provider 不接收自动标识。`DefaultProvider` 转发实际接入的能力。
53
+
54
+ 自动值不写入 `options.session_id`,因此不能满足 `websocket-cached` 的显式配置要求,也不能启动连接复用或 `previous_response_id` 续接。原有显式值、钩子及配置更新的优先级保留;自动值尊重应用自定义请求头。`auto_session_id=False` 恢复旧默认,`cache_retention="none"` 抑制库生成的缓存字段。新增属性与构造参数支持保存、恢复后备标识;消息编解码、事件、结果和 ModelRequest 的 dataclass 字段不变。
55
+
56
+ 证据:`tests/test_cache_sessions.py` 捕获实际构造的 HTTP 请求,覆盖默认头、工具循环、重试、配置替换、跨 Agent/子 Agent 隔离、重置、恢复、显式优先、禁用、通道切换、自定义 Provider 能力及旧请求结构;`tests/test_websocket_continuation.py` 用本地服务器检查连接数、完整输入和续接字段。恢复的自动值也不触发连接复用;显式标识的原有增量行为单独验证。真实后端命中率、订阅配额节省与所有上层应用不在这些离线检查的证明范围内。
57
+
35
58
  ## 有意差异与范围外行为
36
59
 
37
60
  D1–D8 保持原设计的含义。D1 严格输入;D2 执行前钩子不能通过原地修改参数改变调用;D3 资源预算;D4 Python 取消及未知结果;D5 状态复制;D6 输出检查;D7 诊断、原始结果和完整流校验;D8 文本与工具子集。每项有独立 Python 断言,不以 skip 代替验收。
@@ -18,9 +18,9 @@
18
18
  "mcp-interactive"
19
19
  ],
20
20
  "exit_code": 0,
21
- "seconds": 0.02251266618259251,
21
+ "seconds": 0.021437582792714238,
22
22
  "stdout": "",
23
- "stderr": "Resolved 60 packages in 2ms\nAudited 57 packages in 8ms\n"
23
+ "stderr": "Resolved 60 packages in 2ms\nAudited 57 packages in 6ms\n"
24
24
  },
25
25
  {
26
26
  "command": [
@@ -35,7 +35,7 @@
35
35
  "scripts"
36
36
  ],
37
37
  "exit_code": 0,
38
- "seconds": 0.0649345419369638,
38
+ "seconds": 0.061738125048577785,
39
39
  "stdout": "All checks passed!\n",
40
40
  "stderr": ""
41
41
  },
@@ -53,8 +53,8 @@
53
53
  "scripts"
54
54
  ],
55
55
  "exit_code": 0,
56
- "seconds": 0.024958166992291808,
57
- "stdout": "110 files already formatted\n",
56
+ "seconds": 0.02676033414900303,
57
+ "stdout": "115 files already formatted\n",
58
58
  "stderr": ""
59
59
  },
60
60
  {
@@ -65,7 +65,7 @@
65
65
  "src/pi_python"
66
66
  ],
67
67
  "exit_code": 0,
68
- "seconds": 0.5702685830183327,
68
+ "seconds": 0.40783704188652337,
69
69
  "stdout": "Success: no issues found in 43 source files\n",
70
70
  "stderr": ""
71
71
  },
@@ -77,8 +77,8 @@
77
77
  "-q"
78
78
  ],
79
79
  "exit_code": 0,
80
- "seconds": 149.19938791706227,
81
- "stdout": "........................................................................ [ 9%]\n........................................................................ [ 19%]\n........................................................................ [ 28%]\n........................................................................ [ 38%]\n........................................................................ [ 47%]\n........................................................................ [ 57%]\n........................................................................ [ 67%]\n...............................................ss....................... [ 76%]\n........................................................................ [ 86%]\n........................................................................ [ 95%]\n................................ [100%]\n750 passed, 2 skipped in 148.30s (0:02:28)\n",
80
+ "seconds": 143.989434374962,
81
+ "stdout": "........................................................................ [ 8%]\n........................................................................ [ 17%]\n........................................................................ [ 25%]\n........................................................................ [ 34%]\n........................................................................ [ 43%]\n........................................................................ [ 51%]\n........................................................................ [ 60%]\n........................................................................ [ 69%]\n...................................................ss................... [ 77%]\n........................................................................ [ 86%]\n........................................................................ [ 94%]\n.......................................... [100%]\n832 passed, 2 skipped in 143.31s (0:02:23)\n",
82
82
  "stderr": ""
83
83
  },
84
84
  {
@@ -89,10 +89,22 @@
89
89
  "examples/in_memory.py"
90
90
  ],
91
91
  "exit_code": 0,
92
- "seconds": 0.6022720420733094,
92
+ "seconds": 0.49026058288291097,
93
93
  "stdout": "The telemetry contains 3 complete records.\n",
94
94
  "stderr": ""
95
95
  },
96
+ {
97
+ "command": [
98
+ "uv",
99
+ "run",
100
+ "python",
101
+ "examples/capability_composition.py"
102
+ ],
103
+ "exit_code": 0,
104
+ "seconds": 0.5047036670148373,
105
+ "stdout": "run=completed; domain_passed=False\nBoth paths use the same tool; the application batch made zero model requests.\n",
106
+ "stderr": ""
107
+ },
96
108
  {
97
109
  "command": [
98
110
  "npm",
@@ -104,8 +116,8 @@
104
116
  "test/agent.test.ts"
105
117
  ],
106
118
  "exit_code": 0,
107
- "seconds": 1.9349422920495272,
108
- "stdout": "\n> @earendil-works/pi-agent-core@1.0.0 test\n> vitest --run test/agent-loop.test.ts test/agent.test.ts\n\n\n RUN v4.1.11 /Users/daidong/Documents/gitrepos/pi-python/reference/pi/packages/agent\n\n\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\n\n Test Files 2 passed (2)\n Tests 77 passed (77)\n Start at 12:01:38\n Duration 1.40s (transform 916ms, setup 0ms, import 2.00s, tests 281ms, environment 0ms)\n\n",
119
+ "seconds": 1.0130393749568611,
120
+ "stdout": "\n> @earendil-works/pi-agent-core@1.0.0 test\n> vitest --run test/agent-loop.test.ts test/agent.test.ts\n\n\n RUN v4.1.11 /Users/daidong/Documents/gitrepos/pi-python/reference/pi/packages/agent\n\n\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\n\n Test Files 2 passed (2)\n Tests 77 passed (77)\n Start at 12:56:57\n Duration 643ms (transform 451ms, setup 0ms, import 858ms, tests 171ms, environment 0ms)\n\n",
109
121
  "stderr": ""
110
122
  },
111
123
  {
@@ -121,8 +133,8 @@
121
133
  "test/openai-codex-stream.test.ts"
122
134
  ],
123
135
  "exit_code": 0,
124
- "seconds": 1.3067305411677808,
125
- "stdout": "\n> @earendil-works/pi-ai@1.0.0 test\n> vitest --run test/transcript-tool-changes.test.ts test/validation.test.ts test/anthropic-mid-conversation-effort.test.ts test/openai-codex-stream.test.ts\n\n\n RUN v4.1.11 /Users/daidong/Documents/gitrepos/pi-python/reference/pi/packages/ai\n\n\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\n\n Test Files 4 passed (4)\n Tests 70 passed (70)\n Start at 12:01:39\n Duration 992ms (transform 1.03s, setup 0ms, import 1.86s, tests 252ms, environment 0ms)\n\n",
136
+ "seconds": 1.0957162089180201,
137
+ "stdout": "\n> @earendil-works/pi-ai@1.0.0 test\n> vitest --run test/transcript-tool-changes.test.ts test/validation.test.ts test/anthropic-mid-conversation-effort.test.ts test/openai-codex-stream.test.ts\n\n\n RUN v4.1.11 /Users/daidong/Documents/gitrepos/pi-python/reference/pi/packages/ai\n\n\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\n\n Test Files 4 passed (4)\n Tests 70 passed (70)\n Start at 12:56:58\n Duration 808ms (transform 968ms, setup 0ms, import 1.57s, tests 208ms, environment 0ms)\n\n",
126
138
  "stderr": ""
127
139
  },
128
140
  {
@@ -133,7 +145,7 @@
133
145
  "scripts/conformance.py"
134
146
  ],
135
147
  "exit_code": 0,
136
- "seconds": 5.7676790410187095,
148
+ "seconds": 4.930072667077184,
137
149
  "stdout": "C01-text: passed []\nC02-tool: passed []\nC03-sequential: passed []\nC04-parallel: passed []\nC05-one-sequential: passed []\nC06-one-failure: passed []\nC07-unknown: passed []\nC08-prepare: passed []\nC09-before-error: passed []\nC09-block: passed []\nC09-replace: passed []\nC10-length: passed []\nC11-transform: passed []\nC12-system: passed []\nC13-steering: passed []\nC14-followup-all: passed []\nC14-followup-one: passed []\nC15-prepare: passed []\nC16-continue: passed []\nC16-end: passed []\nC16-mixed: passed []\nC16-terminate: passed []\nC19-abort-stream: passed []\nC19-abort-tools: passed []\nC24-provider-error: passed []\n",
138
150
  "stderr": ""
139
151
  },
@@ -145,7 +157,7 @@
145
157
  "scripts/provider_conformance.py"
146
158
  ],
147
159
  "exit_code": 0,
148
- "seconds": 15.009326208150014,
160
+ "seconds": 12.895416000159457,
149
161
  "stdout": "anthropic-oauth.json passed\nanthropic-request-cache-long.json passed\nanthropic-request-cache-none.json passed\nanthropic-request-replay.json passed\nanthropic-request-tools-image.json passed\nanthropic-text.json passed\nanthropic-thinking.json passed\nanthropic-tool.json passed\ncodex-request-tools-image.json passed\ncodex-text.json passed\ncompletions-compat-strict.json passed\ncompletions-deepseek.json passed\ncompletions-format-ant-ling.json passed\ncompletions-format-baseten.json passed\ncompletions-format-string-thinking.json passed\ncompletions-format-together.json passed\ncompletions-format-zai.json passed\ncompletions-midconvo-tools.json passed\ncompletions-moonshot-usage.json passed\ncompletions-openai-off.json passed\ncompletions-openrouter.json passed\ncompletions-qwen-length.json passed\ncompletions-reasoning-content.json passed\ncompletions-request-replay.json passed\ncompletions-text.json passed\ncompletions-tool-stream.json passed\nopenai-incomplete.json passed\nopenai-request-cache-long.json passed\nopenai-request-cache-none.json passed\nopenai-request-reasoning.json passed\nopenai-request-replay.json passed\nopenai-request-tools-image.json passed\nopenai-text.json passed\nopenai-thinking-backfill.json passed\nopenai-thinking.json passed\nopenai-tool.json passed\nproxy-text.json passed\nproxy-thinking-tool.json passed\nsession-anthropic-collapse-budget.json passed\nsession-anthropic-native-tools.json passed\nsession-anthropic-oauth-effort.json passed\nsession-anthropic-redefinition.json passed\nsession-anthropic-server-fallback.json passed\nsession-anthropic-thinking-off.json passed\nsession-codex-tool-search.json passed\nsession-openai-additional-tools.json passed\nsession-openai-collapse.json passed\nsession-openai-explicit-cache-long.json passed\nsession-openai-explicit-cache-none.json passed\nsession-openai-removal.json passed\n",
150
162
  "stderr": ""
151
163
  },
@@ -157,7 +169,7 @@
157
169
  "scripts/websocket_conformance.py"
158
170
  ],
159
171
  "exit_code": 0,
160
- "seconds": 0.9335567080415785,
172
+ "seconds": 0.7594145000912249,
161
173
  "stdout": "codex-continuation-lost-state.json passed\ncodex-continuation-tool-loop.json passed\n",
162
174
  "stderr": ""
163
175
  },
@@ -169,7 +181,7 @@
169
181
  "scripts/recovery_conformance.py"
170
182
  ],
171
183
  "exit_code": 0,
172
- "seconds": 0.628341041970998,
184
+ "seconds": 0.5641300419811159,
173
185
  "stdout": "recovery classifiers: 44/44 match upstream \n",
174
186
  "stderr": ""
175
187
  },
@@ -181,8 +193,8 @@
181
193
  "scripts/plugin_conformance.py"
182
194
  ],
183
195
  "exit_code": 0,
184
- "seconds": 1.306608916958794,
185
- "stdout": "....... [100%]\n7 passed in 0.74s\n",
196
+ "seconds": 1.0472173751331866,
197
+ "stdout": "....... [100%]\n7 passed in 0.63s\n",
186
198
  "stderr": ""
187
199
  },
188
200
  {
@@ -193,7 +205,7 @@
193
205
  "examples/plugin_demo.py"
194
206
  ],
195
207
  "exit_code": 0,
196
- "seconds": 0.576676958007738,
208
+ "seconds": 0.5162342919502407,
197
209
  "stdout": "loaded: lab_tools 0.1.0\ncheck: counts_duplicates passed\nprompt: Find duplicate events in events.csv using a 30 second window. Read the dedup-window skill first.\ntools used: ['read_skill', 'subagent']\nanswer: The reviewer found 2 duplicate rows.\n",
198
210
  "stderr": ""
199
211
  },
@@ -205,7 +217,7 @@
205
217
  "examples/mcp_interactive.py"
206
218
  ],
207
219
  "exit_code": 0,
208
- "seconds": 1.8445827078539878,
220
+ "seconds": 1.685829000081867,
209
221
  "stdout": "Model task=demo-run request=1 profile=default attempt=1 usage=None\nModel task=demo-run request=2 profile=default attempt=1 usage=None\nModel task=demo-run request=3 profile=json attempt=1 usage={'input': 4, 'output': 3, 'cacheRead': 1}\nModel task=demo-run request=4 profile=text attempt=1 usage={'input': 4, 'output': 3, 'cacheRead': 1}\nForm from business: Choose output for report\nDemo user chooses: short, confirmed\n{\"label\": \"report\", \"model_reply\": \"[report] [ready]\", \"action\": \"accept\", \"selection\": {\"style\": \"short\", \"confirmed\": true}, \"auxiliary\": {\"json\": {\"ok\": true}, \"text\": \"Ready\"}}\n",
210
222
  "stderr": ""
211
223
  },
@@ -218,7 +230,7 @@
218
230
  "--help"
219
231
  ],
220
232
  "exit_code": 0,
221
- "seconds": 0.6522026660386473,
233
+ "seconds": 0.5009769999887794,
222
234
  "stdout": "usage: provider_chat.py [-h]\n --provider {anthropic,openai,openai-codex,openai-chatgpt,deepseek}\n --model MODEL [--prompt PROMPT]\n [--credential-file CREDENTIAL_FILE] [--login]\n [--device]\n [--thinking {off,minimal,low,medium,high,xhigh,max}]\n [--transport {sse,websocket,websocket-cached,auto}]\n [--session-id SESSION_ID]\n\nExplicit API/OAuth example. Invoke --help; never imported by the core.\n\noptions:\n -h, --help show this help message and exit\n --provider {anthropic,openai,openai-codex,openai-chatgpt,deepseek}\n --model MODEL Model ID enabled for this account\n --prompt PROMPT\n --credential-file CREDENTIAL_FILE\n App-owned OAuth file (never reuse another client file)\n --login\n --device Codex device authorization instead of browser callback\n --thinking {off,minimal,low,medium,high,xhigh,max}\n --transport {sse,websocket,websocket-cached,auto}\n --session-id SESSION_ID\n Required for websocket-cached\n",
223
235
  "stderr": ""
224
236
  },
@@ -228,10 +240,11 @@
228
240
  "build"
229
241
  ],
230
242
  "exit_code": 0,
231
- "seconds": 1.0952744169626385,
243
+ "seconds": 0.8976772499736398,
232
244
  "stdout": "",
233
- "stderr": "Building source distribution...\nBuilding wheel from source distribution...\nSuccessfully built dist/pi_python_core-0.10.1.tar.gz\nSuccessfully built dist/pi_python_core-0.10.1-py3-none-any.whl\n"
245
+ "stderr": "Building source distribution...\nBuilding wheel from source distribution...\nSuccessfully built dist/pi_python_core-0.11.0.tar.gz\nSuccessfully built dist/pi_python_core-0.11.0-py3-none-any.whl\n"
234
246
  }
235
247
  ],
236
- "status": "passed"
248
+ "status": "passed",
249
+ "runner_timeout_seconds": 600
237
250
  }
@@ -6,11 +6,12 @@ Import the public types from `pi_python`. The minimum Python version is 3.11, an
6
6
 
7
7
  ## Agent and run results
8
8
 
9
- `Agent(provider=..., model="mock" or ModelInfo(...), options={}, system_prompt="", tools=[], messages=[], hooks=Hooks(), limits=RunLimits(), execution_mode="parallel", steering_mode="one_at_a_time", follow_up_mode="one_at_a_time")`. If there is neither a `provider` nor a default stream set with `set_default_stream_fn`, `prompt` raises `ConfigurationError` immediately.
9
+ `Agent(provider=..., model="mock" or ModelInfo(...), options={}, system_prompt="", tools=[], messages=[], hooks=Hooks(), limits=RunLimits(), execution_mode="parallel", steering_mode="one_at_a_time", follow_up_mode="one_at_a_time", include_tool_origin=False)`. If there is neither a `provider` nor a default stream set with `set_default_stream_fn`, `prompt` raises `ConfigurationError` immediately.
10
10
 
11
11
  | Method | Behavior |
12
12
  |---|---|
13
13
  | `await prompt(str_or_message_or_list)` | Appends the input and runs; returns a `RunResult` |
14
+ | `await execute_tools(invocations, source=None)` | Runs a nonempty `list[ToolInvocation]` without requesting a model; returns `RunResult` |
14
15
  | `await continue_run()` | Continues from a valid user/tool-result history, or consumes input queued after the final answer; if the last answer failed or was cancelled, retries that turn (see "Recovering from failures"); never replays past tools |
15
16
  | `prompt_sync(...)` / `continue_run_sync()` | Blocking versions for plain scripts; Ctrl+C cancels the current run; see "Using it from plain scripts" |
16
17
  | `steer(message)` | Accepts guidance at the next safe boundary; also takes a string |
@@ -22,12 +23,40 @@ Import the public types from `pi_python`. The minimum Python version is 3.11, an
22
23
  | `clear_queues(steering=True, follow_up=True)` | Explicitly clears the given queues |
23
24
  | `await aclose()` | Requests cancellation and waits for this instance's cleanup; does not close a shared Provider |
24
25
 
25
- One instance does not accept overlapping `prompt` / `continue_run` calls; the second call raises `AgentBusyError` immediately. `async with Agent(...)` is supported. `state` returns a defensive copy; changing it does not change the history.
26
+ One instance does not accept overlapping `prompt` / `continue_run` / `execute_tools` calls; the second call raises `AgentBusyError` immediately. `async with Agent(...)` is supported. `state` returns a defensive copy; changing it does not change the history.
26
27
 
27
28
  `RunResult` contains `status` (`completed` / `failed` / `cancelled` / `limit_reached`), the `messages` added by this run, the accumulated numeric `usage`, `stop_reason`, `errors`, `tool_outcomes`, `reconciliation_required`, `cleanup_complete`, and the number of items left in each queue. Usage only sums the top-level numeric fields the Provider reports; it does not estimate cost.
28
29
 
29
30
  `tool_outcomes` records, in call order, the raw arguments, the prepared arguments, `raw_result` and the final `result`. `execution_status` is `not_started`, `running`, `succeeded`, `failed`, `cancelled` or `unknown`. `cancelled` means the tool was cancelled or timed out; `unknown` only comes from a `ToolOutcomeUnknownError` that the tool raised itself. If execution succeeded but the output check or the after-hook failed, the status is still `succeeded` and the final result is an error. No error triggers an automatic retry.
30
31
 
32
+ ## Application-selected tools and capability composition
33
+
34
+ Use `require_features(["application-tool-batches-v1"])` to check that an installed build supports this contract.
35
+
36
+ ```python
37
+ from pi_python import ToolInvocation
38
+
39
+ result = await agent.execute_tools(
40
+ [ToolInvocation("check_sample", {"value": -1})], source="sample-checks@1"
41
+ )
42
+ # Inspect tool_outcomes here; completed does not mean a domain check passed.
43
+ await agent.prompt("Interpret the application check results.")
44
+ ```
45
+
46
+ `ToolInvocation(name, arguments)` accepts a registered tool name and a JSON object. Core assigns a unique call ID. Unknown names and schema failures become per-call error results. Empty batches and invalid invocation envelopes are rejected before a run starts. No Provider is required for this entry point.
47
+
48
+ Each batch is an independent run. It shares argument preparation, schema validation, tool hooks, execution order/concurrency, output checks, tool-call budgets, deadlines and cancellation ownership with model-selected calls. The entire batch is refused if it exceeds `max_tool_calls`. It does not invoke model preparation/conversion hooks or `finish_turn`, consume either input queue, or follow a tool's `terminate` flag into a model loop. Tools and configuration stay fixed during the batch; explicit configuration updates made during it wait until the next run (or a later idle `update_config`), in submission order. Task-wide budgets across several runs belong to the application.
49
+
50
+ Tool-event payloads keep their previous field sets by default. Set `Agent(include_tool_origin=True)` to add `origin` (`"application"` or `"model"`) and `source` (the caller's diagnostic string, or None) to tool events, for either entry point. This option is captured at run start. `ToolContext` and `ToolOutcome` expose provenance as read-only properties, not dataclass fields: `dataclasses.asdict()` keeps the previous shape and does not include provenance. Read `.origin` / `.source` explicitly or use the batch history record when storing it. Model-selected calls always have `source=None`. A source label is not an authenticated identity.
51
+
52
+ Existing model runs retain integer `turn_id` values and do not create `application_tool_batch` messages. Application events have `turn_id=None`, because no model turn occurred. Code that computes with turn IDs or stores them in integer-only columns must handle this before adopting `execute_tools` or reading its event logs. An exception from a critical subscriber can fail the run; None itself is valid in the new event codec. Critical end subscribers finish before the Agent becomes idle, subject to the existing cancellation and delivery-failure rules.
53
+
54
+ History gets one `CustomMessage(custom_type="application_tool_batch", data=...)`, including accepted calls that failed, were cancelled or never started. It contains each call's ID, name, original/prepared arguments, origin/source, execution status, error flag/code and final visible content. It never invents an assistant tool call or an unmatched `ToolResultMessage`. Raw results, structured content and local details stay in `tool_outcomes`; they are not automatically sent to the model. On a subsequent model request, remaining built-in batch messages are converted after the application's `convert_to_llm` hook into explicitly labeled application-result text. Other custom types still require an application converter. This default visibility policy is not a sandbox: application hooks can deliberately expose other fields.
55
+
56
+ **Custom-converter compatibility:** no converter is needed for built-in batch messages. If an application already supplies `transform_context` or `convert_to_llm`, it should preserve these messages for core to convert, or explicitly convert them itself. Rejecting unknown custom types will fail a subsequent model run; filtering all custom messages may silently remove the feedback. These cases arise when adopting the new entry point or importing its history, not in ordinary pre-existing model history. The `application_tool_batch` type name is now reserved for the built-in record; an application previously using that same custom name must review its converter and payload.
57
+
58
+ The [offline composition example](../examples/capability_composition.py) uses the same tool with model-selected and application-fixed calls. Plain Python objects supply instruction sections, tools, hooks and validation. The application owns their state and lifecycle, rejects duplicate instruction names, and composes hooks with explicit order and veto rules. Core rejects duplicate tool names. Configuration is recorded in an application-converted `capability_configuration` message; core does not interpret package versions or load a new package format. The example ends with `status="completed"` but `domain_passed=False`: execution success is separate from domain acceptance.
59
+
31
60
  ## Provider
32
61
 
33
62
  ```python
@@ -122,7 +151,7 @@ Every hook can be a sync or an async function. Contexts and arguments are passed
122
151
  | `prepare_request(context, cancel)` | Returns a `TurnUpdate` or None; called before every model request |
123
152
  | `prepare_next_turn(context, cancel)` | Returns a `TurnUpdate` or None; called from the second turn on |
124
153
  | `transform_context(messages, cancel)` | Returns the message list used for the model request; runs before convert |
125
- | `convert_to_llm(messages)` | Returns the message list that can be sent to the model; must handle or explicitly filter out `CustomMessage` |
154
+ | `convert_to_llm(messages)` | Returns the message list that can be sent to the model; must handle or explicitly filter out custom types other than the built-in `application_tool_batch` |
126
155
  | `before_tool_call(call, args, context)` | True / None allows, False blocks, or return a `ToolResult` to use without executing; if the hook raises, that call gets an error result (`hook_error`) and the run continues, as in Pi |
127
156
  | `after_tool_call(call, result, context)` | Returns a full `ToolResult`, a partial `ToolResultUpdate`, or None |
128
157
  | `finish_turn(context, cancel)` | Returns `"continue"`, `"end"` or None |
@@ -242,6 +271,8 @@ The event stream uses a bounded queue. If you only wait for the result, `result(
242
271
 
243
272
  An Agent can also be constructed with `thinking_level`, `thinking_budgets`, `transport`, `session_id`, `get_api_key`, `on_payload`, `on_response` and `on_provider_stream_event`. The last four can also go in Hooks; explicit constructor arguments take precedence. Their protocols, and the Provider parameters, are described in [the connector guide](PROVIDERS.md).
244
273
 
274
+ `auto_session_id=True` supplies an Agent-owned cache-only ID to Providers that opt in (the built-in Codex Provider). `agent.cache_session_id` reads that managed fallback; restore it with `Agent(cache_session_id=saved_id, ...)`. Explicit `session_id` takes precedence. `reset()` rotates the managed fallback, and `auto_session_id=False` disables it. Automatic/restored cache IDs never enable connection reuse. See [cache sessions, persistence and Provider opt-in](PROVIDERS.md#automatic-codex-cache-sessions).
275
+
245
276
  ## Fields of messages, tool results and events
246
277
 
247
278
  `SystemMessage.content` accepts a string or a list of text blocks. `AssistantMessage` also contains `response_model`, `response_id`, `thinking_level`, `diagnostics`, `raw_stop_reason`, `end_turn` and `deferred`. `provider_thinking_level` is the provider's native effort; `thinking_level` is the requested level. Fields whose value is null in arbitrary user JSON are preserved intact.
@@ -0,0 +1,76 @@
1
+ # 应用直接执行工具:实现与验证
2
+
3
+ 这次检查的是:应用能否不经过模型决策,直接执行已注册工具,同时保留与模型调用相同的执行规则。现在两种入口共用工具执行路径,应用调用不会伪装成模型调用;无网络示例也验证了“运行完成,但领域检查未通过”。这些结果支持开始在应用侧组合具体能力包,不代表领域方法或科研判断已经实现。
4
+
5
+ ## 新设计如何分工
6
+
7
+ - **机械 core** 管模型请求、工具执行、参数验证、钩子、事件、预算和取消,不判断领域结论是否正确。
8
+ - **领域能力包** 提供知识、工具、固定步骤和验证规则。应用用普通 Python 代码组合它们,拥有状态与生命周期,并解决名称和规则冲突。
9
+ - **基础模型** 使用这些知识处理具体任务。哪些步骤由模型选择、哪些由应用固定执行,由应用明确决定。
10
+
11
+ 新增的 `Agent.execute_tools([ToolInvocation(...)], source=...)` 让应用可以执行固定检查,而不必先让模型选择工具。它与模型调用共用参数准备、执行前后钩子、输出验证、并发限制、工具预算和取消清理。每批是独立运行;跨批次总预算仍由应用管理。
12
+
13
+ 例如,[组合示例](../examples/capability_composition.py) 检查数值 `-1` 是否达到应用规定的下限 `0`。模型可以选择调用检查工具;应用也可以先执行同一个工具,再把结果交给模型解释。检查程序正常运行,所以运行状态是 `completed`;数值没有达标,所以应用读取的领域验证结果仍是 `False`。
14
+
15
+ ## 记录如何保持可解释
16
+
17
+ 工具上下文和执行结果通过只读属性提供 `origin` 与 `source`,分别表示调用来源和应用自报的诊断标签,不改变原有 dataclass 序列化字段。工具事件默认不新增这两个键,只有显式设置 `Agent(include_tool_origin=True)` 才输出。来源标签不代表认证身份。模型调用标为 `model`,应用调用标为 `application`;应用批次历史始终保存来源。
18
+
19
+ 应用批次不请求模型,不产生模型轮次,也不消费指导或后续输入队列。历史保存一条内置的 `application_tool_batch` 自定义消息,包括各调用的参数、来源、执行状态和整理后的可见内容。后续请求将它转成明确标注的应用结果文本,不制造 assistant 工具调用或悬空工具结果。原始结果和本地详情留在执行结果中,不自动发给模型。
20
+
21
+ 工具名冲突由 core 拒绝。指令段冲突、多个钩子的调用顺序和否决规则由示例中的应用组合器处理。两个能力包实例的可变状态彼此独立。本次没有新增领域包加载器,也没有改动现有插件机制。
22
+
23
+ ## 旧应用的兼容性
24
+
25
+ 按用户要求,来源信息不再默认扩展工具事件或 dataclass 序列化结构。专门的测试使用 v0.10.1 的字段集合,验证严格检查字段的旧消费者仍能走完模型工具循环。来源仍可通过属性显式读取,需要事件记录时再开启选项。
26
+
27
+ 只使用原有 `prompt/continue_run` 且不导入新批次历史的应用,不会收到空轮次或新批次消息。采用 `execute_tools` 后则需要检查接入边界:监听器对 `turn_id=None` 做整数运算会导致订阅失败;已有消息转换器若拒绝新类型,会使后续模型运行失败,若过滤所有自定义消息,则会静默丢掉检查结果。没有自定义转换器时,core 可直接处理内置批次消息;有转换器时,原样保留这类消息也可交给 core 处理。相关成功与失败情况均有测试,但未运行所有实际上层应用的集成测试。
28
+
29
+ ## 验证结果与边界
30
+
31
+ 兼容性修订后的本机全量测试 **798 项通过、2 项跳过**。C28、C29 及兼容性定向测试共 **47 项通过**,覆盖零模型请求、来源与消息编解码、共享执行规则、配置隔离、忙碌互斥、取消与未退出工具、订阅故障和领域验证失败。两项跳过来自既有插件测试:它们只适用于不支持 `meta` 的旧版 SDK,不计入通过数。
32
+
33
+ 实际运行上游和 Python 后,**25 组核心差分全部通过**。新增入口属于 Python 合同 D9,不计作上游直接一致。构建后的 wheel 已在仓库外的独立环境离线安装;43 项应用批次与兼容性测试及组合示例通过。
34
+
35
+ 本报告记录发布准备前的本地验证,环境为 macOS ARM64、Python 3.14。该轮没有重新运行 Linux/HPC、多版本矩阵或真实模型调用;已把新增示例和安装包测试接入现有 CI。0.10.2 的接入说明见[发布说明](releases/0.10.2.md),远端 CI 结果以对应工作流为准。示例只证明接口能够组合,不测量领域效果,也不包含“把科研判断内化进 PiPilot”。
36
+
37
+ ## 方法与核查附录
38
+
39
+ ### 设计依据
40
+
41
+ 实际采用 `/Users/daidong/Documents/rp-writings/research-ideas/pi-python-core-design/` 下的 2026-10-16 修订,而不是代码仓库中同名的旧副本:
42
+
43
+ - `DESIGN.md`:“应用直接执行工具”“领域能力包的最小接入约定”。
44
+ - `DEVELOPMENT-PLAN.md`:C28、C29。
45
+ - `UPSTREAM-COMPATIBILITY.md`:D9。
46
+ - `README.md`、`BOOTSTRAP-PROMPT.md`、`DOMAIN-CAPABILITY-PACKS.md`:范围和分工。
47
+
48
+ 现有发布版本已经扩展了早期草案的范围。本次保留已有 Provider、插件、同步接口,以及默认无预算上限、已知取消与显式未知结果的区分,没有按旧草案回退它们。上述实现验证使用未发布的 0.10.1 开发构建,没有升级上游基线。随后经用户确认兼容性方案,安排以 0.10.2 提交、推送和发布;这一授权不构成对所有上层应用的兼容性保证。
49
+
50
+ ### 实现入口
51
+
52
+ - [Agent API](zh/API.md#应用直接执行工具与能力包组合):参数、来源、可见性和生命周期合同。
53
+ - `src/pi_python/agent.py`:公开入口和运行互斥。
54
+ - `src/pi_python/loop.py`、`run.py`:共用执行器、批次预算、运行收尾和历史记录。
55
+ - `src/pi_python/messages.py`、`events.py`、`tools.py`:内置消息转换、空轮次事件与调用来源。
56
+ - [兼容性测试](../tests/test_application_compatibility.py)、[C28 测试](../tests/test_application_tools.py)、[C29 测试](../tests/test_capability_composition.py)、[验收映射](../compat/COVERAGE.md#d9应用工具批次与能力包最小接入)。
57
+ - 能力检查:`require_features(["application-tool-batches-v1"])`。
58
+
59
+ ### 实际执行的检查
60
+
61
+ | 检查 | 命令或方式 | 结果 |
62
+ |---|---|---|
63
+ | 新合同及兼容性 | `.venv/bin/pytest -q tests/test_application_tools.py tests/test_application_compatibility.py tests/test_capability_composition.py` | 47 通过 |
64
+ | 全量测试 | `.venv/bin/pytest -q -ra` | 798 通过,2 跳过 |
65
+ | 静态检查 | `.venv/bin/ruff check src tests examples compat scripts` | 通过 |
66
+ | 格式检查 | `.venv/bin/ruff format --check src tests examples compat scripts` | 114 文件通过 |
67
+ | 类型检查 | `.venv/bin/mypy src/pi_python` | 43 源文件通过 |
68
+ | 补丁检查 | `git diff --check` | 通过 |
69
+ | 实际上游对照 | `.venv/bin/python scripts/conformance.py` | 25 组通过,无差异 |
70
+ | 构建 | `uv build --out-dir /tmp/pi-python-optional-origin-dist` | sdist、wheel 成功 |
71
+ | 独立安装 | 在前一轮建立的独立环境中重新安装本轮 wheel:`uv pip install --python /tmp/pi-python-execute-tools-installed/bin/python --offline --reinstall-package pi-python-core /tmp/pi-python-optional-origin-dist/pi_python_core-0.10.1-py3-none-any.whl` | 成功 |
72
+ | 安装包行为 | 从 `/tmp` 使用独立环境运行组合示例和 `pytest -q -o pythonpath= <仓库>/tests/test_application_tools.py <仓库>/tests/test_application_compatibility.py` | 示例输出 `run=completed; domain_passed=False`;43 测试通过 |
73
+
74
+ 独立安装的导入路径确认为 `/tmp/pi-python-execute-tools-installed/lib/python3.14/site-packages/pi_python/__init__.py`,不是源码目录。本轮全量测试和构建原始输出保留在 `/tmp/pi-python-optional-origin-*.log`;这些是临时日志,不是持久化存储保证。
75
+
76
+ 补充清理边界测试时,首版测试在释放工具后立即调用 `aclose()`,此时清理回调尚未执行,触发了既有的 `CleanupTimeoutError`。测试改为有界等待实际清理完成后关闭,并检查迟到的成功不能改写已提交记录;修正后新合同和全量测试均重新通过。
@@ -139,6 +139,8 @@ Do not move that connection to `run_sync`'s separate loop. See
139
139
  These APIs and feature declarations are part of the 0.10.0 source line; check
140
140
  `require_features(["task-scope-v1", "loop-portal-v1"])` when depending on them.
141
141
 
142
+ For Codex subscriptions, Agent supplies a stable cache ID by default. Save `agent.cache_session_id` alongside the message history and restore it with `Agent(cache_session_id=..., messages=...)`. This retains cache-only behavior; it does not enable connection reuse. Use `auto_session_id=False` to opt out. Other built-in Providers and custom streams keep their existing behavior. See [cache sessions](PROVIDERS.md#automatic-codex-cache-sessions).
143
+
142
144
  ## 5. Choose the smallest integration surface
143
145
 
144
146
  | Need | Start here |
@@ -100,7 +100,7 @@ Claude models that support changing reasoning effort mid-conversation (`supports
100
100
 
101
101
  OpenAI maps `thinking_level` to reasoning effort. `options` supports `max_tokens`, `reasoning_summary`, `temperature`, `top_p`, `tool_choice`, `parallel_tool_calls`, `metadata`, `service_tier`, `text` and `headers`; Claude also supports `thinking_budgets`, `thinking_display`, `interleaved_thinking`, `top_k`, `stop_sequences`, and direct overrides of `thinking` and `output_config`. As upstream does, Claude forwards only `metadata.user_id`, writes a string `tool_choice` as `{"type": ...}`, and does not send `temperature` when reasoning is on, when the model does not support it, or when effort markers are in use. Which parameters a given model accepts is up to the server.
102
102
 
103
- Claude uses SSE. OpenAI and Codex support `sse`, `websocket`, `websocket-cached` and `auto`. `websocket-cached` requires `session_id`; as upstream does, `auto` also reuses the connection when there is a `session_id`. With `cache_retention="none"`, each request uses a one-off connection. Connections are kept apart by endpoint, session and request headers, and each connection is used by one request at a time. A connection is no longer reused after 5 idle minutes or 55 minutes after it was opened, and a connection the server has closed is rebuilt instead of failing on the old one. These limits can be changed with `HTTPTransport(websocket_idle_ttl=..., websocket_max_age=...)`.
103
+ Claude uses SSE. OpenAI and Codex support `sse`, `websocket`, `websocket-cached` and `auto`. `websocket-cached` requires an application-supplied `session_id`; as upstream does, `auto` also reuses the connection when there is an application-supplied `session_id`. The Agent-managed cache ID does not enable either behavior. With `cache_retention="none"`, each request uses a one-off connection. Connections are kept apart by endpoint, session and request headers, and each connection is used by one request at a time. A connection is no longer reused after 5 idle minutes or 55 minutes after it was opened, and a connection the server has closed is rebuilt instead of failing on the old one. These limits can be changed with `HTTPTransport(websocket_idle_ttl=..., websocket_max_age=...)`.
104
104
 
105
105
  Expired idle connections are swept on WebSocket requests and cache returns. At most 64 idle connections are retained, evicting the least recently returned first; set `HTTPTransport(websocket_cache_size=...)` to change this limit, or `0` to disable retention. Session locks are removed when their last active or waiting request exits. Sweeping is demand-driven, with no background timer. Call `await provider.aclose()` when you are done; the application manages the lifetime of a shared transport.
106
106
 
@@ -123,6 +123,42 @@ Usage is normalized to `input`, `output`, `cache_read`, `cache_write`, `reasonin
123
123
  counters remain present for compatibility; those default zeros are not measured consumption.
124
124
  MCP sampling observations report `usage=None` for this case. A tool's own usage is stored separately in the tool result and is not mixed into the main model's token counts.
125
125
 
126
+ ## Automatic Codex cache sessions
127
+
128
+ From 0.11.0, `Agent` supplies a stable cache ID when using `OpenAICodexProvider` without an application-supplied `session_id`. It fills the `session-id` and `x-client-request-id` headers and `prompt_cache_key`. This applies to the ChatGPT Codex subscription channel, not to OpenAI API-key calls or Claude. The Codex CLI [identifies the session header as the cache-affinity input](https://github.com/openai/codex/blob/e4568a3538c9cab6627d64ff8cdfc7d98b79b7db/codex-rs/core/src/client.rs#L660); sending an ID does not guarantee a cache hit or a particular subscription quota saving.
129
+
130
+ The ID belongs to the Agent, not to the shared Provider or an individual request. Tool loops, later prompts, retries and configuration replacement keep it. Different Agents, including nested Agents, receive independent IDs. `reset()` discards the managed ID, including a restored one; the next access or request generates a new ID. An explicit `session_id` always takes precedence and retains its existing reset behavior. Automatic headers respect application-supplied header values, including differently cased names.
131
+
132
+ The automatic ID **never enables connection reuse or response continuation**. The default transport remains SSE. In `auto` or `websocket` mode it still uses one-off connections and full context unless the application explicitly supplies `session_id` to an eligible mode. `websocket-cached` still requires an explicit `session_id`. Cache affinity and transport session state are separate.
133
+
134
+ Use `Agent(auto_session_id=False, ...)` to keep the previous default. This does not suppress an explicit `session_id`. `options={"cache_retention": "none"}` suppresses library-generated cache keys and session headers for either kind of ID.
135
+
136
+ Save the read-only `agent.cache_session_id` alongside history and restore it through the constructor's `cache_session_id=` argument:
137
+
138
+ ```python
139
+ from pi_python import Agent, encode_messages, decode_messages
140
+
141
+ # provider is an application-owned OpenAICodexProvider.
142
+ agent = Agent(provider=provider, model=model_id)
143
+ await agent.prompt("Remember the task constraints.")
144
+ saved = {
145
+ "messages": encode_messages(list(agent.state.messages)),
146
+ "cache_session_id": agent.cache_session_id,
147
+ }
148
+ restored = Agent(
149
+ provider=provider, model=model_id,
150
+ messages=decode_messages(saved["messages"]),
151
+ cache_session_id=saved["cache_session_id"],
152
+ )
153
+ await restored.prompt("Continue the task.")
154
+ ```
155
+
156
+ The application chooses how to store this object; message encoding itself has not changed. `cache_session_id` is the Agent-managed fallback, not a report of a request's effective routing ID when an explicit `session_id` or custom headers override it. Applications that manage `session_id` must persist that configuration themselves. Restoring with `cache_session_id=` retains cache-only behavior; restoring it as `session_id=` opts into the existing transport semantics. Restored cache IDs must contain 1–64 printable ASCII characters without spaces and require `auto_session_id=True`. The property returns `None` when automatic handling is disabled or the current Provider does not opt in.
157
+
158
+ The core uses an optional Provider capability, `supports_agent_cache_session = True`, rather than inspecting model names or importing a Codex adapter. Only the built-in Codex Provider enables it. Custom Providers may opt in and read `request.cache_session_id`; existing Providers and `stream_fn` implementations require no changes. Wrappers that opt in must preserve the request object or use `deepcopy`: rebuilding it from its serialized dataclass fields drops this cache-only metadata. The default-stream wrapper forwards the underlying Provider's capability. The ID is separate from `request.options`, and does not add dataclass fields to `ModelRequest` or fields to messages, events or results. Calling a Provider directly does not generate an ID.
159
+
160
+ This is an intentional difference from Pi's core Agent, which only forwards an optional ID. Pi's coding-agent layer supplies it through `SessionManager`; pi-python provides that convenience at its primary Agent entry point. The upstream baseline is unchanged. Applications can check `require_features(["codex-auto-cache-session-v1"])` before using this API. See the [0.11.0 compatibility and verification notes](releases/0.11.0.md).
161
+
126
162
  ## Model table and DeepSeek
127
163
 
128
164
  ```python
@@ -0,0 +1,52 @@
1
+ # 0.10.2:应用直接执行工具,来源记录可选输出
2
+
3
+ 本版本新增 `Agent.execute_tools()`,让应用直接执行已注册工具,不必先请求模型决定调用。两种入口共用工具执行与清理路径;旧工具事件默认保持原有字段集合。
4
+
5
+ ## 新增能力
6
+
7
+ - `ToolInvocation(name, arguments)` 与 `await agent.execute_tools(invocations, source=None)`:独立工具批次,无模型请求,共用参数准备、schema 验证、执行前后钩子、并发限制、预算、超时和取消清理。
8
+ - 应用结果通过内置 `application_tool_batch` 自定义消息记录;不伪造 assistant 工具调用,不生成没有对应调用的 `ToolResultMessage`。
9
+ - `ToolContext.origin/source` 与 `ToolOutcome.origin/source` 只读属性,用于区分模型选择与应用指定的调用。来源标签是诊断信息,不是认证身份。
10
+ - `Agent(include_tool_origin=True)` 可将来源加入工具事件;默认关闭,两种入口都遵守该选项。
11
+ - `require_features(["application-tool-batches-v1"])` 可检查是否支持此合同。
12
+ - 无网络能力包组合示例,演示同一个工具的两种调用方式,以及“运行 completed,但领域验证失败”。没有新增包加载器或领域判断引擎。
13
+
14
+ ## 兼容性与接入注意事项
15
+
16
+ ### 沿用旧接口
17
+
18
+ 原有 `prompt/continue_run` 保持整数轮次 ID,不自动生成应用批次消息。工具事件默认不新增 `origin/source`;`ToolContext` 和 `ToolOutcome` 的 dataclass 字段及 `dataclasses.asdict()` 输出结构保持不变。来源信息需要显式读取属性,或从批次历史、开启来源输出后的事件中保存。
19
+
20
+ ### 采用新接口或读取新记录
21
+
22
+ 1. 应用批次没有模型轮次,其事件的 `turn_id=None`。监听器、数据库字段和事件解析器需要允许这种情况;对 None 做整数运算会出错,关键订阅者失败可能使运行失败。
23
+ 2. 没有自定义消息转换器时,core 自动转换内置批次消息。已有 `transform_context/convert_to_llm` 应保留该消息供 core 转换,或自行明确转换。拒绝未知类型可能导致后续模型运行失败;过滤全部自定义消息可能静默丢掉工具反馈。
24
+ 3. `application_tool_batch` 是内置消息类型名;已有同名自定义类型的应用应检查其数据与转换规则。
25
+ 4. 每批预算只约束当前运行;跨多次运行的任务总预算和领域验收由应用负责。`completed` 不代表领域验证通过。
26
+
27
+ 本版本没有更改默认资源上限、现有插件加载机制或上游参照基线。上述接入边界已经说明,并经项目负责人确认后安排发布;这不等于已验证所有实际上层应用。
28
+
29
+ ## 验证
30
+
31
+ 本地 macOS ARM64 / Python 3.14:全量测试 798 通过、2 跳过;C28、C29 与兼容性定向测试 47 通过;25 组实际上游核心差分通过。Ruff、格式与 mypy 检查通过。两项跳过只适用于不支持 `meta` 的旧版 MCP SDK,不计入通过数。
32
+
33
+ 0.10.2 的 wheel 与源码包通过 `twine check --strict`。wheel 已在仓库外的独立 Python 3.11 环境中安装,版本一致性、组合示例和 43 项应用批次及兼容性测试通过。
34
+
35
+ 发布由 `v0.10.2` 标签触发,CI 验证和构建成功后才上传 PyPI。没有以模拟示例宣称真实模型、HPC 或领域效果已经验证。
36
+
37
+ ## 使用与文档
38
+
39
+ ```python
40
+ from pi_python import ToolInvocation
41
+
42
+ result = await agent.execute_tools(
43
+ [ToolInvocation("check_sample", {"value": -1})], source="sample-checks@1"
44
+ )
45
+ # 根据 result.tool_outcomes 验收,而不是仅检查 result.status。
46
+ await agent.prompt("解释应用检查的结果。")
47
+ ```
48
+
49
+ - [中文 API](../zh/API.md#应用直接执行工具与能力包组合)
50
+ - [English API](../API.md#application-selected-tools-and-capability-composition)
51
+ - [无网络组合示例](../../examples/capability_composition.py)
52
+ - [实现与本地验证记录](../APPLICATION_TOOLS_IMPLEMENTATION.md)