pi-python-core 0.9.0__tar.gz → 0.10.1__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 (364) hide show
  1. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/PKG-INFO +12 -2
  2. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/README.md +8 -1
  3. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/README.zh-CN.md +8 -1
  4. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/COVERAGE.md +1 -0
  5. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/verification.json +37 -25
  6. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/API.md +87 -0
  7. pi_python_core-0.10.1/docs/CODING_AGENTS.md +176 -0
  8. pi_python_core-0.10.1/docs/MCP_INTERACTION.md +457 -0
  9. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/PLUGINS.md +168 -0
  10. pi_python_core-0.10.1/docs/PLUGIN_DEVELOPMENT.md +288 -0
  11. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/PROVIDERS.md +11 -2
  12. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/zh/API.md +66 -0
  13. pi_python_core-0.10.1/docs/zh/CODING_AGENTS.md +140 -0
  14. pi_python_core-0.10.1/docs/zh/MCP_HOST_VERIFICATION.md +130 -0
  15. pi_python_core-0.10.1/docs/zh/MCP_INTERACTION.md +396 -0
  16. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/zh/PLUGINS.md +154 -0
  17. pi_python_core-0.10.1/docs/zh/PLUGIN_DEVELOPMENT.md +245 -0
  18. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/zh/PROVIDERS.md +11 -1
  19. pi_python_core-0.10.1/examples/mcp_interactive.py +160 -0
  20. pi_python_core-0.10.1/examples/mcp_interactive_server.py +73 -0
  21. pi_python_core-0.10.1/examples/memory_demo.py +62 -0
  22. pi_python_core-0.10.1/examples/plugins/memory/plugin.py +138 -0
  23. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/subagent.py +2 -7
  24. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/pyproject.toml +3 -1
  25. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/__init__.py +7 -1
  26. pi_python_core-0.10.1/src/pi_python/_mcp_host.py +221 -0
  27. pi_python_core-0.10.1/src/pi_python/_mcp_interaction.py +1000 -0
  28. pi_python_core-0.10.1/src/pi_python/_mcp_process.py +183 -0
  29. pi_python_core-0.10.1/src/pi_python/_mcp_sampling.py +280 -0
  30. pi_python_core-0.10.1/src/pi_python/_version.py +1 -0
  31. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/agent.py +19 -0
  32. pi_python_core-0.10.1/src/pi_python/features.py +45 -0
  33. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/function_tools.py +134 -99
  34. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/loop.py +14 -11
  35. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/mcp.py +136 -14
  36. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/plugins/__init__.py +373 -31
  37. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/plugins/_compose.py +15 -0
  38. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/plugins/_subagents.py +5 -15
  39. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/anthropic.py +3 -1
  40. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/completions.py +34 -9
  41. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/oauth.py +3 -10
  42. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/openai.py +3 -2
  43. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/transport.py +228 -126
  44. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/run.py +28 -2
  45. pi_python_core-0.10.1/src/pi_python/sync.py +150 -0
  46. pi_python_core-0.10.1/src/pi_python/tasks.py +134 -0
  47. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/tools.py +58 -2
  48. pi_python_core-0.10.1/tests/servers/mcp_interactive_server.py +200 -0
  49. pi_python_core-0.10.1/tests/servers/mcp_process_server.py +86 -0
  50. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/servers/mcp_server.py +16 -0
  51. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_completions.py +74 -0
  52. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_contract_v3.py +50 -0
  53. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_examples.py +13 -1
  54. pi_python_core-0.10.1/tests/test_features.py +119 -0
  55. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_function_tools.py +161 -0
  56. pi_python_core-0.10.1/tests/test_lifecycle_contracts.py +278 -0
  57. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_mcp.py +69 -3
  58. pi_python_core-0.10.1/tests/test_mcp_delta.py +477 -0
  59. pi_python_core-0.10.1/tests/test_mcp_host.py +907 -0
  60. pi_python_core-0.10.1/tests/test_mcp_interaction.py +872 -0
  61. pi_python_core-0.10.1/tests/test_mcp_process.py +237 -0
  62. pi_python_core-0.10.1/tests/test_nested_agents.py +273 -0
  63. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_plugins.py +428 -0
  64. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_providers.py +48 -0
  65. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_sync.py +73 -0
  66. pi_python_core-0.10.1/tests/test_tasks.py +158 -0
  67. pi_python_core-0.10.1/tests/test_typeddict_annotations.py +62 -0
  68. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_websocket_continuation.py +142 -0
  69. pi_python_core-0.9.0/src/pi_python/_version.py +0 -1
  70. pi_python_core-0.9.0/src/pi_python/sync.py +0 -78
  71. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/.gitignore +0 -0
  72. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/LICENSE +0 -0
  73. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/NOTICE +0 -0
  74. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/__init__.py +0 -0
  75. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/baseline.json +0 -0
  76. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/contracts/release-candidate.example.json +0 -0
  77. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/contracts/release-candidate.schema.json +0 -0
  78. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/contracts/upstream-baseline.json +0 -0
  79. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C01-text.json +0 -0
  80. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C02-tool.json +0 -0
  81. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C03-sequential.json +0 -0
  82. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C04-parallel.json +0 -0
  83. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C05-one-sequential.json +0 -0
  84. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C06-one-failure.json +0 -0
  85. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C07-unknown.json +0 -0
  86. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C08-prepare.json +0 -0
  87. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C09-before-error.json +0 -0
  88. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C09-block.json +0 -0
  89. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C09-replace.json +0 -0
  90. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C10-length.json +0 -0
  91. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C11-transform.json +0 -0
  92. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C12-system.json +0 -0
  93. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C13-steering.json +0 -0
  94. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C14-followup-all.json +0 -0
  95. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C14-followup-one.json +0 -0
  96. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C15-prepare.json +0 -0
  97. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C16-continue.json +0 -0
  98. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C16-end.json +0 -0
  99. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C16-mixed.json +0 -0
  100. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C16-terminate.json +0 -0
  101. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C19-abort-stream.json +0 -0
  102. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C19-abort-tools.json +0 -0
  103. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/fixtures/C24-provider-error.json +0 -0
  104. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-cases.json +0 -0
  105. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/prompts/notes.txt +0 -0
  106. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/prompts/plan.md +0 -0
  107. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/prompts/review.md +0 -0
  108. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/prompts/sub/nested.md +0 -0
  109. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/prompts/summarize.md +0 -0
  110. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/.hidden/SKILL.md +0 -0
  111. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/alpha/SKILL.md +0 -0
  112. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/alpha/references/notes.md +0 -0
  113. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/beta/SKILL.md +0 -0
  114. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/broken/SKILL.md +0 -0
  115. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/delta/SKILL.md +0 -0
  116. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/gamma/SKILL.md +0 -0
  117. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/longdesc/SKILL.md +0 -0
  118. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/loose.md +0 -0
  119. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/nested/group/epsilon/SKILL.md +0 -0
  120. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/noname/SKILL.md +0 -0
  121. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/plain.md +0 -0
  122. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/zeta/SKILL.md +0 -0
  123. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/plugin-fixtures/skills/zeta/sub/eta/SKILL.md +0 -0
  124. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-oauth.json +0 -0
  125. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-request-cache-long.json +0 -0
  126. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-request-cache-none.json +0 -0
  127. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-request-replay.json +0 -0
  128. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-request-tools-image.json +0 -0
  129. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-text.json +0 -0
  130. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-thinking.json +0 -0
  131. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/anthropic-tool.json +0 -0
  132. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/codex-request-tools-image.json +0 -0
  133. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/codex-text.json +0 -0
  134. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-compat-strict.json +0 -0
  135. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-deepseek.json +0 -0
  136. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-format-ant-ling.json +0 -0
  137. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-format-baseten.json +0 -0
  138. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-format-string-thinking.json +0 -0
  139. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-format-together.json +0 -0
  140. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-format-zai.json +0 -0
  141. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-midconvo-tools.json +0 -0
  142. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-moonshot-usage.json +0 -0
  143. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-openai-off.json +0 -0
  144. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-openrouter.json +0 -0
  145. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-qwen-length.json +0 -0
  146. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-reasoning-content.json +0 -0
  147. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-request-replay.json +0 -0
  148. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-text.json +0 -0
  149. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/completions-tool-stream.json +0 -0
  150. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-incomplete.json +0 -0
  151. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-request-cache-long.json +0 -0
  152. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-request-cache-none.json +0 -0
  153. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-request-reasoning.json +0 -0
  154. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-request-replay.json +0 -0
  155. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-request-tools-image.json +0 -0
  156. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-text.json +0 -0
  157. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-thinking-backfill.json +0 -0
  158. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-thinking.json +0 -0
  159. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/openai-tool.json +0 -0
  160. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/proxy-text.json +0 -0
  161. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/proxy-thinking-tool.json +0 -0
  162. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-anthropic-collapse-budget.json +0 -0
  163. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-anthropic-native-tools.json +0 -0
  164. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-anthropic-oauth-effort.json +0 -0
  165. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-anthropic-redefinition.json +0 -0
  166. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-anthropic-server-fallback.json +0 -0
  167. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-anthropic-thinking-off.json +0 -0
  168. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-codex-tool-search.json +0 -0
  169. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-openai-additional-tools.json +0 -0
  170. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-openai-collapse.json +0 -0
  171. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-openai-explicit-cache-long.json +0 -0
  172. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-openai-explicit-cache-none.json +0 -0
  173. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/provider-fixtures/session-openai-removal.json +0 -0
  174. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/recovery-cases.json +0 -0
  175. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/release.py +0 -0
  176. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C01-text.python.json +0 -0
  177. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C01-text.upstream.json +0 -0
  178. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C02-tool.python.json +0 -0
  179. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C02-tool.upstream.json +0 -0
  180. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C03-sequential.python.json +0 -0
  181. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C03-sequential.upstream.json +0 -0
  182. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C04-parallel.python.json +0 -0
  183. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C04-parallel.upstream.json +0 -0
  184. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C05-one-sequential.python.json +0 -0
  185. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C05-one-sequential.upstream.json +0 -0
  186. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C06-one-failure.python.json +0 -0
  187. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C06-one-failure.upstream.json +0 -0
  188. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C07-unknown.python.json +0 -0
  189. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C07-unknown.upstream.json +0 -0
  190. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C08-prepare.python.json +0 -0
  191. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C08-prepare.upstream.json +0 -0
  192. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C09-before-error.python.json +0 -0
  193. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C09-before-error.upstream.json +0 -0
  194. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C09-block.python.json +0 -0
  195. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C09-block.upstream.json +0 -0
  196. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C09-replace.python.json +0 -0
  197. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C09-replace.upstream.json +0 -0
  198. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C10-length.python.json +0 -0
  199. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C10-length.upstream.json +0 -0
  200. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C11-transform.python.json +0 -0
  201. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C11-transform.upstream.json +0 -0
  202. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C12-system.python.json +0 -0
  203. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C12-system.upstream.json +0 -0
  204. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C13-steering.python.json +0 -0
  205. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C13-steering.upstream.json +0 -0
  206. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C14-followup-all.python.json +0 -0
  207. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C14-followup-all.upstream.json +0 -0
  208. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C14-followup-one.python.json +0 -0
  209. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C14-followup-one.upstream.json +0 -0
  210. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C15-prepare.python.json +0 -0
  211. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C15-prepare.upstream.json +0 -0
  212. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-continue.python.json +0 -0
  213. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-continue.upstream.json +0 -0
  214. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-end.python.json +0 -0
  215. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-end.upstream.json +0 -0
  216. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-mixed.python.json +0 -0
  217. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-mixed.upstream.json +0 -0
  218. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-terminate.python.json +0 -0
  219. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C16-terminate.upstream.json +0 -0
  220. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C19-abort-stream.python.json +0 -0
  221. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C19-abort-stream.upstream.json +0 -0
  222. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C19-abort-tools.python.json +0 -0
  223. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C19-abort-tools.upstream.json +0 -0
  224. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C24-provider-error.python.json +0 -0
  225. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/C24-provider-error.upstream.json +0 -0
  226. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/artifacts.json +0 -0
  227. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/benchmark.json +0 -0
  228. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/bootstrap.json +0 -0
  229. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/candidate-live-noop.json +0 -0
  230. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/conformance.json +0 -0
  231. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/linux-3.11.json +0 -0
  232. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/linux-3.12.json +0 -0
  233. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/linux-3.13.json +0 -0
  234. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/linux-3.14.json +0 -0
  235. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/linux-install-hash-repair.json +0 -0
  236. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-0.5-regression.json +0 -0
  237. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-0.5-session-claude.json +0 -0
  238. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-0.5-session-codex.json +0 -0
  239. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-claude-reasoning-replay.json +0 -0
  240. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-final-matrix.json +0 -0
  241. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-initial.json +0 -0
  242. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-loop-initial.json +0 -0
  243. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-providers.json +0 -0
  244. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-reasoning-replay.json +0 -0
  245. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-refresh.json +0 -0
  246. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-regression-0.4-deepseek.json +0 -0
  247. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-regression-0.4-first-run.json +0 -0
  248. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-regression-0.4.json +0 -0
  249. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-session-claude.json +0 -0
  250. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-session-codex.json +0 -0
  251. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-websocket-cached-recheck.json +0 -0
  252. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-websocket-cached.json +0 -0
  253. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/live-websocket-final.json +0 -0
  254. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/plugins.upstream.json +0 -0
  255. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/provider-conformance.json +0 -0
  256. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/recovery-conformance.upstream.json +0 -0
  257. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/runtime-dependencies.json +0 -0
  258. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/source-verification.json +0 -0
  259. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.1.0/artifacts.json +0 -0
  260. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.1.0/linux-3.11.json +0 -0
  261. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.1.0/linux-3.12.json +0 -0
  262. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.1.0/linux-3.13.json +0 -0
  263. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.1.0/verification.json +0 -0
  264. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.2.0/artifacts.json +0 -0
  265. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.2.0/linux-3.11.json +0 -0
  266. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.2.0/linux-3.12.json +0 -0
  267. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.2.0/linux-3.13.json +0 -0
  268. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.2.0/verification.json +0 -0
  269. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/against-0.4-fixtures.json +0 -0
  270. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/artifacts.json +0 -0
  271. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/linux-3.11.json +0 -0
  272. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/linux-3.12.json +0 -0
  273. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/linux-3.13.json +0 -0
  274. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/provider-conformance.json +0 -0
  275. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.3.0/verification.json +0 -0
  276. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.4.0/artifacts.json +0 -0
  277. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.4.0/linux-3.11.json +0 -0
  278. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.4.0/linux-3.12.json +0 -0
  279. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.4.0/linux-3.13.json +0 -0
  280. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.4.0/verification.json +0 -0
  281. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.5.0/artifacts.json +0 -0
  282. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.5.0/linux-3.11.json +0 -0
  283. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.5.0/linux-3.12.json +0 -0
  284. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.5.0/linux-3.13.json +0 -0
  285. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.5.0/verification.json +0 -0
  286. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.6.0/artifacts.json +0 -0
  287. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.6.0/linux-3.11.json +0 -0
  288. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.6.0/linux-3.12.json +0 -0
  289. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.6.0/linux-3.13.json +0 -0
  290. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.6.0/verification.json +0 -0
  291. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.7.0/artifacts.json +0 -0
  292. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.7.0/linux-3.11.json +0 -0
  293. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.7.0/linux-3.12.json +0 -0
  294. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.7.0/linux-3.13.json +0 -0
  295. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.7.0/linux-3.14.json +0 -0
  296. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.7.0/verification.json +0 -0
  297. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.8.0/artifacts.json +0 -0
  298. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.8.0/linux-3.11.json +0 -0
  299. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.8.0/linux-3.12.json +0 -0
  300. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.8.0/linux-3.13.json +0 -0
  301. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.8.0/linux-3.14.json +0 -0
  302. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/v0.8.0/verification.json +0 -0
  303. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/results/websocket-conformance.json +0 -0
  304. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/runner.py +0 -0
  305. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/websocket-fixtures/codex-continuation-lost-state.json +0 -0
  306. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/compat/websocket-fixtures/codex-continuation-tool-loop.json +0 -0
  307. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/CONCEPTS.md +0 -0
  308. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/IMPLEMENTATION.md +0 -0
  309. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/history/IMPLEMENTATION-0.3.0.md +0 -0
  310. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/history/IMPLEMENTATION-0.4.0.md +0 -0
  311. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/history/IMPLEMENTATION-0.5.0.md +0 -0
  312. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/history/IMPLEMENTATION-0.6.0.md +0 -0
  313. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/history/IMPLEMENTATION-0.7.0.md +0 -0
  314. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/history/IMPLEMENTATION-0.8.0.md +0 -0
  315. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/docs/zh/CONCEPTS.md +0 -0
  316. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/in_memory.py +0 -0
  317. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/local_model.py +0 -0
  318. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/mcp_tools.py +0 -0
  319. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/plugin_demo.py +0 -0
  320. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/plugins/lab_tools/agents/reviewer.md +0 -0
  321. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/plugins/lab_tools/ops.py +0 -0
  322. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/plugins/lab_tools/plugin.py +0 -0
  323. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/plugins/lab_tools/prompts/dedup.md +0 -0
  324. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/plugins/lab_tools/skills/dedup-window/SKILL.md +0 -0
  325. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/provider_chat.py +0 -0
  326. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/quickstart.py +0 -0
  327. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/recovery.py +0 -0
  328. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/examples/save_restore.py +0 -0
  329. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/cancellation.py +0 -0
  330. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/data/models.json +0 -0
  331. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/errors.py +0 -0
  332. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/estimate.py +0 -0
  333. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/events.py +0 -0
  334. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/hooks.py +0 -0
  335. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/limits.py +0 -0
  336. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/lowlevel.py +0 -0
  337. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/messages.py +0 -0
  338. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/models.py +0 -0
  339. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/plugins/_frontmatter.py +0 -0
  340. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/plugins/_resources.py +0 -0
  341. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/provider.py +0 -0
  342. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/__init__.py +0 -0
  343. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/providers/common.py +0 -0
  344. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/proxy.py +0 -0
  345. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/py.typed +0 -0
  346. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/queues.py +0 -0
  347. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/recovery.py +0 -0
  348. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/stream.py +0 -0
  349. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/testing.py +0 -0
  350. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/src/pi_python/transcript.py +0 -0
  351. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_conformance.py +0 -0
  352. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_control.py +0 -0
  353. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_extended_core.py +0 -0
  354. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_loop.py +0 -0
  355. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_messages.py +0 -0
  356. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_models.py +0 -0
  357. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_oauth.py +0 -0
  358. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_plugin_conformance.py +0 -0
  359. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_recovery.py +0 -0
  360. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_recovery_conformance.py +0 -0
  361. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_recovery_helpers.py +0 -0
  362. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_release.py +0 -0
  363. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_session_changes.py +0 -0
  364. {pi_python_core-0.9.0 → pi_python_core-0.10.1}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pi-python-core
3
- Version: 0.9.0
3
+ Version: 0.10.1
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
@@ -23,6 +23,9 @@ Requires-Dist: jsonschema<5,>=4.18
23
23
  Requires-Dist: websockets>=14.2
24
24
  Provides-Extra: mcp
25
25
  Requires-Dist: mcp>=1.10; extra == 'mcp'
26
+ Provides-Extra: mcp-interactive
27
+ Requires-Dist: jsonschema[format-nongpl]<5,>=4.18; extra == 'mcp-interactive'
28
+ Requires-Dist: mcp<3,>=2.3; extra == 'mcp-interactive'
26
29
  Provides-Extra: oauth
27
30
  Requires-Dist: pyjwt[crypto]<3,>=2.8; extra == 'oauth'
28
31
  Provides-Extra: providers
@@ -37,6 +40,9 @@ An embeddable Python agent core, ported from [Pi](https://github.com/earendil-wo
37
40
 
38
41
  `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
42
 
43
+ **Using a coding agent?** Start with the [library integration guide](docs/CODING_AGENTS.md).
44
+ To build an extension, follow the [plugin development workflow and complete example](docs/PLUGIN_DEVELOPMENT.md).
45
+
40
46
  ## Install
41
47
 
42
48
  Python 3.11–3.14 (including free-threaded 3.14t) and PyPy 3.11. No Node.js or model SDK needed.
@@ -53,6 +59,7 @@ You install `pi-python-core` and import `pi_python`.
53
59
  | `[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
60
  | `[providers]` | Same as `[oauth]`; keeps install commands from 0.8.1 and earlier working |
55
61
  | `[mcp]` | Gives the agent the tools of MCP servers, including those a plugin declares |
62
+ | `[mcp-interactive]` | Lets MCP services request host model calls and user forms; includes sampling profiles, metering and retries. See [MCP interaction](docs/MCP_INTERACTION.md) |
56
63
 
57
64
  Dependencies are version ranges rather than pins, so the package fits into most existing environments.
58
65
 
@@ -92,6 +99,7 @@ To try it offline first: `python examples/quickstart.py`.
92
99
  | Use MCP tools | `async with connect_stdio(...) as tools`, or `connect_http(url)` for a remote server | [mcp_tools](examples/mcp_tools.py) |
93
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) |
94
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) |
95
103
  | Intervene mid-run | `steer` injects guidance, `follow_up` queues the next task, `abort` cancels at any time (callable from any thread) | |
96
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) |
97
105
  | Save and restore conversations | `encode_messages` / `decode_messages`; your application decides where to store them | [save_restore](examples/save_restore.py) |
@@ -107,6 +115,8 @@ A few differences are deliberate, such as strict tool-argument validation withou
107
115
 
108
116
  ## Documentation
109
117
 
118
+ - [Coding agents: integrate the library, run an offline example, and verify behavior](docs/CODING_AGENTS.md)
119
+ - [Plugin development: scaffold, readiness checks, packaging, and installed verification](docs/PLUGIN_DEVELOPMENT.md)
110
120
  - [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
111
121
  - [Public API](docs/API.md)
112
122
  - [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
@@ -118,7 +128,7 @@ A few differences are deliberate, such as strict tool-argument validation withou
118
128
  ## Development
119
129
 
120
130
  ```bash
121
- uv sync --locked --extra oauth --extra mcp
131
+ uv sync --locked --extra oauth --extra mcp-interactive
122
132
  uv run pytest -q
123
133
  uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
124
134
  ```
@@ -6,6 +6,9 @@ An embeddable Python agent core, ported from [Pi](https://github.com/earendil-wo
6
6
 
7
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
8
 
9
+ **Using a coding agent?** Start with the [library integration guide](docs/CODING_AGENTS.md).
10
+ To build an extension, follow the [plugin development workflow and complete example](docs/PLUGIN_DEVELOPMENT.md).
11
+
9
12
  ## Install
10
13
 
11
14
  Python 3.11–3.14 (including free-threaded 3.14t) and PyPy 3.11. No Node.js or model SDK needed.
@@ -22,6 +25,7 @@ You install `pi-python-core` and import `pi_python`.
22
25
  | `[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
26
  | `[providers]` | Same as `[oauth]`; keeps install commands from 0.8.1 and earlier working |
24
27
  | `[mcp]` | Gives the agent the tools of MCP servers, including those a plugin declares |
28
+ | `[mcp-interactive]` | Lets MCP services request host model calls and user forms; includes sampling profiles, metering and retries. See [MCP interaction](docs/MCP_INTERACTION.md) |
25
29
 
26
30
  Dependencies are version ranges rather than pins, so the package fits into most existing environments.
27
31
 
@@ -61,6 +65,7 @@ To try it offline first: `python examples/quickstart.py`.
61
65
  | Use MCP tools | `async with connect_stdio(...) as tools`, or `connect_http(url)` for a remote server | [mcp_tools](examples/mcp_tools.py) |
62
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) |
63
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) |
64
69
  | Intervene mid-run | `steer` injects guidance, `follow_up` queues the next task, `abort` cancels at any time (callable from any thread) | |
65
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) |
66
71
  | Save and restore conversations | `encode_messages` / `decode_messages`; your application decides where to store them | [save_restore](examples/save_restore.py) |
@@ -76,6 +81,8 @@ A few differences are deliberate, such as strict tool-argument validation withou
76
81
 
77
82
  ## Documentation
78
83
 
84
+ - [Coding agents: integrate the library, run an offline example, and verify behavior](docs/CODING_AGENTS.md)
85
+ - [Plugin development: scaffold, readiness checks, packaging, and installed verification](docs/PLUGIN_DEVELOPMENT.md)
79
86
  - [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
80
87
  - [Public API](docs/API.md)
81
88
  - [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
@@ -87,7 +94,7 @@ A few differences are deliberate, such as strict tool-argument validation withou
87
94
  ## Development
88
95
 
89
96
  ```bash
90
- uv sync --locked --extra oauth --extra mcp
97
+ uv sync --locked --extra oauth --extra mcp-interactive
91
98
  uv run pytest -q
92
99
  uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
93
100
  ```
@@ -6,6 +6,9 @@
6
6
 
7
7
  PyPI 上的 `pi-agent-core` 是另一个独立项目,移植的是 2026 年初 pi-mono 中的旧版本。本库对照 Pi v1.0.0 的行为,与上游的实际运行结果逐组比较;并自带 Claude、OpenAI、DeepSeek 和本地模型的接入,不需要安装任何模型 SDK。
8
8
 
9
+ **使用 coding agent 开发?** 先读 [library 接入指南](docs/zh/CODING_AGENTS.md)。
10
+ 需要扩展能力时,按 [插件开发流程与完整示例](docs/zh/PLUGIN_DEVELOPMENT.md) 实现、打包和验证。
11
+
9
12
  ## 安装
10
13
 
11
14
  支持 Python 3.11–3.14(包括无 GIL 的 3.14t)和 PyPy 3.11,不需要 Node 或任何模型 SDK。
@@ -22,6 +25,7 @@ pip install pi-python-core # 或 uv add pi-python-core
22
25
  | `[oauth]` | 用 ChatGPT 账号登录(`openai-chatgpt`)时校验身份令牌,会带进需要编译的 `cryptography`。Claude 订阅和 Codex 登录不需要它 |
23
26
  | `[providers]` | 与 `[oauth]` 相同,让 0.8.1 及以前的安装命令仍然可用 |
24
27
  | `[mcp]` | 把 MCP 服务器的工具交给 agent,插件声明的 MCP 服务器也需要它 |
28
+ | `[mcp-interactive]` | 允许 MCP 服务请求宿主模型调用及用户表单,支持配置选择、计量与重试。见 [MCP 交互](docs/zh/MCP_INTERACTION.md) |
25
29
 
26
30
  依赖写的是版本范围而不是固定版本,能和大多数已有环境共存。
27
31
 
@@ -61,6 +65,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
61
65
  | 用 MCP 工具 | `async with connect_stdio(...) as tools`;远程服务器用 `connect_http(url)` | [mcp_tools](examples/mcp_tools.py) |
62
66
  | 让一个 agent 调用另一个 | 把子 agent 包成工具,取消会一路传下去;插件也可以用 Markdown 定义子 agent | [subagent](examples/subagent.py) |
63
67
  | 打包和分享能力 | 插件把工具、说明、钩子、技能、提示模板、子 agent 和 MCP 服务器打成一组;用 `load_plugins` 按名字或路径加载,再用 `plugins.agent(...)` 构造 agent | [plugin_demo](examples/plugin_demo.py) |
68
+ | 跨对话记住信息 | 与 Pi 的核心一样不内置;示例插件把事实存进文件,再放进系统提示,可以复制后修改 | [memory_demo](examples/memory_demo.py) |
64
69
  | 中途干预 | `steer` 插入指导,`follow_up` 排队后续任务,`abort` 随时取消(任何线程都可以调用) | |
65
70
  | 上下文满了、服务出错 | `is_context_overflow`、`is_retryable_error` 判断原因,`continue_run()` 重试,`transform_context` 压缩 | [recovery](examples/recovery.py) |
66
71
  | 保存与恢复对话 | `encode_messages` / `decode_messages`,应用决定存在哪里 | [save_restore](examples/save_restore.py) |
@@ -76,6 +81,8 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
76
81
 
77
82
  ## 文档
78
83
 
84
+ - [Coding agents:接入 library、运行离线示例、验证行为](docs/zh/CODING_AGENTS.md)
85
+ - [插件开发:目录结构、就绪检查、打包与安装验证](docs/zh/PLUGIN_DEVELOPMENT.md)
79
86
  - [一页看懂:五个概念和一轮的流程](docs/zh/CONCEPTS.md)
80
87
  - [公开 API](docs/zh/API.md)
81
88
  - [模型接入、订阅登录与本地模型](docs/zh/PROVIDERS.md)
@@ -87,7 +94,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
87
94
  ## 开发
88
95
 
89
96
  ```bash
90
- uv sync --locked --extra oauth --extra mcp
97
+ uv sync --locked --extra oauth --extra mcp-interactive
91
98
  uv run pytest -q
92
99
  uv run python scripts/verify.py # 全部检查,含与上游的差分(需要 Node)
93
100
  ```
@@ -133,6 +133,7 @@ CI 第一次在 GitHub 上运行,Linux 和 macOS 全部通过,另外发现
133
133
  - 上游由命令行程序发现和加载 package(`pi install`、`settings.json`、项目信任)。本库没有应用程序,由应用代码指定加载哪些插件,不自动发现。已安装的插件通过 entry point 组 `pi_python.plugins` 注册,对应 `pi install npm:...`。
134
134
  - 插件代码是 Python 的 `setup(api)`,对应 Pi 扩展的工厂函数。`PluginAPI` 只保留与界面无关的部分(工具、系统提示、钩子、事件、MCP),没有命令、快捷键、渲染器等终端界面接口;另加了 `service`(应用提供的对象)、`options`(按插件分开的设置)、`add_check`(自检)和 `on_close`。
135
135
  - 插件处理函数出错时交给 `on_error`,默认写日志;上游交给 `emitError`,显示在界面上。`before_tool_call` 与上游一样,出错时让这次调用失败。应用自己的钩子保持核心行为,不做隔离。
136
+ - 插件注册处理函数时检查它能否接收该钩子传入的位置参数(`subscribe` 为事件一个参数,`add_check` 和 `on_close` 没有参数),不能就报 `ConfigurationError`,加载停止(0.10.1 加入)。上游没有这一步:TypeScript 在编译时检查类型,JavaScript 调用时多出的参数会被忽略。Python 会在每次调用时抛出 `TypeError`,插件处理函数的错误又只写日志,不检查的话这样的处理函数会悄悄失效。应用自己的钩子不做这项检查。
136
137
  - 模型用 `read_skill` 工具读取技能(上游让模型用通用的 `read` 工具)。它只能读技能目录里的文件,单个文件不超过 256 KiB。
137
138
  - 子 agent 在同一进程、同一事件循环里运行(上游为每个子 agent 启动一个 pi 子进程),使用主 agent 的 Provider 和合并后的全部钩子。上游的子进程会加载同样的扩展,所以插件的处理函数同样作用于子 agent,这一点与上游一致。不指定模型时,子 agent 用调用那一刻主 agent 的模型(上游从设置里取默认模型)。没有上游的 `agentScope`、`cwd` 参数和项目级 agent 的确认步骤。
138
139
  - 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"
@@ -48,6 +48,8 @@ Every event passes the same check: blocks must pair up, deltas must land in an o
48
48
 
49
49
  Message data also allows `pending` and `deferred`. `pending` is only for in-stream snapshots and cannot be committed to history; `deferred` can hold a background handle, but the library does not yet poll background tasks. For `length`, every tool call gets a not-executed result, and the model may then handle the error. `error` / `aborted` messages cannot declare tool calls: before the message is committed, its tool calls are removed, the rest of the partial content is kept, and a `removed_tool_calls` entry is added to `diagnostics`.
50
50
 
51
+ The Agent closes each request's response iterator when the request ends, including error and cancellation paths. Custom iterators that own resources should implement `aclose()`. This does not close the shared Provider.
52
+
51
53
  ## Tool
52
54
 
53
55
  `Tool(name, description, input_schema, execute, output_schema=None, execution_mode="parallel", prepare_arguments=None)`.
@@ -81,8 +83,34 @@ def search_papers(query: str, year: int | None = None, limit: int = 10) -> list[
81
83
 
82
84
  Supported parameter types: `str`, `int`, `float`, `bool`, `None`, `list`, `set`, `tuple`, `dict[str, T]`, `Literal`, `Enum`, `Optional` and other unions, `Annotated[T, "description"]`, `TypedDict`, dataclasses, `datetime`, `date`, `UUID`, `Path`, and pydantic models. Before the call, the JSON arguments are converted to the types the function asks for, such as enum members, dates, dataclasses or pydantic models. A parameter annotated as `ToolContext` receives the call context and does not appear in the schema. `*args`, `**kwargs` and types that cannot be represented as JSON raise `ConfigurationError` at registration.
83
85
 
86
+ For a union, conversion tries branches in annotation order, using only branches whose JSON Schema matches the input. For example, `list[int] | str` preserves `"abc"` as a string. When several branches match, the first successful conversion wins.
87
+
88
+ Schema and conversion are compiled together at registration, including recursive types and union branches. Bare `set`, `frozenset` and `tuple` annotations also convert JSON arrays to the requested Python container. Nested pydantic definitions are isolated between parameters and union branches, even when their class names match.
89
+
90
+ `TypedDict` requiredness follows resolved `Required` and `NotRequired` annotations,
91
+ including inherited fields and `Annotated` wrappers when annotations are postponed.
92
+
84
93
  Concurrency is unlimited by default: a batch of tools all run at once, as in Pi; set `RunLimits(max_concurrency=...)` when you need a cap. If any tool requires `sequential`, the whole batch runs one at a time. When running concurrently, preparation completes in call order before execution starts; end events follow the order in which post-processing finishes, and results in history follow call order.
85
94
 
95
+ Parallel tool and subagent batches own their child tasks: failure or cancellation cancels unfinished siblings and waits for their cleanup. Repeated caller cancellation does not interrupt that cleanup. The Run's existing cleanup deadline still applies; `cleanup_complete` reports whether cleanup actually finished.
96
+
97
+ Inside a tool, use `await context.run_agent(child, message)` to run an exclusively
98
+ owned, idle child Agent. It returns the child's `RunResult` and closes that Agent;
99
+ the shared Provider stays open. See [the subagent example](../examples/subagent.py).
100
+ An already running child is rejected without cancelling or closing it. Ordinary
101
+ model/tool failures remain results that the calling tool can handle.
102
+
103
+ Nested calls preserve two separate facts: whether an external operation's outcome
104
+ is known, and whether its work has stopped. An unknown child outcome raises
105
+ `ToolOutcomeUnknownError`, marks the enclosing Agent as requiring reconciliation,
106
+ and stops further execution. This also applies across parallel siblings and multiple
107
+ nesting levels. An unfinished child keeps its enclosing tool pending. The parent's
108
+ cleanup deadline still bounds its run, which reports `cleanup_complete=False` until
109
+ the descendant actually finishes. Repeated cancellation cannot abandon that cleanup.
110
+ Standalone `ToolContext` callers must keep awaiting the call or manage its lifetime
111
+ with `TaskScope`; without an enclosing Agent there is no Run cleanup deadline.
112
+ Check `require_features(["nested-agent-ownership-v1"])` when depending on this API.
113
+
86
114
  A standalone program can call `await run_tool_call(tool, call, context, before_tool_call=..., after_tool_call=...)` to reuse the same validation and hook path. For standalone calls, the program manages lifetime, timeouts and cancellation itself; `Agent` adds batch management and `RunLimits`.
87
115
 
88
116
  ## Hooks
@@ -159,6 +187,23 @@ async with connect_stdio("uvx", ["mcp-server-fetch"], prefix="web") as tools:
159
187
 
160
188
  Requires `pip install 'pi-python-core[mcp]'` and works with the official MCP SDK 1.10 and later, including 2.x. `connect_stdio` starts a stdio MCP server and closes it when the `async with` block exits. `connect_http(url, headers=None, prefix=None, names=None)` connects to a streamable HTTP server in the same way; the legacy SSE transport is not supported, as in Pi. It follows redirects only within the server's origin, so headers are never sent to another one. It is tested with MCP SDK 1.10, 1.30 and 2.3. If you already have a `ClientSession`, wrap its tools with `await mcp_tools(session, prefix=None, names=None)`. Conversion follows Pi's MCP adapter: text and images convert directly; embedded text or image resources are unpacked; audio, resource links and binary resources become short text descriptions; a result with only structured content becomes JSON text and is also kept as `structured_content`; MCP's `isError` becomes a tool error; progress notifications become `tool_execution_update` events. After the prefix is added, tool names keep only letters, digits, `_` and `-`, up to 64 characters. A tool whose schema cannot be used is skipped with a warning, without affecting the server's other tools. A stdio server written with Python MCP SDK 2.3 cannot start on PyPy (`fcntl.F_DUPFD_CLOEXEC` is missing); this is a limitation of the SDK itself, and the client side is unaffected.
161
189
 
190
+ On POSIX, `connect_stdio(..., process_scope=True)` also cleans surviving process
191
+ groups and nested pi-python stdio connections on exit. It is opt-in at the outer
192
+ connection and inherited by nested connections. Non-POSIX systems reject explicit
193
+ enablement. See [process ownership](MCP_INTERACTION.md) for scope and limitations.
194
+
195
+ All three MCP entry points accept optional `call_metadata`, a JSON object passed to
196
+ `ClientSession.call_tool(meta=...)`. It is copied per registration and call, stays out
197
+ of tool arguments, and requires an SDK with `meta` support; otherwise registration
198
+ raises `ConfigurationError`. See [runtime metadata and readiness](PLUGINS.md#mcp-servers)
199
+ for plugin configuration and startup checks.
200
+
201
+ `connect_stdio` and `connect_http` also accept `callbacks=MCPCallbacks(...)` for
202
+ host-authorized sampling and form elicitation. `SamplingHandler` adapts a host Provider;
203
+ `SamplingProvider` runs a server Agent through the current MCP request. This opt-in
204
+ feature requires SDK >=2.3,<3. See [MCP interaction](MCP_INTERACTION.md) for public APIs,
205
+ protocol constraints, cancellation, and the runnable offline example.
206
+
162
207
  ## Plugins
163
208
 
164
209
  `pi_python.plugins` loads plugins: named bundles of tools, system prompt text, hooks, skills, prompt templates, subagents and MCP servers, in the format of Pi's packages. `async with load_plugins([...], services=..., options=...) as plugins:` loads them, and `plugins.agent(...)` builds an Agent with everything they contribute. The [plugin guide](PLUGINS.md) covers writing, distributing and combining plugins.
@@ -216,3 +261,45 @@ OpenAI may add encrypted_content back in the final response. If the visible reas
216
261
  `AssistantMessage.provider_thinking_level` matches upstream: it records this answer's effort only for Claude models that use effort markers, so later requests can rebuild the markers in history. Read `thinking_level` when you need the requested level.
217
262
 
218
263
  When replaying history, Providers skip `error` / `aborted` answers following upstream's rules; reasoning from a different model is converted to plain text and its signature is dropped; a system message that sits between a tool call and its result is moved after the result. The session history itself is unchanged.
264
+
265
+ Host sampling profiles, usage observation, retry policy, and build feature markers are documented in [MCP interactions](MCP_INTERACTION.md#real-providers-profiles-accounting-and-retries).
266
+
267
+
268
+ ## Owned tasks and calls from worker threads
269
+
270
+ `TaskScope` owns the asynchronous calls passed to `await scope.run(awaitable,
271
+ cancel=token)`. Each call runs in a child task. Cancelling the caller or token
272
+ cancels that task and waits for its cleanup. `await scope.aclose()` rejects new
273
+ calls, cancels active calls, and joins them, including calls waiting on an
274
+ application lock. Use `async with TaskScope() as scope`, or a plugin's
275
+ `api.task_scope()` to register automatic cleanup. Exceptions go to the individual
276
+ caller. Closing does not re-raise a completed call's error. Work must cooperate
277
+ with cancellation; this API does not terminate threads or impose a deadline.
278
+ An owned task cannot close its own scope. A scope is bound to its first event loop.
279
+
280
+ `LoopPortal` submits async work from a synchronous worker thread onto an **existing**
281
+ event loop. This differs from `run_sync`, which owns a separate background loop.
282
+ Create and close the portal on the loop that owns the connection:
283
+
284
+ ```python
285
+ import asyncio
286
+ from pi_python import LoopPortal
287
+
288
+ async def request(connection):
289
+ async with LoopPortal() as portal:
290
+ # A callable is passed, not a coroutine created in the worker thread.
291
+ return await asyncio.to_thread(portal.call, connection.fetch, "item")
292
+ ```
293
+
294
+ `portal.call(async_function, *args, timeout=None, **kwargs)` returns the result or
295
+ raises the error in the worker thread, preserving its context variables. Blocking
296
+ an event-loop thread is rejected. Closing cancels and joins submitted work without
297
+ closing the borrowed loop. A timeout bounds the blocking wait and requests async
298
+ cancellation; `aclose()` waits for that cleanup. Keep the owning loop running until
299
+ the portal has closed. Application threads themselves remain application-owned.
300
+
301
+ `FEATURES` is the immutable set of versioned APIs provided by the current build.
302
+ `require_features(names, where="Application")` raises `ConfigurationError` for
303
+ unknown or unavailable features. It does not validate installed optional packages,
304
+ MCP peer capabilities, credentials, or host authorization. `MCP_FEATURES` remains
305
+ available and is a subset. Plugin declarations are described in [Plugins](PLUGINS.md#declaring-required-framework-features).