diff --git a/CHANGELOG.md b/CHANGELOG.md index 007fd7f3..c3285ae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,71 @@ For each release we list user-facing changes grouped as **Added**, **Changed**, +## [Unreleased] + +### Added + +- **Skills as a top-level Settings tab** (#224). Promoted from a Claude-mode sub-tab; the tab is now visible in any mode, with a hint banner when Claude mode is off. New admin policy `skills_management_policy` (env `NBI_SKILLS_MANAGEMENT_POLICY`); `force-off` hides the tab, returns HTTP 403 from every `/notebook-intelligence/skills/*` route, and suppresses the managed-skills reconciler. +- **Claude MCP Servers tab** (#225) for managing the user, project, and local-scope MCP entries Claude Code reads from `~/.claude.json` and `/.mcp.json`. Independent of the existing NBI MCP tab; the two never appear at the same time. New admin policy `claude_mcp_management_policy` (env `NBI_CLAUDE_MCP_MANAGEMENT_POLICY`). +- **Claude Plugins tab** (#226) wrapping `claude plugin` for install / uninstall / enable / disable / marketplace add. New admin policies `claude_plugins_management_policy` (env `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY`) and `allow_github_plugin_import` (env `NBI_ALLOW_GITHUB_PLUGIN_IMPORT`), the latter mirroring the existing `allow_github_skill_import` knob for marketplace sources. +- **GitHub auth for plugin marketplace add**. Marketplace sources that resolve as GitHub URLs or `owner/repo` shorthand reuse Skills' `GITHUB_TOKEN` / `GH_TOKEN` / `gh auth token` precedence; tokens are injected into the `claude plugin marketplace add` subprocess via env, never argv. +- **Launcher tiles for opencode, Pi, and GitHub Copilot CLI** (#268), with a follow-on for **OpenAI Codex**. Each tile appears when the corresponding binary is on `PATH` and opens a Jupyter terminal at the file-browser's current directory. CLI path overrides: `NBI_OPENCODE_CLI_PATH`, `NBI_PI_CLI_PATH`, `NBI_GITHUB_COPILOT_CLI_PATH`, `NBI_CODEX_CLI_PATH`. The capabilities response gains matching `*_cli_available` booleans. +- **Claude Code launcher tile is no longer gated by Claude chat mode** (#239); it appears whenever the `claude` CLI is on `PATH`. +- **Dynamic GitHub Copilot model discovery** (#269). NBI now queries `https://api.githubcopilot.com/models` on each Copilot token refresh and the chat-model dropdown rebuilds from the live response, falling back to a hardcoded list on transient failure. +- **Newer GitHub Copilot chat models** added to the fallback list (#255). +- **Skill GitHub archive cap raised to 100 MB**, configurable (#257). New traitlet `skill_max_archive_mb` (env `NBI_SKILL_MAX_ARCHIVE_MB`); `0` disables the cap. +- **`additional_skipped_workspace_directories` accepted in NBI `config.json`** (#241), layered additively on top of the existing traitlet, env, and env-prefix layers so a per-user override can extend (rather than replace) the org-wide list. +- **Real progress feedback during long Claude tasks** (#254). The chat sidebar shows an elapsed-time counter, a heartbeat-driven pulse with a "may be slow" copy flip after 30 seconds, and inline tool-call narration. +- **"New chat session" button** in the chat sidebar header restarts the Claude SDK client, mirroring the `/clear` slash command (#246). +- **Terminal drag-drop file attach** with `@`-mention or shell-escaped raw modes and a per-terminal toolbar toggle (#256). New admin policy `NBI_TERMINAL_DRAG_DROP_POLICY`; tunables `NBI_UPLOAD_MAX_MB` (default `50`) and `NBI_UPLOAD_RETENTION_HOURS` (default `24`) govern the shared upload-staging endpoint used by both terminal drops and chat-sidebar attachments. +- **Hover preview for image context thumbnails** in the chat sidebar (#267). + +### Changed + +- Settings tabs are now an ARIA tablist with arrow-key navigation (#206). +- Accessibility pass across the chat sidebar and popovers covering keyboard and screen-reader behavior (#205). +- Chat-input footer icons reworded for clarity; the gear button gains a `title` attribute (#271). +- Cell-tool descriptions mention zero-based indexing so models pick the right cell (#265). + +### Fixed + +- Websocket writes from worker threads no longer raise `BufferError` after `/clear` or "new chat" on Python 3.13+ (#270). All emitter writes now route through `tornado.IOLoop.call_soon_threadsafe`. +- Cell tools follow the active notebook when the user switches tabs (#253). +- `is_connected()` stabilized against the Claude worker-spawn resurrection race (#250). +- Persisted Claude model now displays after a JupyterLab restart (#244). +- `/clear` no longer duplicated in the `@`-mention autocomplete (#243). +- `@`-mention picker refreshes when workspace files change (#251) and closes on Escape from the search input (#266). +- Notebook-toolbar prompt textarea focuses when the popover opens (#240); the update button works outside Claude mode (#238). +- Inline chat anchors to the cursor line (#191). +- Disabled send button is styled neutrally instead of as a primary action (#276); Claude tool-result check renders on the right of its label (#277). +- Plugin Settings row shows the plugin name even when the CLI returns only `id` (#280). + +### Internal + +- CVE-driven dependency upgrades (#197); `react-icons` bumped to `~5.6.0` (#245). +- Galata-based Playwright UI test suite scaffolded (#207) and expanded with user-flow specs covering the chat sidebar, notebook toolbar, cell outputs, and the launcher (#272). +- Contributor docs cover the traitlet vs env var vs config-file decision (#242). + + + +## [4.8.0] - 2026-05-11 + +### Added + +- **`allow_github_skill_import` traitlet** (env `NBI_ALLOW_GITHUB_SKILL_IMPORT`) gating user-initiated skill imports from GitHub independently of the managed-skills reconciler (#222). When `False`, the **Import from GitHub** button hides and `/skills/import` returns HTTP 403. +- **Workspace picker honors `.gitignore`** and gains the `additional_skipped_workspace_directories` traitlet (env `NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES`, layered additively) for extending the built-in skip list (#223). Dot-prefixed files are also skipped by default (#221). +- Workspace file scan in the `@`-mention picker now runs in parallel (#227). + +### Changed + +- Skill imports from GitHub block and scope HTTP redirects, including refusing HTTPS-to-HTTP downgrades (#203). +- Settings tab content scrolls correctly when its body is taller than the dialog (#228); the tab bar styling is standardized across tabs. + +### Fixed + +- `NBIConfig.save()` is atomic (#202): symlinks are preserved, file mode is preserved across the swap, and the rename is parent-dir fsynced. Prevents the corrupt-config failure mode where a crash mid-write left an empty `config.json`. +- The NBI notebook toolbar is disabled outside Claude mode where its buttons did not work (#228); a stray new-notebook button was removed. + ## [4.7.0] — 2026-05-07 ### Added @@ -181,7 +246,8 @@ For each release we list user-facing changes grouped as **Added**, **Changed**, - Settings UI restructured around Claude vs default mode. - WebSocket connection reliability improvements. -[unreleased]: https://github.com/notebook-intelligence/notebook-intelligence/compare/v4.7.0...HEAD +[unreleased]: https://github.com/notebook-intelligence/notebook-intelligence/compare/v4.8.0...HEAD +[4.8.0]: https://github.com/notebook-intelligence/notebook-intelligence/compare/v4.7.0...v4.8.0 [4.7.0]: https://github.com/notebook-intelligence/notebook-intelligence/compare/v4.6.0...v4.7.0 [4.6.0]: https://github.com/notebook-intelligence/notebook-intelligence/compare/v4.5.0...v4.6.0 [4.5.0]: https://github.com/notebook-intelligence/notebook-intelligence/compare/v4.4.0...v4.5.0 diff --git a/README.md b/README.md index 389415d4..4cc624ea 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ NBI is free and open-source. Connect it to a free or paid LLM provider of your c - [MCP config example](#mcp-config-example) - [Rulesets](#rulesets) - [Claude Skills](#claude-skills) +- [Claude MCP Servers](#claude-mcp-servers) +- [Claude Plugins](#claude-plugins) - [Chat feedback](#chat-feedback) - [Documentation](#documentation) - [Further reading](#further-reading) @@ -64,6 +66,8 @@ A short glossary you'll see referenced throughout these docs. - **Claude Code vs the Anthropic API** — the _Anthropic API_ (`api.anthropic.com`) is the HTTPS endpoint NBI calls directly for inline chat and auto-complete in Claude mode. _Claude Code_ is Anthropic's local CLI agent that NBI shells out to for the chat panel; it talks to Anthropic itself. - **MCP** — [Model Context Protocol](https://modelcontextprotocol.io/). A way for the LLM to call out to external tools (read files, hit APIs, run scripts). - **Ruleset** — markdown files in `~/.jupyter/nbi/rules/` that get injected into the system prompt to enforce conventions, coding standards, or domain rules. +- **Skill**: a directory under `~/.claude/skills/` (or `/.claude/skills/`) holding a `SKILL.md` plus helper files. Claude can invoke it like a callable plugin scoped to a workspace. +- **Claude plugin**: a unit packaged for `claude plugin install`, distributed through a **marketplace** (typically a GitHub repo that publishes a manifest of plugins). Distinct from NBI's own labextension; plugins run inside Claude Code sessions. ## Feature highlights @@ -91,7 +95,18 @@ When Claude mode is on, the chat sidebar shows a history icon next to the gear. #### Claude Code launcher tile -When Claude mode is enabled and the Claude CLI is available, the JupyterLab launcher (the panel that opens with new tabs) shows a **Claude Code** tile alongside the standard kernel launchers. Clicking it opens a session picker — search across past transcripts and resume one in a fresh terminal, or start a new session in the file browser's active subdirectory. Session IDs are copyable from the picker for paste into a `claude --resume ` command. +When the Claude CLI is on `PATH`, the JupyterLab launcher (the panel that opens with new tabs) shows a **Claude Code** tile alongside the standard kernel launchers. Clicking it opens a session picker; search across past transcripts and resume one in a fresh terminal, or start a new session in the file browser's active subdirectory. Session IDs are copyable from the picker for paste into a `claude --resume ` command. + +#### Other coding-agent launcher tiles + +When any of the following CLIs are on `PATH`, the launcher adds a tile for each. Clicking a tile opens a terminal at the file browser's current directory and runs the CLI: + +- **opencode** (override path with `NBI_OPENCODE_CLI_PATH`) +- **Pi** (override path with `NBI_PI_CLI_PATH`) +- **GitHub Copilot CLI** (override path with `NBI_GITHUB_COPILOT_CLI_PATH`) +- **OpenAI Codex** (override path with `NBI_CODEX_CLI_PATH`) + +Tiles add and remove themselves as CLIs become available or unavailable; they do not require Claude mode. ### Agent mode @@ -322,12 +337,24 @@ For full details (frontmatter reference, mode-specific rules, auto-reload), see ## Claude Skills -When Claude mode is enabled, the Settings panel exposes a **Skills** tab for managing the skills that Claude can invoke. Skills are stored under `~/.claude/skills/` (user) or `/.claude/skills/` (project). You can create and edit skills inline, duplicate, rename, delete (with undo), or import from a public GitHub repo. +The Settings panel exposes a top-level **Skills** tab for managing the skills that Claude can invoke. Skills are stored under `~/.claude/skills/` (user) or `/.claude/skills/` (project). You can create and edit skills inline, duplicate, rename, delete (with undo), or import from a public GitHub repo. The tab is visible in any mode; when Claude mode is off, a hint banner notes that skills only take effect inside Claude sessions (handy for authoring skills now and using them when you turn Claude mode on later). -For organization-wide deployments, NBI can install and keep a curated set of skills in sync from a YAML manifest pointed at by `NBI_SKILLS_MANIFEST`. Managed skills are read-only in the UI and refreshed on a schedule. +For organization-wide deployments, your admin can ship a curated manifest of skills and keep them in sync by setting `NBI_SKILLS_MANIFEST`. Skills installed this way are marked **Managed** and are read-only in the UI. Admins who want to keep managed skills but disallow ad-hoc GitHub imports use `NBI_ALLOW_GITHUB_SKILL_IMPORT=false`. For full details, see [`docs/skills.md`](docs/skills.md). +## Claude MCP Servers + +When Claude mode is enabled and the Claude CLI is available, the Settings panel exposes an **MCP Servers** tab that manages the user, project, and local-scope MCP entries Claude Code reads from `~/.claude.json` and the project's `.mcp.json`. This is a different tab from NBI's own MCP Servers tab (which manages the servers used by the non-Claude chat path); the two never appear together, and the Settings dialog shows whichever one applies to your current mode. + +Reads come from Claude's JSON config files directly. Writes (add and remove) shell out to `claude mcp add` and `claude mcp remove` so Claude remains the source of truth for any side effects (project-trust prompts, OAuth bookkeeping). Admins can lock the tab with `NBI_CLAUDE_MCP_MANAGEMENT_POLICY=force-off`. + +## Claude Plugins + +When Claude mode is enabled and the Claude CLI is available, the Settings panel exposes a **Plugins** tab wrapping `claude plugin` for install, uninstall, enable, disable, and marketplace add (for example: add a marketplace from a GitHub repo, then install plugins it publishes). Marketplaces hosted on GitHub reuse the same `GITHUB_TOKEN` / `GH_TOKEN` / `gh auth token` precedence as Skills imports; the token is passed via env to the subprocess and never appears in argv or DEBUG logs. See Anthropic's [plugin docs](https://code.claude.com/docs/en/plugins) for what a plugin is and how marketplaces work. + +Admins can lock the entire tab with `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY=force-off`, or keep the tab and block only GitHub-sourced marketplaces with `NBI_ALLOW_GITHUB_PLUGIN_IMPORT=false`. + ## Chat feedback Enable thumbs-up/down feedback on AI responses by setting: diff --git a/docs/admin-guide.md b/docs/admin-guide.md index e119a529..6ec08675 100644 --- a/docs/admin-guide.md +++ b/docs/admin-guide.md @@ -75,32 +75,44 @@ If users share a home directory across nodes (NFS-backed shared HPC, classroom l The full surface, in one table. -| Name | Type | Default | Source | Purpose | -| ---------------------------------------------- | ---- | --------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `disabled_providers` | List | `[]` | traitlet on `NotebookIntelligence` | Hide providers from the user dropdown. Values: `github-copilot`, `ollama`, `litellm-compatible`, `openai-compatible`. | -| `allow_enabling_providers_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_PROVIDERS` re-enables hidden providers per pod. | -| `NBI_ENABLED_PROVIDERS` | csv | unset | env | Comma-separated provider IDs to re-enable. Effective only when `allow_enabling_providers_with_env=True`. | -| `disabled_tools` | List | `[]` | traitlet | Hide built-in tools from agent mode. Values listed in [Restricting features](#restricting-features-for-managed-deployments). | -| `allow_enabling_tools_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_BUILTIN_TOOLS` re-enables hidden tools per pod. | -| `NBI_ENABLED_BUILTIN_TOOLS` | csv | unset | env | Comma-separated tool IDs to re-enable. Effective only when `allow_enabling_tools_with_env=True`. | -| `enable_chat_feedback` | Bool | `False` | traitlet | Enables thumbs-up/down UI in chat and emits in-process `telemetry` events. | -| `additional_skipped_workspace_directories` | List | `[]` | traitlet | Extra directory names to skip in the chat-sidebar @-mention workspace file picker. Merged with the built-in skips (`__pycache__`, `node_modules`). Match is by directory name only, case-sensitive. | -| `NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES` | csv | unset | env (appends to traitlet) | Comma-separated extra directory names. Resolved at server startup and concatenated with the traitlet value, so a spawn profile can add to (rather than replace) the org-wide list. | -| `allow_github_skill_import` | Bool | `True` | traitlet | When `False`, hides the **Import from GitHub** button in the Skills panel and rejects `/skills/import` POSTs with 403. Does not affect the managed-skills reconciler. | -| `NBI_ALLOW_GITHUB_SKILL_IMPORT` | bool | unset | env (overrides traitlet) | Per-pod override for `allow_github_skill_import`. Accepts `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off` (case-insensitive). Useful for varying the policy across spawn profiles. | -| `skills_manifest` | str | `""` | traitlet | URL or filesystem path to a managed-skills manifest. See [`docs/skills.md`](skills.md#managed-skills-via-an-org-manifest). | -| `NBI_SKILLS_MANIFEST` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. | -| `skills_manifest_interval` | int | `86400` | traitlet | Seconds between reconciles. | -| `NBI_SKILLS_MANIFEST_INTERVAL` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. | -| `managed_skills_token` | str | `""` | traitlet | Bearer token for managed-skills GitHub fetches. | -| `NBI_MANAGED_SKILLS_TOKEN` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. | -| `NBI_GH_ACCESS_TOKEN_PASSWORD` | str | `nbi-access-token-password` | env | Password used to encrypt the stored Copilot token in `user-data.json`. **Change in multi-tenant deployments.** | -| `NBI_RULES_AUTO_RELOAD` | bool | `true` | env | When `false`, ruleset edits require a JupyterLab restart to take effect. | -| `NBI_CLAUDE_CLI_PATH` | str | unset | env | Absolute path to the Claude Code CLI binary. When unset, NBI looks up `claude` on `PATH`. | -| `NBI_GHE_SUBDOMAIN` | str | `""` | env | GitHub Enterprise subdomain for GitHub Copilot users on a GHE tenant. Empty selects github.com. | -| `NBI_LOG_LEVEL` | str | `INFO` | env | Python logging level for the `notebook_intelligence` logger. | -| `GITHUB_TOKEN`, `GH_TOKEN` | str | unset | env | Used (in that order) by user-initiated skill imports for GitHub auth. Falls back to `gh` CLI auth. | -| `NBI_*_POLICY` | str | `user-choice` | env | Lock individual Settings panel toggles. See [README → Admin policies](../README.md#admin-policies) for the full list of `*_POLICY` env vars and matching traitlets. | +| Name | Type | Default | Source | Purpose | +| ---------------------------------------------- | ---- | --------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `disabled_providers` | List | `[]` | traitlet on `NotebookIntelligence` | Hide providers from the user dropdown. Values: `github-copilot`, `ollama`, `litellm-compatible`, `openai-compatible`. | +| `allow_enabling_providers_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_PROVIDERS` re-enables hidden providers per pod. | +| `NBI_ENABLED_PROVIDERS` | csv | unset | env | Comma-separated provider IDs to re-enable. Effective only when `allow_enabling_providers_with_env=True`. | +| `disabled_tools` | List | `[]` | traitlet | Hide built-in tools from agent mode. Values listed in [Restricting features](#restricting-features-for-managed-deployments). | +| `allow_enabling_tools_with_env` | Bool | `False` | traitlet | If true, `NBI_ENABLED_BUILTIN_TOOLS` re-enables hidden tools per pod. | +| `NBI_ENABLED_BUILTIN_TOOLS` | csv | unset | env | Comma-separated tool IDs to re-enable. Effective only when `allow_enabling_tools_with_env=True`. | +| `enable_chat_feedback` | Bool | `False` | traitlet | Enables thumbs-up/down UI in chat and emits in-process `telemetry` events. | +| `additional_skipped_workspace_directories` | List | `[]` | traitlet | Extra directory names to skip in the chat-sidebar @-mention workspace file picker. Merged with the built-in skips (`__pycache__`, `node_modules`). Match is by directory name only, case-sensitive. | +| `NBI_ADDITIONAL_SKIPPED_WORKSPACE_DIRECTORIES` | csv | unset | env (appends to traitlet) | Comma-separated extra directory names. Resolved at server startup and concatenated with the traitlet value, so a spawn profile can add to (rather than replace) the org-wide list. | +| `allow_github_skill_import` | Bool | `True` | traitlet | When `False`, hides the **Import from GitHub** button in the Skills panel and rejects `/skills/import` POSTs with 403. Does not affect the managed-skills reconciler. | +| `NBI_ALLOW_GITHUB_SKILL_IMPORT` | bool | unset | env (overrides traitlet) | Per-pod override for `allow_github_skill_import`. Accepts `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off` (case-insensitive). Useful for varying the policy across spawn profiles. | +| `skills_manifest` | str | `""` | traitlet | URL or filesystem path to a managed-skills manifest. See [`docs/skills.md`](skills.md#managed-skills-via-an-org-manifest). | +| `NBI_SKILLS_MANIFEST` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. | +| `skills_manifest_interval` | int | `86400` | traitlet | Seconds between reconciles. | +| `NBI_SKILLS_MANIFEST_INTERVAL` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. | +| `managed_skills_token` | str | `""` | traitlet | Bearer token for managed-skills GitHub fetches. | +| `NBI_MANAGED_SKILLS_TOKEN` | str | unset | env (overrides traitlet) | Same as above; env takes precedence. | +| `allow_github_plugin_import` | Bool | `True` | traitlet | When `False`, hides the "From GitHub" affordance in the Plugins panel and rejects `claude plugin marketplace add` requests whose source resolves as a GitHub URL or `owner/repo` shorthand. Local-path and arbitrary-URL sources remain available. | +| `NBI_ALLOW_GITHUB_PLUGIN_IMPORT` | bool | unset | env (overrides traitlet) | Per-pod override for `allow_github_plugin_import`. Accepts `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off` (case-insensitive). | +| `skill_max_archive_mb` | Int | `100` | traitlet | Per-archive on-wire size cap (megabytes) for skill bundles fetched from GitHub. Applies to both user imports and managed-skills tarballs. `0` disables the cap. | +| `NBI_SKILL_MAX_ARCHIVE_MB` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. | +| `upload_max_mb` | Int | `50` | traitlet | Per-file size cap (megabytes) for the shared upload endpoint used by chat-sidebar attachments and terminal drag-drop. Requests over the cap return HTTP 413. `0` disables the cap. | +| `NBI_UPLOAD_MAX_MB` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. | +| `upload_retention_hours` | Int | `24` | traitlet | How long staged uploads survive in the temp directory before the next upload sweeps them. `0` keeps only the atexit purge (uploads survive the session). | +| `NBI_UPLOAD_RETENTION_HOURS` | int | unset | env (overrides traitlet) | Same as above; env takes precedence. | +| `NBI_GH_ACCESS_TOKEN_PASSWORD` | str | `nbi-access-token-password` | env | Password used to encrypt the stored Copilot token in `user-data.json`. **Change in multi-tenant deployments.** | +| `NBI_RULES_AUTO_RELOAD` | bool | `true` | env | When `false`, ruleset edits require a JupyterLab restart to take effect. | +| `NBI_CLAUDE_CLI_PATH` | str | unset | env | Absolute path to the Claude Code CLI binary. When unset, NBI looks up `claude` on `PATH`. | +| `NBI_OPENCODE_CLI_PATH` | str | unset | env | Absolute path to the opencode CLI. When unset, NBI looks up `opencode` on `PATH`. Gates the opencode launcher tile. | +| `NBI_PI_CLI_PATH` | str | unset | env | Absolute path to the Pi CLI. When unset, NBI looks up `pi` on `PATH`. Gates the Pi launcher tile. | +| `NBI_GITHUB_COPILOT_CLI_PATH` | str | unset | env | Absolute path to the GitHub Copilot CLI. When unset, NBI looks up `copilot` on `PATH`. Gates the GitHub Copilot launcher tile. | +| `NBI_CODEX_CLI_PATH` | str | unset | env | Absolute path to the OpenAI Codex CLI. When unset, NBI looks up `codex` on `PATH`. Gates the Codex launcher tile. | +| `NBI_GHE_SUBDOMAIN` | str | `""` | env | GitHub Enterprise subdomain for GitHub Copilot users on a GHE tenant. Empty selects github.com. | +| `NBI_LOG_LEVEL` | str | `INFO` | env | Python logging level for the `notebook_intelligence` logger. | +| `GITHUB_TOKEN`, `GH_TOKEN` | str | unset | env | Used (in that order) by user-initiated skill imports and GitHub-sourced plugin marketplace adds for GitHub auth. Falls back to `gh` CLI auth. | +| `NBI_*_POLICY` | str | `user-choice` | env | Lock individual Settings panel toggles. See [README → Admin policies](../README.md#admin-policies) for the full list of `*_POLICY` env vars and matching traitlets, including `NBI_SKILLS_MANAGEMENT_POLICY`, `NBI_CLAUDE_MCP_MANAGEMENT_POLICY`, `NBI_CLAUDE_PLUGINS_MANAGEMENT_POLICY`, and `NBI_TERMINAL_DRAG_DROP_POLICY`. | Configure traitlets in `jupyter_server_config.py`: @@ -128,7 +140,7 @@ For regulated tenants: 1. Disable the most powerful tools — at minimum `nbi-command-execute` and `nbi-file-edit`. See [Restricting features](#restricting-features-for-managed-deployments). 2. Restrict the providers the user can pick. Force a single self-hosted endpoint with `disabled_providers` plus the org's base config. -3. Disable user-initiated skill imports (currently network-layer only — see [`skills.md`](skills.md#disabling-user-initiated-github-imports)). +3. Disable user-initiated skill imports with `allow_github_skill_import = False` (env `NBI_ALLOW_GITHUB_SKILL_IMPORT=false`), and plugin marketplace adds from GitHub with `allow_github_plugin_import = False` (env `NBI_ALLOW_GITHUB_PLUGIN_IMPORT=false`). Reinforce at the network layer where stronger isolation is required. See [`skills.md`](skills.md#disabling-user-initiated-github-imports) and the [Plugins tab section](#disabling-the-plugins-tab) below. 4. Run with a non-root container user, with no host-network access and no host-path mounts beyond the user's PVC. --- @@ -446,6 +458,25 @@ The chain only fires for github.com sources today. **GitHub Enterprise instances For air-gap deployments, marketplace-add inherits the JupyterLab process env, so the same `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` / `NODE_EXTRA_CA_CERTS` settings documented in [Custom CA certs and corporate proxies](#custom-ca-certs-and-corporate-proxies) apply. Pre-installed plugins (under `~/.claude/plugins/`) keep loading without any network access. +### Disabling terminal drag-drop file attach + +```python +c.NotebookIntelligence.terminal_drag_drop_policy = "force-off" +``` + +Or via env: `NBI_TERMINAL_DRAG_DROP_POLICY=force-off`. + +Force-off hides the per-terminal drag-drop toolbar toggle and rejects upload-staging POSTs from a terminal context. Drag-drop is **enabled by default**; flip it off in regulated tenants where the staging file write or the resulting `@`-mention path is undesirable. + +Terminal drag-drop and chat-sidebar file attach both write to the shared upload-staging directory under the JupyterLab process's `tempfile.gettempdir()`. Two tunables govern that endpoint: + +| Env var | Default | Behavior | +| ---------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `NBI_UPLOAD_MAX_MB` | `50` | Per-file size cap (megabytes). Over the cap returns HTTP 413. `0` disables. | +| `NBI_UPLOAD_RETENTION_HOURS` | `24` | How long staged uploads survive before the next upload sweeps them. `0` keeps only the atexit purge (files survive the session). | + +> **Trust model.** Staged files live in the user's `tempfile.gettempdir()` and inherit the directory's POSIX permissions. The same `nbi-file-read` denylist that scopes general file reads does **not** apply to upload-staged files because they sit outside the Jupyter root. For sensitive-data tenants, set `NBI_UPLOAD_RETENTION_HOURS=0` to skip retention beyond the session and pair with `terminal_drag_drop_policy = force-off` to keep the surface to chat-sidebar attachments only. + --- ## Multi-tenancy and per-team scoping @@ -499,33 +530,39 @@ To pipe feedback into your internal observability stack (Kafka, OTel collector), All routes live under `/notebook-intelligence/`. All require Jupyter authentication (XSRF token plus Jupyter login token) — the labextension obtains these automatically. There is no admin-only route; access control runs through Jupyter Server itself. -| Route | Method | Purpose | -| ----------------------------------------------------------- | --------------- | --------------------------------------------------------------------------------- | -| `/notebook-intelligence/capabilities` | GET | Capabilities + tool/provider gate state. | -| `/notebook-intelligence/config` | GET/POST | Read or update user-scope config. | -| `/notebook-intelligence/update-provider-models` | POST | Refresh model list for a provider (e.g., Anthropic SDK refresh). | -| `/notebook-intelligence/mcp-config-file` | GET/POST | Read or write `~/.jupyter/nbi/mcp.json`. | -| `/notebook-intelligence/reload-mcp-servers` | POST | Re-discover MCP servers without restarting JupyterLab. | -| `/notebook-intelligence/emit-telemetry-event` | POST | Used by the frontend to emit `telemetry` events (e.g., chat feedback). | -| `/notebook-intelligence/gh-login-status` | GET | GitHub Copilot login state. | -| `/notebook-intelligence/gh-login` | POST | Begin GitHub Copilot device-flow login. | -| `/notebook-intelligence/gh-logout` | GET | Sign out of GitHub Copilot. | -| `/notebook-intelligence/copilot` | WS | Streaming chat / inline-completion WebSocket. | -| `/notebook-intelligence/rules` | GET | List discovered rules. | -| `/notebook-intelligence/rules//toggle` | PUT | Toggle a rule's `active` field. | -| `/notebook-intelligence/rules/reload` | POST | Manually reload all rules. | -| `/notebook-intelligence/skills` | GET/POST | List or create skills. | -| `/notebook-intelligence/skills/context` | GET | Skill context info for the active workspace. | -| `/notebook-intelligence/skills/import/preview` | POST | Preview a GitHub-hosted skill before installing. | -| `/notebook-intelligence/skills/import` | POST | Install a GitHub-hosted skill (user-initiated). | -| `/notebook-intelligence/skills/reconcile` | POST | Run the managed-skills reconciler. Returns 409 if `NBI_SKILLS_MANIFEST` is unset. | -| `/notebook-intelligence/skills//` | GET/PUT/DELETE | Skill detail; managed skills are read-only. | -| `/notebook-intelligence/skills///rename` | POST | Rename a skill (denied for managed skills). | -| `/notebook-intelligence/skills///files` | GET/POST/DELETE | Skill bundle file ops. | -| `/notebook-intelligence/skills///files/rename` | POST | Rename a file inside a skill bundle. | -| `/notebook-intelligence/upload-file` | POST | Upload a file to attach as chat context. | -| `/notebook-intelligence/claude-sessions` | GET | List Claude Code sessions for the working directory. | -| `/notebook-intelligence/claude-sessions/resume` | POST | Resume a Claude session. | +| Route | Method | Purpose | +| ----------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- | +| `/notebook-intelligence/capabilities` | GET | Capabilities + tool/provider gate state. | +| `/notebook-intelligence/config` | GET/POST | Read or update user-scope config. | +| `/notebook-intelligence/update-provider-models` | POST | Refresh model list for a provider (e.g., Anthropic SDK refresh). | +| `/notebook-intelligence/mcp-config-file` | GET/POST | Read or write `~/.jupyter/nbi/mcp.json`. | +| `/notebook-intelligence/reload-mcp-servers` | POST | Re-discover MCP servers without restarting JupyterLab. | +| `/notebook-intelligence/emit-telemetry-event` | POST | Used by the frontend to emit `telemetry` events (e.g., chat feedback). | +| `/notebook-intelligence/gh-login-status` | GET | GitHub Copilot login state. | +| `/notebook-intelligence/gh-login` | POST | Begin GitHub Copilot device-flow login. | +| `/notebook-intelligence/gh-logout` | GET | Sign out of GitHub Copilot. | +| `/notebook-intelligence/copilot` | WS | Streaming chat / inline-completion WebSocket. | +| `/notebook-intelligence/rules` | GET | List discovered rules. | +| `/notebook-intelligence/rules//toggle` | PUT | Toggle a rule's `active` field. | +| `/notebook-intelligence/rules/reload` | POST | Manually reload all rules. | +| `/notebook-intelligence/skills` | GET/POST | List or create skills. | +| `/notebook-intelligence/skills/context` | GET | Skill context info for the active workspace. | +| `/notebook-intelligence/skills/import/preview` | POST | Preview a GitHub-hosted skill before installing. | +| `/notebook-intelligence/skills/import` | POST | Install a GitHub-hosted skill (user-initiated). | +| `/notebook-intelligence/skills/reconcile` | POST | Run the managed-skills reconciler. Returns 409 if `NBI_SKILLS_MANIFEST` is unset. | +| `/notebook-intelligence/skills//` | GET/PUT/DELETE | Skill detail; managed skills are read-only. | +| `/notebook-intelligence/skills///rename` | POST | Rename a skill (denied for managed skills). | +| `/notebook-intelligence/skills///files` | GET/POST/DELETE | Skill bundle file ops. | +| `/notebook-intelligence/skills///files/rename` | POST | Rename a file inside a skill bundle. | +| `/notebook-intelligence/upload-file` | POST | Upload a file to attach as chat context (size and retention governed by `upload_max_mb` / `upload_retention_hours`). | +| `/notebook-intelligence/claude-sessions` | GET | List Claude Code sessions for the working directory. | +| `/notebook-intelligence/claude-sessions/resume` | POST | Resume a Claude session. | +| `/notebook-intelligence/claude-mcp` | GET/POST | List or add Claude-mode MCP servers. Gated by `claude_mcp_management_policy`. | +| `/notebook-intelligence/claude-mcp//` | GET/DELETE | Get or remove a Claude-mode MCP server by scope (user/project/local) and name. | +| `/notebook-intelligence/plugins` | GET/POST | List or install Claude plugins. Gated by `claude_plugins_management_policy`. | +| `/notebook-intelligence/plugins//` | POST/DELETE | Enable or disable (POST with `{"action": "enable"\|"disable"}`) or uninstall (DELETE) a plugin. | +| `/notebook-intelligence/plugins/marketplace` | GET/POST | List or add plugin marketplaces. GitHub-sourced adds are gated by `allow_github_plugin_import`. | +| `/notebook-intelligence/plugins/marketplace/` | DELETE | Remove a plugin marketplace. | The extension respects `c.ServerApp.base_url`. Behind JupyterHub at `/user//` everything still works because JupyterLab proxies routes through the per-user base URL automatically. @@ -533,19 +570,23 @@ The extension respects `c.ServerApp.base_url`. Behind JupyterHub at `/user/