pi-python-core 0.9.0__tar.gz → 0.10.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 (362) hide show
  1. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/PKG-INFO +11 -2
  2. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/README.md +7 -1
  3. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/README.zh-CN.md +7 -1
  4. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/API.md +87 -0
  5. pi_python_core-0.10.0/docs/CODING_AGENTS.md +176 -0
  6. pi_python_core-0.10.0/docs/MCP_INTERACTION.md +457 -0
  7. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/PLUGINS.md +112 -0
  8. pi_python_core-0.10.0/docs/PLUGIN_DEVELOPMENT.md +287 -0
  9. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/PROVIDERS.md +11 -2
  10. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/API.md +66 -0
  11. pi_python_core-0.10.0/docs/zh/CODING_AGENTS.md +140 -0
  12. pi_python_core-0.10.0/docs/zh/MCP_HOST_VERIFICATION.md +130 -0
  13. pi_python_core-0.10.0/docs/zh/MCP_INTERACTION.md +396 -0
  14. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/PLUGINS.md +98 -0
  15. pi_python_core-0.10.0/docs/zh/PLUGIN_DEVELOPMENT.md +244 -0
  16. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/PROVIDERS.md +11 -1
  17. pi_python_core-0.10.0/examples/mcp_interactive.py +160 -0
  18. pi_python_core-0.10.0/examples/mcp_interactive_server.py +73 -0
  19. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/subagent.py +2 -7
  20. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/pyproject.toml +3 -1
  21. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/__init__.py +7 -1
  22. pi_python_core-0.10.0/src/pi_python/_mcp_host.py +221 -0
  23. pi_python_core-0.10.0/src/pi_python/_mcp_interaction.py +1000 -0
  24. pi_python_core-0.10.0/src/pi_python/_mcp_process.py +183 -0
  25. pi_python_core-0.10.0/src/pi_python/_mcp_sampling.py +280 -0
  26. pi_python_core-0.10.0/src/pi_python/_version.py +1 -0
  27. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/agent.py +19 -0
  28. pi_python_core-0.10.0/src/pi_python/features.py +45 -0
  29. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/function_tools.py +134 -99
  30. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/loop.py +14 -11
  31. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/mcp.py +136 -14
  32. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/__init__.py +354 -28
  33. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_subagents.py +5 -15
  34. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/anthropic.py +3 -1
  35. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/completions.py +34 -9
  36. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/oauth.py +3 -10
  37. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/openai.py +3 -2
  38. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/transport.py +228 -126
  39. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/run.py +28 -2
  40. pi_python_core-0.10.0/src/pi_python/sync.py +150 -0
  41. pi_python_core-0.10.0/src/pi_python/tasks.py +134 -0
  42. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/tools.py +58 -2
  43. pi_python_core-0.10.0/tests/servers/mcp_interactive_server.py +200 -0
  44. pi_python_core-0.10.0/tests/servers/mcp_process_server.py +86 -0
  45. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/servers/mcp_server.py +16 -0
  46. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_completions.py +74 -0
  47. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_contract_v3.py +50 -0
  48. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_examples.py +12 -1
  49. pi_python_core-0.10.0/tests/test_features.py +119 -0
  50. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_function_tools.py +161 -0
  51. pi_python_core-0.10.0/tests/test_lifecycle_contracts.py +278 -0
  52. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_mcp.py +69 -3
  53. pi_python_core-0.10.0/tests/test_mcp_delta.py +477 -0
  54. pi_python_core-0.10.0/tests/test_mcp_host.py +907 -0
  55. pi_python_core-0.10.0/tests/test_mcp_interaction.py +872 -0
  56. pi_python_core-0.10.0/tests/test_mcp_process.py +237 -0
  57. pi_python_core-0.10.0/tests/test_nested_agents.py +273 -0
  58. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_plugins.py +378 -0
  59. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_providers.py +48 -0
  60. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_sync.py +73 -0
  61. pi_python_core-0.10.0/tests/test_tasks.py +158 -0
  62. pi_python_core-0.10.0/tests/test_typeddict_annotations.py +62 -0
  63. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_websocket_continuation.py +142 -0
  64. pi_python_core-0.9.0/src/pi_python/_version.py +0 -1
  65. pi_python_core-0.9.0/src/pi_python/sync.py +0 -78
  66. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/.gitignore +0 -0
  67. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/LICENSE +0 -0
  68. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/NOTICE +0 -0
  69. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/COVERAGE.md +0 -0
  70. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/__init__.py +0 -0
  71. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/baseline.json +0 -0
  72. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/contracts/release-candidate.example.json +0 -0
  73. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/contracts/release-candidate.schema.json +0 -0
  74. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/contracts/upstream-baseline.json +0 -0
  75. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C01-text.json +0 -0
  76. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C02-tool.json +0 -0
  77. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C03-sequential.json +0 -0
  78. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C04-parallel.json +0 -0
  79. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C05-one-sequential.json +0 -0
  80. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C06-one-failure.json +0 -0
  81. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C07-unknown.json +0 -0
  82. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C08-prepare.json +0 -0
  83. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C09-before-error.json +0 -0
  84. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C09-block.json +0 -0
  85. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C09-replace.json +0 -0
  86. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C10-length.json +0 -0
  87. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C11-transform.json +0 -0
  88. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C12-system.json +0 -0
  89. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C13-steering.json +0 -0
  90. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C14-followup-all.json +0 -0
  91. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C14-followup-one.json +0 -0
  92. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C15-prepare.json +0 -0
  93. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-continue.json +0 -0
  94. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-end.json +0 -0
  95. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-mixed.json +0 -0
  96. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-terminate.json +0 -0
  97. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C19-abort-stream.json +0 -0
  98. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C19-abort-tools.json +0 -0
  99. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C24-provider-error.json +0 -0
  100. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-cases.json +0 -0
  101. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/notes.txt +0 -0
  102. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/plan.md +0 -0
  103. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/review.md +0 -0
  104. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/sub/nested.md +0 -0
  105. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/summarize.md +0 -0
  106. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/.hidden/SKILL.md +0 -0
  107. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/alpha/SKILL.md +0 -0
  108. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/alpha/references/notes.md +0 -0
  109. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/beta/SKILL.md +0 -0
  110. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/broken/SKILL.md +0 -0
  111. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/delta/SKILL.md +0 -0
  112. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/gamma/SKILL.md +0 -0
  113. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/longdesc/SKILL.md +0 -0
  114. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/loose.md +0 -0
  115. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/nested/group/epsilon/SKILL.md +0 -0
  116. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/noname/SKILL.md +0 -0
  117. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/plain.md +0 -0
  118. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/zeta/SKILL.md +0 -0
  119. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/zeta/sub/eta/SKILL.md +0 -0
  120. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-oauth.json +0 -0
  121. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-cache-long.json +0 -0
  122. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-cache-none.json +0 -0
  123. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-replay.json +0 -0
  124. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-tools-image.json +0 -0
  125. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-text.json +0 -0
  126. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-thinking.json +0 -0
  127. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-tool.json +0 -0
  128. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/codex-request-tools-image.json +0 -0
  129. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/codex-text.json +0 -0
  130. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-compat-strict.json +0 -0
  131. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-deepseek.json +0 -0
  132. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-ant-ling.json +0 -0
  133. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-baseten.json +0 -0
  134. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-string-thinking.json +0 -0
  135. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-together.json +0 -0
  136. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-zai.json +0 -0
  137. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-midconvo-tools.json +0 -0
  138. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-moonshot-usage.json +0 -0
  139. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-openai-off.json +0 -0
  140. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-openrouter.json +0 -0
  141. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-qwen-length.json +0 -0
  142. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-reasoning-content.json +0 -0
  143. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-request-replay.json +0 -0
  144. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-text.json +0 -0
  145. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-tool-stream.json +0 -0
  146. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-incomplete.json +0 -0
  147. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-cache-long.json +0 -0
  148. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-cache-none.json +0 -0
  149. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-reasoning.json +0 -0
  150. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-replay.json +0 -0
  151. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-tools-image.json +0 -0
  152. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-text.json +0 -0
  153. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-thinking-backfill.json +0 -0
  154. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-thinking.json +0 -0
  155. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-tool.json +0 -0
  156. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/proxy-text.json +0 -0
  157. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/proxy-thinking-tool.json +0 -0
  158. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-collapse-budget.json +0 -0
  159. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-native-tools.json +0 -0
  160. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-oauth-effort.json +0 -0
  161. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-redefinition.json +0 -0
  162. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-server-fallback.json +0 -0
  163. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-thinking-off.json +0 -0
  164. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-codex-tool-search.json +0 -0
  165. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-additional-tools.json +0 -0
  166. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-collapse.json +0 -0
  167. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-explicit-cache-long.json +0 -0
  168. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-explicit-cache-none.json +0 -0
  169. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-removal.json +0 -0
  170. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/recovery-cases.json +0 -0
  171. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/release.py +0 -0
  172. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C01-text.python.json +0 -0
  173. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C01-text.upstream.json +0 -0
  174. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C02-tool.python.json +0 -0
  175. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C02-tool.upstream.json +0 -0
  176. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C03-sequential.python.json +0 -0
  177. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C03-sequential.upstream.json +0 -0
  178. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C04-parallel.python.json +0 -0
  179. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C04-parallel.upstream.json +0 -0
  180. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C05-one-sequential.python.json +0 -0
  181. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C05-one-sequential.upstream.json +0 -0
  182. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C06-one-failure.python.json +0 -0
  183. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C06-one-failure.upstream.json +0 -0
  184. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C07-unknown.python.json +0 -0
  185. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C07-unknown.upstream.json +0 -0
  186. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C08-prepare.python.json +0 -0
  187. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C08-prepare.upstream.json +0 -0
  188. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-before-error.python.json +0 -0
  189. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-before-error.upstream.json +0 -0
  190. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-block.python.json +0 -0
  191. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-block.upstream.json +0 -0
  192. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-replace.python.json +0 -0
  193. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-replace.upstream.json +0 -0
  194. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C10-length.python.json +0 -0
  195. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C10-length.upstream.json +0 -0
  196. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C11-transform.python.json +0 -0
  197. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C11-transform.upstream.json +0 -0
  198. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C12-system.python.json +0 -0
  199. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C12-system.upstream.json +0 -0
  200. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C13-steering.python.json +0 -0
  201. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C13-steering.upstream.json +0 -0
  202. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-all.python.json +0 -0
  203. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-all.upstream.json +0 -0
  204. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-one.python.json +0 -0
  205. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-one.upstream.json +0 -0
  206. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C15-prepare.python.json +0 -0
  207. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C15-prepare.upstream.json +0 -0
  208. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-continue.python.json +0 -0
  209. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-continue.upstream.json +0 -0
  210. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-end.python.json +0 -0
  211. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-end.upstream.json +0 -0
  212. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-mixed.python.json +0 -0
  213. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-mixed.upstream.json +0 -0
  214. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-terminate.python.json +0 -0
  215. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-terminate.upstream.json +0 -0
  216. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-stream.python.json +0 -0
  217. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-stream.upstream.json +0 -0
  218. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-tools.python.json +0 -0
  219. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-tools.upstream.json +0 -0
  220. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C24-provider-error.python.json +0 -0
  221. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C24-provider-error.upstream.json +0 -0
  222. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/artifacts.json +0 -0
  223. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/benchmark.json +0 -0
  224. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/bootstrap.json +0 -0
  225. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/candidate-live-noop.json +0 -0
  226. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/conformance.json +0 -0
  227. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.11.json +0 -0
  228. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.12.json +0 -0
  229. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.13.json +0 -0
  230. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.14.json +0 -0
  231. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-install-hash-repair.json +0 -0
  232. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-0.5-regression.json +0 -0
  233. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-0.5-session-claude.json +0 -0
  234. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-0.5-session-codex.json +0 -0
  235. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-claude-reasoning-replay.json +0 -0
  236. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-final-matrix.json +0 -0
  237. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-initial.json +0 -0
  238. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-loop-initial.json +0 -0
  239. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-providers.json +0 -0
  240. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-reasoning-replay.json +0 -0
  241. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-refresh.json +0 -0
  242. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-regression-0.4-deepseek.json +0 -0
  243. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-regression-0.4-first-run.json +0 -0
  244. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-regression-0.4.json +0 -0
  245. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-session-claude.json +0 -0
  246. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-session-codex.json +0 -0
  247. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-websocket-cached-recheck.json +0 -0
  248. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-websocket-cached.json +0 -0
  249. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-websocket-final.json +0 -0
  250. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/plugins.upstream.json +0 -0
  251. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/provider-conformance.json +0 -0
  252. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/recovery-conformance.upstream.json +0 -0
  253. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/runtime-dependencies.json +0 -0
  254. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/source-verification.json +0 -0
  255. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/artifacts.json +0 -0
  256. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/linux-3.11.json +0 -0
  257. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/linux-3.12.json +0 -0
  258. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/linux-3.13.json +0 -0
  259. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/verification.json +0 -0
  260. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/artifacts.json +0 -0
  261. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/linux-3.11.json +0 -0
  262. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/linux-3.12.json +0 -0
  263. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/linux-3.13.json +0 -0
  264. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/verification.json +0 -0
  265. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/against-0.4-fixtures.json +0 -0
  266. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/artifacts.json +0 -0
  267. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/linux-3.11.json +0 -0
  268. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/linux-3.12.json +0 -0
  269. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/linux-3.13.json +0 -0
  270. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/provider-conformance.json +0 -0
  271. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/verification.json +0 -0
  272. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/artifacts.json +0 -0
  273. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/linux-3.11.json +0 -0
  274. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/linux-3.12.json +0 -0
  275. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/linux-3.13.json +0 -0
  276. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/verification.json +0 -0
  277. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/artifacts.json +0 -0
  278. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/linux-3.11.json +0 -0
  279. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/linux-3.12.json +0 -0
  280. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/linux-3.13.json +0 -0
  281. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/verification.json +0 -0
  282. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/artifacts.json +0 -0
  283. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/linux-3.11.json +0 -0
  284. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/linux-3.12.json +0 -0
  285. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/linux-3.13.json +0 -0
  286. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/verification.json +0 -0
  287. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/artifacts.json +0 -0
  288. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.11.json +0 -0
  289. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.12.json +0 -0
  290. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.13.json +0 -0
  291. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.14.json +0 -0
  292. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/verification.json +0 -0
  293. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/artifacts.json +0 -0
  294. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.11.json +0 -0
  295. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.12.json +0 -0
  296. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.13.json +0 -0
  297. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.14.json +0 -0
  298. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/verification.json +0 -0
  299. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/verification.json +0 -0
  300. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/websocket-conformance.json +0 -0
  301. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/runner.py +0 -0
  302. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/websocket-fixtures/codex-continuation-lost-state.json +0 -0
  303. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/websocket-fixtures/codex-continuation-tool-loop.json +0 -0
  304. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/CONCEPTS.md +0 -0
  305. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/IMPLEMENTATION.md +0 -0
  306. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.3.0.md +0 -0
  307. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.4.0.md +0 -0
  308. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.5.0.md +0 -0
  309. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.6.0.md +0 -0
  310. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.7.0.md +0 -0
  311. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.8.0.md +0 -0
  312. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/CONCEPTS.md +0 -0
  313. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/in_memory.py +0 -0
  314. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/local_model.py +0 -0
  315. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/mcp_tools.py +0 -0
  316. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugin_demo.py +0 -0
  317. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/agents/reviewer.md +0 -0
  318. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/ops.py +0 -0
  319. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/plugin.py +0 -0
  320. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/prompts/dedup.md +0 -0
  321. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/skills/dedup-window/SKILL.md +0 -0
  322. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/provider_chat.py +0 -0
  323. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/quickstart.py +0 -0
  324. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/recovery.py +0 -0
  325. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/save_restore.py +0 -0
  326. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/cancellation.py +0 -0
  327. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/data/models.json +0 -0
  328. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/errors.py +0 -0
  329. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/estimate.py +0 -0
  330. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/events.py +0 -0
  331. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/hooks.py +0 -0
  332. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/limits.py +0 -0
  333. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/lowlevel.py +0 -0
  334. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/messages.py +0 -0
  335. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/models.py +0 -0
  336. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_compose.py +0 -0
  337. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_frontmatter.py +0 -0
  338. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_resources.py +0 -0
  339. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/provider.py +0 -0
  340. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/__init__.py +0 -0
  341. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/common.py +0 -0
  342. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/proxy.py +0 -0
  343. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/py.typed +0 -0
  344. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/queues.py +0 -0
  345. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/recovery.py +0 -0
  346. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/stream.py +0 -0
  347. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/testing.py +0 -0
  348. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/transcript.py +0 -0
  349. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_conformance.py +0 -0
  350. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_control.py +0 -0
  351. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_extended_core.py +0 -0
  352. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_loop.py +0 -0
  353. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_messages.py +0 -0
  354. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_models.py +0 -0
  355. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_oauth.py +0 -0
  356. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_plugin_conformance.py +0 -0
  357. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_recovery.py +0 -0
  358. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_recovery_conformance.py +0 -0
  359. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_recovery_helpers.py +0 -0
  360. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_release.py +0 -0
  361. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_session_changes.py +0 -0
  362. {pi_python_core-0.9.0 → pi_python_core-0.10.0}/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.0
4
4
  Summary: An embeddable asyncio agent core with opt-in Claude and OpenAI providers
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -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
 
@@ -107,6 +114,8 @@ A few differences are deliberate, such as strict tool-argument validation withou
107
114
 
108
115
  ## Documentation
109
116
 
117
+ - [Coding agents: integrate the library, run an offline example, and verify behavior](docs/CODING_AGENTS.md)
118
+ - [Plugin development: scaffold, readiness checks, packaging, and installed verification](docs/PLUGIN_DEVELOPMENT.md)
110
119
  - [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
111
120
  - [Public API](docs/API.md)
112
121
  - [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
@@ -118,7 +127,7 @@ A few differences are deliberate, such as strict tool-argument validation withou
118
127
  ## Development
119
128
 
120
129
  ```bash
121
- uv sync --locked --extra oauth --extra mcp
130
+ uv sync --locked --extra oauth --extra mcp-interactive
122
131
  uv run pytest -q
123
132
  uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
124
133
  ```
@@ -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
 
@@ -76,6 +80,8 @@ A few differences are deliberate, such as strict tool-argument validation withou
76
80
 
77
81
  ## Documentation
78
82
 
83
+ - [Coding agents: integrate the library, run an offline example, and verify behavior](docs/CODING_AGENTS.md)
84
+ - [Plugin development: scaffold, readiness checks, packaging, and installed verification](docs/PLUGIN_DEVELOPMENT.md)
79
85
  - [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
80
86
  - [Public API](docs/API.md)
81
87
  - [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
@@ -87,7 +93,7 @@ A few differences are deliberate, such as strict tool-argument validation withou
87
93
  ## Development
88
94
 
89
95
  ```bash
90
- uv sync --locked --extra oauth --extra mcp
96
+ uv sync --locked --extra oauth --extra mcp-interactive
91
97
  uv run pytest -q
92
98
  uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
93
99
  ```
@@ -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
 
@@ -76,6 +80,8 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
76
80
 
77
81
  ## 文档
78
82
 
83
+ - [Coding agents:接入 library、运行离线示例、验证行为](docs/zh/CODING_AGENTS.md)
84
+ - [插件开发:目录结构、就绪检查、打包与安装验证](docs/zh/PLUGIN_DEVELOPMENT.md)
79
85
  - [一页看懂:五个概念和一轮的流程](docs/zh/CONCEPTS.md)
80
86
  - [公开 API](docs/zh/API.md)
81
87
  - [模型接入、订阅登录与本地模型](docs/zh/PROVIDERS.md)
@@ -87,7 +93,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
87
93
  ## 开发
88
94
 
89
95
  ```bash
90
- uv sync --locked --extra oauth --extra mcp
96
+ uv sync --locked --extra oauth --extra mcp-interactive
91
97
  uv run pytest -q
92
98
  uv run python scripts/verify.py # 全部检查,含与上游的差分(需要 Node)
93
99
  ```
@@ -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).
@@ -0,0 +1,176 @@
1
+ # Using pi-python-core with a coding agent
2
+
3
+ **English** | [中文](zh/CODING_AGENTS.md)
4
+
5
+ Use this guide when building an application **with** this library. Start with the
6
+ offline tool call below, then replace its model provider. For a reusable extension,
7
+ follow [Plugin development](PLUGIN_DEVELOPMENT.md). Repository maintenance commands
8
+ are at the end; they are not required in a consuming application.
9
+
10
+ ## 1. Establish the environment
11
+
12
+ The distribution is `pi-python-core`; the import is `pi_python`. Python 3.11 or later
13
+ is required. The library uses asyncio. Import public APIs from `pi_python`,
14
+ `pi_python.providers`, `pi_python.plugins`, or `pi_python.mcp`, rather than private
15
+ modules whose names start with `_`.
16
+
17
+ In a consuming project, install with `uv add pi-python-core` or
18
+ `python -m pip install pi-python-core`. Optional extras are `[mcp]` for MCP tools,
19
+ `[mcp-interactive]` for host sampling/forms, and `[oauth]` for ChatGPT identity-token
20
+ verification. Built-in model connectors do not require an extra.
21
+
22
+ When working from this checkout, run these commands from the repository root:
23
+
24
+ ```bash
25
+ uv sync --locked --extra mcp-interactive
26
+ uv run python examples/quickstart.py
27
+ uv run python examples/plugin_demo.py
28
+ ```
29
+
30
+ These examples are offline and need no credentials. The docs describe the checked-out
31
+ code; its version may not yet be published. Inspect `pyproject.toml` and check the
32
+ installed version with `python -c "from importlib.metadata import version; print(version('pi-python-core'))"`.
33
+ For another project to use this checkout, install it in that project's environment
34
+ with `python -m pip install -e /absolute/path/to/pi-python` (append
35
+ `[mcp-interactive]` to that path if needed).
36
+
37
+ ## 2. Run a complete offline tool call
38
+
39
+ Save this as `app.py` and run `python app.py` in the installed environment, or
40
+ `uv run python app.py` in this checkout. It prints `3 words.` and checks the actual
41
+ tool result, not just the model's scripted answer.
42
+
43
+ ```python
44
+ import asyncio
45
+
46
+ from pi_python import (
47
+ Agent, AssistantMessage, RunLimits, ScriptedProvider, TextContent, ToolCall, tool,
48
+ )
49
+
50
+
51
+ @tool
52
+ def count_words(text: str) -> dict[str, int]:
53
+ """Count whitespace-separated words in text."""
54
+ return {"words": len(text.split())}
55
+
56
+
57
+ async def main() -> None:
58
+ provider = ScriptedProvider([
59
+ AssistantMessage([
60
+ ToolCall("count-1", "count_words", {"text": "one two three"}),
61
+ ], stop_reason="tool_use"),
62
+ AssistantMessage.text("3 words."),
63
+ ])
64
+ async with Agent(
65
+ provider=provider,
66
+ tools=[count_words],
67
+ limits=RunLimits(max_model_requests=4, max_tool_calls=4, run_timeout=30),
68
+ ) as agent:
69
+ result = await agent.prompt("Count the words in: one two three")
70
+ if result.status != "completed":
71
+ raise RuntimeError(f"{result.status}: {result.errors}")
72
+ outcome = result.tool_outcomes[0]
73
+ assert outcome.execution_status == "succeeded"
74
+ assert outcome.result.structured_content == {"words": 3}
75
+ answer = result.messages[-1]
76
+ assert isinstance(answer, AssistantMessage)
77
+ print("".join(b.text for b in answer.content if isinstance(b, TextContent)))
78
+
79
+
80
+ if __name__ == "__main__":
81
+ asyncio.run(main())
82
+ ```
83
+
84
+ `Agent` owns the conversation and tool loop. A `Provider` supplies model responses.
85
+ `@tool` turns a typed function and its docstring into a tool declaration. The model
86
+ chooses tool names and JSON arguments; the library validates arguments and executes
87
+ the functions. `ScriptedProvider` fixes those choices for a deterministic test. It
88
+ does not demonstrate that a real model will choose the same tool or answer.
89
+
90
+ ## 3. Replace the provider, keep application ownership explicit
91
+
92
+ For a real model, pass the application's credentials and chosen model explicitly.
93
+ Inside an async function, the replacement looks like this (it makes a paid/network
94
+ call and is not part of the offline example):
95
+
96
+ ```python
97
+ import os
98
+ from pi_python.providers import AnthropicProvider
99
+
100
+ provider = AnthropicProvider(api_key=os.environ["ANTHROPIC_API_KEY"])
101
+ try:
102
+ async with Agent(
103
+ provider=provider,
104
+ model=os.environ["ANTHROPIC_MODEL"],
105
+ tools=[count_words],
106
+ limits=RunLimits(max_model_requests=4, run_timeout=60),
107
+ ) as agent:
108
+ result = await agent.prompt("Count the words in: one two three")
109
+ if result.status != "completed":
110
+ raise RuntimeError(f"{result.status}: {result.errors}")
111
+ finally:
112
+ await provider.aclose()
113
+ ```
114
+
115
+ `Agent.aclose()` closes the agent's work, not a shared provider. The application
116
+ closes its provider after all agents using it are done. Keep creation, use and
117
+ cleanup on the same event loop. For another connector, use
118
+ [Providers](PROVIDERS.md) and the runnable [local-model example](../examples/local_model.py).
119
+
120
+ ## 4. Observe the contracts when writing application code
121
+
122
+ | Concern | Contract to follow |
123
+ |---|---|
124
+ | Async versus sync | In servers/notebooks use `await agent.prompt(...)`. In a plain synchronous script use `agent.prompt_sync(...)`. Do not call a blocking API on an event-loop thread or repeatedly wrap a loop-bound provider in separate `asyncio.run` calls. |
125
+ | Conversation ownership | One agent accepts one active run. Await turns in order; use separate agents for independent conversations. `agent.state` is a copy. |
126
+ | Completion | Check `result.status`: `completed`, `failed`, `cancelled`, or `limit_reached`. Model/tool failures are not all raised as Python exceptions. Inspect `errors` and `tool_outcomes` before accepting a business result. |
127
+ | Tool inputs | Give functions typed parameters and clear docstrings. A `ToolContext` parameter is injected and omitted from the model schema. Arguments are validated without JSON type coercion. |
128
+ | Tool outputs | Return JSON-compatible values or `ToolResult`. Dicts/numbers are available as `structured_content` and text. `details` is application data; it is not automatically sent to the model. |
129
+ | Concurrency and limits | Tool batches run in parallel by default. Set `RunLimits`; use `@tool(execution_mode="sequential")` for tools that require a sequential batch. Plain functions run in worker threads; cancellation cannot forcibly stop those threads. |
130
+ | Cancellation | Use `agent.abort()` and await completion/cleanup. Check `cleanup_complete` and `reconciliation_required` before reusing an agent or retrying a side effect. Prefer async tools for cancellable I/O. |
131
+ | Recovery | `continue_run()` can retry a failed model turn without replaying past tools. It is not a general retry mechanism for failed business operations. See [recovery](../examples/recovery.py). |
132
+ | Persistence | Store `encode_messages(list(agent.state.messages))`; restore with `decode_messages` and `Agent(messages=...)`. `result.messages` contains only the current run's additions. See [save/restore](../examples/save_restore.py). |
133
+ | Streaming | `agent.subscribe(listener)` receives `message_update` events with `delta_type` and `delta`. Critical listeners are awaited, so keep them bounded. See [quickstart](../examples/quickstart.py). |
134
+
135
+ For application-owned async work use `TaskScope`. To call an existing async connection
136
+ from a synchronous tool's worker thread, use `LoopPortal` created on its owning loop.
137
+ Do not move that connection to `run_sync`'s separate loop. See
138
+ [task ownership and thread calls](API.md#owned-tasks-and-calls-from-worker-threads).
139
+ These APIs and feature declarations are part of the 0.10.0 source line; check
140
+ `require_features(["task-scope-v1", "loop-portal-v1"])` when depending on them.
141
+
142
+ ## 5. Choose the smallest integration surface
143
+
144
+ | Need | Start here |
145
+ |---|---|
146
+ | A few tools in one application | `Agent(..., tools=[...])`; no plugin is necessary. |
147
+ | Reusable tools, instructions and resources | [Plugin development workflow](PLUGIN_DEVELOPMENT.md), then [plugin API](PLUGINS.md). |
148
+ | Tools from an MCP server | [MCP tools example](../examples/mcp_tools.py); keep the connection open for the agent's lifetime. |
149
+ | MCP server requests a host model or form | [MCP interactions](MCP_INTERACTION.md) and [offline example](../examples/mcp_interactive.py). Use the interactive extra and explicit host callbacks. |
150
+ | A nested agent | Use `await context.run_agent(child, message)` inside a tool to preserve cleanup ownership and unknown outcomes; see the [subagent example](../examples/subagent.py). Plugins can supply definitions in `agents/`. |
151
+ | Your own model adapter | The [Provider contract](API.md#provider); emit exactly one terminal `ModelEvent.done` and honor cancellation. |
152
+
153
+ MCP incremental sampling is a negotiated extension. It reconstructs the full request
154
+ before host policy and model access. It reduces repeated MCP transmission, not the
155
+ model's history or token count; business code should not manage its internal cache.
156
+
157
+ ## 6. Verify before handing off
158
+
159
+ For a consuming application, run an offline tool-call test, assert the actual tool
160
+ result, exercise failure/cancellation when relevant, and then test the configured real
161
+ provider separately. Report which paths ran. For plugins, also verify the installed
162
+ wheel using the [plugin workflow](PLUGIN_DEVELOPMENT.md).
163
+
164
+ When changing this library itself, read the applicable `AGENTS.md`, follow its scope,
165
+ and run the relevant tests. The repository checks are:
166
+
167
+ ```bash
168
+ uv run ruff check src tests examples compat scripts
169
+ uv run ruff format --check src tests examples compat scripts
170
+ uv run mypy src/pi_python
171
+ uv run pytest -q
172
+ ```
173
+
174
+ Use [API](API.md) for exact contracts, [concepts](CONCEPTS.md) for the execution model,
175
+ and [reference verification](../reference/README.md) when a change affects upstream
176
+ compatibility. Do not invent methods or assume a different agent framework's APIs apply.