pi-python-core 0.10.0__tar.gz → 0.10.2__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 (369) hide show
  1. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/PKG-INFO +2 -1
  2. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/README.md +1 -0
  3. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/README.zh-CN.md +1 -0
  4. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/COVERAGE.md +15 -1
  5. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/verification.json +37 -25
  6. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/API.md +32 -3
  7. pi_python_core-0.10.2/docs/APPLICATION_TOOLS_IMPLEMENTATION.md +76 -0
  8. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/PLUGINS.md +56 -0
  9. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/PLUGIN_DEVELOPMENT.md +1 -0
  10. pi_python_core-0.10.2/docs/releases/0.10.2.md +52 -0
  11. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/API.md +32 -3
  12. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/PLUGINS.md +56 -0
  13. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/PLUGIN_DEVELOPMENT.md +1 -0
  14. pi_python_core-0.10.2/examples/capability_composition.py +149 -0
  15. pi_python_core-0.10.2/examples/memory_demo.py +62 -0
  16. pi_python_core-0.10.2/examples/plugins/memory/plugin.py +138 -0
  17. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/pyproject.toml +1 -1
  18. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/__init__.py +2 -0
  19. pi_python_core-0.10.2/src/pi_python/_version.py +1 -0
  20. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/agent.py +53 -4
  21. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/events.py +3 -4
  22. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/features.py +1 -0
  23. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/loop.py +36 -6
  24. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/messages.py +57 -0
  25. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/plugins/__init__.py +19 -3
  26. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/plugins/_compose.py +15 -0
  27. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/run.py +91 -11
  28. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/tools.py +39 -2
  29. pi_python_core-0.10.2/tests/test_application_compatibility.py +207 -0
  30. pi_python_core-0.10.2/tests/test_application_tools.py +508 -0
  31. pi_python_core-0.10.2/tests/test_capability_composition.py +59 -0
  32. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_examples.py +2 -0
  33. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_features.py +7 -1
  34. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_plugins.py +50 -0
  35. pi_python_core-0.10.0/src/pi_python/_version.py +0 -1
  36. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/.gitignore +0 -0
  37. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/LICENSE +0 -0
  38. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/NOTICE +0 -0
  39. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/__init__.py +0 -0
  40. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/baseline.json +0 -0
  41. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/contracts/release-candidate.example.json +0 -0
  42. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/contracts/release-candidate.schema.json +0 -0
  43. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/contracts/upstream-baseline.json +0 -0
  44. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C01-text.json +0 -0
  45. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C02-tool.json +0 -0
  46. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C03-sequential.json +0 -0
  47. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C04-parallel.json +0 -0
  48. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C05-one-sequential.json +0 -0
  49. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C06-one-failure.json +0 -0
  50. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C07-unknown.json +0 -0
  51. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C08-prepare.json +0 -0
  52. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C09-before-error.json +0 -0
  53. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C09-block.json +0 -0
  54. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C09-replace.json +0 -0
  55. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C10-length.json +0 -0
  56. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C11-transform.json +0 -0
  57. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C12-system.json +0 -0
  58. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C13-steering.json +0 -0
  59. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C14-followup-all.json +0 -0
  60. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C14-followup-one.json +0 -0
  61. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C15-prepare.json +0 -0
  62. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C16-continue.json +0 -0
  63. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C16-end.json +0 -0
  64. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C16-mixed.json +0 -0
  65. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C16-terminate.json +0 -0
  66. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C19-abort-stream.json +0 -0
  67. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C19-abort-tools.json +0 -0
  68. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/fixtures/C24-provider-error.json +0 -0
  69. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-cases.json +0 -0
  70. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/prompts/notes.txt +0 -0
  71. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/prompts/plan.md +0 -0
  72. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/prompts/review.md +0 -0
  73. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/prompts/sub/nested.md +0 -0
  74. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/prompts/summarize.md +0 -0
  75. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/.hidden/SKILL.md +0 -0
  76. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/alpha/SKILL.md +0 -0
  77. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/alpha/references/notes.md +0 -0
  78. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/beta/SKILL.md +0 -0
  79. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/broken/SKILL.md +0 -0
  80. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/delta/SKILL.md +0 -0
  81. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/gamma/SKILL.md +0 -0
  82. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/longdesc/SKILL.md +0 -0
  83. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/loose.md +0 -0
  84. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/nested/group/epsilon/SKILL.md +0 -0
  85. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/noname/SKILL.md +0 -0
  86. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/plain.md +0 -0
  87. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/zeta/SKILL.md +0 -0
  88. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/plugin-fixtures/skills/zeta/sub/eta/SKILL.md +0 -0
  89. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-oauth.json +0 -0
  90. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-request-cache-long.json +0 -0
  91. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-request-cache-none.json +0 -0
  92. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-request-replay.json +0 -0
  93. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-request-tools-image.json +0 -0
  94. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-text.json +0 -0
  95. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-thinking.json +0 -0
  96. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/anthropic-tool.json +0 -0
  97. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/codex-request-tools-image.json +0 -0
  98. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/codex-text.json +0 -0
  99. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-compat-strict.json +0 -0
  100. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-deepseek.json +0 -0
  101. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-format-ant-ling.json +0 -0
  102. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-format-baseten.json +0 -0
  103. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-format-string-thinking.json +0 -0
  104. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-format-together.json +0 -0
  105. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-format-zai.json +0 -0
  106. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-midconvo-tools.json +0 -0
  107. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-moonshot-usage.json +0 -0
  108. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-openai-off.json +0 -0
  109. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-openrouter.json +0 -0
  110. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-qwen-length.json +0 -0
  111. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-reasoning-content.json +0 -0
  112. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-request-replay.json +0 -0
  113. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-text.json +0 -0
  114. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/completions-tool-stream.json +0 -0
  115. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-incomplete.json +0 -0
  116. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-request-cache-long.json +0 -0
  117. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-request-cache-none.json +0 -0
  118. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-request-reasoning.json +0 -0
  119. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-request-replay.json +0 -0
  120. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-request-tools-image.json +0 -0
  121. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-text.json +0 -0
  122. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-thinking-backfill.json +0 -0
  123. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-thinking.json +0 -0
  124. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/openai-tool.json +0 -0
  125. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/proxy-text.json +0 -0
  126. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/proxy-thinking-tool.json +0 -0
  127. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-anthropic-collapse-budget.json +0 -0
  128. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-anthropic-native-tools.json +0 -0
  129. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-anthropic-oauth-effort.json +0 -0
  130. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-anthropic-redefinition.json +0 -0
  131. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-anthropic-server-fallback.json +0 -0
  132. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-anthropic-thinking-off.json +0 -0
  133. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-codex-tool-search.json +0 -0
  134. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-openai-additional-tools.json +0 -0
  135. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-openai-collapse.json +0 -0
  136. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-openai-explicit-cache-long.json +0 -0
  137. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-openai-explicit-cache-none.json +0 -0
  138. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/provider-fixtures/session-openai-removal.json +0 -0
  139. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/recovery-cases.json +0 -0
  140. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/release.py +0 -0
  141. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C01-text.python.json +0 -0
  142. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C01-text.upstream.json +0 -0
  143. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C02-tool.python.json +0 -0
  144. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C02-tool.upstream.json +0 -0
  145. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C03-sequential.python.json +0 -0
  146. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C03-sequential.upstream.json +0 -0
  147. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C04-parallel.python.json +0 -0
  148. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C04-parallel.upstream.json +0 -0
  149. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C05-one-sequential.python.json +0 -0
  150. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C05-one-sequential.upstream.json +0 -0
  151. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C06-one-failure.python.json +0 -0
  152. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C06-one-failure.upstream.json +0 -0
  153. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C07-unknown.python.json +0 -0
  154. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C07-unknown.upstream.json +0 -0
  155. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C08-prepare.python.json +0 -0
  156. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C08-prepare.upstream.json +0 -0
  157. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C09-before-error.python.json +0 -0
  158. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C09-before-error.upstream.json +0 -0
  159. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C09-block.python.json +0 -0
  160. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C09-block.upstream.json +0 -0
  161. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C09-replace.python.json +0 -0
  162. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C09-replace.upstream.json +0 -0
  163. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C10-length.python.json +0 -0
  164. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C10-length.upstream.json +0 -0
  165. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C11-transform.python.json +0 -0
  166. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C11-transform.upstream.json +0 -0
  167. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C12-system.python.json +0 -0
  168. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C12-system.upstream.json +0 -0
  169. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C13-steering.python.json +0 -0
  170. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C13-steering.upstream.json +0 -0
  171. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C14-followup-all.python.json +0 -0
  172. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C14-followup-all.upstream.json +0 -0
  173. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C14-followup-one.python.json +0 -0
  174. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C14-followup-one.upstream.json +0 -0
  175. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C15-prepare.python.json +0 -0
  176. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C15-prepare.upstream.json +0 -0
  177. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-continue.python.json +0 -0
  178. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-continue.upstream.json +0 -0
  179. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-end.python.json +0 -0
  180. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-end.upstream.json +0 -0
  181. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-mixed.python.json +0 -0
  182. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-mixed.upstream.json +0 -0
  183. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-terminate.python.json +0 -0
  184. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C16-terminate.upstream.json +0 -0
  185. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C19-abort-stream.python.json +0 -0
  186. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C19-abort-stream.upstream.json +0 -0
  187. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C19-abort-tools.python.json +0 -0
  188. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C19-abort-tools.upstream.json +0 -0
  189. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C24-provider-error.python.json +0 -0
  190. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/C24-provider-error.upstream.json +0 -0
  191. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/artifacts.json +0 -0
  192. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/benchmark.json +0 -0
  193. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/bootstrap.json +0 -0
  194. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/candidate-live-noop.json +0 -0
  195. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/conformance.json +0 -0
  196. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/linux-3.11.json +0 -0
  197. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/linux-3.12.json +0 -0
  198. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/linux-3.13.json +0 -0
  199. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/linux-3.14.json +0 -0
  200. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/linux-install-hash-repair.json +0 -0
  201. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-0.5-regression.json +0 -0
  202. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-0.5-session-claude.json +0 -0
  203. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-0.5-session-codex.json +0 -0
  204. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-claude-reasoning-replay.json +0 -0
  205. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-final-matrix.json +0 -0
  206. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-initial.json +0 -0
  207. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-loop-initial.json +0 -0
  208. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-providers.json +0 -0
  209. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-reasoning-replay.json +0 -0
  210. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-refresh.json +0 -0
  211. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-regression-0.4-deepseek.json +0 -0
  212. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-regression-0.4-first-run.json +0 -0
  213. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-regression-0.4.json +0 -0
  214. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-session-claude.json +0 -0
  215. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-session-codex.json +0 -0
  216. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-websocket-cached-recheck.json +0 -0
  217. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-websocket-cached.json +0 -0
  218. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/live-websocket-final.json +0 -0
  219. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/plugins.upstream.json +0 -0
  220. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/provider-conformance.json +0 -0
  221. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/recovery-conformance.upstream.json +0 -0
  222. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/runtime-dependencies.json +0 -0
  223. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/source-verification.json +0 -0
  224. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.1.0/artifacts.json +0 -0
  225. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.1.0/linux-3.11.json +0 -0
  226. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.1.0/linux-3.12.json +0 -0
  227. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.1.0/linux-3.13.json +0 -0
  228. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.1.0/verification.json +0 -0
  229. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.2.0/artifacts.json +0 -0
  230. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.2.0/linux-3.11.json +0 -0
  231. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.2.0/linux-3.12.json +0 -0
  232. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.2.0/linux-3.13.json +0 -0
  233. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.2.0/verification.json +0 -0
  234. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/against-0.4-fixtures.json +0 -0
  235. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/artifacts.json +0 -0
  236. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/linux-3.11.json +0 -0
  237. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/linux-3.12.json +0 -0
  238. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/linux-3.13.json +0 -0
  239. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/provider-conformance.json +0 -0
  240. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.3.0/verification.json +0 -0
  241. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.4.0/artifacts.json +0 -0
  242. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.4.0/linux-3.11.json +0 -0
  243. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.4.0/linux-3.12.json +0 -0
  244. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.4.0/linux-3.13.json +0 -0
  245. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.4.0/verification.json +0 -0
  246. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.5.0/artifacts.json +0 -0
  247. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.5.0/linux-3.11.json +0 -0
  248. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.5.0/linux-3.12.json +0 -0
  249. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.5.0/linux-3.13.json +0 -0
  250. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.5.0/verification.json +0 -0
  251. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.6.0/artifacts.json +0 -0
  252. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.6.0/linux-3.11.json +0 -0
  253. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.6.0/linux-3.12.json +0 -0
  254. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.6.0/linux-3.13.json +0 -0
  255. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.6.0/verification.json +0 -0
  256. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.7.0/artifacts.json +0 -0
  257. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.7.0/linux-3.11.json +0 -0
  258. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.7.0/linux-3.12.json +0 -0
  259. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.7.0/linux-3.13.json +0 -0
  260. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.7.0/linux-3.14.json +0 -0
  261. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.7.0/verification.json +0 -0
  262. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.8.0/artifacts.json +0 -0
  263. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.8.0/linux-3.11.json +0 -0
  264. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.8.0/linux-3.12.json +0 -0
  265. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.8.0/linux-3.13.json +0 -0
  266. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.8.0/linux-3.14.json +0 -0
  267. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/v0.8.0/verification.json +0 -0
  268. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/results/websocket-conformance.json +0 -0
  269. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/runner.py +0 -0
  270. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/websocket-fixtures/codex-continuation-lost-state.json +0 -0
  271. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/compat/websocket-fixtures/codex-continuation-tool-loop.json +0 -0
  272. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/CODING_AGENTS.md +0 -0
  273. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/CONCEPTS.md +0 -0
  274. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/IMPLEMENTATION.md +0 -0
  275. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/MCP_INTERACTION.md +0 -0
  276. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/PROVIDERS.md +0 -0
  277. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/history/IMPLEMENTATION-0.3.0.md +0 -0
  278. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/history/IMPLEMENTATION-0.4.0.md +0 -0
  279. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/history/IMPLEMENTATION-0.5.0.md +0 -0
  280. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/history/IMPLEMENTATION-0.6.0.md +0 -0
  281. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/history/IMPLEMENTATION-0.7.0.md +0 -0
  282. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/history/IMPLEMENTATION-0.8.0.md +0 -0
  283. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/CODING_AGENTS.md +0 -0
  284. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/CONCEPTS.md +0 -0
  285. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/MCP_HOST_VERIFICATION.md +0 -0
  286. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/MCP_INTERACTION.md +0 -0
  287. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/docs/zh/PROVIDERS.md +0 -0
  288. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/in_memory.py +0 -0
  289. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/local_model.py +0 -0
  290. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/mcp_interactive.py +0 -0
  291. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/mcp_interactive_server.py +0 -0
  292. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/mcp_tools.py +0 -0
  293. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/plugin_demo.py +0 -0
  294. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/plugins/lab_tools/agents/reviewer.md +0 -0
  295. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/plugins/lab_tools/ops.py +0 -0
  296. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/plugins/lab_tools/plugin.py +0 -0
  297. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/plugins/lab_tools/prompts/dedup.md +0 -0
  298. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/plugins/lab_tools/skills/dedup-window/SKILL.md +0 -0
  299. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/provider_chat.py +0 -0
  300. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/quickstart.py +0 -0
  301. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/recovery.py +0 -0
  302. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/save_restore.py +0 -0
  303. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/examples/subagent.py +0 -0
  304. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/_mcp_host.py +0 -0
  305. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/_mcp_interaction.py +0 -0
  306. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/_mcp_process.py +0 -0
  307. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/_mcp_sampling.py +0 -0
  308. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/cancellation.py +0 -0
  309. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/data/models.json +0 -0
  310. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/errors.py +0 -0
  311. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/estimate.py +0 -0
  312. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/function_tools.py +0 -0
  313. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/hooks.py +0 -0
  314. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/limits.py +0 -0
  315. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/lowlevel.py +0 -0
  316. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/mcp.py +0 -0
  317. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/models.py +0 -0
  318. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/plugins/_frontmatter.py +0 -0
  319. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/plugins/_resources.py +0 -0
  320. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/plugins/_subagents.py +0 -0
  321. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/provider.py +0 -0
  322. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/__init__.py +0 -0
  323. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/anthropic.py +0 -0
  324. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/common.py +0 -0
  325. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/completions.py +0 -0
  326. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/oauth.py +0 -0
  327. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/openai.py +0 -0
  328. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/providers/transport.py +0 -0
  329. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/proxy.py +0 -0
  330. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/py.typed +0 -0
  331. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/queues.py +0 -0
  332. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/recovery.py +0 -0
  333. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/stream.py +0 -0
  334. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/sync.py +0 -0
  335. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/tasks.py +0 -0
  336. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/testing.py +0 -0
  337. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/src/pi_python/transcript.py +0 -0
  338. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/servers/mcp_interactive_server.py +0 -0
  339. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/servers/mcp_process_server.py +0 -0
  340. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/servers/mcp_server.py +0 -0
  341. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_completions.py +0 -0
  342. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_conformance.py +0 -0
  343. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_contract_v3.py +0 -0
  344. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_control.py +0 -0
  345. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_extended_core.py +0 -0
  346. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_function_tools.py +0 -0
  347. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_lifecycle_contracts.py +0 -0
  348. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_loop.py +0 -0
  349. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_mcp.py +0 -0
  350. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_mcp_delta.py +0 -0
  351. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_mcp_host.py +0 -0
  352. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_mcp_interaction.py +0 -0
  353. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_mcp_process.py +0 -0
  354. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_messages.py +0 -0
  355. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_models.py +0 -0
  356. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_nested_agents.py +0 -0
  357. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_oauth.py +0 -0
  358. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_plugin_conformance.py +0 -0
  359. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_providers.py +0 -0
  360. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_recovery.py +0 -0
  361. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_recovery_conformance.py +0 -0
  362. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_recovery_helpers.py +0 -0
  363. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_release.py +0 -0
  364. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_session_changes.py +0 -0
  365. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_sync.py +0 -0
  366. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_tasks.py +0 -0
  367. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_tools.py +0 -0
  368. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_typeddict_annotations.py +0 -0
  369. {pi_python_core-0.10.0 → pi_python_core-0.10.2}/tests/test_websocket_continuation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pi-python-core
3
- Version: 0.10.0
3
+ Version: 0.10.2
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
@@ -99,6 +99,7 @@ To try it offline first: `python examples/quickstart.py`.
99
99
  | Use MCP tools | `async with connect_stdio(...) as tools`, or `connect_http(url)` for a remote server | [mcp_tools](examples/mcp_tools.py) |
100
100
  | Let one agent call another | Wrap the sub-agent as a tool; cancellation propagates down. Plugins can also define subagents in Markdown | [subagent](examples/subagent.py) |
101
101
  | Package and share capabilities | A plugin bundles tools, instructions, hooks, skills, prompt templates, subagents and MCP servers; load it by name or path with `load_plugins` and build the agent with `plugins.agent(...)` | [plugin_demo](examples/plugin_demo.py) |
102
+ | Remember across conversations | Not built in, as in Pi's core; an example plugin saves facts to a file and puts them in the system prompt, ready to copy and adapt | [memory_demo](examples/memory_demo.py) |
102
103
  | Intervene mid-run | `steer` injects guidance, `follow_up` queues the next task, `abort` cancels at any time (callable from any thread) | |
103
104
  | Context full, service errors | `is_context_overflow` and `is_retryable_error` tell you why, `continue_run()` retries, `transform_context` compacts | [recovery](examples/recovery.py) |
104
105
  | Save and restore conversations | `encode_messages` / `decode_messages`; your application decides where to store them | [save_restore](examples/save_restore.py) |
@@ -65,6 +65,7 @@ To try it offline first: `python examples/quickstart.py`.
65
65
  | Use MCP tools | `async with connect_stdio(...) as tools`, or `connect_http(url)` for a remote server | [mcp_tools](examples/mcp_tools.py) |
66
66
  | Let one agent call another | Wrap the sub-agent as a tool; cancellation propagates down. Plugins can also define subagents in Markdown | [subagent](examples/subagent.py) |
67
67
  | Package and share capabilities | A plugin bundles tools, instructions, hooks, skills, prompt templates, subagents and MCP servers; load it by name or path with `load_plugins` and build the agent with `plugins.agent(...)` | [plugin_demo](examples/plugin_demo.py) |
68
+ | Remember across conversations | Not built in, as in Pi's core; an example plugin saves facts to a file and puts them in the system prompt, ready to copy and adapt | [memory_demo](examples/memory_demo.py) |
68
69
  | Intervene mid-run | `steer` injects guidance, `follow_up` queues the next task, `abort` cancels at any time (callable from any thread) | |
69
70
  | Context full, service errors | `is_context_overflow` and `is_retryable_error` tell you why, `continue_run()` retries, `transform_context` compacts | [recovery](examples/recovery.py) |
70
71
  | Save and restore conversations | `encode_messages` / `decode_messages`; your application decides where to store them | [save_restore](examples/save_restore.py) |
@@ -65,6 +65,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
65
65
  | 用 MCP 工具 | `async with connect_stdio(...) as tools`;远程服务器用 `connect_http(url)` | [mcp_tools](examples/mcp_tools.py) |
66
66
  | 让一个 agent 调用另一个 | 把子 agent 包成工具,取消会一路传下去;插件也可以用 Markdown 定义子 agent | [subagent](examples/subagent.py) |
67
67
  | 打包和分享能力 | 插件把工具、说明、钩子、技能、提示模板、子 agent 和 MCP 服务器打成一组;用 `load_plugins` 按名字或路径加载,再用 `plugins.agent(...)` 构造 agent | [plugin_demo](examples/plugin_demo.py) |
68
+ | 跨对话记住信息 | 与 Pi 的核心一样不内置;示例插件把事实存进文件,再放进系统提示,可以复制后修改 | [memory_demo](examples/memory_demo.py) |
68
69
  | 中途干预 | `steer` 插入指导,`follow_up` 排队后续任务,`abort` 随时取消(任何线程都可以调用) | |
69
70
  | 上下文满了、服务出错 | `is_context_overflow`、`is_retryable_error` 判断原因,`continue_run()` 重试,`transform_context` 压缩 | [recovery](examples/recovery.py) |
70
71
  | 保存与恢复对话 | `encode_messages` / `decode_messages`,应用决定存在哪里 | [save_restore](examples/save_restore.py) |
@@ -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,19 @@
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
+
35
48
  ## 有意差异与范围外行为
36
49
 
37
50
  D1–D8 保持原设计的含义。D1 严格输入;D2 执行前钩子不能通过原地修改参数改变调用;D3 资源预算;D4 Python 取消及未知结果;D5 状态复制;D6 输出检查;D7 诊断、原始结果和完整流校验;D8 文本与工具子集。每项有独立 Python 断言,不以 skip 代替验收。
@@ -133,6 +146,7 @@ CI 第一次在 GitHub 上运行,Linux 和 macOS 全部通过,另外发现
133
146
  - 上游由命令行程序发现和加载 package(`pi install`、`settings.json`、项目信任)。本库没有应用程序,由应用代码指定加载哪些插件,不自动发现。已安装的插件通过 entry point 组 `pi_python.plugins` 注册,对应 `pi install npm:...`。
134
147
  - 插件代码是 Python 的 `setup(api)`,对应 Pi 扩展的工厂函数。`PluginAPI` 只保留与界面无关的部分(工具、系统提示、钩子、事件、MCP),没有命令、快捷键、渲染器等终端界面接口;另加了 `service`(应用提供的对象)、`options`(按插件分开的设置)、`add_check`(自检)和 `on_close`。
135
148
  - 插件处理函数出错时交给 `on_error`,默认写日志;上游交给 `emitError`,显示在界面上。`before_tool_call` 与上游一样,出错时让这次调用失败。应用自己的钩子保持核心行为,不做隔离。
149
+ - 插件注册处理函数时检查它能否接收该钩子传入的位置参数(`subscribe` 为事件一个参数,`add_check` 和 `on_close` 没有参数),不能就报 `ConfigurationError`,加载停止(0.10.1 加入)。上游没有这一步:TypeScript 在编译时检查类型,JavaScript 调用时多出的参数会被忽略。Python 会在每次调用时抛出 `TypeError`,插件处理函数的错误又只写日志,不检查的话这样的处理函数会悄悄失效。应用自己的钩子不做这项检查。
136
150
  - 模型用 `read_skill` 工具读取技能(上游让模型用通用的 `read` 工具)。它只能读技能目录里的文件,单个文件不超过 256 KiB。
137
151
  - 子 agent 在同一进程、同一事件循环里运行(上游为每个子 agent 启动一个 pi 子进程),使用主 agent 的 Provider 和合并后的全部钩子。上游的子进程会加载同样的扩展,所以插件的处理函数同样作用于子 agent,这一点与上游一致。不指定模型时,子 agent 用调用那一刻主 agent 的模型(上游从设置里取默认模型)。没有上游的 `agentScope`、`cwd` 参数和项目级 agent 的确认步骤。
138
152
  - MCP:工具名为 `mcp__<服务器>__<工具>`,但工具名里的 `-` 保留(上游换成 `_`)。字符串里只展开 `${PLUGIN_ROOT}`、`${PYTHON}` 和环境变量,前两个是本库新增;不支持 `!命令`。不支持 OAuth、`exposure` / `toolExposure` 和单次请求超时,这些设置会被忽略并给出警告。新增 `pi_python.mcp.connect_http`,连接 streamable HTTP 服务器;在 MCP SDK 1.10、1.30 和 2.3 上测过。
@@ -15,12 +15,12 @@
15
15
  "--extra",
16
16
  "providers",
17
17
  "--extra",
18
- "mcp"
18
+ "mcp-interactive"
19
19
  ],
20
20
  "exit_code": 0,
21
- "seconds": 0.021038709091953933,
21
+ "seconds": 0.02251266618259251,
22
22
  "stdout": "",
23
- "stderr": "Resolved 47 packages in 3ms\nAudited 44 packages in 3ms\n"
23
+ "stderr": "Resolved 60 packages in 2ms\nAudited 57 packages in 8ms\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.06494950002525002,
38
+ "seconds": 0.0649345419369638,
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.02588170801755041,
57
- "stdout": "89 files already formatted\n",
56
+ "seconds": 0.024958166992291808,
57
+ "stdout": "110 files already formatted\n",
58
58
  "stderr": ""
59
59
  },
60
60
  {
@@ -65,8 +65,8 @@
65
65
  "src/pi_python"
66
66
  ],
67
67
  "exit_code": 0,
68
- "seconds": 0.35578474996145815,
69
- "stdout": "Success: no issues found in 37 source files\n",
68
+ "seconds": 0.5702685830183327,
69
+ "stdout": "Success: no issues found in 43 source files\n",
70
70
  "stderr": ""
71
71
  },
72
72
  {
@@ -77,8 +77,8 @@
77
77
  "-q"
78
78
  ],
79
79
  "exit_code": 0,
80
- "seconds": 11.12525479204487,
81
- "stdout": "........................................................................ [ 18%]\n........................................................................ [ 36%]\n........................................................................ [ 54%]\n........................................................................ [ 72%]\n........................................................................ [ 90%]\n...................................... [100%]\n398 passed in 10.69s\n",
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",
82
82
  "stderr": ""
83
83
  },
84
84
  {
@@ -89,7 +89,7 @@
89
89
  "examples/in_memory.py"
90
90
  ],
91
91
  "exit_code": 0,
92
- "seconds": 0.10870087496004999,
92
+ "seconds": 0.6022720420733094,
93
93
  "stdout": "The telemetry contains 3 complete records.\n",
94
94
  "stderr": ""
95
95
  },
@@ -104,8 +104,8 @@
104
104
  "test/agent.test.ts"
105
105
  ],
106
106
  "exit_code": 0,
107
- "seconds": 1.1702424169052392,
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 00:19:15\n Duration 758ms (transform 566ms, setup 0ms, import 1.06s, tests 171ms, environment 0ms)\n\n",
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",
109
109
  "stderr": ""
110
110
  },
111
111
  {
@@ -121,8 +121,8 @@
121
121
  "test/openai-codex-stream.test.ts"
122
122
  ],
123
123
  "exit_code": 0,
124
- "seconds": 1.1117059160023928,
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 00:19:16\n Duration 816ms (transform 907ms, setup 0ms, import 1.59s, tests 244ms, environment 0ms)\n\n",
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",
126
126
  "stderr": ""
127
127
  },
128
128
  {
@@ -133,7 +133,7 @@
133
133
  "scripts/conformance.py"
134
134
  ],
135
135
  "exit_code": 0,
136
- "seconds": 4.8161368750734255,
136
+ "seconds": 5.7676790410187095,
137
137
  "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
138
  "stderr": ""
139
139
  },
@@ -145,7 +145,7 @@
145
145
  "scripts/provider_conformance.py"
146
146
  ],
147
147
  "exit_code": 0,
148
- "seconds": 13.814684083103202,
148
+ "seconds": 15.009326208150014,
149
149
  "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
150
  "stderr": ""
151
151
  },
@@ -157,7 +157,7 @@
157
157
  "scripts/websocket_conformance.py"
158
158
  ],
159
159
  "exit_code": 0,
160
- "seconds": 0.42606441595125943,
160
+ "seconds": 0.9335567080415785,
161
161
  "stdout": "codex-continuation-lost-state.json passed\ncodex-continuation-tool-loop.json passed\n",
162
162
  "stderr": ""
163
163
  },
@@ -169,7 +169,7 @@
169
169
  "scripts/recovery_conformance.py"
170
170
  ],
171
171
  "exit_code": 0,
172
- "seconds": 0.1813259170157835,
172
+ "seconds": 0.628341041970998,
173
173
  "stdout": "recovery classifiers: 44/44 match upstream \n",
174
174
  "stderr": ""
175
175
  },
@@ -181,8 +181,8 @@
181
181
  "scripts/plugin_conformance.py"
182
182
  ],
183
183
  "exit_code": 0,
184
- "seconds": 0.39384091598913074,
185
- "stdout": "....... [100%]\n7 passed in 0.05s\n",
184
+ "seconds": 1.306608916958794,
185
+ "stdout": "....... [100%]\n7 passed in 0.74s\n",
186
186
  "stderr": ""
187
187
  },
188
188
  {
@@ -193,10 +193,22 @@
193
193
  "examples/plugin_demo.py"
194
194
  ],
195
195
  "exit_code": 0,
196
- "seconds": 0.11929479194805026,
196
+ "seconds": 0.576676958007738,
197
197
  "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
198
  "stderr": ""
199
199
  },
200
+ {
201
+ "command": [
202
+ "uv",
203
+ "run",
204
+ "python",
205
+ "examples/mcp_interactive.py"
206
+ ],
207
+ "exit_code": 0,
208
+ "seconds": 1.8445827078539878,
209
+ "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
+ "stderr": ""
211
+ },
200
212
  {
201
213
  "command": [
202
214
  "uv",
@@ -206,7 +218,7 @@
206
218
  "--help"
207
219
  ],
208
220
  "exit_code": 0,
209
- "seconds": 0.10245537490118295,
221
+ "seconds": 0.6522026660386473,
210
222
  "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",
211
223
  "stderr": ""
212
224
  },
@@ -216,9 +228,9 @@
216
228
  "build"
217
229
  ],
218
230
  "exit_code": 0,
219
- "seconds": 1.1796516249887645,
231
+ "seconds": 1.0952744169626385,
220
232
  "stdout": "",
221
- "stderr": "Building source distribution...\nBuilding wheel from source distribution...\nSuccessfully built dist/pi_python_core-0.9.0.tar.gz\nSuccessfully built dist/pi_python_core-0.9.0-py3-none-any.whl\n"
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"
222
234
  }
223
235
  ],
224
236
  "status": "passed"
@@ -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 |
@@ -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`。测试改为有界等待实际清理完成后关闭,并检查迟到的成功不能改写已提交记录;修正后新合同和全量测试均重新通过。
@@ -311,6 +311,62 @@ Several plugins can handle the same hook. Handlers run in this order: your own h
311
311
 
312
312
  When a plugin's handler raises an exception or returns the wrong type, a `PluginFailure` report goes to the `on_error` callback of `load_plugins`, which may be sync or async and logs the failure by default. That handler's contribution is skipped and the other handlers still run, so one faulty plugin does not stop the agent. The exception is `before_tool_call`, where an error fails that tool call, as in Pi. Your own hooks keep the core behavior described in the [API](API.md#hooks).
313
313
 
314
+ A handler that cannot accept the arguments its hook passes, such as a `transform_context` handler written as `def inject(messages)` instead of `def inject(messages, cancel)`, is rejected when it is registered: `api.on` raises `ConfigurationError` naming the expected arguments, and loading stops. The same check applies to `subscribe` (one argument, the event), `add_check` and `on_close` (no arguments). Otherwise such a handler would fail on every call and only be logged.
315
+
316
+ ## Calling a model from a plugin
317
+
318
+ The application owns providers and credentials, so a plugin has no model of its own. A plugin that needs a model call, for example to summarize or to merge notes, asks for the Provider as the `"provider"` service and reads the model name from its options:
319
+
320
+ ```python
321
+ from pi_python import Agent, ToolContext
322
+
323
+
324
+ def setup(api):
325
+ provider = api.service("provider")
326
+
327
+ async def summarize(text: str, context: ToolContext) -> str:
328
+ """Summarize a text in three sentences.
329
+
330
+ Args:
331
+ text: The text to summarize.
332
+ """
333
+ writer = Agent(
334
+ provider=provider,
335
+ model=api.options.get("model") or context.agent_context.model,
336
+ system_prompt="Summarize the text in three sentences.",
337
+ )
338
+ result = await context.run_agent(writer, text) # aborting the run aborts this call
339
+ reply = "".join(getattr(block, "text", "") for block in result.messages[-1].content)
340
+ if result.status != "completed" or not reply:
341
+ raise RuntimeError(f"The summary failed: {result.status}")
342
+ return reply
343
+
344
+ api.add_tool(summarize)
345
+ ```
346
+
347
+ The application passes `services={"provider": provider}`, and `options={"summary": {"model": "claude-haiku-4-5"}}` to use a cheaper model for the plugin named `summary`. Without the option, the call uses the model of the agent that called the tool. Inside a tool, run the call with `context.run_agent`, so that aborting the main run also aborts it. When the model call is optional, use `api.service("provider", default=None)` and say in the plugin's documentation what happens without it.
348
+
349
+ ## Memory across conversations
350
+
351
+ Neither this library nor Pi's core has built-in memory. Pi's application remembers through instruction files it reads into the system prompt (`AGENTS.md`), saved sessions and compaction. What is worth remembering, and where to keep it, differs between applications. A plugin can add memory with two parts: tools that save facts to storage the application chooses, and a `transform_context` handler that puts the saved facts into every request.
352
+
353
+ [examples/plugins/memory](../examples/plugins/memory) does this in one file, and [examples/memory_demo.py](../examples/memory_demo.py) runs it offline:
354
+
355
+ ```python
356
+ async with load_plugins(
357
+ ["./examples/plugins/memory"],
358
+ services={"provider": provider}, # optional: merges facts when the list is full
359
+ options={"memory": {"path": "memory.json", "max_facts": 50}},
360
+ ) as plugins:
361
+ agent = plugins.agent(provider=provider, model="claude-sonnet-5-5")
362
+ ```
363
+
364
+ - The model saves a fact with `remember` and removes one with `forget`. The facts are kept in the JSON file.
365
+ - Before every request, the facts become the `memory` section of the system prompt (`SystemMessage.sections`). The stored conversation does not change. The section changes only when the facts do, so the cached prompt prefix stays valid in between.
366
+ - When a new fact would make the list longer than `max_facts`, the plugin asks a model to merge the list, as in [Calling a model from a plugin](#calling-a-model-from-a-plugin). Without a provider, the oldest facts are dropped.
367
+
368
+ Copy the directory and change what your application needs: what is worth saving, retrieval when there are too many facts to list, or a database in place of the file. The example assumes that one process writes the file at a time.
369
+
314
370
  ## Differences from Pi
315
371
 
316
372
  Pi's command-line application finds and loads packages; this library has no application, so your code names the plugins to load. The other differences:
@@ -258,6 +258,7 @@ install. Do not claim packaging is verified merely because directory loading wor
258
258
  | Async work tied to plugin lifetime | Create `scope = api.task_scope()` after registering resources it uses. Await `scope.run(client.fetch(...), cancel=context.cancel)` in an async tool with a `ToolContext` parameter. Declare `task-scope-v1` if using it. |
259
259
  | Worker thread needs a loop-bound connection | Host creates a `LoopPortal` on the connection's loop and passes it as a service. The synchronous tool uses `portal.call(async_function, ...)`. See [API](API.md#owned-tasks-and-calls-from-worker-threads). |
260
260
  | A hook | Register `@api.on("before_tool_call")`; return `False` to block. Use [documented hook signatures](API.md#hooks), and test the blocked path. |
261
+ | Model call inside the plugin | Ask for `provider = api.service("provider")`; take the model from `api.options.get("model")`, else `context.agent_context.model`. In an async tool, run `await context.run_agent(Agent(provider=provider, model=...), text)`. See [Calling a model from a plugin](PLUGINS.md#calling-a-model-from-a-plugin). |
261
262
  | Subagent | Add `agents/reviewer.md` with `name`, `description`, optional `tools`/`model`, and a system-prompt body. See the [lab_tools example](../examples/plugins/lab_tools). |
262
263
  | MCP server | Add `mcp.json` or call `api.add_mcp_server`. Install `[mcp]`; specify required servers/tools in readiness. Use the [interactive workflow](MCP_INTERACTION.md) for host model/form callbacks. |
263
264
 
@@ -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)