ulysses/static/js
Kenny Van de Maele 8ce945d338
feat: Add plan mode to the chat agent (#638)
* feat: Add plan mode to the chat agent

Adds a plan mode: the agent investigates read-only, proposes a checklist, and
waits for approval before changing anything. On approval it runs with full
tools and checks items off as it goes. Enforcement reuses the existing
disabled_tools gate.

Includes a slash command: `/plan [on|off]` (and `/toggle plan`) to flip the
plan toggle from the chat input.

- src/tool_security.py, src/mcp_manager.py: read-only allowlist (tools + MCP).
- src/agent_loop.py, routes/chat_routes.py: union the disabled set, prepend the
  plan directive, force agent mode.
- static/: plan toggle pill, Approve & Run, dockable plan window, task-list
  checkboxes, and the /plan slash command.
- tests/test_plan_mode.py.

* Plan mode: persistent re-referenceable plan + agent write-back

Three improvements so a long plan survives a weak model and stays in reach:

1. Re-reference the plan (out-of-context fix). On the execution turn the frontend
   sends the approved checklist back (`approved_plan`); the backend pins it as a
   top-of-context `## ACTIVE PLAN` system note (kept by the context trimmer), so
   the agent can always re-read the plan instead of losing the thread on a long
   run. New `build_active_plan_note()` (unit-tested).

2. Re-open / dock the plan anytime. The plan checklist is stored per-session
   (localStorage). When a plan exists, the plan-mode button opens a small menu
   ("Show plan" / "Plan mode: On/Off") that re-opens the side-dockable plan
   window — so it can stay docked while the agent works. The window live-refreshes
   as the plan changes.

3. Agent write-back: new `update_plan` tool. The agent calls it to tick steps
   `- [x]` after finishing them, or to revise steps when the user asks. Marker
   tool (no I/O) → `plan_update` SSE event → the stored plan + docked window
   update live. The ACTIVE PLAN note instructs the agent to use it.

Backend: src/agent_loop.py (param + pin + note builder + emit + prompt blurb),
src/tool_execution.py (update_plan handler), routes/chat_routes.py (parse
`approved_plan`, relay `plan_update`), registration in tool_schemas / agent_tools
/ tool_index (always-available, not admin-gated).
Frontend: static/js/chat.js (plan store, send `approved_plan`, handle
`plan_update`, capture restated checklists), static/app.js (plan-button menu),
static/js/planWindow.js (`isPlanWindowOpen`), static/js/storage.js (PLAN key).
Tests: tests/test_plan_mode.py (plan-note), tests/test_update_plan_tool.py.

* Plan mode: drop bash/python, rely on read-only discovery tools

Shell can mutate (write files, hit the network) and can't be constrained to
read-only at the tool layer, so plan mode no longer relies on a prompt to keep
it well-behaved — bash/python are removed from the read-only allowlist and added
to the fail-closed block set. Discovery is covered by the dedicated read-only
tools (read_file, grep, glob, ls) instead.

Rewrites the plan-mode directive to state shell is disabled and lists the
available read-only tools positively. Addresses review feedback on #638.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Comment: note _MCP_READONLY_VERBS are prefixes not whole words

Clarifies that entries like "summar" are intentional stems matched via
startswith (covers summarise/summarize/summary), not typos. Addresses review
feedback on #638.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Plan mode: clarify why gating inverts the allowlist into a denylist

Rename _PLAN_MODE_FALLBACK_BLOCK -> _PLAN_MODE_KNOWN_MUTATORS and rewrite the
comments. The tool gate is a denylist (disabled_tools); plan mode's policy is an
allowlist, so it returns the inverse (all known tool names minus the allowlist).
The static mutator set is a backstop for the schema-derived name list, which
misses XML-only tools and can fail to import. Addresses review feedback on #638.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Plan mode: stop hardcoding the read-only tool list in the directive

The model is already shown its available (read-only) tools by _assemble_prompt,
which removes every disabled tool. Enumerating them again in the directive only
duplicated that list and would drift as tools change. Point at the tools listed
below instead. Addresses review feedback on #638.
2026-06-05 16:32:25 +02:00
..
calendar Ignore non-string calendar date inputs (#1649) 2026-06-03 14:16:58 +09:00
color fix: theme color parsing breaks on #rgb shorthand hex (#1213) 2026-06-03 00:30:03 +09:00
compare Isolate HTML popup openers (#2501) 2026-06-04 20:52:41 +02:00
editor fix: computeSnap throws when ctx.otherLayers is not an array (#1716) 2026-06-03 13:34:25 +09:00
emailLibrary Harden email HTML URL sanitization (#2496) 2026-06-04 20:47:47 +02:00
markdown Ignore non-string markdown table rows (#1648) 2026-06-03 14:17:02 +09:00
model fix: model cost/info matches first substring key (gpt-4o-mini billed as gpt-4o) (#1439) 2026-06-04 03:05:37 +01:00
research Whitelist research source links (#2499) 2026-06-04 20:41:35 +02:00
util fix: monthly schedule label shows 21th/22th/31th (ordinal suffix for days >20) (#1577) 2026-06-03 08:57:47 +09:00
a11y.js Improve accessibility across core flows (#86) 2026-06-01 22:04:00 +02:00
admin.js Settings polish: /setup provider subs, Add API defaults to api kind, picker shows offline endpoints, doc library tracks sub-tab 2026-06-05 14:41:54 +09:00
assistant.js Rename Character copy to Persona 2026-06-02 12:42:15 +09:00
calendar.js Calendar: cross-session delete sync — 404 = success, refetch on tab focus 2026-06-05 17:05:04 +09:00
censor.js Ignore censor preference storage errors (#1652) 2026-06-03 14:16:55 +09:00
chat.js feat: Add plan mode to the chat agent (#638) 2026-06-05 16:32:25 +02:00
chatRenderer.js fix: prevent document link click from resetting active session (#2055) 2026-06-05 15:05:30 +02:00
chatStream.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
codeRunner.js Fix/windows llama cpp serve and test upstream (#2669) 2026-06-05 14:53:33 +02:00
colorPicker.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
cookbook-diagnosis.js Fix Windows Cookbook background tasks, exit statuses, and empty SSH logs wrapper (#1389) 2026-06-05 14:41:07 +02:00
cookbook-hwfit.js Fix/windows llama cpp serve and test upstream (#2669) 2026-06-05 14:53:33 +02:00
cookbook.js Fix Windows Cookbook background tasks, exit statuses, and empty SSH logs wrapper (#1389) 2026-06-05 14:41:07 +02:00
cookbookDownload.js Cookbook polish: auto-reconnect, ctx slider fixes, scoring, lots of UI 2026-06-03 20:25:25 +09:00
cookbookProgressSignal.js Don't falsely declare a dependency build stale (#1568) (#1768) 2026-06-03 13:23:35 +09:00
cookbookRunning.js Fix/windows llama cpp serve and test upstream (#2669) 2026-06-05 14:53:33 +02:00
cookbookSchedule.js Cookbook scheduler + serve: schedule via Tasks, Stop verifies kill, Ollama auto port-pick 2026-06-05 14:41:43 +09:00
cookbookServe.js Fix/windows llama cpp serve and test upstream (#2669) 2026-06-05 14:53:33 +02:00
document.js fix: prevent document link click from resetting active session (#2055) 2026-06-05 15:05:30 +02:00
documentLibrary.js Settings polish: /setup provider subs, Add API defaults to api kind, picker shows offline endpoints, doc library tracks sub-tab 2026-06-05 14:41:54 +09:00
dragSort.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
emailInbox.js fix: reply-all Cc's the user's own other addresses (multi-account) (#672) 2026-06-02 11:42:20 +09:00
emailLibrary.js fix: guard sp.destroy() in _loadScheduled against null spinner (#1495) 2026-06-03 08:12:47 +09:00
emojiPicker.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
emojiShortcodes.js Render emoji shortcodes as icons in chat (#345) (#629) 2026-06-05 02:28:42 +02:00
escMenuStack.js fix: make transient dropdown/popup menus close on Escape 2026-06-01 14:23:22 -04:00
fileHandler.js feat(ui): allow expanding consolidated file chip regardless of count (#1849) (#2086) 2026-06-04 14:02:52 +01:00
gallery.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
galleryEditor.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
group.js Harden chat streaming DOM sinks (#2498) 2026-06-04 20:49:37 +02:00
init.js fix(ui): keep minimized windows above composer (#1197) 2026-06-02 23:31:09 +09:00
keyboard-shortcuts.js Ignore AltGr keystrokes in Ctrl+Alt keyboard shortcuts (#825) 2026-06-02 11:12:54 +09:00
langIcons.js fix: langIcon throws on an explicit null opts argument (#1740) 2026-06-03 13:29:21 +09:00
markdown.js feat: Add plan mode to the chat agent (#638) 2026-06-05 16:32:25 +02:00
memory.js fix(memory): let manual add specify memory category 2026-06-05 13:17:14 +07:00
modalManager.js feat: Claude Agent integration + cookbook reconnect + UI polish 2026-06-04 08:27:26 +09:00
modalSnap.js Polish email and cookbook flows 2026-06-02 22:42:07 +09:00
modelPicker.js Settings polish: /setup provider subs, Add API defaults to api kind, picker shows offline endpoints, doc library tracks sub-tab 2026-06-05 14:41:54 +09:00
models.js fix(models): stabilize proxy endpoint refresh behavior 2026-06-04 04:56:11 +01:00
modelSort.js Ignore invalid model sort inputs (#1653) 2026-06-03 14:16:52 +09:00
MODULE_SUMMARY.md docs: fix stale documentation references (#1769) 2026-06-03 13:23:21 +09:00
notes.js fix(notes): track + remove the select-mode Esc keydown listener so it doesn't leak per open (#2792) 2026-06-05 16:25:05 +02:00
package.json Fix test suite: ESM module loading and stub isolation (#844) 2026-06-02 11:29:29 +09:00
planWindow.js feat: Add plan mode to the chat agent (#638) 2026-06-05 16:32:25 +02:00
platform.js Ignore AltGr keystrokes in Ctrl+Alt keyboard shortcuts (#825) 2026-06-02 11:12:54 +09:00
presets.js Keep presets loading with bad local state (#1417) 2026-06-03 04:09:28 +09:00
providers.js Show the serving provider in the model-info card (#2185) 2026-06-04 18:22:31 +02:00
rag.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
researchSynapse.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
search-chat.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
search.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
section-management.js Fix Models section collapse dead pause and missing animation 2026-06-01 16:53:46 +02:00
sessions.js fix: prevent document link click from resetting active session (#2055) 2026-06-05 15:05:30 +02:00
settings.js fix: Settings now correctly displays CalDAV integrations when more than one isconfigured (#2901) 2026-06-05 16:11:08 +02:00
sidebar-layout.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
signature.js Constrain signature uploads to PNG data (#2844) 2026-06-05 13:17:43 +02:00
skills.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
slashAutocomplete.js Cookbook: scoring fixes, UI polish, false-finished + stale-state bug fixes 2026-06-03 16:32:20 +09:00
slashCommands.js feat: Add plan mode to the chat agent (#638) 2026-06-05 16:32:25 +02:00
spinner.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
storage.js feat: Add plan mode to the chat agent (#638) 2026-06-05 16:32:25 +02:00
tasks.js Cookbook scheduler + serve: schedule via Tasks, Stop verifies kill, Ollama auto port-pick 2026-06-05 14:41:43 +09:00
theme.js fix: theme color parsing breaks on #rgb shorthand hex (#1213) 2026-06-03 00:30:03 +09:00
tileManager.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
tourAutoplay.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
tourHints.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
tts-ai.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
ui.js Revert "fix(ui): allow manual prompt bar resize (#1201)" 2026-06-03 23:03:58 +09:00
voiceRecorder.js Odysseus v1.0 2026-05-31 23:58:26 +09:00
windowDrag.js fix(ui): modal drag + removed startDrag func (#2430) 2026-06-04 20:34:18 +02:00
windowResize.js Make tool windows resizable by dragging edges or corners 2026-06-01 19:49:23 +02:00
workspace.js feat: Add workspace: confine agent tools to a folder (#1103) 2026-06-05 00:06:37 +02:00