notebook-intelligence 5.3.1__tar.gz → 5.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/CHANGELOG.md +74 -1
  2. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/CONTRIBUTING.md +8 -1
  3. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/PKG-INFO +82 -25
  4. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/README.md +77 -21
  5. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/docs/admin-guide.md +192 -90
  6. notebook_intelligence-5.4.0/docs/performance-diagnostics.md +344 -0
  7. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/docs/rulesets.md +33 -11
  8. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/docs/troubleshooting.md +8 -0
  9. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/_version.py +1 -1
  10. notebook_intelligence-5.4.0/notebook_intelligence/acp_agent.py +864 -0
  11. notebook_intelligence-5.4.0/notebook_intelligence/acp_mcp_server.py +89 -0
  12. notebook_intelligence-5.4.0/notebook_intelligence/acp_registry.py +159 -0
  13. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/ai_service_manager.py +166 -10
  14. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/api.py +273 -45
  15. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/base_chat_participant.py +57 -2
  16. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/built_in_toolsets.py +67 -2
  17. notebook_intelligence-5.4.0/notebook_intelligence/chat_history_budget.py +919 -0
  18. notebook_intelligence-5.4.0/notebook_intelligence/checks.py +209 -0
  19. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/claude.py +514 -46
  20. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/config.py +39 -1
  21. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/extension.py +1305 -304
  22. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/feature_flags.py +108 -0
  23. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/github_copilot.py +18 -7
  24. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/package.json +18 -3
  25. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/schemas/@plmbr/notebook-intelligence/package.json.orig +17 -2
  26. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/156.7e517ead254858a9eb50.js +1 -0
  27. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/326.7bbdffb7a0499a27928a.js → notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/326.9fc28ccfe3ac71ac4b73.js +2 -2
  28. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/694.97c3499ce07ad99e125a.js +1 -0
  29. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/728.41375a78203cf41e178f.js +1 -0
  30. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/768.7edd3110b64beb9041b7.js +1 -0
  31. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/803.70ff31c9657a3f3e4af6.js +1 -0
  32. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/976.7dffd54c5614c4e8083f.js +1 -0
  33. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/editor.worker.js +1407 -0
  34. notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/remoteEntry.540aae311dd92e1312f1.js +1 -0
  35. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/third-party-licenses.json +29 -29
  36. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/llm_providers/github_copilot_llm_provider.py +22 -4
  37. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/llm_providers/litellm_compatible_llm_provider.py +10 -0
  38. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/llm_providers/openai_compatible_llm_provider.py +10 -0
  39. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/mcp_client.py +20 -1
  40. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/mcp_manager.py +430 -93
  41. notebook_intelligence-5.4.0/notebook_intelligence/mcp_ui_proxy.py +203 -0
  42. notebook_intelligence-5.4.0/notebook_intelligence/perf.py +829 -0
  43. notebook_intelligence-5.4.0/notebook_intelligence/perf_probe.py +1035 -0
  44. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/prompts.py +20 -0
  45. notebook_intelligence-5.4.0/notebook_intelligence/readiness.py +906 -0
  46. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/rule_injector.py +32 -3
  47. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/package.json +17 -2
  48. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/pyproject.toml +18 -3
  49. notebook_intelligence-5.4.0/tests/test_acp_agent.py +490 -0
  50. notebook_intelligence-5.4.0/tests/test_ai_service_manager_dispatch.py +148 -0
  51. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_ai_service_manager_integration.py +51 -0
  52. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_api_hygiene.py +44 -0
  53. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_base_chat_participant_integration.py +179 -1
  54. notebook_intelligence-5.4.0/tests/test_builtin_cell_output.py +51 -0
  55. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_cell_output_features_response.py +74 -0
  56. notebook_intelligence-5.4.0/tests/test_chat_history_budget.py +940 -0
  57. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_client.py +229 -0
  58. notebook_intelligence-5.4.0/tests/test_claude_tool_results.py +441 -0
  59. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_config_integration.py +48 -0
  60. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_feature_flags.py +99 -0
  61. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_github_copilot_llm_provider.py +124 -0
  62. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_image_context.py +12 -0
  63. notebook_intelligence-5.4.0/tests/test_litellm_compatible_llm_provider.py +20 -0
  64. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_mcp_client_shim.py +2 -0
  65. notebook_intelligence-5.4.0/tests/test_mcp_manager.py +718 -0
  66. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_openai_compatible_llm_provider.py +17 -0
  67. notebook_intelligence-5.4.0/tests/test_perf.py +672 -0
  68. notebook_intelligence-5.4.0/tests/test_perf_instrumentation.py +764 -0
  69. notebook_intelligence-5.4.0/tests/test_perf_probe.py +544 -0
  70. notebook_intelligence-5.4.0/tests/test_readiness.py +823 -0
  71. notebook_intelligence-5.4.0/tests/test_response_emitter.py +532 -0
  72. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_rule_injector.py +42 -1
  73. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_user_input_wait.py +41 -2
  74. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_websocket_handler_integration.py +173 -3
  75. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_ws_callback_handler_leak.py +78 -11
  76. notebook_intelligence-5.4.0/tests/ts/markdown-code-class.test.ts +58 -0
  77. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/mcp-util.test.ts +26 -1
  78. notebook_intelligence-5.4.0/tests/ts/perf-panel.test.tsx +614 -0
  79. notebook_intelligence-5.4.0/tests/ts/readiness-card.test.tsx +180 -0
  80. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/yarn.lock +120 -88
  81. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/319.15af761e454a61953ddf.js +0 -1
  82. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/694.e1e90b407cd10bd17b8b.js +0 -1
  83. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/728.ad926d50b34f75e72a21.js +0 -1
  84. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/977.718985cfcf5d26eaa97f.js +0 -1
  85. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/editor.worker.js +0 -1407
  86. notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/remoteEntry.603f6633fa64aacdff4c.js +0 -1
  87. notebook_intelligence-5.3.1/tests/test_mcp_manager.py +0 -82
  88. notebook_intelligence-5.3.1/tests/test_response_emitter.py +0 -83
  89. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.copier-answers.yml +0 -0
  90. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.editorconfig +0 -0
  91. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.gitignore +0 -0
  92. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/.gitignore +0 -0
  93. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/applypatch-msg +0 -0
  94. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/commit-msg +0 -0
  95. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/h +0 -0
  96. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/husky.sh +0 -0
  97. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/post-applypatch +0 -0
  98. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/post-checkout +0 -0
  99. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/post-commit +0 -0
  100. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/post-merge +0 -0
  101. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/post-rewrite +0 -0
  102. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/pre-applypatch +0 -0
  103. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/pre-auto-gc +0 -0
  104. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/pre-commit +0 -0
  105. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/pre-merge-commit +0 -0
  106. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/pre-push +0 -0
  107. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/pre-rebase +0 -0
  108. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/_/prepare-commit-msg +0 -0
  109. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.husky/pre-commit +0 -0
  110. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.prettierignore +0 -0
  111. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.vscode/extensions.json +0 -0
  112. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.vscode/settings.json +0 -0
  113. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/.yarnrc.yml +0 -0
  114. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/LICENSE +0 -0
  115. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/PRIVACY.md +0 -0
  116. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/RELEASE.md +0 -0
  117. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/SECURITY.md +0 -0
  118. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/README.md +0 -0
  119. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/bench_nbi_ws.py +0 -0
  120. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/bench_runner.py +0 -0
  121. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/bench_terminal.py +0 -0
  122. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/prompts.py +0 -0
  123. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/report.py +0 -0
  124. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/benchmarks/claude_perf/stats.py +0 -0
  125. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/docs/admin-tour-config.md +0 -0
  126. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/docs/skills.md +0 -0
  127. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/install.json +0 -0
  128. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/jest.config.js +0 -0
  129. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/jupyter-config/server-config/notebook_intelligence.json +0 -0
  130. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/__init__.py +0 -0
  131. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/_claude_cli.py +0 -0
  132. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/cell_output.py +0 -0
  133. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/claude_mcp_manager.py +0 -0
  134. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/claude_sessions.py +0 -0
  135. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/context_factory.py +0 -0
  136. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/github_copilot_chat_participant.py +0 -0
  137. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/schemas/@plmbr/notebook-intelligence/plugin.json +0 -0
  138. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/196.ae4c84cd4812d9d7cbcf.js +0 -0
  139. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/1b55f12eca93ba6743e7.ttf +0 -0
  140. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/24c0eed6cf7014abfb18.module.wasm +0 -0
  141. /notebook_intelligence-5.3.1/notebook_intelligence/labextension/static/326.7bbdffb7a0499a27928a.js.LICENSE.txt → /notebook_intelligence-5.4.0/notebook_intelligence/labextension/static/326.9fc28ccfe3ac71ac4b73.js.LICENSE.txt +0 -0
  142. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/537.e250b395dd56252f433b.js +0 -0
  143. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/631.c3b7a19d8e9bb293fff8.js +0 -0
  144. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/67.c8b2c6b35e88046e69ea.js +0 -0
  145. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/67.c8b2c6b35e88046e69ea.js.LICENSE.txt +0 -0
  146. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/671.dd4f3f162a9464547691.js +0 -0
  147. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/938a2c885cbaa713e543.module.wasm +0 -0
  148. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/editor.worker.js.LICENSE.txt +0 -0
  149. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/labextension/static/style.js +0 -0
  150. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/llm_providers/ollama_llm_provider.py +0 -0
  151. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/mcp_config_validation.py +0 -0
  152. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/mcp_policy.py +0 -0
  153. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/plugin_manager.py +0 -0
  154. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/rule_manager.py +0 -0
  155. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/ruleset.py +0 -0
  156. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/skill_github_import.py +0 -0
  157. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/skill_manager.py +0 -0
  158. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/skill_manifest.py +0 -0
  159. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/skill_reconciler.py +0 -0
  160. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/skillset.py +0 -0
  161. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/tour_config.py +0 -0
  162. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/notebook_intelligence/util.py +0 -0
  163. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/schema/plugin.json +0 -0
  164. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/setup.py +0 -0
  165. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/conftest.py +0 -0
  166. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/fixtures/rules/01-test-global.md +0 -0
  167. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/fixtures/rules/02-test-security.md +0 -0
  168. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/fixtures/rules/03-invalid-rule.md +0 -0
  169. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/fixtures/rules/modes/agent/01-test-production.md +0 -0
  170. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/fixtures/rules/modes/ask/01-test-exploration.md +0 -0
  171. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/fixtures/rules/modes/inline-chat/01-test-quick.md +0 -0
  172. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_builtin_file_read.py +0 -0
  173. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_builtin_search_files_sandbox.py +0 -0
  174. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_builtin_toolset_cwd_sandbox.py +0 -0
  175. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_cell_output.py +0 -0
  176. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_mcp_manager.py +0 -0
  177. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_models.py +0 -0
  178. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_sessions.py +0 -0
  179. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_sessions_handler.py +0 -0
  180. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_system_prompt.py +0 -0
  181. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_tool_humanizer.py +0 -0
  182. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_tool_path_sandbox.py +0 -0
  183. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_claude_turn_usage.py +0 -0
  184. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_coding_agent_launchers.py +0 -0
  185. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_config_atomic_save.py +0 -0
  186. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_context_factory.py +0 -0
  187. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_default_password_refusal.py +0 -0
  188. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_end_to_end_rule_integration.py +0 -0
  189. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_feedback.py +0 -0
  190. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_file_upload.py +0 -0
  191. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_github_copilot_user_data_mode.py +0 -0
  192. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_lazy_provider_imports.py +0 -0
  193. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_mcp_config_validation.py +0 -0
  194. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_mcp_manager_command_allowlist.py +0 -0
  195. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_mcp_policy.py +0 -0
  196. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_models.py +0 -0
  197. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_permission_modes.py +0 -0
  198. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_plugin_manager.py +0 -0
  199. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_policy_gate.py +0 -0
  200. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_process_tree_teardown.py +0 -0
  201. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_redact_process_secrets.py +0 -0
  202. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_rule_auto_reload.py +0 -0
  203. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_rule_manager.py +0 -0
  204. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_safe_anchor_uri.py +0 -0
  205. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_skill_fetch_redirects.py +0 -0
  206. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_skill_github_import.py +0 -0
  207. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_skill_manager.py +0 -0
  208. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_skill_manifest.py +0 -0
  209. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_skill_reconciler.py +0 -0
  210. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_skills_handlers.py +0 -0
  211. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_tour_config.py +0 -0
  212. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/test_ws_handler_auth.py +0 -0
  213. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/__mocks__/jupyterlab-apputils.ts +0 -0
  214. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/__mocks__/jupyterlab-filebrowser.ts +0 -0
  215. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/__mocks__/svg.ts +0 -0
  216. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/__mocks__/tiktoken.ts +0 -0
  217. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/ask-user-question.test.tsx +0 -0
  218. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/cell-output-bundle.test.ts +0 -0
  219. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/cell-output-features.test.ts +0 -0
  220. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/cell-output-toolbar.test.ts +0 -0
  221. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/chat-progress-feedback.test.ts +0 -0
  222. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/checkbox.test.tsx +0 -0
  223. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/choose-workspace-directory.test.ts +0 -0
  224. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/claude-mcp-paste.test.ts +0 -0
  225. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/coding-agent-launchers.test.ts +0 -0
  226. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/inline-completer-telemetry.test.ts +0 -0
  227. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/launcher-picker.test.tsx +0 -0
  228. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/markdown-renderer.test.tsx +0 -0
  229. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/notebook-generation.test.ts +0 -0
  230. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/open-file-refresh-watcher.test.ts +0 -0
  231. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/permission-mode-select.test.tsx +0 -0
  232. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/plugins-panel.test.tsx +0 -0
  233. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/safe-anchor.test.tsx +0 -0
  234. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/setup.ts +0 -0
  235. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/skill-wire-format.test.ts +0 -0
  236. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/task-target-notebook.test.ts +0 -0
  237. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/terminal-drag.test.ts +0 -0
  238. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tool-call-card.test.tsx +0 -0
  239. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tool-call-group.test.tsx +0 -0
  240. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tool-call-stream.test.ts +0 -0
  241. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tour-config.test.ts +0 -0
  242. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tour-events.test.ts +0 -0
  243. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tour-overlay.test.tsx +0 -0
  244. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tour-state.test.ts +0 -0
  245. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/tsconfig.json +0 -0
  246. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/utils.test.ts +0 -0
  247. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tests/ts/workspace-skips.test.ts +0 -0
  248. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/tsconfig.json +0 -0
  249. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/.eslintrc.js +0 -0
  250. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/README.md +0 -0
  251. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/jupyter_server_test_config.py +0 -0
  252. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/package.json +0 -0
  253. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/playwright.config.ts +0 -0
  254. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/cell-output.spec.ts +0 -0
  255. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/chat-sidebar.spec.ts +0 -0
  256. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/claude-mcp-patch.spec.ts +0 -0
  257. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/extension.spec.ts +0 -0
  258. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/helpers.ts +0 -0
  259. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/launcher.spec.ts +0 -0
  260. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tests/notebook-toolbar.spec.ts +0 -0
  261. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/tsconfig.json +0 -0
  262. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/ui-tests/yarn.lock +0 -0
  263. {notebook_intelligence-5.3.1 → notebook_intelligence-5.4.0}/webpack.config.js +0 -0
@@ -8,7 +8,80 @@ For each release we list user-facing changes grouped as **Added**, **Changed**,
8
8
 
9
9
  <!-- <START NEW CHANGELOG ENTRY> -->
10
10
 
11
- ## [Unreleased]
11
+ ## [5.4.0] - unreleased
12
+
13
+ 5.4.0 adds a second agent mode (ACP, with Codex as the first agent type) alongside Claude mode, a way to serve NBI's Jupyter-UI tools to an agent running outside the Lab page, and two diagnostic surfaces for deployments where something is misconfigured or slow: a configuration readiness preflight that names the missing piece, and opt-in performance diagnostics with per-turn timelines. Every one of them is opt-in and defaults to off. No traitlet, env-var, REST route, or on-disk-format renames or removals.
14
+
15
+ ### Added
16
+
17
+ - **Experimental ACP agent mode** (#378). A second agent mode that drives an external coding agent over the [Agent Client Protocol](https://agentclientprotocol.com/), with OpenAI Codex (via the `codex-acp` adapter) as the first selectable agent type. Adds an ACP tab in Settings with an agent-type dropdown, chat model, API key, and base URL; session listing and resume through the agent's own `session/list` and `session/load`; and streaming, tool-call, and permission-request handling that reuses NBI's existing confirmation flow. ACP mode and Claude mode are mutually exclusive: enabling one disables the other on save, and a hand-edited config that enables both resolves to Claude. Two new admin policies both default to `force-off` rather than `user-choice`: `acp_mode_policy` (`NBI_ACP_MODE_POLICY`), the off switch for the whole mode, and `acp_full_access_policy` (`NBI_ACP_FULL_ACCESS_POLICY`), which governs whether the agent may run tools without asking. While full access is `force-off`, NBI pins Codex to `approval_policy = untrusted` through the adapter's `-c` command-line override, so it asks before anything beyond trusted read-only commands. Credentials and model follow the same env-override locks as Claude (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `NBI_ACP_CHAT_MODEL`), and `NBI_ACP_AGENT_COMMAND` overrides the adapter command NBI launches. When an API key is configured, codex runs with an isolated `CODEX_HOME` under the NBI user directory so neither the workspace's nor the user's `~/.codex` config is read. See [Gating the experimental ACP agent](docs/admin-guide.md#gating-the-experimental-acp-agent-378).
18
+ - **Proxied Jupyter UI tools for external agents** (#398). NBI's Jupyter-UI tools (create and edit notebooks, run cells, open files, drive the terminal) were only reachable from the in-process MCP server NBI registers with the Claude SDK. Managed and enterprise Claude Code configurations can forbid dynamically configured MCP servers, which made those tools unavailable in exactly the deployments that most need them. A standalone stdio MCP server, `python -m notebook_intelligence.mcp_ui_proxy`, now serves the same tool manifest and relays each call over HTTP to the running Jupyter Server, so it can be declared in a static `mcp.json` like any other server. Setting `jupyter_ui_tools_external` in the Claude settings switches Claude mode from the in-process tools to the relay. The relay endpoint is `/notebook-intelligence/ui-tools` (GET for the manifest, POST to invoke); it requires Jupyter authentication like every other NBI route, and the proxy additionally sends a per-process bridge secret in an `X-NBI-UI-Tools-Token` header, which the relay uses only to exempt the call from the XSRF check (not as an identity). `NBI_UI_TOOLS_URL`, `NBI_UI_TOOLS_TOKEN`, `NBI_UI_TOOLS_SECRET`, `NBI_UI_TOOLS_HTTP_TIMEOUT`, and `NBI_UI_TOOLS_SERVER_NAME` configure the proxy when discovery cannot find the server on its own. See [Serving the Jupyter UI tools to an external agent](docs/admin-guide.md#serving-the-jupyter-ui-tools-to-an-external-agent-398).
19
+
20
+ - **Configuration readiness preflight** (#410). NBI Settings now opens on a Status card answering whether the deployment is configured to work and, if not, which specific piece is missing. Checks resolve the provider and model, confirm the model list is reachable and that the selected model is one the endpoint actually serves, and for the agent modes confirm the CLI answers `--version` and that credentials are present; a missing key is a warning rather than an error, because the Claude CLI and ACP agents can hold subscription logins NBI cannot see. Every row that is not `ok` carries a remedy naming the next action. `GET /notebook-intelligence/readiness` runs the checks that bill nothing. An opt-in `POST` with `{"live": true}` sends one short real request, which is the only way to catch a gateway that returns 200s but buffers instead of streaming, or a proxy that strips the `tools` field so agent mode silently never calls a tool; it is refused when `NBI_READINESS_LIVE_CHECK=off` (traitlet `readiness_live_check_allowed`) and returns 429 if one is already running. No model output is retained. See [Configuration readiness](docs/admin-guide.md#configuration-readiness).
21
+ - **Opt-in performance diagnostics** (#408). A Performance tab that answers "where did this turn's time go," aimed at internal LLM gateways, network home directories, and TLS-intercepting proxies. Each chat turn records a phase timeline (rule and context preparation, agent connect and spawn, time to first token, streaming with stall events, tool calls, time spent waiting on the user) plus token counts and the SDK-reported API duration, with a verdict naming the dominant phase; comparing active time against API time separates a slow gateway from a slow machine. An environment probe measures filesystem, subprocess, and optional network latency from the server's own vantage point. Off by default, and when off the cost is a single boolean check per instrumentation site. Attributes are redacted by default (paths hashed to basenames), turns are held in a capped ring buffer, and each turn is also summarized as one `perf turn ...` INFO line so headless installs get the same signal. `NBI_PERF_DIAGNOSTICS` locks the enabled state, `NBI_PERF_DIAGNOSTICS_POLICY` governs it fleet-wide, `NBI_PERF_PROBE_NETWORK=off` disables just the network check, and `NBI_PERF_LOG_DIR` redirects the optional JSONL log. See [`docs/performance-diagnostics.md`](docs/performance-diagnostics.md).
22
+ - **Separate model for inline chat** (#413). New optional `claude_settings.inline_chat_model` picks the model used by the inline chat popover, falling back to `chat_model` when unset so existing configs behave exactly as before. Inline chat calls the Anthropic API directly while the other Claude modes go through the Claude Code CLI, and the two transports do not accept the same model-id grammar: a CLI-only id spelling reaches the raw API verbatim and is rejected. Exposed as a collapsible "Inline chat model" control nested under Claude → Chat model, and pinnable via `NBI_CLAUDE_INLINE_CHAT_MODEL`. `NBI_CLAUDE_CHAT_MODEL` keeps governing both surfaces as it does today (while it is set the inline key is ignored and its control is disabled), so an existing pin loses no coverage on upgrade; pin the inline var as well to give the two transports different models.
23
+
24
+ ### Changed
25
+
26
+ - **Dependency floors raised past known-vulnerable releases** (#399). `cryptography>=48.0.1`, `jupyter_server>=2.20.0`, `litellm>=1.83.7`, `mcp>=1.28.1`, `urllib3>=2.7.0`, `tornado>=6.5.7`, `starlette>=1.3.1`, `pydantic-settings>=2.14.2`, `aiohttp>=3.14.1`, `mistune>=3.3.0`, and `python-multipart>=0.0.27`, plus npm-side overrides for the transitive packages whose parents did not pin tightly enough. `mcp` is additionally capped below `2.0`: 2.x moved the FastMCP server out of the SDK, so `mcp.server.fastmcp.tools` no longer exists there and the server extension fails to load.
27
+ - **Chat response streams now finalize when participant handlers return** (#403). Participant implementations must complete streaming and UI-command work before their awaited `handle_chat_request` coroutine returns; detached post-return writes (including provider events arriving after a timeout) are logged and ignored so every request delivers exactly one terminal response. Cancelling a GitHub Copilot chat stream now also stops consuming later provider events.
28
+ - **Large cell-output and Claude terminal-tool results are bounded before returning to the agent** (#404). Oversized responses now retain their beginning and end with an explicit truncation marker instead of consuming the agent's context without limit, and cell-output tools accept optional `offset` and `limit` arguments so agents can retrieve omitted ranges. Source-returning and structured tools remain lossless, while unexpected structured values from the text-oriented cell-output and terminal bridges are stringified and bounded. Claude's approximate output budget defaults to 10,000 tokens and can be adjusted with `NBI_CLAUDE_TOOL_RESULT_MAX_OUTPUT_TOKENS`; set it to numeric zero to restore unbounded output.
29
+ - **Ask-mode chat requests are budgeted against the active model's context window** (#412). Prior conversation and current-turn context are now fitted to 80% of the model's input budget instead of being sent whole. System instructions, injected rules, required inline-edit source, an MCP prompt sequence, and the newest request are preserved, with the newest request truncated only when mandatory context cannot otherwise fit; older history is retained only as complete turns, so a tool-call exchange is never split. Fenced and multimodal context is dropped whole rather than cut mid-delimiter, and an omission notice is added when anything was left out. Agent tool loops are unchanged. OpenAI-compatible and LiteLLM-compatible providers require an explicit **Context window** setting; left blank, history is passed through unchanged rather than pruned against a guessed limit.
30
+
31
+ ### Fixed
32
+
33
+ - **Claude inline edits now receive their intended system instructions and applicable workspace rules** (#405). The inline-edit prompt is forwarded through the Claude handler to Anthropic's `system` field instead of being silently dropped before generation. Injected repository guidance is capped against the model context window, and disabling rules now suppresses `AGENTS.md` instructions as well as configured rule files.
34
+ - **Empty code blocks no longer render as inline code with actions attached** (#407). A fenced block that streamed in empty was misclassified as an inline span, so the chat renderer offered copy and insert actions on nothing.
35
+ - **GitHub Copilot chat no longer sends an unsupported `stop` parameter** (#397). Chat requests carried `stop: ['<END>']` to `/chat/completions`, with `gpt-5` and `gpt-5-mini` excluded by literal id comparison after they began rejecting it. Every GPT-5 family model released since inherited that blind spot: selecting GPT-5.4 made chat unusable, with each message failing as `Unsupported parameter: 'stop' is not supported with this model`. The parameter is now omitted for every model on that endpoint. The `<END>` sentinel was never load-bearing there, and the model id does not predict which models reject it, so no id-keyed exclusion list can stay correct. Inline completions, which use a different endpoint where the sentinel does bound a completion, are unchanged.
36
+ - **AI-streamed code blocks no longer pick up trailing whitespace** (#384). The chat renderer ran a blanket `\n` to ` \n` string replace before markdown parsing, intended for user input but applied to every message, which injected two trailing spaces into every line of a streamed fenced code block and carried them into anything copy-pasted out. The pre-parse transform is gone; NBI now uses the `remark-breaks` plugin, which converts soft breaks to hard breaks at the AST level and is fence-aware, so prose still breaks on single newlines while code blocks are left alone in both directions.
37
+ - **Inline-code styling no longer leaks into fenced code blocks** (#401). `react-markdown` 9 stopped setting the `inline` prop, so the chat renderer's `inline || !match` branch also caught fenced blocks with an unrecognized or missing language and gave them the inline-code pill background; the visible symptom reported was a first-line indent on highlighted blocks, which is that rule's `padding: 1px 4px` landing on a `<code>` it was never meant to reach. Inline and fenced content are now told apart by content shape (`remark-rehype` appends a trailing newline to fenced code and never to an inline span), and the CSS targets an explicit `.inline-code` class instead of `:not(pre) > code`, which had itself been broken by the `PreTag="div"` wrapper the syntax highlighter inserts.
38
+ - **Korean and other IME composition needs only one Enter to submit** (#400). The chat input listened for `keydown` without checking composition state, so the Enter that commits an IME candidate also submitted the message, and users typing Hangul had to retype the last syllable. The handler now ignores Enter while a composition is in progress.
39
+
40
+ ## [5.3.1] - 2026-07-29
41
+
42
+ A patch release: one skills-import fix. No traitlet, env-var, REST route, or on-disk-format changes.
43
+
44
+ ### Fixed
45
+
46
+ - **The GitHub skill-import size cap applies to the skill subpath, not the whole repository** (#394). Importing a single skill from a large monorepo could be rejected because the cap was measured against every file in the archive rather than the subdirectory actually being imported, so legitimate imports failed on repository size alone.
47
+
48
+ ## [5.3.0] - 2026-07-22
49
+
50
+ 5.3.0 makes notebook work kernel-aware rather than Python-assuming, modernizes the Claude model defaults, and adds an opt-in per-turn usage footer. It also carries two workspace-containment fixes for the built-in file tools. One ruleset frontmatter key was removed; see the migration note.
51
+
52
+ ### Migration note
53
+
54
+ - **`scope.kernels` is no longer accepted in ruleset frontmatter** (#379). The old key conflated a kernel name with a language. Rules now scope on `scope.languages` (the notebook's language, e.g. `python`) or `scope.kernel_names` (the kernelspec name, e.g. `python3`), and a rule file that still uses `scope.kernels` is rejected with a `ValueError` naming the file rather than being silently ignored. Update any rule that used it; see [Ruleset system](docs/rulesets.md).
55
+
56
+ ### Added
57
+
58
+ - **Multi-language and kernel-aware notebook support** (#379). Notebook creation, cell insertion, and inline code generation previously assumed Python. NBI now resolves the active notebook's kernel and language and carries them through the chat context, the built-in toolsets, and the ruleset scope, so generated code matches the notebook it is going into. A `list-available-notebook-kernels` tool lets the agent enumerate installed kernelspecs instead of guessing a name, and the Claude system prompt instructs it to call that tool before creating a notebook in a language the current context has not established. Requesting a notebook in a kernel that is not installed now raises instead of silently falling back to Python.
59
+ - **Opt-in per-turn usage footer in Claude mode** (#391). The Claude SDK ends every turn with a `ResultMessage` carrying duration, token counts, and a cost estimate, which NBI previously discarded. Setting `show_turn_usage` in the Claude settings (default off) appends a one-line italic footer to each completed turn. The `$` cost segment is shown only when NBI is running against a direct Anthropic API key on Anthropic's own endpoint: the SDK prices from the CLI's built-in public list rates, so on a subscription login the marginal cost is really zero and on a custom `base_url` the endpoint's pricing is unknown. Duration and token counts, which are always accurate, are shown either way.
60
+ - **Inline-completion and inline-chat acceptance are tracked as distinct telemetry events** (#383). Acceptance of a ghost-text completion and acceptance of an inline-chat edit were previously indistinguishable. JupyterLab's inline-completer provider interface has no accept callback, so NBI wraps JupyterLab's own `IInlineCompleterFactory` and patches `accept()` on the widget instance rather than subclassing the completer. The factory is taken as an optional dependency, so NBI still loads if JupyterLab stops providing it. See [Telemetry events](docs/admin-guide.md#telemetry-events).
61
+
62
+ ### Changed
63
+
64
+ - **Claude model defaults modernized, and inline-completion output bounded** (#389). The chat default resolves to `claude-sonnet-5` and the inline-completion default to `claude-haiku-4-5`, both resolved against the live model list rather than a hardcoded id, with a version-aware fallback within the tier (a bogus `claude-sonnet-99` resolves to the newest Sonnet, not a lexicographic pick). Because the Models API lists Haiku only under its dated id, the inline default resolves to that snapshot and logs an INFO line at startup; that line is expected, not an error. Inline-completion output is capped at 1024 tokens instead of 10,000, since a suggestion is at most a few dozen lines and the old ceiling let a rambling response generate for seconds before the extraction regex discarded most of it. `NBI_CLAUDE_INLINE_COMPLETION_MAX_TOKENS` overrides the cap and is clamped to `[1, 4096]`. The fetched model list is cached per endpoint, so switching `base_url` no longer reuses the previous endpoint's models.
65
+ - **Context token budget derives from the Claude model in Claude mode** (#390). The budget was computed from the general chat model's context window even when Claude mode was active, so history was trimmed against the wrong number.
66
+
67
+ ### Fixed
68
+
69
+ - **`search_files` reads are confined to the workspace** (#386). The tool sandboxed only the search root, then opened each glob hit directly, so a symlink inside the workspace pointing outside it (`leak.txt -> /etc/passwd`) let an agent-mode model read arbitrary host files through `content_pattern` matches. `read_file` already routed every path through the workspace gate and rejected outbound symlinks after resolution; `search_files` now does the same for each match. Confidentiality only, no write path.
70
+ - **Outbound symlinks are no longer stat-followed during enumeration** (#387). The directory-descent check called `is_dir(follow_symlinks=True)` on a symlink's target before testing that target for workspace containment. The containment check still rejected it afterwards, so nothing was read, but the probe itself reached past the workspace boundary. The path is now resolved and range-checked first, and only an in-workspace target is stat'd. No behavior change beyond that.
71
+ - **Attachment context survives a slash-command prompt** (#388). When the last user line started with `/`, the CLI query assembly kept only that line and silently discarded the context lines NBI had just appended for the same turn: attachment `@`-mentions, cell pointers, and output context. Control-only commands (`/clear`, `/cost`, and friends) still drop the context, since it means nothing to them, but any other command is now moved to the front of the query, where the CLI expects it, with the context lines preserved after it. Custom skill and plugin commands therefore receive the user's attachments.
72
+ - **Kernel-scoped rules apply to inline code generation** (#379). Inline generation did not pass the notebook's kernel into rule selection, so rules scoped to a kernel or language never matched there.
73
+
74
+ ## [5.2.1] - 2026-06-26
75
+
76
+ A patch release: one Claude-mode timeout fix. No traitlet, env-var, REST route, or on-disk-format changes.
77
+
78
+ ### Fixed
79
+
80
+ - **A turn waiting on a user prompt no longer times out** (#381). Time spent blocked on the user counted toward the agent response timeout, so an approval left on screen for about 30 minutes failed the turn with "Claude agent response timeout" while the worker was still mid-request, and the orphaned request then wedged the next prompt. NBI now tracks the wall-clock time a turn spends waiting on input (every tool approval, plan confirmation, and question) and subtracts it from the timeout window, so a slow human reply no longer looks like an unresponsive agent while a genuine post-approval hang still trips the timeout. The user-input listener is also disconnected in a `finally`, so a cancelled turn cannot leak the subscription. Because a parked approval no longer self-resolves via the timeout, in-flight requests are cancelled when the WebSocket closes: an abandoned turn (the user closes the tab) tears down its worker thread and the Claude subprocess instead of spinning until the process exits.
81
+
82
+ ## [5.2.0] - 2026-06-18
83
+
84
+ 5.2.0 adds the Claude permission-mode selector to the chat input, halves the server-extension import cost by deferring provider SDK imports, and makes three Claude-CLI integration points honor `CLAUDE_CONFIG_DIR`. One new admin policy (`claude_bypass_permissions_policy`), which defaults to `force-off`; no traitlet, env-var, REST route, or on-disk-format renames or removals.
12
85
 
13
86
  ### Added
14
87
 
@@ -165,7 +165,14 @@ TypeScript unit tests:
165
165
  jlpm test
166
166
  ```
167
167
 
168
- There is no Python test suite at the moment. Manual end-to-end verification is documented per change in pull request descriptions.
168
+ Python tests (the `test` extra pulls in pytest, `pytest-timeout`, `psutil`, and `pyyaml`):
169
+
170
+ ```bash
171
+ pip install -e ".[test]"
172
+ pytest tests/ -q
173
+ ```
174
+
175
+ CI runs both suites on every push, in that order. Every test is capped at 30 seconds by `pytest-timeout`, so a hung subprocess or pipe fails the run instead of stalling the job.
169
176
 
170
177
  ## Linting
171
178
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: notebook_intelligence
3
- Version: 5.3.1
3
+ Version: 5.4.0
4
4
  Summary: AI coding assistant for JupyterLab
5
5
  Project-URL: Homepage, https://github.com/plmbr/notebook-intelligence
6
6
  Project-URL: Bug Tracker, https://github.com/plmbr/notebook-intelligence/issues
@@ -694,14 +694,15 @@ Classifier: Programming Language :: Python :: 3.10
694
694
  Classifier: Programming Language :: Python :: 3.11
695
695
  Classifier: Programming Language :: Python :: 3.12
696
696
  Requires-Python: >=3.10
697
+ Requires-Dist: agent-client-protocol==0.10.1
697
698
  Requires-Dist: aiohttp>=3.14.1
698
699
  Requires-Dist: anthropic>=0.22.1
699
700
  Requires-Dist: claude-agent-sdk
700
- Requires-Dist: cryptography
701
+ Requires-Dist: cryptography>=48.0.1
701
702
  Requires-Dist: fuzy-jon==0.1.0
702
703
  Requires-Dist: jupyter-server<3,>=2.20.0
703
704
  Requires-Dist: litellm>=1.83.7
704
- Requires-Dist: mcp>=1.28.1
705
+ Requires-Dist: mcp<2,>=1.28.1
705
706
  Requires-Dist: mistune>=3.3.0
706
707
  Requires-Dist: ollama
707
708
  Requires-Dist: openai
@@ -739,6 +740,7 @@ NBI is free and open-source. Connect it to a free or paid LLM provider of your c
739
740
  - [Chat interface](#chat-interface)
740
741
  - [Cell output actions](#cell-output-actions)
741
742
  - [Notebook toolbar generation](#notebook-toolbar-generation)
743
+ - [Multi-language and kernel-aware notebooks](#multi-language-and-kernel-aware-notebooks)
742
744
  - [Reload open files when changed on disk](#reload-open-files-when-changed-on-disk)
743
745
  - [Configuration](#configuration)
744
746
  - [Configuration files](#configuration-files)
@@ -751,6 +753,8 @@ NBI is free and open-source. Connect it to a free or paid LLM provider of your c
751
753
  - [Claude MCP Servers](#claude-mcp-servers)
752
754
  - [Claude Plugins](#claude-plugins)
753
755
  - [Chat feedback](#chat-feedback)
756
+ - [Configuration readiness](#configuration-readiness)
757
+ - [Performance diagnostics](#performance-diagnostics)
754
758
  - [Documentation](#documentation)
755
759
  - [Further reading](#further-reading)
756
760
  - [Roadmap](#roadmap)
@@ -807,6 +811,9 @@ Configure via the NBI Settings dialog (gear icon in the chat panel, or _Settings
807
811
  - **Chat Agent setting sources** — user, project, or both, mirroring [Claude Code's settings](https://code.claude.com/docs/en/settings).
808
812
  - **Chat Agent tools** — which tool sets to activate. _Claude Code tools_ are always on. _Jupyter UI tools_ are NBI's own (authoring notebooks, running cells, etc.).
809
813
  - **API key** and **Base URL** — point at Anthropic or a self-hosted endpoint.
814
+ - **Show turn usage**: off by default. When on, each completed turn ends with a one-line italic footer giving the turn's duration and token counts. A `$` cost figure appears only when NBI is running against a direct Anthropic API key on Anthropic's own endpoint: the SDK prices from the CLI's built-in public list rates, which are not the marginal cost on a subscription login and not the rate on a custom `base_url`, so NBI omits a number it cannot stand behind rather than showing a misleading one.
815
+
816
+ **Model defaults.** With no model selected, the chat model resolves to the newest Sonnet (`claude-sonnet-5` today) and auto-complete to the newest Haiku, both matched against the model list fetched from the configured endpoint rather than a hardcoded ID. If a configured ID no longer exists, NBI falls back within the same tier by version rather than alphabetically, so `claude-sonnet-99` resolves to the newest Sonnet. The fetched list is cached per endpoint, so changing **Base URL** does not reuse the previous endpoint's models. Auto-complete responses are capped at 1024 tokens, since a suggestion is at most a few dozen lines; `NBI_CLAUDE_INLINE_COMPLETION_MAX_TOKENS` overrides the cap and is clamped to `[1, 4096]`.
810
817
 
811
818
  If the Claude Code CLI is on `PATH`, NBI launches it automatically. To override the location, set the `NBI_CLAUDE_CLI_PATH` environment variable before starting JupyterLab.
812
819
 
@@ -827,7 +834,7 @@ Bypass Permissions never persists: starting a **New chat session** (or `/clear`)
827
834
 
828
835
  Choosing **Bypass Permissions** does not arm it immediately. It opens a confirmation step; only after you confirm does bypass take effect, and while it is active the shield turns into a red warning icon as a persistent indicator. Bypass must be re-armed each session: starting a new chat or restarting the Claude client drops back to Default. And because the server re-checks the requested mode on every message, an armed bypass can never outlive a policy that an administrator has since turned off.
829
836
 
830
- **Admin gating.** Bypass Permissions is **off by default** and hidden from the selector unless an administrator enables it: it is governed by the `claude_bypass_permissions` policy (`NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY`), the only admin policy whose default is `force-off` rather than `user-choice`. The requested mode is also clamped on the server for every message, so the gate can't be bypassed by a hand-crafted request. Independently, NBI honors Claude Code's enterprise [managed settings](https://code.claude.com/docs/en/settings): `permissions.disableBypassPermissionsMode` removes the option regardless of the NBI policy, and `permissions.defaultMode` sets the selector's starting mode (Bypass excepted, since it never auto-arms). See [Allowing Bypass Permissions](docs/admin-guide.md#allowing-bypass-permissions-in-the-claude-permission-mode-selector) in the admin guide.
837
+ **Admin gating.** Bypass Permissions is **off by default** and hidden from the selector unless an administrator enables it: it is governed by the `claude_bypass_permissions` policy (`NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY`), one of three admin policies that default to `force-off` rather than `user-choice` (the others gate the experimental ACP agent mode and ACP full access). The requested mode is also clamped on the server for every message, so the gate can't be bypassed by a hand-crafted request. Independently, NBI honors Claude Code's enterprise [managed settings](https://code.claude.com/docs/en/settings): `permissions.disableBypassPermissionsMode` removes the option regardless of the NBI policy, and `permissions.defaultMode` sets the selector's starting mode (Bypass excepted, since it never auto-arms). See [Allowing Bypass Permissions](docs/admin-guide.md#allowing-bypass-permissions-in-the-claude-permission-mode-selector) in the admin guide.
831
838
 
832
839
  The `/enter-plan-mode` and `/exit-plan-mode` slash commands still work if typed but are no longer offered in autocomplete; the selector replaces them and will retire the commands in a future release.
833
840
 
@@ -900,6 +907,12 @@ Each is per-user toggleable from Settings (saved as `enable_explain_error`, `ena
900
907
 
901
908
  Active notebooks show a sparkle icon on the toolbar. Click it to open a popover that scopes the generation request to that specific notebook — handy for multi-notebook sessions where you don't want the chat sidebar to compete for context.
902
909
 
910
+ ### Multi-language and kernel-aware notebooks
911
+
912
+ Notebook creation, cell insertion, and inline generation follow the notebook you are actually in rather than assuming Python. NBI resolves the active notebook's kernel and language and carries both through the chat context, the built-in tools, and ruleset scoping, so generated code matches the target notebook, and a rule scoped to `languages: ['r']` or `kernel_names: ['ir']` applies where you would expect (including in inline generation, which previously did not receive the kernel).
913
+
914
+ When the agent needs a notebook in a language the current context has not established, it calls a `list-available-notebook-kernels` tool and picks from the kernelspecs that are actually installed instead of guessing a name. Asking for a kernel that is not installed now fails with a clear error rather than silently producing a Python notebook.
915
+
903
916
  ### Reload open files when changed on disk
904
917
 
905
918
  NBI reloads open document tabs when their files change on disk, so edits an AI agent makes via its Read/Write tools appear in the editor without a manual refresh. Tabs with unsaved local edits are skipped so user work is never clobbered. Toggle via the **NBI Settings dialog → External changes → "Refresh open files when changed on disk"** (default on).
@@ -924,24 +937,26 @@ Most settings panel toggles can be locked by org administrators. Two shapes:
924
937
 
925
938
  **Boolean policies** use the `*_POLICY` suffix and accept three values: `user-choice` (default — user toggles freely), `force-on` (locked enabled), `force-off` (locked disabled). When forced, the panel control is disabled with a "Locked by your administrator" tooltip and any client-side write is ignored.
926
939
 
927
- | Env var | Locks the Settings panel control for |
928
- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
929
- | `NBI_EXPLAIN_ERROR_POLICY` | "Explain cell errors" |
930
- | `NBI_OUTPUT_FOLLOWUP_POLICY` | "Ask about cell outputs" |
931
- | `NBI_OUTPUT_TOOLBAR_POLICY` | "Show output toolbar" |
932
- | `NBI_CLAUDE_MODE_POLICY` | "Enable Claude mode" |
933
- | `NBI_CLAUDE_CONTINUE_CONVERSATION_POLICY` | "Remember conversation history" |
934
- | `NBI_CLAUDE_CODE_TOOLS_POLICY` | "Claude Code tools" |
935
- | `NBI_CLAUDE_JUPYTER_UI_TOOLS_POLICY` | "Jupyter UI tools" |
936
- | `NBI_CLAUDE_SETTING_SOURCE_USER_POLICY` | Setting source: User |
937
- | `NBI_CLAUDE_SETTING_SOURCE_PROJECT_POLICY` | Setting source: Project |
938
- | `NBI_STORE_GITHUB_ACCESS_TOKEN_POLICY` | "Remember my GitHub Copilot access token" |
939
- | `NBI_SKILLS_MANAGEMENT_POLICY` | The Skills tab (force-off hides it and 403s the API; also disables the managed-skills reconciler) |
940
- | `NBI_CLAUDE_MCP_MANAGEMENT_POLICY` | The Claude-mode MCP Servers tab (force-off hides it and 403s `/claude-mcp/*`; independent of the non-Claude MCP Servers tab) |
941
- | `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY` | The Claude-mode Plugins tab (force-off hides it and 403s `/plugins/*`) |
942
- | `NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY` | "Bypass Permissions" in the Claude permission-mode selector (defaults to `force-off`, the only policy that does; `user-choice` exposes the option, which the user still arms per session) |
943
- | `NBI_TERMINAL_DRAG_DROP_POLICY` | Terminal drag-drop file attach feature |
944
- | `NBI_REFRESH_OPEN_FILES_ON_DISK_CHANGE_POLICY` | "Refresh open files when changed on disk" |
940
+ | Env var | Locks the Settings panel control for |
941
+ | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
942
+ | `NBI_EXPLAIN_ERROR_POLICY` | "Explain cell errors" |
943
+ | `NBI_OUTPUT_FOLLOWUP_POLICY` | "Ask about cell outputs" |
944
+ | `NBI_OUTPUT_TOOLBAR_POLICY` | "Show output toolbar" |
945
+ | `NBI_CLAUDE_MODE_POLICY` | "Enable Claude mode" |
946
+ | `NBI_ACP_MODE_POLICY` | "Enable ACP mode" (experimental agent mode over the Agent Client Protocol, with Codex as the first agent type; defaults to `force-off`, set `user-choice` to allow it) |
947
+ | `NBI_ACP_FULL_ACCESS_POLICY` | "Full access" for the ACP agent: run tools without asking (defaults to `force-off`, so the agent asks before anything beyond trusted read-only commands; `user-choice` lets users opt into unattended runs) |
948
+ | `NBI_CLAUDE_CONTINUE_CONVERSATION_POLICY` | "Remember conversation history" |
949
+ | `NBI_CLAUDE_CODE_TOOLS_POLICY` | "Claude Code tools" |
950
+ | `NBI_CLAUDE_JUPYTER_UI_TOOLS_POLICY` | "Jupyter UI tools" |
951
+ | `NBI_CLAUDE_SETTING_SOURCE_USER_POLICY` | Setting source: User |
952
+ | `NBI_CLAUDE_SETTING_SOURCE_PROJECT_POLICY` | Setting source: Project |
953
+ | `NBI_STORE_GITHUB_ACCESS_TOKEN_POLICY` | "Remember my GitHub Copilot access token" |
954
+ | `NBI_SKILLS_MANAGEMENT_POLICY` | The Skills tab (force-off hides it and 403s the API; also disables the managed-skills reconciler) |
955
+ | `NBI_CLAUDE_MCP_MANAGEMENT_POLICY` | The Claude-mode MCP Servers tab (force-off hides it and 403s `/claude-mcp/*`; independent of the non-Claude MCP Servers tab) |
956
+ | `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY` | The Claude-mode Plugins tab (force-off hides it and 403s `/plugins/*`) |
957
+ | `NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY` | "Bypass Permissions" in the Claude permission-mode selector (defaults to `force-off`, as do `NBI_ACP_MODE_POLICY` and `NBI_ACP_FULL_ACCESS_POLICY`; `user-choice` exposes the option, which the user still arms per session) |
958
+ | `NBI_TERMINAL_DRAG_DROP_POLICY` | Terminal drag-drop file attach feature |
959
+ | `NBI_REFRESH_OPEN_FILES_ON_DISK_CHANGE_POLICY` | "Refresh open files when changed on disk" |
945
960
 
946
961
  The first three also have matching traitlets on `NotebookIntelligence` (`explain_error_policy`, `output_followup_policy`, `output_toolbar_policy`); add the others as needed in the same shape:
947
962
 
@@ -962,10 +977,18 @@ Per-user preferences (default on for the cell-output features) live in `config.j
962
977
  | `NBI_CHAT_MODEL_ID` | General → Chat model → Model |
963
978
  | `NBI_INLINE_COMPLETION_MODEL_PROVIDER` | General → Auto-complete model → Provider |
964
979
  | `NBI_INLINE_COMPLETION_MODEL_ID` | General → Auto-complete model → Model |
965
- | `NBI_CLAUDE_CHAT_MODEL` | Claude → Chat model |
980
+ | `NBI_CLAUDE_CHAT_MODEL` | Claude → Chat model (and Inline chat model, unless pinned separately) |
981
+ | `NBI_CLAUDE_INLINE_CHAT_MODEL` | Claude → Chat model → Inline chat model |
966
982
  | `NBI_CLAUDE_INLINE_COMPLETION_MODEL` | Claude → Auto-complete model |
967
983
  | `ANTHROPIC_API_KEY` | Claude → API Key (input is locked + blanked; the SDK reads the env directly) |
968
984
  | `ANTHROPIC_BASE_URL` | Claude → Base URL |
985
+ | `NBI_ACP_CHAT_MODEL` | ACP → Chat model |
986
+ | `OPENAI_API_KEY` | ACP → API Key (input is locked + blanked; the agent reads the env directly) |
987
+ | `OPENAI_BASE_URL` | ACP → Base URL |
988
+
989
+ Inline chat calls the Anthropic API directly while the other Claude modes go through the Claude Code CLI, so a deployment can need a different model id for each. It uses `inline_chat_model`, falling back to `chat_model` when that is unset. `NBI_CLAUDE_CHAT_MODEL` therefore governs both surfaces: while it is set, a stored `inline_chat_model` is ignored and its control is disabled. Set `NBI_CLAUDE_INLINE_CHAT_MODEL` as well to pin inline chat to a different model — it takes precedence and re-enables nothing, so both surfaces stay administrator-controlled.
990
+
991
+ `NBI_ACP_AGENT_COMMAND` (not a settings lock) overrides the adapter command NBI launches for the selected ACP agent type; the value is shell-split. Leave it unset to use the agent's registered default command.
969
992
 
970
993
  Provider IDs: `github-copilot`, `openai-compatible`, `litellm-compatible`, `ollama`, `none`. The `*_MODEL_ID` value is whatever the chosen provider exposes (e.g. `gpt-4o`, `llama3:latest`). Claude model IDs are the literal IDs from the Anthropic API (e.g. `claude-opus-4-7`, `claude-sonnet-4-6`); empty string = "Default (recommended)"; `NBI_CLAUDE_INLINE_COMPLETION_MODEL` also accepts `none` (no inline completion in Claude mode) or `inherit` (use the General-tab Auto-complete model).
971
994
 
@@ -1012,7 +1035,7 @@ In multi-tenant deployments, `nbi-command-execute` and `nbi-file-edit` are effec
1012
1035
 
1013
1036
  ## Model Context Protocol (MCP) support
1014
1037
 
1015
- NBI integrates with [MCP](https://modelcontextprotocol.io) servers. It supports both stdio and Streamable HTTP transports. **MCP server tools are supported; resources and prompts are not yet supported.**
1038
+ NBI integrates with [MCP](https://modelcontextprotocol.io) servers. It supports both stdio and Streamable HTTP transports. **Tools and prompts are supported; resources are not.** A server's prompts appear in the chat input's slash-command autocomplete as `/mcp:<server>:<prompt>`.
1016
1039
 
1017
1040
  Add MCP servers by editing `~/.jupyter/nbi/mcp.json`. An environment-wide base file at `<env-prefix>/share/jupyter/nbi/mcp.json` is also supported.
1018
1041
 
@@ -1139,12 +1162,46 @@ c.NotebookIntelligence.enable_chat_feedback_always_visible = True
1139
1162
 
1140
1163
  <img src="media/chat-feedback.png" alt="Chat feedback" width=500 />
1141
1164
 
1165
+ ## Configuration readiness
1166
+
1167
+ NBI Settings opens on a **Status** card that answers one question: is this deployment configured to work, and if not, which specific piece is missing? Each check reports `ok`, `warn`, or `blocked`, and anything that is not `ok` carries the next action rather than just the failure ("the claude CLI was not found" plus "install it, or set `NBI_CLAUDE_CLI_PATH` and restart").
1168
+
1169
+ It runs on open and costs nothing: it resolves your provider and model, checks the model list is reachable and that your selected model is one the endpoint actually serves, and for the agent modes checks that the CLI answers `--version` and that credentials are present. A missing key is a warning rather than an error, because the Claude CLI and ACP agents can hold their own subscription logins that NBI cannot see.
1170
+
1171
+ **Test the endpoint** sends one short request to your configured model, after asking first. It is the only way to catch two failures nothing cheaper can see: a gateway that returns 200s but buffers instead of streaming, and a proxy that strips the `tools` field so agent mode silently never calls a tool. No model output is kept, so the result is safe to paste into a support ticket.
1172
+
1173
+ That button is the only readiness check that bills anything, so it has an off switch: `NBI_READINESS_LIVE_CHECK=off` (traitlet `readiness_live_check_allowed`, default on) refuses it while leaving every other check available. One live test runs at a time per server; a second concurrent request gets a 429 rather than spending tokens.
1174
+
1175
+ ## Performance diagnostics
1176
+
1177
+ An opt-in mode for answering "where did this turn's time go," aimed at deployments where NBI feels slower than it should: internal LLM gateways, network home directories, TLS-intercepting proxies. Off by default, and when off the cost is a single boolean check per instrumentation site.
1178
+
1179
+ **Turning it on.** NBI Settings, **Performance** tab, **Enabled**. It takes effect immediately with no restart.
1180
+
1181
+ **Reading a turn.** Every chat turn records a phase timeline (rule and context preparation, agent connect and spawn, time to first token, streaming with stall events, tool calls, time spent waiting on you) plus token counts and the SDK-reported API duration. The table shows the recent turns, one row each, with a **Verdict** column naming the phase that dominated. The comparison that matters most is **Active** against **API ms**: close together means the time is in the gateway or the model, far apart means it is local. Expanding a row shows that turn's spans as proportional bars with their attributes. Each turn is also summarized as one `perf turn ...` INFO line in the Jupyter server log, so headless installs get the same signal with no UI.
1182
+
1183
+ **Reading the environment.** **Run probe** measures the machine instead of a turn: filesystem latency and throughput for the config and `~/.claude` directories with filesystem type and mount options, interpreter and CLI cold-start cost, and process and host contention. Results are grouped and banded, so a slow network filesystem or a throttled container is called out in a sentence rather than left in a JSON blob. An opt-in network check adds connections to your configured endpoint to time DNS, TCP, and TLS and to capture the presented certificate chain, which is what makes TLS interception visible. It asks for confirmation first and names the host it will contact.
1184
+
1185
+ **Privacy.** Recorded: durations, counts, byte sizes, status enums, token counts, and tool, server, model, and provider names. Never recorded: prompt or response text, file contents, absolute paths, environment variable values, API keys, exception messages, or hostnames. This is enforced by a fixed per-attribute allowlist rather than by convention, and the default `redacted` detail level additionally hashes file basenames and model, tool, and server names.
1186
+
1187
+ **Collecting across sessions.** Turns can optionally be appended as JSON Lines to `<nbi-dir>/perf/`, with size- and age-capped retention. On network home directories, point the log at local scratch with `NBI_PERF_LOG_DIR`.
1188
+
1189
+ Full guide, including the span and event reference, the probe thresholds, worked diagnoses for a slow gateway, a slow home directory, and an intercepting proxy, and the JSONL schema: [`docs/performance-diagnostics.md`](docs/performance-diagnostics.md).
1190
+
1191
+ | Environment variable | Effect |
1192
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------- |
1193
+ | `NBI_PERF_DIAGNOSTICS` | Locks the enabled state on or off (value lock; the UI shows the toggle as locked). |
1194
+ | `NBI_PERF_DIAGNOSTICS_POLICY` | `user-choice` (default), `force-off`, or `force-on` (fleet-wide collection with redacted attributes enforced). |
1195
+ | `NBI_PERF_PROBE_NETWORK` | `off` disables just the probe's network check while leaving the rest available. |
1196
+ | `NBI_PERF_LOG_DIR` | Redirects the JSONL perf log directory. |
1197
+
1142
1198
  ## Documentation
1143
1199
 
1144
1200
  - [`docs/admin-guide.md`](docs/admin-guide.md) — deployment, env vars, security model, air-gap, multi-tenancy.
1145
1201
  - [`docs/skills.md`](docs/skills.md) — Claude Skills management and the org-manifest reconciler.
1146
1202
  - [`docs/rulesets.md`](docs/rulesets.md) — ruleset frontmatter and discovery.
1147
1203
  - [`docs/troubleshooting.md`](docs/troubleshooting.md) — common problems with copy-pasteable fixes.
1204
+ - [`docs/performance-diagnostics.md`](docs/performance-diagnostics.md): turn timelines, the environment probe, and how to read both.
1148
1205
  - [`PRIVACY.md`](PRIVACY.md) — what NBI sends to which provider, and the egress allowlist.
1149
1206
  - [`SECURITY.md`](SECURITY.md) — how to report a vulnerability.
1150
1207
  - [`CHANGELOG.md`](CHANGELOG.md) — release history.
@@ -17,6 +17,7 @@ NBI is free and open-source. Connect it to a free or paid LLM provider of your c
17
17
  - [Chat interface](#chat-interface)
18
18
  - [Cell output actions](#cell-output-actions)
19
19
  - [Notebook toolbar generation](#notebook-toolbar-generation)
20
+ - [Multi-language and kernel-aware notebooks](#multi-language-and-kernel-aware-notebooks)
20
21
  - [Reload open files when changed on disk](#reload-open-files-when-changed-on-disk)
21
22
  - [Configuration](#configuration)
22
23
  - [Configuration files](#configuration-files)
@@ -29,6 +30,8 @@ NBI is free and open-source. Connect it to a free or paid LLM provider of your c
29
30
  - [Claude MCP Servers](#claude-mcp-servers)
30
31
  - [Claude Plugins](#claude-plugins)
31
32
  - [Chat feedback](#chat-feedback)
33
+ - [Configuration readiness](#configuration-readiness)
34
+ - [Performance diagnostics](#performance-diagnostics)
32
35
  - [Documentation](#documentation)
33
36
  - [Further reading](#further-reading)
34
37
  - [Roadmap](#roadmap)
@@ -85,6 +88,9 @@ Configure via the NBI Settings dialog (gear icon in the chat panel, or _Settings
85
88
  - **Chat Agent setting sources** — user, project, or both, mirroring [Claude Code's settings](https://code.claude.com/docs/en/settings).
86
89
  - **Chat Agent tools** — which tool sets to activate. _Claude Code tools_ are always on. _Jupyter UI tools_ are NBI's own (authoring notebooks, running cells, etc.).
87
90
  - **API key** and **Base URL** — point at Anthropic or a self-hosted endpoint.
91
+ - **Show turn usage**: off by default. When on, each completed turn ends with a one-line italic footer giving the turn's duration and token counts. A `$` cost figure appears only when NBI is running against a direct Anthropic API key on Anthropic's own endpoint: the SDK prices from the CLI's built-in public list rates, which are not the marginal cost on a subscription login and not the rate on a custom `base_url`, so NBI omits a number it cannot stand behind rather than showing a misleading one.
92
+
93
+ **Model defaults.** With no model selected, the chat model resolves to the newest Sonnet (`claude-sonnet-5` today) and auto-complete to the newest Haiku, both matched against the model list fetched from the configured endpoint rather than a hardcoded ID. If a configured ID no longer exists, NBI falls back within the same tier by version rather than alphabetically, so `claude-sonnet-99` resolves to the newest Sonnet. The fetched list is cached per endpoint, so changing **Base URL** does not reuse the previous endpoint's models. Auto-complete responses are capped at 1024 tokens, since a suggestion is at most a few dozen lines; `NBI_CLAUDE_INLINE_COMPLETION_MAX_TOKENS` overrides the cap and is clamped to `[1, 4096]`.
88
94
 
89
95
  If the Claude Code CLI is on `PATH`, NBI launches it automatically. To override the location, set the `NBI_CLAUDE_CLI_PATH` environment variable before starting JupyterLab.
90
96
 
@@ -105,7 +111,7 @@ Bypass Permissions never persists: starting a **New chat session** (or `/clear`)
105
111
 
106
112
  Choosing **Bypass Permissions** does not arm it immediately. It opens a confirmation step; only after you confirm does bypass take effect, and while it is active the shield turns into a red warning icon as a persistent indicator. Bypass must be re-armed each session: starting a new chat or restarting the Claude client drops back to Default. And because the server re-checks the requested mode on every message, an armed bypass can never outlive a policy that an administrator has since turned off.
107
113
 
108
- **Admin gating.** Bypass Permissions is **off by default** and hidden from the selector unless an administrator enables it: it is governed by the `claude_bypass_permissions` policy (`NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY`), the only admin policy whose default is `force-off` rather than `user-choice`. The requested mode is also clamped on the server for every message, so the gate can't be bypassed by a hand-crafted request. Independently, NBI honors Claude Code's enterprise [managed settings](https://code.claude.com/docs/en/settings): `permissions.disableBypassPermissionsMode` removes the option regardless of the NBI policy, and `permissions.defaultMode` sets the selector's starting mode (Bypass excepted, since it never auto-arms). See [Allowing Bypass Permissions](docs/admin-guide.md#allowing-bypass-permissions-in-the-claude-permission-mode-selector) in the admin guide.
114
+ **Admin gating.** Bypass Permissions is **off by default** and hidden from the selector unless an administrator enables it: it is governed by the `claude_bypass_permissions` policy (`NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY`), one of three admin policies that default to `force-off` rather than `user-choice` (the others gate the experimental ACP agent mode and ACP full access). The requested mode is also clamped on the server for every message, so the gate can't be bypassed by a hand-crafted request. Independently, NBI honors Claude Code's enterprise [managed settings](https://code.claude.com/docs/en/settings): `permissions.disableBypassPermissionsMode` removes the option regardless of the NBI policy, and `permissions.defaultMode` sets the selector's starting mode (Bypass excepted, since it never auto-arms). See [Allowing Bypass Permissions](docs/admin-guide.md#allowing-bypass-permissions-in-the-claude-permission-mode-selector) in the admin guide.
109
115
 
110
116
  The `/enter-plan-mode` and `/exit-plan-mode` slash commands still work if typed but are no longer offered in autocomplete; the selector replaces them and will retire the commands in a future release.
111
117
 
@@ -178,6 +184,12 @@ Each is per-user toggleable from Settings (saved as `enable_explain_error`, `ena
178
184
 
179
185
  Active notebooks show a sparkle icon on the toolbar. Click it to open a popover that scopes the generation request to that specific notebook — handy for multi-notebook sessions where you don't want the chat sidebar to compete for context.
180
186
 
187
+ ### Multi-language and kernel-aware notebooks
188
+
189
+ Notebook creation, cell insertion, and inline generation follow the notebook you are actually in rather than assuming Python. NBI resolves the active notebook's kernel and language and carries both through the chat context, the built-in tools, and ruleset scoping, so generated code matches the target notebook, and a rule scoped to `languages: ['r']` or `kernel_names: ['ir']` applies where you would expect (including in inline generation, which previously did not receive the kernel).
190
+
191
+ When the agent needs a notebook in a language the current context has not established, it calls a `list-available-notebook-kernels` tool and picks from the kernelspecs that are actually installed instead of guessing a name. Asking for a kernel that is not installed now fails with a clear error rather than silently producing a Python notebook.
192
+
181
193
  ### Reload open files when changed on disk
182
194
 
183
195
  NBI reloads open document tabs when their files change on disk, so edits an AI agent makes via its Read/Write tools appear in the editor without a manual refresh. Tabs with unsaved local edits are skipped so user work is never clobbered. Toggle via the **NBI Settings dialog → External changes → "Refresh open files when changed on disk"** (default on).
@@ -202,24 +214,26 @@ Most settings panel toggles can be locked by org administrators. Two shapes:
202
214
 
203
215
  **Boolean policies** use the `*_POLICY` suffix and accept three values: `user-choice` (default — user toggles freely), `force-on` (locked enabled), `force-off` (locked disabled). When forced, the panel control is disabled with a "Locked by your administrator" tooltip and any client-side write is ignored.
204
216
 
205
- | Env var | Locks the Settings panel control for |
206
- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
207
- | `NBI_EXPLAIN_ERROR_POLICY` | "Explain cell errors" |
208
- | `NBI_OUTPUT_FOLLOWUP_POLICY` | "Ask about cell outputs" |
209
- | `NBI_OUTPUT_TOOLBAR_POLICY` | "Show output toolbar" |
210
- | `NBI_CLAUDE_MODE_POLICY` | "Enable Claude mode" |
211
- | `NBI_CLAUDE_CONTINUE_CONVERSATION_POLICY` | "Remember conversation history" |
212
- | `NBI_CLAUDE_CODE_TOOLS_POLICY` | "Claude Code tools" |
213
- | `NBI_CLAUDE_JUPYTER_UI_TOOLS_POLICY` | "Jupyter UI tools" |
214
- | `NBI_CLAUDE_SETTING_SOURCE_USER_POLICY` | Setting source: User |
215
- | `NBI_CLAUDE_SETTING_SOURCE_PROJECT_POLICY` | Setting source: Project |
216
- | `NBI_STORE_GITHUB_ACCESS_TOKEN_POLICY` | "Remember my GitHub Copilot access token" |
217
- | `NBI_SKILLS_MANAGEMENT_POLICY` | The Skills tab (force-off hides it and 403s the API; also disables the managed-skills reconciler) |
218
- | `NBI_CLAUDE_MCP_MANAGEMENT_POLICY` | The Claude-mode MCP Servers tab (force-off hides it and 403s `/claude-mcp/*`; independent of the non-Claude MCP Servers tab) |
219
- | `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY` | The Claude-mode Plugins tab (force-off hides it and 403s `/plugins/*`) |
220
- | `NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY` | "Bypass Permissions" in the Claude permission-mode selector (defaults to `force-off`, the only policy that does; `user-choice` exposes the option, which the user still arms per session) |
221
- | `NBI_TERMINAL_DRAG_DROP_POLICY` | Terminal drag-drop file attach feature |
222
- | `NBI_REFRESH_OPEN_FILES_ON_DISK_CHANGE_POLICY` | "Refresh open files when changed on disk" |
217
+ | Env var | Locks the Settings panel control for |
218
+ | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
219
+ | `NBI_EXPLAIN_ERROR_POLICY` | "Explain cell errors" |
220
+ | `NBI_OUTPUT_FOLLOWUP_POLICY` | "Ask about cell outputs" |
221
+ | `NBI_OUTPUT_TOOLBAR_POLICY` | "Show output toolbar" |
222
+ | `NBI_CLAUDE_MODE_POLICY` | "Enable Claude mode" |
223
+ | `NBI_ACP_MODE_POLICY` | "Enable ACP mode" (experimental agent mode over the Agent Client Protocol, with Codex as the first agent type; defaults to `force-off`, set `user-choice` to allow it) |
224
+ | `NBI_ACP_FULL_ACCESS_POLICY` | "Full access" for the ACP agent: run tools without asking (defaults to `force-off`, so the agent asks before anything beyond trusted read-only commands; `user-choice` lets users opt into unattended runs) |
225
+ | `NBI_CLAUDE_CONTINUE_CONVERSATION_POLICY` | "Remember conversation history" |
226
+ | `NBI_CLAUDE_CODE_TOOLS_POLICY` | "Claude Code tools" |
227
+ | `NBI_CLAUDE_JUPYTER_UI_TOOLS_POLICY` | "Jupyter UI tools" |
228
+ | `NBI_CLAUDE_SETTING_SOURCE_USER_POLICY` | Setting source: User |
229
+ | `NBI_CLAUDE_SETTING_SOURCE_PROJECT_POLICY` | Setting source: Project |
230
+ | `NBI_STORE_GITHUB_ACCESS_TOKEN_POLICY` | "Remember my GitHub Copilot access token" |
231
+ | `NBI_SKILLS_MANAGEMENT_POLICY` | The Skills tab (force-off hides it and 403s the API; also disables the managed-skills reconciler) |
232
+ | `NBI_CLAUDE_MCP_MANAGEMENT_POLICY` | The Claude-mode MCP Servers tab (force-off hides it and 403s `/claude-mcp/*`; independent of the non-Claude MCP Servers tab) |
233
+ | `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY` | The Claude-mode Plugins tab (force-off hides it and 403s `/plugins/*`) |
234
+ | `NBI_CLAUDE_BYPASS_PERMISSIONS_POLICY` | "Bypass Permissions" in the Claude permission-mode selector (defaults to `force-off`, as do `NBI_ACP_MODE_POLICY` and `NBI_ACP_FULL_ACCESS_POLICY`; `user-choice` exposes the option, which the user still arms per session) |
235
+ | `NBI_TERMINAL_DRAG_DROP_POLICY` | Terminal drag-drop file attach feature |
236
+ | `NBI_REFRESH_OPEN_FILES_ON_DISK_CHANGE_POLICY` | "Refresh open files when changed on disk" |
223
237
 
224
238
  The first three also have matching traitlets on `NotebookIntelligence` (`explain_error_policy`, `output_followup_policy`, `output_toolbar_policy`); add the others as needed in the same shape:
225
239
 
@@ -240,10 +254,18 @@ Per-user preferences (default on for the cell-output features) live in `config.j
240
254
  | `NBI_CHAT_MODEL_ID` | General → Chat model → Model |
241
255
  | `NBI_INLINE_COMPLETION_MODEL_PROVIDER` | General → Auto-complete model → Provider |
242
256
  | `NBI_INLINE_COMPLETION_MODEL_ID` | General → Auto-complete model → Model |
243
- | `NBI_CLAUDE_CHAT_MODEL` | Claude → Chat model |
257
+ | `NBI_CLAUDE_CHAT_MODEL` | Claude → Chat model (and Inline chat model, unless pinned separately) |
258
+ | `NBI_CLAUDE_INLINE_CHAT_MODEL` | Claude → Chat model → Inline chat model |
244
259
  | `NBI_CLAUDE_INLINE_COMPLETION_MODEL` | Claude → Auto-complete model |
245
260
  | `ANTHROPIC_API_KEY` | Claude → API Key (input is locked + blanked; the SDK reads the env directly) |
246
261
  | `ANTHROPIC_BASE_URL` | Claude → Base URL |
262
+ | `NBI_ACP_CHAT_MODEL` | ACP → Chat model |
263
+ | `OPENAI_API_KEY` | ACP → API Key (input is locked + blanked; the agent reads the env directly) |
264
+ | `OPENAI_BASE_URL` | ACP → Base URL |
265
+
266
+ Inline chat calls the Anthropic API directly while the other Claude modes go through the Claude Code CLI, so a deployment can need a different model id for each. It uses `inline_chat_model`, falling back to `chat_model` when that is unset. `NBI_CLAUDE_CHAT_MODEL` therefore governs both surfaces: while it is set, a stored `inline_chat_model` is ignored and its control is disabled. Set `NBI_CLAUDE_INLINE_CHAT_MODEL` as well to pin inline chat to a different model — it takes precedence and re-enables nothing, so both surfaces stay administrator-controlled.
267
+
268
+ `NBI_ACP_AGENT_COMMAND` (not a settings lock) overrides the adapter command NBI launches for the selected ACP agent type; the value is shell-split. Leave it unset to use the agent's registered default command.
247
269
 
248
270
  Provider IDs: `github-copilot`, `openai-compatible`, `litellm-compatible`, `ollama`, `none`. The `*_MODEL_ID` value is whatever the chosen provider exposes (e.g. `gpt-4o`, `llama3:latest`). Claude model IDs are the literal IDs from the Anthropic API (e.g. `claude-opus-4-7`, `claude-sonnet-4-6`); empty string = "Default (recommended)"; `NBI_CLAUDE_INLINE_COMPLETION_MODEL` also accepts `none` (no inline completion in Claude mode) or `inherit` (use the General-tab Auto-complete model).
249
271
 
@@ -290,7 +312,7 @@ In multi-tenant deployments, `nbi-command-execute` and `nbi-file-edit` are effec
290
312
 
291
313
  ## Model Context Protocol (MCP) support
292
314
 
293
- NBI integrates with [MCP](https://modelcontextprotocol.io) servers. It supports both stdio and Streamable HTTP transports. **MCP server tools are supported; resources and prompts are not yet supported.**
315
+ NBI integrates with [MCP](https://modelcontextprotocol.io) servers. It supports both stdio and Streamable HTTP transports. **Tools and prompts are supported; resources are not.** A server's prompts appear in the chat input's slash-command autocomplete as `/mcp:<server>:<prompt>`.
294
316
 
295
317
  Add MCP servers by editing `~/.jupyter/nbi/mcp.json`. An environment-wide base file at `<env-prefix>/share/jupyter/nbi/mcp.json` is also supported.
296
318
 
@@ -417,12 +439,46 @@ c.NotebookIntelligence.enable_chat_feedback_always_visible = True
417
439
 
418
440
  <img src="media/chat-feedback.png" alt="Chat feedback" width=500 />
419
441
 
442
+ ## Configuration readiness
443
+
444
+ NBI Settings opens on a **Status** card that answers one question: is this deployment configured to work, and if not, which specific piece is missing? Each check reports `ok`, `warn`, or `blocked`, and anything that is not `ok` carries the next action rather than just the failure ("the claude CLI was not found" plus "install it, or set `NBI_CLAUDE_CLI_PATH` and restart").
445
+
446
+ It runs on open and costs nothing: it resolves your provider and model, checks the model list is reachable and that your selected model is one the endpoint actually serves, and for the agent modes checks that the CLI answers `--version` and that credentials are present. A missing key is a warning rather than an error, because the Claude CLI and ACP agents can hold their own subscription logins that NBI cannot see.
447
+
448
+ **Test the endpoint** sends one short request to your configured model, after asking first. It is the only way to catch two failures nothing cheaper can see: a gateway that returns 200s but buffers instead of streaming, and a proxy that strips the `tools` field so agent mode silently never calls a tool. No model output is kept, so the result is safe to paste into a support ticket.
449
+
450
+ That button is the only readiness check that bills anything, so it has an off switch: `NBI_READINESS_LIVE_CHECK=off` (traitlet `readiness_live_check_allowed`, default on) refuses it while leaving every other check available. One live test runs at a time per server; a second concurrent request gets a 429 rather than spending tokens.
451
+
452
+ ## Performance diagnostics
453
+
454
+ An opt-in mode for answering "where did this turn's time go," aimed at deployments where NBI feels slower than it should: internal LLM gateways, network home directories, TLS-intercepting proxies. Off by default, and when off the cost is a single boolean check per instrumentation site.
455
+
456
+ **Turning it on.** NBI Settings, **Performance** tab, **Enabled**. It takes effect immediately with no restart.
457
+
458
+ **Reading a turn.** Every chat turn records a phase timeline (rule and context preparation, agent connect and spawn, time to first token, streaming with stall events, tool calls, time spent waiting on you) plus token counts and the SDK-reported API duration. The table shows the recent turns, one row each, with a **Verdict** column naming the phase that dominated. The comparison that matters most is **Active** against **API ms**: close together means the time is in the gateway or the model, far apart means it is local. Expanding a row shows that turn's spans as proportional bars with their attributes. Each turn is also summarized as one `perf turn ...` INFO line in the Jupyter server log, so headless installs get the same signal with no UI.
459
+
460
+ **Reading the environment.** **Run probe** measures the machine instead of a turn: filesystem latency and throughput for the config and `~/.claude` directories with filesystem type and mount options, interpreter and CLI cold-start cost, and process and host contention. Results are grouped and banded, so a slow network filesystem or a throttled container is called out in a sentence rather than left in a JSON blob. An opt-in network check adds connections to your configured endpoint to time DNS, TCP, and TLS and to capture the presented certificate chain, which is what makes TLS interception visible. It asks for confirmation first and names the host it will contact.
461
+
462
+ **Privacy.** Recorded: durations, counts, byte sizes, status enums, token counts, and tool, server, model, and provider names. Never recorded: prompt or response text, file contents, absolute paths, environment variable values, API keys, exception messages, or hostnames. This is enforced by a fixed per-attribute allowlist rather than by convention, and the default `redacted` detail level additionally hashes file basenames and model, tool, and server names.
463
+
464
+ **Collecting across sessions.** Turns can optionally be appended as JSON Lines to `<nbi-dir>/perf/`, with size- and age-capped retention. On network home directories, point the log at local scratch with `NBI_PERF_LOG_DIR`.
465
+
466
+ Full guide, including the span and event reference, the probe thresholds, worked diagnoses for a slow gateway, a slow home directory, and an intercepting proxy, and the JSONL schema: [`docs/performance-diagnostics.md`](docs/performance-diagnostics.md).
467
+
468
+ | Environment variable | Effect |
469
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------- |
470
+ | `NBI_PERF_DIAGNOSTICS` | Locks the enabled state on or off (value lock; the UI shows the toggle as locked). |
471
+ | `NBI_PERF_DIAGNOSTICS_POLICY` | `user-choice` (default), `force-off`, or `force-on` (fleet-wide collection with redacted attributes enforced). |
472
+ | `NBI_PERF_PROBE_NETWORK` | `off` disables just the probe's network check while leaving the rest available. |
473
+ | `NBI_PERF_LOG_DIR` | Redirects the JSONL perf log directory. |
474
+
420
475
  ## Documentation
421
476
 
422
477
  - [`docs/admin-guide.md`](docs/admin-guide.md) — deployment, env vars, security model, air-gap, multi-tenancy.
423
478
  - [`docs/skills.md`](docs/skills.md) — Claude Skills management and the org-manifest reconciler.
424
479
  - [`docs/rulesets.md`](docs/rulesets.md) — ruleset frontmatter and discovery.
425
480
  - [`docs/troubleshooting.md`](docs/troubleshooting.md) — common problems with copy-pasteable fixes.
481
+ - [`docs/performance-diagnostics.md`](docs/performance-diagnostics.md): turn timelines, the environment probe, and how to read both.
426
482
  - [`PRIVACY.md`](PRIVACY.md) — what NBI sends to which provider, and the egress allowlist.
427
483
  - [`SECURITY.md`](SECURITY.md) — how to report a vulnerability.
428
484
  - [`CHANGELOG.md`](CHANGELOG.md) — release history.