Changes
- **Single-channel notebook workflow** — Removed Chat/Notebook/Auto send targeting. Every chat now owns one frozen notebook context while all user commands and agent summaries remain in the center channel.
- **Read-only notebook projection** — The right Notebook panel is now a passive SSE-backed record with no Save, Save As, Undo, cell mutation, execution, restart, kernel selection, or context-pill controls.
- **Project-bound notebook lifecycle** — Chat notebook records continue to persist automatically under `{cwd}/.annodex/notebooks/` with lifecycle metadata. Archiving a chat releases runtime resources but leaves its notebook in place as an orphan project asset.
Fixes
- **Deleted notebook resurrection** — Externally deleted chat notebook files are reported as `missing` and are no longer silently recreated from stale process memory. A new record requires an explicit chat request.
- **Draft first-turn MCP targeting** — New chats re-sync notebook MCP environment after receiving the real thread id, preventing first-turn tools from writing to the draft notebook namespace.
Fixes
- **Memory promotion data race** — `markPromoted` in `memory-runtime.ts` now performs read-modify-write atomically inside `withFileMutationQueue`, preventing concurrent `retainMemory` calls from silently dropping records.
- **Tree builder cycle detection** — `buildTreeFromMessageEntries` in `session-reader.ts` now checks the entry parentId chain via `isAncestorInEntryChain`, catching cycles that `isDescendantInTree` misses when the parent node's children haven't been attached yet.
Changes
- **Port allocation TOCTOU** — `findFreePort` in `codex-server.ts` keeps the probe server alive until codex app-server is spawned, then closes the holder via `releasePort()`. Eliminates the race window where another process could grab the ephemeral port before codex binds.
- **Dead code removal** — Removed unused `replaceRecords` function from `memory-runtime.ts` (replaced by inlined write in `markPromoted`).
Changes
- **Jupyter UI clean-up** — Removed Jupyter-centric UI from `NotebookRuntimePopover` (replaced "Jupyter: not connected" with "Engine: Science Kernel"), `KernelSelector` (deleted Jupyter Server management section ~340 lines), `NotebookKernelPicker`, `NotebookPanel`, `useAgentSession`, and `context-pills`. All notebook execution uses Science Kernel (python3/Rscript directly); no Jupyter Server required.
- **Science Kernel idle timeout configurable** — `python-kernel.ts` and `r-kernel.ts` now read `notebookIdleTimeoutMs` from app settings instead of hardcoding 30 minutes. Default 2 hours. Settings → Kernels → Kernel Lifecycle provides dropdown (30m / 2h / 6h / 24h / 48h / Never).
- **Project kernel command resolution** — Science Kernel now uses the project-configured conda/venv kernel binary (`resolveEffectiveKernelCommand`) instead of always spawning PATH's `python3`. `getKernel()` chain accepts optional `command` parameter; switching project kernel auto-releases old process and spawns new one. Falls back to PATH detection when no project kernel is configured.
Fixes
- **Runtime popover empty state** — No longer shows "Jupyter server is not running" when kernel processes exist; says "No active kernel processes. Open a notebook and run a cell to start."
- **Settings Kernels tab** — Jupyter start/stop/scan/attach buttons removed (APIs were deleted in Phase 5). Replaced with Science Kernel configuration and idle timeout dropdown.
- **kernelCommands cleanup** — `release()` and `shutdownAll()` in both `PythonKernelManager` and `RKernelManager` now properly clean up the `kernelCommands` map.
Changes
- **Notebook double-SSE merged** — `NotebookInner` no longer opens its own `/api/science-kernel/events` SSE connection; kernel status, kernelStateReason, and executionCount flow via props from `NotebookPanel` → `useAgentNotebook` unified SSE.
- **Notebook sessionId single source of truth** — `NotebookPanel` now prefers `activeNotebookTarget.sessionId` from `AppShell`'s `buildActiveNotebookTarget`, eliminating duplicate sessionId computation across components.
- **Scroll-to-bottom rewrite** — staggered re-pin timers (80 / 200 / 450 / 800 / 1500 ms) + `ResizeObserver` (3 s deadline) replace the old rAF settle loop; `scrollToBottom` uses direct `el.scrollTop` assignment instead of `scrollIntoView`, bypassing `overflow-anchor` cross-browser inconsistencies.
- **Optimistic session delete** — delete fires `onDeleted` first (optimistic removal from the sidebar list), then calls the API; on failure `onDeleteFailed` triggers `loadSessionsForCwd` to restore the item.
Fixes
- **Cell output consistency** — local output cache in `NotebookInner` is now cleared when server-side outputs differ, preventing stale user-run outputs from overriding agent-updated outputs for the same cell. `setOutputs` is awaited before `onRefreshCells()` to avoid a race where the server hasn't persisted yet.
- **reloadFromFile concurrent guard** — added per-session `reloadInFlight` Set in the cells API route to prevent overlapping reloads from mount useEffect, the 2 s poll interval, and SSE `notebook_saved` events.
- **Kernel language-switch mutex** — added `withSessionLock` per-session promise-chain mutex to `lib/science-kernel/manager.ts`, serializing `getKernel` and `releaseKernel` calls to prevent Python ↔ R fast-switch kernel process leaks.
- **MCP env sync race** — `syncNotebookMcpEnv` is now awaited before the turn's prompt is sent, ensuring `ANNODEX_NOTEBOOK_SESSION_ID` is injected before the agent's first tool call.
- **loadSession deduplication** — added `loadSessionInflightRef` Map to prevent concurrent duplicate `readThread` requests for the same sessionId (e.g. initial load + SSE reconnect reconcile).
- **Scroll settle initialScrollDoneRef** — delayed `initialScrollDoneRef.current = true` to the 3 s settle deadline to prevent the agent-end smooth scroll effect from competing with staggered instant pins during session switch.
Changes
- **Notebook single data source** — `NotebookInner` no longer creates a duplicate `useAgentNotebook` hook; cells and refresh are owned by `NotebookPanel` and passed as props. Each notebook view now has one SSE connection and one poll loop (was two of each).
- **Immediate external notebook reload via SSE** — agent-signalled `.ipynb` saves (`POST /api/notebook/reload`) now reach the client through the existing SSE event stream (path-matched to file-mode sessions), triggering `reloadFromFile` without waiting for the 2s poll interval. Own saves carry a sessionId and are ignored on the client.
Fixes
- Fixed `eventMatchesSession` dropping session-less `notebook_saved` events — the events route now matches by the file path encoded in the session id.
- Fixed missing `cwd` in the SSE effect dependency array (`useAgentNotebook`), eliminating a React hooks warning.
Fixes
- Fixed npm publish failure — reverted `--provenance` from the publish workflow (sigstore provenance requires a public repository; v0.1.129 code identical, republishing).
Changes
- **Science kernel replaces Thebe** — notebook execution now runs on annodex's own persistent Python/R kernel processes (`lib/science-kernel/`) with per-session serialization; the thebe-core/thebe-react frontend, Jupyter server management, and the `/api/kernel/*` proxy routes are removed. New `components/notebook/` cell UI edits/executes directly through the cells + science-kernel APIs.
- **Notebook file-sync hydration tracker** — per-session file mtime tracking distinguishes own writes from external edits: external `.ipynb` changes (editor, git, agent file tools) are picked up on next API call, own exports no longer trigger redundant re-imports, and deleted files are recreated.
- **Notebook security hardening** — `assertNotebookFileAccess` + cwd resolution (explicit → draft session → registry). Read APIs without cwd only serve `.ipynb` (blocks secret exfiltration via context views); write APIs additionally require a scope anchor; context apply enforces write scope; registry openFile/saveFile/export share the same realpath-safe checks.
- **Kernel lifecycle** — language switches release the previous kernel (no more leaked python processes); `isKernelReady`/`getKernelStatus` report real process state; deleting a notebook cascades to snapshots, kernel leases, and the live kernel.
- **Snapshots** — dedup consecutive identical snapshots, skip >8MB notebooks, no more snapshots on failed validations, per-notebook purge on delete.
- **Draft scratch persistence** — draft chats (no thread yet) persist scratch cells to `.annodex/notebooks/scratch-draft-<hash>.ipynb` and survive server restarts; draft→chat migration writes the chat file before removing the draft file.
- **Bounded runtime state** — async execute jobs (100 + 10min TTL), reload tokens (200 FIFO + 1h TTL), hydration tracker (500 FIFO).
- **Lazy notebook instructions** — the ~150-line notebook API block is only injected into chats whose project shows notebook activity.
- **Tests** — new `tests/notebook-core.test.mjs` (session ids, hydration, memory, state, snapshots, registry, security) via `npm run test:notebook`; shared `tests/compile-lib.mjs` harness fixes tsc alias/js-sibling compilation for codex-server/rpc-manager/session-reader/codex-session/stream-session suites — full suite is green again (76/76).
Fixes
- **Fixed notebook Save / Save As wiping the entire notebook** — the client source handle was never wired after the UI rewrite, so saves pushed an empty cell list and overwrote the project `.ipynb`. Saving now exports the server-side session (the true source); the cells API also refuses to replace a non-empty notebook with an empty sync unless `force: true`.
- Fixed file-change reload never firing — reload token lived in the server process while the panel polled a browser-local map; panel now polls `GET /api/notebook/reload`.
- Fixed UI cell execution hardcoding Python — the notebook language preference now reaches the execute call.
- Fixed cell reorder dropping outputs/execution counts — new atomic `move` cells action replaces delete+add.
- Fixed `execution_count` never persisted to cells or exported `.ipynb` files.
- Fixed memory recall returning results when no query term matched; execution memories are now retained under the real project cwd.
- Fixed notebook session ids not round-tripping paths containing `~`.
- Fixed registry saveFile resetting R notebook kernelspec to python3 — existing kernelspec/language_info metadata is preserved on save.
- Fixed cells/registry GET creating sessions and default notebooks as read side effects.
- Fixed chat target block falling back to a shared global `default` notebook session.
- Fixed execute route not hydrating sessions after a server restart (runFromIndex saw zero cells).
- Fixed HMR duplicate notebook sidecar subscriptions causing duplicate trajectory/memory writes.
Changes
- **Composer memory** — new chats inherit the last-used model and thinking level from the previous session (persisted in browser localStorage).
Fixes
- Fixed Save As rejecting paths on symlinked project cwd (e.g. `/Volumes/...` vs `/work/...` mount aliases).
- Fixed notebook Save dialog browse listing no subfolders — API returns `isDir`, not `kind: directory`.
- Fixed user messages showing the raw `[annodex target: notebook]…[/annodex target]` block — UI now shows clean prompt text with a `to notebook` pill.
- Fixed new chat defaulting to notebook send target instead of chat.
- Fixed notebook Save As filename normalization when the input includes a path segment.
Fixes
- Fixed npm publish CI build failure — split client-safe `notebook-scratch-path` and `extension-ids` modules so notebook context pills no longer pull Node `fs` into the browser bundle.
Changes
- **Notebook context layer** — agent-view, projection apply, TOC/cell query, dependency analyzer, mutation preview cards in chat, cross-session memory, snapshot/undo, trajectory export; `annodex-notebook-mcp` wrapper.
- **Scratch & file true-source** — `.ipynb` on disk is primary store for both modes; runtime JSON disabled; hydrate on API entry; execute/setOutputs sync outputs to file.
- **`kernel_state` SSE** — idle/busy/dead events from execute + Thebe; UI banners, context pills, and agent prompt injection for error recovery (§7).
- **Save / Save As** — notebook save flows use cells API + `syncToFile` (preserves agent outputs and kernelspec metadata).
Fixes
- Fixed Save As writing Thebe-only sources via raw file PUT — now exports full runtime session to `.ipynb`.
- Fixed cells API accepting `filePath` outside project cwd — `assertNotebookProjectScope` on GET/POST.
- Notebook execute server-side `python3 -c` fallback remains off by default (`ANNODEX_NOTEBOOK_EXECUTE_FALLBACK`).
Changes
- **Notebook offline view** — scratch/file notebook shows agent cells with a Jupyter-offline banner when no server is connected, instead of a full-screen blocker.
Fixes
- Fixed thinking dropdown showing `high (minimal)` / `high (medium)` for DeepSeek — inferred API routing maps no longer override UI labels; only custom `thinkingLevelMap` in providers.json changes display text.
- Fixed Settings → Kernels tab not scrolling — Jupyter server, memory, and kernel list now scroll together when content exceeds the dialog height.
- Fixed context overflow errors showing raw upstream JSON — readable message with `/compact` guidance.
- Fixed codex context budgeting using the largest model window across providers — workspace server now uses the smallest configured `contextWindow` so auto-compaction triggers before any model's limit.
Changes
- **Composer thinking (v0.1.116 style)** — desktop restores separate thinking dropdown; model picker is models-only with clearer selected state and provider grouping.
- **DeepSeek thinking levels** — infer `thinkingLevelMap` from compat/reasoning profile; `deepseek-v4-pro` caps at `high` (no `xhigh`).
Fixes
- Fixed notebook/agent `401 Invalid token` on local router for deeprouter and other compat-proxy providers — annodex router now routes them through Responses→Chat compat instead of native `/v1/responses`.
- Fixed missing API key returning opaque upstream 401 — router now reports which provider needs a key in Settings → Models.
- Fixed codex server reusing stale router after provider API key changes.
- Fixed notebook agent instructions hardcoding `localhost:30121` — use dynamic annodex origin/port.
Fixes
- Fixed annodex startup crash `TypeError: WebSocket.Server is not a constructor` — use `WebSocketServer` from `ws` v8 ESM exports in Jupyter WebSocket proxy.
Fixes
- Fixed npm publish build failure: split client-safe `jupyter-browser-url` from server-only `jupyter-upstream` so NotebookPanel no longer bundles Node modules.
Changes
- **Jupyter browser proxy** — notebook thebe connects via same-origin `/api/kernel/jupyter` (HTTP + WebSocket) instead of `127.0.0.1:8900`, fixing remote/mobile LAN access.
- **Custom annodex server** — `server/annodex-next-server.mjs` wraps Next.js with Jupyter WebSocket upgrade proxy; `npm run dev` and production `annodex` use it.
Fixes
- Fixed right-panel notebook **Server: not reachable (127.0.0.1:8900) / Failed to fetch** when annodex is opened from another device or non-localhost URL.
- Fixed model/thinking display to match v0.1.116 semantics — model name and reasoning level shown separately; custom `thinkingLevelMap` labels only when configured.
Changes
- **Notebook chat targeting** — chat notebook target follows persisted `notebookSelection`, independent of which right-panel tab is focused; opening `.ipynb` registers the file notebook and updates the target.
- **Draft notebook migration** — scratch/file notebook cells in draft scope migrate to the real thread session on first message (`POST /api/notebook/migrate-draft`).
- **Notebook runtime UX** — top bar **Runtime** popover merges server status, kernel picker entry, and memory; notebook panel uses a unified kernel picker modal.
- **Notebook kernels** — auto-pin effective Py/R kernels when selecting notebook send target; notebook language preference per project.
- **Mobile composer** — model/thinking bottom sheets portal to `document.body`; **More (⋯)** menu splits **Model** and **Thinking** entry points.
Fixes
- Fixed thebe-core load failure in notebook panel (`ThebeCoreShim` for `window.thebeCore.module` mismatch).
- Fixed notebook chat target falling back to scratch while the Files tab was focused.
- Fixed mobile model/thinking sheets mispositioned or untappable under sticky `backdrop-filter` composer chrome.
- Fixed notebook targeting when Jupyter server is not yet connected (agent can still write cells).
- Removed top-bar Generate UI toggle (generative UI stays on by default).
Changes
- **Cursor-style composer** — unified `+` menu (plan mode, tools, extensions, slash commands), integrated model/thinking picker, streaming stop button, drag/paste attachments with vision-model guard.
- **Chat send target** — Send to Chat / Notebook / Auto via `+` menu; segmented control appears when Notebook tab is open; status chip when routing away from Chat.
- **Session sidebar** — pin chats to a strip above Projects; unread blue dot and running indicator; New Project button in Projects header.
- **Notebook memory manager** — top-bar Notebook button opens global kernel memory popover (RSS by chat/project, Release, idle timeout); kernel lease tracking for thebe + agent execute APIs.
- **Plan mode** — plan/code toggle in composer; plan panel integration; can switch back to code while agent is running.
Fixes
- Fixed notebook kernel leases not updating when a draft/unattributed kernel is later claimed by a chat session.
- Fixed plan mode toggle blocked entirely during agent runs (only switching into plan is blocked now).
- Fixed historical pasted images not opening in lightbox; block send when model lacks image input.
Fixes
- Fixed scratch notebook remounting on every agent cell sync — stable key prevents Thebe session/kernel teardown.
- Fixed `findOrphanJupyterPids` doing 100 sequential `lsof` calls; now single `lsof` with port-range filter.
- Fixed `@types/ws` fragility by adding `declare module "ws"` to types file (prevents breakage on `npm install`).
Changes
- **KernelSelector: three-state server UI** — Disconnected / Managed / External mode cards with port + label display, replacing single-status indicator.
- **External Jupyter validation** — connect flow validates tokens, shows auth-required hints, separates port scan from external probe (`lib/jupyter-external-probe.ts`).
- **Notebook save** — Save / Save As buttons in notebook toolbar; new `NotebookSaveDialog` with folder browser, kernel picker, and auto-increment filenames (`lib/notebook-ipynb.ts`, `lib/notebook-save-paths.ts`).
- **Scratch notebook ↔ agent sync** — bidirectional source sync via `useScratchNotebookSync`, fingerprint-based dedup to avoid loops (`lib/notebook-scratch-sync.ts`).
- **Project kernel settings** — Default Python/R kernel selector per project in Settings → Project, with effective/default/project-selected labels.
- **`replaceNotebookCells` API** — `POST /api/notebook/cells { action: "sync" }` for bulk cell replacement.
- **Jupyter scan extras** — `/api/kernel/scan` accepts `extraPorts` / `probeToken` for user-specified ports.
Fixes
- Fixed `no-store` cache header on `/api/files/[...path]?type=list` responses.
- Fixed `findOrphanJupyterPids` to only scan annodex-managed ports (8900–8999), never touching external Jupyter on other ports.
- Fixed `replaceNotebookCells` duplicate function definition causing TS compile failure.
- Fixed all pre-existing TypeScript errors: missing `@types/react` / `@types/react-dom` / `@types/ws` installs via `overrides`; added `types/missing-modules.d.ts` for packages without type definitions.
- Fixed `docx-preview` renderAsync call signature (4 args → 3 args).
Changes
- **Interactive & Agent Output merged** — removed Agent Output tab; agent now edits .ipynb files directly via file tools, thebe reloads on change via POST /api/notebook/reload. Single unified notebook experience.
- **Notebook create card** — when no .ipynb is selected, panel shows a creation UI with folder browser (navigate subdirs, ../ up, pick name) instead of typing paths.
- **External server probe** — `/api/kernel/config` now probes external Jupyter servers and returns `reachable`/`probeError`; NotebookPanel and Settings only render thebe when reachable.
- **Server status in Settings** — external servers now show "Not reachable" with probe error when port is configured but unreachable (e.g. missing token).
Fixes
- Fixed Settings → Kernels showing "Running" for external servers that were configured but unreachable.
Fixes
- Fixed CI typecheck failure: `ScanResponse` to `Record<string, unknown>` cast (added `unknown` intermediate).
Changes
- **Jupyter server management moved to Settings → Kernels** — start/stop/scan for Jupyter servers from the Settings dialog. Right-panel notebook no longer shows kernel setup UI; instead shows a guide button when no server is connected.
- **Server config persistence** — Jupyter server connection (managed or external) saved to `~/.config/annodex/kernel-server.json`, survives restarts.
- **Port scanning** — Settings → Kernels can scan local ports (8888–9000) for running Jupyter servers and connect to them with one click.
- **Science kernel → Jupyter registration** — selecting a Python/R kernel for a project now auto-registers it as a Jupyter kernel spec (`ipykernel install` / `IRkernel::installspec`), so the notebook server can use the selected environment.
- **Notebook execute uses project kernel** — `/api/notebook/execute` now looks up the project's selected Python/R kernel instead of hardcoding `python3`. Falls back to default when no project kernel is selected.
- **Notebook panel shows active kernel** — status bar displays project-selected kernel (e.g. `K: Python 3.10 (conda:bio)`).
- **Right-panel file tab scoping** — notebook/file tabs are filtered when switching projects; tabs from other projects are auto-closed.
Fixes
- Fixed R kernel registration single-quote escaping (was a no-op).
- Fixed external Jupyter servers not being discoverable by the execute API (added `getServerConfig()` fallback).
- Removed dead `buildNotebookPrompt` function (replaced by inline cwd injection in rpc-manager).
Fixes
- Fixed `TypeError: Failed to fetch` when opening chat sessions — the FileExplorer's `useEffect` had `store` (entire object, new reference every render) in its dependency array, causing an infinite loop of file-list requests that exhausted browser connections. Changed to `store.refresh` (stable `useCallback` reference).
Fixes
- Fixed old chat sessions failing with `TypeError: Failed to fetch` after upgrading to v0.1.108 — the auth middleware was blocking localhost API requests (`/api/sessions/:id`, etc.) when `web-auth.json` is enabled, but localhost access should never require authentication. Removed `isLocalSidecarApiPath` restriction; all localhost requests now bypass auth (password is only for remote tunnel access).
Fixes
- Fixed old chat sessions returning empty messages — tree building failure from malformed parent references no longer prevents messages from loading. `buildTreeFromMessageEntries` is now wrapped in `safeBuildTree` try-catch, falling back to empty tree on error.
Fixes
- Fixed old chat sessions showing "Failed to fetch" — old Codex sessions (pre ~2026-05) lack item-level `id` fields, causing circular references in the session tree that broke JSON serialization. Synthetic IDs are now generated for items without real IDs, and cycle detection guards the tree builder.
Changes
- **Projects tab redesigned** — split layout (left project list + right detail panel), drag-to-reorder projects with persistence, "current" badge, hover-to-show-remove.
- **Remote tunnel UI** — new **Settings → Remote** tab with start/stop/copy URL controls.
- **Auth middleware activated** — password auth now enforced for all non-localhost requests (existing proxy.ts promoted to middleware.ts).
- **Default sandbox mode** — changed to Full access ("danger-full-access"), Safe/Full toggle removed from sidebar.
- **Docs updated** — file tree features (context menu, shortcuts, search), project tab, auth middleware, tunnel UI documented in both languages.
Changes
- **Tunnel simplified** — removed JWT/session/token complexity, tunnel is now just a network bridge (start/stop cloudflared). Authentication handled by annodex's existing password system via the activated middleware.
- **Auth middleware activated** — `proxy.ts` renamed to `middleware.ts`, enabling the existing `annodex-auth` cookie-based authentication for all external requests.
Fixes
- Fixed JWT double-encoding bug in tunnel token verification.
- Fixed cloudflared `require()` usage replaced with ESM `import`.
- Fixed tunnel stop race condition (state nulled before process kill).
Changes
- **Remote access via Cloudflare tunnels** — `POST /api/tunnel/start` starts a `cloudflared` quick tunnel exposing the local annodex server. Returns a share URL with an embedded JWT for authenticated access. JWT verification via Next.js middleware (Edge-compatible WebCrypto) protects all routes when a tunnel is active. Includes tunnel status, session management, token revocation, and a dedicated login page.
- **Usage docs updated** — Remote access (Tunnel) section added to both Chinese and English usage guides with setup instructions, API reference, and security notes.
Changes
- **Fuzzy file search API** — `GET /api/file-tree/search` endpoint with BFS traversal and fuzzy match scoring (exact/prefix/substring/character-by-character).
- **Button reorder** — Settings and permission toggle buttons swapped in sidebar footer.
Changes
- **File tree redesign** — in-memory caching with O(1) path lookups, manual virtual scrolling (only renders visible rows), right-click context menu (rename / delete / copy path / reveal), keyboard shortcuts (↑↓→← Enter F2 Delete), dimmed secondary directories (.git / node_modules / etc), and file operations (New File / New Folder / Rename / Delete / Reveal in Finder).
- **File-tree API module** — new `/api/file-tree/*` endpoints (write / mkdir / rename / delete / reveal) with atomic writes (tmp+rename), workspace boundary enforcement, and cross-platform reveal support.
Changes
- **Chat-owned notebook registry** — notebook state is now scoped to chat threads instead of project-wide `scratch:{cwd}`, with active notebook selection, multi-notebook support per chat, and copy-on-fork behavior.
- **Agent notebook output panel** — Notebook panel adds an Agent Output view that renders cells produced through the notebook API, including stream output, errors, HTML/text, and PNG/JPEG/SVG display data.
- **Notebook persistence and export** — Annodex scratch/agent notebooks persist runtime cells under `~/.config/annodex/notebooks/runtime-sessions/`, registry metadata under `~/.config/annodex/notebooks/index.json`, and can be exported to project `.ipynb` files.
- **File-backed `.ipynb` bridge** — project `.ipynb` files register as file-backed notebooks, import into runtime cells for agent execution/editing, and can be explicitly saved back to their project path.
- **Permission toggle wiring** — the left-bottom Safe/Full permission toggle now controls new sessions, existing sessions, and resumed sessions via `defaultSandboxMode`.
Fixes
- **Notebook execution robustness** — notebook execution supports async jobs with polling, longer kernel timeouts, and `sourceFile` cell creation/update to avoid fragile shell JSON escaping.
- **Notebook ownership isolation** — different chat sessions in the same project no longer share or overwrite each other's notebook cells.
- **Project asset boundary** — `.ipynb` files are never stored under `~/.config/annodex`; that directory only contains Annodex registry metadata and runtime cache.
Fixes
- **自动清理僵尸 Jupyter 进程** — `startJupyterServer()` 启动前用 `lsof` 扫描并 kill 所有孤儿 `jupyter-server` 进程,防止 annodex 重启后端口被旧进程占用累积
- **Notebook 面板错误可见性** — 顶部状态栏直接显示 kernel 错误信息(不再仅藏于初始设置面板中),新增 "↻ Retry" 按钮用于重新检查状态
- **删除 ThebeNotebookInner 死代码** — 移除 `dangerouslySetInnerHTML` 注入的 `<script>` 标签(React 中不执行),thebe-core 已自行处理 `window.thebeCore.module` 包装
- **NotebookStatusBar 错误信息增强** — 新增 `loader.error` 显示,错误文本更宽、字号更大、可 hover 查看全文
Changes
- **Notebook target routing** — ChatInput 新增 Chat/Notebook/Auto 三态目标切换按钮,消息按目标路由:
- **Notebook tab 常驻右侧面板** — 右侧面板新增独立 Notebook tab,支持 scratch notebook 和 `.ipynb` 文件两种模式,通过 dropdown 切换项目目录及其子目录下的所有 notebook 文件
- **新建按钮文案** — 左侧 channel 右上角 `+New` 改为 `+Chat`
Fixes
- **修复 thebe-core.min.js 被认证中间件拦截** — `proxy.ts` 添加 `STATIC_ASSET_RE` 正则,放行 `public/` 目录下静态资源(`.js/.css/.woff2/.wasm` 等),notebook 引擎不再因 307 重定向到 `/login` 而加载失败
Fixes
- **Fix CI: sync package-lock.json after version bump** — `npm ci` failed in both
Changes
- **annodex science — interactive notebook** — new notebook system with Jupyter kernel
Fixes
- **Login page password-manager autofill** — password input switched from React
- **Legacy cookie cleanup** — `getClearLegacyCookieHeader()` and
Changes
- **Cross-platform process manager** — new `lib/process-manager.ts` module unifies
- **Zombie process detection on Linux** — `isZombieProcess()` reads `/proc/<pid>/status`
- **Escape hatch** — `ANNODEX_CODEX_DETACHED=0` disables detached mode on POSIX,
Fixes
- Codex app-server shutdown in `stopInstance()` now waits for the process to exit
- Windows `taskkill` subprocesses are `.unref()`'d so a hung taskkill cannot
Fixes
- **Session rename robustness** — five improvements: (1) wasted wrapper cleaned up after connection errors so follow-up operations don't fail on a dead connection; (2) empty name now rejected with 400 instead of sent to codex; (3) `CodexSessionWrapper.send("rename")` now syncs to `session-runtime.json` so direct calls (not through HTTP PATCH) also persist; (4) AppShell and SessionSidebar now share a single `renameSession()` helper in `agent-client.ts` eliminating duplicated fetch/error-handling logic.
Fixes
- **Session rename process cleanup** — session rename now first uses the live session connection and only falls back to a managed codex server when needed, avoiding unnecessary codex app-server/router process churn. Codex app-server shutdown now includes a SIGKILL fallback for detached Linux processes, reducing orphaned background processes after idle cleanup or dev reloads.
Fixes
- **Session rename not working** — three root causes fixed: (1) frontend `commitRename` didn't check HTTP response status, silently proceeding after server errors; (2) session name had no local persistence, so it was lost when `thread/list` didn't return the `name` field or when falling back to local cache; (3) top bar and sidebar rename were completely independent — renaming from the sidebar never updated the top bar's `selectedSession`. Fixes: `commitRename` now checks `res.ok` and shows error toasts; PATCH endpoint persists name to `session-runtime.json` via `upsertSessionRuntime`; `sessionInfoFromThreadSummary` and `summarizeCodexSessionFile` recover name from runtime store; new `onSessionRenamed` callback syncs sidebar rename to AppShell's top bar.
Fixes
- **Stop button triggers HTTP 500 during streaming** — clicking Stop while the AI was responding could cause a 500 error due to a race condition between SSE cleanup and the abort POST handler. The abort case in `CodexSessionWrapper.send()` and the `POST /api/agent/[id]` route now both wrap their logic in try/catch, ensuring any exception is safely caught and a clean `{ running: false }` response is always returned.
- **"Stream idle timeout" after AI finishes** — when the SSE connection closed after turn completion and the client auto-reconnected, the new SSE stream would receive no events (turn already done), causing the idle watchdog to fire after 3 minutes. The SSE `events/route.ts` now checks `session.isBusy()` after subscribing; if the session has no active turn, it immediately sends `agent_end` so the watchdog never starts.
Fixes
- **Stuck chat after agent completion** — when the SSE `agent_end` event was missed (due to network disconnect or timing), the UI would stay stuck showing "Stop" button and appear to be streaming, even though the agent had actually finished. Root cause: the `reconcileSession` polling loop fetched the session state every 10s but never updated `agentRunning` based on the server response. Now `reconcileSession` checks `agentState.running` after each reconcile and self-heals the UI if the agent has actually stopped, preventing the stuck state without requiring a page refresh.
- **Reduce left sidebar polling interval** — changed from 10 seconds to 1.5 minutes (90s) to reduce unnecessary network traffic.
Changes
- **Rename right-panel tab "Tools" → "Status"** — the right-panel tab previously labeled "Tools" primarily shows agent runtime status (phase, uptime, active tools, model info) and working memory, not tool presets. The rename aligns the tab label with its actual content. A new pulse/activity SVG icon is used for the Status tab.
Fixes
- **Defensive error handling in runtime status** — `getRuntimeStatus()` now wraps each session's `isAlive()` and `getRuntimeStatus()` calls in try/catch blocks. Previously, a partially destroyed session wrapper could cause the entire status query to throw, resulting in an empty Tools panel.
- **Runtime API route hardening** — `GET /api/agent/runtime` now catches errors from `getRuntimeStatus()` and `getMemoryStatus()` independently, falling back to an empty status instead of returning a 500 error. This prevents transient state issues from breaking the frontend Status panel.
Fixes
- **Sidebar refresh delay** — new chat sessions now appear in the sidebar within seconds. Two changes: (1) `AppShell.handleSessionCreated` dispatches a `session-created` DOM event, and (2) `SessionSidebar` listens for that event and polls every 10s as a fallback (pattern adopted from CodePilot). Previously the sidebar relied solely on `refreshKey` prop propagation, which could lag behind the Codex app-server thread index.
- **Scroll-to-bottom on session open (v2)** — the settle loop now uses a `lastSettledSessionIdRef` to reliably detect session switches, preventing the settle from being skipped when messages arrive in batches across renders. Also aborts the animation if the session changes mid-settle.