pi-python-core 0.8.1__tar.gz → 0.9.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 (336) hide show
  1. pi_python_core-0.9.0/PKG-INFO +126 -0
  2. pi_python_core-0.9.0/README.md +95 -0
  3. pi_python_core-0.8.1/README.md → pi_python_core-0.9.0/README.zh-CN.md +17 -12
  4. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/COVERAGE.md +31 -3
  5. pi_python_core-0.9.0/compat/plugin-cases.json +424 -0
  6. pi_python_core-0.9.0/compat/plugin-fixtures/prompts/notes.txt +1 -0
  7. pi_python_core-0.9.0/compat/plugin-fixtures/prompts/plan.md +6 -0
  8. pi_python_core-0.9.0/compat/plugin-fixtures/prompts/review.md +6 -0
  9. pi_python_core-0.9.0/compat/plugin-fixtures/prompts/sub/nested.md +1 -0
  10. pi_python_core-0.9.0/compat/plugin-fixtures/prompts/summarize.md +1 -0
  11. pi_python_core-0.9.0/compat/plugin-fixtures/skills/.hidden/SKILL.md +4 -0
  12. pi_python_core-0.9.0/compat/plugin-fixtures/skills/alpha/SKILL.md +8 -0
  13. pi_python_core-0.9.0/compat/plugin-fixtures/skills/alpha/references/notes.md +1 -0
  14. pi_python_core-0.9.0/compat/plugin-fixtures/skills/beta/SKILL.md +5 -0
  15. pi_python_core-0.9.0/compat/plugin-fixtures/skills/broken/SKILL.md +5 -0
  16. pi_python_core-0.9.0/compat/plugin-fixtures/skills/delta/SKILL.md +8 -0
  17. pi_python_core-0.9.0/compat/plugin-fixtures/skills/gamma/SKILL.md +4 -0
  18. pi_python_core-0.9.0/compat/plugin-fixtures/skills/longdesc/SKILL.md +5 -0
  19. pi_python_core-0.9.0/compat/plugin-fixtures/skills/loose.md +4 -0
  20. pi_python_core-0.9.0/compat/plugin-fixtures/skills/nested/group/epsilon/SKILL.md +7 -0
  21. pi_python_core-0.9.0/compat/plugin-fixtures/skills/noname/SKILL.md +4 -0
  22. pi_python_core-0.9.0/compat/plugin-fixtures/skills/plain.md +1 -0
  23. pi_python_core-0.9.0/compat/plugin-fixtures/skills/zeta/SKILL.md +5 -0
  24. pi_python_core-0.9.0/compat/plugin-fixtures/skills/zeta/sub/eta/SKILL.md +4 -0
  25. pi_python_core-0.9.0/compat/results/linux-3.11.json +54 -0
  26. pi_python_core-0.9.0/compat/results/linux-3.12.json +54 -0
  27. pi_python_core-0.9.0/compat/results/linux-3.13.json +54 -0
  28. pi_python_core-0.9.0/compat/results/linux-3.14.json +54 -0
  29. pi_python_core-0.9.0/compat/results/plugins.upstream.json +277 -0
  30. pi_python_core-0.9.0/compat/results/v0.8.0/artifacts.json +12 -0
  31. pi_python_core-0.9.0/compat/results/verification.json +225 -0
  32. pi_python_core-0.9.0/docs/API.md +218 -0
  33. pi_python_core-0.9.0/docs/CONCEPTS.md +75 -0
  34. pi_python_core-0.9.0/docs/IMPLEMENTATION.md +65 -0
  35. pi_python_core-0.9.0/docs/PLUGINS.md +239 -0
  36. pi_python_core-0.9.0/docs/PROVIDERS.md +215 -0
  37. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/docs/history/IMPLEMENTATION-0.3.0.md +1 -1
  38. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/docs/history/IMPLEMENTATION-0.4.0.md +2 -2
  39. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/docs/history/IMPLEMENTATION-0.5.0.md +4 -4
  40. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/docs/history/IMPLEMENTATION-0.6.0.md +2 -2
  41. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/docs/history/IMPLEMENTATION-0.7.0.md +1 -1
  42. pi_python_core-0.8.1/docs/IMPLEMENTATION.md → pi_python_core-0.9.0/docs/history/IMPLEMENTATION-0.8.0.md +10 -8
  43. {pi_python_core-0.8.1/docs → pi_python_core-0.9.0/docs/zh}/API.md +22 -2
  44. {pi_python_core-0.8.1/docs → pi_python_core-0.9.0/docs/zh}/CONCEPTS.md +3 -1
  45. pi_python_core-0.9.0/docs/zh/PLUGINS.md +239 -0
  46. {pi_python_core-0.8.1/docs → pi_python_core-0.9.0/docs/zh}/PROVIDERS.md +15 -11
  47. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/local_model.py +1 -1
  48. pi_python_core-0.9.0/examples/plugin_demo.py +46 -0
  49. pi_python_core-0.9.0/examples/plugins/lab_tools/agents/reviewer.md +6 -0
  50. pi_python_core-0.9.0/examples/plugins/lab_tools/ops.py +16 -0
  51. pi_python_core-0.9.0/examples/plugins/lab_tools/plugin.py +24 -0
  52. pi_python_core-0.9.0/examples/plugins/lab_tools/prompts/dedup.md +5 -0
  53. pi_python_core-0.9.0/examples/plugins/lab_tools/skills/dedup-window/SKILL.md +12 -0
  54. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/quickstart.py +1 -1
  55. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/pyproject.toml +7 -4
  56. pi_python_core-0.9.0/src/pi_python/_version.py +1 -0
  57. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/mcp.py +93 -10
  58. pi_python_core-0.9.0/src/pi_python/plugins/__init__.py +993 -0
  59. pi_python_core-0.9.0/src/pi_python/plugins/_compose.py +270 -0
  60. pi_python_core-0.9.0/src/pi_python/plugins/_frontmatter.py +444 -0
  61. pi_python_core-0.9.0/src/pi_python/plugins/_resources.py +385 -0
  62. pi_python_core-0.9.0/src/pi_python/plugins/_subagents.py +269 -0
  63. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/__init__.py +1 -1
  64. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/oauth.py +6 -1
  65. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/tools.py +23 -14
  66. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/servers/mcp_server.py +11 -1
  67. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_examples.py +1 -2
  68. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_oauth.py +6 -0
  69. pi_python_core-0.9.0/tests/test_plugin_conformance.py +115 -0
  70. pi_python_core-0.9.0/tests/test_plugins.py +1293 -0
  71. pi_python_core-0.8.1/PKG-INFO +0 -119
  72. pi_python_core-0.8.1/src/pi_python/_version.py +0 -1
  73. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/.gitignore +0 -0
  74. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/LICENSE +0 -0
  75. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/NOTICE +0 -0
  76. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/__init__.py +0 -0
  77. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/baseline.json +0 -0
  78. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/contracts/release-candidate.example.json +0 -0
  79. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/contracts/release-candidate.schema.json +0 -0
  80. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/contracts/upstream-baseline.json +0 -0
  81. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C01-text.json +0 -0
  82. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C02-tool.json +0 -0
  83. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C03-sequential.json +0 -0
  84. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C04-parallel.json +0 -0
  85. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C05-one-sequential.json +0 -0
  86. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C06-one-failure.json +0 -0
  87. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C07-unknown.json +0 -0
  88. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C08-prepare.json +0 -0
  89. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C09-before-error.json +0 -0
  90. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C09-block.json +0 -0
  91. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C09-replace.json +0 -0
  92. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C10-length.json +0 -0
  93. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C11-transform.json +0 -0
  94. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C12-system.json +0 -0
  95. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C13-steering.json +0 -0
  96. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C14-followup-all.json +0 -0
  97. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C14-followup-one.json +0 -0
  98. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C15-prepare.json +0 -0
  99. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C16-continue.json +0 -0
  100. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C16-end.json +0 -0
  101. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C16-mixed.json +0 -0
  102. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C16-terminate.json +0 -0
  103. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C19-abort-stream.json +0 -0
  104. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C19-abort-tools.json +0 -0
  105. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/fixtures/C24-provider-error.json +0 -0
  106. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-oauth.json +0 -0
  107. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-request-cache-long.json +0 -0
  108. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-request-cache-none.json +0 -0
  109. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-request-replay.json +0 -0
  110. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-request-tools-image.json +0 -0
  111. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-text.json +0 -0
  112. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-thinking.json +0 -0
  113. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/anthropic-tool.json +0 -0
  114. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/codex-request-tools-image.json +0 -0
  115. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/codex-text.json +0 -0
  116. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-compat-strict.json +0 -0
  117. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-deepseek.json +0 -0
  118. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-format-ant-ling.json +0 -0
  119. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-format-baseten.json +0 -0
  120. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-format-string-thinking.json +0 -0
  121. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-format-together.json +0 -0
  122. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-format-zai.json +0 -0
  123. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-midconvo-tools.json +0 -0
  124. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-moonshot-usage.json +0 -0
  125. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-openai-off.json +0 -0
  126. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-openrouter.json +0 -0
  127. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-qwen-length.json +0 -0
  128. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-reasoning-content.json +0 -0
  129. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-request-replay.json +0 -0
  130. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-text.json +0 -0
  131. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/completions-tool-stream.json +0 -0
  132. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-incomplete.json +0 -0
  133. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-request-cache-long.json +0 -0
  134. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-request-cache-none.json +0 -0
  135. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-request-reasoning.json +0 -0
  136. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-request-replay.json +0 -0
  137. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-request-tools-image.json +0 -0
  138. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-text.json +0 -0
  139. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-thinking-backfill.json +0 -0
  140. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-thinking.json +0 -0
  141. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/openai-tool.json +0 -0
  142. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/proxy-text.json +0 -0
  143. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/proxy-thinking-tool.json +0 -0
  144. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-anthropic-collapse-budget.json +0 -0
  145. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-anthropic-native-tools.json +0 -0
  146. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-anthropic-oauth-effort.json +0 -0
  147. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-anthropic-redefinition.json +0 -0
  148. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-anthropic-server-fallback.json +0 -0
  149. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-anthropic-thinking-off.json +0 -0
  150. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-codex-tool-search.json +0 -0
  151. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-openai-additional-tools.json +0 -0
  152. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-openai-collapse.json +0 -0
  153. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-openai-explicit-cache-long.json +0 -0
  154. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-openai-explicit-cache-none.json +0 -0
  155. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/provider-fixtures/session-openai-removal.json +0 -0
  156. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/recovery-cases.json +0 -0
  157. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/release.py +0 -0
  158. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C01-text.python.json +0 -0
  159. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C01-text.upstream.json +0 -0
  160. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C02-tool.python.json +0 -0
  161. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C02-tool.upstream.json +0 -0
  162. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C03-sequential.python.json +0 -0
  163. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C03-sequential.upstream.json +0 -0
  164. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C04-parallel.python.json +0 -0
  165. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C04-parallel.upstream.json +0 -0
  166. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C05-one-sequential.python.json +0 -0
  167. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C05-one-sequential.upstream.json +0 -0
  168. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C06-one-failure.python.json +0 -0
  169. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C06-one-failure.upstream.json +0 -0
  170. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C07-unknown.python.json +0 -0
  171. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C07-unknown.upstream.json +0 -0
  172. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C08-prepare.python.json +0 -0
  173. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C08-prepare.upstream.json +0 -0
  174. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C09-before-error.python.json +0 -0
  175. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C09-before-error.upstream.json +0 -0
  176. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C09-block.python.json +0 -0
  177. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C09-block.upstream.json +0 -0
  178. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C09-replace.python.json +0 -0
  179. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C09-replace.upstream.json +0 -0
  180. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C10-length.python.json +0 -0
  181. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C10-length.upstream.json +0 -0
  182. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C11-transform.python.json +0 -0
  183. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C11-transform.upstream.json +0 -0
  184. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C12-system.python.json +0 -0
  185. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C12-system.upstream.json +0 -0
  186. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C13-steering.python.json +0 -0
  187. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C13-steering.upstream.json +0 -0
  188. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C14-followup-all.python.json +0 -0
  189. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C14-followup-all.upstream.json +0 -0
  190. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C14-followup-one.python.json +0 -0
  191. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C14-followup-one.upstream.json +0 -0
  192. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C15-prepare.python.json +0 -0
  193. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C15-prepare.upstream.json +0 -0
  194. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-continue.python.json +0 -0
  195. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-continue.upstream.json +0 -0
  196. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-end.python.json +0 -0
  197. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-end.upstream.json +0 -0
  198. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-mixed.python.json +0 -0
  199. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-mixed.upstream.json +0 -0
  200. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-terminate.python.json +0 -0
  201. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C16-terminate.upstream.json +0 -0
  202. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C19-abort-stream.python.json +0 -0
  203. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C19-abort-stream.upstream.json +0 -0
  204. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C19-abort-tools.python.json +0 -0
  205. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C19-abort-tools.upstream.json +0 -0
  206. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C24-provider-error.python.json +0 -0
  207. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/C24-provider-error.upstream.json +0 -0
  208. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/artifacts.json +0 -0
  209. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/benchmark.json +0 -0
  210. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/bootstrap.json +0 -0
  211. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/candidate-live-noop.json +0 -0
  212. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/conformance.json +0 -0
  213. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/linux-install-hash-repair.json +0 -0
  214. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-0.5-regression.json +0 -0
  215. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-0.5-session-claude.json +0 -0
  216. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-0.5-session-codex.json +0 -0
  217. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-claude-reasoning-replay.json +0 -0
  218. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-final-matrix.json +0 -0
  219. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-initial.json +0 -0
  220. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-loop-initial.json +0 -0
  221. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-providers.json +0 -0
  222. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-reasoning-replay.json +0 -0
  223. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-refresh.json +0 -0
  224. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-regression-0.4-deepseek.json +0 -0
  225. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-regression-0.4-first-run.json +0 -0
  226. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-regression-0.4.json +0 -0
  227. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-session-claude.json +0 -0
  228. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-session-codex.json +0 -0
  229. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-websocket-cached-recheck.json +0 -0
  230. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-websocket-cached.json +0 -0
  231. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/live-websocket-final.json +0 -0
  232. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/provider-conformance.json +0 -0
  233. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/recovery-conformance.upstream.json +0 -0
  234. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/runtime-dependencies.json +0 -0
  235. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/source-verification.json +0 -0
  236. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.1.0/artifacts.json +0 -0
  237. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.1.0/linux-3.11.json +0 -0
  238. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.1.0/linux-3.12.json +0 -0
  239. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.1.0/linux-3.13.json +0 -0
  240. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.1.0/verification.json +0 -0
  241. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.2.0/artifacts.json +0 -0
  242. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.2.0/linux-3.11.json +0 -0
  243. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.2.0/linux-3.12.json +0 -0
  244. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.2.0/linux-3.13.json +0 -0
  245. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.2.0/verification.json +0 -0
  246. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/against-0.4-fixtures.json +0 -0
  247. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/artifacts.json +0 -0
  248. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/linux-3.11.json +0 -0
  249. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/linux-3.12.json +0 -0
  250. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/linux-3.13.json +0 -0
  251. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/provider-conformance.json +0 -0
  252. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.3.0/verification.json +0 -0
  253. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.4.0/artifacts.json +0 -0
  254. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.4.0/linux-3.11.json +0 -0
  255. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.4.0/linux-3.12.json +0 -0
  256. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.4.0/linux-3.13.json +0 -0
  257. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.4.0/verification.json +0 -0
  258. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.5.0/artifacts.json +0 -0
  259. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.5.0/linux-3.11.json +0 -0
  260. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.5.0/linux-3.12.json +0 -0
  261. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.5.0/linux-3.13.json +0 -0
  262. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.5.0/verification.json +0 -0
  263. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.6.0/artifacts.json +0 -0
  264. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.6.0/linux-3.11.json +0 -0
  265. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.6.0/linux-3.12.json +0 -0
  266. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.6.0/linux-3.13.json +0 -0
  267. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.6.0/verification.json +0 -0
  268. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.7.0/artifacts.json +0 -0
  269. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.7.0/linux-3.11.json +0 -0
  270. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.7.0/linux-3.12.json +0 -0
  271. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.7.0/linux-3.13.json +0 -0
  272. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.7.0/linux-3.14.json +0 -0
  273. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/v0.7.0/verification.json +0 -0
  274. {pi_python_core-0.8.1/compat/results → pi_python_core-0.9.0/compat/results/v0.8.0}/linux-3.11.json +0 -0
  275. {pi_python_core-0.8.1/compat/results → pi_python_core-0.9.0/compat/results/v0.8.0}/linux-3.12.json +0 -0
  276. {pi_python_core-0.8.1/compat/results → pi_python_core-0.9.0/compat/results/v0.8.0}/linux-3.13.json +0 -0
  277. {pi_python_core-0.8.1/compat/results → pi_python_core-0.9.0/compat/results/v0.8.0}/linux-3.14.json +0 -0
  278. {pi_python_core-0.8.1/compat/results → pi_python_core-0.9.0/compat/results/v0.8.0}/verification.json +0 -0
  279. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/results/websocket-conformance.json +0 -0
  280. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/runner.py +0 -0
  281. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/websocket-fixtures/codex-continuation-lost-state.json +0 -0
  282. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/compat/websocket-fixtures/codex-continuation-tool-loop.json +0 -0
  283. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/in_memory.py +0 -0
  284. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/mcp_tools.py +0 -0
  285. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/provider_chat.py +0 -0
  286. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/recovery.py +0 -0
  287. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/save_restore.py +0 -0
  288. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/examples/subagent.py +0 -0
  289. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/__init__.py +0 -0
  290. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/agent.py +0 -0
  291. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/cancellation.py +0 -0
  292. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/data/models.json +0 -0
  293. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/errors.py +0 -0
  294. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/estimate.py +0 -0
  295. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/events.py +0 -0
  296. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/function_tools.py +0 -0
  297. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/hooks.py +0 -0
  298. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/limits.py +0 -0
  299. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/loop.py +0 -0
  300. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/lowlevel.py +0 -0
  301. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/messages.py +0 -0
  302. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/models.py +0 -0
  303. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/provider.py +0 -0
  304. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/anthropic.py +0 -0
  305. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/common.py +0 -0
  306. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/completions.py +0 -0
  307. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/openai.py +0 -0
  308. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/providers/transport.py +0 -0
  309. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/proxy.py +0 -0
  310. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/py.typed +0 -0
  311. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/queues.py +0 -0
  312. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/recovery.py +0 -0
  313. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/run.py +0 -0
  314. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/stream.py +0 -0
  315. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/sync.py +0 -0
  316. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/testing.py +0 -0
  317. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/src/pi_python/transcript.py +0 -0
  318. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_completions.py +0 -0
  319. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_conformance.py +0 -0
  320. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_contract_v3.py +0 -0
  321. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_control.py +0 -0
  322. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_extended_core.py +0 -0
  323. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_function_tools.py +0 -0
  324. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_loop.py +0 -0
  325. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_mcp.py +0 -0
  326. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_messages.py +0 -0
  327. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_models.py +0 -0
  328. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_providers.py +0 -0
  329. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_recovery.py +0 -0
  330. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_recovery_conformance.py +0 -0
  331. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_recovery_helpers.py +0 -0
  332. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_release.py +0 -0
  333. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_session_changes.py +0 -0
  334. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_sync.py +0 -0
  335. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_tools.py +0 -0
  336. {pi_python_core-0.8.1 → pi_python_core-0.9.0}/tests/test_websocket_continuation.py +0 -0
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: pi-python-core
3
+ Version: 0.9.0
4
+ Summary: An embeddable asyncio agent core with opt-in Claude and OpenAI providers
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ License-File: NOTICE
8
+ Classifier: Framework :: AsyncIO
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: httpx<1,>=0.27
22
+ Requires-Dist: jsonschema<5,>=4.18
23
+ Requires-Dist: websockets>=14.2
24
+ Provides-Extra: mcp
25
+ Requires-Dist: mcp>=1.10; extra == 'mcp'
26
+ Provides-Extra: oauth
27
+ Requires-Dist: pyjwt[crypto]<3,>=2.8; extra == 'oauth'
28
+ Provides-Extra: providers
29
+ Requires-Dist: pyjwt[crypto]<3,>=2.8; extra == 'providers'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # pi-python-core
33
+
34
+ **English** | [中文](README.zh-CN.md)
35
+
36
+ An embeddable Python agent core, ported from [Pi](https://github.com/earendil-works/pi) (pinned to `v1.0.0`). It does one job: send the conversation to a model, run the tools the model asks for, hand the results back, and repeat until the model answers. Tools are plain Python functions; the model can be Claude, GPT, DeepSeek, or an open model running on your own machine or cluster.
37
+
38
+ `pi-agent-core` on PyPI is a separate project that ports an older version from pi-mono (early 2026). This library follows the behavior of Pi v1.0.0, compared case by case with the upstream code's actual output, and ships its own connectors for Claude, OpenAI, DeepSeek and local models, with no model SDK required.
39
+
40
+ ## Install
41
+
42
+ Python 3.11–3.14 (including free-threaded 3.14t) and PyPy 3.11. No Node.js or model SDK needed.
43
+
44
+ ```bash
45
+ pip install pi-python-core # or: uv add pi-python-core
46
+ ```
47
+
48
+ You install `pi-python-core` and import `pi_python`.
49
+
50
+ | Option | What it adds |
51
+ |---|---|
52
+ | (none) | The agent core and the built-in model connectors: Claude, OpenAI, Codex, DeepSeek, and any OpenAI-compatible server (Ollama, vLLM, llama.cpp, …) |
53
+ | `[oauth]` | Verifies the identity token when you sign in with a ChatGPT account (`openai-chatgpt`); it brings in the compiled `cryptography` package. Claude subscription and Codex sign-in do not need it |
54
+ | `[providers]` | Same as `[oauth]`; keeps install commands from 0.8.1 and earlier working |
55
+ | `[mcp]` | Gives the agent the tools of MCP servers, including those a plugin declares |
56
+
57
+ Dependencies are version ranges rather than pins, so the package fits into most existing environments.
58
+
59
+ ## Five-minute start
60
+
61
+ ```python
62
+ from pi_python import Agent, tool
63
+ from pi_python.providers import AnthropicProvider
64
+
65
+ @tool
66
+ def word_count(text: str) -> int:
67
+ """Count the words in a text."""
68
+ return len(text.split())
69
+
70
+ agent = Agent(provider=AnthropicProvider(api_key="..."), model="claude-sonnet-4-5", tools=[word_count])
71
+ result = agent.prompt_sync("How many words are in 'to be or not to be'?")
72
+ print(result.messages[-1].content[0].text)
73
+ ```
74
+
75
+ `@tool` builds a tool from the function's signature and docstring; both plain and `async` functions work. In async code, use `await agent.prompt(...)`. Switching to a local model changes two lines:
76
+
77
+ ```python
78
+ from pi_python.providers import OpenAICompletionsProvider
79
+
80
+ llm = OpenAICompletionsProvider(base_url="http://localhost:11434/v1", name="ollama")
81
+ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), tools=[word_count])
82
+ ```
83
+
84
+ To try it offline first: `python examples/quickstart.py`.
85
+
86
+ ## What it does
87
+
88
+ | Need | How | Example |
89
+ |---|---|---|
90
+ | Write tools | Decorate a plain function with `@tool`; arguments such as pydantic models, dataclasses, enums and dates are converted automatically; hand-written or MCP-generated JSON Schema works too | [quickstart](examples/quickstart.py) |
91
+ | Connect a model | Claude and GPT through an API key or subscription sign-in; DeepSeek; any OpenAI-compatible server | [local_model](examples/local_model.py), [provider_chat](examples/provider_chat.py) |
92
+ | Use MCP tools | `async with connect_stdio(...) as tools`, or `connect_http(url)` for a remote server | [mcp_tools](examples/mcp_tools.py) |
93
+ | 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) |
94
+ | 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) |
95
+ | Intervene mid-run | `steer` injects guidance, `follow_up` queues the next task, `abort` cancels at any time (callable from any thread) | |
96
+ | Context full, service errors | `is_context_overflow` and `is_retryable_error` tell you why, `continue_run()` retries, `transform_context` compacts | [recovery](examples/recovery.py) |
97
+ | Save and restore conversations | `encode_messages` / `decode_messages`; your application decides where to store them | [save_restore](examples/save_restore.py) |
98
+ | Observe and audit | Subscribe to events; hooks before and after tool execution can block or rewrite tool calls | |
99
+
100
+ Apart from `provider_chat`, which needs real credentials, every example runs offline without an API key, and the tests run each one.
101
+
102
+ ## Relationship to Pi
103
+
104
+ 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.
105
+
106
+ 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.
107
+
108
+ ## Documentation
109
+
110
+ - [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
111
+ - [Public API](docs/API.md)
112
+ - [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
113
+ - [Plugins: skills, prompt templates, subagents and MCP servers](docs/PLUGINS.md)
114
+ - [Implementation and verification results](docs/IMPLEMENTATION.md) (Chinese)
115
+ - [Item-by-item comparison with Pi, and deliberate differences](compat/COVERAGE.md) (Chinese)
116
+ - [Rebuilding the reference and checking release candidates](reference/README.md) (Chinese)
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ uv sync --locked --extra oauth --extra mcp
122
+ uv run pytest -q
123
+ uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
124
+ ```
125
+
126
+ CI is defined in `.github/workflows/ci.yml` and runs on GitHub Actions for every push: each Python version on Linux (including 3.14t and PyPy), plus macOS and Windows.
@@ -0,0 +1,95 @@
1
+ # pi-python-core
2
+
3
+ **English** | [中文](README.zh-CN.md)
4
+
5
+ An embeddable Python agent core, ported from [Pi](https://github.com/earendil-works/pi) (pinned to `v1.0.0`). It does one job: send the conversation to a model, run the tools the model asks for, hand the results back, and repeat until the model answers. Tools are plain Python functions; the model can be Claude, GPT, DeepSeek, or an open model running on your own machine or cluster.
6
+
7
+ `pi-agent-core` on PyPI is a separate project that ports an older version from pi-mono (early 2026). This library follows the behavior of Pi v1.0.0, compared case by case with the upstream code's actual output, and ships its own connectors for Claude, OpenAI, DeepSeek and local models, with no model SDK required.
8
+
9
+ ## Install
10
+
11
+ Python 3.11–3.14 (including free-threaded 3.14t) and PyPy 3.11. No Node.js or model SDK needed.
12
+
13
+ ```bash
14
+ pip install pi-python-core # or: uv add pi-python-core
15
+ ```
16
+
17
+ You install `pi-python-core` and import `pi_python`.
18
+
19
+ | Option | What it adds |
20
+ |---|---|
21
+ | (none) | The agent core and the built-in model connectors: Claude, OpenAI, Codex, DeepSeek, and any OpenAI-compatible server (Ollama, vLLM, llama.cpp, …) |
22
+ | `[oauth]` | Verifies the identity token when you sign in with a ChatGPT account (`openai-chatgpt`); it brings in the compiled `cryptography` package. Claude subscription and Codex sign-in do not need it |
23
+ | `[providers]` | Same as `[oauth]`; keeps install commands from 0.8.1 and earlier working |
24
+ | `[mcp]` | Gives the agent the tools of MCP servers, including those a plugin declares |
25
+
26
+ Dependencies are version ranges rather than pins, so the package fits into most existing environments.
27
+
28
+ ## Five-minute start
29
+
30
+ ```python
31
+ from pi_python import Agent, tool
32
+ from pi_python.providers import AnthropicProvider
33
+
34
+ @tool
35
+ def word_count(text: str) -> int:
36
+ """Count the words in a text."""
37
+ return len(text.split())
38
+
39
+ agent = Agent(provider=AnthropicProvider(api_key="..."), model="claude-sonnet-4-5", tools=[word_count])
40
+ result = agent.prompt_sync("How many words are in 'to be or not to be'?")
41
+ print(result.messages[-1].content[0].text)
42
+ ```
43
+
44
+ `@tool` builds a tool from the function's signature and docstring; both plain and `async` functions work. In async code, use `await agent.prompt(...)`. Switching to a local model changes two lines:
45
+
46
+ ```python
47
+ from pi_python.providers import OpenAICompletionsProvider
48
+
49
+ llm = OpenAICompletionsProvider(base_url="http://localhost:11434/v1", name="ollama")
50
+ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), tools=[word_count])
51
+ ```
52
+
53
+ To try it offline first: `python examples/quickstart.py`.
54
+
55
+ ## What it does
56
+
57
+ | Need | How | Example |
58
+ |---|---|---|
59
+ | Write tools | Decorate a plain function with `@tool`; arguments such as pydantic models, dataclasses, enums and dates are converted automatically; hand-written or MCP-generated JSON Schema works too | [quickstart](examples/quickstart.py) |
60
+ | Connect a model | Claude and GPT through an API key or subscription sign-in; DeepSeek; any OpenAI-compatible server | [local_model](examples/local_model.py), [provider_chat](examples/provider_chat.py) |
61
+ | Use MCP tools | `async with connect_stdio(...) as tools`, or `connect_http(url)` for a remote server | [mcp_tools](examples/mcp_tools.py) |
62
+ | 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) |
63
+ | 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) |
64
+ | Intervene mid-run | `steer` injects guidance, `follow_up` queues the next task, `abort` cancels at any time (callable from any thread) | |
65
+ | Context full, service errors | `is_context_overflow` and `is_retryable_error` tell you why, `continue_run()` retries, `transform_context` compacts | [recovery](examples/recovery.py) |
66
+ | Save and restore conversations | `encode_messages` / `decode_messages`; your application decides where to store them | [save_restore](examples/save_restore.py) |
67
+ | Observe and audit | Subscribe to events; hooks before and after tool execution can block or rewrite tool calls | |
68
+
69
+ Apart from `provider_chat`, which needs real credentials, every example runs offline without an API key, and the tests run each one.
70
+
71
+ ## Relationship to Pi
72
+
73
+ 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.
74
+
75
+ 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.
76
+
77
+ ## Documentation
78
+
79
+ - [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
80
+ - [Public API](docs/API.md)
81
+ - [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
82
+ - [Plugins: skills, prompt templates, subagents and MCP servers](docs/PLUGINS.md)
83
+ - [Implementation and verification results](docs/IMPLEMENTATION.md) (Chinese)
84
+ - [Item-by-item comparison with Pi, and deliberate differences](compat/COVERAGE.md) (Chinese)
85
+ - [Rebuilding the reference and checking release candidates](reference/README.md) (Chinese)
86
+
87
+ ## Development
88
+
89
+ ```bash
90
+ uv sync --locked --extra oauth --extra mcp
91
+ uv run pytest -q
92
+ uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
93
+ ```
94
+
95
+ CI is defined in `.github/workflows/ci.yml` and runs on GitHub Actions for every push: each Python version on Linux (including 3.14t and PyPy), plus macOS and Windows.
@@ -1,5 +1,7 @@
1
1
  # pi-python-core
2
2
 
3
+ [English](README.md) | **中文**
4
+
3
5
  一个可嵌入的 Python agent 核心,移植自 [Pi](https://github.com/earendil-works/pi)(固定参照 `v1.0.0`)。它负责一件事:把对话发给模型,执行模型要求的工具,把结果交回模型,直到得到回答。工具就是普通的 Python 函数;模型可以是 Claude、GPT、DeepSeek,也可以是本机或集群上的开源模型。
4
6
 
5
7
  PyPI 上的 `pi-agent-core` 是另一个独立项目,移植的是 2026 年初 pi-mono 中的旧版本。本库对照 Pi v1.0.0 的行为,与上游的实际运行结果逐组比较;并自带 Claude、OpenAI、DeepSeek 和本地模型的接入,不需要安装任何模型 SDK。
@@ -9,16 +11,17 @@ PyPI 上的 `pi-agent-core` 是另一个独立项目,移植的是 2026 年初
9
11
  支持 Python 3.11–3.14(包括无 GIL 的 3.14t)和 PyPy 3.11,不需要 Node 或任何模型 SDK。
10
12
 
11
13
  ```bash
12
- pip install 'pi-python-core[providers]' # 或 uv add 'pi-python-core[providers]'
14
+ pip install pi-python-core # 或 uv add pi-python-core
13
15
  ```
14
16
 
15
17
  安装名是 `pi-python-core`,导入名是 `pi_python`。
16
18
 
17
19
  | 安装选项 | 带来什么 |
18
20
  |---|---|
19
- | 不加选项 | 执行核心,只依赖 `jsonschema` |
20
- | `[providers]` | 真实模型接入:Claude、OpenAI、Codex、DeepSeek,以及任何 OpenAI 兼容服务(Ollama、vLLM、llama.cpp 等) |
21
- | `[mcp]` | 把 MCP 服务器的工具交给 agent |
21
+ | 不加选项 | 执行核心和内置模型接入:Claude、OpenAI、Codex、DeepSeek,以及任何 OpenAI 兼容服务(Ollama、vLLM、llama.cpp 等) |
22
+ | `[oauth]` | 用 ChatGPT 账号登录(`openai-chatgpt`)时校验身份令牌,会带进需要编译的 `cryptography`。Claude 订阅和 Codex 登录不需要它 |
23
+ | `[providers]` | 与 `[oauth]` 相同,让 0.8.1 及以前的安装命令仍然可用 |
24
+ | `[mcp]` | 把 MCP 服务器的工具交给 agent,插件声明的 MCP 服务器也需要它 |
22
25
 
23
26
  依赖写的是版本范围而不是固定版本,能和大多数已有环境共存。
24
27
 
@@ -55,8 +58,9 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
55
58
  |---|---|---|
56
59
  | 写工具 | `@tool` 装饰普通函数;pydantic 模型、dataclass、枚举、日期等参数自动转换;也接受手写或 MCP 生成的 JSON Schema | [quickstart](examples/quickstart.py) |
57
60
  | 接模型 | Claude 与 GPT 的 API key 或订阅登录;DeepSeek;任何 OpenAI 兼容服务 | [local_model](examples/local_model.py),[provider_chat](examples/provider_chat.py) |
58
- | 用 MCP 工具 | `async with connect_stdio(...) as tools` | [mcp_tools](examples/mcp_tools.py) |
59
- | 让一个 agent 调用另一个 | 把子 agent 包成工具,取消会一路传下去 | [subagent](examples/subagent.py) |
61
+ | 用 MCP 工具 | `async with connect_stdio(...) as tools`;远程服务器用 `connect_http(url)` | [mcp_tools](examples/mcp_tools.py) |
62
+ | 让一个 agent 调用另一个 | 把子 agent 包成工具,取消会一路传下去;插件也可以用 Markdown 定义子 agent | [subagent](examples/subagent.py) |
63
+ | 打包和分享能力 | 插件把工具、说明、钩子、技能、提示模板、子 agent 和 MCP 服务器打成一组;用 `load_plugins` 按名字或路径加载,再用 `plugins.agent(...)` 构造 agent | [plugin_demo](examples/plugin_demo.py) |
60
64
  | 中途干预 | `steer` 插入指导,`follow_up` 排队后续任务,`abort` 随时取消(任何线程都可以调用) | |
61
65
  | 上下文满了、服务出错 | `is_context_overflow`、`is_retryable_error` 判断原因,`continue_run()` 重试,`transform_context` 压缩 | [recovery](examples/recovery.py) |
62
66
  | 保存与恢复对话 | `encode_messages` / `decode_messages`,应用决定存在哪里 | [save_restore](examples/save_restore.py) |
@@ -66,15 +70,16 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
66
70
 
67
71
  ## 与 Pi 的关系
68
72
 
69
- 执行循环、事件顺序、钩子、队列、出错与取消的处理都与 Pi 一致,并用差分验证:同一组输入分别交给固定版本的上游代码和本库运行,逐项比较模型请求、工具调用、事件和最终记录。目前核心循环 25 组、模型接入 50 组、WebSocket 多轮 2 组、出错判断 44 条样例全部一致。
73
+ 执行循环、事件顺序、钩子、队列、出错与取消的处理都与 Pi 一致,并用差分验证:同一组输入分别交给固定版本的上游代码和本库运行,逐项比较模型请求、工具调用、事件和最终记录。目前核心循环 25 组、模型接入 50 组、WebSocket 多轮 2 组、出错判断 44 条样例全部一致。插件里技能、提示模板和文件头元数据的规则,也用同样的方法和 Pi 的 coding-agent 代码对照。
70
74
 
71
- 有几处是有意的差异,例如工具参数严格校验、不自动转换类型,返回给调用者的状态是副本;另一些是为 Python 用户加的,例如 `@tool`、同步调用、失败后直接 `continue_run()`。逐项记录见[验收映射](compat/COVERAGE.md)。Pi 放在应用层的功能(终端界面、会话文件格式、上下文压缩)不在本库核心里;压缩可以用钩子实现,示例里有完整做法。本项目使用自己的版本号,并非 Pi 官方发行版。
75
+ 有几处是有意的差异,例如工具参数严格校验、不自动转换类型,返回给调用者的状态是副本;另一些是为 Python 用户加的,例如 `@tool`、同步调用、失败后直接 `continue_run()`。逐项记录见[验收映射](compat/COVERAGE.md)。Pi 放在应用层的功能(终端界面、会话文件格式、上下文压缩)不在本库核心里;压缩可以用钩子实现,示例里有完整做法。插件是从应用层取来的唯一一块:一个可选模块,读取 Pi 的 package 格式,再据此构造普通的 Agent。本项目使用自己的版本号,并非 Pi 官方发行版。
72
76
 
73
77
  ## 文档
74
78
 
75
- - [一页看懂:五个概念和一轮的流程](docs/CONCEPTS.md)
76
- - [公开 API](docs/API.md)
77
- - [模型接入、订阅登录与本地模型](docs/PROVIDERS.md)
79
+ - [一页看懂:五个概念和一轮的流程](docs/zh/CONCEPTS.md)
80
+ - [公开 API](docs/zh/API.md)
81
+ - [模型接入、订阅登录与本地模型](docs/zh/PROVIDERS.md)
82
+ - [插件:技能、提示模板、子 agent 和 MCP 服务器](docs/zh/PLUGINS.md)
78
83
  - [实施与验证结果](docs/IMPLEMENTATION.md)
79
84
  - [与 Pi 的逐项对照和有意差异](compat/COVERAGE.md)
80
85
  - [参照重建与候选版本验证](reference/README.md)
@@ -82,7 +87,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
82
87
  ## 开发
83
88
 
84
89
  ```bash
85
- uv sync --locked --extra providers --extra mcp
90
+ uv sync --locked --extra oauth --extra mcp
86
91
  uv run pytest -q
87
92
  uv run python scripts/verify.py # 全部检查,含与上游的差分(需要 Node)
88
93
  ```
@@ -36,7 +36,7 @@
36
36
 
37
37
  D1–D8 保持原设计的含义。D1 严格输入;D2 执行前钩子不能通过原地修改参数改变调用;D3 资源预算;D4 Python 取消及未知结果;D5 状态复制;D6 输出检查;D7 诊断、原始结果和完整流校验;D8 文本与工具子集。每项有独立 Python 断言,不以 skip 代替验收。
38
38
 
39
- 范围外项目:真实模型 SDK、图片/音频/专有推理、OAuth、MCP 客户端、TUI/CLI、持久化与崩溃恢复、子 agent 管理器、Slurm/PBS,以及真实 HPC/科研任务效果。模型侧不支持的内容显式拒绝。上游真实模型 e2e、代理和其他应用测试未运行;它们不计入通过数。
39
+ 范围外项目:真实模型 SDK、图片/音频/专有推理、OAuth、MCP 客户端、TUI/CLI、持久化与崩溃恢复、子 agent 管理器、Slurm/PBS,以及真实 HPC/科研任务效果。(后来的版本加入了其中几项的可选模块,核心仍不包含它们:0.7 起有 MCP 适配,0.9 起插件模块提供 Pi 示例扩展里的 `subagent` 工具,见下面各版本的小节。)模型侧不支持的内容显式拒绝。上游真实模型 e2e、代理和其他应用测试未运行;它们不计入通过数。
40
40
 
41
41
  ## 已知验收边界
42
42
 
@@ -48,7 +48,7 @@ D1–D8 保持原设计的含义。D1 严格输入;D2 执行前钩子不能通
48
48
 
49
49
  新增 Python 合同见 `test_extended_core.py`、`test_providers.py`、`test_oauth.py`。8 个共享网络流输入在 `provider-fixtures/`,实际上游解析器输出和 Python 输出保存在 [provider-conformance.json](results/provider-conformance.json)。它们对比最终内容、签名和停止原因,不等价于所有供应商请求选项或真实账号端到端兼容。
50
50
 
51
- 当前边界与明确差异见 [PROVIDERS.md](../docs/PROVIDERS.md)。0.1 的 accepted baseline 保留为历史核心验收记录,新增能力单独记录,不修改固定上游提交。
51
+ 当前边界与明确差异见 [PROVIDERS.md](../docs/zh/PROVIDERS.md)。0.1 的 accepted baseline 保留为历史核心验收记录,新增能力单独记录,不修改固定上游提交。
52
52
 
53
53
  ## 0.3 范围修订
54
54
 
@@ -93,7 +93,7 @@ D1–D8 不变。本轮明确记录的差异:WebSocket 帧不带 `stream` 字
93
93
  这一轮的目标是好装、多版本可用、容易在上面搭完整的 agent。按与上游的关系分三类记录。
94
94
 
95
95
  与上游一致,并有对照证据:
96
- - **Chat Completions 接入**:逐项移植 `openai-completions.ts`,新增 16 组 Provider 差分(Provider 差分共 50 组),覆盖 Ollama、vLLM、llama.cpp、OpenRouter、DeepSeek 等兼容配置和全部 11 种推理参数格式。与上游的差异见 [PROVIDERS](../docs/PROVIDERS.md#本地模型与-openai-兼容服务)。
96
+ - **Chat Completions 接入**:逐项移植 `openai-completions.ts`,新增 16 组 Provider 差分(Provider 差分共 50 组),覆盖 Ollama、vLLM、llama.cpp、OpenRouter、DeepSeek 等兼容配置和全部 11 种推理参数格式。与上游的差异见 [PROVIDERS](../docs/zh/PROVIDERS.md#本地模型与-openai-兼容服务)。
97
97
  - **出错判断**:`is_context_overflow`、`is_retryable_error`、`is_recoverable_length` 移植自 `overflow.ts` 和 `retry.ts`,44 条共享样例与上游函数的实际输出一致(`scripts/recovery_conformance.py`)。另外认识本库自己的错误格式:Python 网络错误名,以及按 HTTP 状态码判断本库的 HTTP 错误;上游格式的文字仍按上游规则判断。
98
98
  - **工具 schema**:不再只接受一个子集。与上游一样接受 draft-07 等标准 schema,并按 `format`、`pattern` 校验参数(已用上游校验器对同一 schema 实测);仍只允许指向 schema 内部的引用。D1 的"不转换类型"不变。
99
99
  - **HTTP 错误正文**:与上游一样保留,最多 4000 字符;本库另外把请求所带的凭据替换为 `[redacted]`。
@@ -117,3 +117,31 @@ CI 第一次在 GitHub 上运行,Linux 和 macOS 全部通过,另外发现
117
117
 
118
118
  - **Windows 读错文本文件。** Windows 默认按 cp1252 读文本,含中文的测试数据被读坏。库、compat、测试和示例里的文本读写现在都明确使用 UTF-8;库读取内置模型表时也一样(该文件目前全是 ASCII,以前没有出错)。凭据文件 `0o600` 权限的检查只在 POSIX 上进行,Windows 没有这种权限位。
119
119
  - **PyPy 在极深的 schema 上崩溃。** 以前靠 `RecursionError` 拒绝嵌套过深的工具 schema。CPython 上实际上限随 schema 写法变化(`anyOf` 嵌套 81 层,`items` 嵌套 122 层),PyPy 的 JIT 偶尔在报错之前就撑爆底层栈,进程段错误退出。现在注册时先用非递归方式计数,对象和数组嵌套超过 100 层就报 `ConfigurationError`,在所有解释器上相同。上游 Pi 没有这项限制,属于本库新增的差异。代价是:嵌套超过 100 层、以前在 CPython 上能通过的 schema 现在被拒绝。以前子 schema 的嵌套最多能到 197 层,`default`、`const` 等数据值里的嵌套能到约 1000 层。
120
+
121
+ ## 0.9 新增:插件
122
+
123
+ 0.9 加了可选模块 `pi_python.plugins`。它读取 Pi coding-agent 的 package 格式(技能、提示模板、子 agent 定义、`mcp.json` 和扩展代码),再据此构造普通的 Agent。执行循环没有改动。核心只改了一处:把后置钩子应用部分更新(`ToolResultUpdate`)的代码提取成函数 `apply_result_update`,供插件模块复用,行为不变。按与上游的关系分三类记录。
124
+
125
+ 与上游一致,并有对照证据(命令 `scripts/plugin_conformance.py`,运行器 `reference/plugin-runner.ts`,输入 `compat/plugin-cases.json` 和 `compat/plugin-fixtures/`,测试 `tests/test_plugin_conformance.py`):
126
+ - **提示模板**:`substituteArgs` 38 条、`parseCommandArgs` 11 条、`expandPromptTemplate` 10 条,与上游函数的实际输出一致;读取 `prompts/` 目录得到的名字、描述、参数提示和正文与 `loadPromptTemplates` 一致。
127
+ - **技能**:用一个 13 项的技能目录对照 `loadSkillsFromDir`,覆盖合法技能、名字不合规、缺少描述、禁止模型调用、深层嵌套、技能目录下不再查找、隐藏目录、顶层松散的 `.md`、YAML 写错和描述过长。加载的技能和警告一致;YAML 语法错误的警告文字不同,只比较出现在哪个文件。`formatSkillsForPrompt` 输出的 `<available_skills>` 部分逐字一致,开头的说明改为使用 `read_skill` 工具。`/skill:name` 的展开格式照 `agent-session.ts` 的 `_expandSkillCommand` 移植;它是私有方法,没有做差分。
128
+ - **frontmatter**:10 条共享样例与 Pi 的 `parseFrontmatter` 一致。另外用 Pi 依赖的 `yaml` 库,对本机 2056 个真实的技能、子 agent 和命令文件做了一次比对(不在 CI 里):两边都能解析的 2034 个结果完全相同;13 个未加引号、含 `: ` 的描述,YAML 报错而本库接受;4 个含 `{{TITLE}}` 的页面模板本库拒绝;5 个两边都拒绝。
129
+ - **多个处理函数怎样合并**:照 `runner.ts` 的 `emitToolCall`、`emitToolResult`、`emitContext`、`emitBeforeProviderRequest` 等移植。只有 Python 测试,没有共享差分:这些规则在 coding-agent 的扩展运行器里,不在固定参照的核心测试范围内。
130
+ - **子 agent 工具**:参数、三种模式、并行上限(8 个任务,同时 4 个)、并行汇总里每个回答 50 KiB 的截断和结果文字,照示例扩展 `examples/extensions/subagent` 移植,只有 Python 测试。
131
+
132
+ 本库新增,或与上游做法不同:
133
+ - 上游由命令行程序发现和加载 package(`pi install`、`settings.json`、项目信任)。本库没有应用程序,由应用代码指定加载哪些插件,不自动发现。已安装的插件通过 entry point 组 `pi_python.plugins` 注册,对应 `pi install npm:...`。
134
+ - 插件代码是 Python 的 `setup(api)`,对应 Pi 扩展的工厂函数。`PluginAPI` 只保留与界面无关的部分(工具、系统提示、钩子、事件、MCP),没有命令、快捷键、渲染器等终端界面接口;另加了 `service`(应用提供的对象)、`options`(按插件分开的设置)、`add_check`(自检)和 `on_close`。
135
+ - 插件处理函数出错时交给 `on_error`,默认写日志;上游交给 `emitError`,显示在界面上。`before_tool_call` 与上游一样,出错时让这次调用失败。应用自己的钩子保持核心行为,不做隔离。
136
+ - 模型用 `read_skill` 工具读取技能(上游让模型用通用的 `read` 工具)。它只能读技能目录里的文件,单个文件不超过 256 KiB。
137
+ - 子 agent 在同一进程、同一事件循环里运行(上游为每个子 agent 启动一个 pi 子进程),使用主 agent 的 Provider 和合并后的全部钩子。上游的子进程会加载同样的扩展,所以插件的处理函数同样作用于子 agent,这一点与上游一致。不指定模型时,子 agent 用调用那一刻主 agent 的模型(上游从设置里取默认模型)。没有上游的 `agentScope`、`cwd` 参数和项目级 agent 的确认步骤。
138
+ - MCP:工具名为 `mcp__<服务器>__<工具>`,但工具名里的 `-` 保留(上游换成 `_`)。字符串里只展开 `${PLUGIN_ROOT}`、`${PYTHON}` 和环境变量,前两个是本库新增;不支持 `!命令`。不支持 OAuth、`exposure` / `toolExposure` 和单次请求超时,这些设置会被忽略并给出警告。新增 `pi_python.mcp.connect_http`,连接 streamable HTTP 服务器;在 MCP SDK 1.10、1.30 和 2.3 上测过。
139
+ - 技能查找不读 `.gitignore`、`.ignore`、`.fdignore`;同一目录里的条目按名字排序(上游按文件系统返回的顺序);经符号链接回到已经找过的目录时不再进入。
140
+ - 发布前的安全审查加了几处上游没有的保护:`read_skill` 在访问文件系统之前,先按文字拒绝绝对路径、盘符和网络路径(Windows 上解析 `//主机/共享` 时就会连接该主机),`SKILL.md` 也受 256 KiB 上限约束;MCP 的 header 和 URL 不得含控制字符,取自环境变量的值在警告里显示为 `***`;`connect_http` 只在同一源内跟随重定向(MCP SDK 1.10 会跟随到任何源,并把自定义 header 一起带过去);frontmatter 超过 64 KiB 或嵌套超过 64 层时不予读取;子 agent 运行带着主 agent 的 `RunLimits`。接力步数仍和上游一样不设上限。
141
+ - frontmatter 由内置的小解析器读取,不依赖 YAML 库(比对结果见上)。
142
+
143
+ 已知差异与限制:
144
+ - 没有上游的项目信任机制。插件文档写明只应加载可信的插件;因为不会自动发现插件,只有应用代码点名的插件才会运行。
145
+ - 子 agent 定义里 Claude Code 格式的其他字段(如 `color`)会被忽略。
146
+
147
+ 验证结果见 [实施与验证结果](../docs/IMPLEMENTATION.md)。