ipinb 0.1.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.
- ipinb-0.1.0/BREAKING_CHANGES.md +1844 -0
- ipinb-0.1.0/CHANGELOG.md +248 -0
- ipinb-0.1.0/LICENSE +21 -0
- ipinb-0.1.0/MANIFEST.in +12 -0
- ipinb-0.1.0/PKG-INFO +438 -0
- ipinb-0.1.0/README.md +375 -0
- ipinb-0.1.0/docs/architecture.md +423 -0
- ipinb-0.1.0/docs/autopilot/ipi-mcp-redesign.md +1154 -0
- ipinb-0.1.0/docs/autopilot/notebook-service-log.md +98 -0
- ipinb-0.1.0/docs/autopilot/notebook-service-plan.md +102 -0
- ipinb-0.1.0/docs/autopilot/performance-log.md +154 -0
- ipinb-0.1.0/docs/autopilot/performance-plan.md +160 -0
- ipinb-0.1.0/docs/autopilot/performance-results.md +188 -0
- ipinb-0.1.0/docs/autopilot/reduce-output-tokens-log.md +280 -0
- ipinb-0.1.0/docs/autopilot/reduce-output-tokens-plan.md +94 -0
- ipinb-0.1.0/docs/debugging.md +36 -0
- ipinb-0.1.0/docs/execution-and-results.md +34 -0
- ipinb-0.1.0/docs/harness-shell-history.md +132 -0
- ipinb-0.1.0/docs/kernel-environments.md +46 -0
- ipinb-0.1.0/docs/notebook-publishing.md +55 -0
- ipinb-0.1.0/docs/parallel-execution.md +40 -0
- ipinb-0.1.0/docs/releasing.md +53 -0
- ipinb-0.1.0/docs/rich-output.md +40 -0
- ipinb-0.1.0/docs/validation/compact-output-blocks-2026-09-13.md +30 -0
- ipinb-0.1.0/docs/validation/execution-api-2026-09-10.md +89 -0
- ipinb-0.1.0/docs/validation/fastmcp4-2026-09-13.md +33 -0
- ipinb-0.1.0/docs/your-first-extension.md +885 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/SKILL.md +130 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/references/authoring-guide.md +885 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/references/evaluations.md +119 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/references/source-map.md +154 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/references/testing-and-troubleshooting.md +250 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/templates/installed-plugin/pyproject.toml +22 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/templates/installed-plugin/src/example_ipi_plugin/__init__.py +47 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/templates/installed-plugin/tests/test_plugin.py +54 -0
- ipinb-0.1.0/ipi/skills/developing-ipi-plugins/templates/project-plugin.py +58 -0
- ipinb-0.1.0/ipi/skills/notebook/SKILL.md +24 -0
- ipinb-0.1.0/ipi/skills/notebook/references/debugging.md +36 -0
- ipinb-0.1.0/ipi/skills/notebook/references/execution-and-results.md +34 -0
- ipinb-0.1.0/ipi/skills/notebook/references/kernel-environments.md +46 -0
- ipinb-0.1.0/ipi/skills/notebook/references/notebook-publishing.md +55 -0
- ipinb-0.1.0/ipi/skills/notebook/references/parallel-execution.md +40 -0
- ipinb-0.1.0/ipi/skills/notebook/references/rich-output.md +40 -0
- ipinb-0.1.0/ipi/skills/notebook-http/SKILL.md +8 -0
- ipinb-0.1.0/jupyterlab-extension/package-lock.json +8386 -0
- ipinb-0.1.0/jupyterlab-extension/package.json +47 -0
- ipinb-0.1.0/jupyterlab-extension/src/cell_chrome.ts +434 -0
- ipinb-0.1.0/jupyterlab-extension/src/index.ts +24 -0
- ipinb-0.1.0/jupyterlab-extension/src/intent.ts +1033 -0
- ipinb-0.1.0/jupyterlab-extension/src/metadata.ts +297 -0
- ipinb-0.1.0/jupyterlab-extension/src/trace_chrome.ts +897 -0
- ipinb-0.1.0/jupyterlab-extension/style/index.css +554 -0
- ipinb-0.1.0/jupyterlab-extension/tests/index.spec.ts +1800 -0
- ipinb-0.1.0/jupyterlab-extension/tests/setup.ts +19 -0
- ipinb-0.1.0/jupyterlab-extension/tsconfig.json +17 -0
- ipinb-0.1.0/jupyterlab-extension/tsconfig.test.json +15 -0
- ipinb-0.1.0/jupyterlab-extension/vitest.config.ts +8 -0
- ipinb-0.1.0/pyproject.toml +176 -0
- ipinb-0.1.0/setup.cfg +4 -0
- ipinb-0.1.0/src/ipi/__init__.py +5 -0
- ipinb-0.1.0/src/ipi/_ansi.py +274 -0
- ipinb-0.1.0/src/ipi/_dependency_metadata.py +149 -0
- ipinb-0.1.0/src/ipi/_exception_groups.py +12 -0
- ipinb-0.1.0/src/ipi/_git_provenance.py +122 -0
- ipinb-0.1.0/src/ipi/_kernel_argv.py +25 -0
- ipinb-0.1.0/src/ipi/_kernel_launcher.py +56 -0
- ipinb-0.1.0/src/ipi/_launch_gate.py +29 -0
- ipinb-0.1.0/src/ipi/_markdown.py +14 -0
- ipinb-0.1.0/src/ipi/_model_output.py +287 -0
- ipinb-0.1.0/src/ipi/_names.py +16 -0
- ipinb-0.1.0/src/ipi/_notebook_mirror.py +284 -0
- ipinb-0.1.0/src/ipi/_owner_watchdog.py +44 -0
- ipinb-0.1.0/src/ipi/_parent_process.py +39 -0
- ipinb-0.1.0/src/ipi/_platform_support.py +30 -0
- ipinb-0.1.0/src/ipi/_self_wheel.py +139 -0
- ipinb-0.1.0/src/ipi/_timing.py +34 -0
- ipinb-0.1.0/src/ipi/_toml.py +67 -0
- ipinb-0.1.0/src/ipi/_transport/__init__.py +41 -0
- ipinb-0.1.0/src/ipi/_transport/address.py +190 -0
- ipinb-0.1.0/src/ipi/_transport/artifacts.py +23 -0
- ipinb-0.1.0/src/ipi/_transport/contract.py +125 -0
- ipinb-0.1.0/src/ipi/_transport/local.py +268 -0
- ipinb-0.1.0/src/ipi/_transport/master_owner.py +98 -0
- ipinb-0.1.0/src/ipi/_transport/owned_directory.py +163 -0
- ipinb-0.1.0/src/ipi/_transport/portable_uv.py +158 -0
- ipinb-0.1.0/src/ipi/_transport/ssh.py +1816 -0
- ipinb-0.1.0/src/ipi/_transport/staging.py +135 -0
- ipinb-0.1.0/src/ipi/_transport/supervisor.py +817 -0
- ipinb-0.1.0/src/ipi/_transport/supervisor_protocol.py +8 -0
- ipinb-0.1.0/src/ipi/_ynotebook_ops.py +235 -0
- ipinb-0.1.0/src/ipi/cell_export.py +478 -0
- ipinb-0.1.0/src/ipi/config.py +376 -0
- ipinb-0.1.0/src/ipi/execution_backend.py +305 -0
- ipinb-0.1.0/src/ipi/execution_models.py +193 -0
- ipinb-0.1.0/src/ipi/formatting.py +699 -0
- ipinb-0.1.0/src/ipi/jupyter_server.py +207 -0
- ipinb-0.1.0/src/ipi/kernel.py +7252 -0
- ipinb-0.1.0/src/ipi/labextensions/@ipi/notebook-extension/package.json +52 -0
- ipinb-0.1.0/src/ipi/labextensions/@ipi/notebook-extension/static/585.5ce41466a93651634fda.js +1 -0
- ipinb-0.1.0/src/ipi/labextensions/@ipi/notebook-extension/static/665.bfdd2e9a96724c3cb559.js +1 -0
- ipinb-0.1.0/src/ipi/labextensions/@ipi/notebook-extension/static/remoteEntry.e0b57e0b0817ceee899e.js +1 -0
- ipinb-0.1.0/src/ipi/labextensions/@ipi/notebook-extension/static/style.js +4 -0
- ipinb-0.1.0/src/ipi/labextensions/@ipi/notebook-extension/static/third-party-licenses.json +16 -0
- ipinb-0.1.0/src/ipi/notebook_publisher.py +646 -0
- ipinb-0.1.0/src/ipi/notebook_trace.py +47 -0
- ipinb-0.1.0/src/ipi/plugins/__init__.py +47 -0
- ipinb-0.1.0/src/ipi/plugins/_model.py +1119 -0
- ipinb-0.1.0/src/ipi/plugins/api.py +568 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/__init__.py +47 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/_agents_md_impl.py +100 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/_image_outputs_impl.py +176 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/agents_md.py +25 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/autoreload.py +26 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/dataframe_display.py +21 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/http_user_agent.py +65 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/image_outputs.py +41 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/import_guidance.py +55 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/pdb.py +1029 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/pip_guidance.py +193 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/project_import.py +28 -0
- ipinb-0.1.0/src/ipi/plugins/builtins/shell_history.py +152 -0
- ipinb-0.1.0/src/ipi/plugins/catalog.py +387 -0
- ipinb-0.1.0/src/ipi/plugins/discovery.py +1058 -0
- ipinb-0.1.0/src/ipi/plugins/kernel.py +661 -0
- ipinb-0.1.0/src/ipi/plugins/py.typed +0 -0
- ipinb-0.1.0/src/ipi/plugins/server_deps.py +372 -0
- ipinb-0.1.0/src/ipi/py.typed +1 -0
- ipinb-0.1.0/src/ipi/records/__init__.py +433 -0
- ipinb-0.1.0/src/ipi/records/py.typed +1 -0
- ipinb-0.1.0/src/ipi/redaction.py +136 -0
- ipinb-0.1.0/src/ipi/references.py +84 -0
- ipinb-0.1.0/src/ipi/resource_registry.py +1386 -0
- ipinb-0.1.0/src/ipi/resources/notebook_http_skill.md +8 -0
- ipinb-0.1.0/src/ipi/resources/notebook_skill.md +24 -0
- ipinb-0.1.0/src/ipi/resources/references/debugging.md +36 -0
- ipinb-0.1.0/src/ipi/resources/references/execution-and-results.md +34 -0
- ipinb-0.1.0/src/ipi/resources/references/kernel-environments.md +46 -0
- ipinb-0.1.0/src/ipi/resources/references/notebook-publishing.md +55 -0
- ipinb-0.1.0/src/ipi/resources/references/parallel-execution.md +40 -0
- ipinb-0.1.0/src/ipi/resources/references/rich-output.md +40 -0
- ipinb-0.1.0/src/ipi/runtime/__init__.py +94 -0
- ipinb-0.1.0/src/ipi/runtime/_subprocess.py +233 -0
- ipinb-0.1.0/src/ipi/runtime/audit.py +81 -0
- ipinb-0.1.0/src/ipi/runtime/bootstrap.py +65 -0
- ipinb-0.1.0/src/ipi/runtime/cell_types.py +228 -0
- ipinb-0.1.0/src/ipi/runtime/concurrent.py +312 -0
- ipinb-0.1.0/src/ipi/runtime/context.py +29 -0
- ipinb-0.1.0/src/ipi/runtime/debugger.py +587 -0
- ipinb-0.1.0/src/ipi/runtime/environment.py +72 -0
- ipinb-0.1.0/src/ipi/runtime/formatters.py +78 -0
- ipinb-0.1.0/src/ipi/runtime/magics.py +412 -0
- ipinb-0.1.0/src/ipi/runtime/path_context.py +268 -0
- ipinb-0.1.0/src/ipi/runtime/project.py +74 -0
- ipinb-0.1.0/src/ipi/runtime/py.typed +0 -0
- ipinb-0.1.0/src/ipi/runtime/replay.py +80 -0
- ipinb-0.1.0/src/ipi/runtime/shell_history.py +458 -0
- ipinb-0.1.0/src/ipi/runtime/state_delta.py +55 -0
- ipinb-0.1.0/src/ipi/server/__init__.py +1 -0
- ipinb-0.1.0/src/ipi/server/__main__.py +747 -0
- ipinb-0.1.0/src/ipi/server/builtins/__init__.py +46 -0
- ipinb-0.1.0/src/ipi/server/builtins/harness_context.py +246 -0
- ipinb-0.1.0/src/ipi/server/builtins/inline_python_guidance.py +191 -0
- ipinb-0.1.0/src/ipi/server/builtins/session_trace.py +436 -0
- ipinb-0.1.0/src/ipi/server/builtins/shell_forwarding.py +194 -0
- ipinb-0.1.0/src/ipi/server/builtins/shell_history.py +236 -0
- ipinb-0.1.0/src/ipi/server/builtins/view_image.py +174 -0
- ipinb-0.1.0/src/ipi/server/execution_content.py +75 -0
- ipinb-0.1.0/src/ipi/server/execution_server.py +663 -0
- ipinb-0.1.0/src/ipi/server/harness.py +839 -0
- ipinb-0.1.0/src/ipi/server/harness_environment.py +172 -0
- ipinb-0.1.0/src/ipi/server/harness_transport.py +105 -0
- ipinb-0.1.0/src/ipi/server/integration.py +176 -0
- ipinb-0.1.0/src/ipi/server/narration_doctor.py +340 -0
- ipinb-0.1.0/src/ipi/server/notebook_channel.py +278 -0
- ipinb-0.1.0/src/ipi/server/plugin_resolver.py +296 -0
- ipinb-0.1.0/src/ipi/server/plugin_tools.py +208 -0
- ipinb-0.1.0/src/ipi/server/py.typed +1 -0
- ipinb-0.1.0/src/ipi/server/rollout_narration.py +190 -0
- ipinb-0.1.0/src/ipi/server/server.py +204 -0
- ipinb-0.1.0/src/ipi/session.py +373 -0
- ipinb-0.1.0/src/ipi/tunnel.py +352 -0
- ipinb-0.1.0/src/ipinb.egg-info/PKG-INFO +438 -0
- ipinb-0.1.0/src/ipinb.egg-info/SOURCES.txt +276 -0
- ipinb-0.1.0/src/ipinb.egg-info/dependency_links.txt +1 -0
- ipinb-0.1.0/src/ipinb.egg-info/entry_points.txt +2 -0
- ipinb-0.1.0/src/ipinb.egg-info/requires.txt +49 -0
- ipinb-0.1.0/src/ipinb.egg-info/top_level.txt +1 -0
- ipinb-0.1.0/tests/core/test_ansi.py +95 -0
- ipinb-0.1.0/tests/core/test_build_info.py +114 -0
- ipinb-0.1.0/tests/core/test_config.py +392 -0
- ipinb-0.1.0/tests/core/test_context_runtime.py +211 -0
- ipinb-0.1.0/tests/core/test_credential_boundaries.py +108 -0
- ipinb-0.1.0/tests/core/test_credential_redaction.py +131 -0
- ipinb-0.1.0/tests/core/test_debugger.py +804 -0
- ipinb-0.1.0/tests/core/test_editable_requirements.py +172 -0
- ipinb-0.1.0/tests/core/test_iopub_reducer.py +523 -0
- ipinb-0.1.0/tests/core/test_jupyter_server.py +31 -0
- ipinb-0.1.0/tests/core/test_kernel.py +3381 -0
- ipinb-0.1.0/tests/core/test_kernel_address.py +154 -0
- ipinb-0.1.0/tests/core/test_kernel_regressions.py +695 -0
- ipinb-0.1.0/tests/core/test_late_iopub.py +406 -0
- ipinb-0.1.0/tests/core/test_notebook_mirror.py +410 -0
- ipinb-0.1.0/tests/core/test_notebook_mirror_writer.py +315 -0
- ipinb-0.1.0/tests/core/test_notebook_publisher.py +307 -0
- ipinb-0.1.0/tests/core/test_notebook_publisher_e2e.py +150 -0
- ipinb-0.1.0/tests/core/test_output_formatting.py +616 -0
- ipinb-0.1.0/tests/core/test_output_noise.py +181 -0
- ipinb-0.1.0/tests/core/test_owner_watchdog.py +40 -0
- ipinb-0.1.0/tests/core/test_packaging_namespace.py +38 -0
- ipinb-0.1.0/tests/core/test_platform_support.py +39 -0
- ipinb-0.1.0/tests/core/test_plugin_kernel_integration.py +154 -0
- ipinb-0.1.0/tests/core/test_portable_uv.py +143 -0
- ipinb-0.1.0/tests/core/test_references.py +80 -0
- ipinb-0.1.0/tests/core/test_self_wheel.py +104 -0
- ipinb-0.1.0/tests/core/test_session.py +573 -0
- ipinb-0.1.0/tests/core/test_smoke.py +15 -0
- ipinb-0.1.0/tests/core/test_ssh_transport.py +1605 -0
- ipinb-0.1.0/tests/core/test_timing.py +48 -0
- ipinb-0.1.0/tests/core/test_toml.py +59 -0
- ipinb-0.1.0/tests/core/test_transport_contract.py +593 -0
- ipinb-0.1.0/tests/core/test_transport_staging.py +71 -0
- ipinb-0.1.0/tests/core/test_tunnel.py +226 -0
- ipinb-0.1.0/tests/release_smoke.py +77 -0
- ipinb-0.1.0/tests/runtime/test_bootstrap.py +115 -0
- ipinb-0.1.0/tests/runtime/test_builtin_agents_md.py +171 -0
- ipinb-0.1.0/tests/runtime/test_builtin_autoreload.py +29 -0
- ipinb-0.1.0/tests/runtime/test_builtin_dataframe_display.py +74 -0
- ipinb-0.1.0/tests/runtime/test_builtin_http_user_agent.py +131 -0
- ipinb-0.1.0/tests/runtime/test_builtin_image_outputs.py +231 -0
- ipinb-0.1.0/tests/runtime/test_builtin_import_guidance.py +144 -0
- ipinb-0.1.0/tests/runtime/test_builtin_pdb.py +266 -0
- ipinb-0.1.0/tests/runtime/test_builtin_pip_guidance.py +117 -0
- ipinb-0.1.0/tests/runtime/test_builtin_project_import.py +54 -0
- ipinb-0.1.0/tests/runtime/test_builtin_shell_history.py +161 -0
- ipinb-0.1.0/tests/runtime/test_context.py +38 -0
- ipinb-0.1.0/tests/runtime/test_environment.py +43 -0
- ipinb-0.1.0/tests/runtime/test_formatters.py +43 -0
- ipinb-0.1.0/tests/runtime/test_import_path.py +43 -0
- ipinb-0.1.0/tests/runtime/test_magics.py +234 -0
- ipinb-0.1.0/tests/runtime/test_model_output.py +89 -0
- ipinb-0.1.0/tests/runtime/test_path_context.py +147 -0
- ipinb-0.1.0/tests/runtime/test_plugin_api.py +451 -0
- ipinb-0.1.0/tests/runtime/test_plugin_authoring_skill.py +315 -0
- ipinb-0.1.0/tests/runtime/test_plugin_catalog.py +477 -0
- ipinb-0.1.0/tests/runtime/test_plugin_discovery.py +1189 -0
- ipinb-0.1.0/tests/runtime/test_plugin_kernel.py +1104 -0
- ipinb-0.1.0/tests/runtime/test_plugin_server_deps.py +464 -0
- ipinb-0.1.0/tests/runtime/test_project.py +71 -0
- ipinb-0.1.0/tests/runtime/test_shell_history.py +214 -0
- ipinb-0.1.0/tests/runtime/test_smoke.py +76 -0
- ipinb-0.1.0/tests/server/conftest.py +31 -0
- ipinb-0.1.0/tests/server/test_builtin_harness_context.py +581 -0
- ipinb-0.1.0/tests/server/test_builtin_inline_python_guidance.py +269 -0
- ipinb-0.1.0/tests/server/test_builtin_shell_history.py +275 -0
- ipinb-0.1.0/tests/server/test_builtin_view_image.py +184 -0
- ipinb-0.1.0/tests/server/test_harness_bridge.py +1305 -0
- ipinb-0.1.0/tests/server/test_harness_environment.py +85 -0
- ipinb-0.1.0/tests/server/test_integration.py +140 -0
- ipinb-0.1.0/tests/server/test_main.py +1270 -0
- ipinb-0.1.0/tests/server/test_multi_project_plugins.py +91 -0
- ipinb-0.1.0/tests/server/test_narration_doctor.py +140 -0
- ipinb-0.1.0/tests/server/test_notebook_channel.py +215 -0
- ipinb-0.1.0/tests/server/test_plugin_resolver.py +145 -0
- ipinb-0.1.0/tests/server/test_plugin_server_integration.py +53 -0
- ipinb-0.1.0/tests/server/test_plugin_tools.py +251 -0
- ipinb-0.1.0/tests/server/test_rollout_narration.py +219 -0
- ipinb-0.1.0/tests/server/test_server.py +261 -0
- ipinb-0.1.0/tests/server/test_session_trace.py +531 -0
- ipinb-0.1.0/tests/server/test_shell_forwarding.py +275 -0
- ipinb-0.1.0/tests/test_cell_export.py +344 -0
- ipinb-0.1.0/tests/test_consolidated_tools.py +233 -0
- ipinb-0.1.0/tests/test_debugger.py +715 -0
- ipinb-0.1.0/tests/test_execution_content_errors.py +44 -0
- ipinb-0.1.0/tests/test_execution_registry.py +1061 -0
- ipinb-0.1.0/tests/test_execution_transports.py +286 -0
- ipinb-0.1.0/tests/test_namespace.py +47 -0
- ipinb-0.1.0/tests/test_package_boundaries.py +311 -0
- ipinb-0.1.0/tests/test_resource_registry.py +609 -0
|
@@ -0,0 +1,1844 @@
|
|
|
1
|
+
# Breaking Changes
|
|
2
|
+
|
|
3
|
+
## 2026-10-03: PyPI distribution is `ipinb`
|
|
4
|
+
|
|
5
|
+
IPi is published to PyPI as `ipinb` starting with version 0.1.0. Python imports, the `ipi` command, configuration keys, plugin entry-point groups, and notebook metadata stay unchanged. The PyPI project named `ipi` belongs to unrelated software.
|
|
6
|
+
|
|
7
|
+
**Migration:** Replace package requirements such as `ipi[mcp] @ git+...` with `ipinb[mcp]` or a pinned `ipinb[mcp]==0.1.0`. Git installations use the new requirement name with the same repository URL. Reinstall native integrations with `uvx --isolated --from 'ipinb[mcp]' ipi install --provider codex --replace` after reviewing the existing configuration. Kernel overlays, version reporting, remote wheel staging, and notebook publisher wheels now resolve `ipinb`.
|
|
8
|
+
|
|
9
|
+
## 2026-10-03: Optional asynchronous Codex startup
|
|
10
|
+
|
|
11
|
+
`install-hooks --async-startup` is an opt-in Codex path for hosts that accept messages while IPi prepares. SessionStart and SubagentStart forwarding runs in the background with a positive 300-second bridge readiness wait. Existing synchronous installation remains the default. Requires typed-agent-hooks 0.2.4 or newer. No migration is needed unless choosing asynchronous startup.
|
|
12
|
+
|
|
13
|
+
## 2026-09-25: Portable native host installation
|
|
14
|
+
|
|
15
|
+
`ipi install` now configures MCP startup and forwarding hooks together for
|
|
16
|
+
Codex and Claude Code. Project scope is the default; `--scope user` selects
|
|
17
|
+
personal configuration. Existing `install-hooks` remains available when MCP
|
|
18
|
+
startup is managed separately. No running server or kernel is restarted.
|
|
19
|
+
|
|
20
|
+
The installer refuses to overwrite a different `ipi` MCP entry unless
|
|
21
|
+
`--replace` is explicit. Review that entry before replacing it. Local/editable
|
|
22
|
+
IPi installations require an explicit portable `--requirement`; Git and index
|
|
23
|
+
installs derive a pinned requirement from installed metadata.
|
|
24
|
+
|
|
25
|
+
## 2026-09-24: Concise Markdown submission receipts
|
|
26
|
+
|
|
27
|
+
- `markdown` now returns only `{"kind":"markdown","cell_id":"..."}` for new submissions and identical retries. It no longer repeats the submitted `source` or its metadata in the write response.
|
|
28
|
+
- Call `read_cell(cell_id)` when the retained Markdown source, origin, creation time, request ID, or revision is needed. Large sources remain available through the resource URI returned by `read_cell`.
|
|
29
|
+
|
|
30
|
+
## 2026-09-24: Native Claude provenance and narration
|
|
31
|
+
|
|
32
|
+
- Claude MCP calls now use the exact `claudecode/toolUseId` metadata and native hook `prompt_id` to retain their originating session and prompt. Prompt groups are explicitly named `prompt:<id>`; Claude's distinct display turn UUID is not substituted for its prompt ID. Subagent origins remain separate from the parent's trace.
|
|
33
|
+
- Kernel environments expose `AGENT_SESSION_PROVIDER` and `AGENT_SESSION_ID` for either harness. Codex retains `CODEX_THREAD_ID`; Claude clears inherited Codex attribution. Use the neutral pair in shared tooling.
|
|
34
|
+
- Claude `MessageDisplay` batches become notebook narration, with retry-safe assembly and no duplicate Stop final message. Retained execution cells now have a stable prompt parent, including the first cell before a prompt hook could be mirrored. No tool-call migration is required.
|
|
35
|
+
- Requires `typed-agent-hooks==0.2.2`. The redundant root `CLAUDE.md` link is removed; Claude Code 2.1.277 or newer reads `AGENTS.md` natively.
|
|
36
|
+
|
|
37
|
+
## 2026-09-21: Lean Python cell reads
|
|
38
|
+
|
|
39
|
+
- Remove `include_source` from every `read_cell` call. The argument is no longer accepted. Python replies always contain state and output without `source`, `origin`, `created_at`, or `request_id`, matching the former `include_source=False` behavior.
|
|
40
|
+
- These fields remain stored internally. Retrieve retained Python source through the MCP resource `ipi://cells/{cell_id}/source` when needed. Markdown reads still include source and metadata. Observation windows, caller-owned cursors, cancellation, and output artifact metadata are unchanged.
|
|
41
|
+
|
|
42
|
+
## Per-cell replay exports
|
|
43
|
+
|
|
44
|
+
- Completed Python cells expose `files` when `read_cell(..., include_artifact_metadata=True)` is requested: raw `input.py` and dependency-selected PEP 723 `replay.py`. These are additive; the existing `source` JSON resource and lean polling are unchanged.
|
|
45
|
+
- File resource bodies are Python text. Clients may read their MCP URIs or request host paths with `include_artifact_metadata=True`. The replay's diagnostics describe limitations; export failures never turn a successful cell into a failed execution. No migration is required.
|
|
46
|
+
|
|
47
|
+
## Per-execution debugger (feature/per-cell-debugger)
|
|
48
|
+
|
|
49
|
+
- Adds `ipdb(kernel_id, command, cell_id=None, yield_time_ms=1000, request_id=None)`. Kernel identity is always required; cell identity additionally targets a particular pause or failed execution. This does not restore the retired `pdb` tool or its implicit active target.
|
|
50
|
+
- Unhandled Python exceptions finish as before, with an optional debugger inspection hint. `breakpoint()` and configured source breakpoints can now pause a live execution for stepping. `exceptions raised` opts into stops before exception handling; `exceptions off` restores normal handling.
|
|
51
|
+
- A new ordinary execution to the same kernel discards existing postmortem inspection and aborts existing live pauses, allowing cleanup before admitting the new work. Already-running siblings are unaffected. Deliberately configured breakpoints persist until cleared or the kernel closes.
|
|
52
|
+
- Debugger commands may yield with a durable `result_uri`. Read that resource or the cell transcript to recover completion; do not repeat a yielded command. Notebook debugger transcripts remain attached to their original cells.
|
|
53
|
+
|
|
54
|
+
## 2026-09-15: Consolidated notebook tools and one execution runtime
|
|
55
|
+
|
|
56
|
+
- Replace `wait(cell_id, ...)` with `read_cell(cell_id, yield_time_ms=10000, ...)`. `read_cell` defaults to an immediate read (`yield_time_ms=0`); Markdown never waits. The original source-inclusion switch was removed on 2026-09-21 as described above. Cursor and immutable artifact semantics are unchanged.
|
|
57
|
+
- Replace `show(cell_id, caption)` and `publish_notebook(kernel_id)` with `publish(kernel_id, cells={cell_id: caption})`. The optional map highlights terminal Python cells in the full live notebook; it does not filter or rerun cells. All selected cells must belong to that kernel. Null captions highlight without text; omitted cells retain their existing highlights. Publishing without a map leaves highlights unchanged and supports empty notebooks. Publication creates or repairs the live URL and retains the kernel until close or owner exit.
|
|
58
|
+
- Bash remains available but is disabled by default. Set `IPI_MCP_ENABLE_BASH_TOOL=1` before server startup to expose it. Remove `tools.bash` from configuration; the environment variable is the only opt-in. Remove the obsolete `tools.auto_create_kernel_for_python` field; use explicit kernel IDs and `server.prewarm_kernel` for optional startup provisioning.
|
|
59
|
+
- Remove `request_id` from `list_kernels` and `list_cells`; those arguments were ignored. Retry keys remain supported on creation, execution, and Markdown submission.
|
|
60
|
+
- The old `RuntimeManager`, `ToolRuntime`, implicit-kernel dispatchers, kernel-pool/worker lifecycle, logical-session registry, and skill-reading tool helpers are removed. Internal consumers and hook routing use the explicit `ExecutionRuntime`/`Registry` path. Direct importers of those internal modules must migrate. The current kernel implementation, transports, archival records, publication, and plugin APIs remain supported.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
This document records user-visible breaking changes made during the MCP-first
|
|
64
|
+
redesign. Entries describe the old contract, the replacement, the reason, and
|
|
65
|
+
the required migration.
|
|
66
|
+
|
|
67
|
+
## Unreleased MCP-First Redesign
|
|
68
|
+
|
|
69
|
+
### Compact cell IDs, cursors, and artifact references
|
|
70
|
+
|
|
71
|
+
New cell IDs use one persisted registry-wide counter: `c1`, `c2`, ..., shared by Python, Bash, markdown, and browser executions across all kernels. Allocation never reuses a handle after close, expiry, failed acceptance, or reopening the same registry. Retained IDs are not renamed. Like kernel IDs, cell IDs are scoped to their originating registry; an independent registry can issue the same IDs. Cell lookup tools still take `cell_id` alone.
|
|
72
|
+
|
|
73
|
+
Output cursors now have the form `c1:17`; kernel-list cursors use `k:50`, and cell-list cursors use `k0:50`. They remain bound to their resource and page type. Treat cursors as opaque: pass the returned `next_cursor` unchanged. Previously retained cursors using `output:`, `kernels:`, or `cells:` prefixes must be discarded; omit the cursor to replay from the beginning and obtain a new one.
|
|
74
|
+
|
|
75
|
+
Large source and event references now contain only `uri` and `bytes` by default. Read the URI through MCP resources to retrieve the full JSON body. Call `read_cell(cell_id=..., include_artifact_metadata=True)` when filesystem paths, full SHA-256 checksums, and MIME types are needed. Consumers that previously accessed `artifact`, `sha256`, or `mime_type` directly must opt into metadata or use the resource URI. Full content hashes and immutable artifact files remain intact internally; authentication and upstream protocol IDs are unchanged.
|
|
76
|
+
|
|
77
|
+
### Compact ordered output blocks
|
|
78
|
+
|
|
79
|
+
Execution results expose `output.blocks` instead of `output.events`. Adjacent stdout or stderr writes become one `{"type":"stdout","text":"..."}` or `{"type":"stderr","text":"..."}` block. Stream blocks omit transport sequence numbers and the nested content wrapper. Rich outputs, control events, and large-event artifact references retain their event identity and content. Update programmatic consumers to the block schema; the MCP output schema describes both variants.
|
|
80
|
+
|
|
81
|
+
The immutable event log and caller-owned cursors are unchanged. A page cursor advances over the original events consumed, independently of the number of blocks returned. Response limits apply to compact blocks; stream ordering, exact text, replay, and image delivery are preserved.
|
|
82
|
+
|
|
83
|
+
### Structured-only execution results, compact kernel IDs, and FastMCP 4
|
|
84
|
+
|
|
85
|
+
Execution tools return their JSON payload once in `structuredContent`. The duplicate JSON text block is removed; `content` is empty unless it carries images. The MCP output schema describes the result, and the host's standard MCP handling applies. Image MIME payloads are retained as artifacts instead of also embedding their bytes in structured output.
|
|
86
|
+
|
|
87
|
+
Kernel IDs are unpadded registry-local counters: `k0`, `k1`, ..., `k11`. Allocation persists in the registry database and does not recycle IDs after close, expiry, or owner restart. An independent registry begins at `k0`; IDs are not globally unique and must be used with their originating registry. Existing retained IDs are not renamed. The subsequent cell-ID change is described above.
|
|
88
|
+
|
|
89
|
+
The MCP extra now pins FastMCP 4.0.3 and MCP SDK 2.2.0. Reinstall the extra to upgrade the environment. Python protocol-model attributes now use snake_case, and request metadata is a dictionary; JSON wire aliases remain governed by MCP. Codex itself requires no changes.
|
|
90
|
+
|
|
91
|
+
### Explicit kernels and concurrent retained executions
|
|
92
|
+
|
|
93
|
+
The stdio and HTTP MCP APIs now share one explicit resource contract. `ipython`, `markdown`, publication, Bash, and plugin tools require `kernel_id`; observation and interruption require `cell_id`. Replace `switch_kernel` and `list_active_kernels` with explicit IDs and `list_kernels`. Replace `kill_session` with `close_kernel` for each kernel whose state you intend to discard. HTTP no longer accepts a logical `session_id`.
|
|
94
|
+
|
|
95
|
+
Independent submissions to one kernel overlap by default and share globals. Observe terminal success before submitting dependent code. `yield_time_ms=0` always returns a handle. Observation cancellation leaves accepted work running; use explicit best-effort interruption for cancellation. Output cursors belong to each reader and can replay retained output. Optional `request_id` deduplicates identical accepted create, Python, Bash, and markdown operations; reuse with another payload is rejected.
|
|
96
|
+
|
|
97
|
+
Live notebook publication remains executable and receives later updates. Closing its kernel or exiting the server ends the endpoint. Published kernels are exempt from idle expiry. Retained records and retry keys survive under the registry policy, while process restart truthfully marks unfinished work and live state lost.
|
|
98
|
+
|
|
99
|
+
The old global PDB MCP plugin is not exposed by this release. Its behavior is incompatible with independent concurrent cells; the optional per-cell debugger validation gate must pass before it is advertised again. Dependency upgrades outside the validated ipykernel/IPython pins fail setup explicitly until the local concurrency repair is revalidated.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
### Codex Apps and web search move out of IPi
|
|
103
|
+
|
|
104
|
+
**Status:** Implemented.
|
|
105
|
+
|
|
106
|
+
**Change:** The `ipi.codex_apps` built-in, `ipi_codex_tools` generated package, authenticated host bridge, connector file staging, and Codex-backed `web_search` helper are removed.
|
|
107
|
+
|
|
108
|
+
**Why:** Connected services and general web search are independent agent tools, not notebook runtime responsibilities. Keeping their authentication, discovery, transport, code generation, and file handling inside IPi coupled notebook startup and remote-kernel lifecycle to unrelated services.
|
|
109
|
+
|
|
110
|
+
**Migration:** Install the standalone `codex-apps` and `web-search` toolfuncs. Import them through `import toolfuncs as tools`, then call flat generated operations such as `tools.codex_apps.google_drive_search(...)` or `tools.web_search.search(...)`. Run `codex-apps login` once to authenticate its dedicated ChatGPT account; `web-search` uses the normal active `codex login` profile.
|
|
111
|
+
|
|
112
|
+
### Script-only path imports are delegated to toolfuncs
|
|
113
|
+
|
|
114
|
+
**Status:** Implemented.
|
|
115
|
+
|
|
116
|
+
**Change:** `ipi.import_path` is now the public `toolfuncs.import_path` function. It imports one local `.py` file or extensionless script and derives the module name from the filename. Optional PEP 723 requirements are installed into the running kernel before import; `dependency_installer=` may replace the default pip operation with a callable accepting `list[str]` and returning `None`. IPi's package, project, distribution-artifact, fsspec URL, `module=`, and `storage_options=` import paths are removed.
|
|
117
|
+
|
|
118
|
+
**Why:** Importing a standalone script and preparing its optional inline dependencies is the reusable operation shared with toolfuncs. Package acquisition and arbitrary project installation made IPi own a second, much broader source loader that no current toolfunc contract requires.
|
|
119
|
+
|
|
120
|
+
**Migration:** Keep calls that pass one local script path. Remove `module=` and `storage_options=`. Install packages, projects, wheels, and source distributions through the environment manager, then use normal Python imports. Materialize remote scripts locally before calling `import_path`. Pass `dependency_installer=` only when the caller must replace installation into the current kernel environment.
|
|
121
|
+
|
|
122
|
+
### Native harness sessions replace IPi artifact IDs in model-facing provenance
|
|
123
|
+
|
|
124
|
+
**Status:** Implemented.
|
|
125
|
+
|
|
126
|
+
**Change:** Harness grounding now emits the exact native Codex or Claude Code session identity in an `agent-session` block. Stateful runtime output and notebook publication no longer expose the durable IPi artifact-directory ID as a standalone `session` attribute. They expose only the explicitly labeled `IPi artifact root` path. Stateless HTTP-family transports still return their required short-lived routing handle as `logical-session-id` instead of the generic `session` attribute.
|
|
127
|
+
|
|
128
|
+
**Why:** Durable IPi artifact IDs such as `8212394626483155999-558398bc` identify `.agents/sessions/...` storage, not Codex or Claude history. Advertising that value as a generic session caused agents to record it as native session provenance, which cannot be resolved by harness-history tools.
|
|
129
|
+
|
|
130
|
+
**Migration:** Use the `agent-session` block's `provider` and `id` attributes for note provenance and harness-history lookup. Use `IPi artifact root` to inspect notebook artifacts; an ID embedded in that path is storage metadata. Stateless clients must read `logical-session-id` from `runtime-state` and continue passing it unchanged as the `session_id` tool argument.
|
|
131
|
+
|
|
132
|
+
### Notebook frontend dependencies are explicitly shared
|
|
133
|
+
|
|
134
|
+
**Status:** Implemented.
|
|
135
|
+
|
|
136
|
+
**Change:** Strict configuration now accepts `publication.shared_dependencies`, a list of PEP 508 requirements installed in both each matching kernel and its lazily created Notebook 7 publisher. A caller-supplied `additional_dependencies` requirement for the same distribution wins on both sides. Plotly is no longer a built-in member of the `publish` extra.
|
|
137
|
+
|
|
138
|
+
**Why:** Packages such as Plotly and anywidget contain browser-side JupyterLab code as well as kernel-side Python code. Installing them in only one of IPi's isolated environments either cannot create the output or cannot render it. Declaring the shared set once makes that two-environment requirement explicit without inspecting `%pip`, mutating live publishers, or hard-coding optional visualization libraries into IPi.
|
|
139
|
+
|
|
140
|
+
**Migration:** Move Plotly, anywidget, and any other distribution whose JupyterLab extension is required from `kernel.additional_dependencies` to `publication.shared_dependencies`. Keep packages built on a generic shared provider, such as ipymolstar on anywidget, in `kernel.additional_dependencies`. Recreate the kernel and publication after changing or upgrading a shared provider; ordinary live kernel package installs need no publication restart.
|
|
141
|
+
|
|
142
|
+
### Runtime-only state uses temporary storage and notebook publication dependencies are lazy
|
|
143
|
+
|
|
144
|
+
**Status:** Implemented.
|
|
145
|
+
|
|
146
|
+
**Change:** The generated `ipi_codex_tools` project, Codex Apps bridge staging, connector-file staging, Jupyter configuration, Jupyter runtime data, IPython configuration, collaboration state, and exact-running-IPi publication wheel now live in process-owned operating-system temporary directories. Codex Apps uses one authenticated loopback TCP server for local and remote kernels; it no longer creates request, response, tombstone, heartbeat, or stale-root-sweep files. `generated_package_dir` and `stale_root_ttl_s` are removed and now fail directly when present.
|
|
147
|
+
|
|
148
|
+
Codex configuration and auth are loaded once per bridge lifetime, outside the event loop. An explicit inventory refresh reloads them. Durable auth updates still use `fsync`, but generated packages, connector staging, and other disposable files do not. Repeated durability requests for the same notebook artifact are coalesced to one pending flush, plus at most one follow-up when a new generation arrives during an active flush.
|
|
149
|
+
|
|
150
|
+
The base distribution now includes `nbformat`, which core notebook persistence requires. Notebook 7, Jupyter collaboration, YDoc, PyCRDT, and Tornado are in the `publish` extra instead of the MCP host extra. The first `publish_notebook` call builds one process-cached wheel from the exact running `ipi` package and starts the publisher with `uv run --isolated --no-project --no-config --with "ipi[publish] @ file://..."`. Concurrent first publications share that build. The child inherits `UV_CACHE_DIR`, but all Jupyter state directories are redirected into the publication's private temporary root.
|
|
151
|
+
|
|
152
|
+
The default local-plugin server-dependency cache now uses this precedence: `IPI_SERVER_DEPS_CACHE`, then `$UV_CACHE_DIR/ipi/server-deps`, then `$XDG_CACHE_HOME/ipi/server-deps`, then `~/.cache/ipi/server-deps`. Slow runtime boundaries emit value-free warnings at one second or longer, naming only a fixed operation, fixed phase, and elapsed milliseconds.
|
|
153
|
+
|
|
154
|
+
Every IPi-managed local or SSH kernel now sets `HistoryManager.hist_file=:memory:`. The IPi notebook and session log remain the durable execution record; kernels no longer create or share `~/.ipython/profile_default/history.sqlite`. A recorded kernel-start failure raises one internal `KernelLaunchError` instead of returning content that can be mistaken for a live kernel. Failed prewarm state is discarded, automatic execution stops after that one failed launch, and cancellation uses an operation-local launch-abort signal that does not poison the next attempt. Cancelled launch ownership is retained until the worker has closed any unpublished child and transport state.
|
|
155
|
+
|
|
156
|
+
**Why:** Runtime-only metadata was doing small-file creation, polling, sweeping, SQLite locking, and synchronous durability work on user homes and shared filesystems. On Lustre and NFS, metadata latency dominates despite abundant CPU and RAM. Temporary local state, in-memory kernel history, and a single socket remove that filesystem from the request path, while the UV cache remains durable because reusing resolved dependencies is valuable. Treating a failed launch as ordinary content could also make one `ipython` call start a second kernel and wait through the readiness timeout twice.
|
|
157
|
+
|
|
158
|
+
**Migration:** Delete `generated_package_dir` and `stale_root_ttl_s` from every `ipi.codex_apps` plugin configuration. Set `UV_CACHE_DIR` to node-local storage, or set `IPI_SERVER_DEPS_CACHE` explicitly, when the default home or XDG cache is remote. Keep `uv` available for the first notebook publication; a cold publish cache may require dependency network access. Install `ipi[publish]` explicitly only when a preinstalled publisher environment is useful. After changing Codex config or auth in a running host, call the generated tools refresh or restart IPi to reload it immediately. Code that called IPython's cross-process persistent history must use the IPi notebook or session artifacts instead. Callers must treat a failed `create_kernel` response as a tool error rather than parsing a normal content result.
|
|
159
|
+
|
|
160
|
+
### Structured turn traces replace narration Markdown cells
|
|
161
|
+
|
|
162
|
+
**Status:** Implemented.
|
|
163
|
+
|
|
164
|
+
**Change:** The `ipi.reasoning_forwarding` built-in and `ipi-reasoning` Markdown cells are removed. `ipi.session_trace` records each user prompt in a structured raw `session-turn` anchor and adds chronological `session-activity` children for the associated agent messages and Codex reasoning summaries. Only immediately consecutive reasoning updates share an activity. Portable tools without a richer notebook cell use a stable `tool-activity` child keyed within its turn. Every top-level member receives a notebook-wide `activity_index`; late work is inserted at its turn's tail. Executions, authored Markdown, host-shell records, and suspension transcripts name their exact parent with `metadata.ipi.parent_id`. The Notebook 7 extension presents this flat notebook log as nested turn, user, agent, activity, and execution disclosures, and renders prompt and agent text as untrusted Markdown through Jupyter's rendermime registry. Every raw trace source remains a readable plain-text fallback.
|
|
165
|
+
|
|
166
|
+
**Why:** One Markdown cell per narration sweep consumed excessive vertical space and made forwarded agent text look like deliberate notebook prose. A turn is the useful reading unit, but a single mutable cell per turn cannot preserve the actual chronology between narration, executions, and tools. A flat cell graph with explicit parent IDs and one total activity order preserves chronology and notebook interoperability while letting the extension render the hierarchy without reparenting Jupyter widgets.
|
|
167
|
+
|
|
168
|
+
**Migration:** Remove `ipi.reasoning_forwarding` from any built-in plugin overrides and use `ipi.session_trace`. Consumers that inspect notebooks should find raw cells with `metadata.ipi.kind` equal to `session-turn`, `session-activity`, or `tool-activity`; group them by `session_id` and `turn_id`, and resolve `parent_id` by the notebook cell model ID. Prompt, reasoning, and message records retain `metadata.ipi.items` entries with `role`, `kind`, and `text`. The former single-cell `ipi-session-trace` representation is not supported.
|
|
169
|
+
|
|
170
|
+
### Explicit per-kernel publication replaces the global notebook service
|
|
171
|
+
|
|
172
|
+
**Status:** Implemented.
|
|
173
|
+
|
|
174
|
+
**Change:** `publish_notebook` now lazily publishes one existing notebook and
|
|
175
|
+
returns its complete tokenized Cloudflare quick-tunnel URL. Stateful mode takes
|
|
176
|
+
`kernel_id`; stateless mode takes `session_id`. A successful publication starts
|
|
177
|
+
one attached Notebook 7 child and one tunnel, retains the kernel past kernel and
|
|
178
|
+
logical-session idle expiry, and lasts until the kernel is killed or its owning
|
|
179
|
+
IPi process exits. The `ipi notebook-service` command, global dashboard,
|
|
180
|
+
per-user rendezvous, registration client/API, symlink farm, GC and
|
|
181
|
+
re-registration machinery are removed.
|
|
182
|
+
|
|
183
|
+
The older automatic per-kernel Jupyter sidecar design is also gone. The entire
|
|
184
|
+
`[jupyter]` configuration table is removed and now fails validation; live
|
|
185
|
+
Notebook 7 processes exist only after an explicit publication call.
|
|
186
|
+
|
|
187
|
+
**Why:** Notebook sharing is sparse and explicit. Kernel ownership already
|
|
188
|
+
provides the necessary lifetime boundary, so a machine-wide discovery and
|
|
189
|
+
registration system made every launch and deployment more complex without
|
|
190
|
+
improving the selected-notebook workflow.
|
|
191
|
+
|
|
192
|
+
**Migration:** Delete user services and wrappers that launch
|
|
193
|
+
`ipi notebook-service`, including dashboard URL lookup scripts. Remove any
|
|
194
|
+
service runtime-directory configuration and every `[jupyter]` table. Call
|
|
195
|
+
`publish_notebook(kernel_id="kN")` or
|
|
196
|
+
`publish_notebook(session_id="sNNNNNNNNN")` when a notebook must be shared, and
|
|
197
|
+
treat the returned URL as a bearer credential. Install `cloudflared`; the tool
|
|
198
|
+
fails rather than returning a localhost fallback when no public tunnel can be
|
|
199
|
+
created.
|
|
200
|
+
|
|
201
|
+
### Session termination uses `kill_session` in every transport
|
|
202
|
+
|
|
203
|
+
**Status:** Implemented.
|
|
204
|
+
|
|
205
|
+
**Change:** `kill_session` is the terminal session lifecycle tool in every
|
|
206
|
+
transport. In stateful stdio, `kill_session()` cancels active work and prewarm,
|
|
207
|
+
closes every kernel, and permanently ends the implicit notebook session; later
|
|
208
|
+
notebook calls fail. In stateless HTTP-family transports,
|
|
209
|
+
`kill_session(session_id)` removes and closes that logical session and
|
|
210
|
+
invalidates its ID. The stateless-only `close_session` tool is removed.
|
|
211
|
+
|
|
212
|
+
**Why:** Session termination is destructive and permanent in both ownership
|
|
213
|
+
modes. One explicit verb and one terminal contract avoid a transport-specific
|
|
214
|
+
alias that described the same lifecycle less precisely.
|
|
215
|
+
|
|
216
|
+
**Migration:** Replace `close_session(session_id=...)` with
|
|
217
|
+
`kill_session(session_id=...)` for stateless clients. Stateful clients may call
|
|
218
|
+
`kill_session()` only as their final notebook operation.
|
|
219
|
+
|
|
220
|
+
### Harness hook configuration is code-first
|
|
221
|
+
|
|
222
|
+
**Status:** Implemented.
|
|
223
|
+
|
|
224
|
+
**Change:** IPi now delegates hook configuration directly to `typed_agent_hooks.fastmcp.ForwardingHooks`. The removed TAH hookset manifests, compiler, loader, and global `typed-agent-hooks` CLI are no longer part of IPi's installation path. Forwarding commands invoke the dedicated `tah-fastmcp-forward` Cyclopts function and carry the stable `--managed-app ipi` ownership marker.
|
|
225
|
+
|
|
226
|
+
**Why:** The executable TAH application now owns its dependency environment, provider metadata, installation behavior, and Python API in one code-first definition. Keeping IPi's parallel hookset construction would restore the duplicate configuration model that TAH removed.
|
|
227
|
+
|
|
228
|
+
**Migration:** Remove existing IPi hook handlers containing `--hookset-name ipi`, then run `ipi install-hooks --provider codex --scope <project|user>` and `ipi install-hooks --provider claude_code --scope <project|user>` as applicable. Restart the harness after reinstalling. Existing entries are deliberately not interpreted as the new format.
|
|
229
|
+
|
|
230
|
+
### Kernel idle reaping defaults to one hour and published kernels are retained
|
|
231
|
+
|
|
232
|
+
**Status:** Implemented.
|
|
233
|
+
|
|
234
|
+
**Change:** `server.kernel_idle_timeout_s` defaults to `3600`. An explicitly
|
|
235
|
+
published kernel is exempt from that reaper, and its stateless manager is exempt
|
|
236
|
+
from logical-session TTL expiry, until explicit kill or IPi process exit.
|
|
237
|
+
|
|
238
|
+
**Why:** Ordinary forgotten kernels should release resources, while a notebook
|
|
239
|
+
that was deliberately shared must remain usable for the publication lifetime.
|
|
240
|
+
|
|
241
|
+
**Migration:** Set `kernel_idle_timeout_s = 0` under `[server]` to disable idle
|
|
242
|
+
reaping globally. Prefer `publish_notebook` when only a specific shared kernel
|
|
243
|
+
needs retention.
|
|
244
|
+
|
|
245
|
+
### Flat string results and typed model-facing handles
|
|
246
|
+
|
|
247
|
+
**Status:** Implemented.
|
|
248
|
+
|
|
249
|
+
**Change:** Agent-facing tools retain their string return contract, but their text now uses flat, non-nested, product-neutral XML-like delimiters. Compact facts that describe a block, such as its identity, kind, state, source, counts, and flags, live in opening-tag attributes. What the block says or delivers remains in Markdown or verbatim bodies, including explanations, source, logs, errors, and raw output. Small inventories are Markdown tables. The delimiters are not a strict XML contract. Opening and closing tags provide the block boundary without a separate length attribute. IPi reparses only the generated top-level blocks needed for incremental output delivery. Runtime inspection consolidates the former scalar `status`, `session-id`, `kernel-state`, and related blocks into one `runtime-state` block with descriptive attributes and a body for its substantive details.
|
|
250
|
+
|
|
251
|
+
Model-facing cells retain `cN`; live kernels now use `kN`, including
|
|
252
|
+
`switch_kernel(kernel_id="k1")`; cell-local outputs use `oN`; stateless HTTP
|
|
253
|
+
sessions use `s` plus nine random decimal digits. New session artifacts use the
|
|
254
|
+
same handles in `kernels/kN/outputs/cN/g-<hex>/oN-*`. Durable session directory
|
|
255
|
+
IDs, `g-<hex>` generation IDs, Jupyter IDs, and other external or internal
|
|
256
|
+
protocol IDs are unchanged.
|
|
257
|
+
|
|
258
|
+
**Why:** Flat semantic boundaries make mixed instructions, Markdown, and raw
|
|
259
|
+
payloads easy for an agent to distinguish without turning prompts into a
|
|
260
|
+
machine data protocol. Short type prefixes disambiguate local handles while
|
|
261
|
+
retaining the measured token behavior of decimal counters and random suffixes.
|
|
262
|
+
|
|
263
|
+
**Migration:** Treat result tags as descriptive boundaries, not strict XML. Read facts that describe a block from its opening-tag attributes and read what the block says or delivers from its body. Replace tab-record parsing with the corresponding top-level blocks and Markdown fields. Pass returned cell, kernel, and session handles unchanged; replace numeric `switch_kernel` arguments with `kN` strings. Follow returned artifact paths rather than constructing the former padded path components.
|
|
264
|
+
|
|
265
|
+
### Python execution uses fixed yielding and addressed cell controls
|
|
266
|
+
|
|
267
|
+
**Status:** Implemented.
|
|
268
|
+
|
|
269
|
+
**Change:** `ipython` is now `ipython(code)` and uses a fixed 5-second initial observation window. A still-running cell yields with `cell_id=cN` and status `yielded`. `wait` is now `wait(cell_id, yield_time_ms=10_000)`, `interrupt` requires `cell_id`, and `show` is now `show(cell_id, caption=None)`. Stateless forms additionally require `session_id`. `show` promotes a terminal whole cell only; output references and the `last` sentinel are removed. Explicit interruption returns status `interrupted` without a synthetic `TimeoutError`. Execution records expose their count, state, duration, cell handle, exit code, and post-mortem flag as `status` attributes; any changed cwd, `sys.path`, or environment values remain in the body.
|
|
270
|
+
|
|
271
|
+
**Why:** These names and continuation semantics match the agent harness's cooperative-yield UX. Explicit cell addressing prevents stale continuation calls from controlling the wrong execution, and whole-cell presentation removes an unnecessary output-addressing protocol.
|
|
272
|
+
|
|
273
|
+
**Migration:** Remove `timeout_s` from every `ipython` call. Read the returned `cell_id`, then call `wait(cell_id=..., yield_time_ms=...)` or `interrupt(cell_id=...)`. Replace `show(target=...)`, `cell_ref`, and `show_ref` handling with `show(cell_id=...)` and `cell_id`. Do not pass `last` or composite values such as `c7/o2`.
|
|
274
|
+
|
|
275
|
+
### Object-driven Agent Introspection Protocol and auto-context are removed
|
|
276
|
+
|
|
277
|
+
**Status:** Implemented.
|
|
278
|
+
|
|
279
|
+
**Change:** The Agent Introspection Protocol object model and the generic automatic context coordinator are deleted. IPi no longer inspects imports, new bindings, displayed values, or exceptions for `__agent_summary__`, `__agent_doc__`, resolver methods, synthesized dataclass or enum guidance, or inspection debt. The AIP tier model, well-known-object policy, suppression API, debugger suppression state, `ipi.auto_context` built-in, and public runtime enable/disable/status functions are removed. Generated Codex Apps modules no longer define `__agent_summary__`, and generated package schema 15 invalidates old stubs.
|
|
280
|
+
|
|
281
|
+
Structured context blocks remain, under neutral `ContextBlock` and `PersistedContextBlock` names, as the transport for explicitly authored semantic events. The built-in producers are AGENTS file discovery after path or cwd changes, written-image advice, `ModuleNotFoundError` guidance, and pip guidance. Each producer emits directly and owns its recurrence policy. Kernel cwd, `sys.path`, and environment deltas are captured by a separate private tracker. Host-side context providers, harness context, state-specific result notices, and session-history context-block deduplication are unchanged.
|
|
282
|
+
|
|
283
|
+
**Why:** Object inspection guessed relevance from ordinary Python activity and spent tokens on information already available through normal language knowledge and `help()`. It also coupled unrelated concerns: object metadata, exception scanning, context recurrence, plugin coordination, debugger behavior, and process-state tracking. Explicit semantic producers preserve the passive hints that carry hidden or local information while removing the speculative path and its policy machinery.
|
|
284
|
+
|
|
285
|
+
**Migration:** There is no compatibility layer or phased migration. Delete uses of the removed AIP dunders, resolver and formatting APIs, inspection registry, trigger scanners, auto-context registration and suppression APIs, and `ipi.auto_context` plugin ID. Use standard `help(...)` for object and generated-tool inspection. A plugin that owns genuinely hidden semantic information should emit a `ContextBlock` directly from the event where that information becomes relevant and should own its own recurrence rule.
|
|
286
|
+
|
|
287
|
+
### Kernel execution locations are per-kernel local paths or SSH URIs
|
|
288
|
+
|
|
289
|
+
**Status:** Implemented; pending user acceptance.
|
|
290
|
+
|
|
291
|
+
**Change:** `create_kernel(cwd=...)` now selects an immutable execution
|
|
292
|
+
location for that kernel. An ordinary absolute path remains local;
|
|
293
|
+
`ssh://[user@]host[:port]/absolute/path` creates a kernel at that absolute path
|
|
294
|
+
through non-interactive system OpenSSH. One server can own local kernels and
|
|
295
|
+
kernels on multiple SSH targets. Each SSH kernel owns its private control
|
|
296
|
+
master, five host-loopback Jupyter forwards, target supervisor, approximately
|
|
297
|
+
60-second owner lease, latest officially released portable `uv` verified with
|
|
298
|
+
its publisher checksum, and an exact staged build of the running IPi. Remote
|
|
299
|
+
projects require no host mirror. Remote kernels run built-in plugins only;
|
|
300
|
+
external project, user, and installed plugins are ignored with one redacted
|
|
301
|
+
warning and are not imported, executed, or staged.
|
|
302
|
+
|
|
303
|
+
Native Windows and WSL support, the `ipi-windows` and `ipi-remote` Jupyter
|
|
304
|
+
provisioners, path mapping, static command prefixes, Windows harness transport,
|
|
305
|
+
and every process-wide `IPI_KERNEL_*` selector have been removed. IPi now
|
|
306
|
+
supports Linux and macOS only. A removed variable fails startup rather than
|
|
307
|
+
being silently ignored.
|
|
308
|
+
|
|
309
|
+
**Why:** A process-wide launch prefix cannot represent mixed local/multi-host
|
|
310
|
+
kernels and does not own target Jupyter ports, authentication, staged runtime,
|
|
311
|
+
health, or cleanup. WSL path maps also required a host mirror and allowed host
|
|
312
|
+
plugin execution to be confused with target execution. The resource-owned
|
|
313
|
+
transport boundary makes target identity transactional and gives every acquired
|
|
314
|
+
host and target resource a bounded rollback and cleanup path. It is private but
|
|
315
|
+
is shaped for later native Docker and Kubernetes transports without treating
|
|
316
|
+
those systems as SSH command prefixes.
|
|
317
|
+
|
|
318
|
+
**Migration:** Remove all `IPI_KERNEL_*` variables and Windows/WSL provisioner
|
|
319
|
+
configuration. Run IPi on Linux or macOS. Keep local calls as
|
|
320
|
+
`create_kernel(cwd="/absolute/local/path")`; replace remote prefixes and path
|
|
321
|
+
maps with `create_kernel(cwd="ssh://user@host/absolute/target/path")`. Configure
|
|
322
|
+
aliases, keys, ports, `ProxyJump`, and host trust in OpenSSH, and verify the
|
|
323
|
+
connection works non-interactively before launch. Move required remote behavior
|
|
324
|
+
into built-ins or ordinary target project code; external IPi plugins do not run
|
|
325
|
+
for SSH kernels. Targets must provide `python3` 3.10 or newer for the isolated
|
|
326
|
+
session owner; the kernel environment itself continues to use the verified
|
|
327
|
+
portable `uv` and exact staged IPi build. The last reviewed pre-change lineage is
|
|
328
|
+
`origin/autopilot/ipi-http-session-ids@ae37c31502eb46c17e4df537f4f74babcccd49a2`.
|
|
329
|
+
|
|
330
|
+
### HTTP logical session IDs shrink to nine digits behind a session limit
|
|
331
|
+
|
|
332
|
+
**Status:** Implemented.
|
|
333
|
+
|
|
334
|
+
**Change:** The logical `session_id` returned by stateless HTTP `create_kernel`
|
|
335
|
+
is now an opaque fixed-width 9-digit decimal string (previously 18 digits).
|
|
336
|
+
Allocation is unique-by-retry: a candidate that collides with a live session or
|
|
337
|
+
a leftover `.agents/sessions/` directory is redrawn, `Session.create` and
|
|
338
|
+
`SessionRegistry.register` raise the new `SessionIdCollisionError` for those
|
|
339
|
+
collisions (`register` previously raised `ValueError`), and the new
|
|
340
|
+
`server.stateless_max_sessions` config (default 256; `0` disables) makes
|
|
341
|
+
`create_kernel` fail with a clear error instead of launching a kernel once that
|
|
342
|
+
many sessions are live.
|
|
343
|
+
|
|
344
|
+
**Why:** The ID is repeated in every session-bound tool call. Nine decimal
|
|
345
|
+
digits cost three tokens bare and four inside JSON tool arguments under both
|
|
346
|
+
benchmark tokenizers, versus six and seven for 18 digits. The $9 \times
|
|
347
|
+
10^8$-value space is sized for loud failure, not secrecy: at 100 live sessions
|
|
348
|
+
a stale ID hits a live one with probability about $10^{-7}$ per retry, so
|
|
349
|
+
expired or pre-restart IDs keep dying with `UnknownSessionError` instead of
|
|
350
|
+
aliasing another session. Authentication remains the HTTP token, never the ID.
|
|
351
|
+
The cap exists because an authorized client loop could otherwise fork-bomb the
|
|
352
|
+
host with kernel launches.
|
|
353
|
+
|
|
354
|
+
**Migration:** Treat session IDs as opaque strings of any width and retain the
|
|
355
|
+
exact value returned by `create_kernel`. Logical sessions never survive a
|
|
356
|
+
server restart, so there is no persisted-ID migration. Callers that caught
|
|
357
|
+
`ValueError` from `SessionRegistry.register` must catch
|
|
358
|
+
`SessionIdCollisionError`. Deployments expecting more than 256 concurrent
|
|
359
|
+
sessions must raise `server.stateless_max_sessions`.
|
|
360
|
+
|
|
361
|
+
### Codex Apps tool results carry file bytes, and kernels name themselves to HTTP
|
|
362
|
+
|
|
363
|
+
**Status:** Implemented.
|
|
364
|
+
|
|
365
|
+
**Change:** A Codex Apps tool call that returns a file now performs a host-side
|
|
366
|
+
download during the call. The stored copy lands in a `files` directory beside
|
|
367
|
+
the endpoint's bridge spools, each file reference in the result gains an
|
|
368
|
+
`__ipi_file__` entry, and the kernel receives it as a `CodexAppFile` exposing
|
|
369
|
+
`path`, `read_bytes()`, and `mime_type`. A result holding at least one file is a
|
|
370
|
+
`CodexAppResult` with a `files` tuple, and renders when it holds a single image.
|
|
371
|
+
Both are `dict` subclasses, so every key the connector returned still indexes as
|
|
372
|
+
before. A non-text content block returned alongside `structuredContent` is no
|
|
373
|
+
longer discarded; it appears under `__ipi_content__`. Separately, kernels now
|
|
374
|
+
install a `urllib.request` opener that sends `ipi/<version>` instead of the
|
|
375
|
+
stdlib default User-Agent.
|
|
376
|
+
|
|
377
|
+
**Why:** Connectors return a file as a credentialed URL that expires in minutes,
|
|
378
|
+
never as bytes, and nothing fetched it. Notebook code had to, but a kernel
|
|
379
|
+
environment carries no HTTP client unless the project depends on one, so code
|
|
380
|
+
fell back to `urllib.request` — whose exact `Python-urllib/<version>`
|
|
381
|
+
User-Agent the bot rules in front of those file hosts reject outright, with a
|
|
382
|
+
403 whose explanation is only in a response body `urlopen` raises away. The
|
|
383
|
+
result read as a permission error on the file. Two adjacent defects made the
|
|
384
|
+
same path worse: `structuredContent` silently dropped any content block beside
|
|
385
|
+
it, and content blocks were dumped in python mode, so a `ResourceLink` or
|
|
386
|
+
`EmbeddedResource` carried an `AnyUrl` that could not be serialized and failed
|
|
387
|
+
the whole call at the bridge with an unrelated-looking error.
|
|
388
|
+
|
|
389
|
+
**Migration:** None required for reading tool results. Code that downloaded
|
|
390
|
+
`download_url` itself still can — the URL is preserved — but should prefer
|
|
391
|
+
`read_bytes()`, which does not race the expiry. A failed fetch never fails the
|
|
392
|
+
call: `path` is `None` and `error` explains why, including the response body.
|
|
393
|
+
Files over 25 MiB are not stored. Set
|
|
394
|
+
`[plugins.config."ipi.http_user_agent"] user_agent` to choose a different
|
|
395
|
+
User-Agent, `""` to keep urllib's own, or disable `ipi.http_user_agent`
|
|
396
|
+
entirely; an explicit per-request header or a later `install_opener` already
|
|
397
|
+
takes precedence.
|
|
398
|
+
|
|
399
|
+
### The stdio server shuts down on SIGTERM and Ctrl-C
|
|
400
|
+
|
|
401
|
+
**Status:** Implemented.
|
|
402
|
+
|
|
403
|
+
**Change:** The stdio entrypoint now installs its own SIGINT and SIGTERM
|
|
404
|
+
handlers and, once serving has ended, runs `atexit` hooks, flushes, and exits
|
|
405
|
+
the process directly. HTTP transports are untouched, since uvicorn installs its
|
|
406
|
+
own handlers.
|
|
407
|
+
|
|
408
|
+
**Why:** Two defects met on this path. FastMCP installs no SIGTERM handler, so
|
|
409
|
+
stdio ran under Python's default disposition and the normal end of a Codex or
|
|
410
|
+
Claude Code session killed the process outright, skipping the lifespan
|
|
411
|
+
`finally` and every `atexit` hook. Ctrl-C did reach Python, but `asyncio.Runner`
|
|
412
|
+
claimed SIGINT and cancelled the main task instead, and that cancellation never
|
|
413
|
+
completed, so the server hung until something force-killed it. Together these
|
|
414
|
+
meant no owned cleanup operation ran on either stop path, which is why
|
|
415
|
+
generated package roots accumulated. Even after cleanup was made to run, the
|
|
416
|
+
interpreter still could not exit: anyio's worker threads are not daemons and
|
|
417
|
+
park on an unbounded queue, so `threading._shutdown` waited on them forever.
|
|
418
|
+
|
|
419
|
+
**Migration:** None required. A stop signal now completes teardown and exits 0.
|
|
420
|
+
Anything embedding IPi that installs its own SIGINT or SIGTERM handler before
|
|
421
|
+
`main()` keeps it; IPi only claims a signal still on its default disposition.
|
|
422
|
+
|
|
423
|
+
### Generated Codex Apps stubs document their parameters
|
|
424
|
+
|
|
425
|
+
**Status:** Implemented.
|
|
426
|
+
|
|
427
|
+
**Change:** Generated tool docstrings gain a Google-style `Args:` section built
|
|
428
|
+
from each JSON Schema property's `description`, declared choices, and `default`.
|
|
429
|
+
Signatures render `Literal[...]` for a declared `enum` or `const` when the value
|
|
430
|
+
list is small enough to stay readable, and fall back to the plain type
|
|
431
|
+
otherwise. Generated modules now begin with `from __future__ import
|
|
432
|
+
annotations`, the `_CodexAppTool` constructor takes `doc=` instead of
|
|
433
|
+
`description=`, and each module's `TOOLS` entry carries a `parameters` tuple.
|
|
434
|
+
`GENERATED_PACKAGE_SCHEMA` is bumped to 11. A failed tool call now appends what
|
|
435
|
+
the named parameter accepts, or a pointer to `help(tools.<app>.<tool>)` when no
|
|
436
|
+
known parameter is named.
|
|
437
|
+
|
|
438
|
+
**Why:** Codegen kept only the property name, type, and requiredness, so
|
|
439
|
+
everything an agent needed in order to choose a value was discarded. In the real
|
|
440
|
+
inventory 96% of properties carry a description and only 3% declare an `enum`,
|
|
441
|
+
so the values are usually stated in prose rather than machine-readable. Slack's
|
|
442
|
+
`response_format` is the case that motivated this: it advertises no `enum` while
|
|
443
|
+
the server enforces one, and agents that inspected the tool first still had to
|
|
444
|
+
guess. That guess failed 67 times across recorded sessions.
|
|
445
|
+
|
|
446
|
+
**Migration:** None required. The generated package is rebuilt on every host
|
|
447
|
+
process, schema `default`s are still never sent on the wire, and the rendered
|
|
448
|
+
annotations are display-only and unenforced.
|
|
449
|
+
|
|
450
|
+
### Stale generated package roots are reclaimed
|
|
451
|
+
|
|
452
|
+
**Status:** Implemented.
|
|
453
|
+
|
|
454
|
+
**Superseded:** Generated package roots now use process-owned operating-system temporary storage, so this sweep, its heartbeat, and both related configuration keys have been removed. See “Runtime-only state uses temporary storage and notebook publication dependencies are lazy.”
|
|
455
|
+
|
|
456
|
+
**Change:** On startup each host process sweeps sibling generated-package roots
|
|
457
|
+
under its cache directory on a background thread, removing those whose owner is
|
|
458
|
+
gone and whose age exceeds `stale_root_ttl_s` (default 24 hours; `0` disables).
|
|
459
|
+
The owning process refreshes a `.alive` heartbeat so a live root is never
|
|
460
|
+
mistaken for an abandoned one. Setting `generated_package_dir` opts out of both
|
|
461
|
+
the sweep and the existing teardown, since the caller owns that directory.
|
|
462
|
+
|
|
463
|
+
**Why:** Roots are unique per host process and are removed at teardown, but a
|
|
464
|
+
stdio server killed with `SIGTERM` never runs that teardown, so nothing reclaimed
|
|
465
|
+
them. One observed cache had grown to 452 roots and 160 MB, 441 of them owned by
|
|
466
|
+
dead processes, with 409 byte-identical copies of a single module.
|
|
467
|
+
|
|
468
|
+
**Migration:** None required. To keep the previous behavior, set
|
|
469
|
+
`stale_root_ttl_s = 0` in the `ipi.codex_apps` plugin config.
|
|
470
|
+
|
|
471
|
+
### Agent work cells collapse by default and `show` promotes
|
|
472
|
+
|
|
473
|
+
**Status:** Implemented.
|
|
474
|
+
|
|
475
|
+
**Change:** A new `show(cell_id, caption)` MCP tool promotes a completed cell
|
|
476
|
+
into the human's view. `ipython` results carry `cell_id=c7` on the status record
|
|
477
|
+
as the address `show` accepts. The bundled extension collapses agent work cells
|
|
478
|
+
to a one-line header and renders promoted material expanded.
|
|
479
|
+
|
|
480
|
+
**Why:** The notebook served the agent's scratch work and the human's reading at
|
|
481
|
+
once, with no way to tell them apart. Promotion is retroactive because a cell
|
|
482
|
+
becomes interesting when its result is surprising, which the agent cannot
|
|
483
|
+
predict before running it.
|
|
484
|
+
|
|
485
|
+
**Migration:** None required. `[jupyter] collapse_cells` still writes Jupyter's
|
|
486
|
+
own `collapsed` and `source_hidden` keys and remains off by default; the
|
|
487
|
+
frontend now collapses without it, which keeps those keys owned by the reader so
|
|
488
|
+
promotion never reverts a manual collapse.
|
|
489
|
+
|
|
490
|
+
### `action_description` and `description_display` are removed
|
|
491
|
+
|
|
492
|
+
**Status:** Implemented.
|
|
493
|
+
|
|
494
|
+
**Change:** The optional `[tools] action_descriptions` mode, the
|
|
495
|
+
`action_description` argument on `ipython`, `bash`, and `pdb`, and the
|
|
496
|
+
`[jupyter] description_display` renderer choice are all gone. Cell metadata no
|
|
497
|
+
longer carries `description` or `description_display`, the `#### Intent:`
|
|
498
|
+
Markdown cell is no longer emitted, and PDB suspension transcripts no longer
|
|
499
|
+
show an `**Intent:**` line. The bundled Notebook 7 extension now always loads
|
|
500
|
+
rather than being selected by `description_display = "extension"`.
|
|
501
|
+
|
|
502
|
+
**Why:** The argument asked the agent to pay tokens on every call to restate
|
|
503
|
+
intent the model had already produced. IPi now records the agent's own
|
|
504
|
+
reasoning summaries and preambles in the structured turn trace
|
|
505
|
+
(`ipi.session_trace`), which is both cheaper and more faithful: there is no
|
|
506
|
+
reliable pairing between reasoning and calls, so a per-call field could never
|
|
507
|
+
represent what the agent was actually doing.
|
|
508
|
+
|
|
509
|
+
**Migration:** Remove `action_descriptions` from `[tools]` and
|
|
510
|
+
`description_display` from `[jupyter]` in `~/.ipi/config.toml` and any
|
|
511
|
+
`.ipi.toml`. Unknown config keys fail validation, so a stale file stops the
|
|
512
|
+
server at startup with the offending key named. Callers passing
|
|
513
|
+
`action_description` to a tool must drop it; extra arguments are rejected.
|
|
514
|
+
Reasoning forwarding requires `model_reasoning_summary` to be set in
|
|
515
|
+
`~/.codex/config.toml`, and is Codex-only.
|
|
516
|
+
|
|
517
|
+
### Codex Apps startup refresh is a background kernel thread
|
|
518
|
+
|
|
519
|
+
**Status:** Implemented.
|
|
520
|
+
|
|
521
|
+
**Change:** The `refresh` bootstrap cell no longer blocks kernel readiness on
|
|
522
|
+
the inventory refresh (a real Codex backend round trip, ~0.5 s). The cell
|
|
523
|
+
imports the generated package, wires the bridge, installs the `tools` alias,
|
|
524
|
+
arms a settle event, and runs the refresh on a daemon thread. First attribute
|
|
525
|
+
access on an app module that is not materialized yet waits (bounded, 10 s)
|
|
526
|
+
for the armed refresh before failing, so `tools.<app>` still behaves as if
|
|
527
|
+
the refresh were synchronous when the inventory is genuinely needed.
|
|
528
|
+
`GENERATED_PACKAGE_SCHEMA` bumped to 10.
|
|
529
|
+
|
|
530
|
+
**Why:** Kernel creation (and therefore prewarm completion and the cold first
|
|
531
|
+
call) paid the network refresh serially even though the server's own startup
|
|
532
|
+
refresh freshens the same package concurrently.
|
|
533
|
+
|
|
534
|
+
**Migration:** Code that asserted on the bootstrap cell's source must match
|
|
535
|
+
`arm_startup_refresh` / `run_startup_refresh` instead of a synchronous
|
|
536
|
+
`refresh(silent=True, ...)` call.
|
|
537
|
+
|
|
538
|
+
### `ipi.runtime.bootstrap(ip)` renamed to `bootstrap_shell(ip)`
|
|
539
|
+
|
|
540
|
+
**Status:** Implemented.
|
|
541
|
+
|
|
542
|
+
**Change:** The package-level convenience function `ipi.runtime.bootstrap`
|
|
543
|
+
collided with the `ipi.runtime.bootstrap` submodule: importing the submodule
|
|
544
|
+
rebinds the package attribute to the module object, which silently shadowed
|
|
545
|
+
the function once imports became lazy. The function is now
|
|
546
|
+
`ipi.runtime.bootstrap_shell`; `%load_ext ipi.runtime` is unaffected. The
|
|
547
|
+
heavy `ipi.runtime` members also import lazily now, so host-side code can use
|
|
548
|
+
light leaves such as `ipi.runtime.project` without paying for IPython.
|
|
549
|
+
|
|
550
|
+
**Migration:** Call `bootstrap_shell(ip)` (or `load_ipython_extension(ip)`).
|
|
551
|
+
|
|
552
|
+
### On-disk notebook writes are coalesced and asynchronous without collaboration
|
|
553
|
+
|
|
554
|
+
**Status:** Implemented.
|
|
555
|
+
|
|
556
|
+
**Change:** Without the collaborative mirror (`jupyter.enabled = false`, or a
|
|
557
|
+
sidecar with `collaboration = false`), `notebook.ipynb` is no longer rewritten
|
|
558
|
+
synchronously on every cell mutation. A background saver persists the latest
|
|
559
|
+
notebook state, debounced to at most one full rewrite per ~200 ms burst, and
|
|
560
|
+
`Kernel.close()` still writes the authoritative final notebook synchronously.
|
|
561
|
+
The new `Kernel.flush_notebook()` is the read-back barrier for anything that
|
|
562
|
+
inspects the file mid-session. Cell artifacts (`input.py`, outputs,
|
|
563
|
+
`manifest.json`) and `session.jsonl` are unchanged: still written before the
|
|
564
|
+
tool result is returned. Relatedly, per-cell artifact fsyncs moved to a
|
|
565
|
+
background durability flusher earlier in this cycle; atomic tmp-write + rename
|
|
566
|
+
still happens on the response path.
|
|
567
|
+
|
|
568
|
+
**Why:** A full-notebook rewrite per mutation made warm call latency grow
|
|
569
|
+
linearly with session length (~13 ms at cell 25 to ~31 ms at cell 150 on the
|
|
570
|
+
benchmark machine). Collaboration mode already treated the on-disk file as
|
|
571
|
+
eventually consistent (the live room owns the document); this aligns the
|
|
572
|
+
non-collaborative path with the same contract and keeps per-call latency flat.
|
|
573
|
+
|
|
574
|
+
**Migration:** Tools or tests that read `notebook.ipynb` while the kernel is
|
|
575
|
+
alive must call `flush_notebook()` first (or accept up to ~200 ms of
|
|
576
|
+
staleness). Readers that only look after `close()` are unaffected.
|
|
577
|
+
|
|
578
|
+
### Exception results are inline errors with an optional armed post-mortem
|
|
579
|
+
|
|
580
|
+
**Status:** Implemented.
|
|
581
|
+
|
|
582
|
+
**Change:** An uncaught exception in an `ipython` cell now publishes its
|
|
583
|
+
concise, chain-aware traceback as that cell's own error output at throw time,
|
|
584
|
+
exactly like plain IPython, and the tool result renders the cell as `err` with
|
|
585
|
+
a `post-mortem=armed` status marker plus a one-line optional `pdb` hint. The
|
|
586
|
+
previous shape suppressed the traceback from the cell output ("Traceback
|
|
587
|
+
omitted here"), framed the result as a debugger stop ("Kernel is suspended"
|
|
588
|
+
with `w`/`q`/`c` instructions and a `Location:`/`Exception:` suspension body),
|
|
589
|
+
and appended an internally-run `pdb w` stack section. Dismissal is now silent
|
|
590
|
+
and honest: the next `ipython` or `bash` call no longer prepends
|
|
591
|
+
"automatically aborted the pdb suspension before ...", and the failed cell
|
|
592
|
+
finalizes as `err` carrying the original exception instead of `aborted` (for
|
|
593
|
+
automatic dismissal and for a manual `pdb` `q` alike). `breakpoint()` stops
|
|
594
|
+
are unchanged: they keep the "Kernel is suspended" framing, the automatic
|
|
595
|
+
`pdb w` stack, and the explicit auto-quit prefix with `aborted` status.
|
|
596
|
+
|
|
597
|
+
**Why:** Agents reflexively answered every exception with `pdb("q")` even
|
|
598
|
+
though ignoring the suspension always worked, and the failing cell's notebook
|
|
599
|
+
record only received its traceback once the suspension resolved on the next
|
|
600
|
+
call. Traceback-in-the-throwing-cell parity removes the ceremony while the
|
|
601
|
+
armed post-mortem keeps live-state inspection one optional call away.
|
|
602
|
+
|
|
603
|
+
**Migration:** Consumers matching "Traceback omitted", the exception-pause
|
|
604
|
+
"Kernel is suspended" notice, or "automatically aborted the pdb suspension"
|
|
605
|
+
must update their patterns. The exception suspension record is now a bare
|
|
606
|
+
`suspension<TAB>ipi.pdb<TAB>exception<TAB>tool=pdb` line with no body, and the
|
|
607
|
+
inline traceback strips IPython infrastructure frames and elides long middles
|
|
608
|
+
(`... N frame(s) omitted ...`).
|
|
609
|
+
|
|
610
|
+
### The stateful server prewarms the default kernel at startup
|
|
611
|
+
|
|
612
|
+
**Status:** Implemented.
|
|
613
|
+
|
|
614
|
+
**Change:** A stateful server now launches the default kernel in a background
|
|
615
|
+
task as soon as its lifespan starts, so the first `ipython` call usually
|
|
616
|
+
attaches to a warm kernel instead of paying kernel startup. The prewarm
|
|
617
|
+
respects `tools.auto_create_kernel_for_python`, never blocks or fails server
|
|
618
|
+
startup, and a failed prewarm logs a warning and falls back to the existing
|
|
619
|
+
lazy auto-create. The first-result `auto_created_kernel` wrapper now appears
|
|
620
|
+
only when that call actually created the kernel, and the auto-read notebook
|
|
621
|
+
skill rides the first execution result even when the kernel was prewarmed.
|
|
622
|
+
|
|
623
|
+
**Why:** The redesign makes bare `ipython` the whole default workflow; hiding
|
|
624
|
+
kernel startup behind server startup removes the last visible setup cost, and
|
|
625
|
+
the session-start harness context can advertise a kernel that already exists.
|
|
626
|
+
|
|
627
|
+
**Migration:** Set `[server] prewarm_kernel = false` to opt out (for example
|
|
628
|
+
on hosts where most sessions never run Python).
|
|
629
|
+
|
|
630
|
+
### Auto-created kernels reuse the last kernel spec; reap notices target executions
|
|
631
|
+
|
|
632
|
+
**Status:** Implemented.
|
|
633
|
+
|
|
634
|
+
**Change:** When `ipython` or `bash` auto-creates a kernel because the previous
|
|
635
|
+
one died or was idle-reaped, the new kernel reuses the most recently created
|
|
636
|
+
or activated kernel's cwd, venv, and `additional_dependencies` instead of the
|
|
637
|
+
server-default cwd with no overlay. The idle-reap "state is gone" notice is
|
|
638
|
+
now delivered only into the next `create_kernel`, `ipython`, or `bash` result;
|
|
639
|
+
`state`, `list_active_kernels`, and `switch_kernel` no longer consume it, so a
|
|
640
|
+
read-only call between the reap and the next execution cannot swallow the
|
|
641
|
+
warning.
|
|
642
|
+
|
|
643
|
+
**Why:** Auto-create previously used a cwd frozen at first runtime creation
|
|
644
|
+
(often the server working directory), silently moving the session to a
|
|
645
|
+
different directory with no venv or dependency overlay after a reap. The
|
|
646
|
+
notice draining into whichever call arrived first meant the execution that
|
|
647
|
+
actually hit the empty kernel could see no warning at all.
|
|
648
|
+
|
|
649
|
+
**Migration:** None. To land in a different cwd after a reap, call
|
|
650
|
+
`create_kernel` explicitly, as before.
|
|
651
|
+
|
|
652
|
+
### Written-image guidance no longer recommends an absent `view_image` tool
|
|
653
|
+
|
|
654
|
+
**Status:** Implemented.
|
|
655
|
+
|
|
656
|
+
**Change:** The `ipi.view_image` kernel plugin's post-cell context block
|
|
657
|
+
("Images saved this cell. ...") now depends on whether the public `view_image`
|
|
658
|
+
MCP tool is actually registered. The server resolves the effective
|
|
659
|
+
`tools.view_image` value (transport default or explicit override) and injects
|
|
660
|
+
it into the kernel plugin's spec config at kernel launch. When the tool
|
|
661
|
+
exists, the guidance keeps recommending `view_image` with kernel-cwd-relative
|
|
662
|
+
paths; when it does not (the stdio default), the guidance lists absolute paths
|
|
663
|
+
and points at the client's own file/image reading instead.
|
|
664
|
+
|
|
665
|
+
**Why:** Stdio agents were told to "Use `view_image`" for a tool their client
|
|
666
|
+
does not have, which taught them to route figures through `savefig` plus
|
|
667
|
+
harness file reading. Relative paths were also only resolvable against the
|
|
668
|
+
kernel cwd, not the client cwd.
|
|
669
|
+
|
|
670
|
+
**Migration:** None. Anything matching the old guidance string must accept
|
|
671
|
+
both variants; the block's `source` (`path:written-images`) and `reason`
|
|
672
|
+
(`path-written`) are unchanged.
|
|
673
|
+
|
|
674
|
+
### `%pip` no longer shells out through `$SHELL`
|
|
675
|
+
|
|
676
|
+
**Status:** Implemented.
|
|
677
|
+
|
|
678
|
+
**Change:** IPi registers its own `%pip` line magic that executes
|
|
679
|
+
`sys.executable -m pip ...` as a direct argv subprocess with piped output,
|
|
680
|
+
replacing IPython's stock magic, which runs the installer through
|
|
681
|
+
`$SHELL -c`. A failed install now raises `CalledProcessError`, so the cell
|
|
682
|
+
reports `err` instead of quietly printing a nonzero exit; the stock
|
|
683
|
+
"restart the kernel" note is printed only on success.
|
|
684
|
+
|
|
685
|
+
**Why:** Routing installs through the user's login shell let shell startup
|
|
686
|
+
hooks (fnm, nvm, direnv, ...) interleave their own errors into `%pip` output —
|
|
687
|
+
for example fnm's "Requested version ... is not currently installed" in any
|
|
688
|
+
repo with a `.node-version` — and made install output depend on per-machine
|
|
689
|
+
shell configuration.
|
|
690
|
+
|
|
691
|
+
**Migration:** None for normal use; `%pip install <pkg>` behaves the same but
|
|
692
|
+
with deterministic output. Anything that relied on shell syntax inside the
|
|
693
|
+
`%pip` line (environment prefixes, `&&` chains) must move to `%%bash` or a
|
|
694
|
+
plain cell.
|
|
695
|
+
|
|
696
|
+
### Idle kernels are shut down after one hour by default
|
|
697
|
+
|
|
698
|
+
**Status:** Implemented.
|
|
699
|
+
|
|
700
|
+
**Change:** A kernel with no agent or browser executions for
|
|
701
|
+
`server.kernel_idle_timeout_s` seconds (default `3600`) is now closed
|
|
702
|
+
automatically; previously kernels lived until server shutdown. Executing,
|
|
703
|
+
suspended, and browser-busy kernels are never culled. The durable notebook and
|
|
704
|
+
output artifacts are saved as usual, the shutdown is recorded as a
|
|
705
|
+
`kernel`/`death` event with reason `idle_timeout`, and the next tool response
|
|
706
|
+
carries a notice. An open JupyterLab tab alone does not keep a kernel alive;
|
|
707
|
+
only executions reset the timer.
|
|
708
|
+
|
|
709
|
+
**Why:** Each live kernel holds an IPython child process and usually a Jupyter
|
|
710
|
+
sidecar process. Sessions left open for hours accumulated idle kernels that
|
|
711
|
+
wasted memory and CPU indefinitely.
|
|
712
|
+
|
|
713
|
+
**Migration:** To restore forever-lived kernels, set
|
|
714
|
+
`kernel_idle_timeout_s = 0` under `[server]` in `~/.ipi/config.toml` or
|
|
715
|
+
`<cwd>/.ipi.toml`, or pass `-c server.kernel_idle_timeout_s=0`. Raise or lower
|
|
716
|
+
the value to tune the horizon.
|
|
717
|
+
|
|
718
|
+
### Action descriptions are opt-in
|
|
719
|
+
|
|
720
|
+
**Status:** Implemented.
|
|
721
|
+
|
|
722
|
+
**Change:** `ipython`, the optional `bash` tool, and the built-in `pdb` tool no
|
|
723
|
+
longer expose `action_description` by default. IPi injects an empty internal
|
|
724
|
+
description, does not render an Intent block for it, and otherwise keeps the
|
|
725
|
+
execution and debugger behavior unchanged. Set
|
|
726
|
+
`[tools] action_descriptions = true` before server startup to restore the
|
|
727
|
+
required, nonblank argument and its notebook intent metadata. This is a strict
|
|
728
|
+
boolean startup schema setting.
|
|
729
|
+
|
|
730
|
+
**Why:** Requiring a prose preamble for every notebook action adds tool-schema
|
|
731
|
+
and call overhead even when the code or debugger command already states the
|
|
732
|
+
operation clearly. Keeping intent opt-in preserves the auditable notebook
|
|
733
|
+
workflow for users who value it without imposing it on every client.
|
|
734
|
+
|
|
735
|
+
**Migration:** Remove `action_description` from default `ipython`, `bash`, and
|
|
736
|
+
`pdb` calls. To retain the former signatures and nonblank validation, add
|
|
737
|
+
`action_descriptions = true` under `[tools]` and restart the MCP server.
|
|
738
|
+
|
|
739
|
+
### `view_image` availability follows the MCP transport
|
|
740
|
+
|
|
741
|
+
**Status:** Implemented.
|
|
742
|
+
|
|
743
|
+
**Change:** `[tools] view_image` is now a strict `bool | None` startup setting.
|
|
744
|
+
When omitted, `view_image` is absent from every stdio server and present on
|
|
745
|
+
every HTTP-family transport (`http`, `sse`, and `streamable-http`). Explicit
|
|
746
|
+
`true` or `false` overrides that transport default. Filtering removes only the
|
|
747
|
+
server-side MCP tool contribution; the `ipi.view_image` kernel plugin continues
|
|
748
|
+
to report image files written by cells. Stateless plugin schemas now put
|
|
749
|
+
`session_id` first, including `view_image(session_id, path)` and
|
|
750
|
+
`pdb(session_id, command)` with default action-description settings.
|
|
751
|
+
|
|
752
|
+
**Why:** Stdio clients can inspect local image artifacts through their native
|
|
753
|
+
filesystem/image surface, while HTTP clients need an MCP tool that reads from
|
|
754
|
+
the session kernel's filesystem. Transport-aware defaults avoid a duplicate
|
|
755
|
+
stdio tool without removing runtime image-output context.
|
|
756
|
+
|
|
757
|
+
**Migration:** Stdio clients that still need IPi's MCP image loader must set
|
|
758
|
+
`view_image = true` under `[tools]`. HTTP clients can omit the setting, or set
|
|
759
|
+
it to `false` to remove the tool. Treat stateless `session_id` as the first
|
|
760
|
+
schema field; MCP invocation remains named-argument based.
|
|
761
|
+
|
|
762
|
+
### Model-facing MCP tool references use canonical bare names
|
|
763
|
+
|
|
764
|
+
**Status:** Implemented.
|
|
765
|
+
|
|
766
|
+
**Change:** Server instructions, runtime hints and errors, harness guidance, and
|
|
767
|
+
the local and HTTP notebook skills now refer to tools by their canonical names,
|
|
768
|
+
such as `ipython`, `create_kernel`, `wait`, and `pdb`. The model-facing alias
|
|
769
|
+
resolver and `IPI_MCP_TOOL_PREFIX` environment variable have been removed. The
|
|
770
|
+
registered FastMCP tool names were already bare and are unchanged.
|
|
771
|
+
|
|
772
|
+
**Why:** The `mcp__<alias>__<tool>` spelling is a client-specific presentation
|
|
773
|
+
detail. Repeating it in IPi-authored prose added noise, coupled the instructions
|
|
774
|
+
to one host naming convention, and could become incorrect when the configured
|
|
775
|
+
server alias differed from the client-visible alias.
|
|
776
|
+
|
|
777
|
+
**Migration:** Remove `IPI_MCP_TOOL_PREFIX` from launch environments and refresh
|
|
778
|
+
or reinstall any copied notebook skills. Integrations that inspect IPi-authored
|
|
779
|
+
guidance must expect canonical bare names. MCP clients remain responsible for
|
|
780
|
+
any namespace they add to transport-level tool identifiers.
|
|
781
|
+
|
|
782
|
+
### Python execution tool renamed to `ipython`
|
|
783
|
+
|
|
784
|
+
**Status:** Implemented.
|
|
785
|
+
|
|
786
|
+
**Change:** The stateful and stateless Python/IPython execution MCP tool is now
|
|
787
|
+
registered as `ipython` (previously `python`). Both notebook skills, the
|
|
788
|
+
README, server instructions, and error/collision messages now reference the
|
|
789
|
+
new name. The optional `bash` tool and its `IPI_MCP_ENABLE_BASH_TOOL`/
|
|
790
|
+
`tools.bash` gating are unchanged.
|
|
791
|
+
|
|
792
|
+
**Why:** `ipython` more accurately reflects that the tool runs a persistent
|
|
793
|
+
IPython kernel, not just plain Python.
|
|
794
|
+
|
|
795
|
+
**Migration:** Any client-side permission allowlist, config, or automation
|
|
796
|
+
that references the `python` MCP tool name (e.g. `mcp__notebook__python`) must
|
|
797
|
+
be updated to `ipython`. Refresh or reinstall any copied notebook skills.
|
|
798
|
+
|
|
799
|
+
### Jupyter Cloudflare tunnels are quick-tunnel only
|
|
800
|
+
|
|
801
|
+
**Status:** Implemented.
|
|
802
|
+
|
|
803
|
+
**Change:** `jupyter.tunnel = true` now launches only an accountless Cloudflare
|
|
804
|
+
quick tunnel. The `jupyter.tunnel_domain` and
|
|
805
|
+
`jupyter.tunnel_hostname_prefix` fields are removed and rejected. Jupyter waits
|
|
806
|
+
up to 30 seconds for `cloudflared` to publish the quick-tunnel URL so
|
|
807
|
+
`create_kernel` can return it immediately; failure stops the tunnel and falls
|
|
808
|
+
back to the local notebook URL with a diagnostic notice.
|
|
809
|
+
|
|
810
|
+
**Why:** Named Jupyter tunnels required Cloudflare login state, tunnel and DNS
|
|
811
|
+
provisioning, generated credentials, and custom-hostname configuration. Quick
|
|
812
|
+
tunnels need none of that setup. The previous asynchronous startup also returned
|
|
813
|
+
localhost before the quick-tunnel URL appeared a few seconds later.
|
|
814
|
+
|
|
815
|
+
**Migration:** Remove `tunnel_domain` and `tunnel_hostname_prefix` from every
|
|
816
|
+
`[jupyter]` table. Keep `jupyter.tunnel = true` to request a quick tunnel. MCP
|
|
817
|
+
HTTP named tunnels and the `--tunnel-domain` CLI option are unchanged.
|
|
818
|
+
|
|
819
|
+
### HTTP logical session IDs use compact decimal strings
|
|
820
|
+
|
|
821
|
+
**Status:** Implemented.
|
|
822
|
+
|
|
823
|
+
**Change:** The logical `session_id` returned by stateless HTTP `create_kernel`
|
|
824
|
+
is now an opaque fixed-width 18-digit decimal string. The prior implementation
|
|
825
|
+
reused the 28-character timestamp-sortable durable-session directory ID.
|
|
826
|
+
|
|
827
|
+
**Why:** The logical ID is repeated in every later HTTP tool call. Measurements
|
|
828
|
+
over 10,000 samples with `o200k_base` and `cl100k_base` reduced its mean cost
|
|
829
|
+
from 12.56 tokens to exactly 6. The new $9 \times 10^{17}$-value random space
|
|
830
|
+
retains about 59.6 bits of entropy; even one million generated IDs have an
|
|
831
|
+
approximate birthday-collision probability of $5.6 \times 10^{-7}$.
|
|
832
|
+
|
|
833
|
+
**Migration:** Treat session IDs as opaque strings and retain the exact value
|
|
834
|
+
returned by `create_kernel`. Do not parse timestamps, UUID fragments, or numeric
|
|
835
|
+
meaning from either format. Logical sessions never survive an MCP server
|
|
836
|
+
restart, so there is no persisted-ID migration.
|
|
837
|
+
|
|
838
|
+
### Parked execution control returns incremental output
|
|
839
|
+
|
|
840
|
+
**Status:** Implemented.
|
|
841
|
+
|
|
842
|
+
**Change:** After the initial Python result parks a running cell, each `wait`
|
|
843
|
+
and `interrupt` result now contains current status plus only output not already
|
|
844
|
+
returned. New output records include `update=new`; a growing stream uses
|
|
845
|
+
`update=append from_chars=N` and contains only its suffix. An unchanged poll has
|
|
846
|
+
no output record. Non-append changes emit `notice\tkind=output-reset` followed by
|
|
847
|
+
the complete current snapshot. Artifact paths continue to name complete output,
|
|
848
|
+
and canonical cell records remain full snapshots.
|
|
849
|
+
|
|
850
|
+
**Why:** Replaying all output accumulated so far on every poll made repeated
|
|
851
|
+
waits consume quadratic model tokens and obscured which output was actually new.
|
|
852
|
+
|
|
853
|
+
**Migration:** Clients that assemble a live view must retain earlier output,
|
|
854
|
+
add `update=new` records, append untruncated suffixes at the Unicode code-point
|
|
855
|
+
offset `from_chars=N`, and replace the assembled view after
|
|
856
|
+
`kind=output-reset`. A truncated suffix is only a preview; read its complete
|
|
857
|
+
`PATH` and replace that output when lossless assembly is required. Clients that
|
|
858
|
+
need a stateless complete snapshot should likewise read the referenced artifact
|
|
859
|
+
or current manifest instead of expecting every `wait` response to repeat prior
|
|
860
|
+
output.
|
|
861
|
+
|
|
862
|
+
### AGENTS context hints use absolute paths
|
|
863
|
+
|
|
864
|
+
**Status:** Implemented.
|
|
865
|
+
|
|
866
|
+
**Change:** The `source` field emitted by the `ipi.agents_md` built-in now uses
|
|
867
|
+
the resolved absolute `AGENTS.md` path with portable forward slashes. It no
|
|
868
|
+
longer renders the path relative to a detected project or ancestor directory.
|
|
869
|
+
|
|
870
|
+
**Why:** Relative hints can contain several `..` components when applicable
|
|
871
|
+
instructions live above the nearest project root, making the model-visible path
|
|
872
|
+
hard to identify and resolve correctly.
|
|
873
|
+
|
|
874
|
+
**Migration:** Treat the text after `agents-md:` as an absolute path and open it
|
|
875
|
+
directly. Consumers must stop joining the hint to the kernel cwd or project
|
|
876
|
+
root.
|
|
877
|
+
|
|
878
|
+
### Model-facing output no longer contains ANSI controls
|
|
879
|
+
|
|
880
|
+
**Status:** Implemented.
|
|
881
|
+
|
|
882
|
+
**Change:** MCP responses, live output callbacks, PDB results, and persisted
|
|
883
|
+
text artifacts remove both terminal control bytes and serialized spellings such
|
|
884
|
+
as `\x1b[31m`. Runtime bootstrap also undoes ipykernel's forced-color child
|
|
885
|
+
process environment. Raw notebook stream/error output retains explicitly
|
|
886
|
+
emitted controls so Jupyter can render them, but ordinary child CLI output is
|
|
887
|
+
no longer forced to use color.
|
|
888
|
+
|
|
889
|
+
**Why:** ANSI syntax expands further when MCP JSON is encoded, consumes model
|
|
890
|
+
tokens without adding information, and can make captured machine-readable
|
|
891
|
+
output invalid before the agent can parse it.
|
|
892
|
+
|
|
893
|
+
**Migration:** Do not depend on ANSI syntax in model-facing output or captured
|
|
894
|
+
subprocess data. Emit semantic text or a structured rich display instead. Code
|
|
895
|
+
that intentionally needs colored child-process output must opt in explicitly
|
|
896
|
+
for that subprocess and consume the resulting control sequences itself.
|
|
897
|
+
|
|
898
|
+
### Execution descriptions become required action intent
|
|
899
|
+
|
|
900
|
+
**Status:** Implemented.
|
|
901
|
+
|
|
902
|
+
**Change:** Execution intent is now the first required argument of Python,
|
|
903
|
+
bash, and PDB. The public signatures are
|
|
904
|
+
`python(action_description, code, timeout_s=10)`,
|
|
905
|
+
`bash(action_description, cmd, timeout=None)`, and
|
|
906
|
+
`pdb(action_description, command)`. The former optional Python `description`
|
|
907
|
+
argument, internal Python `source` argument, and bash `command` argument have
|
|
908
|
+
been removed rather than aliased. Blank descriptions are rejected.
|
|
909
|
+
|
|
910
|
+
**Why:** The old Python description was effectively a no-op after the tool call.
|
|
911
|
+
Putting user-facing intent first makes the agent state what an execution is for
|
|
912
|
+
before composing it, and preserving that intent makes notebook history easier
|
|
913
|
+
to follow and audit.
|
|
914
|
+
|
|
915
|
+
**Migration:** Rename Python `description` to `action_description` and place it
|
|
916
|
+
before `code`; rename Python `source` payloads to `code`; rename bash `command`
|
|
917
|
+
to `cmd`; and add `action_description` before every bash and PDB command. The raw
|
|
918
|
+
tool-call arguments remain available in session records, while normalized intent
|
|
919
|
+
is stored on execution records and notebook metadata. Notebook 7 uses the
|
|
920
|
+
bundled intent-header extension by default. Set
|
|
921
|
+
`[jupyter] description_display = "markdown"` to stage an intent Markdown cell
|
|
922
|
+
before each agent code cell instead.
|
|
923
|
+
|
|
924
|
+
### Python execution yields after 10 seconds by default
|
|
925
|
+
|
|
926
|
+
**Status:** Implemented.
|
|
927
|
+
|
|
928
|
+
**Change:** Omitting `python.timeout_s` now uses a 10-second cooperative yield
|
|
929
|
+
budget instead of inheriting the kernel's 1,800-second timeout. If the cell is
|
|
930
|
+
still running, IPi returns it as parked with partial output; the cell continues
|
|
931
|
+
until `wait` collects it or `interrupt` stops it. `timeout_s=0` parks
|
|
932
|
+
immediately. The MCP parameter is now a non-null integer greater than or equal
|
|
933
|
+
to zero.
|
|
934
|
+
|
|
935
|
+
**Why:** The prior default kept the MCP request occupied for up to 30 minutes,
|
|
936
|
+
so an agent that did not predict a slow call could not reach the control tools
|
|
937
|
+
that cooperative timeout was designed to expose.
|
|
938
|
+
|
|
939
|
+
**Migration:** Omit `timeout_s` for the new responsive default. Pass an explicit
|
|
940
|
+
larger value when one blocking call is intentional; `timeout_s=1800` recreates
|
|
941
|
+
the former wait. Replace explicit `null` with either omission or a non-negative
|
|
942
|
+
integer. Existing MCP processes must be restarted before they advertise and use
|
|
943
|
+
the new schema.
|
|
944
|
+
|
|
945
|
+
### TUI and embedded LLM harness removal
|
|
946
|
+
|
|
947
|
+
**Status:** Implemented.
|
|
948
|
+
|
|
949
|
+
**Change:** The terminal UI and its `ipi.tui` package have been removed. The
|
|
950
|
+
root `ipi` command now starts the MCP server. The embedded agent loop,
|
|
951
|
+
`ipi.agent`, `ipi.client`, `ipi.core`, provider prompt assembly, diagnostics,
|
|
952
|
+
and client transport APIs have also been removed.
|
|
953
|
+
|
|
954
|
+
**Why:** These layers have no active TUI users, duplicate responsibilities
|
|
955
|
+
already owned by Codex and Claude Code, and force the MCP path through a much
|
|
956
|
+
larger compatibility architecture. Removing them makes the supported product
|
|
957
|
+
boundary explicit and allows the runtime and extension APIs to model only real
|
|
958
|
+
use cases.
|
|
959
|
+
|
|
960
|
+
**Migration:** Configure IPi as an MCP server through the `ipi` command. The
|
|
961
|
+
`ipi-mcp` command no longer exists. Existing users of the old TUI must remain on
|
|
962
|
+
an older release; there is no replacement embedded agent harness.
|
|
963
|
+
|
|
964
|
+
### Harness-only configuration removal
|
|
965
|
+
|
|
966
|
+
**Status:** Implemented.
|
|
967
|
+
|
|
968
|
+
**Change:** The `[provider]`, `[display]`, and `[tui]` configuration sections
|
|
969
|
+
and their Python model types have been removed. IPi config now describes MCP
|
|
970
|
+
tools, Jupyter behavior, and extensions only. Removed sections are rejected as
|
|
971
|
+
invalid configuration.
|
|
972
|
+
|
|
973
|
+
**Why:** Provider selection, reasoning effort, display verbosity, and terminal
|
|
974
|
+
keybindings belonged to the deleted embedded harness. Accepting those settings
|
|
975
|
+
would imply behavior the MCP server cannot provide.
|
|
976
|
+
|
|
977
|
+
**Migration:** Remove those sections from user and project `.ipi.toml` files.
|
|
978
|
+
Configure models, reasoning, display, and keybindings in Codex or Claude Code.
|
|
979
|
+
|
|
980
|
+
### Provider-era package removal
|
|
981
|
+
|
|
982
|
+
**Status:** Implemented.
|
|
983
|
+
|
|
984
|
+
**Change:** Every bundled `ipi-plugin-*` distribution has been removed. The
|
|
985
|
+
following provider-era capabilities were deleted rather than migrated:
|
|
986
|
+
|
|
987
|
+
- `ipi-plugin-provider-codex`
|
|
988
|
+
- `ipi-plugin-provider-generic`
|
|
989
|
+
- `ipi-plugin-compaction`
|
|
990
|
+
- `ipi-plugin-skills`
|
|
991
|
+
- `ipi-plugin-slash-lifecycle`
|
|
992
|
+
- `ipi-plugin-write-tool`
|
|
993
|
+
- `ipi-plugin-notebook-transcript`
|
|
994
|
+
|
|
995
|
+
Actively retained behavior, including AGENTS path hints, automatic context,
|
|
996
|
+
autoreload, project imports, dataframe formatting, image output tracking, PDB,
|
|
997
|
+
Codex Apps, and harness context, now ships as in-tree built-in plugins in the
|
|
998
|
+
single `ipi` distribution.
|
|
999
|
+
|
|
1000
|
+
**Why:** Codex and Claude Code already own provider calls, skills, slash
|
|
1001
|
+
commands, patch/write tools, conversation transcripts, and context compaction.
|
|
1002
|
+
Keeping duplicate implementations in IPi enlarged the dependency and extension
|
|
1003
|
+
surface without contributing to the MCP execution product.
|
|
1004
|
+
|
|
1005
|
+
**Migration:** Remove these package names from IPi configuration and launch
|
|
1006
|
+
requirements. Use the corresponding Codex or Claude Code harness capability.
|
|
1007
|
+
AGENTS instructions are discovered by the `ipi.agents_md` built-in, which emits
|
|
1008
|
+
file path hints for the harness to read rather than injecting full file
|
|
1009
|
+
contents. Executed notebook cells and outputs remain recorded by core;
|
|
1010
|
+
conversation messages are no longer mirrored into the notebook.
|
|
1011
|
+
|
|
1012
|
+
### Extension API replacement
|
|
1013
|
+
|
|
1014
|
+
**Status:** Implemented.
|
|
1015
|
+
|
|
1016
|
+
**Change:** The provider/TUI-oriented `HostInitAPI` and its priority registries,
|
|
1017
|
+
string event namespace, tool-object/dispatcher pairs, provider registration,
|
|
1018
|
+
slash commands, record renderers, and agent control handle were replaced by
|
|
1019
|
+
plain plugin callbacks with separate server, per-kernel environment, and
|
|
1020
|
+
in-kernel scopes.
|
|
1021
|
+
|
|
1022
|
+
First-class MCP tools are registered only by bundled, installed,
|
|
1023
|
+
user-level, and startup-project plugins discovered before the MCP server starts.
|
|
1024
|
+
Project plugins discovered later through `create_kernel(cwd=...)` may customize
|
|
1025
|
+
that kernel and its environment, but cannot add a new MCP tool schema because
|
|
1026
|
+
Codex does not currently refresh its model tool registry after
|
|
1027
|
+
`tools/list_changed`.
|
|
1028
|
+
|
|
1029
|
+
**Why:** The current API combines unrelated process lifetimes and obsolete
|
|
1030
|
+
agent-harness concepts. Explicit scopes make ownership, failure behavior, and
|
|
1031
|
+
project isolation understandable while preserving the extensibility that is
|
|
1032
|
+
the product's central requirement.
|
|
1033
|
+
|
|
1034
|
+
**Migration:** Export one direct `plugin` value from each extension entry point
|
|
1035
|
+
or local file:
|
|
1036
|
+
|
|
1037
|
+
```python
|
|
1038
|
+
from ipi.plugins import EnvironmentSetup, Plugin, ServerSetup, ToolContext
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
async def inspect(ctx: ToolContext, name: str) -> str:
|
|
1042
|
+
return f"{ctx.environment}: {name}"
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
def setup_server(setup: ServerSetup) -> None:
|
|
1046
|
+
setup.tool("inspect", inspect)
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
def setup_environment(setup: EnvironmentSetup) -> None:
|
|
1050
|
+
setup.bootstrap("imports", "import ipi_codex_tools as tools")
|
|
1051
|
+
setup.require("example-dependency>=1")
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
plugin = Plugin(server=setup_server, environment=setup_environment)
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
Installed distributions must expose `module:plugin` in the `ipi.extensions`
|
|
1058
|
+
entry-point group and already be present in the host environment. User plugins
|
|
1059
|
+
move to `~/.ipi/extensions/`; project plugins move to
|
|
1060
|
+
`<cwd>/.ipi/extensions/`. The old `.agents/extensions` loader, PEP 723
|
|
1061
|
+
sidecars, split `host.py`/`kernel.py` packages, priority values, and trust
|
|
1062
|
+
allowlist are not part of the replacement.
|
|
1063
|
+
|
|
1064
|
+
Configuration moves from `[extensions]` to:
|
|
1065
|
+
|
|
1066
|
+
```toml
|
|
1067
|
+
[plugins]
|
|
1068
|
+
installed = ["my-ipi-plugin==1.2.3"]
|
|
1069
|
+
disabled = ["ipi.optional_feature"]
|
|
1070
|
+
|
|
1071
|
+
[plugins.config."my.plugin"]
|
|
1072
|
+
enabled = true
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
Configuration selects installed entry points but never installs a missing host
|
|
1076
|
+
package; add external distributions to the `uvx` launch environment. Tool
|
|
1077
|
+
extensions must be discoverable from the server startup project. Kernel-only
|
|
1078
|
+
project behavior remains independently discoverable for each
|
|
1079
|
+
`create_kernel(cwd)`.
|
|
1080
|
+
|
|
1081
|
+
### Strict setup callbacks and threaded synchronous tools
|
|
1082
|
+
|
|
1083
|
+
**Status:** Implemented.
|
|
1084
|
+
|
|
1085
|
+
**Change:** `Plugin.server`, `Plugin.environment`, and `Plugin.kernel` callbacks
|
|
1086
|
+
must be synchronous and return `None`. Coroutine functions, async callable
|
|
1087
|
+
objects, decorated callbacks that return an awaitable, and any other non-`None`
|
|
1088
|
+
result fail setup with plugin and phase attribution. Synchronous plugin MCP
|
|
1089
|
+
tools now execute in a worker thread; async tools continue on the serving loop.
|
|
1090
|
+
Tool names must contain 1-128 ASCII letters, digits, underscores, hyphens, or
|
|
1091
|
+
dots. A tool must expose an inspectable signature whose first positional
|
|
1092
|
+
parameter is named `ctx`; public positional-only, `*args`, and `**kwargs`
|
|
1093
|
+
parameters are rejected because they do not define one object-shaped MCP input
|
|
1094
|
+
schema.
|
|
1095
|
+
|
|
1096
|
+
Every contributed tool invocation now participates in the connection's runtime
|
|
1097
|
+
operation owner. It is serialized with kernel control calls and shutdown. A
|
|
1098
|
+
cancelled synchronous tool remains owned until its worker exits. Direct calls
|
|
1099
|
+
such as `await ctx.session.cell(...)` are reentrant; child tasks spawned by a
|
|
1100
|
+
plugin are tracked and drained before that invocation or session shutdown can
|
|
1101
|
+
complete.
|
|
1102
|
+
|
|
1103
|
+
**Why:** An async setup callback previously produced an unawaited coroutine,
|
|
1104
|
+
contributed nothing, and still let startup succeed. A synchronous MCP tool ran
|
|
1105
|
+
inside IPi's async wrapper on the shared event loop, so one blocking file or
|
|
1106
|
+
remote operation stalled every HTTP session and harness event.
|
|
1107
|
+
|
|
1108
|
+
**Migration:** Define setup with ordinary `def` and do all registration before
|
|
1109
|
+
returning `None`. Give tools stable valid names and ordinary typed keyword
|
|
1110
|
+
parameters after `ctx`; replace variadic or positional-only public APIs with an
|
|
1111
|
+
explicit object schema. Put asynchronous behavior inside the registered async
|
|
1112
|
+
tool, handler, observer, or provider rather than making setup async. Make
|
|
1113
|
+
synchronous MCP tools thread-safe and avoid event-loop-local state, or convert
|
|
1114
|
+
them to `async def` and use nonblocking APIs. Do not use a plugin task as a way
|
|
1115
|
+
to detach notebook operations beyond the MCP call lifetime.
|
|
1116
|
+
|
|
1117
|
+
### Recursively immutable plugin configuration
|
|
1118
|
+
|
|
1119
|
+
**Status:** Implemented.
|
|
1120
|
+
|
|
1121
|
+
**Change:** `EnvironmentSetup.config` and `KernelSetup.config` are isolated,
|
|
1122
|
+
recursively immutable views. Nested mappings are read-only, lists and tuples
|
|
1123
|
+
become tuples, sets become frozensets, and bytearrays become bytes.
|
|
1124
|
+
|
|
1125
|
+
**Why:** A top-level mapping proxy still allowed a plugin to mutate nested
|
|
1126
|
+
configuration shared with later callbacks. Setup is transactional only when its
|
|
1127
|
+
inputs cannot be changed behind the loader's back.
|
|
1128
|
+
|
|
1129
|
+
**Migration:** Treat configuration as `Mapping` and immutable sequences rather
|
|
1130
|
+
than concrete `dict` and `list` values. Make an explicit local copy inside the
|
|
1131
|
+
callback when an algorithm genuinely needs mutable working state.
|
|
1132
|
+
|
|
1133
|
+
### Explicit HTTP logical sessions and per-kernel project ownership
|
|
1134
|
+
|
|
1135
|
+
**Status:** Implemented for the MCP runtime and new plugin system.
|
|
1136
|
+
|
|
1137
|
+
**Change:** Session ownership is selected by strict `server.session_mode`
|
|
1138
|
+
configuration. Stateful mode remains the stdio default and owns one implicit,
|
|
1139
|
+
multi-kernel runtime. Every HTTP transport requires stateless mode and uses
|
|
1140
|
+
FastMCP's request-stateless transport. In that mode `create_kernel` creates one
|
|
1141
|
+
logical session with one kernel and returns its `session_id`; every later
|
|
1142
|
+
session-bound core or plugin tool requires the explicit ID. Stateless mode adds
|
|
1143
|
+
`kill_session`, removes `switch_kernel` and `list_active_kernels`, and safely
|
|
1144
|
+
expires idle sessions after `server.stateless_session_idle_timeout_s`. Each
|
|
1145
|
+
kernel still owns the config and plugins resolved from its exact cwd. The
|
|
1146
|
+
optional `read_notebook_skill` tool is session-independent and returns the
|
|
1147
|
+
common notebook workflow composed with the stateless lifecycle in this mode.
|
|
1148
|
+
|
|
1149
|
+
**Why:** MCP clients may reconnect or create a new protocol session for every
|
|
1150
|
+
tool call. Keying notebook ownership by that transport detail silently replaced
|
|
1151
|
+
the active runtime, lost Python state, and made `list_active_kernels` appear
|
|
1152
|
+
empty. Logical ownership must survive transport churn and remain visible in the
|
|
1153
|
+
tool contract.
|
|
1154
|
+
|
|
1155
|
+
**Migration:** Remove `--session-isolation` and `--no-session-isolation`. Set
|
|
1156
|
+
`[server] session_mode = "stateless"` for HTTP, or pass
|
|
1157
|
+
`-c 'server.session_mode="stateless"'`. Call `create_kernel` first, retain its
|
|
1158
|
+
logical ID, pass `session_id` to every later tool call, and close it explicitly.
|
|
1159
|
+
HTTP transport lifecycle guidance remains in server instructions and
|
|
1160
|
+
`ipi/skills/notebook-http/SKILL.md`; `read_notebook_skill` returns it together
|
|
1161
|
+
with the common notebook workflow. Local stdio agents continue to use
|
|
1162
|
+
`ipi/skills/notebook/SKILL.md`. Plugins must read active state from their
|
|
1163
|
+
injected `ToolContext`, not module globals or the server startup cwd.
|
|
1164
|
+
|
|
1165
|
+
### Explicit programmatic runtime ownership
|
|
1166
|
+
|
|
1167
|
+
**Status:** Implemented.
|
|
1168
|
+
|
|
1169
|
+
**Change:** `create_server` now accepts an explicit `session_mode`.
|
|
1170
|
+
`shared_manager=` is valid only for stateful mode. `manager_factory=` is valid
|
|
1171
|
+
only for stateless mode and receives the newly generated logical session ID.
|
|
1172
|
+
Omitting the applicable owner constructs the corresponding default manager or
|
|
1173
|
+
registry.
|
|
1174
|
+
|
|
1175
|
+
**Why:** The old parameter names did not describe whether state was shared or
|
|
1176
|
+
isolated. Injecting a mutable registry also split ownership across the caller
|
|
1177
|
+
and server and could cache a manager before server plugin binding succeeded.
|
|
1178
|
+
The factory boundary keeps construction, binding, insertion, and shutdown under
|
|
1179
|
+
one owner.
|
|
1180
|
+
|
|
1181
|
+
**Migration:** Pass `session_mode="stateful"` with `shared_manager=` for the
|
|
1182
|
+
implicit multi-kernel server. Pass `session_mode="stateless"` with
|
|
1183
|
+
`manager_factory=lambda session_id: CustomRuntimeManager(session_id=...)` for
|
|
1184
|
+
explicit one-kernel sessions. Supplying an owner intended for the other mode is
|
|
1185
|
+
an error.
|
|
1186
|
+
|
|
1187
|
+
### Initial built-in plugin IDs and image result
|
|
1188
|
+
|
|
1189
|
+
**Status:** Implemented.
|
|
1190
|
+
|
|
1191
|
+
**Change:** The lightweight AGENTS and automatic context behavior now use
|
|
1192
|
+
reserved built-in IDs `ipi.agents_md` and `ipi.auto_context`. The image tool is
|
|
1193
|
+
the server plugin `ipi.view_image` and returns native MCP `ImageContent` for
|
|
1194
|
+
PNG, JPEG, GIF, and WebP rather than a text-only path description. The old
|
|
1195
|
+
`%%view_image` magic is not retained.
|
|
1196
|
+
|
|
1197
|
+
**Why:** Built-ins should exercise the same small public contract as external
|
|
1198
|
+
plugins. Native MCP image content lets capable clients render the actual file
|
|
1199
|
+
without provider-specific text or base64 conventions.
|
|
1200
|
+
|
|
1201
|
+
**Migration:** Replace old `ipi-plugin-*` config tables with
|
|
1202
|
+
`[plugins.config."ipi.<name>"]` tables and disabled IDs with the new reserved
|
|
1203
|
+
IDs. Call the `view_image(path=...)` MCP tool directly; remove uses of the cell
|
|
1204
|
+
magic or assumptions that its result is plain text.
|
|
1205
|
+
|
|
1206
|
+
### Internal extension tool calls
|
|
1207
|
+
|
|
1208
|
+
**Status:** Implemented.
|
|
1209
|
+
|
|
1210
|
+
**Change:** `ctx.impersonate(...)`, the `impersonated` tool-hook flag, and the
|
|
1211
|
+
synthetic tool-call helpers have been replaced by `ctx.run_tool(...)`, the
|
|
1212
|
+
`internal` flag, and `run_internal_tool(...)`. Internal calls no longer append
|
|
1213
|
+
a synthetic assistant-message record.
|
|
1214
|
+
|
|
1215
|
+
**Why:** IPi is no longer an LLM provider or transcript owner. Startup probes
|
|
1216
|
+
and debugger automation still need to execute tools through normal hooks, but
|
|
1217
|
+
representing those operations as an assistant message is inaccurate and tied
|
|
1218
|
+
the execution core to provider wire formats.
|
|
1219
|
+
|
|
1220
|
+
**Migration:** Replace `ctx.impersonate(name, ...)` with
|
|
1221
|
+
`ctx.run_tool(name, ...)` and `ctx.impersonated` with `ctx.internal`. Consumers
|
|
1222
|
+
of session JSONL must no longer expect an assistant-message record before
|
|
1223
|
+
host-initiated tool calls; the tool-call and result records remain.
|
|
1224
|
+
|
|
1225
|
+
### Canonical MCP tool arguments
|
|
1226
|
+
|
|
1227
|
+
**Status:** Implemented.
|
|
1228
|
+
|
|
1229
|
+
**Change:** Provider-specific tool normalization, custom/freeform tool metadata,
|
|
1230
|
+
Lark grammar declarations, wire serializers, and the public
|
|
1231
|
+
`ToolCallRecord.wire_kind` field have been removed. PDB now accepts the normal
|
|
1232
|
+
MCP argument object `{"command": "..."}` only. The live-only record schema is
|
|
1233
|
+
strict; existing JSONL records with a `wire_kind` key must be migrated before
|
|
1234
|
+
IPi reads them.
|
|
1235
|
+
|
|
1236
|
+
**Why:** The deleted embedded providers were the only consumers of alternate
|
|
1237
|
+
wire shapes. Retaining two representations forced every execution and record
|
|
1238
|
+
path to normalize data that already arrives from MCP in canonical form.
|
|
1239
|
+
|
|
1240
|
+
**Migration:** Extension tools must define one typed MCP argument schema and
|
|
1241
|
+
consume it directly. Remove `freeform`, `grammar`, `normalize_arguments`, and
|
|
1242
|
+
`wire_arguments` members. Replace raw PDB custom-tool bodies with the `command`
|
|
1243
|
+
field.
|
|
1244
|
+
|
|
1245
|
+
### Conversation record removal
|
|
1246
|
+
|
|
1247
|
+
**Status:** Implemented.
|
|
1248
|
+
|
|
1249
|
+
**Change:** `UserMessageRecord`, `AssistantMessageRecord`, `ProviderMessage`,
|
|
1250
|
+
`ContextRecord`, `UnknownRecord`, visibility/render policies, provider-history
|
|
1251
|
+
rendering, baked context-text parsing, and user-message rewind helpers have been
|
|
1252
|
+
removed. The session log accepts only the current execution record schema;
|
|
1253
|
+
unknown and provider-era lines are no longer preserved or interpreted. Output
|
|
1254
|
+
summaries affect only the current MCP tool result after durable persistence.
|
|
1255
|
+
|
|
1256
|
+
**Why:** Codex and Claude Code own the conversation transcript, compaction, and
|
|
1257
|
+
rewind lifecycle. IPi owns notebook execution records and output artifacts.
|
|
1258
|
+
Maintaining a second partial transcript created incorrect ownership and coupled
|
|
1259
|
+
kernel persistence to provider request formats.
|
|
1260
|
+
|
|
1261
|
+
**Migration:** Consumers of session JSONL should use the current cell, bash,
|
|
1262
|
+
tool-call, tool-result, and kernel event records only. Read conversation and
|
|
1263
|
+
context history from the active agent harness. Migrate any retained legacy log
|
|
1264
|
+
before passing it to IPi; the runtime does not provide a compatibility loader.
|
|
1265
|
+
|
|
1266
|
+
### Session resume and fork removal
|
|
1267
|
+
|
|
1268
|
+
**Status:** Implemented.
|
|
1269
|
+
|
|
1270
|
+
**Change:** `Session.open`, `SessionInfo`, `list_sessions`, `resolve_session`,
|
|
1271
|
+
`fork_session`, and the associated resolution errors and artifact-copy helpers
|
|
1272
|
+
have been removed. A session exists only for its live MCP connection and is not
|
|
1273
|
+
reopened after process exit.
|
|
1274
|
+
|
|
1275
|
+
**Why:** The agreed product has disposable per-connection kernels and no restart
|
|
1276
|
+
resumability. Keeping discovery and fork code implied a durable lifecycle that
|
|
1277
|
+
the kernel processes, plugin environments, and HTTP ownership model did not
|
|
1278
|
+
support.
|
|
1279
|
+
|
|
1280
|
+
**Migration:** Treat `.agents/sessions/<id>` as an append-only execution and
|
|
1281
|
+
artifact directory, not a resumable IPi object. Inspect or copy files with
|
|
1282
|
+
ordinary filesystem tools when archival workflows need them.
|
|
1283
|
+
|
|
1284
|
+
### Embedded provider runtime removal
|
|
1285
|
+
|
|
1286
|
+
**Status:** Implemented.
|
|
1287
|
+
|
|
1288
|
+
**Change:** The internal `Provider` API, provider stream/response models,
|
|
1289
|
+
`LLMSessionInfo`, `ipi.llm`, `ipi.runtime.llm`, `%ipi_set_session`, provider
|
|
1290
|
+
debug sinks, credential storage, reasoning models, and agent-side LLM helpers
|
|
1291
|
+
have been removed. Kernels no longer receive provider/model metadata in their
|
|
1292
|
+
environment or notebook metadata.
|
|
1293
|
+
|
|
1294
|
+
**Why:** IPi does not call an LLM in the MCP product. Codex and Claude Code own
|
|
1295
|
+
models, credentials, reasoning settings, provider requests, and conversation
|
|
1296
|
+
state. Propagating a fake `provider_name="mcp"` session into every kernel added
|
|
1297
|
+
state and API surface without representing a real capability.
|
|
1298
|
+
|
|
1299
|
+
**Migration:** Remove imports from `ipi.tools.providers`, `ipi.tools.agent`,
|
|
1300
|
+
`ipi.auth`, `ipi.reasoning`, `ipi.llm`, and `ipi.runtime.llm`. Extensions that
|
|
1301
|
+
need harness event data must use the new typed Codex/Claude harness hook API;
|
|
1302
|
+
kernel extensions must not infer the active model.
|
|
1303
|
+
|
|
1304
|
+
### Public prompt helper removal
|
|
1305
|
+
|
|
1306
|
+
**Status:** Implemented.
|
|
1307
|
+
|
|
1308
|
+
**Change:** `ipi.prompts`, `Prompt`, and the public prompt/tag helper package
|
|
1309
|
+
have been removed. IPi retains only a private model-output renderer used by its
|
|
1310
|
+
own MCP result formatting.
|
|
1311
|
+
|
|
1312
|
+
**Why:** IPi no longer assembles system prompts or owns model conversations.
|
|
1313
|
+
Keeping a public prompt object after deleting the embedded agent loop preserved
|
|
1314
|
+
an API with no supported product consumer.
|
|
1315
|
+
|
|
1316
|
+
**Migration:** Build any harness-owned prompt text in Codex or Claude Code.
|
|
1317
|
+
Plugins should return typed harness results, context strings, or ordinary MCP
|
|
1318
|
+
tool values rather than importing IPi prompt helpers.
|
|
1319
|
+
|
|
1320
|
+
### Legacy tool abstraction removal
|
|
1321
|
+
|
|
1322
|
+
**Status:** Implemented.
|
|
1323
|
+
|
|
1324
|
+
**Change:** The kernel-side `ipi.tools` priority registry, canonical `Tool`
|
|
1325
|
+
object classes, provider/TUI tool-name aliases, and IPi's internal
|
|
1326
|
+
`apply_patch` parser and dispatcher have been removed. IPi now retains only
|
|
1327
|
+
the argument validation used by its fixed notebook-control dispatchers and the
|
|
1328
|
+
packaged Notebook Agent Skill helpers. `apply_patch` remains a capability of
|
|
1329
|
+
supporting coding harnesses, not an IPi MCP tool.
|
|
1330
|
+
|
|
1331
|
+
**Why:** None of these abstractions had an MCP consumer. The legacy
|
|
1332
|
+
`apply_patch` dispatcher targeted a `%%apply_patch` kernel magic that no longer
|
|
1333
|
+
exists, and the provider-name translation layer described an embedded agent
|
|
1334
|
+
runtime that IPi no longer owns.
|
|
1335
|
+
|
|
1336
|
+
**Migration:** Register first-class MCP tools through `ServerSetup.tool()` and
|
|
1337
|
+
kernel behavior through `EnvironmentSetup` callbacks. Use `ipi.plugins.ToolContext`
|
|
1338
|
+
for tool access to the active session and kernel. Remove imports from
|
|
1339
|
+
`ipi.tools`, `ipi.tools.names`, and `ipi._patch`; call the coding harness's
|
|
1340
|
+
native patch tool when file patching is required.
|
|
1341
|
+
|
|
1342
|
+
### Single distribution and MCP extra
|
|
1343
|
+
|
|
1344
|
+
**Status:** Implemented.
|
|
1345
|
+
|
|
1346
|
+
**Change:** The `ipi-core`, `ipi-runtime`, `ipi-mcp`, `ipi-tui`, and bundled
|
|
1347
|
+
`ipi-plugin-*` workspace distributions were replaced by one installable
|
|
1348
|
+
`ipi` distribution. Built-in plugins become modules in that distribution. The
|
|
1349
|
+
base dependency set supports kernel/shared code; the `mcp` extra supplies host,
|
|
1350
|
+
Jupyter, and harness dependencies.
|
|
1351
|
+
|
|
1352
|
+
**Why:** The workspace members share one version, repository, release, and
|
|
1353
|
+
runtime product. Their manifests and exact cross-pins add resolution and
|
|
1354
|
+
maintenance complexity without providing independent ownership or reuse. A
|
|
1355
|
+
clean uv experiment verified that a Git-installed `ipi[mcp]` host can launch a
|
|
1356
|
+
base `ipi` kernel pinned to the same resolved commit without installing host-only
|
|
1357
|
+
dependencies in the kernel.
|
|
1358
|
+
|
|
1359
|
+
**Migration:** Replace dependencies on internal IPi distributions with `ipi`.
|
|
1360
|
+
Replace `ipi_mcp` imports with `ipi.server`; imports from the shared `ipi.*`
|
|
1361
|
+
modules otherwise retain their names. Replace `ipi-mcp` launchers with:
|
|
1362
|
+
|
|
1363
|
+
```bash
|
|
1364
|
+
uvx --isolated --from "ipi[mcp] @ git+https://github.com/nimashoghi/ipi.git@<ref>" ipi
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
External plugins remain separate distributions using the `ipi.extensions`
|
|
1368
|
+
entry-point group and are added to the host with `uvx --with`. IPi installs its
|
|
1369
|
+
base distribution into each child kernel at the exact source checkout, Git
|
|
1370
|
+
commit, or released version used by the host; launchers must not add the `mcp`
|
|
1371
|
+
extra to child kernels.
|
|
1372
|
+
|
|
1373
|
+
### Host pins and kernel compatibility ranges
|
|
1374
|
+
|
|
1375
|
+
**Status:** Implemented.
|
|
1376
|
+
|
|
1377
|
+
**Change:** IPi now exactly pins its build dependency and every direct host
|
|
1378
|
+
dependency in the `mcp` extra to the versions validated for the release. The
|
|
1379
|
+
extra repeats exact versions for base packages used by the host. Base
|
|
1380
|
+
dependencies retain bounded compatibility ranges so a child-kernel overlay can
|
|
1381
|
+
select another supported version. `typed-agent-hooks` remains pinned to an
|
|
1382
|
+
immutable Git commit.
|
|
1383
|
+
|
|
1384
|
+
**Why:** `uvx` installs from wheel metadata and does not consume the
|
|
1385
|
+
repository's `uv.lock`. Broad lower bounds allowed an unchanged Git tag to
|
|
1386
|
+
resolve a different FastMCP, MCP, IPython, ipykernel, Notebook, or Plotly release
|
|
1387
|
+
depending on installation date. IPi is an application whose host and child
|
|
1388
|
+
runtime need a tested compatibility envelope.
|
|
1389
|
+
|
|
1390
|
+
**Migration:** Resolve plugin host dependencies against the exact direct host
|
|
1391
|
+
versions in the target IPi release. When a plugin genuinely needs a conflicting
|
|
1392
|
+
host dependency, release and validate a new IPi dependency set instead of
|
|
1393
|
+
relying on the installer to choose an untested combination. Kernel-only
|
|
1394
|
+
dependencies may select any version inside the declared base ranges when they
|
|
1395
|
+
do not conflict with IPi or another plugin contribution. Do not treat wheel
|
|
1396
|
+
metadata as a complete transitive lock; use the committed `uv.lock` for
|
|
1397
|
+
development and CI reproduction.
|
|
1398
|
+
|
|
1399
|
+
### Server package import boundary
|
|
1400
|
+
|
|
1401
|
+
**Status:** Implemented.
|
|
1402
|
+
|
|
1403
|
+
**Change:** `ipi.server` no longer eagerly imports or re-exports
|
|
1404
|
+
`create_server`. Importing the package now works in a base child-kernel install,
|
|
1405
|
+
while executing the `ipi` command without the `mcp` extra reports the missing
|
|
1406
|
+
extra directly instead of raising `ModuleNotFoundError: fastmcp`.
|
|
1407
|
+
|
|
1408
|
+
**Why:** The base distribution is intentionally usable in child kernels without
|
|
1409
|
+
FastMCP or other host-only dependencies. Eager package initialization violated
|
|
1410
|
+
that boundary and made even metadata-level imports fail.
|
|
1411
|
+
|
|
1412
|
+
**Migration:** Replace:
|
|
1413
|
+
|
|
1414
|
+
```python
|
|
1415
|
+
from ipi.server import create_server
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
with:
|
|
1419
|
+
|
|
1420
|
+
```python
|
|
1421
|
+
from ipi.server.server import create_server
|
|
1422
|
+
```
|
|
1423
|
+
|
|
1424
|
+
Launch the server from `ipi[mcp]`; the base-only console entry point is not a
|
|
1425
|
+
server installation.
|
|
1426
|
+
|
|
1427
|
+
### Exact kernel dependency and plugin provenance
|
|
1428
|
+
|
|
1429
|
+
**Status:** Implemented.
|
|
1430
|
+
|
|
1431
|
+
**Change:** Caller and plugin kernel dependencies are now resolved by canonical
|
|
1432
|
+
distribution name before launch. Exact duplicates are deduplicated, but
|
|
1433
|
+
different requirements or editable modes for the same distribution are
|
|
1434
|
+
rejected. Explicit `pip` and `ipykernel` requirements replace IPi's defaults.
|
|
1435
|
+
Versioned and direct requirements are no longer rewritten to an implicit
|
|
1436
|
+
workspace editable.
|
|
1437
|
+
|
|
1438
|
+
Installed plugin selection validates the configured requirement against the
|
|
1439
|
+
host distribution version and PEP 610 `direct_url.json`. The child validates
|
|
1440
|
+
its installed version, direct source, owning distribution, and entry-point
|
|
1441
|
+
target before loading the callback. Directory distributions prove their owned
|
|
1442
|
+
files or bounded editable runtime tree plus resolved import roots. Built-in,
|
|
1443
|
+
user, and project runtime trees are hashed at discovery and must have the same
|
|
1444
|
+
content when the child imports them.
|
|
1445
|
+
|
|
1446
|
+
**Why:** Allowing pip to resolve conflicting plugin contributions made the
|
|
1447
|
+
child source depend on argument order and resolver behavior. Rewriting pinned
|
|
1448
|
+
requirements to a nearby checkout silently violated the selected source.
|
|
1449
|
+
Loading an entry point by name alone could then run a different plugin version,
|
|
1450
|
+
Git revision, archive, subdirectory, or mutated local file in the child.
|
|
1451
|
+
|
|
1452
|
+
**Migration:** Make every contributor use one identical PEP 508 requirement and
|
|
1453
|
+
editable mode for each distribution. Declare an editable local path explicitly
|
|
1454
|
+
when live source is intended; relative paths passed through `EnvironmentSetup`
|
|
1455
|
+
resolve against that environment's cwd. Ensure selected direct URLs match the
|
|
1456
|
+
host installation's VCS source/revision, archive hash, and subdirectory;
|
|
1457
|
+
reinstall the host distribution from the intended source when they do not.
|
|
1458
|
+
Finish editing local plugin code before creating a kernel, or create a new
|
|
1459
|
+
kernel after rediscovery. Restart the IPi process after changing an installed
|
|
1460
|
+
directory/editable plugin: once its module is admitted, later source drift is
|
|
1461
|
+
rejected rather than executing bytecode from a different revision. A
|
|
1462
|
+
kernel-enabled entry-point module is imported in both the host and base-only
|
|
1463
|
+
child, so move host-only imports inside host callbacks and declare all
|
|
1464
|
+
module-level and kernel imports as child dependencies.
|
|
1465
|
+
|
|
1466
|
+
### Credential-safe dependency reporting
|
|
1467
|
+
|
|
1468
|
+
**Status:** Implemented.
|
|
1469
|
+
|
|
1470
|
+
**Change:** Authenticated PEP 508 URLs remain available only in transient launch
|
|
1471
|
+
inputs and private live-kernel state. User information is removed from notebook
|
|
1472
|
+
metadata, child plugin payloads, session records, manifests, launch diagnostics,
|
|
1473
|
+
logs, kernel inventories, snapshots, MCP-visible runtime state, and validation,
|
|
1474
|
+
plugin-setup, or kernel-creation errors.
|
|
1475
|
+
|
|
1476
|
+
**Why:** A private Git requirement must remain usable by `uv`, but copying that
|
|
1477
|
+
requirement into durable artifacts made it easy to commit a credential by
|
|
1478
|
+
accident. Provenance identity and child validation do not require URL user
|
|
1479
|
+
information.
|
|
1480
|
+
|
|
1481
|
+
**Migration:** Do not read authenticated requirements back from inventory or
|
|
1482
|
+
session output. For private Git metadata with redacted credentials, provide the
|
|
1483
|
+
matching authenticated source through `IPI_INSTALL_REQUIREMENT` for child
|
|
1484
|
+
launches. `IPI_UV_WITH` is deprecated. IPi verifies repository identity and
|
|
1485
|
+
never writes these credential-bearing values to its artifacts. Hook forwarding
|
|
1486
|
+
now runs from the public `typed-agent-hooks` source and needs no IPi repository
|
|
1487
|
+
credential.
|
|
1488
|
+
`IPI_INSTALL_REQUIREMENT` authenticates only the IPi overlay itself and is no
|
|
1489
|
+
longer reused for unrelated installed plugin distributions. Private plugins
|
|
1490
|
+
must supply their own usable direct requirement through their plugin dependency
|
|
1491
|
+
declaration or host installation.
|
|
1492
|
+
|
|
1493
|
+
### Strict and credential-safe configuration errors
|
|
1494
|
+
|
|
1495
|
+
**Status:** Implemented.
|
|
1496
|
+
|
|
1497
|
+
**Change:** `load_config()` now converts Pydantic `ValidationError` values into
|
|
1498
|
+
an attributed, input-free `ConfigError`. A missing TOML file remains optional,
|
|
1499
|
+
but permission errors, directory paths, and other non-missing I/O failures now
|
|
1500
|
+
propagate instead of silently loading defaults. `PluginSetupError` no longer
|
|
1501
|
+
retains a raw source or callback exception; its safe fields are `source_kind`,
|
|
1502
|
+
`source_location`, `error_type`, and `detail`. Runtime operations preserve a
|
|
1503
|
+
safe exception unchanged and replace only an exception whose text contains URL
|
|
1504
|
+
user information before FastMCP renders or logs it.
|
|
1505
|
+
|
|
1506
|
+
Boolean tool/Jupyter fields and integer Jupyter fields are now strict: strings,
|
|
1507
|
+
integer booleans, floats, and Python booleans used as ports are not coerced.
|
|
1508
|
+
`jupyter.tunnel = true` requires Jupyter itself to be enabled. The named
|
|
1509
|
+
Jupyter tunnel fields described by the original change were subsequently
|
|
1510
|
+
removed; see "Jupyter Cloudflare tunnels are quick-tunnel only" above.
|
|
1511
|
+
|
|
1512
|
+
**Why:** Pydantic's default error text includes the raw input value, and Python
|
|
1513
|
+
exception chains retain the original callback exception. Sanitizing only the
|
|
1514
|
+
outer message still exposed authenticated requirements through MCP failures,
|
|
1515
|
+
logs, or formatted tracebacks. Treating every `OSError` as a missing config also
|
|
1516
|
+
made an unreadable project silently run with unrelated defaults.
|
|
1517
|
+
|
|
1518
|
+
**Migration:** Catch `ConfigError`, not Pydantic `ValidationError`, around
|
|
1519
|
+
`load_config()`. Code inspecting `PluginSetupError.source` or `.error` must use
|
|
1520
|
+
the safe fields above. Fix filesystem access errors rather than expecting IPi to
|
|
1521
|
+
ignore them. Error consumers must not depend on authenticated URL user
|
|
1522
|
+
information being present in an exception string. Replace coercible config
|
|
1523
|
+
values with literal TOML booleans and integers, and make tunnel dependencies
|
|
1524
|
+
explicit.
|
|
1525
|
+
|
|
1526
|
+
### Immutable content-addressed output generations
|
|
1527
|
+
|
|
1528
|
+
**Status:** Implemented.
|
|
1529
|
+
|
|
1530
|
+
**Change:** Cell inputs and output artifacts now live below
|
|
1531
|
+
`outputs/cN/g-<sha256-prefix>/` rather than directly below `outputs/cN/`.
|
|
1532
|
+
The usual prefix is eight hexadecimal characters and extends automatically if
|
|
1533
|
+
another generation has the same prefix. Each generation stores its full digest
|
|
1534
|
+
in `.sha256`, and `manifest.json` records it as `generation_sha256`. Every path
|
|
1535
|
+
returned in a tool result or written to a session record names an immutable
|
|
1536
|
+
snapshot. Late output, `clear_output`, and cross-cell display updates publish a
|
|
1537
|
+
new generation and atomically replace `outputs/manifest.json`; they do not
|
|
1538
|
+
rewrite a path already given to an agent. Prior generations remain readable for
|
|
1539
|
+
the lifetime of the session.
|
|
1540
|
+
|
|
1541
|
+
**Why:** Reusing one filename meant a late update or failed manifest commit
|
|
1542
|
+
could change or delete bytes referenced by an earlier tool result or JSONL
|
|
1543
|
+
record. Publishing complete immutable generations before swapping one manifest
|
|
1544
|
+
keeps old references valid and makes the latest-state commit failure-atomic.
|
|
1545
|
+
|
|
1546
|
+
**Migration:** Do not construct artifact paths from handles or assume an
|
|
1547
|
+
artifact path will change in place. Use paths recorded in a tool result or
|
|
1548
|
+
`session.jsonl` when the exact response-time snapshot is required. Read
|
|
1549
|
+
`outputs/manifest.json` and follow its recorded input/artifact paths when the
|
|
1550
|
+
latest reduced state is required, including late display updates.
|
|
1551
|
+
|
|
1552
|
+
### Compact lossless result protocol and identifiers
|
|
1553
|
+
|
|
1554
|
+
**Status:** Implemented.
|
|
1555
|
+
|
|
1556
|
+
**Change:** Model-visible MCP text results no longer use XML-shaped tags. They
|
|
1557
|
+
use tab-separated line records. Fields escape backslash, tab, carriage return,
|
|
1558
|
+
and line feed reversibly. A multiline header ends with an exact
|
|
1559
|
+
Unicode-code-point count; its body remains unchanged and the record ends with
|
|
1560
|
+
`end<TAB><record-name>`. Alternative
|
|
1561
|
+
artifact `part` records follow their exact output preview and carry its ordinal.
|
|
1562
|
+
Kernel inventories use counted tables with explicit columns and `\N` for null.
|
|
1563
|
+
|
|
1564
|
+
Persisted Python and bash inputs are returned as `input<TAB><language><TAB><path>`
|
|
1565
|
+
references instead of echoing source already present in the tool call. The path
|
|
1566
|
+
names the exact immutable input artifact. Output bodies, errors, state changes,
|
|
1567
|
+
artifact MIME types, alternative parts, and suspension actions remain in the
|
|
1568
|
+
result. When the automatic PDB stack repeats a suspension location and exception,
|
|
1569
|
+
the live response emits those details once while the durable cell record retains
|
|
1570
|
+
its concise summary.
|
|
1571
|
+
|
|
1572
|
+
Session directories now use `<inverted-nanoseconds>-<random-hex>` instead of
|
|
1573
|
+
also repeating a readable UTC timestamp. The inverted timestamp still preserves
|
|
1574
|
+
exact creation time and newest-first lexical ordering. PEP 610 build output
|
|
1575
|
+
omits a requested revision only when it is byte-for-byte identical to the full
|
|
1576
|
+
commit ID. Shortened hashes remain visible because the metadata cannot
|
|
1577
|
+
distinguish one from a hexadecimal branch or tag name.
|
|
1578
|
+
|
|
1579
|
+
**Why:** Structural punctuation, repeated source, high-entropy path text, and
|
|
1580
|
+
duplicate identity fields consumed model context without adding information.
|
|
1581
|
+
The replacement keeps arbitrary bodies exact and makes every omitted repetition
|
|
1582
|
+
recoverable from an explicit artifact or durable record.
|
|
1583
|
+
|
|
1584
|
+
**Migration:** Replace XML parsing and literal tag assertions with the line
|
|
1585
|
+
grammar above. Use the Unicode-code-point count, not a search for the terminator,
|
|
1586
|
+
to read multiline bodies. Split ordinary fields on tabs and reverse their
|
|
1587
|
+
escapes; treat `\N` as null only in table cells. Resolve input and output paths
|
|
1588
|
+
below the printed kernel output directory. Treat session and generation
|
|
1589
|
+
directory names as opaque identifiers; read `.sha256` or `manifest.json` when
|
|
1590
|
+
the full generation digest is required.
|
|
1591
|
+
|
|
1592
|
+
### Bounded MCP output delivery
|
|
1593
|
+
|
|
1594
|
+
**Status:** Implemented.
|
|
1595
|
+
|
|
1596
|
+
**Change:** Individual text previews are limited to 64,000 characters and the
|
|
1597
|
+
aggregate content returned by one MCP tool call is limited to 256,000 UTF-8
|
|
1598
|
+
bytes. A bounded response retains its head and tail and includes the session
|
|
1599
|
+
artifact directory. Complete retained raw kernel outputs are still written to
|
|
1600
|
+
disk.
|
|
1601
|
+
|
|
1602
|
+
**Why:** Unbounded MCP results can exceed client or model context limits and can
|
|
1603
|
+
make otherwise successful notebook execution unusable. Persistence, rather than
|
|
1604
|
+
the model-facing preview, is the authoritative output record.
|
|
1605
|
+
|
|
1606
|
+
**Migration:** Consumers that require complete large outputs must read the
|
|
1607
|
+
artifact path recorded in the cell output or inspect the session's `kernels/`
|
|
1608
|
+
directory. Do not treat an MCP response preview as a lossless transport for
|
|
1609
|
+
arbitrarily large stdout, stderr, display data, or result text.
|
|
1610
|
+
|
|
1611
|
+
### Typed Codex and Claude hook contract
|
|
1612
|
+
|
|
1613
|
+
**Status:** Implemented.
|
|
1614
|
+
|
|
1615
|
+
**Change:** Harness plugins now subscribe with `EnvironmentSetup.on_harness()`
|
|
1616
|
+
or `observe_harness()` using exact `typed-agent-hooks` shared or provider-native
|
|
1617
|
+
event classes. Both callbacks must be declared with `async def`; synchronous
|
|
1618
|
+
callbacks are rejected during plugin setup. The old `harness:*` string events
|
|
1619
|
+
and mutable `Harness*Ctx` values are no longer dispatched. Hook installation
|
|
1620
|
+
forwards every native event for the selected provider. For a recognized harness
|
|
1621
|
+
ancestor, failure to attach its expected bridge stops
|
|
1622
|
+
stdio startup; an unrecognized ancestor simply runs without forwarding.
|
|
1623
|
+
Individual hook callbacks still fail open.
|
|
1624
|
+
|
|
1625
|
+
**Why:** Parallel wrapper models drifted from the provider schemas and made event
|
|
1626
|
+
composition, correlation, and failure semantics implicit. Direct typed models
|
|
1627
|
+
keep Codex and Claude behavior explicit while preserving a small IPi-owned
|
|
1628
|
+
runtime snapshot. Async-only callbacks can run on IPi's isolated callback loop;
|
|
1629
|
+
arbitrary synchronous Python cannot be stopped at a deadline.
|
|
1630
|
+
|
|
1631
|
+
**Migration:** Replace string event registrations with typed event classes and
|
|
1632
|
+
return `typed_agent_hooks.shared.outputs` result values rather than mutating a
|
|
1633
|
+
context object. Convert every registered handler and observer from `def` to
|
|
1634
|
+
`async def`, use async I/O, and allow cancellation to propagate. Re-run
|
|
1635
|
+
`ipi install-hooks --provider codex` or
|
|
1636
|
+
`ipi install-hooks --provider claude_code` and restart the harness so all native
|
|
1637
|
+
events are installed.
|
|
1638
|
+
|
|
1639
|
+
### Isolated hook callbacks and hard deadlines
|
|
1640
|
+
|
|
1641
|
+
**Status:** Implemented.
|
|
1642
|
+
|
|
1643
|
+
**Change:** `EnvironmentSetup.context()` now accepts only providers declared
|
|
1644
|
+
with `async def`, matching harness handlers and native observers. The timed hook
|
|
1645
|
+
path no longer loads config, discovers or imports plugins, or runs setup
|
|
1646
|
+
callbacks. It uses the pre-resolved startup environment for the exact startup
|
|
1647
|
+
cwd or the atomically published active environment for its exact cwd. An event
|
|
1648
|
+
from any other cwd fails open without plugin output.
|
|
1649
|
+
|
|
1650
|
+
Actual handlers, observers, and context providers now run on one IPi-owned
|
|
1651
|
+
daemon event-loop thread rather than FastMCP's serving loop. The five-second
|
|
1652
|
+
callback and 25-second dispatch limits are hard response deadlines: IPi requests
|
|
1653
|
+
cancellation, fails open, and ignores a late result without waiting for a
|
|
1654
|
+
callback that suppresses `CancelledError`. Shutdown cancels submitted callback
|
|
1655
|
+
futures and boundedly joins the worker; truly stuck user code remains only on a
|
|
1656
|
+
daemon thread and cannot keep the MCP process alive.
|
|
1657
|
+
|
|
1658
|
+
`HarnessRuntime.manager` has been removed. The callback runtime exposes only
|
|
1659
|
+
the immutable `snapshot`, `environment`, and `config` data needed to observe the
|
|
1660
|
+
current IPi state.
|
|
1661
|
+
|
|
1662
|
+
**Why:** Cancellation-resistant plugin code must not block every HTTP operation,
|
|
1663
|
+
prevent the server event loop from closing, or retain direct access to mutable
|
|
1664
|
+
manager operations from another event loop. A daemon callback owner makes the
|
|
1665
|
+
response and process-lifecycle boundaries enforceable even though Python cannot
|
|
1666
|
+
forcibly terminate arbitrary user code.
|
|
1667
|
+
|
|
1668
|
+
**Migration:** Convert every context provider to `async def`, use async I/O, and
|
|
1669
|
+
propagate `CancelledError`; code that suppresses it may continue running, but its
|
|
1670
|
+
result is discarded. Replace `runtime.manager` access with
|
|
1671
|
+
`runtime.snapshot`, `runtime.environment`, or `runtime.config`. Do not capture
|
|
1672
|
+
FastMCP-loop-bound futures, tasks, transports, locks, or clients in a callback;
|
|
1673
|
+
create async resources on the callback loop and use ordinary thread-safe state
|
|
1674
|
+
when sharing data with server setup. Create and activate a kernel for a project
|
|
1675
|
+
cwd, or restart IPi from that cwd, before expecting its project-specific hooks.
|
|
1676
|
+
Do not rely on a hook event to discover a new project environment.
|
|
1677
|
+
|
|
1678
|
+
### Harness forwarding uses typed-agent-hooks directly
|
|
1679
|
+
|
|
1680
|
+
**Status:** Implemented.
|
|
1681
|
+
|
|
1682
|
+
**Change:** `ipi install-hooks` now writes a self-bootstrapping TAH forwarding
|
|
1683
|
+
command from the installed TAH Git source.
|
|
1684
|
+
IPi no longer installs an `ipi-hook-forward` tool environment or exposes the
|
|
1685
|
+
`ipi forward-hook` command. Hook configuration contains no IPi source or
|
|
1686
|
+
repository credential. Linux uses the existing Unix-socket rendezvous; macOS
|
|
1687
|
+
and stdio processes without a recognized Codex or Claude Code ancestor continue
|
|
1688
|
+
as notebook servers with forwarding inactive.
|
|
1689
|
+
|
|
1690
|
+
**Why:** The forwarder belongs to TAH and imports no IPi or FastMCP runtime.
|
|
1691
|
+
Installing the full private IPi MCP distribution duplicated ownership, created
|
|
1692
|
+
a 156-package environment, required private Git authentication, and could place
|
|
1693
|
+
that environment on a different filesystem from uv's cache. TAH now emits an
|
|
1694
|
+
immutable uvx launcher itself, so its small cached environment survives the
|
|
1695
|
+
installer process without a second environment lifecycle.
|
|
1696
|
+
|
|
1697
|
+
**Migration:** Re-run `ipi install-hooks` for Codex and Claude Code, then restart
|
|
1698
|
+
the harness. Keep `uv` available when hooks run. Remove
|
|
1699
|
+
`IPI_HOOK_INSTALL_REQUIREMENT` and `IPI_HOOK_RUNTIME_ROOT`; they are no longer
|
|
1700
|
+
read. The old `~/.local/share/ipi/hooks` directory can be deleted after both
|
|
1701
|
+
provider configs have been regenerated.
|
|
1702
|
+
|
|
1703
|
+
### Authenticated MCP HTTP tunnels
|
|
1704
|
+
|
|
1705
|
+
**Status:** Implemented.
|
|
1706
|
+
|
|
1707
|
+
**Change:** Non-stdio transports accept `--tunnel` and optional
|
|
1708
|
+
`--tunnel-domain`. A public MCP tunnel now requires
|
|
1709
|
+
`IPI_MCP_HTTP_AUTH_TOKEN`; setup failure stops the server rather than silently
|
|
1710
|
+
advertising a local URL. Stdio rejects tunnel flags.
|
|
1711
|
+
|
|
1712
|
+
**Why:** A public endpoint exposes trusted arbitrary code execution. A local
|
|
1713
|
+
fallback is acceptable for the Jupyter follow view but is misleading for an
|
|
1714
|
+
explicit MCP publication command, and an unauthenticated public tunnel is not a
|
|
1715
|
+
reasonable default even under the trusted-local execution model.
|
|
1716
|
+
|
|
1717
|
+
**Migration:** Set `IPI_MCP_HTTP_AUTH_TOKEN` and give the remote MCP client the
|
|
1718
|
+
same bearer token or `token` query parameter. Use `--tunnel-domain` only with a
|
|
1719
|
+
configured named Cloudflare tunnel; omit `--tunnel` for local HTTP.
|
|
1720
|
+
|
|
1721
|
+
### Built-in initialization and debugger plugins
|
|
1722
|
+
|
|
1723
|
+
**Status:** Implemented.
|
|
1724
|
+
|
|
1725
|
+
**Change:** Autoreload, project import, dataframe formatting, PDB, and Codex Apps
|
|
1726
|
+
now use reserved IDs `ipi.autoreload`, `ipi.project_import`,
|
|
1727
|
+
`ipi.dataframe_display`, `ipi.pdb`, and `ipi.codex_apps`. Startup actions are
|
|
1728
|
+
visible bootstrap cells. Bootstrap failure discards the new kernel and restores
|
|
1729
|
+
the previous active kernel. PDB is a normal annotated plugin tool, and Codex
|
|
1730
|
+
Apps supplies a generated kernel-side `ipi_codex_tools` package instead of
|
|
1731
|
+
dynamic MCP tools. The MCP host owns configuration, credentials, keyring access,
|
|
1732
|
+
network clients, and web search; the child package uses a private local bridge.
|
|
1733
|
+
IPi registers the generated package as top-level `tools` only when that import
|
|
1734
|
+
name is not already owned by the project or another dependency.
|
|
1735
|
+
|
|
1736
|
+
**Why:** These features should exercise the same scoped plugin contract as
|
|
1737
|
+
third-party extensions. Transactional bootstraps prevent a half-initialized
|
|
1738
|
+
kernel from becoming active, and a static MCP catalog matches Codex's current
|
|
1739
|
+
tool-discovery behavior.
|
|
1740
|
+
|
|
1741
|
+
**Migration:** Move configuration to `[plugins.config."ipi.<name>"]`. Import and
|
|
1742
|
+
call Codex app wrappers with `import ipi_codex_tools as tools`. Existing
|
|
1743
|
+
`import tools` cells still work only when the environment has no real `tools`
|
|
1744
|
+
module or package; reusable code must use the canonical name. Do not depend on
|
|
1745
|
+
old `ipi-plugin-*` setup hooks, dynamic Codex-app MCP schemas, child-side Codex
|
|
1746
|
+
credentials or clients, or provider/model metadata inside the kernel.
|
|
1747
|
+
|
|
1748
|
+
### Bounded image reads
|
|
1749
|
+
|
|
1750
|
+
**Status:** Implemented.
|
|
1751
|
+
|
|
1752
|
+
**Change:** `view_image` rejects local or remote files larger than 25 MiB before
|
|
1753
|
+
base64 encoding them into an MCP image result. In remote mode, relative paths
|
|
1754
|
+
resolve against the target kernel cwd; unmapped target-native files are read by
|
|
1755
|
+
a bounded helper on the target instead of being resolved against the host.
|
|
1756
|
+
|
|
1757
|
+
**Why:** Base64 expands the payload and MCP transports generally buffer the
|
|
1758
|
+
complete image. An unbounded read can exhaust the host or client before either
|
|
1759
|
+
can inspect the result. Target-aware resolution also prevents a WSL path from
|
|
1760
|
+
accidentally naming an unrelated host file.
|
|
1761
|
+
|
|
1762
|
+
**Migration:** Resize or recompress larger images, or inspect them through the
|
|
1763
|
+
saved artifact and Jupyter notebook instead of `view_image`. Remote callers do
|
|
1764
|
+
not need a path-map entry for target-native image files.
|
|
1765
|
+
|
|
1766
|
+
The old bundled distributions and transition loader have now been deleted. The
|
|
1767
|
+
Python subprocess hint and subprocess-pip gate are intentionally not replaced:
|
|
1768
|
+
IPi is a trusted local execution environment, and package-install policy belongs
|
|
1769
|
+
to the calling harness or project. Existing users that depended on the one-shot
|
|
1770
|
+
block must enforce that policy outside IPi.
|
|
1771
|
+
|
|
1772
|
+
### Legacy extension runtime removal
|
|
1773
|
+
|
|
1774
|
+
**Status:** Implemented.
|
|
1775
|
+
|
|
1776
|
+
**Change:** `ipi.extension`, `HostExtensionsState`, `HostInitAPI`,
|
|
1777
|
+
`KernelInitAPI`, mutable tool registries, priority overrides, execution hooks,
|
|
1778
|
+
PEP 723 extension sidecars, `.agents/extensions`, and the `[extensions]`
|
|
1779
|
+
configuration namespace have been removed. The bundled `ipi-plugin-*`
|
|
1780
|
+
distributions no longer exist. Core notebook tools use one fixed dispatcher;
|
|
1781
|
+
plugin MCP tools register directly with FastMCP.
|
|
1782
|
+
|
|
1783
|
+
The replacement `EnvironmentSetup` exposes only behavior that has a live
|
|
1784
|
+
consumer: typed harness handlers/observers, context providers, visible
|
|
1785
|
+
bootstrap cells, and kernel requirements. The speculative generic `on(...)`
|
|
1786
|
+
lifecycle API was removed until concrete, stable core events exist.
|
|
1787
|
+
|
|
1788
|
+
**Why:** The legacy system maintained two competing plugin models and carried
|
|
1789
|
+
agent-loop concepts through every kernel launch and tool call. Inert parameters
|
|
1790
|
+
and compatibility loaders made ownership unclear and allowed extensions to
|
|
1791
|
+
replace core state-machine operations. The smaller contract keeps MCP schemas
|
|
1792
|
+
static, kernel setup attributable, and project state isolated without a general
|
|
1793
|
+
dependency-injection framework.
|
|
1794
|
+
|
|
1795
|
+
**Migration:** Use `ipi.plugins.Plugin` and export a direct `plugin` value as
|
|
1796
|
+
described above. Move local files to `~/.ipi/extensions/` or
|
|
1797
|
+
`<project>/.ipi/extensions/`, and move configuration to `[plugins]`. Register
|
|
1798
|
+
model-facing tools with `ServerSetup.tool`; do not attempt to override
|
|
1799
|
+
`python`, `create_kernel`, `switch_kernel`, `wait`, or `interrupt`. Replace
|
|
1800
|
+
legacy execution hooks with a first-class MCP tool, kernel callback, visible
|
|
1801
|
+
bootstrap, context provider, or typed Codex/Claude hook according to the
|
|
1802
|
+
behavior being implemented.
|
|
1803
|
+
|
|
1804
|
+
### Remote project paths require a shared host view
|
|
1805
|
+
|
|
1806
|
+
**Status:** Implemented.
|
|
1807
|
+
|
|
1808
|
+
**Change:** In remote kernel mode, `create_kernel(cwd=...)` now treats `cwd` as
|
|
1809
|
+
the kernel-side POSIX project path and resolves a separate, host-visible project
|
|
1810
|
+
root for `.ipi.toml`, local plugin discovery, MCP callbacks, harness callbacks,
|
|
1811
|
+
and context providers. Kernel creation fails when that host view is unavailable.
|
|
1812
|
+
Local user and project plugin paths are translated back into the kernel view
|
|
1813
|
+
before their kernel callbacks load.
|
|
1814
|
+
|
|
1815
|
+
**Why:** Resolving `/home/...` with the host's `pathlib.Path` silently loaded
|
|
1816
|
+
defaults and plugins from the wrong filesystem under a Windows-host/WSL setup.
|
|
1817
|
+
Host callbacks cannot safely execute project code that exists only on the
|
|
1818
|
+
kernel target, so the shared boundary must be explicit rather than best effort.
|
|
1819
|
+
|
|
1820
|
+
**Migration:** Configure `IPI_KERNEL_PATH_MAP` with semicolon-separated
|
|
1821
|
+
`<host-prefix>=><kernel-prefix>` pairs that make every remote project visible to
|
|
1822
|
+
both processes. For example, map a WSL UNC project root on Windows to its WSL
|
|
1823
|
+
POSIX root. Windows drive paths under `/mnt/<drive>` continue to use the built-in
|
|
1824
|
+
drive mapping. Move host-only project extensions to a shared location or install
|
|
1825
|
+
them as selected `ipi.extensions` distributions.
|
|
1826
|
+
|
|
1827
|
+
### Portable model-facing paths on Windows
|
|
1828
|
+
|
|
1829
|
+
**Status:** Implemented.
|
|
1830
|
+
|
|
1831
|
+
**Change:** Artifact paths stored in cell records and AGENTS context identifiers
|
|
1832
|
+
now use `/` separators on every host platform. Native filesystem operations
|
|
1833
|
+
continue to use `pathlib.Path`; only strings exposed to models, records, and
|
|
1834
|
+
context consumers are normalized.
|
|
1835
|
+
|
|
1836
|
+
**Why:** These values form a portable protocol between IPi, MCP clients, and
|
|
1837
|
+
saved session artifacts. Emitting Windows-only `\\` separators made otherwise
|
|
1838
|
+
identical sessions host-dependent and contradicted the documented relative-path
|
|
1839
|
+
examples.
|
|
1840
|
+
|
|
1841
|
+
**Migration:** Consumers that compare these strings literally must expect `/`
|
|
1842
|
+
on Windows. Convert a returned path with `Path(value)` before performing native
|
|
1843
|
+
filesystem operations instead of constructing paths by splitting on either
|
|
1844
|
+
separator.
|