synabun 2026.4.21 → 2026.4.25-beta.2
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.
- package/CHANGELOG.md +175 -881
- package/README.md +1 -1
- package/hooks/claude-code/prompt-submit.mjs +39 -18
- package/hooks/claude-code/session-start.mjs +10 -3
- package/hooks/claude-code/shared.mjs +39 -2
- package/hooks/claude-code/stop.mjs +33 -6
- package/mcp-server/src/index.ts +9 -1
- package/mcp-server/src/services/neural-interface.ts +42 -14
- package/mcp-server/src/services/profiles.ts +8 -7
- package/mcp-server/src/tools/browser-advanced.ts +12 -10
- package/mcp-server/src/tools/browser-interact.ts +39 -38
- package/mcp-server/src/tools/browser-navigate.ts +12 -4
- package/mcp-server/src/tools/loop.ts +7 -3
- package/mcp-server/src/tools/profile.ts +19 -1
- package/neural-interface/lib/loop-logger.js +103 -0
- package/neural-interface/lib/mcp-installer.js +109 -0
- package/neural-interface/lib/plugin-installer.js +264 -0
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/shared/api.js +66 -17
- package/neural-interface/public/shared/cdx/cdx-icons.js +5 -4
- package/neural-interface/public/shared/cdx/cdx-panel.js +117 -14
- package/neural-interface/public/shared/cdx/cdx-render.js +170 -43
- package/neural-interface/public/shared/cdx/cdx-styles.js +55 -10
- package/neural-interface/public/shared/cdx/cdx-tabs.js +394 -72
- package/neural-interface/public/shared/cli-status.js +157 -0
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/html-shell.js +32 -11
- package/neural-interface/public/shared/ocp/ocp-panel.js +127 -28
- package/neural-interface/public/shared/ocp/ocp-render.js +52 -44
- package/neural-interface/public/shared/ocp/ocp-styles.js +33 -0
- package/neural-interface/public/shared/ocp/ocp-tabs.js +693 -82
- package/neural-interface/public/shared/styles.css +635 -0
- package/neural-interface/public/shared/ui-automation-studio.js +38 -12
- package/neural-interface/public/shared/ui-claude-panel.js +322 -15
- package/neural-interface/public/shared/ui-file-explorer.js +38 -18
- package/neural-interface/public/shared/ui-menubar.js +34 -10
- package/neural-interface/public/shared/ui-navbar.js +8 -6
- package/neural-interface/public/shared/ui-schedules-studio.js +1464 -0
- package/neural-interface/public/shared/ui-settings.js +274 -50
- package/neural-interface/public/shared/ui-sidepanel-layout.js +18 -7
- package/neural-interface/public/shared/ui-sidepanel-tray.js +287 -0
- package/neural-interface/public/shared/ui-sync.js +9 -5
- package/neural-interface/public/shared/ui-terminal.js +76 -27
- package/neural-interface/public/shared/ui-tool-updates.js +49 -15
- package/neural-interface/public/shared/ui-tutorial-steps.js +4 -2
- package/neural-interface/public/shared/ui-tutorial.js +24 -18
- package/neural-interface/public/shared/ui-whiteboard.js +79 -31
- package/neural-interface/public/shared/vterm-buffer.js +9 -8
- package/neural-interface/public/variant/2d/main.js +9 -7
- package/neural-interface/public/variant/3d/main.js +10 -8
- package/neural-interface/server.js +1292 -255
- package/package.json +1 -1
- package/skills/leonardo/SKILL.md +5 -1
- package/skills/leonardo/modules/image-prompter.md +5 -1
- package/skills/leonardo/modules/video-prompter.md +5 -1
- package/skills/synabun/SKILL.md +44 -26
- package/skills/synabun/modules/audit.md +40 -36
- package/skills/synabun/modules/changelog.md +22 -18
- package/skills/synabun/modules/memorize.md +19 -15
- package/skills/synabun/modules/schedule.md +285 -0
- package/CLAUDE.md +0 -217
package/CHANGELOG.md
CHANGED
|
@@ -2,884 +2,178 @@
|
|
|
2
2
|
|
|
3
3
|
Raw internal changelogs are archived in [changelog/](changelog/).
|
|
4
4
|
|
|
5
|
-
## 2026-04-
|
|
6
|
-
|
|
7
|
-
### Added —
|
|
8
|
-
- **
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- **
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
###
|
|
25
|
-
- **
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- **
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- **
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- **
|
|
65
|
-
- **
|
|
66
|
-
- **
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- **
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
###
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- **
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
- **`
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- **
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
- **
|
|
146
|
-
|
|
147
|
-
### Fixed —
|
|
148
|
-
- **
|
|
149
|
-
- **
|
|
150
|
-
- **
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- **
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- **
|
|
161
|
-
- **
|
|
162
|
-
- **
|
|
163
|
-
- **
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
- **
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
- **
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- **
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- **
|
|
180
|
-
|
|
181
|
-
### Fixed — Automation Studio Folder Popover Dead Buttons
|
|
182
|
-
- **Popover rendered behind panel** — `positionFloating()` in `neural-interface/public/shared/ui-automation-studio.js` was setting `z-index: 10001` on the folder create/edit popover, but the Automation Studio panel itself uses `z-index: 300000`. Since both are `position: fixed` siblings of `<body>`, the popover stacked underneath and absorbed no clicks. Bumped popover z-index to `300010` so it renders above the panel.
|
|
183
|
-
- **Save / Cancel / Delete buttons non-responsive** — The panel-level delegated click handler (`_panel.addEventListener('click', handlePanelClick)`) could not see the popover because `openFolderPopover()` appends it to `document.body`, not to `_panel`. Attached a direct `pop.addEventListener('click', …)` inside `openFolderPopover()` that dispatches the `folder-popover-save`, `folder-popover-cancel`, and `folder-popover-delete` actions so the buttons actually trigger their handlers.
|
|
184
|
-
|
|
185
|
-
### Changed — Automation Studio Runtime Card (Chip → Native Select)
|
|
186
|
-
- **CLI / Model / MCP Profile converted to `<select>`** — Rewrote the CLI, Model, and MCP Profile chip rows in `buildDetailMeta()` (`neural-interface/public/shared/ui-automation-studio.js`) as `<select class="as-input as-select">` dropdowns, grouping CLI + Model into a 2-column `.as-field-row` grid inside a new `.as-runtime-card` container with a "Runtime" header. Solves truncated chip labels (e.g. "Most capa…") and the `flex: 1` skinny-pill layout that appeared once ~10 MCP profiles were loaded. The Think (effort) field remains chip-based since it has only five options.
|
|
187
|
-
- **`wireLaunchDefaults()` rewired for `change` events** — Replaced the obsolete chip selectors (`.as-launch-profile`, `.as-launch-model`, `.as-launch-mcp-profile`) and containers (`#as-f-profiles`, `#as-f-models`, `#as-f-mcp-profiles`) with `change` handlers on `#as-f-profile` / `#as-f-model` / `#as-f-mcp`. On CLI change, Model options rebuild from `CLI_MODELS[profileId]` (selecting the tier-default), `#as-f-effort-field` and `#as-f-mcp-field` toggle visibility (Claude vs Codex/Gemini), and `loadMcpOptions()` populates the MCP `<select>` from `fetchMcpProfiles()` with `label — N tools` option text. The old wiring was a silent no-op because it early-returned on the missing container — template edits to CLI / Model / MCP never marked the form dirty.
|
|
188
|
-
- **`.as-select` CSS** — Added `select.as-select` rules to `neural-interface/public/shared/styles.css` with `appearance: none`, a dual-gradient custom chevron, `padding-right: 28px`, `text-overflow: ellipsis`, hover border, and dark-themed `option { background: #1a1f2a; color: var(--t-bright); }` so the open dropdown list matches the frosty panel aesthetic instead of inheriting the OS default.
|
|
189
|
-
|
|
190
|
-
### Fixed — OpenCode Sidepanel Permission Timeout
|
|
191
|
-
- **Unhandled `permission.asked` SSE event** — OpenCode sessions hung indefinitely whenever a tool requested user approval. The server relayed the native `permission.asked` event to the client, but `handleSSEEvent()` in `neural-interface/public/shared/ocp/ocp-tabs.js` had no matching `case`, so the permission card was never rendered and the reply never sent — OpenCode waited forever. Added handler cases for `permission.asked`, `permission.replied`, and `permission.rejected`, plus `_activePermissionId` dedupe state and dedicated `renderOpencodePermission()` / `lockPermissionCard()` helpers that render a three-button card (Allow once / Always allow / Reject) wired to `requestWs('permission:respond', …)`.
|
|
192
|
-
- **`permission:respond` WS proxy case** — Added handler in `neural-interface/server.js` (next to `question:reply`) that forwards to OpenCode's `POST /session/:sid/permissions/:permId` endpoint with `{response: "once"|"always"|"reject"}` body. Endpoint and response values verified against `@opencode-ai/sdk/v2` docs via Context7.
|
|
193
|
-
- **`.ocp-permission-actions` / `.ocp-permission-reject` styles** — Added CSS in `neural-interface/public/shared/ocp/ocp-styles.js` for the flex-row button layout and the red-tinted Reject variant, reusing the existing `.ocp-question-card` / `.ocp-question-submit` shell.
|
|
194
|
-
|
|
195
|
-
### Changed — New Session Name Prompt Rebuilt as Centered Modal (All Three Sidepanels)
|
|
196
|
-
- **Inline header rename replaced by centered modal overlay** — When users click "+" to open a new session/tab in the OpenCode, Claude, or Codex sidepanels, the panel now dims with a blurred backdrop and centers a dialog titled "Name this session" with a text input and Skip / Save buttons. Previously the "+" click mutated the header session label into an inline `<input>` and appended a "Don't ask again" escape button next to it — the session label had to double as both title and form field, which made the affordance unclear. The new overlay is appended to the panel element (`position: absolute; inset: 0`), so it stays confined to the sidepanel without spilling onto the page. Enter commits, Escape skips, and clicking outside the modal box skips.
|
|
197
|
-
- **OpenCode** — `promptNameNewSession()` in `neural-interface/public/shared/ocp/ocp-panel.js` rewritten to build the `.ocp-name-modal-overlay` DOM, auto-focus the input via `setTimeout(..., 10)`, and commit through the existing `renameSession(tab.sessionId, nextTitle)` path when a session already exists or assign `tab.pendingTitle` otherwise. `.ocp-name-modal*` CSS added to `neural-interface/public/shared/ocp/ocp-styles.js` (#E8E0DC tan accent on Save).
|
|
198
|
-
- **Claude Code** — `promptNameNewSession()` in `neural-interface/public/shared/ui-claude-panel.js` rewritten to build the `.cp-name-modal-overlay` DOM and persist via `storage.setItem(LABEL_PREFIX + sid, val)` (or `tab.pendingLabel` pre-session), then refresh the pill with `updatePillLabel(t)` + `saveTabs()`. `.cp-name-modal*` CSS added inline in the same file (blue accent `rgba(100,160,255,…)` on Save).
|
|
199
|
-
- **Codex** — Exported `promptNameNewSession()` in `neural-interface/public/shared/cdx/cdx-tabs.js` rewritten to build the `.cxp-name-modal-overlay` DOM and commit through `requestSocketPayload('thread_rename', { threadId, name })` when connected or fall back to `setSessionLabel(label, { persist: false })`. `.cxp-name-modal*` CSS added to `neural-interface/public/shared/cdx/cdx-styles.js` (green accent `rgba(120,180,150,…)` on Save).
|
|
200
|
-
- **`Don't ask again` opt-out removed** — The `ocp-prompt-name-on-new` / `cp-prompt-name-on-new` / `cxp-prompt-name-on-new` storage keys and their guarding `storage.getItem(...) === 'false'` short-circuit were deleted from all three panels. The modal now fires on every new session by design.
|
|
201
|
-
|
|
202
|
-
### Added — OpenCode CLI in Automation Studio
|
|
203
|
-
- **Fourth CLI option across Launch Configuration + Template Editor** — OpenCode now appears alongside Claude Code, Codex, and Gemini in both the Launch Configuration modal and the Template Editor's Runtime panel. Loop mode only; Agent mode remains Claude-exclusive.
|
|
204
|
-
- Added `{ id: 'opencode', label: 'OpenCode CLI', desc: 'Multi-provider' }` to `CLI_PROFILES` in `neural-interface/public/shared/ui-automation-studio.js`
|
|
205
|
-
- Added `CLI_MODELS['opencode']` with curated 6-model set (Opus 4.7, Sonnet 4.6, Haiku 4.5, GPT-5.4, Gemini 3.1 Pro, GLM 5.1) using OpenCode's `provider/model` ID format
|
|
206
|
-
- New `applyAgentModeGating(root, profileId)` helper disables the Agent mode toggle for non-Claude CLIs and force-reverts `_launchMode` to `'loop'`; wired into both initial render and the profile-change handler
|
|
207
|
-
- `.as-launch-mode--disabled` visual state added in `neural-interface/public/shared/styles.css`
|
|
208
|
-
- New `case 'opencode':` branch in `buildExecCommand()` in `neural-interface/server.js` produces `cat <taskfile> | opencode run --model <m> --format default` — the non-interactive equivalent of `codex exec`
|
|
209
|
-
- THINK chips hidden for OpenCode (no reasoning/effort flag in `opencode run`); MCP Profile section auto-shows via existing `profile !== 'claude-code'` conditions — no extra wiring needed
|
|
210
|
-
|
|
211
|
-
### Changed — New Session Modal Adds Project + Branch Selection (All Three Sidepanels)
|
|
212
|
-
- **Project and branch dropdowns inside the "Name this session" overlay** — The modal that fires on "+" new-tab in the OpenCode, Claude, and Codex sidepanels now includes two new `<select>` rows ("Project" and "Branch") above the existing name input. Users can change the target project and git branch at tab-creation time without leaving the modal — previously they had to either rely on the projectbar dropdown before clicking "+" or reconfigure after the fact.
|
|
213
|
-
- **OpenCode** — `promptNameNewSession()` in `neural-interface/public/shared/ocp/ocp-panel.js` extended with a new `fetchBranchesForModal(path)` helper that hits `GET /api/terminal/branches?path=...` and populates a `.ocp-name-modal-select[data-role="branch"]` row. A `loadBranches()` inner function re-runs whenever the project `<select>` changes. The Save handler, when the chosen project differs from `tab.project`, updates `tab.project`, `storage.setItem(STOR.project, …)`, `saveTabs()`, and syncs the projectbar `#ocp-project-dd` label.
|
|
214
|
-
- **Claude Code** — `promptNameNewSession()` in `neural-interface/public/shared/ui-claude-panel.js` extended with the same helper and wiring. Project mirror uses `ddPopulate($project, items, chosenProject)` to keep `#cp-project` in sync after Save. Inline `.cp-name-modal-row / -label / -select` CSS added with JetBrains Mono font and blue focus ring (`rgba(100,160,255,0.5)`).
|
|
215
|
-
- **Codex** — exported `promptNameNewSession()` in `neural-interface/public/shared/cdx/cdx-tabs.js` extended. Reuses module-level `_project` and the existing `renderProjects()` / `_loadBranches()` callbacks to mirror changes back into `#cxp-project` / `#cxp-branch` and kick off a branch refresh after Save. `.cxp-name-modal-row / -label / -select` CSS added to `neural-interface/public/shared/cdx/cdx-styles.js` with the Codex green focus palette (`rgba(120,180,150,0.45)`).
|
|
216
|
-
- **Branch checkout deferred to Save (not on-change)** — Unlike the projectbar's immediate-on-change branch switcher, the modal's branch `<select>` records `branchOriginal` on load and only fires `POST /api/terminal/checkout {path, branch}` if the user commits via Save AND the selection differs. Escape / click-outside / Skip all cancel cleanly without touching the working tree.
|
|
217
|
-
- **Modal chrome bumped 340→380 px** — `.ocp-name-modal / .cp-name-modal / .cxp-name-modal` max-width increased to accommodate the new dropdown rows in `neural-interface/public/shared/ocp/ocp-styles.js`, inline in `neural-interface/public/shared/ui-claude-panel.js`, and `neural-interface/public/shared/cdx/cdx-styles.js`. New `-row / -label / -select` classes share a 4px-gap flex-column layout, 10px uppercase letter-spacing labels, and plain `<select>` with `appearance: none` so the overlay avoids the complexity of the custom `.cp-dd-*` / `#ocp-project-dd` dropdown primitives.
|
|
218
|
-
|
|
219
|
-
## 2026-04-17
|
|
220
|
-
|
|
221
|
-
### Fixed — Floating CLI / Docked Terminal Rendering Glitches (Long Sessions + Deep Scroll)
|
|
222
|
-
- **Single canonical CLI renderer** — Collapsed the `synabun.cliRenderer` localStorage fork. `_openCliSession` / `_reconnectCliSession` in `neural-interface/public/shared/ui-terminal.js` now always route CLI profiles (claude-code, codex, gemini, opencode) through `openSession()` / `reconnectSession()` (xterm.js + WebGL with Canvas fallback). `HtmlTermRenderer` / `VTermBuffer` / `AnsiParser` files remain on disk for non-CLI surfaces but are no longer reachable from CLI profile routing. Eliminates the dual-path bug surface that has been the source of most rendering fixes over the last month.
|
|
223
|
-
- **xterm Terminal option hardening** — Added `rescaleOverlappingGlyphs: true` (the official xterm fix for glyph bleed — capybara logo, spinners, box-drawing), `drawBoldTextInBrightColors: false` (prevents texture atlas duplication), and `windowsPty: false` (explicit POSIX) to both `new _Terminal({ ... })` sites. Reduced `scrollback: 5000 → 1500` — Ink TUIs rarely need more and resize reflow cost drops 3.3× over the full buffer. Git-output tab kept at its existing 2000 (non-Ink path).
|
|
224
|
-
- **Periodic WebGL texture atlas pruning** — Long-running Ink sessions accumulate atlas entries for every unique glyph×attr combo until GPU pressure forces a context loss → drawn-glitch artifacts. Added a per-session hook that clears the atlas + repaints at two safe points: on alt-screen exit (Claude Code leaving `/help`, spinner, menus — via `term.buffer.onBufferChange(buf.type === 'normal')`), and every 500 parsed writes (via `term.onWriteParsed`). Both hooks are optional-chained and wrapped in try/catch so a proposed-API mismatch never breaks the session.
|
|
225
|
-
- **WebGL context-loss recovery now resyncs everything** — The previous handler only disposed WebGL and loaded Canvas. After context loss Ink's next frame painted into a stale cell grid. Now the handler: (1) guards against double-dispose with a flag, (2) swaps to Canvas, (3) schedules `fitAddon.fit()` + `term.refresh(0, rows-1)` on the next rAF, (4) nulls `_lastSentResize` and fires an unconditional PTY resize so Ink's next SIGWINCH draws into the fresh grid. Mirrored in both `openSession` and `reconnectSession`.
|
|
226
|
-
- **Edge-drag visual fit coalesced to ~64 ms** — `_scheduleFit()` was re-measuring the DOM and fitting every rAF (~60 ×/s) for the entire duration of a resize drag, re-laying the full 1500-line scrollback every frame. This was the single biggest glitch source on long sessions. During `_draggingResize`, fits now coalesce to 1 per 4 rAF frames (~64 ms). PTY resize debounce is unchanged. `_sendResize()` (drag end) resets the drag-fit counter and calls `term.refresh()` once to guarantee a clean final paint.
|
|
227
|
-
- **Post-open fit+refresh for both fresh and resumed sessions** — Added one `requestAnimationFrame(() => { fitAddon.fit(); term.refresh(0, rows-1); })` after all addons load in both `openSession` and `reconnectSession`. Locks in correct cell metrics against the freshly-loaded WebGL atlas so frame 1 doesn't paint with a stale font-measurement or addon-ordering artifact. Fixes the occasional "first Claude Code frame looks slightly wrong" on resume.
|
|
228
|
-
- **Server output burst cap at 64 KB** — In `neural-interface/server.js` the `ptyProcess.onData` coalesce buffer had no size limit: a 1 MB Claude response would pile into one giant JSON WebSocket message, blocking the client write loop for enough frames to produce visible rendering tearing. Added `OUTPUT_BURST_CAP = 64 * 1024` — when the accumulated buffer crosses the cap, `_flushCoalesced()` fires synchronously instead of deferring via `setImmediate`. Progressive rendering preserved; huge bursts no longer stall the client.
|
|
229
|
-
- **Ordered WebGL dispose before xterm dispose on session close** — The GPU texture atlas leaks across session close if xterm's `Terminal.dispose()` runs before the WebGL addon's `dispose()`. Attached `_webgl` handle to every session object and dispose it first in both `closeSession()` (single tab close) and the mass-dispose loop. New sessions now start with a clean GPU state instead of inheriting the previous session's atlas.
|
|
230
|
-
- **Audit — xterm viewport CSS clean** — Verified no `content-visibility: auto` on `.term-viewport` / `.xterm-viewport` / `.xterm-screen` / `.term-float-tab-body .xterm-viewport`. The two remaining `content-visibility: auto` usages are on `.html-term-rows` / `.html-term-row` which are now off the CLI path. No CSS changes required.
|
|
231
|
-
|
|
232
|
-
### Fixed — OpenCode Sidepanel `question` Tool Aborted During Plan Mode
|
|
233
|
-
- **`question` tool stalled with empty `{}` args and "Tool execution aborted"** — When the opencode agent called the `question` tool (especially in plan mode), the sidepanel rendered a generic tool card with empty arguments and the result `Tool execution aborted`; the user could never answer and the session stalled. Root cause: OpenCode's `question` tool is NOT a regular tool-use flow — it suspends server-side and emits a dedicated `question.asked` SSE event over `/global/event`, and answers must be delivered via `POST /question/{requestID}/reply` with body `{ answers: string[][] }` (one string array per question, each containing selected option labels). The prior workaround in `neural-interface/public/shared/ocp/ocp-tabs.js` intercepted the tool from `message.part.updated`, called `abortMessage()`, rendered a stand-in card, then sent the answer as a new user message — which caused OpenCode to resolve the original tool call with "aborted" and the error card to stick.
|
|
234
|
-
- **Server proxy for the `/question` endpoints** — Added three new WS handlers in `neural-interface/server.js` that proxy to the opencode REST endpoints: `question:reply` → `POST /question/{requestID}/reply` (forwards the `{ answers }` body), `question:reject` → `POST /question/{requestID}/reject`, and `question:list` → `GET /question`. Each handler posts through the existing `ocpProxy()` helper and replies with `question:reply:result` / `question:reject:result` / `question:list:result` so the client never hangs on a missing ack.
|
|
235
|
-
- **SSE handlers for `question.asked` / `question.replied` / `question.rejected`** — `handleSSEEvent` in `neural-interface/public/shared/ocp/ocp-tabs.js` now routes these three event types. On `question.asked`, the new `renderOpencodeQuestion()` helper sets `_activeQuestionToolId = requestID`, renders the interactive card from `event.questions`, and wires `onAnswer` to call `requestWs('question:reply', { requestID, body: { answers } })` — no abort, no fake user message, session keeps running. On `question.replied` / `question.rejected`, the card is locked via `lockQuestionCard()` and the queue is drained through `processQuestionQueue()`.
|
|
236
|
-
- **Raw tool card suppressed for the `question` tool** — The `message.part.updated` branch at `String(part?.type).includes('tool')` now short-circuits when `isQuestionTool(toolName)` is true: it only sets status to "Waiting for input…" while a question is active and skips `renderAssistantPayload()` entirely, eliminating the duplicate "running/aborted" tool card that previously shadowed the interactive card.
|
|
237
|
-
- **Answer-payload mapping** — `buildQuestionReplyPayload(questions, answers)` converts the question card's `{ questionText: "label1, label2" }` map (or bare string for single-question cards) into OpenCode's expected `{ answers: [["label1","label2"], ["label3"]] }` shape, aligned positionally with the `questions[]` array and padded with empty arrays for unanswered entries.
|
|
238
|
-
- **`multiple` + `custom` field mapping** — Extended `extractAllQuestions()` in `neural-interface/public/shared/ocp/ocp-render.js` to map OpenCode's `multiple` field to the existing `multiSelect` flag and preserve `custom` (defaults to true), so multi-select questions render with the correct UI and custom-answer input is permitted per opencode's contract.
|
|
239
|
-
- **Queue shape updated** — `queueQuestion()` / `processQuestionQueue()` now accept the new `{ eventType: 'question.asked', event, reqId }` shape and route through `renderOpencodeQuestion()` while the legacy tool-based branch is preserved for older `tool.start`-style fallbacks.
|
|
240
|
-
|
|
241
|
-
### Fixed — Codex CLI Skill Prompts Left `$ARGUMENTS` As Literal Text
|
|
242
|
-
- **`/synabun` and `/leonardo` installed to Codex but arguments never substituted** — After toggling the Codex chip on Settings > Skills, the generated `~/.codex/prompts/<name>.md` files contained `$ARGUMENTS` tokens verbatim. Codex CLI only expands shell-style positionals (`$1..$N`) and does not substitute `$ARGUMENTS`, so the agent reading the prompt saw the literal placeholder string where the user's input should have appeared. `buildCliSkillPrompt()` in `neural-interface/server.js` had a `void target;` no-op instead of per-target rewriting.
|
|
243
|
-
- **Per-target `$ARGUMENTS` rewrite for Codex** — When `target === 'codex'`, every `$ARGUMENTS` occurrence in the prompt body is now replaced with `$1` before the file is written, so Codex injects the full argument string that follows `/<command>`. OpenCode and Claude targets are untouched — both substitute `$ARGUMENTS` natively.
|
|
244
|
-
- **Codex invocation banner added to installed prompts** — The post-frontmatter banner built inside `buildCliSkillPrompt` now appends a Codex-only line (`> Invocation: /<skillName> <args> — the full argument string is available as $1 below.`) so the agent understands the positional contract. Stale JSDoc above the function was rewritten to describe the new per-target behavior.
|
|
245
|
-
|
|
246
|
-
### Added — OpenCode Sidepanel `/synabun changelog` Quick Skill Button
|
|
247
|
-
- **`/synabun changelog` quick skill button** — Added a changelog icon action to the OpenCode sidepanel project bar in `neural-interface/public/shared/ocp/ocp-panel.js`, positioned in `ocp-bar-actions` alongside the existing Revert and Share buttons to match the Codex and ClaudeCode sidepanel placement. The button uses the existing `.ocp-bar-btn` styling with a document SVG icon. Clicking it pre-fills `/synabun changelog` into the input field, runs `autosizeInput()`, and dispatches `handleSend()` only when the OpenCode server is ready and the active tab is not already running.
|
|
248
|
-
|
|
249
|
-
### Changed — OpenCode Sidepanel Share Button Removal
|
|
250
|
-
- **Removed share button from project bar** — The `#ocp-share` button was removed from the `.ocp-bar-actions` container in `neural-interface/public/shared/ocp/ocp-panel.js`, along with its `ICON_SHARE` import from `ocp-icons.js`, click event listener that dispatched `shareSession()`, and `syncActionBar()` state management. The `/share` and `/unshare` slash commands remain available via text input for users who want to share sessions.
|
|
251
|
-
|
|
252
|
-
### Fixed — Premature PLAN COMPLETE During Plan-Mode AskUserQuestion (Claude Sidepanel)
|
|
253
|
-
- **Plan finalized before user answered questions** — In `--print` mode the Claude CLI does not pause on `AskUserQuestion` (control_requests are routed to the browser, not back to the CLI over stdin), so Claude kept emitting `Write` / `Bash` / `ExitPlanMode` tool_use blocks in the same assistant turn as the question — producing a premature "PLAN COMPLETE" card before the user could answer, plus a second one after answering. Added a hard-stop handler in `neural-interface/server.js` (~line 5629) that runs BEFORE the existing `ExitPlanMode` check: finds the first `AskUserQuestion` block in `event.message.content`, trims any trailing tool_use blocks (so stray `Write` / `Bash` / `ExitPlanMode` entries don't render under the question card), forwards the trimmed assistant event, emits the `control_request` for the question, sets `awaitingPermission` and `exitPlanKilled`, then calls `killProc()` + `sendToClient({ type: 'done', code: 0 })`. Removed the now-dead `AskUserQuestion` branch from the proactive-card for-loop at `~5653`. Ordering guarantees the question wins when both `AskUserQuestion` and `ExitPlanMode` appear in the same message — user must answer before any plan-approval card renders.
|
|
254
|
-
|
|
255
|
-
### Added — OpenCode Panel Greeting (Isolated From Claude + Codex)
|
|
256
|
-
- **New `OpenCode Panel Greeting` section in Settings** — `neural-interface/public/shared/ui-settings.js` renders a third greeting section under Claude and Codex via the existing provider-agnostic `buildGreetingSection({ prefix: 'opencode', bodyId: 'opencode-greeting-body', toggleAttrs: 'data-opencode-greeting-toggle="enabled"' })`. A cloned enable/disable toggle handler posts to `/api/opencode-panel/greeting/config`, and a third `wireGreetingEditor({ prefix: 'opencode', ... })` reuses the already-parameterized editor with zero internal changes. Section ships disabled by default.
|
|
257
|
-
- **Config file + server helpers** — Added `OPENCODE_GREETING_CONFIG_PATH` (`data/opencode-greeting-config.json`) and a dedicated family of non-shared helpers in `neural-interface/server.js`: `buildSeededOpencodeGreetingConfig()` (deep-clones `loadGreetingConfig()` defaults/projects), `normalizeOpencodeGreetingConfig()`, `loadOpencodeGreetingConfig()`, `saveOpencodeGreetingConfig()`, and `buildOpencodeGreetingUserPrompt({ cwd, userPrompt })` modeled on `buildCodexGreetingUserPrompt()`. Only the pure read helpers (`detectGreetingProject`, `getTimeGreeting`, `getGreetingGitBranch`, `resolveGreetingTemplate`, `getProviderGreetingProjectConfig`) are shared across providers — all mutable state stays isolated.
|
|
258
|
-
- **REST endpoints** — Added `GET /api/opencode-panel/greeting/config`, `PUT /api/opencode-panel/greeting/config` (top-level `enabled` flip), and `PUT /api/opencode-panel/greeting/config/:project` (per-project template / reminders / labels) after the Codex endpoint block in `server.js`. Each uses its own `loadOpencodeGreetingConfig` / `saveOpencodeGreetingConfig` — no shared file writes with Codex or Claude.
|
|
259
|
-
- **WS-scoped fire-once injection in `handleOpencodeWs`** — Added `const pendingGreetingSessions = new Map()` alongside `_sendAbortControllers` (per-socket closure, no globals). On `case 'session:create'`, the new sessionId is mapped to `msg.cwd || PACKAGE_ROOT` (handles `data.id` / `data.sessionID` / `data.info.id` response shapes). On `case 'message:send'`, after the existing skill-injection block and before the fetch, a guarded block checks `pendingGreetingSessions.has(sid)` + config `enabled: true` + first text part does not start with `/`, then calls `buildOpencodeGreetingUserPrompt({ cwd, userPrompt })` and replaces the first `parts[i].type === 'text'` entry (or `msg.content`). The Map entry is always deleted after the first `message:send` (fire-once guarantee). `case 'session:delete'` removes the entry as well; WS close discards the closure naturally.
|
|
260
|
-
- **Client threads `cwd` through the WS payload** — `neural-interface/public/shared/ocp/ocp-tabs.js` now includes `cwd: tab.project` in both `requestWs('session:create', ...)` and `requestWs('message:send', ...)` payloads so the server can resolve the git branch / project label in the greeting. Server falls back to `PACKAGE_ROOT` when `cwd` is absent.
|
|
261
|
-
- **Backup allowlist extended** — `opencode-greeting-config.json` added to both backup file lists in `server.js` (the manual-backup and auto-backup loops at ~lines 9598 and 9980). While there, also added the previously-missing `codex-greeting-config.json` to both lists so Codex greeting state is no longer dropped from backups.
|
|
262
|
-
- **Isolation table (no shared mutable state)** — Claude: `greeting-config.json` + `hook-features.json` + `prompt-submit.mjs` hook + `/api/greeting/*` + UI prefix `cc-`. Codex: `codex-greeting-config.json` + `buildCodexGreetingUserPrompt` + `pendingGreetingThreads` Set + `/api/codex-panel/greeting/*` + UI prefix `codex-`. OpenCode: `opencode-greeting-config.json` + `buildOpencodeGreetingUserPrompt` + `pendingGreetingSessions` Map + `/api/opencode-panel/greeting/*` + UI prefix `opencode-`. Toggling one provider's greeting cannot mutate another's config, state, or endpoint.
|
|
263
|
-
|
|
264
|
-
### Changed — Hooks And Knowledge Settings Labeled Claude-Only
|
|
265
|
-
- **Scope hint inside HOOKS and KNOWLEDGE section bodies** — `neural-interface/public/shared/ui-settings.js` now renders a `.cc-hint` line at the top of the HOOKS section body ("Claude Code hooks only — Codex and OpenCode panels use their own greeting system and do not read these toggles.") and a matching hint inside the KNOWLEDGE section body. Verified these features are Claude-only by construction: `HOOK_FEATURES_PATH` (`data/hook-features.json`) is read and written exclusively by `/api/claude-code/*` routes in `server.js`, and the 6 hook toggles only touch `~/.claude/settings.json` via `/api/claude-code/integrations`. No logic changes — the existing `providerBadge({ cli: true, vscode: true, web: false })` already marked the provider surface; these hints make the cross-panel boundary explicit to users who might otherwise expect the toggles to affect Codex/OpenCode.
|
|
266
|
-
|
|
267
|
-
### Changed — AskUserQuestion Card Spacing Polish (Claude Sidepanel)
|
|
268
|
-
- **Stacked question cards felt crammed and asymmetric** — Multi-question `AskUserQuestion` prompts in the Claude Code sidepanel rendered with tight card padding, minimal separation between stacked cards, narrow option gaps, and an "Or type your own answer" divider that collapsed against the custom-answer text input. The inline `<style>` block in `neural-interface/public/shared/ui-claude-panel.js` (~lines 669–784) governing these cards was rewritten for a consistent spacing hierarchy so multiple questions stack with visible rhythm and each option row gets room to breathe.
|
|
269
|
-
- `.ask-card` — padding `10px 12px` → `14px 16px 16px`, radius `8 → 10`, margin-top `6 → 10`, added `box-sizing: border-box`. New `.ask-card + .ask-card { margin-top: 12px }` rule gives stacked questions consistent vertical rhythm.
|
|
270
|
-
- `.ask-header` — uppercase with `letter-spacing: 0.5px` and `margin-bottom: 10px` (was `6px`).
|
|
271
|
-
- `.ask-question` — `margin-bottom: 8 → 12`, `line-height: 1.45 → 1.5`.
|
|
272
|
-
- `.ask-options` — gap `3 → 6`; `.ask-option` padding `6px 9px` → `9px 12px`, gap `8 → 10`, radius `6 → 7`, added `width: 100%` + `box-sizing: border-box` so hover/select states no longer overflow the card.
|
|
273
|
-
- `.ask-option-wrap` — added `min-width: 0; flex: 1; gap: 3px` to prevent option-label truncation inside the flex row.
|
|
274
|
-
- `.ask-option-desc` — opacity `0.3 → 0.38`, line-height `1.3 → 1.4` for readability.
|
|
275
|
-
- `.ask-submit-bar` — margin-top `10 → 14`, padding-top `8 → 12`; `.ask-submit` padding `6px 14px` → `7px 16px`, uppercased with `letter-spacing: 0.5px`.
|
|
276
|
-
- `.ask-text-input` — padding `6px 8px` → `8px 10px`, added `box-sizing: border-box`, focus state now also tints the background (`rgba(255,255,255,0.04)`).
|
|
277
|
-
- `.ask-or-type` — margin `8px 0 4px` → `14px 0 8px`, padding-top `6 → 10`, `text-align: center`, dashed-border alpha `0.05 → 0.06`.
|
|
278
|
-
|
|
279
|
-
## 2026-04-16
|
|
280
|
-
|
|
281
|
-
### Fixed — Claude Sidepanel Plan Mode Ignored AskUserQuestion Answers & Freeform Input Missing
|
|
282
|
-
- **Plan was never rebuilt from the user's AskUserQuestion answers** — When the sidepanel was in plan mode and Claude called `AskUserQuestion`, submitting the answers caused Claude to jump straight to `ExitPlanMode` without integrating the user's selections into the plan. Root cause in `neural-interface/server.js`: the respawn prompt after a `control_response` was literally `The user answered your question: "${Object.values(answers).join(', ')}"` — it collapsed all answers into one comma-joined string (losing Q→A mapping) and dropped the simulated `[PLAN MODE ...]` prefix that the client prepends on the first turn, so Claude's next turn no longer believed it was in plan mode. Fixed by adding `buildAskAnswerPrompt(questions, answers, wasPlanMode)` inside `handleClaudeSkinWebSocket`. The helper emits per-question `- Q: ... / A: ...` lines (supporting `question.text` / `header` / `id` / index keying plus Codex-style `{ answers: [...] }` values), and when `wasPlanMode` is true — detected via `/\[PLAN MODE/.test(lastPrompt)` — re-injects the full plan-mode prefix plus a "do NOT call `ExitPlanMode` yet — integrate these answers first" directive. `lastPrompt` is updated to the new prompt after respawn so chained AskUserQuestion turns keep re-injecting the prefix. Applied to both the `control_response` AskUserQuestion handler and the `tool_result` AskUserQuestion fallback path.
|
|
283
|
-
- **Ask cards had no freeform text input when options were present** — If none of Claude's 2–4 option buttons matched what the user wanted, there was no way to type a custom answer; the text input only rendered in the zero-options branch. In `neural-interface/public/shared/ui-claude-panel.js`, both `renderAskUserQuestion()` and `buildAskFromToolUse()` now always append a text input below the options, separated by a dashed "Or type your own answer" divider (new `.ask-or-type` inline style). Typing overrides any selected option (deselects siblings); clicking an option clears the input. Placeholder is `Custom answer…` when options coexist, `Type your answer...` when the question has no options. `buildAskFromToolUse()` gained a `qIndex` counter plus `data-qidx` attributes on `.ask-options` / `.ask-text-input` so per-question cross-wiring works when multiple questions share the same container.
|
|
284
|
-
|
|
285
|
-
### Fixed — Codex & OpenCode Sidepanel Abort Broken (ESC / Stop Button / Server Still Running)
|
|
286
|
-
- **ESC did nothing, Stop button was missing, clicks did nothing, and the server kept running even when the UI reset** — all four abort failure modes reported by the user on both Codex and OpenCode sidepanels. Claude Code sidepanel was left untouched.
|
|
287
|
-
- **Document-level ESC while panel is open** — Previously `_panel.addEventListener('keydown', ...)` required a focusable descendant to own focus; ESC was lost when the user was clicking on the 3D graph, menubar, or anywhere outside the panel. Replaced panel-level ESC with a `document.addEventListener('keydown', handler, { capture: true })` installed in `setVisible(true)` and removed in `setVisible(false)` in both `neural-interface/public/shared/cdx/cdx-panel.js` and `neural-interface/public/shared/ocp/ocp-panel.js`. Handler gates on panel visible + any tab running, yields to slash-hint dismissal, and calls `event.stopImmediatePropagation()` only when it actually consumes the event.
|
|
288
|
-
- **Panel-wide abort scope (every tab + its server-side work)** — Aborting previously only acted on the bound active tab. Added `interruptAllTabs()` in `cdx-tabs.js` and `abortAllTabs()` in `ocp-tabs.js` that iterate every tab, send `interrupt` / `message:abort` per tab WS, schedule a 2 s `force_kill` fallback per tab (Codex), reject the tab's in-flight `message:send` promise (OpenCode), reset each tab's UI via `withTab(tab, () => setRunning(false))` (Codex) or per-tab state (OpenCode), and clear queued drafts. Exposed `anyTabRunning()` from both modules and wired all ESC handlers, Stop button click handlers, and input-level ESC through the new panel-wide helpers.
|
|
289
|
-
- **Stop button visibility follows panel-wide running state** — `syncInputEnabled()` in `cdx-tabs.js` and `syncSendButton()` in `ocp-panel.js` now check `_tabs.some(t => t?.running)` instead of the bound-tab-only `_running`, so the button switches to Stop mode as long as any tab in the panel is running — matching the new abort scope.
|
|
290
|
-
- **Per-tab `_activeSendRequestId` for OpenCode** — The module-level `_activeSendRequestId` tracked only the last-sent tab; a second tab's in-flight send would overwrite it, so aborting one tab could never reject the other tab's promise. Now stored on `tab._activeSendRequestId` in addition to the legacy module-level copy, and `abortAllTabs()` rejects each tab's own pending request.
|
|
291
|
-
- **Codex server `turn/interrupt` no longer throws when `activeTurnId` is null** — `neural-interface/server.js` previously threw "No active turn to interrupt" if the `interrupt` WS message arrived before a turn started or after it ended, leaving the child process running and relying on a 2.5 s client-side retry to escalate to force-kill. Rewritten: `interrupt` tries `turn/interrupt` RPC when the ids exist, falls through to inline SIGTERM → SIGKILL (2 s) + synthetic `turn/completed` + `interrupt_ack` on any RPC failure or missing id. No more stale client wait loops.
|
|
292
|
-
- **Both `interrupt` fallback and `force_kill` now reject all pending RPCs** — Previously the server killed the child but left `pending` map entries unresolved; their promises hung until the 10 s RPC timeout fired. Added `pending.keys()` iteration that calls `clearPendingRequest(key)` and rejects with "Aborted by user" / "Force-stopped by user". `pendingServerRequests` is also cleared.
|
|
293
|
-
- **OpenCode `message:abort` always acks** — Wrapped the `/session/{sid}/abort` proxy call in try/catch and the ack send runs unconditionally, so the client never hangs waiting for the WS reply when the opencode-serve instance is unreachable.
|
|
294
|
-
|
|
295
|
-
### Fixed — Claude Code Sidepanel Context Gauge Accuracy & 1M Context Activation
|
|
296
|
-
- **1M context window variants silently ran at 200K** — Selecting "Opus 4.6" / "Opus 4.7 xhigh" / "Sonnet 4.6" (the 1M variants) in the sidepanel model dropdown never actually enabled the 1M beta context window. The dropdown encodes composite values like `claude-opus-4-6:1000000`, but `_getModelId()` in `neural-interface/public/shared/ui-claude-panel.js` stripped the `:1000000` suffix before sending, leaving the CLI with the bare model id. Claude Code CLI 2.1.x requires the literal `[1m]` suffix (e.g. `claude-opus-4-6[1m]`) to activate the 1M window — without it the CLI silently defaults to 200K while the gauge denominator displayed 1M. Fixed `_getModelId()` to append `[1m]` when the composite context window exceeds 200K, and added a server-side `toCliModelName(model)` normalizer in `neural-interface/server.js` applied to the WebSocket query path, agent spawn (`--model`), and `buildShellProfile()` exec path so backward-compat payloads also map correctly.
|
|
297
|
-
- **Session history endpoint falsely locked every resumed session to 200K** — The session-history handler in `neural-interface/server.js` hardcoded `contextWindow: 200000` for all four known models based on the `lastModel` string pulled from JSONL. But JSONL strips the `[1m]` suffix before persisting (confirmed via live session inspection), so a session actually run at 1M was indistinguishable from a 200K one. The gauge flipped to 200K on reload until the next live `result` event re-anchored it. Removed the inference block — the client now falls back to the dropdown's current selection and re-anchors to the real value from `result.modelUsage.contextWindow` on the next turn.
|
|
298
|
-
- **Gauge label rendered `1000k` instead of `1M`** — `fmt()` inside `renderGauge()` only had `≥1000 → Math.round(v/1000)+'k'` logic, producing awkward `1000k / 1000k ctx` labels on 1M windows. Extended `fmt()` to format values ≥ 1,000,000 as `1M` (or `1.5M` for fractional values).
|
|
299
|
-
- **`/context` slash command double-counted output tokens toward context consumption** — The info card at `case 'context'` in `ui-claude-panel.js` summed `inputTokens + outputTokens` for its "tokens used" line. Output tokens are generated, not part of the input context window — a long response inflated the percentage incorrectly. Replaced with `inputTokens + cacheRead + cacheWrite`, matching the formula already used by `renderGauge()`.
|
|
300
|
-
- **Defensive: `calcMessageCost()` now strips `[1m]` before pricing lookup** — Result events key `modelUsage` by the suffixed name (e.g. `claude-opus-4-6[1m]`) while `MODEL_PRICING` keys by base id. Added `model.replace(/\[1m\]$/, '')` so either form resolves to the correct rate.
|
|
301
|
-
|
|
302
|
-
### Fixed — CLI Notification Spam on AskUserQuestion / Permission Prompts
|
|
303
|
-
- **Claude Code CLI fired repeated OS/panel notifications until the user answered the prompt** — In `neural-interface/public/shared/ui-terminal.js`, `_scheduleCliStatusCheck()` unconditionally flipped session status to `WORKING` on every terminal output burst (cursor blinks, ANSI redraws, spinner ticks). While an `ACTION` prompt (AskUserQuestion, "Allow? Yes/No/Always") was on screen, each micro-burst produced a `WORKING → ACTION` re-transition that passed the `if (prev !== WORKING) return` dedup guard in `_notifyStatusChange()`, causing `notify('cli', NOTIF_TYPE.ACTION, ...)` to fire again and again. Fixed by introducing a `_lastNotified` Map that records the last-fired `NOTIF_TYPE` per `sessionId` and suppresses repeat notifications of the same type until the session visits `IDLE` (i.e., the prompt clears / user responds). The entry is removed in `_untrackCliSession()`. Badge UI still updates normally — only duplicate notifications are suppressed. Applies to all tracked CLI profiles: `claude-code`, `codex`, `gemini`, `opencode`.
|
|
304
|
-
|
|
305
|
-
### Added — Multi-CLI Resume with Provider Picker
|
|
306
|
-
- **Provider selector bar in Resume dropdown for switching between CLI tools** — The top-navbar Resume dropdown now renders a `.resume-provider-bar` with pill buttons for 4 CLI providers: Claude Code, Codex CLI, Gemini CLI, and OpenCode. Each button displays the provider's SVG icon from `getProviderMeta()` (`provider-icons.js`) and brand name. Active provider gets `--provider-color` CSS variable applied for brand-colored border and text (e.g., Codex green, OpenCode blue). Selection persists across page loads via `storage.setItem('synabun-resume-provider')`.
|
|
307
|
-
- **Codex CLI session discovery endpoint (`GET /api/codex/sessions`)** — Recursively walks `~/.codex/sessions/` reading each `.jsonl` file's first line (`session_meta`) with a 64KB buffer to extract `id`, `cwd`, and `git.branch`. Matches `cwd` to registered projects from `claude-code-projects.json`. Supports `limit`, `offset`, `search`, and `refresh` query params. Returns uniform `{ projects: [{ path, label, total, sessions }] }` shape.
|
|
308
|
-
- **OpenCode session discovery endpoint (`GET /api/opencode/sessions`)** — Queries `~/.local/share/opencode/opencode.db` SQLite database, joining `session` and `project` tables using `p.worktree` (not `p.directory`) for the actual project path. Filters out archived sessions, includes message counts, and matches `project_worktree` to registered projects.
|
|
309
|
-
- **Gemini CLI session stub endpoint (`GET /api/gemini/sessions`)** — Returns empty projects array (Gemini CLI has no known on-disk session storage).
|
|
310
|
-
- **Codex resume subcommand support in `getTerminalProfile()`** — Extended the terminal profile builder in `server.js` to handle Codex's resume syntax (`codex resume <sessionId>` subcommand) alongside Claude Code's existing flag syntax (`claude --resume <sessionId>`).
|
|
311
|
-
- **Complete rewrite of `ui-resume.js` for multi-provider architecture** — `RESUME_PROVIDERS` array and `PROVIDER_FETCH` map route data loading to the correct endpoint per provider. `renderProviderBar()` builds the selector UI, `loadAllProjects()` fetches from the active provider, and `launchResume()` passes `profile: _selectedProvider` to the terminal. Index button and memory search hidden for non-Claude providers. ~580 lines replacing the single-provider implementation.
|
|
312
|
-
- **Three new API fetch functions in `api.js`** — `fetchCodexSessions()`, `fetchOpencodeSessions()`, and `fetchGeminiSessions()` mirror the existing `fetchClaudeSessions()` pattern with provider-specific endpoint URLs.
|
|
313
|
-
|
|
314
|
-
### Changed — TODO Widget Slide-Out Drawer (Claude Code Sidepanel)
|
|
315
|
-
- **Floating widget → left-edge slide-out drawer** — Reworked the sticky TODO widget in `neural-interface/public/shared/ui-claude-panel.js`. Previously a 260px panel anchored top-right inside `.cp-messages-container` that hid entirely on dismiss. Now renders a slim vertical **tag** docked on the left edge whenever `tab.todos.length > 0`. Clicking the tag slides a 260px drawer open via a `width: 0 → 260px` CSS transition driven by an `.is-open` class on the dock root.
|
|
316
|
-
- Tag shows icon (`✓` idle, `◐` if any in_progress task), a vertical `done/total` count, and a pulsing blue dot when an in_progress task exists.
|
|
317
|
-
- Drawer × button force-collapses back to tag-only.
|
|
318
|
-
- **State semantics for `tab.todosVisible`** — Reinterpreted the existing flag: `true`=drawer expanded, `false`=tag-only. TodoWrite handler now defaults to collapsed via `if (typeof tab.todosVisible !== 'boolean') tab.todosVisible = false;` instead of always-true, so the user's expand/collapse choice persists across TODO updates within a tab.
|
|
319
|
-
- **CSS dock structure** — Replaced `.cp-todo-widget` block with `.cp-todo-dock` / `.cp-todo-tag` / `.cp-todo-drawer` rules. Dock root uses `pointer-events: none` so the empty area between tag and drawer doesn't intercept clicks on the messages list; tag and drawer override to `pointer-events: auto`. All inner item styles re-scoped under `.cp-todo-drawer`.
|
|
320
|
-
- **Ctrl+T keybinding** — Unchanged binding at lines 7080–7087; with the new render it now toggles the drawer (tag remains visible while todos exist).
|
|
321
|
-
|
|
322
|
-
### Added — Hybrid Content Search for Resume Sessions (FTS5 + Semantic)
|
|
323
|
-
- **Full-body session search via SQLite FTS5 virtual table** — Resume dropdown and Claude/Codex sidepanel session menus previously filtered only on `firstPrompt` + `gitBranch` + `sessionId`. Typing "pixel render bug" missed sessions where that phrase appeared deeper in the transcript. Added `session_cache(session_id, provider, ...)` table and `session_fts` FTS5 virtual table (`porter unicode61 remove_diacritics 2` tokenizer) in `neural-interface/lib/db.js`, populated by a new `neural-interface/lib/session-cache-builder.js` module that walks `~/.claude/projects/*`, `~/.codex/sessions/`, and OpenCode's `message` table. Per-session body capped at 256 KB, per-message at 8 KB. `stripTags()` removes `<system-reminder>`, `<ide_opened_file>`, `<ide_selection>`, and `<*>` tags before indexing.
|
|
324
|
-
- **Hybrid search endpoint `GET /api/session-search`** — New endpoint in `neural-interface/server.js` runs FTS5 substring search against `session_fts` then for Claude Code only embeds the query and cosine-searches the existing `session_chunks.vector` table (threshold 0.35). Merges hits into `{ fts, semantic-only, both }` with composite score `0.6 * fts_norm + 0.4 * semantic`. Returns `snippet()` with `<mark>`-wrapped matches via bm25 rank; hydrates results from `session_cache` joined against registered projects.
|
|
325
|
-
- **Match-kind visual feedback in Resume dropdown** — `neural-interface/public/shared/ui-resume.js` now routes queries ≥2 chars through the hybrid endpoint. Each result renders a 2-line snippet row with `<mark>` highlights plus a match-kind pill: `strong match` (both FTS + semantic) in green, `related` (semantic-only) in purple, or no pill for FTS-only. Styles added in `neural-interface/public/shared/styles.css` under `.resume-session-snippet`, `.resume-match-semantic`, `.resume-match-both`.
|
|
326
|
-
- **Claude sidepanel session search routed through FTS5** — `cpSessLoadBatch()` in `neural-interface/public/shared/ui-claude-panel.js` calls `searchSessions({ provider: 'claude-code' })` on first-page loads when the query is ≥2 chars, falling back to the paginated `fetchClaudeSessions` when the query clears.
|
|
327
|
-
- **Codex sidepanel search union with FTS5 body hits** — `cxpSessApplyFilters()` in `neural-interface/public/shared/cdx/cdx-tabs.js` now unions the existing label/source/id substring filter with an async-populated `_cxpFtsMatchIds` set fetched via `searchSessions({ provider: 'codex' })` so threads whose titles don't contain the query but whose message bodies do still appear.
|
|
328
|
-
- **Boot-time async cache rebuild** — Server boot (after a 2 s delay) calls `rebuildClaudeCache()`, `rebuildCodexCache()`, and `rebuildOpencodeCache({ getOpencodeDb })` non-blocking so first-keystroke search has data. Unchanged files are skipped via `file_size + file_mtime` probe against existing `session_cache` rows; only new/modified sessions re-parse.
|
|
329
|
-
- **Input-sanitized FTS5 MATCH queries** — `buildFtsQuery()` helper in `db.js` strips `"'()\r\n\t` from raw input, splits on whitespace, filters tokens <2 chars, wraps each survivor as `"tok"*` prefix match, and AND-joins. Prevents FTS MATCH parser errors from stray quotes/parens while enabling partial-word matches.
|
|
330
|
-
- **`searchSessions()` API client** — New helper in `neural-interface/public/shared/api.js` fronting the hybrid endpoint: `searchSessions({ q, provider, project, limit })`.
|
|
331
|
-
|
|
332
|
-
### Fixed — Claude Sidepanel Plan Mode "Edit Plan" Flow
|
|
333
|
-
- **Plan file reliably materialized at ExitPlanMode time** — Added `extractPlanTextFromMessage(msg)` helper in `neural-interface/public/shared/ui-claude-panel.js` that reads plan text from the in-memory `msg.content[]` text blocks (with legacy `ExitPlanMode.input.plan` fallback) BEFORE DOM insertion. Previously, the new-message capture path ran `extractPlanText(tab)` against `.msg.msg-assistant` before the current message was appended, so the plan-bearing message was missed and `tab.planFilePath` stayed empty — leading to "No plan file found" when clicking **Edit plan**.
|
|
334
|
-
- **Capture gate no longer permanently blocks fallbacks** — All three capture sites (ExitPlanMode detection, dedup path, new-message path) now only set `tab._planContentCaptured = true` when capture yields non-empty content. Previously the flag was set unconditionally, which blocked the later DOM-walk fallbacks from ever running.
|
|
335
|
-
- **`extractPlanText(tab)` accepts plan-card bodies** — The walker now matches `.msg-body, .plan-body` so content rendered via `buildPlanCard` also becomes a valid source.
|
|
336
|
-
- **"Edit plan" click handler hardened** — `noFile()` is guarded by a `card._noFileShown` flag so repeated clicks no longer stack duplicate "No plan file found" status lines. Before giving up, the handler makes one `requestAnimationFrame` retry of the text extraction to catch late-stream DOM renders. Server / network errors from `/api/create-plan` are now surfaced via `console.warn`.
|
|
337
|
-
- **Isolated from Codex and OpenCode sidepanels** — All edits are confined to `ui-claude-panel.js`; the existing `source: 'claude'` routing on `open-plan-editor`, `plan-saved`, and `plan-edit-cancelled` events keeps Codex (`ui-codex-panel.js`) and OpenCode (`ocp/*`) flows untouched.
|
|
338
|
-
|
|
339
|
-
### Fixed — Git UI Hidden in File Explorer for Large-Status Repos
|
|
340
|
-
- **Branch button never appeared next to the project selector on repos with massive `git status` output** — `getGitInfo()` in `neural-interface/server.js` used `execSync('git status --porcelain -u .', ...)` which has a 1 MB default `maxBuffer`. The Synabun repo's status output is ~5.2 MB / ~59,711 lines (driven by ~21k tracked `data/browser-profiles/` cache files), so the call threw `ENOBUFS`, the outer try/catch silently returned `null`, and `updateBranchLabel(null)` in `neural-interface/public/shared/ui-file-explorer.js` hid the `fe-branch-btn` element. Switched the porcelain call to `spawnSync('git', ['status', '--porcelain', '-u', '.'], { maxBuffer: 256 * 1024 * 1024 })` and made `getGitInfo()` return `{ branch, statuses: new Map() }` on a non-zero git exit so the branch button still renders even when status parsing fails.
|
|
341
|
-
- **`/api/git/status` endpoint had the same bug** — Would have broken the git popover the moment the user opened it on the same repos. Applied the identical `execSync` → `spawnSync` swap with a 256 MB `maxBuffer`; returns 500 with stderr if git fails.
|
|
342
|
-
|
|
343
|
-
### Added — Multi-Runtime Skill Compatibility for `/synabun` and `/leonardo`
|
|
344
|
-
- **OpenCode sidepanel skill injection** — The WebSocket `message:send` handler in `neural-interface/server.js` now scans `msg.body.parts` for any text part starting with `/`, runs `maybeInjectSkillPrompt()`, and replaces the part's `text` with the `<skill-instructions>`-wrapped SKILL.md body before POSTing to OpenCode's `/session/<id>/message`. Also handles the legacy `msg.content` shape. Matches the injection behavior previously wired only for Claude Code (`server.js:5568`) and Codex (`server.js:3366`) sidepanels.
|
|
345
|
-
- **Dynamic slash hints in OpenCode panel** — `neural-interface/public/shared/ocp/ocp-panel.js` replaces the hardcoded `SLASH_COMMANDS` array with `BUILTIN_SLASH_COMMANDS` plus a `loadSkillSlashCommands()` loader that fetches `/api/skills` on panel init and merges bundled/global skills into the hint list. `executeSlashCommand()` returns `false` for any registered skill so `handleSend()` falls through to `sendMessage()`; the boolean return from `executeSlashCommand()` is now respected instead of unconditionally returning after slash parse.
|
|
346
|
-
- **Native CLI install for Codex and OpenCode terminals** — New helpers `getCodexPromptsDir()` → `~/.codex/prompts`, `getOpenCodeCommandDir()` → `~/.config/opencode/command` (legacy fallback `~/.opencode/command`), plus `buildCliSkillPrompt(name, target)`, `installSkillToTarget(target, name)`, and `uninstallSkillFromTarget(target, name)` in `server.js`. `buildCliSkillPrompt()` preserves SKILL.md's YAML frontmatter, rewrites `$SKILL_DIR` to the bundled absolute path, and inserts a short orientation banner so CLI agents know module files live in the bundled repo. Claude install continues to copy the full directory; Codex/OpenCode install writes a single `<name>.md` bridge file.
|
|
347
|
-
- **New `/api/skills` endpoints (with legacy aliases)** — `GET /api/skills` now returns `{ dirName, name, description, installed, installedTargets: { claude, codex, opencode } }`. `POST /api/skills/install` `{ name, target }` installs to one runtime; `DELETE /api/skills/install` uninstalls. `SKILL_TARGETS` constant enforces valid targets. Legacy `GET/POST/DELETE /api/claude-code/skills` kept as thin aliases that hard-code `target: 'claude'` for backward compat.
|
|
348
|
-
- **Settings chip row replaces single skills toggle** — `buildSkillsTab()` in `neural-interface/public/shared/ui-settings.js` now renders three independent pill chips per skill (Claude / Codex / OpenCode) driven by `installedTargets`. Chip click posts/deletes `/api/skills/install` with the matching target, updates `.on` state, recomputes the row's `.installed` class based on whether any chip is on, and toasts a per-runtime restart message (`Restart Claude Code / Codex CLI / OpenCode CLI to apply`). Header copy updated to "Slash commands that extend Claude Code, Codex, and OpenCode with specialized capabilities."
|
|
349
|
-
- **Chip styling** — Added `.cc-skill-targets` flex container and `.cc-skill-target` pill rule to `neural-interface/public/shared/styles.css` — ghost border in the off state, accent-filled solid in the on state.
|
|
350
|
-
- **Codex sidepanel slash loader repointed to `/api/skills`** — `neural-interface/public/shared/cdx/cdx-tabs.js` `loadSlashCommands()` now fetches from the new endpoint (legacy URL still works via alias). Keeps Codex sidepanel's skill hints in sync with the new install-state schema.
|
|
351
|
-
|
|
352
|
-
### Fixed — Blank Avatar-Only Row in Claude Code Sidepanel During Streaming
|
|
353
|
-
- **Empty assistant bubble could linger for the full duration of a hidden block** — `handleStreamDelta()` in `neural-interface/public/shared/ui-claude-panel.js` (~L4103) unconditionally created the `msg.msg-assistant` skeleton (avatar + empty `.msg-content`) on the first `content_block_start` event, regardless of block type. It only populated the skeleton for `text` blocks and `thinking` blocks when the effort toggle was on — so a `thinking` block with effort off (or a leading `tool_use` block) left a blank avatar row in the thread until a later block (or the `assistant` event) filled it. Observed in the wild with a ~136-second empty row during a long hidden thinking phase. Fixed by making skeleton creation lazy: compute `isRenderable = blockType === 'text' || (blockType === 'thinking' && _getEffort())` and early-return before appending the `msg` element when the block isn't renderable. `tool_use`-only and effort-off-thinking messages now fall through to `renderAssistant()`'s "new assistant message" branch, which only builds the DOM once real content exists.
|
|
354
|
-
|
|
355
|
-
### Added — Streamlined MCP Installation from GitHub
|
|
356
|
-
- **One-click GitHub installer** — New `POST /api/mcp/install/github` endpoint clones `github.com/owner/repo` into `data/mcp/<name>/repo/`, auto-detects MCP config via a 4-heuristic priority chain, runs dependency install, and returns a prefilled form payload. Supports `https://`, `git@`, and shorthand `owner/repo` URL forms via `parseGithubUrl()`. Paired with `POST /api/mcp/install/detect`, `POST /api/mcp/install/run`, and `GET /api/mcp/install/log?name=foo&tail=N` for re-scan, re-install, and log tailing.
|
|
357
|
-
- **Auto-detection module** — New `neural-interface/lib/mcp-installer.js` (ESM, ~380 LOC) exports `detectServer()` which probes in priority order: (1) `mcp.json` / `smithery.yaml` / `manifest.json` manifests, (2) `package.json` `bin` → `main` → `scripts.start`, (3) `pyproject.toml` entry points (`uv run -m` / `python -m`), (4) `README.md` fenced ```json``` blocks containing `mcpServers`. Relative paths like `./dist/index.js` are rewritten to absolute via `resolveDetectedPaths()` so CLIs launching from a different cwd don't break.
|
|
358
|
-
- **Multi-package-manager dep install** — `installDependencies()` detects `pnpm-lock.yaml` / `yarn.lock` / `bun.lockb` / `package-lock.json` / `uv.lock` / `requirements.txt` and runs the appropriate install + build command, streaming output line-by-line to `data/mcp/<name>/install.log`. 5-minute timeout per command, SIGKILL fallback.
|
|
359
|
-
- **Source-tab UI in Settings > MCP** — `ui-settings.js` now renders `[From GitHub] [From JSON / Command]` tabs inside `#stg-mcp-add-form`. GitHub panel includes URL input, Install button, `runInstall` consent checkbox ("runs package scripts — only install repos you trust"), live progress panel with status line, and a tailing log `<pre>`. `applyDetected()` populates Name/Type/Command/Args/Env rows from the server response; `addForm.dataset.transport` updates to hide irrelevant fields.
|
|
360
|
-
|
|
361
|
-
### Added — Centralized MCP Env Vault
|
|
362
|
-
- **Per-server `env.json`** — Secrets live at `data/mcp/<name>/env.json` (chmod 0600), managed via `GET /api/mcp/env/:name` and `PUT /api/mcp/env/:name`. GET masks values as `****xx` unless `?reveal=1`. PUT merges existing values (skipping masked `****` placeholders so repeat saves don't destroy real secrets) and accepts an optional `platforms` array to immediately re-sync the change to every selected CLI on the same call.
|
|
363
|
-
- **Sync endpoint merges env.json over registry config** — `POST /api/mcp/sync` was refactored to route through a shared `internalMcpSync()` helper in `neural-interface/server.js` and now always overlays `readEnvFile()` onto the incoming config before writing to each CLI. Edits to `env.json` propagate to Claude Code, OpenCode, Codex, and Gemini on the next sync without the user re-entering secrets in the UI.
|
|
364
|
-
|
|
365
|
-
### Added — Auto Profile Registration for MCP Servers
|
|
366
|
-
- **New servers land in every profile** — `PUT /api/mcp/registry/servers/:name` now calls `addServerToAllProfiles()` on create, honoring `registry.profileDefaults = { autoAddNewServers: true, excludeProfiles: ['core'] }`. Paired with `removeServerFromAllProfiles()` on DELETE so `profiles[].servers[]` stays consistent.
|
|
367
|
-
- **Idempotent startup backfill** — `ensureProfileServersBackfilled()` runs on `neural-interface/server.js` module load: sets `profileDefaults` if missing, adds `servers: []` to any profile lacking it, and pushes every existing server name into each non-excluded profile's `servers[]` array. Existing `renderExtServerList()` chip editor in `ui-settings.js` picks the new servers up automatically — the per-profile toggle chips already rendered from `profile.servers[]`, so no UI change was needed for the profile editor.
|
|
368
|
-
- **Purge-on-delete option** — `DELETE /api/mcp/registry/servers/:name?purgeFiles=1` additionally removes the cloned `data/mcp/<name>/` folder (repo + env.json + install log). Default behavior keeps files for recovery.
|
|
369
|
-
|
|
370
|
-
### Added — Sensitive Env Var Masking in MCP Add-Server Form
|
|
371
|
-
- **Auto-detected sensitive keys** — `addEnvRow()` in `ui-settings.js` now defaults rows whose key matches `/token|secret|key|password|api_key/i` to `<input type="password">` and adds an eye-toggle button per row for reveal/hide. New `opts.sensitive` parameter lets callers force the masked type explicitly. CSS additions in `styles.css` cover the eye button + source-tab pill UI + GitHub install progress panel.
|
|
372
|
-
|
|
373
|
-
### Added — `data/mcp/` Folder Scaffold for User-Installed Servers
|
|
374
|
-
- **Folder structure for cloned MCPs** — Created `data/mcp/.gitkeep` (tracked) and `data/mcp/.gitignore` that excludes `*/repo/`, `*/env.json`, and `*/install.log` so cloned upstream source and user secrets never get committed, while keeping the folder visible in git. Per-server layout is `data/mcp/<name>/{repo,env.json,install.log}`.
|
|
375
|
-
|
|
376
|
-
### Added — Browser MCP Selector Cheatsheet Tool
|
|
377
|
-
- **`browser_cheatsheet` tool for on-demand platform guidance** — New `mcp-server/src/tools/browser-cheatsheet.ts` registered in `registerBrowserCoreTools()` via `browser.ts`. Input schema: `platform` enum (`twitter | facebook | tiktok | whatsapp | instagram | linkedin`) + optional `action` (`click | type | fill | scroll | snapshot | extract`). Output: selector cheatsheet for that platform/action. Replaces the per-platform PT/EN/TR selector dumps that were previously inlined into every browser tool's description — net ~15–20 KB (~4–5K tokens) removed from per-session `tools/list` overhead.
|
|
378
|
-
|
|
379
|
-
### Added — Snapshot Modes & Viewport Filtering for `browser_snapshot`
|
|
380
|
-
- **`mode`, `viewport`, `maxChars`, `format` optional params** — `browserSnapshotSchema` in `mcp-server/src/tools/browser-observe.ts` now accepts `mode: 'full' | 'interactive' | 'landmarks'`, `viewport: boolean`, `maxChars: number`, `format: 'text' | 'json'`. Defaults preserve today's behavior. `'interactive'` bypasses the accessibility tree and returns only clickable/fillable elements with stable selector hints (~85% smaller on feed pages). `'landmarks'` filters to `main / nav / banner / region / form / search / heading` for page-structure orientation.
|
|
381
|
-
- **Viewport-aware ax-tree filtering** — Server-side `buildSnapshotResponse()` in `neural-interface/server.js` plus new helpers `getViewportNamedRects()` and `filterTreeByViewport()` drop ax nodes whose names don't match any in-viewport DOM element.
|
|
382
|
-
- **Exported `formatInlineSnapshot()`** — Shared formatter from `browser-observe.ts` so navigate/click/scroll can render an attached snapshot consistently.
|
|
383
|
-
|
|
384
|
-
### Added — `returnSnapshot` Folds Actions and Observations into One Call
|
|
385
|
-
- **`returnSnapshot` optional param on `browser_navigate`, `browser_click`, `browser_scroll`** — Schemas in `browser-navigate.ts` and `browser-interact.ts` now accept `{ mode, selector, viewport, maxChars, format }`. Server endpoints `/api/browser/sessions/:id/navigate`, `…/click`, `…/scroll` call `buildSnapshotResponse()` after the action and merge `{ snapshot, interactive, mode }` into the response. MCP handlers append a `--- Snapshot (<mode>) ---` block to the tool text. One MCP round-trip replaces two.
|
|
386
|
-
- **`ni` client plumbing** — `navigate()`, `click()`, and `scroll()` in `mcp-server/src/services/neural-interface.ts` gained an optional `returnSnapshot` argument.
|
|
387
|
-
|
|
388
|
-
### Added — Selector Auto-Heal via `textHint`
|
|
389
|
-
- **`textHint` optional param on `browser_click` / `browser_fill` / `browser_type` / `browser_hover`** — When the primary selector matches 0 elements, the server retries once via `healWithTextHint()` probing `role=button[name="…"]`, `role=link[name="…"]`, `role=textbox[name="…"]`, `[aria-label="…"]`, `text="…"`, `:has-text("…")` and uses the first candidate that yields exactly one match. Response includes `healed: true`; MCP handlers append `(auto-healed via textHint)` to the success text.
|
|
390
|
-
|
|
391
|
-
### Changed — Trimmed Per-Platform Hints Out of Browser Tool Descriptions
|
|
392
|
-
- **5 bloated descriptions collapsed to ≤280-char one-liners** — `browserClickDescription` (was ~5.5 KB), `browserTypeDescription` (~1.5 KB), `browserFillDescription` (~600 B), `browserScrollDescription` (~1.5 KB), and `browserSnapshotDescription` (~1.8 KB) in `mcp-server/src/tools/browser-interact.ts` and `browser-observe.ts` now describe only generic semantics and point at `browser_cheatsheet` for platform specifics. Schema field descriptions for `selector`, `nthMatch`, and upload's `filePaths` also trimmed. Zero behavior change; tool-list token overhead drops ~4–5K per fresh conversation.
|
|
393
|
-
|
|
394
|
-
### Changed — Browser Selector Hints Now Carry Stable Selectors and Match Context
|
|
395
|
-
- **`getInteractiveHints()` rewrite** — `neural-interface/server.js`. Returns `{ role, text, ariaLabel, placeholder, selector, nth?, rect }`. Stable selector priority: `data-testid` → `data-e2e` → `data-tt` → unique `#id` (filtered against ember/ext/react auto-ids) → `[aria-label="…"]` → `role=<role>[name="…"]` → `tag:has-text("…")` → bare tag. Accepts `{ limit, viewportOnly }` options; the old hard-coded 15-element cap is now configurable.
|
|
396
|
-
- **Shared `resolveInteractLocator()` helper** — Used by the `click`, `fill`, `type`, `hover`, and `select` endpoints. On selector parse error, emits a 400 with ranked hints instead of a 500. On `count === 0` without `textHint`, returns hints ranked by `rankHintsAgainst()` (fuzzy-matches needle text from the attempted selector against hint text/aria/placeholder). On `count > 1` without `nthMatch`, calls new `describeMatches()` which enumerates up to 10 actual matches with stable selectors and nth indices so the caller can pick without blind `nthMatch: 0`.
|
|
397
|
-
- **`formatClickHints()` renders selectors** — `mcp-server/src/tools/browser-interact.ts`. Each hint line now shows `role: "label" → selector [nth:N]` and the error body uses `result.hintsLabel` (e.g. "Try one of these selectors", "Matched elements (pass nthMatch 0..N or switch to these selectors)") so the LLM can retry without re-composing a selector from scratch.
|
|
398
|
-
|
|
399
|
-
## 2026-04-15
|
|
400
|
-
|
|
401
|
-
### Fixed — Browser Session Collision Between CLI and Sidepanel
|
|
402
|
-
- **Sidepanel `browser_navigate` hijacked CLI's active browser session instead of creating its own** — When a CLI Claude Code session had a browser open and the sidepanel called `browser_navigate` without an explicit `sessionId`, `resolveSession()` in `mcp-server/src/services/neural-interface.ts` auto-selected the CLI's session as the only available one (`if (sessions.length === 1)`) and navigated away from whatever the CLI was doing. The ownership model only covered `_loopOwned` (loops) and `_agentOwned` (agents) — interactive CLI sessions had no ownership flag. Fixed by adding `_affinitySessionId` — a per-MCP-process variable that remembers which browser session this process created or used. When `autoCreate` is set (`browser_navigate`) and unowned sessions exist but none have affinity to the current process, a new session is created instead of auto-selecting. Non-autoCreate tools (`browser_click`, `browser_snapshot`) still auto-select on first use, then set affinity.
|
|
403
|
-
|
|
404
|
-
### Changed — Recall Output Always Includes Full Metadata
|
|
405
|
-
- **Tags, related files, and source now always appear in recall results** — Previously `mcp-server/src/tools/recall.ts` conditionally omitted `tags` (when empty array) and `related_files` (when empty array) from recall output, and never included `source`. Now every recall result always shows `Tags: tag1, tag2` (or `Tags: none`), `Files: path1, path2` (or `Files: none`), and `source: user-told|self-discovered|auto-saved` in the header line.
|
|
406
|
-
- `hooks/claude-code/prompt-submit.mjs` recall nudge format updated to match — tags and files lines always present with `none` fallback instead of being conditionally hidden.
|
|
407
|
-
|
|
408
|
-
### Fixed — Post-Plan Action Card Appearing After Implementation Instead of Immediately
|
|
409
|
-
- **Accept/Edit/Compact card rendered too late in sidepanel plan mode** — When Claude exited plan mode via `ExitPlanMode`, the post-plan actions card appeared only after the `result` terminal event in `--print` mode, by which time Claude had already streamed implementation text. Root cause: the `ExitPlanMode` detection block in `renderAssistant()` (~line 3971) only set `_exitPlanPending = true` and deferred rendering to downstream handlers. Fixed by rendering `renderPostPlanActions(tab)` immediately at detection time, capturing plan content and setting `_exitPlanHandled = true` right there. Added `if (tab._exitPlanHandled) return;` guard in `updateToolResult()` to prevent double-rendering. Both downstream capture paths (`_planContentCaptured` dedup and new-message) are now no-ops since the flag is set early.
|
|
410
|
-
|
|
411
|
-
### Added — Browser Session Rule in CLAUDE-template.md Ruleset
|
|
412
|
-
- **New "Browser Sessions" rule requiring new tabs instead of reusing existing ones** — Added a `### Browser Sessions` section to the full ruleset in `neural-interface/templates/CLAUDE-template.md` instructing Claude Code to always create a new browser tab when starting a session, never reusing or navigating an existing tab (which may contain the user's active work, unsaved state, or authenticated sessions). Also added the rule as a one-liner (`- Browser: always create a new tab — never reuse or navigate an existing tab.`) to all 4 condensed rulesets: Cursor, Generic, Gemini, and Codex.
|
|
413
|
-
|
|
414
|
-
### Changed — OpenCode Sidepanel Multi-Question Card Parity with Claude/Codex
|
|
415
|
-
- **Question cards now batch all questions into one interactive card with `Submit (x/y)` progress** — Previously `extractQuestionData()` in `neural-interface/public/shared/ocp/ocp-render.js` only extracted the first question (`input.questions?.[0]`), so multi-question `AskUserQuestion` calls from plan mode only showed one question. Replaced with `extractAllQuestions()` which returns the full array, and rewrote `renderQuestionCard()` to render all questions in a single card — each as its own `.ocp-question-block` with header chip, question text, and clickable options.
|
|
416
|
-
- Multi-select support: questions with `multiSelect: true` use toggle selection with square checkbox indicators and a "Select all that apply" hint
|
|
417
|
-
- Auto-added "Other" option per question reveals a freeform `.ocp-question-other-input` text field
|
|
418
|
-
- `pendingAnswers` object + `updateSubmitState()` tracks progress as `Submit (x/y)` button text
|
|
419
|
-
- Submit locks all buttons/inputs, changes text to "Submitted", sends full answers object
|
|
420
|
-
- **SSE handler formats batched answers as structured text** — `tool.start` case in `neural-interface/public/shared/ocp/ocp-tabs.js` now receives the `pendingAnswers` object instead of a single string. Single answers send the value directly; multiple answers format as `"Here are my answers:\n- Q: A\n- Q: A"` via `sendMessage()`.
|
|
421
|
-
- **New CSS classes for multi-question layout** — `neural-interface/public/shared/ocp/ocp-styles.js` adds `.ocp-question-intro`, `.ocp-question-block`, `.ocp-question-header-chip`, `.ocp-question-multi-hint`, `.ocp-question-other`, `.ocp-question-other-input`, and `.ocp-question-option.multi` (square checkbox variant).
|
|
422
|
-
|
|
423
|
-
### Fixed — OpenCode Plan Mode Premature and Duplicate PLAN COMPLETE Cards
|
|
424
|
-
- **Multiple PLAN COMPLETE cards stacked instantly without the model finishing its plan** — `message.updated` and `message.completed` SSE events fire per-step in OpenCode's agentic loop, each triggering `showPostPlanUI()` in `neural-interface/public/shared/ocp/ocp-tabs.js`. This caused 3+ PLAN COMPLETE cards to appear almost immediately as intermediate tool turns completed. Removed both premature trigger blocks and replaced with dual detection: `tool.start` handler now watches for `ExitPlanMode` / `plan_exit` tool calls (Anthropic models) and immediately fires `capturePlanContent()` + `showPostPlanUI()` with a `_exitPlanDetected` flag. A new `session.status` case handles `idle` status as a fallback for non-Anthropic models (e.g., minimax-m2.7) that don't call ExitPlanMode — it only triggers plan completion if `_exitPlanDetected` is still false. Flag resets in `setTabMode()` when re-entering plan mode.
|
|
425
|
-
|
|
426
|
-
### Fixed — OpenCode Reading CLAUDE.md Instead of AGENTS.md
|
|
427
|
-
- **OpenCode's Claude Code compatibility mode loaded `CLAUDE.md`, causing slow tools and confused model behavior** — OpenCode falls back to reading `CLAUDE.md` when present, which injected Claude Code-specific instructions (AskUserQuestion mandates, subagent spawning, memory obligation hooks, explore agents) into the OpenCode session. This caused the model to spawn unnecessary subagents, attempt memory tool calls it didn't have, and respond slowly due to instruction overhead. Fixed by adding `OPENCODE_DISABLE_CLAUDE_CODE: '1'` to the spawn env in `neural-interface/server.js`, ensuring OpenCode only reads `AGENTS.md`.
|
|
428
|
-
|
|
429
|
-
### Fixed — Claude Code Sidepanel Post-Plan Card Pushed Off-Screen by Streaming
|
|
430
|
-
- **Accept/Edit/Compact action card buried below streamed content after plan presentation** — After `renderPostPlanActions()` placed the post-plan card, subsequent `renderAssistant()` calls for streamed implementation text appended new `.msg` elements below it, pushing the action buttons off-screen. Fixed by adding reposition blocks at both exit points of `renderAssistant()` in `neural-interface/public/shared/ui-claude-panel.js` — the dedup path and the new-message path each check if a `.post-plan-card` exists and move its parent `.msg` element to `$msgs.lastElementChild` position if displaced.
|
|
431
|
-
|
|
432
|
-
### Fixed — Plan Mode Implements Before User Approval (ExitPlanMode Process Kill)
|
|
433
|
-
- **Claude continued executing Edit/Read tool calls after calling `ExitPlanMode` in sidepanel plan mode** — In `--print` mode, plan mode is simulated via a prompt prefix (not native `EnterPlanMode`), so `ExitPlanMode` auto-approves instantly and Claude continues generating tool calls in the same turn. The "PLAN COMPLETE" card appeared but Claude had already made file changes. Fixed by adding server-side process kill on `ExitPlanMode` detection in `processEvent()` inside `spawnProc()` (`neural-interface/server.js`): when an `assistant` event contains an `ExitPlanMode` tool_use, the server forwards the event to the client, sets an `exitPlanKilled` flag to suppress any buffered events, kills the CLI process, and sends `done`. No client-side changes needed — existing `renderAssistant()` ExitPlanMode detection and `finishTab()` already handle the UI correctly.
|
|
434
|
-
|
|
435
|
-
### Fixed — AskUserQuestion Hang in OpenCode Sidepanel
|
|
436
|
-
- **Question tool rendered as generic tool card instead of interactive question card** — OpenCode SSE emits `message.part.updated` events for tool calls, not `tool.start` events. The `isQuestionTool()` check only existed in the `tool.start` handler (`ocp-tabs.js:817`), which OpenCode never triggers. The `message.part.updated` handler at line 771 sent all tool-type parts through `renderAssistantPayload()` → `renderToolCard()` with no question detection, causing AskUserQuestion to render as a raw JSON card with a "running" badge instead of an interactive question card. Additionally, `sendMessage()` guard checked `tab.running` (true during the turn) and silently dropped any answer attempt. Fixed by intercepting question tools in the `message.part.updated` handler: calls `abortMessage()` to clear the in-flight request and `tab.running` state, then renders an interactive `renderQuestionCard()` with an `onAnswer` callback that sends the user's selection as a new message.
|
|
437
|
-
|
|
438
|
-
### Added — Syntax Highlighting for Code Blocks
|
|
439
|
-
- **highlight.js integration with GitHub Dark Dimmed theme** — Added `loadHighlightJs()` in `neural-interface/public/shared/ocp/ocp-render.js` that lazy-loads highlight.js from jsDelivr CDN on panel init. `highlightCodeBlock()` and `highlightCodeBlocks()` apply syntax highlighting to all `<pre>` blocks. CSS token colors for `.hljs-keyword`, `.hljs-string`, `.hljs-comment`, `.hljs-function`, etc. added to `ocp-styles.js`.
|
|
440
|
-
|
|
441
|
-
### Added — File Path Linkification
|
|
442
|
-
- **Clickable file path links with copy-on-click** — Added `linkifyFilePath()` using regex `/\/(?:[a-zA-Z0-9_.-]+\/)*[a-zA-Z0-9_.-]+\.[a-zA-Z0-9]{1,10})(?::(\d+))?(?::(\d+)))?/g` to convert paths like `/path/to/file.ts:123` to `<a class="ocp-file-link">` elements. Click handler copies path to clipboard and shows "Copied!" feedback for 1.5s. Styled with dashed underline and hover state. `linkifyFilePaths()` walks text nodes in assistant messages and tool results, `attachFileLinkHandlers()` manages click behavior.
|
|
443
|
-
|
|
444
|
-
### Added — Language Labels on Code Blocks
|
|
445
|
-
- **Language name badge on `<pre>` blocks** — Added `detectCodeLanguage()`, `attachLanguageLabel()`, and `attachLanguageLabels()` in `neural-interface/public/shared/ocp/ocp-render.js`. Detects language from code block class (e.g., `language-typescript` → `typescript`), falls back to auto-detect via highlight.js. Language aliases map `js→javascript`, `py→python`, `sh→bash`, `yml→yaml`, etc. Creates `.ocp-lang-label` pill badge in top-left corner of `<pre>`. Post-process chain in `postProcessRenderedHtml()` runs language labels → highlighting → linkification in order.
|
|
446
|
-
|
|
447
|
-
### Added — Copy Button Hover-Only Visibility
|
|
448
|
-
- **Copy button shows on hover with `.copied` green state** — Changed `.ocp-copy-btn` in `neural-interface/public/shared/ocp/ocp-styles.js` to start at `opacity: 0` (previously always visible on `pre:hover`). Shows at `opacity: 1` on `pre:hover`. Added `.ocp-copy-btn.copied` state with green text color (`#4ade80`) and `border-color: rgba(74,222,128,0.3)`. `attachCopyButton()` in `ocp-render.js` updated to add `.copied` class with 1.5s timeout, replacing the text-change approach.
|
|
449
|
-
|
|
450
|
-
### Added — Throttled Streaming Rendering at 30fps
|
|
451
|
-
- **32ms throttle prevents full markdown re-parse on every SSE delta** — Added `throttle(fn, ms)` helper in `neural-interface/public/shared/ocp/ocp-render.js`. Modified `appendStreamChunk()` to accumulate raw text in `dataset.rawText` and only call `setAssistantHtml()` through the throttle at 32ms intervals (~30fps). Previously re-rendered full markdown + all post-processing on every SSE chunk which was wasteful. `finalizeStreamingMessage()` now does a final full render after streaming completes to ensure all post-processing (highlighting, linkification, language labels) is applied.
|
|
452
|
-
|
|
453
|
-
### Added — Real-time Thinking Block Streaming
|
|
454
|
-
- **`appendThinkChunk()` updates thinking content live during streaming** — Existing `appendThinkChunk()` in `neural-interface/public/shared/ocp/ocp-render.js` accumulates thinking text in `dataset.thinkText` and re-renders the think block + accumulated response text on each chunk. Think block stays `open` during streaming so users see thinking content as it arrives. `finalizeStreamingMessage()` cleans up `dataset.thinkText` and removes streaming state. `finalizeStreamingMessage()` now also calls `setAssistantHtml()` for final render.
|
|
455
|
-
|
|
456
|
-
## 2026-04-14
|
|
457
|
-
|
|
458
|
-
### Changed — Codex Sidepanel Verbose Rendering and Protocol Coverage
|
|
459
|
-
- **Reworked the modular Codex sidepanel renderer to keep operational detail inside collapsible containers** — `neural-interface/public/shared/cdx/cdx-render.js` now builds nested fold sections for `commandExecution`, `mcpToolCall`, `dynamicToolCall`, `fileChange`, and collaboration items, so raw arguments, tool results, command output, and diffs stay hidden until expanded instead of spilling directly into the transcript.
|
|
460
|
-
- `neural-interface/public/shared/cdx/cdx-requests.js` now falls back to a generic request card for unsupported server requests instead of hard-failing immediately, keeping payload inspection inside a dedicated card with explicit actions.
|
|
461
|
-
- `neural-interface/public/shared/cdx/cdx-tabs.js` and `neural-interface/public/shared/cdx/cdx-panel.js` now persist card expansion state through `scheduleThreadSnapshotSave()` and restore it in `renderStoredTranscript()`, so verbose sections survive transcript replay instead of resetting to a blunt collapsed/default state.
|
|
462
|
-
- `neural-interface/server.js` now normalizes official `collabToolCall` items to the sidepanel’s collaboration renderer shape and the panel now surfaces additional App Server notifications including `fuzzyFileSearch/*`, `windowsSandbox/setupCompleted`, `thread/archived`, and `thread/unarchived`.
|
|
463
|
-
|
|
464
|
-
### Changed — Codex Sidepanel Sanitized Commentary and Shell Summaries
|
|
465
|
-
- **Replaced noisy transcript text with action-oriented summaries** — `agentMessage.phase` commentary in `neural-interface/public/shared/cdx/cdx-render.js` now renders as compact status-style text instead of echoing long internal commentary into the visible transcript.
|
|
466
|
-
- **Hidden raw `/bin/zsh -lc ...` wrappers behind a dedicated fold** — `summarizeCommandExecution()` now turns shell-heavy command cards into readable labels like “Searching within files”, “Reading file contents”, and “Inspecting git changes”, while the full raw shell invocation is moved into a closed `Shell command` section inside the card body.
|
|
467
|
-
|
|
468
|
-
### Fixed — File Change Diff Fold Showing Blank Content
|
|
469
|
-
- **Nested file-change diff sections could open with an empty body** — When a `fileChange` item completed while its parent card was collapsed, the lazy-flush path in `flushCardBody()` did not rebuild `outputBuf` from `_lastItem`. Updated `neural-interface/public/shared/cdx/cdx-render.js` so the diff is reconstructed from `aggregatedOutput` or `changes[*].diff` / `patch` / `content` / `output` before the fold is revealed.
|
|
470
|
-
|
|
471
|
-
### Fixed — File Change Cards Missing Human-Readable Change Description
|
|
472
|
-
- **Expanded file-change cards did not explain what changed before the raw patch** — `ensureFileChangeState()` in `neural-interface/public/shared/cdx/cdx-render.js` now adds a `Files changed` section, and `resolveFileChangeSummary()` / `describeFileChange()` produce per-file descriptions plus diff stats so the user can see which file changed and what kind of edit happened before opening `Verbose diff`.
|
|
473
|
-
|
|
474
|
-
### Fixed — `claude mcp add` Command Fails on Windows PowerShell
|
|
475
|
-
- **`--` separator and `-e KEY="VALUE"` quoting mangled by Windows cmd.exe shim** — On Windows, `claude` is a `.cmd` batch file shim created by npm. When PowerShell calls it, arguments pass through `cmd.exe` which mangles the `--` separator and quoted `-e` values, causing Commander.js to miss the `node` positional argument (`error: missing required argument 'commandOrUrl'`). Fixed by removing the `--` separator entirely (placing `node "path"` as positional args right after the server name) and quoting the full key=value pair (`-e "KEY=VALUE"` instead of `-e KEY="VALUE"`). Updated across 5 files: `onboarding.html` `getMcpSetup()`, `server.js` `/api/claude-code/integrations` and `/api/system` endpoints, `README.md`, `CLAUDE-template.md`, and `CLAUDE.md`.
|
|
476
|
-
|
|
477
|
-
### Fixed — Codex Sidepanel Image Attachments Not Rendering in Chat Area
|
|
478
|
-
- **Attached images invisible in conversation after sending** — When a user attached an image (via attach button, clipboard paste, drag-drop, or whiteboard) and sent it in the Codex sidepanel, the image was delivered to Codex via `turnParams.local_images` but never appeared in the chat render area. The Codex panel relied on server-echoed `userMessage` items via `item/started` notifications, but the echoed item's `content` array only contained `[{ type: 'text' }]` — no image items. Unlike the Claude panel which calls `appendUser()` with images immediately on send, the Codex panel had no client-side image rendering.
|
|
479
|
-
- Added `appendLocalUserMessage()` in `cdx/cdx-tabs.js` — creates a user message shell via `createMessageShell('user', 'User')` and renders `localImage` + text content items through the existing `renderUserMessageContent()` pipeline, tagged with `.cxp-local-user-msg` for dedup. Called from `sendPrompt()` and `advanceQueue()` after `dispatchPrompt()` succeeds, only when images are attached.
|
|
480
|
-
- Added server echo dedup + enrichment in `handleNotify()` — on `item/started` for `userMessage`, removes the `.cxp-local-user-msg` element and injects `_pendingSendImages` as `localImage` content items into `params.item.content`. Fallback injection in `item/completed` if not consumed at `item/started`.
|
|
481
|
-
- Added server-side enrichment in `server.js` for history/resume — `lastTurnLocalImages` tracks persisted image paths per turn, `sanitizeCodexNotifyParams()` injects `localImage` content items with file paths into forwarded `userMessage` notifications, cleared on `turn/completed`. Images served via existing `/api/images/file/:filename` route.
|
|
482
|
-
|
|
483
|
-
### Fixed — Stale `awaitingPermission` Permanently Breaks Claude Skin Chat After AskUserQuestion
|
|
484
|
-
- **Sending a new prompt after an unanswered AskUserQuestion froze the chat permanently** — When a Claude skin process exited (code 0, `--print` mode) while `awaitingPermission` was `true` (AskUserQuestion shown), and the user sent a new prompt instead of answering, the flag was never reset. `killProc()` returned early when `activeProc` was already `null`, and `spawnProc()` never explicitly cleared the flag. This caused every subsequent process's close handler to suppress the `done` event via the `if (!awaitingPermission)` guard, permanently breaking the chat UI. Fixed by adding unconditional `awaitingPermission = false; lastPermissionDenials = null;` in `spawnProc()` after `killProc()` in `neural-interface/server.js`.
|
|
485
|
-
|
|
486
|
-
### Fixed — Session Leak Scanner Broadcasting Redundant `session:leaks` Events
|
|
487
|
-
- **Leak dedup hash changed every ~60 seconds due to age-dependent descriptions** — The `setInterval` leak scanner stripped `ageMs` from the hash but left `description` intact, which embedded `${Math.round(ageMs / 60000)}m old`. Each time the minute counter ticked, the description changed, the hash changed, and a new `session:leaks` broadcast was emitted. Fixed by also stripping `description` from the hash computation in `neural-interface/server.js`, so broadcasts only fire when the actual leak set changes.
|
|
488
|
-
|
|
489
|
-
### Fixed — Orphan Timer Firing on Dead Processes After Natural Exit
|
|
490
|
-
- **30-minute orphan kill timer persisted after process already exited** — When a Claude skin process was orphaned (WebSocket closed while process running) and then exited naturally (code 0), the orphan entry and its `killTimer` remained in `_orphanedProcs` for the full 30-minute grace period. Fixed by cleaning up the orphan entry (clearing `killTimer`, deleting from `_orphanedProcs`) in the process close handler in `neural-interface/server.js`.
|
|
491
|
-
|
|
492
|
-
### Fixed — Codex Sidepanel Compaction Fails on Restored Threads
|
|
493
|
-
- **Clicking `compact` could return `Thread not found` instead of compacting the active conversation** — `compactThread()` in `neural-interface/server.js` now always resumes the target Codex thread before calling `thread/compact/start`, fixing the mismatch where the sidepanel had hydrated a saved thread into the UI but the Codex app-server had not activated it as the live thread yet.
|
|
494
|
-
- `applySocketError()` in `neural-interface/public/shared/cdx/cdx-tabs.js` now clears the `compacting` UI state and sets an error status when the compact request is rejected, so the button no longer gets stuck in the `compacting` state after a failed attempt.
|
|
495
|
-
|
|
496
|
-
### Fixed — Codex Sidepanel SynaBun Styling False Positives on Informational Lists
|
|
497
|
-
- **Regular assistant text wrapped in green SynaBun interactive containers** — `isSynaBunChoicePrompt()` in `neural-interface/public/shared/cdx/cdx-render.js` joined all extracted option labels into a single string and ran `SYNABUN_CHOICE_LABEL_RE` once against the combined result. A single generic match like "DONE" (matching `\bDone\b`) was enough to flag the entire list as a SynaBun interactive prompt. Combined with `isLikelyInteractivePrompt()` short-circuiting on line 144 — bypassing sanity checks for option count, question mark ending, and label length — informational markdown lists with a bold label like **DONE** or **Back** were converted into green SynaBun branded button cards. Fixed by testing each label individually and requiring at least 2 matches before triggering SynaBun branding.
|
|
498
|
-
|
|
499
|
-
### Fixed — Codex Sidepanel Multi-Question Plan Input Flow
|
|
500
|
-
- **Plan-phase `request_user_input` cards now enforce the same answer-then-submit flow Claude uses** — `neural-interface/public/shared/cdx/cdx-requests.js` now renders all requested questions inside a single grouped card, keeps the action blocked until every question is answered, and shows live `Submit (x/y)` progress so Codex no longer presents plan questions as loosely independent selections.
|
|
501
|
-
- Choice questions now inject a client-side `Other` path with inline text entry, and the submitted payload still matches the expected `result: { answers: { ... } }` structure when the user confirms.
|
|
502
|
-
- `neural-interface/public/shared/cdx/cdx-styles.js` adds the grouped ask-card, inline input, and explicit submit-bar styling, while `neural-interface/public/shared/cdx/cdx-tabs.js` updates blocking-status text to `Answer N questions, then submit` so the panel chrome reflects the new interaction model.
|
|
503
|
-
|
|
504
|
-
### Added — Universal MCP Server Add Form with Smart Paste and Multi-Platform Sync
|
|
505
|
-
- **Replaced the minimal 3-field "Add Server" form with a full smart-paste + guided form** — `neural-interface/public/shared/ui-settings.js` now exposes a paste textarea at top that auto-detects 5 config formats (Claude Desktop JSON, named server object, bare `{ command, args }`, OpenCode `{ type: "local", command: [...] }`, and plain command strings like `npx -y @scope/server /path`). `parseMcpPaste()` auto-populates all form fields and derives server names from npm scope packages.
|
|
506
|
-
- Expanded form fields: Name + Type dropdown (toggles between stdio command/args and SSE/HTTP URL views via `data-transport` attribute), tag-style arg chips (Enter to add, click x to remove), key-value environment variable rows with add/remove, and platform registration checkboxes
|
|
507
|
-
- `GET /api/mcp/platforms` endpoint in `neural-interface/server.js` detects installed platforms by checking `~/.claude.json`, `~/.config/opencode/`, `~/.codex/`, `~/.gemini/` — pre-checks discovered platforms, dims unavailable ones
|
|
508
|
-
- `POST /api/mcp/sync` endpoint writes server config to each platform's native format: Claude Code (`~/.claude.json` `mcpServers`), OpenCode (via `persistMcpToOpencodeConfig()`), Codex (`~/.codex/config.toml` via `tomlUpsertSection()`), Gemini (`~/.gemini/settings.json` `mcpServers`)
|
|
509
|
-
- `DELETE /api/mcp/sync` endpoint removes server from all platform configs on delete — also wired into the existing external server delete handler
|
|
510
|
-
- Per-platform sync feedback shown inline after save (checkmark/x/skip icons per platform)
|
|
511
|
-
- 14 new CSS component classes in `styles.css`: `.stg-mcp-paste-zone` (dashed monospace textarea), `.stg-mcp-arg-chips`/`.stg-mcp-arg-chip` (flex-wrap removable chips), `.stg-mcp-env-rows`/`.stg-mcp-env-row` (key-value pairs), `.stg-mcp-platform-checks`/`.stg-mcp-platform-check` (custom checkboxes with dim state), `.stg-mcp-form-label`, `.stg-mcp-sync-result` (ok/err/skip badges), and `data-transport` visibility toggles
|
|
512
|
-
|
|
513
|
-
### Changed — MCP Settings Tab Visual Refinements
|
|
514
|
-
- **Modernized toggle matrix appearance and fixed layout issues** — `neural-interface/public/shared/styles.css` toggle cells redesigned from filled circles to 16px rounded squares with CSS checkmark pseudo-elements on active state. Tool count badges and "Always On" chips changed from pill shape (`border-radius: 999px`) to subtle rectangles (`border-radius: 4px`). Removed total tools count from profile column headers. Reduced header height from 52px minimum to 32px with tighter padding. Made the add-column (`+` button) sticky with opaque background so it remains visible during horizontal scroll.
|
|
515
|
-
- Fixed sticky column bug where tool group names didn't scroll with horizontal scroll — `display: flex` on `<td>` was overriding `display: table-cell`, breaking `position: sticky`. Fixed by wrapping td contents in `<div class="stg-mx-label-inner">` and applying flex there instead
|
|
516
|
-
- Fixed CSS specificity bug where tool names lacked padding — `#stg-mcp-matrix td { padding: 0 }` (specificity 0,1,1) was overriding `.stg-mx-label-td { padding: 5px 14px }` (specificity 0,1,0). Resolved by upgrading selector to `#stg-mcp-matrix .stg-mx-label-td` (specificity 0,2,0)
|
|
517
|
-
- Widened label column from 132px to 148px with responsive breakpoints at 134px and 120px
|
|
518
|
-
|
|
519
|
-
### Fixed — Browser Session Isolation Race Condition Between Concurrent Agents/Loops
|
|
520
|
-
- **A second agent/loop could invade an already-running agent's browser session** — When multiple agents or automations launched concurrently, the ownership flags (`_agentOwned`, `_loopOwned`) on browser sessions were set too late, leaving windows where `findReusableBrowserSession()` and the MCP-side `resolveSession()` listing path saw active sessions as unowned and grabbed them.
|
|
521
|
-
- `POST /api/agents/launch` in `neural-interface/server.js` now sets `_agentOwned = '__pending__'` immediately after `createBrowserSession()`, before calling `launchAgentController()` — closes the race window where ownership was deferred to a background async IIFE
|
|
522
|
-
- `POST /api/loop/launch` in `neural-interface/server.js` now generates `terminalSessionId` earlier and calls `_loopOwned.add()` immediately after session acquisition, before any `await createSessionTab()` / `await _switchSessionTab()` calls that created a TOCTOU window
|
|
523
|
-
- `findReusableBrowserSession()` in `neural-interface/server.js` now cross-checks the `agentRegistry` for running agents claiming a session — catches any remaining race where `_agentOwned` hasn't been set yet on the session object
|
|
524
|
-
- `GET /api/browser/sessions` in `neural-interface/server.js` now includes an `agentRegistry` fallback in the `agentOwned` API response field, so the MCP-side `resolveSession()` ownership filter sees accurate ownership during the race window
|
|
525
|
-
|
|
526
|
-
## 2026-04-13
|
|
527
|
-
|
|
528
|
-
### Changed — MCP Settings Tab: Toggle Matrix Replaces Drag-and-Drop Pills
|
|
529
|
-
- **Replaced two-column drag-and-drop pill system with a toggle matrix** — The MCP Profiles settings tab previously required users to drag tool group pills from a left sidebar into profile card dropzones on the right. This became unwieldy as profiles and tool groups grew. Replaced with a permission-grid pattern: tool groups as rows, profiles as columns, each cell is a clickable toggle circle. Entire mapping visible at a glance with one-click assignment.
|
|
530
|
-
- `buildMcpTab()` in `ui-settings.js` — removed two-column `stg-mcp-columns` layout, replaced with single-view containing `#stg-mcp-matrix-wrap` (scrollable table container), always-on footer, and external servers section
|
|
531
|
-
- `renderMcpGroupList()`, `renderMcpServerList()`, `renderMcpProfiles()` — all three replaced by single `renderMcpMatrix()` building a `<table>` with `<thead>` profile column headers and `<tbody>` toggle rows, plus `renderAlwaysOn()` and `renderExtServerList()` helpers
|
|
532
|
-
- Profile column headers show active indicator dot, tool count badge, and hover-reveal delete button. Click name to activate profile
|
|
533
|
-
- Inline profile creation via `+` button at end of column headers — click transforms to text input, Enter creates, Escape cancels
|
|
534
|
-
- Row labels use `position: sticky; left: 0` so group names stay visible during horizontal scroll
|
|
535
|
-
- Always-on groups (Memory, Categories, Sync, Loop, Profile) shown as compact read-only chips below the matrix instead of locked rows
|
|
536
|
-
- CSS in `styles.css` — removed all `.stg-mcp-columns`, `.stg-mcp-chip-*`, `.stg-mcp-dropzone`, `.dragging` styles (~130 lines). Added `.stg-mx-*` matrix styles (~160 lines): sticky labels, toggle circles with filled/empty states + hover glow, column highlight for active profile, inline input, separator rows
|
|
537
|
-
|
|
538
|
-
### Fixed — Escape Key in Inline Profile Input Closing Settings Overlay
|
|
539
|
-
- **Escape key bubbled up from inline profile name input** — When pressing Escape to cancel inline profile creation in the MCP matrix, the keydown event propagated to the settings overlay's own Escape handler, closing the entire settings dialog. Added `e.stopPropagation()` and `e.preventDefault()` to both Enter and Escape keydown handlers in the inline input listener within `renderMcpMatrix()`.
|
|
540
|
-
|
|
541
|
-
## 2026-04-12
|
|
542
|
-
|
|
543
|
-
### Fixed — TDZ Crash in `recall.ts` Prevents Codex MCP Handshake
|
|
544
|
-
- **Temporal Dead Zone crash on MCP startup** — `export const recallSchema = buildRecallSchema()` executed at module level, calling `getRecallDefaults()` which referenced `RECALL_DEFAULTS`. But `RECALL_DEFAULTS` was defined *after* the module-level call (line 111 vs line 63 in `mcp-server/src/tools/recall.ts`). When `display-settings.json` was missing (wrong data directory), the catch block spread `{ ...RECALL_DEFAULTS }` before initialization, triggering a `ReferenceError`. Moved `RecallDefaults` interface, `RECALL_DEFAULTS` constant, and `getRecallDefaults()` function above `buildRecallSchema()` so they're initialized before the module-level schema export evaluates.
|
|
545
|
-
|
|
546
|
-
### Changed — Codex MCP Server Environment Configuration
|
|
547
|
-
- **Added `env` section to `~/.codex/config.toml`** — The `[mcp_servers.SynaBun]` registration was missing environment variables, causing Codex to spawn the MCP server without `MEMORY_DATA_DIR`. This made `config.dataDir` resolve to `mcp-server/data/` instead of `mcp-data/`, so `display-settings.json` and `active-profile.json` weren't found. Added `env = { DOTENV_PATH, SYNABUN_DATA_HOME, MEMORY_DATA_DIR }` pointing to the correct project paths.
|
|
548
|
-
|
|
549
|
-
### Fixed — Codex Sidepanel Session Rename Non-Functional
|
|
550
|
-
- **Clicking session label or pencil icon did nothing** — `renameActiveSession()` in `cdx/cdx-tabs.js` had an early-exit guard `if (!_threadId || _running || !_connected) return` that silently blocked rename when no Codex thread existed yet, when a task was running, or when the WebSocket wasn't connected. Removed the guard entirely — rename now always works. When a thread exists and is connected, the rename goes through the WebSocket `thread_rename` call; otherwise it applies locally via `setSessionLabel()`, matching how the Claude Code panel handles it.
|
|
551
|
-
- **Rename button permanently grayed out** — `syncSessionControls()` set `rename.disabled = !_threadId || _running || _startingThread || !_connected`, disabling the pencil button under the same conditions. Changed to `rename.disabled = false` so the button is always interactive.
|
|
552
|
-
- **Missing event handlers** — Added `placeholder` attribute, `dblclick` stopPropagation, and compact event listener style to match the Claude Code panel's `renameSession()` implementation.
|
|
553
|
-
|
|
554
|
-
### Fixed — Codex Sidepanel Project Dropdown Showing Only One Project
|
|
555
|
-
- **Duplicate `_projects` state across two modules** — Both `cdx-panel.js` and `cdx-tabs.js` had their own `_projects`, `_projectsLoaded`, `_projectsPromise` variables and separate `ensureProjectsLoaded()` functions. Only `cdx-panel.js`'s version was called (from `toggleCodexPanel`), but `renderProjects()` (imported from `cdx-tabs.js`) read `cdx-tabs.js`'s `_projects` — which was always `[]`. The single project that appeared was injected by `projectItemsForValue()` from the current tab's project value.
|
|
556
|
-
- **Consolidated to single source of truth** — Exported `ensureProjectsLoaded` from `cdx-tabs.js` (the authoritative owner of `_projects`, `projectItems()`, `renderProjects()`). Removed all duplicate state from `cdx-panel.js`: `_projects`, `_projectsPromise`, `_projectsLoaded` variables, `ensureProjectsLoaded()`, `projectItems()`, `projectItemsForValue()`, `basenamePath()`, and the unused `fetchProjects` import.
|
|
557
|
-
|
|
558
|
-
### Changed — Unified Context Gauge Style Across Claude and Codex Panels
|
|
559
|
-
- **Replaced multi-section colored gauge with OpenCode-style single-fill bar** — Both the Claude panel (`ui-claude-panel.js`) and Codex panel (`cdx/cdx-styles.js`, `cdx/cdx-tabs.js`, `cdx/cdx-panel.js`) previously rendered context usage as multiple colored gauge sections (cache-read=blue, cache-write=purple, input=green) with urgency data-attributes, glow effects, and a per-section tooltip system. Replaced with a single `cp-ctx-fill` / `cxp-ctx-fill` div filling left-to-right, matching the OpenCode panel's cleaner approach.
|
|
560
|
-
- Fill color changes by threshold: warm gray default (`rgba(232,224,220,0.18)`), amber at >60%, red at >80%
|
|
561
|
-
- Label overlaid left-aligned: `42k / 200k ctx` or `context pending` when no data
|
|
562
|
-
- Tooltip details preserved via `title` attribute
|
|
563
|
-
- Removed ~100 lines of gauge-section, urgency-glow, and floating tooltip CSS/JS from each panel
|
|
564
|
-
|
|
565
|
-
### Changed — OpenCode Sidepanel Context Gauge Accuracy & Caching
|
|
566
|
-
- **Exact context usage with model-specific windows** — Replaced the hardcoded `MAX_CTX = 200000` in `neural-interface/public/shared/ocp/ocp-panel.js` `syncTokens()` with model-specific context window lookup via `getModelContextWindow()`. Supports GPT-4 (8k), GPT-4o (128k), Claude 3 (200k), Gemini (128k), and falls back to 200k only for unknown models. The gauge now shows `4k / 8k ctx` instead of `4k / 200k ctx` when using an 8k model.
|
|
567
|
-
- **Full token breakdown capture** — Added `normalizeThreadTokenUsage()` and `normalizeTokenBreakdown()` in `neural-interface/public/shared/ocp/ocp-tabs.js` to normalize both camelCase (`inputTokens`) and snake_case (`input_tokens`) fields from OpenCode server events. The tab state now stores `threadTokenUsage` with `inputTokens`, `outputTokens`, `cachedInputTokens`, `cacheCreationInputTokens`, and `reasoningOutputTokens`.
|
|
568
|
-
- **Cached tokens included in gauge calculation** — Added `resolveContextInputTokens()` which computes exact context used as `input + cached_read + cache_write` tokens (matching the Codex panel's calculation). Previously only raw `inputTokens` were counted, under-reporting actual context consumption when prompt caching was active.
|
|
569
|
-
- **Token usage persists across tab switches and reloads** — Updated `saveTabs()` and `restoreTabs()` in `ocp-tabs.js` to serialize and deserialize `threadTokenUsage`. When switching tabs, the gauge immediately restores the selected tab's last-known usage instead of showing "context pending".
|
|
570
|
-
- **Rich tooltip on hover** — Added a detailed tooltip to the gauge label in `ocp-panel.js` showing: percentage used, input tokens, cached read, cache write, output tokens, reasoning tokens, and context window size. Added custom tooltip CSS in `ocp-styles.js` with a dark floating panel styled to match the sidepanel chrome.
|
|
571
|
-
|
|
572
|
-
## 2026-04-10
|
|
573
|
-
|
|
574
|
-
### Fixed — OpenCode Sidepanel Stop Button and ESC Key Non-Functional
|
|
575
|
-
|
|
576
|
-
- **Stop button clicked but task kept running** — `abortMessage()` sent `message:abort` via fire-and-forget `sendWs()`, but `sendMessage()` was still awaiting `requestWs('message:send', ...)` with a 360-second timeout. The UI toggled `tab.running = false` but the underlying promise hung until OpenCode's synchronous endpoint returned or timed out, causing the agent to keep working.
|
|
577
|
-
- **Client WS layer** (`ocp-ws.js`) — Added `rejectPending(id, reason)` export to manually reject a specific pending request by ID. Modified `requestWs()` to expose `.requestId` property on the returned promise for cancellation.
|
|
578
|
-
- **Client abort flow** (`ocp-tabs.js`) — Added `_activeSendRequestId` tracking. `sendMessage()` stores `sendPromise.requestId` before awaiting. `abortMessage()` calls `rejectPending(_activeSendRequestId, 'Aborted by user')` to immediately reject the pending promise. Catch block skips error rendering for user-initiated aborts since `abortMessage()` already cleaned up the UI.
|
|
579
|
-
- **ESC key handler** (`ocp-panel.js`) — Added Escape handling on both the input element (inside existing keydown listener) and a panel-level `keydown` listener that catches ESC anywhere in the panel, both calling `abortMessage()` with `stopPropagation()`.
|
|
580
|
-
- **Server-side fetch cancellation** (`server.js`) — Replaced `ocpProxy()` call for `message:send` with inline `fetch()` using a per-session `AbortController` tracked in `_sendAbortControllers` Map. `message:abort` handler calls `sendAc.abort()` before forwarding to OpenCode. WS close handler cleans up all controllers.
|
|
581
|
-
|
|
582
|
-
### Changed — Codex Sidepanel Modularized into `cdx/` Directory
|
|
583
|
-
|
|
584
|
-
- **9,222-line monolith split into 7 ES modules** — `ui-codex-panel.js` was a single monolithic file containing all Codex panel functionality. Split into `cdx/` directory with a DAG dependency graph (no circular imports), following the same architecture pattern established by the OpenCode panel's `ocp/` modularization. Total: 9,899 lines across 7 modules.
|
|
585
|
-
- `cdx-icons.js` (71 lines) — SVG icon constants, `STATUS_TONE`, `windowId`, `STOR` storage keys, `EFFORT_LEVELS`, panel identity constants, stall detection thresholds
|
|
586
|
-
- `cdx-styles.js` (2,661 lines) — Single `injectStyles()` function containing all Codex panel CSS
|
|
587
|
-
- `cdx-ws.js` (235 lines) — Callback-based WebSocket transport: `connectTab()`, `disconnectTab()`, `sendSocket()`, `scheduleReconnect()`, heartbeat management. Routes messages to ~20 callback handlers registered by `cdx-tabs.js`
|
|
588
|
-
- `cdx-render.js` (1,942 lines) — 79 exported rendering functions: markdown rendering, file path linking, card system, delta appending, history rendering, post-plan actions. Uses shared `_ctx` context object via `setRenderContext()`
|
|
589
|
-
- `cdx-requests.js` (668 lines) — 17 exported request handling functions: user input requests, MCP elicitation, approval flows, dynamic tool calls. Uses shared `_ctx` context via `setRequestsContext()`
|
|
590
|
-
- `cdx-tabs.js` (3,443 lines) — 106 exported functions owning all ~60 mutable state variables. Tab CRUD, thread/snapshot persistence, session menu, notification handling, settings, prompt dispatch, queue management, voice/slash commands. Bridges state to render/requests via `initContextBridge()` with getter-based context objects
|
|
591
|
-
- `cdx-panel.js` (879 lines) — Entry point: `buildPanel()`, `wireEvents()`, `toggleCodexPanel()`, `isCodexPanelOpen()`. Injects DOM refs into `cdx-tabs.js` via `setPanelRef()` and `setCallbacks()`
|
|
592
|
-
- **Barrel re-export preserves public API** — `ui-codex-panel.js` reduced to 7 lines re-exporting `toggleCodexPanel` and `isCodexPanelOpen` from `cdx/cdx-panel.js`. External consumers (`ui-navbar.js`, `ocp/ocp-panel.js`) require zero changes
|
|
593
|
-
- **Shared context pattern avoids circular imports** — `cdx-tabs.js` builds getter-based context objects and passes them to `cdx-render.js` and `cdx-requests.js` via setter functions, providing access to ~60 shared state variables without circular module dependencies
|
|
594
|
-
- **State binding pattern preserved for multi-tab support** — `withTab()` / `snapshotBoundState()` / `bindTabState()` / `commitBoundState()` / `restoreActiveBinding()` pattern maintained in `cdx-tabs.js`, allowing module-level `let` variables to be swapped when switching between tabs
|
|
595
|
-
|
|
596
|
-
## 2026-04-08
|
|
597
|
-
|
|
598
|
-
### Added — Model Capability Badges for OpenCode
|
|
599
|
-
- **9-capability badge system** — Each model displays colored text pills showing its capabilities: Reason (`#c084fc`), Tools (`#60a5fa`), Vision (`#4ade80`), Audio (`#fb923c`), Video (`#f87171`), PDF (`#fbbf24`), ImgGen (`#f472b6`), TTS (`#2dd4bf`), Files (`#94a3b8`). Data sourced from OpenCode `/provider` API's `capabilities` object per model — no API changes needed.
|
|
600
|
-
- **Chat panel dropdown** (`ocp-tabs.js`) — `CAP_BADGES` array with `test` functions, `capBadgesHtml()` helper. Badges hidden by default via `.ocp-dd-caps { display: none }`, shown on hover via `.ocp-dd-option:hover .ocp-dd-caps`. Each option stores `data-model-name` for search and `data-caps` JSON for filtering.
|
|
601
|
-
- **Settings provider cards** (`ui-settings.js`) — `STG_CAP_BADGES` array, `stgCapBadgesHtml()`/`stgCapBadgesText()` helpers. Models with capabilities get a dotted underline hint; hover triggers a JS-powered `position: fixed` tooltip (`#stg-cap-tooltip`) with pill badges, avoiding CSS specificity issues.
|
|
602
|
-
- **`extractModelObjects(p)`** — New helper parallel to `extractModels(p)` that returns full model objects with capabilities instead of bare string IDs.
|
|
603
|
-
- **Badge CSS** — `.ocp-cap` and `.stg-cap` pill styles (8px font, 2px 4px padding, 3px border-radius, 600 weight, colored background) in `ocp-styles.js` and `styles.css`.
|
|
604
|
-
|
|
605
|
-
### Added — Capability Filter Pills (Providers & Models)
|
|
606
|
-
- **Chat panel dropdown filter bar** (`ocp-tabs.js`) — `.ocp-cap-filter-bar` sticky bar below search input with clickable pills per capability. `activeCaps` Set tracks selections; `applyFilters()` combines text search + capability filtering with AND logic (model must have ALL selected capabilities).
|
|
607
|
-
- **Settings Model & Agent filter bar** (`ui-settings.js`) — `#stg-ocp-cap-filters` pill bar below the model filter input. `_ocpCapFilters` Set state; `populateModelSelect()` checks each model's capabilities against `STG_CAP_BADGES` test functions before adding to `<option>` list.
|
|
608
|
-
- **Settings Providers & Authentication filter bar** (`ui-settings.js`) — `#stg-ocp-provider-cap-filters` pill bar between search input and provider list. `_ocpProviderCapFilters` Set state; `renderProviderCards()` filters models within each provider card (only matching models shown), hides providers with zero matches, and shows `"3/25 models"` filtered count format in card meta.
|
|
609
|
-
- **AND-logic filtering throughout** — All three filter locations use AND logic: a model must have ALL selected capabilities to appear. `.stg-cap-filter` / `.ocp-cap-filter` CSS with 0.5 opacity default, 1.0 when `.active`, inset box-shadow.
|
|
610
|
-
|
|
611
|
-
### Fixed — Provider Search Filter Only Searched Configured Providers
|
|
612
|
-
- **`baseVisible` scope too narrow** — `renderProviderCards()` filtered from `baseVisible` (configured-only when "show all" was off), so searching for an unconfigured provider returned no results. Changed to use `searchPool = sorted` (all providers) when `providerTerm` or `hasCapFilter` is active, falling back to `baseVisible` only when no filters are applied.
|
|
613
|
-
|
|
614
|
-
### Changed — One-Click SynaBun MCP Connection for OpenCode
|
|
615
|
-
- **"Connect SynaBun" button** (`ui-settings.js`) — Green-accented button in MCP Servers section that auto-sends `POST /api/opencode/mcp` with `name: 'SynaBun'`, `command: 'node'`, `args: [setupStatus.paths.mcpIndexPath]`, `env: { DOTENV_PATH: setupStatus.paths.envPath }`. Shows "SynaBun Connected" (disabled, dimmed) when already present in MCP list via `renderMcpServers()` state check.
|
|
616
|
-
- **Tool permissions auto-refresh** — `renderToolPermissions()` now called after both "Connect SynaBun" and generic "Add MCP Server" save actions, so SynaBun tools immediately appear in the Permissions & Tools section.
|
|
617
|
-
|
|
618
|
-
## 2026-04-07
|
|
619
|
-
|
|
620
|
-
### Fixed — Edit Plan Button Opens Wrong File After Date-Organized Plan Restructure
|
|
621
|
-
|
|
622
|
-
- **`/api/latest-plan` only scanned flat files** — The endpoint at `server.js:8402` iterated `readdirSync(dir).filter(f => f.endsWith('.md'))` on `data/plans/` and `~/.claude/plans/`, which only found legacy flat plan files. After the date-organized restructure (`data/plans/YYYY-MM-DD/slug.md`), no plans were discoverable via this fallback path. Updated to recurse into `YYYY-MM-DD/` date subdirectories using the same scan pattern as `post-plan.mjs` and `stop.mjs`.
|
|
623
|
-
- **`/api/create-plan` created flat timestamped files** — When the Edit Plan button's fallback path materialized plan content from DOM extraction via `POST /api/create-plan`, it wrote `synabun-plan-{timestamp}.md` in the flat `data/plans/` root. Now creates `data/plans/YYYY-MM-DD/slug.md` with an H1-derived kebab-case slug (max 60 chars), collision-safe `-2`/`-3` suffixes, and automatic date folder creation — matching the hook-side convention.
|
|
624
|
-
|
|
625
|
-
### Fixed — Plan Content Extraction Captures Pre-Plan Preamble Instead of Actual Plan
|
|
626
|
-
|
|
627
|
-
- **`extractPlanText()` walked past plan-mode boundary** — The backward DOM walk through `.msg.msg-assistant` elements had no lower bound, so when plan-mode responses were mostly tool calls (Read, Grep) with short explanatory text (< 100 chars), it crossed into pre-plan messages and grabbed conversational preamble (e.g., "Good context from memory...") as the "plan". This garbage text was then slugified and materialized via `/api/create-plan` into files like `good-context-from-memory-this-was-all-implemented-yesterday-.md`.
|
|
628
|
-
- **Sidepanel never detected `EnterPlanMode`** — Added EnterPlanMode tool detection in `renderAssistant()` (`ui-claude-panel.js:~4050`): when `tools.some(t => t.name === 'EnterPlanMode')` fires, sets `tab.planMode = true` and records `tab._planModeStartMsgIndex` = current assistant message count. This index is also set in all 3 UI toggle paths (`/plan` slash command, `Ctrl+Shift+P` shortcut, button click).
|
|
629
|
-
- **`extractPlanText()` bounded to plan-mode messages only** — Backward walk now starts at `msgs.length - 1` but stops at `_planModeStartMsgIndex`, ensuring pre-plan preamble is never captured. This is the 4th fix for this recurring bug — prior attempts targeted symptoms (DOM order, streaming dedup path, eager capture timing) without addressing the fundamental missing boundary.
|
|
630
|
-
|
|
631
|
-
### Changed — OpenCode Settings Tab UI Overhaul
|
|
632
|
-
- **OpenCode nav icon invisible** — The settings navigation icon used a fill-based SVG (`fill: currentColor`), but the settings nav CSS applies `fill: none` globally, making it invisible. Replaced with a stroked SVG using `<rect>` elements in `ui-settings.js:43` so the icon renders correctly with `stroke: currentColor`.
|
|
633
|
-
- **Missing CSS class definitions** — The OpenCode tab HTML used classes `stg-row`, `stg-label`, `stg-input`, `stg-btn`, `stg-badge`, and `stg-list` that were never defined in `styles.css`, causing all form elements to render as unstyled browser defaults. Added ~150 lines of CSS definitions for these classes plus `stg-provider-row`, `stg-ocp-key-form`, `stg-ocp-oauth-wait`, `stg-tool-grid`, and `stg-mcp-row` component families.
|
|
634
|
-
- **`buildOpencodeTab()` HTML rewrite** — Rewrote the entire OpenCode settings tab builder (`ui-settings.js:1281-1452`) to use `stg-inline-field`, `stg-action-btn compact`, `stg-action-row`, `stg-toggle-row`, and `stg-mcp-form` classes consistent with other settings tabs. Fixed-width `<select>` elements use `flex:none` to prevent `flex:1` inheritance from `.stg-input-row` CSS.
|
|
635
|
-
- **`display:none` CSS vs inline style conflict** — Elements toggled via `element.style.display = ''` (clears inline style → falls back to CSS) stayed hidden when CSS had `display:none`. Fixed by keeping `display:none` as inline HTML `style` attribute only for `.stg-ocp-oauth-wait`, `.stg-ocp-oauth-code`, and `.stg-mcp-form`, with no corresponding CSS `display:none` rules.
|
|
636
|
-
- **Select styling extended** — `.stg-inline-field .stg-input-row` CSS (`styles.css:~9469`) previously only styled `input` elements; extended to also style `select` elements with matching appearance.
|
|
637
|
-
- **Tools & MCP renderers** — Tools permission grid (`ui-settings.js:6309-6320`) uses `stg-tool-grid` compact 2-column CSS grid. MCP server list (`ui-settings.js:6368-6375`) uses `stg-mcp-row`/`dot`/`name`/`type`/`status` styled components.
|
|
638
|
-
|
|
639
|
-
### Fixed — OpenCode Provider OAuth Flow Non-Functional
|
|
640
|
-
- **OAuth authorize sent empty body** — `POST /provider/:id/oauth/authorize` via `ocpProxy()` requires `{ method: <index> }` body (where index is the position in the provider's auth methods array), but the client sent no body. The server's `ocpProxy()` function only sends body when `body !== null && method !== 'GET'`, and the proxy route at `server.js:3736` didn't forward `req.body`. OpenCode returned "Malformed JSON in request body" which was silently caught. Fixed client (`ui-settings.js:6198-6240`) to send `{ method: idx }` and server to forward `req.body || {}`.
|
|
641
|
-
- **`oauthMethods` lost original array index** — OAuth methods filtered from the full auth methods array lost their original index position. Added `_idx` property tracking: `authMethods.map((m, i) => ({ ...m, _idx: i })).filter(m => m.type === 'oauth')` at `ui-settings.js:6088`. OAuth buttons include `data-method="${om._idx}"` for correct index dispatch.
|
|
642
|
-
- **Auth buttons shown for all 109 providers** — The `!authMethods.length` fallback showed "Enter API Key" for every provider, even the 103 that use environment variables with no explicit auth methods. Fixed to only render auth buttons when `authMethods.length > 0` — only 6 providers (cloudflare-ai-gateway, cloudflare-workers-ai, github-copilot, gitlab, openai, poe) have explicit auth methods.
|
|
643
|
-
|
|
644
|
-
### Fixed — OpenCode API Key Save No Feedback
|
|
645
|
-
- **Save succeeded silently** — API key save via the provider settings form worked at the API level but gave no visual feedback to the user. Added `showCCToast()` calls for both success ("API key saved") and failure states in `ui-settings.js:6174-6185`.
|
|
646
|
-
|
|
647
|
-
### Fixed — OpenCode OAuth Button UX Missing Feedback
|
|
648
|
-
- **Button state and error handling** — OAuth buttons now show loading state during the authorize request, restore original label text via `origLabel` on completion, display error toasts on failure, and show success toasts with instructions from the API response on success (`ui-settings.js:6198-6240`).
|
|
649
|
-
|
|
650
|
-
## 2026-04-06
|
|
651
|
-
|
|
652
|
-
### Added — Codex Settings Panel Wired to SDK
|
|
653
|
-
- **Config read/write RPC fixed** — `config_read` handler in `server.js` now passes `{ keys: [...] }` to Codex app-server's `config/read` RPC, returning layered response with `value`, `layer`, and `source` per key. `config_write` handler converted from broken flat-object `config/write` call to proper `config/batchWrite` format with `{ edits: [{ key, value }] }` array
|
|
654
|
-
- **Effective config cached and wired into thread/turn creation** — Added `_cachedConfig` object inside `handleCodexSkinWebSocket()` scope, populated on `config/read` response and updated after `config/batchWrite`. `getDefaultThreadParams()`, `getDefaultResumeParams()`, and `startTurn()` now read `approval_policy`, `sandbox_mode`, `model`, `model_reasoning_effort`, and `web_search` from cache instead of hardcoded `'never'`/`'workspace-write'`
|
|
655
|
-
- **`buildCodexSandboxPolicy()` accepts mode parameter** — Supports `read-only` (returns `readOnly` type), `danger-full-access` (returns `dangerFullAccess` type), and `workspace-write` (default, with `writableRoots`)
|
|
656
|
-
- **Settings UI field cleanup** — Removed 4 unused fields (`forced_chatgpt_workspace_id`, `forced_login_method`, `approvals_reviewer`, `workspace_write`). Added `model_reasoning_effort` select (Low/Medium/High/(default))
|
|
657
|
-
- **SDK-aligned dropdown values** — Approval Policy uses `never`/`unlessTrusted`/`onRequest` with friendly labels. Sandbox Mode uses `workspace-write`/`read-only`/`danger-full-access`
|
|
658
|
-
- **Dynamic model list** — Model and Review Model selects populated from `requestModelList()` API response, falling back to `CODEX_DEFAULT_MODELS`
|
|
659
|
-
- **Config layer badges** — Each field shows a subtle `(user)`/`(project)`/`(default)` badge parsed from the SDK's layered response
|
|
660
|
-
|
|
661
|
-
### Changed — Codex Settings Panel Visual Redesign
|
|
662
|
-
- **Glass-morphism styling** — Panel background changed from flat `#1e1e24` to `rgba(18,20,24,0.95)` with `backdrop-filter: blur(40px) saturate(1.3)`, layered box-shadows, 14px border-radius matching `.codex-panel` aesthetic
|
|
663
|
-
- **Entry animations** — Overlay fades in (`cxp-settings-fadein`), panel slides in with spring easing (`cxp-settings-slidein`, `cubic-bezier(0.16,1,0.3,1)`)
|
|
664
|
-
- **Sticky header** — `position: sticky` with uppercase 11px JetBrains Mono, 0.8px letter-spacing
|
|
665
|
-
- **Refined inputs** — `rgba(255,255,255,0.03)` background, 0.5px borders, hover/focus transitions with green accent (`rgba(115,213,167,0.35)`)
|
|
666
|
-
- **Account section** — Shows email when signed in; when not signed in, displays hint text ("Not signed in — run codex login in terminal") with a Login button
|
|
667
|
-
- **MCP section** — Empty state shows hint ("Configure in ~/.codex/config.toml under [mcp_servers]") instead of bare "No MCP servers configured"
|
|
668
|
-
- **Field placeholders** — Context Window shows "tokens", Auto-Compact shows "token limit", Provider shows "openai, ollama…"
|
|
669
|
-
- **Settings button hidden** — `.cxp-settings-btn` set to `display: none` while SDK integration matures; all code remains intact for re-enabling
|
|
670
|
-
|
|
671
|
-
### Fixed — Codex Sandbox Mode Enum Mismatch
|
|
672
|
-
- **camelCase vs kebab-case** — `thread/start` and `thread/resume` `sandbox` param requires kebab-case (`workspace-write`, `read-only`, `danger-full-access`) but was being sent as camelCase (`workspaceWrite`, `readOnly`, `dangerFullAccess`), causing "unknown variant" errors on thread restore. Fixed in `buildCodexSandboxPolicy()`, `getDefaultThreadParams()`, `getDefaultResumeParams()` in `server.js` and dropdown values in `ui-codex-panel.js`
|
|
673
|
-
|
|
674
|
-
### Changed — OpenCode Toolbar Icon & Button Polish
|
|
675
|
-
- **Official logo SVG** — Replaced placeholder rectangle SVG (`viewBox="0 0 240 300"` with semi-transparent rect overlay) with the actual OpenCode logomark (`viewBox="0 0 24 30"`, hollow rectangle frame) sourced from opencode.ai JS bundle. Updated in `html-shell.js` (toolbar button) and `ui-settings.js` (`OPENCODE_ICON` constant)
|
|
676
|
-
- **Glassmorphic button style** — Added `#topright-opencode-panel-btn` to the standalone button CSS rule (32×32, `rgba(18,18,22,0.65)` background, `backdrop-filter: blur(24px)`, border, box-shadow) matching Claude and Codex toolbar buttons. Added `fill: currentColor; stroke: none` SVG override in `styles.css`
|
|
677
|
-
|
|
678
|
-
### Added — OpenCode Tray Pills (Minimized Sessions)
|
|
679
|
-
- **Tray pill creation** — `createTrayPill()` in `ocp-tabs.js` creates `term-minimized-pill ocp-session-pill` elements in `#term-minimized-tray` with OpenCode logo icon, session label, and × close button. Click handler opens panel and switches to the correct tab via `setOnShow()` callback
|
|
680
|
-
- **Visibility-aware rendering** — `renderPills()` extended to manage both in-panel tab pills and tray pills simultaneously. Tray pills shown when panel is hidden or tab isn't active. `setPanelVisible()` and `setOnShow()` exported from `ocp-tabs.js` for panel state coordination
|
|
681
|
-
- **Panel visibility sync** — `setVisible()` in `ocp-panel.js` now calls `setPanelVisible()` on every toggle and `renderPills()` on both show and hide (previously only triggered `onUpdate()` on show)
|
|
682
|
-
- **Spinning border animation** — `ocp-session-pill` CSS in `ocp-styles.js` with `@property --ocp-pill-angle`, conic gradient border using OpenCode accent `#E8E0DC`, hover glow intensification, and `ocp-pill-running` pulse dot for active sessions
|
|
683
|
-
- **Mutual exclusion** — OpenCode tray listener extended to close on `.cxp-session-pill` (Codex) clicks alongside existing `.cp-session-pill` (Claude) handling. Codex tray listener in `ui-codex-panel.js` extended to close on `.ocp-session-pill` clicks
|
|
684
|
-
|
|
685
|
-
## 2026-04-05
|
|
686
|
-
|
|
687
|
-
### Added — Session Menu Overhaul (Claude + Codex Sidepanels)
|
|
688
|
-
- **Search bar** — Sticky search input at the top of the session dropdown with 300ms debounce. Claude panel searches server-side via `fetchClaudeSessions({ search })` (matches `firstPrompt`, `gitBranch`, `sessionId`) plus client-side label matching. Codex panel filters client-side on thread label, source, and thread ID.
|
|
689
|
-
- **Filter controls** — Filter row with "Hide empty" pill (skips sessions with `messageCount === 0` and no custom label) and "Archived" pill (toggles visibility of archived sessions). Claude panel also includes a branch dropdown (`<select>`) populated from unique `gitBranch` values across loaded sessions.
|
|
690
|
-
- **Per-item archive** — Archive button (✖) appears on hover next to the rename pencil on each session item. Claude panel stores archive state client-side via `storage.js` with prefix `synabun-session-archived:`. Codex panel also calls `thread_archive` via WebSocket. When "Archived" filter is active, archived items appear dimmed with italic prompt text and an unarchive button (↩). Archive storage prefix is shared across both panels.
|
|
691
|
-
- **Infinite scroll** — `IntersectionObserver` on a sentinel `<div>` at the bottom of the session list. Claude panel uses true server-side pagination (`limit: 20`, `offset`-based via REST API). Codex panel fetches 100 threads upfront via WebSocket `thread_list` and virtual-paginates client-side in batches of 20. Spinner shown during load, "all sessions loaded" text when exhausted.
|
|
692
|
-
- **Refactored `renderSessionMenu()`** — Split into helper functions: `cpSessApplyFilters()` / `cxpSessApplyFilters()` for client-side filtering, `cpSessRenderItem()` / `cxpSessRenderItem()` for per-item DOM construction with archive/rename/click handlers, `cpSessLoadBatch()` for paginated fetching, `cxpSessRebuildList()` for re-rendering after filter changes. Renamed `.cxp-sess-archive` CSS class to `.cxp-sess-archive-current` to avoid conflict with per-item `.cxp-sess-archive-btn`.
|
|
693
|
-
|
|
694
|
-
### Fixed — Codex Sidepanel "Invalid Transport" Config Error
|
|
695
|
-
- **Orphaned TOML tool sections crash Codex CLI** — `syncCodexToolPermissions()` in `server.js` wrote `[mcp_servers.SynaBun.tools.*]` sub-sections to `~/.codex/config.toml` without checking whether the base `[mcp_servers.SynaBun]` section (with transport definition) existed. TOML sub-table headers implicitly create a parent table — Codex saw an MCP server with no transport and rejected it with "invalid transport" at startup.
|
|
696
|
-
- **Guard added to `syncCodexToolPermissions()`** — Now checks `tomlHasSection(content, 'mcp_servers.SynaBun')` before writing tool sections. If the base section is missing, strips orphaned tool sections via new `tomlRemoveAllToolSections()` helper and returns early.
|
|
697
|
-
- **DELETE handler cleanup** — `DELETE /api/setup/codex/mcp` now calls `tomlRemoveAllToolSections(content, 'mcp_servers.SynaBun.tools.')` after removing the base section, preventing orphaned tool sections from surviving a disconnect.
|
|
698
|
-
|
|
699
|
-
### Fixed — Codex Sidepanel Tasks Halt on Minimize or Focus Loss
|
|
700
|
-
- **No WebSocket keepalive** — Codex panel had no heartbeat mechanism (unlike Claude panel's 15s interval), making the WS connection vulnerable to browser idle/throttle teardown. Added `_startHeartbeat()` / `_stopHeartbeat()` in `ui-codex-panel.js` sending `{ type: 'heartbeat', windowId }` every 15s for all connected tabs, with a matching no-op handler in `handleCodexSkinWebSocket()` in `server.js`
|
|
701
|
-
- **Orphan grace period too aggressive** — `CODEX_ORPHAN_GRACE_MS` was `30_000` (30 seconds), killing the Codex child process before the client could reconnect after a WS drop. Increased to `30 * 60 * 1000` (30 minutes) in `server.js`, matching terminal session grace periods
|
|
702
|
-
- **Stall timer false positives on background tabs** — `STALL_WARN_MS` (30s) and `STALL_KILL_MS` (120s) used `setTimeout` which browsers aggressively throttle in hidden tabs, causing false stall detection and unwarranted task interrupts on return. Enhanced `visibilitychange` handler in `ui-codex-panel.js` to be bidirectional: pauses stall timer via `stopStallTimer()` on hidden, resumes fresh via `resetStallTimer()` on visible if `_running`
|
|
703
|
-
|
|
704
|
-
### Fixed — Codex Sidepanel Stop Button Non-Functional
|
|
705
|
-
- **Architectural mismatch with Claude panel** — Claude panel kills the OS process directly on stop (`activeProc.kill()` → process is dead → `aborted` sent → done). Codex panel sent a polite `turn/interrupt` JSON-RPC to the persistent `codex app-server` and awaited its response with a 10s timeout — the app-server accepted the RPC but didn't reliably stop the turn. The Codex Rust binary also catches SIGINT, making signal-based fallbacks ineffective.
|
|
706
|
-
- **Client rewired for immediate UI reset** — `interruptTurn()` in `ui-codex-panel.js` gutted from a complex 3s-retry → 2s-force-kill escalation chain (with 1.5s button disable, nested timers, and `interrupt_ack` round-trip dependency) down to 5 lines: calls `setRunning(false)` immediately on click to reset the UI, then sends `{ type: 'force_kill' }` to the server as best-effort process cleanup. No timers, no retries, no waiting for server confirmation.
|
|
707
|
-
- **Server interrupt handler reduced to no-op** — The `interrupt` message handler in `handleCodexSkinWebSocket()` in `server.js` (previously: guard on `activeTurnId`, `await request('turn/interrupt', ...)` with 10s timeout, SIGINT fallback) now logs and returns immediately. All stop logic routes through the existing `force_kill` handler which sends SIGTERM with SIGKILL escalation after 2s.
|
|
708
|
-
- **`interrupt_ack` handler as safety net** — `handleSocketMessage` case for `interrupt_ack` now calls `clearInterruptTimer()` + `setRunning(false)` + `setStatus('Stopped', 'info')` to guarantee UI reset even if the synthetic `turn/completed` from `force_kill` is missed.
|
|
709
|
-
|
|
710
|
-
### Fixed — Claude Sidepanel Drops Connection Mid-Task and Hangs
|
|
711
|
-
- **Orphan grace period too aggressive** — `ORPHAN_GRACE_MS` was `30_000` (30 seconds) in `handleClaudeSkinWebSocket()`, killing the Claude CLI process before the client could reconnect after a WS drop from macOS sleep, network hiccup, or tab throttling. Increased to `30 * 60 * 1000` (30 minutes) in `server.js`, matching `CODEX_ORPHAN_GRACE_MS`
|
|
712
|
-
- **No WebSocket-level keepalive** — The existing 15s heartbeat in `ui-claude-panel.js` was application-level only (JSON over an open WS), unable to prevent the OS or network intermediaries from silently killing the TCP connection. Added server-side `ws.ping()` frames every 30s inside `handleClaudeSkinWebSocket()` with pong-timeout detection — if no pong is received within the next interval, `ws.terminate()` fires to trigger a clean close/reconnect cycle instead of silent death
|
|
713
|
-
- **Leak scanner dedup broken** — `scanForLeaks()` runs every 10s and hashes `JSON.stringify(leaks)` to avoid redundant `session:leaks` broadcasts. But leak objects included `ageMs` (recomputed each tick from `Date.now() - fstat.mtimeMs`), making the hash different every cycle and broadcasting on every scan indefinitely. Stripped `ageMs` from the hash computation via destructuring (`const { ageMs, ...stable } = l`)
|
|
714
|
-
|
|
715
|
-
### Fixed — Claude Sidepanel Session Cross-Talk on Reconnect
|
|
716
|
-
- **Orphan registry keyed by `windowId` only** — `_orphanedProcs` Map in `handleClaudeSkinWebSocket()` used a flat `windowId` key, but all sidepanel tabs share the same `_windowId` (from `sessionStorage`). When multiple tabs disconnected simultaneously (page refresh, WS drop), the second tab's orphan overwrote the first — on reattach, a tab could reclaim the wrong session's process and display another tab's buffered events (e.g., `AskUserQuestion` cards from a plan session appearing in a task session). Added `_orphanKey(wid, sid)` composite key helper returning `"windowId:sessionId"`, updated all 5 Map operations (`set`, `get`, 3× `delete`) in the reattach handler and `ws.on('close')` handler. No client changes needed — `ui-claude-panel.js` already sent `sessionId` in the reattach message but the server ignored it.
|
|
717
|
-
|
|
718
|
-
### Fixed — Settings Provider Tooltip Clipped by Section Overflow
|
|
719
|
-
- **Tooltips cropped on hover** — `.cc-compat-popup` (provider compatibility popups) inside the Greetings section were clipped by `.iface-section`'s `overflow: hidden` (line ~10172 in `styles.css`). Added `overflow: visible` to `.iface-section[data-collapsible]` — safe because collapse uses `display: none`, not height/overflow animation.
|
|
720
|
-
|
|
721
|
-
### Fixed — Provider Compatibility Badges Incorrect
|
|
722
|
-
- **OpenAI Codex CLI marked incompatible** — `openaiItems` in `providerBadge()` had `{ label: 'Codex CLI', on: false }` — changed to `on: true` since SynaBun supports Codex CLI via MCP.
|
|
723
|
-
- **Claude Web and Cowork marked incompatible** — `providerBadge()` call for the Greeting section passed `{ web: false, cowork: false }` — changed both to `true` so all 4 Claude platforms (CLI, VSCode, Web, Cowork) show as compatible.
|
|
724
|
-
|
|
725
|
-
### Changed — Greeting Section Title
|
|
726
|
-
- **"Claude Greeting" → "Greeting"** — Removed redundant "Claude" from the collapsible section title in the Automations tab since the provider badges already indicate platform compatibility.
|
|
727
|
-
|
|
728
|
-
### Changed — Plan Files Reorganized to Date-Organized Folders
|
|
729
|
-
|
|
730
|
-
- **Plans now stored under `data/plans/YYYY-MM-DD/slug.md`** — Restructured plan file storage from flat `data/plans/synabun-plan-{timestamp}.md` to date-organized subdirectories with descriptive kebab-case slugs (e.g., `data/plans/2026-04-06/fix-session-crosstalk.md`). `post-plan.mjs` rewrote with `getTodayPlanDir()`, `slugify()`, and `uniqueSlugPath()` helpers. `listPlanFiles()` now recurses into `YYYY-MM-DD/` subdirectories in addition to scanning legacy flat files and `~/.claude/plans/` fallback. Filename strategy 1 regex broadened from 3-word pattern to any kebab slug (`[a-z][a-z0-9-]+\.md`).
|
|
731
|
-
- Root `PLAN.md` auto-deleted on `ExitPlanMode` if modified within the last 60 seconds via new `cleanupRootPlanMd(cwd)` helper — prevents root clutter when Claude Code's built-in plan mode creates it
|
|
732
|
-
- `EnterPlanMode` context now instructs Claude to write to today's date folder with a descriptive slug and explicitly prohibits `PLAN.md` at project root and `~/.claude/plans/` writes
|
|
733
|
-
- `findUnstoredRecentPlan()` in `stop.mjs` updated with matching recursive date-subdir scan for the stop-hook fallback path
|
|
734
|
-
- `session-start.mjs` post-compaction plan path instruction updated to reference `YYYY-MM-DD/slug.md` format
|
|
735
|
-
- `neural-interface/templates/CLAUDE-template.md` Plan Files section rewritten with slug naming rules, format examples, and no-root-PLAN.md directive
|
|
736
|
-
- **One-time migration of existing flat plans** — New `migrateFlatPlans()` in `post-plan.mjs` runs automatically on the first `ExitPlanMode` call after upgrade. Moves all flat `.md` files from `data/plans/` into `data/plans/YYYY-MM-DD/` folders keyed by file mtime, renames to descriptive slugs extracted from the H1 heading via `slugify()`, handles collisions with `-2`/`-3` suffixes, and updates `stored-plans.json` keys to the new basenames. Writes `data/plans/.migrated` marker to prevent re-runs.
|
|
737
|
-
|
|
738
|
-
### Added — Claude-Style Changelog Quick Action in Codex Sidepanel
|
|
739
|
-
- **`#cxp-action-changelog` quick skill button** — Added a changelog icon action to the Codex sidepanel project bar in `neural-interface/public/shared/ui-codex-panel.js`, positioned after the branch dropdown to match the Claude sidepanel placement instead of the footer. Clicking it pre-fills `/synabun changelog`, runs `autosizeInput()`, and dispatches `sendPrompt()` only when Codex is idle, connected, and bootstrapped.
|
|
740
|
-
- **Project-bar action styling** — Added `.cxp-bar-action` and `.cxp-projectbar-actions` so the new button uses the same compact 22x22 hover/active treatment as the Claude changelog action.
|
|
741
|
-
|
|
742
|
-
### Changed — Native Skills Support Both Codex and Claude Runtimes
|
|
743
|
-
- **Claude-only skill assumptions removed** — Updated `skills/synabun/SKILL.md`, `skills/leonardo/SKILL.md`, `skills/synabun/modules/idea.md`, `skills/synabun/modules/audit.md`, `skills/synabun/modules/memorize.md`, `skills/synabun/modules/changelog.md`, `skills/leonardo/modules/image-prompter.md`, and `skills/leonardo/modules/video-prompter.md` so the repo-native skills can execute in Codex without breaking Claude behavior.
|
|
744
|
-
- **Runtime-neutral interaction and file-loading language** — Replaced hard dependencies on `AskUserQuestion`, literal `Read` / `Write` / `Edit` tool names, legacy category helper naming, and `Task`-only delegation wording with runtime-compatibility mappings and generic local file-loading/editing instructions.
|
|
745
|
-
- **Skill flow cleanup** — `skills/synabun/modules/audit.md` now allows delegated or local verification depending on runtime capability, and `skills/synabun/modules/idea.md` now uses `memories` `action: "stats"` wording instead of the stale `mode` form.
|
|
746
|
-
|
|
747
|
-
## 2026-04-04
|
|
748
|
-
|
|
749
|
-
### Added — Codex Sidepanel Activity Indicator
|
|
750
|
-
- **Thinking dots + elapsed timer** — New `.cxp-thinking` element appended to messages area when Codex is processing. Animated three-dot pulse with OpenAI avatar icon and live elapsed-seconds counter, matching Claude Code panel's indicator style.
|
|
751
|
-
- **Step detail text** — `updateThinkingDetail()` displays current work status (file being edited, command running, etc.) sourced from `syncWorkStatus()` branches, shown as right-aligned muted label inside the thinking element.
|
|
752
|
-
- **Lifecycle wiring** — `showThinking()` fires on `setRunning(true)` and after `sendPrompt()`. `hideThinking()` fires on first `item/started`, `item/agentMessage/delta`, `item/plan/delta`, `turn/completed`, and `setRunning(false)`. `repositionThinking()` keeps the element at the bottom of messages when new system content is appended.
|
|
753
|
-
|
|
754
|
-
### Fixed — Codex Sidepanel Stops Working on Page Reload or Tab Background
|
|
755
|
-
- **Orphan process registry** — Added `_codexOrphanedProcs` Map and `CODEX_ORPHAN_GRACE_MS` (30 s) to `server.js`. On WebSocket close, if a `windowId` is known and child is alive, the process is orphaned instead of killed — events buffer into an array via `sendToClient()` redirect.
|
|
756
|
-
- **Reattach message type** — New `reattach` handler in `handleCodexSkinWebSocket` looks up orphan by `windowId`, swaps the WebSocket reference via `orphan.swapWs(newWs)`, replays all buffered events, clears the kill timer, and sends `reattach_result` with thread state.
|
|
757
|
-
- **Client reattach-first flow** — `connectTab()` in `ui-codex-panel.js` now sends `reattach` with `_windowId` on reconnect before falling back to `bootstrap`. A 3 s timeout guards against stale orphans; on `reattach_result ok=false` or timeout, normal bootstrap proceeds.
|
|
758
|
-
- **Bootstrap windowId** — `bootstrap` message now includes `windowId` so the server can track the connection for future orphaning.
|
|
759
|
-
|
|
760
|
-
### Fixed — Codex Sidepanel No Reconnect on Visibility Change
|
|
761
|
-
- **`visibilitychange` handler** — Added in `wireEvents()`: when `document.visibilityState` becomes `'visible'`, checks the active tab's WebSocket state and immediately calls `connectTab()` if the socket is dead, bypassing the default reconnect delay.
|
|
762
|
-
- **Reattach timer cleanup** — `ws.onclose` handler in `connectTab()` now clears any pending `_reattachTimer` to prevent stale fallback bootstrap after intentional disconnect.
|
|
763
|
-
- **`reattach_result` handler** — New case in `handleSocketMessage` restores `_connected`, `_bootstrapped`, and `_running` state from the server response, requests model list, and appends a "Reconnected to Codex session" system message.
|
|
764
|
-
- **Seamless session continuity** — Combined with the orphan registry, tab backgrounding or page reload no longer loses the active Codex process — the user returns to an intact session with all missed events replayed.
|
|
765
|
-
|
|
766
|
-
### Fixed — Loop and Interactive Sidepanel Fighting Over Browser Session
|
|
767
|
-
- **No ownership tracking for loop-acquired sessions** — When a loop automation owned the only browser session (e.g., running on Facebook), the sidepanel Claude's `resolveSession()` auto-selected it as the single available session. Both Claude instances then competed for the same tab, causing navigation conflicts and broken automations.
|
|
768
|
-
- **`_loopOwned` Set added to browser sessions** — `createBrowserSession()` now initializes `_loopOwned: new Set()` on every session object. `/api/loop/launch` adds the loop's `terminalSessionId` to the set after acquiring the session; `/api/loop/stop` removes it during cleanup.
|
|
769
|
-
- **Ownership exposed in sessions API** — `GET /api/browser/sessions` now includes `loopOwned: boolean` and `agentOwned: boolean` fields per session, allowing the MCP layer to make informed routing decisions.
|
|
770
|
-
- **`findReusableBrowserSession()` respects loop ownership** — New `excludeLoopOwned = true` option (default on) skips sessions where `_loopOwned.size > 0`, alongside the existing `excludeAgentOwned` filter.
|
|
771
|
-
- **`resolveSession()` filters owned sessions for interactive use** — When no `SYNABUN_BROWSER_SESSION` env var is set (interactive sidepanel sessions), the MCP now filters out `loopOwned` and `agentOwned` sessions before auto-selection. If all sessions are owned, `browser_navigate` auto-creates a new session; other tools return a clear error message. Pinned loop/agent sessions bypass the filter entirely via their env var.
|
|
772
|
-
|
|
773
|
-
### Fixed — Concurrent Automation Browser Sessions Getting Destroyed
|
|
774
|
-
- **Grace timer used racy file-based ownership check** — `isSessionUsedByActiveLoop(sessionId)` read `LOOP_DIR/*.json` files to determine if a loop was using a session. Between reading and the timer firing, `/api/loop/stop` could delete the file, causing the timer to conclude no loop was active and destroy the session while other loops were still running. Replaced with in-memory `session._loopOwned?.size > 0` check — zero file I/O, no race window.
|
|
775
|
-
- **`context.on('close')` unconditionally deleted session from Map** — When Chrome crashed or was externally closed, the handler called `browserSessions.delete(sessionId)` with no ownership awareness. Loops pinned via `SYNABUN_BROWSER_SESSION` env var became stale with no recovery path. Added orphaned loop logging (`[...session._loopOwned]`) and broadcasts `orphanedLoops` array in the `browser:session-deleted` sync event.
|
|
776
|
-
- **`isSessionUsedByActiveLoop()` rewritten for in-memory checks** — Replaced the file-based implementation (read `LOOP_DIR`, parse JSON per file) with lookups against `session._loopOwned` Set, `session._agentOwned` flag, and `agentRegistry`. Eliminates the check-then-act race condition where files could be deleted between directory listing and file read.
|
|
777
|
-
- **PTY spawned before loop state file was written** — `createTerminalSession()` at line 5515 executed before `writeFileSync()` at line 5538, creating a race where `prompt-submit.mjs` hook fired before the pending file existed. `buildBrowserNote()` couldn't inject the browser session ID into Claude's context. Swapped order: loop state file is now written first, then PTY is spawned.
|
|
778
|
-
- **`/api/loop/stop` killed ALL loops indiscriminately** — No per-loop stop capability. Stopping one automation deleted all loop files, triggering cascade failures in the grace timer for remaining loops' browser sessions. Now accepts optional `terminalSessionId` in request body — if provided, only that specific loop is stopped and its `_loopOwned` entry removed. Omitting the param preserves backward-compatible stop-all behavior.
|
|
779
|
-
|
|
780
|
-
### Fixed — MCP Browser Session Recovery for Loops
|
|
781
|
-
- **`resolveSession()` returned hard error on dead pinned session** — When a loop's `SYNABUN_BROWSER_SESSION` pointed to a destroyed session (grace timer, crash), every subsequent MCP browser tool call failed with "Pinned browser session is no longer available" and no recovery. Added `_recoveredSessionId` module-level cache in `mcp-server/src/services/neural-interface.ts`. When the pinned session is gone, `resolveSession()` auto-creates a new session via `POST /api/browser/sessions`, caches its ID, and returns it. Subsequent calls reuse the cached session. Loops now survive browser crashes without manual relaunch.
|
|
782
|
-
|
|
783
|
-
### Fixed — Automation Studio Per-Loop Stop UI
|
|
784
|
-
- **Stop button killed all concurrent automations** — The "Stop" button in the running loop card and sidebar mini-stop button both called `stopLoop()` with no parameters, hitting `/api/loop/stop` which stopped everything. Updated `stopLoop()` in `api.js` to accept optional `terminalSessionId`. Running card stop button now includes `data-id="${loop.terminalSessionId}"`. Sidebar mini-stop button gets its `data-id` synced in `updateSidebarFooter()`. The `force-stop` handler passes the specific loop's ID through to the API.
|
|
785
|
-
|
|
786
|
-
### Added — Whiteboard "Send to Panel" Routes to Active Side Panel
|
|
787
|
-
- **`lastActivePanel` shared state** — Added `lastActivePanel: 'claude'` field to `state.js` shared state object. Both `ui-claude-panel.js` (`toggleClaudePanel()`) and `ui-codex-panel.js` (`setVisible()`) update this value when their panel becomes visible, defaulting to `'claude'` for backward compatibility.
|
|
788
|
-
- **Active panel routing** — The `wb:send-to-panel` event handler in `ui-claude-panel.js` now checks `state.lastActivePanel` before acting — skips if Codex is the active/last-active panel. New matching handler added to `ui-codex-panel.js` using Codex's native image format (`{ name, dataUrl }` vs Claude's `{ base64, mediaType }`). If neither panel is open, the last-active one auto-opens as fallback.
|
|
789
|
-
|
|
790
|
-
### Fixed — Neural Interface Crash on Playwright Dialog Race Condition
|
|
791
|
-
- **`ProtocolError: No dialog is showing` killed server** — Playwright's internal `DialogManager` auto-dismisses JS dialogs (`alert`/`confirm`/`prompt`) on pages without a `dialog` event listener. When the dialog disappears before the CDP `Page.handleJavaScriptDialog` command reaches Chromium, a `ProtocolError` is thrown as an uncaught exception. The existing `uncaughtException` handler in `server.js` only ignored `Frame has been detached` — the dialog error fell through to `process.exit(1)`, crashing the Neural Interface. Added `No dialog is showing` to the ignore list alongside the existing frame guard.
|
|
792
|
-
|
|
793
|
-
### Changed — Codex Sidepanel Activity Indicator Enriched with Structured Status
|
|
794
|
-
- **Title + detail layout** — Thinking row markup restructured from `avatar → dots → timer → detail` to `avatar → dots → title → detail → timer`. New `.cxp-think-title` element (11px, 50% white) shows the semantic action label ("Running command", "Updating files", "Working through plan", "Waiting for approval"), while `.cxp-think-detail` shows contextual specifics (command text, file path, plan step name). Timer moved to rightmost position with `margin-left: auto`.
|
|
795
|
-
- **`updateThinkingDetail()` replaced with `updateThinkingState({ title, detail })`** — Structured updater sets both `.cxp-think-title` and `.cxp-think-detail` independently and toggles `.cxp-thinking--waiting` class when the title starts with "Waiting", switching dots and title to amber (`rgba(255, 191, 71)`).
|
|
796
|
-
- **`syncWorkStatus()` feeds structured state** — All 7 branches now call `updateThinkingState()` with explicit `{ title, detail }` pairs derived from `itemHeadline()`, `_activePlan`, and `_threadActiveFlags`. Fallback branch shows project basename instead of blank.
|
|
797
|
-
- **DOM reuse in `showThinking()`** — No longer destroys and recreates the element on every call. If the thinking element already exists in the DOM, `repositionThinking()` is called instead, eliminating flicker during rapid `item/completed` → `showThinking()` cycles.
|
|
798
|
-
- **Thinking row persists through deltas** — Delta handlers (`item/agentMessage/delta`, `item/plan/delta`, `item/reasoning/textDelta`, `item/commandExecution/outputDelta`, `item/fileChange/outputDelta`) no longer call `hideThinking()`. Instead, content is appended first, then `repositionThinking()` keeps the indicator pinned at the bottom. Only `setRunning(false)` on `turn/completed` hides it.
|
|
799
|
-
- **Approval/input cards no longer hide indicator** — Removed `hideThinking()` from `handleServerRequest()`. Approval and input request cards render via `appendElement()` which already repositions thinking. `syncWorkStatus()` then updates the row to show "Waiting for approval" or "Waiting for input" with amber styling.
|
|
800
|
-
- **Detail width expanded** — `.cxp-think-detail` `max-width` increased from 180px to 260px; `margin-left: auto` removed (timer handles right-alignment now).
|
|
801
|
-
|
|
802
|
-
### Fixed — Codex Sidepanel Collapsible Cards Losing Verbose Content
|
|
803
|
-
- **Expanded Codex cards opened with blank bodies or `(reasoning complete)` even when the turn had already streamed detailed output** — `neural-interface/public/shared/cdx/cdx-render.js` was rebuilding collapsed cards from thin `item/completed` payloads, and the reasoning completion path replaced buffered `summaryLines` / `contentLines` with empty arrays, so command, tool, and reasoning cards lost their verbose content on expand.
|
|
804
|
-
- **Collapsed-card body state now survives collapse/expand instead of depending only on `_lastItem`** — Added `cacheCollapsedCardState()` plus explicit `flushCardBody()` handling for `commandExecution`, `mcpToolCall`, `dynamicToolCall`, `fileChange`, and `collabAgentToolCall`, preserving command text, tool arguments, progress, results, file summaries, diffs, and delegation payloads for the expand path.
|
|
805
|
-
- **Reasoning completion no longer wipes streamed thought text** — The reasoning update branch now keeps existing buffered `summaryLines` / `contentLines` unless the completed item actually carries richer arrays, preventing the UI from degrading to the placeholder-only `(reasoning complete)` state after expansion.
|
|
806
|
-
- **Open cards and collapsed cards now render from the same preserved buffers** — The live render branches for command, tool, file-change, and collaboration cards now reuse the cached state as well, so already-open cards do not blank out when a final `item/completed` payload omits data that was previously streamed.
|
|
807
|
-
|
|
808
|
-
## 2026-04-03
|
|
809
|
-
|
|
810
|
-
### Added — Codex Side Panel Feature Parity (8 Phases)
|
|
811
|
-
- **Header toolbar** — Model selector dropdown (populated via `model/list` RPC), effort toggle (low/medium/high/auto), plan mode toggle, and auto-accept toggle that automatically approves `commandExecution`, `fileChange`, and `permissions` approval requests during turns. All state is per-tab and persisted in `sessionStorage`.
|
|
812
|
-
- **Input area enhancements** — Image attachments via paste, drag-drop, or file picker (up to 5, base64 data URLs). File path chips for referencing workspace files. Hold-to-talk voice input via Web Speech API (`initVoiceInput()`). Slash command hints (`/compact`, `/clear`, `/plan`, `/model`, `/effort`, `/queue`) with arrow-key navigation and Enter/Escape. Shift+Enter sends current text as additional prompt during active turns.
|
|
813
|
-
- **Queue system** — FIFO prompt queue with tray UI, drag-to-reorder, inline edit/remove per item, pause/clear controls, and badge count. `advanceQueue()` auto-sends the next queued prompt on `turn/completed`.
|
|
814
|
-
- **UX polish** — Copy-to-clipboard buttons on code blocks, file path linkification, syntax-highlighted code rendering, and collapsible reasoning/thinking blocks in message output.
|
|
815
|
-
- **Session management** — Thread fork (`thread/fork` RPC) creates a new tab with the forked thread. Thread archive (`thread/archive` RPC) archives the current thread and starts fresh. Both wired through `requestSocketPayload()` with generic `_renameRequests` tracking map.
|
|
816
|
-
- **SDK extras** — Settings panel overlay with three sections: Account info (`account/read`), MCP server status list with per-server Refresh and OAuth Login buttons (`mcp/serverStatus/list`, `mcp/server/refresh`, `mcp/oauth/login`), and Configuration viewer (`config/read`). Accessible via gear icon in the context bar.
|
|
817
|
-
- **Stall detection & auto-compact** — Client-side stall timer warns at 30 seconds of silence, auto-interrupts at 120 seconds during active turns (`resetStallTimer()`/`stopStallTimer()`). Auto-compact detection (`checkAutoCompact()`) flags >50% input token drops between `thread/tokenUsage/updated` notifications.
|
|
818
|
-
- **Enhanced interactive features** — Dynamic tool call requests now render a textarea with "Send Result"/"Decline" buttons instead of a dead-end "Return Unavailable". Image views render actual `<img>` elements with click-to-lightbox overlay. Terminal interactions provide inline text input with Enter-to-send for live process I/O via `processInput/send` RPC. Approval cards dynamically render buttons from the SDK's `availableDecisions` array (supporting `accept`, `acceptForSession`, `acceptWithExecpolicyAmendment`, `applyNetworkPolicyAmendment`, `decline`, `cancel`).
|
|
819
|
-
- **Server-side RPC bridge** — 11 new handlers in `neural-interface/server.js` Codex bridge section (~lines 2231–2716): `terminal_input` → `processInput/send`, `thread_fork` → `thread/fork`, `thread_archive` → `thread/archive`, `thread_unarchive` → `thread/unarchive`, `account_read` → `account/read`, `config_read` → `config/read`, `config_write` → `config/write`, `mcp_status` → `mcp/serverStatus/list`, `mcp_refresh` → `mcp/server/refresh`, `mcp_oauth_login` → `mcp/oauth/login`. All follow ensureInitialized → RPC request → sendToClient pattern.
|
|
820
|
-
- All CSS uses `.cxp-` prefix, all storage keys use `synabun-codex-panel-*` prefix — complete isolation from the Claude Code side panel (`ui-claude-panel.js` confirmed untouched).
|
|
821
|
-
|
|
822
|
-
### Added — Brand Icons in Panel Footers
|
|
823
|
-
- **Claude icon in Claude side panel** — Replaced the SynaBun `favicon-32x32.png` brand mark in `.cp-toolbar-left` with an inline Claude "A" logo SVG. Updated `.cp-brand` CSS to include `color` for SVG fill support.
|
|
824
|
-
- **OpenAI icon in Codex side panel** — Added the OpenAI logo SVG (`<svg class="cxp-brand">`) before the attach button in `.cxp-footer-left`. Added `.cxp-brand { height: 16px; opacity: 0.6 }` CSS rule (was missing entirely — no icon appeared previously).
|
|
825
|
-
|
|
826
|
-
### Fixed — Reasoning Blocks Not Collapsible (Codex Panel)
|
|
827
|
-
- **Panels opened empty** — The `<details>` element in `ensureReasoningState()` was created without the `open` attribute, so reasoning content was hidden during streaming. Added `details.open = true` on creation so content is visible live.
|
|
828
|
-
- **Body content blank on expand** — When OpenAI redacts raw reasoning content (common), `state.bodyEl` had no text. `updateItemFromData()` now falls back to `state.summaryLines.join('\n')` or `'(reasoning complete)'` when `contentLines` is empty.
|
|
829
|
-
- **Auto-collapse on completion** — Sets `details.open = false` via `item/completed` so finished reasoning blocks are collapsed by default. Click to expand shows the summary or full reasoning.
|
|
830
|
-
- **Chevron indicator** — Added `summary::before` with a `▸` triangle that rotates 90° via `[open] summary::before` CSS to signal the block is clickable. Added `max-height: 300px; overflow-y: auto` to `.cxp-reasoning-body` for long content.
|
|
831
|
-
|
|
832
|
-
### Fixed — Message Cards Squashed from Below (Codex Panel)
|
|
833
|
-
- **`flex-shrink` missing on direct children** — `.cxp-messages` is a flex column with `position: absolute; inset: 0`. Child elements defaulted to `flex-shrink: 1`, causing flex to compress cards instead of scrolling. Added `flex-shrink: 0` to `.cxp-msg`, `.cxp-card`, and `.cxp-system` so they maintain their natural height and the container overflow-scrolls correctly.
|
|
834
|
-
|
|
835
|
-
### Fixed — Old Codex Sessions Reopening to Black Empty Panel
|
|
836
|
-
- **Resume path had no durable history fallback** — The sidepanel only restored old threads from the SDK `thread/resume` payload or a locally cached HTML snapshot. If an older Codex thread resumed without usable turn items and no fresh snapshot existed, the panel dropped to the empty `Open the Codex panel to start...` state instead of reconstructing the previous session.
|
|
837
|
-
- **Server-side transcript recovery** — The Codex bridge in `neural-interface/server.js` now looks up the saved thread in `~/.codex/state_5.sqlite`, reads its `rollout_path`, parses the corresponding Codex JSONL session transcript under `~/.codex/sessions/...`, and synthesizes structured fallback history items for user messages, assistant messages, MCP tool calls, dynamic tool calls, and command executions.
|
|
838
|
-
- **Client restore now consumes fallback history** — `renderHistory()` in `ui-codex-panel.js` now accepts `fallbackItems` from the server and renders them when the normal resume payload lacks usable turn data, while still preferring a newer local HTML snapshot when one exists. Old sessions selected from the Codex sidepanel session picker now reopen with their prior conversation/cards instead of a blank black panel.
|
|
839
|
-
|
|
840
|
-
### Fixed — Stop Button Not Stopping Codex
|
|
841
|
-
- **No feedback, no retry** — Single `sendSocket({ type: 'interrupt' })` on click with no visual confirmation. If the Codex RPC failed silently, nothing happened.
|
|
842
|
-
- **New `interruptTurn()` function** — Sets "Stopping…" status and appends a system message immediately. Temporarily disables the stop button for 1.5s to prevent spam. Retries interrupt after 3 seconds if `_running` is still true. Sends `{ type: 'force_kill' }` after 6 seconds total, which calls `child.kill('SIGTERM')` on the Codex process server-side. `clearInterruptTimer()` is called from `setRunning(false)` to clean up on normal turn completion.
|
|
843
|
-
- **Server `force_kill` handler** — New message type in the Codex bridge WebSocket (`server.js`). Kills the child process, clears `activeTurnId`, and sends a synthetic `turn/completed` notification to reset client state.
|
|
844
|
-
|
|
845
|
-
### Changed — Footer Toggles Match Claude Panel Style (Codex Panel)
|
|
846
|
-
- **Flat pill toggles** — Replaced `.cxp-toolbar-toggle` from inheriting `.cxp-btn` rounded button style (chunky green buttons) to transparent flat pills matching Claude's `.cp-think-toggle` / `.cp-plan-toggle` / `.cp-auto-toggle` pattern: 9px JetBrains Mono uppercase, `padding: 4px 7px 6px`, `::after` underline indicator with `scaleX(0)` default, hover `translateY(-1px)` + underline reveal, `:active` micro-press `scale(0.96)`.
|
|
847
|
-
- **Effort dots as `<i>` elements** — Changed from `::before`/`::after` pseudo-elements to three `<i></i>` children with `.lit` class toggled by `syncToolbarState()`. Progressive brightness: low = 1 dot, medium = 2, high = 3.
|
|
848
|
-
- **Per-toggle active states** — `#cxp-plan-toggle.active` uses blue (`rgba(130,175,255,0.85)`) + full `::after` underline; `#cxp-autoaccept-toggle.active` uses green (`rgba(100,210,140,0.85)`), matching Claude panel's plan/auto active colors exactly.
|
|
849
|
-
- Removed `cxp-btn` and `cxp-labeled` classes from toggle button HTML in the footer template.
|
|
850
|
-
|
|
851
|
-
### Fixed — Edit Plan Button Never Opening Plans
|
|
852
|
-
|
|
853
|
-
- **Stale `_rawMd` in dedup rendering path** — In `renderAssistant()`, the dedup branch updated `existingBody.innerHTML` but never set `existingBody._rawMd`. Since `extractPlanText()` walks `.msg-body` elements and reads `_rawMd` for content, it read stale partial text from a previous streaming tick. Fixed by computing `rawMd` before calling `md()` and assigning it to `existingBody._rawMd` before updating `innerHTML` (~line 3696 in `ui-claude-panel.js`).
|
|
854
|
-
- **No eager plan file creation at detection time** — `tab.planFilePath` was only set lazily inside the Edit button's click handler, meaning the plan file didn't exist yet when the button was first clicked. Fixed by issuing `POST /api/create-plan` immediately when plan content is captured at ExitPlanMode detection time, storing the returned path in `tab.planFilePath` so the Edit button always has a valid file to open. Applies in both the dedup rendering path (~line 3731) and the new-message rendering path (~line 3785).
|
|
855
|
-
|
|
856
|
-
### Fixed — CLI Auto-Detect Not Finding Claude After Reinstall
|
|
857
|
-
- **`which`/`where` blind to user bin dirs** — The settings auto-detect button (`POST /api/cli/detect/:profileId`) and runtime detection functions (`getClaudeBin()`, `getCodexBin()`) used bare `execSync('which claude')` inheriting the Node server's process PATH, which doesn't include `~/.local/bin` (Claude Code's default install location). Shell profiles (.zshrc) add this directory, but Node processes don't source them. Added `getAugmentedPath()` helper (~line 2112 in `server.js`) that prepends `~/.local/bin`, `/usr/local/bin`, and `~/.npm-global/bin` (filtered by `existsSync`). Applied to all 4 detection call sites via `env: { ...process.env, PATH: getAugmentedPath() }`. Cross-platform safe — `delimiter` from `path` module handles `:` vs `;`, non-existent directories are silently excluded.
|
|
858
|
-
|
|
859
|
-
### Added — Branch Dropdown in Codex Side Panel
|
|
860
|
-
- **Branch selector in projectbar** — Added `#cxp-branch` dropdown to the Codex side panel's projectbar, matching the Claude panel's existing branch dropdown. Populated via `/api/terminal/branches?path=...` endpoint using the shared `ddPopulate()`/`ddSetup()` utilities. Auto-refreshes when the active project changes in `updateActiveTabView()`. Added `.cxp-dropdown-sm { max-width: 72px; }` CSS for compact display.
|
|
861
|
-
|
|
862
|
-
### Fixed — PLAN COMPLETE Card Appearing After Non-Plan Tasks
|
|
863
|
-
- **Exit plan flags leaking across turns** — `_exitPlanPending`, `_exitPlanHandled`, and `_exitPlanWasPlanMode` flags set in `renderAssistant()` persisted on the tab object if `finishTab()` hadn't fired before the next `send()`. A new turn's `updateToolResult()` or `done` handler would read stale flags and render the PLAN COMPLETE card for a non-plan task. Fixed by explicitly clearing all three flags at the top of both `send()` and `_sendQueued()` in `ui-claude-panel.js`, ensuring no prior-turn state leaks into the new turn.
|
|
864
|
-
|
|
865
|
-
### Changed — Codex Footer Alignment with Claude Panel
|
|
866
|
-
- **13 CSS selectors updated** — Matched all padding, margin, font-size, height, gap, and border-radius values between `.cxp-*` footer elements and their `.cp-*` counterparts: `.cxp-bottom` (padding `14px 16px 12px`), `.cxp-input-shell` (min-height `44px`, padding `10px 14px`, border-radius `12px`), `.cxp-input` (font-size `13.5px`), `.cxp-send` (dimensions `30×30`, border-radius `8px`), `.cxp-footer-toolbar` (padding `6px 2px 0`, gap `6px`), `.cxp-footer-left`/`.cxp-footer-right` (gap `6px`), `.cxp-attach-btn`/`.cxp-mic-btn` (dimensions `26×26`, font-size `13px`, border-radius `6px`), `.cxp-cost` (font-size `9px`). Preserved Codex's green accent color while matching Claude's geometry exactly.
|
|
867
|
-
|
|
868
|
-
### Changed — Draft Preservation Across Panel Switches
|
|
869
|
-
- **Textarea text no longer lost on toggle** — When switching between Claude and Codex side panels, the current textarea value is now saved to `activeTab().draft` before hiding. In `ui-claude-panel.js`, `toggleClaudePanel()` saves `$input.value` to `tab.draft` in the close branch. In `ui-codex-panel.js`, `setVisible(false)` saves `panelEl('#cxp-input').value` to `tab.draft`. Both panels already restore `tab.draft` on re-render via `updateActiveTabView()`.
|
|
870
|
-
|
|
871
|
-
### Changed — Collapsible Cards with Lazy Body Rendering (Codex Panel)
|
|
872
|
-
- **Cards start collapsed by default** — All `.cxp-card` elements now render with `cxp-collapsed` class, hiding `.cxp-card-body` via `display: none`. A chevron indicator (`▾`/`▸` via CSS `::after` pseudo-element with rotation transform) signals clickability. `.cxp-card-head` gets `cursor: pointer` and `user-select: none`. Border-bottom on the head is removed when collapsed. Request cards (permission prompts with Accept/Decline buttons) remain expanded since they require user interaction.
|
|
873
|
-
- **Event delegation for expand/toggle** — Single click listener on `#cxp-messages-container` handles all `.cxp-card-head` clicks, toggling `cxp-collapsed` on the parent card. Skips clicks on interactive elements (`button`, `input`, `select`, `textarea`, `a`). Delegation pattern works for both newly created cards and snapshot-restored HTML.
|
|
874
|
-
- **Snapshot restore collapse enforcement** — `renderStoredTranscript()` now adds `cxp-collapsed` to all restored `.cxp-card` elements and injects missing `.cxp-card-chevron` elements for old snapshots saved before this change.
|
|
875
|
-
- **Lazy body rendering** — DOM writes are deferred while cards are collapsed to avoid unnecessary rendering overhead. `appendOutputDelta()` (commands, file changes), `appendPlanDelta()`, and `appendToolProgress()` always update their JS state buffers (`outputBuf`, `buffer`, `_lastProgress`) but skip DOM writes when `isCardBodyHidden(state)` returns true, setting `state._bodyDirty = true` instead. `updateItemFromData()` stores `state._lastItem` for all `.cxp-card` elements, always updates visible head elements (title via `itemHeadline()`, pill via `formatStatus()`), but skips the entire body switch block when collapsed. Non-card elements (`agentMessage`, `userMessage`, `reasoning`) always render normally. On expand click, `flushCardBody(state)` re-renders buffered content: plan cards get `setMarkdownBuffer()`, command/file-change cards get `setOptionalSection()` from `outputBuf` plus meta text from `_lastItem`, and all other types re-run `updateItemFromData(state._lastItem)`.
|
|
876
|
-
|
|
877
|
-
## 2026-04-01
|
|
878
|
-
|
|
879
|
-
### Added — Copy Button on Code Blocks in Claude Sidepanel
|
|
880
|
-
- **`addCopyButtons(el)` function** — Every `<pre>` block in assistant messages now has a "Copy" button in the top-right corner. Clicking copies the block's text content to clipboard via `navigator.clipboard.writeText()`, shows "Copied!" feedback for 1.5s with green accent
|
|
881
|
-
- **CSS: `.cp-copy-btn`** — Absolute-positioned button, JetBrains Mono 9px uppercase, always visible at 0.6 opacity, brighter on hover with subtle background. Added `position: relative` to `.cp-messages .msg-body pre`
|
|
882
|
-
- **Wired at 7 final render points** — `loadSessionHistory()`, `message_stop`, `content_block_stop`, `renderAssistant` (3 paths), and plan card body. Excluded from throttled 32ms streaming render to avoid flicker
|
|
883
|
-
|
|
884
|
-
### Fixed — Edit Plan Shows Wrong Content (Streaming Dedup Path)
|
|
885
|
-
- **Eager capture in wrong code path** — `extractPlanText(tab)` eager capture was placed in the new-message branch of `renderAssistant()`, but streaming always routes through the dedup branch via `handleStreamDelta()` — the capture was dead code. Added eager capture to the dedup path (line ~3723) where `tab._exitPlanPending` is set and the plan `.msg-body` already exists in the DOM. Third fix attempt over 6 days — all prior attempts targeted the correct function but the wrong branch
|
|
5
|
+
## 2026-04-25
|
|
6
|
+
|
|
7
|
+
### Added — Dedicated Schedules Studio
|
|
8
|
+
- **Standalone schedule window** — Added `neural-interface/public/shared/ui-schedules-studio.js` and initialized it from both `neural-interface/public/variant/2d/main.js` and `neural-interface/public/variant/3d/main.js`. The new window opens from `automations:open-schedules` and keeps schedule management out of the Automation Studio detail pane.
|
|
9
|
+
- **Tabbed schedule workspace** — Built Cron, Timers, Groups, and Activity tabs so recurring schedules, quick timers, group management, and run history have separate focused surfaces instead of sharing the automation editor area.
|
|
10
|
+
- **Schedule editor flow** — Added a dedicated editor with cron presets, timezone, day selection themes, enabled state, group assignment, and launch overrides for profile/model/effort/MCP/browser settings.
|
|
11
|
+
- **Timer controls** — Added quick timer creation, run-now actions, active timer countdowns, saved schedule timers, pause/resume, trigger, edit, and delete controls inside the schedules window.
|
|
12
|
+
|
|
13
|
+
### Added — Schedule Groups API and Persistence
|
|
14
|
+
- **Group-aware schedule store** — Added `loadScheduleStore()`, `saveScheduleStore()`, `loadScheduleGroups()`, `saveScheduleGroups()`, `validateScheduleGroupId()`, and `applyScheduleLaunchOverrides()` in `neural-interface/server.js`. Existing `loop-schedules.json` data remains compatible while the store can now persist `{ schedules, groups }`.
|
|
15
|
+
- **Schedule group endpoints** — Added `GET /api/schedule-groups`, `POST /api/schedule-groups`, `PUT /api/schedule-groups/:id`, `DELETE /api/schedule-groups/:id`, and `POST /api/schedule-groups/reorder` for group CRUD and ordering.
|
|
16
|
+
- **Schedule assignment metadata** — Extended schedule create/update and launch payloads to carry `groupId`, `profile`, `model`, `effort`, `mcpProfile`, and `usesBrowser`, allowing schedules in a group to keep their launch configuration with the schedule itself.
|
|
17
|
+
- **Client sync helpers** — Added schedule group API helpers in `neural-interface/public/shared/api.js` and broadcast handling for `schedule-group:created`, `schedule-group:updated`, `schedule-group:deleted`, and `schedule-group:reordered` in `neural-interface/public/shared/ui-sync.js`.
|
|
18
|
+
|
|
19
|
+
### Changed — Automation Studio Schedule Routing
|
|
20
|
+
- **Schedules removed from the Automation Studio content flow** — `initAutomationStudio()` no longer handles `automations:open-schedules` by switching `_view` to `schedules`, and Automation Studio no longer initializes schedule WebSocket rendering. The header Schedules button now emits the shared schedules event for the standalone window.
|
|
21
|
+
- **Automations submenu expanded** — Added Schedules, New Cron Schedule, Quick Timer, and Schedule Groups menu items in `neural-interface/public/shared/html-shell.js`, with handlers in `neural-interface/public/shared/ui-menubar.js` that open the dedicated schedules window directly to the requested flow.
|
|
22
|
+
- **Schedules window styling** — Added the `.schedules-studio-panel` glass shell and `.sched-*` responsive layout styles in `neural-interface/public/shared/styles.css` so the standalone window has its own grouped sidebar, tab rail, schedule lists, dialogs, and mobile layout.
|
|
23
|
+
|
|
24
|
+
### Changed — Live Countdown on Schedule Cards
|
|
25
|
+
- **Live ticking countdown** — Replaced static "Next: 4h" text on Cron-tab schedule cards with a 1-second-tick live countdown. `formatNextRun()` in `neural-interface/public/shared/ui-schedules-studio.js` rewritten as adaptive 2-unit format (`2d 3h`, `4h 23m`, `15m 42s`, `45s`, `Overdue`) that drops zero second-unit. Card markup wraps the value in `<span class="sched-countdown" data-countdown="<iso>">` so `updateTimerCountdowns()` can refresh text without re-rendering each tick. `POLL_INTERVAL` lowered from `5000` to `1000`; tick lifecycle stays bound to `startPolling()` / `stopPolling()` so it only runs while the panel is open.
|
|
26
|
+
- **Tabular-nums styling** — Added `.sched-countdown` rule in `neural-interface/public/shared/styles.css` with `font-variant-numeric: tabular-nums` to prevent layout jitter as digits change each second.
|
|
27
|
+
|
|
28
|
+
### Added — Edit Prompt Shortcut on Schedule Cards
|
|
29
|
+
- **Edit prompt icon button** — Each cron schedule card in `neural-interface/public/shared/ui-schedules-studio.js` now has an "Edit prompt" icon button (document-with-lines `script` icon) between Run Now and Edit Schedule. Click closes the Schedules panel and opens Automation Studio with the schedule's linked template selected directly in detail/editor view.
|
|
30
|
+
- New `script` icon added to the `ICONS` map
|
|
31
|
+
- New button uses `data-action="schedule-edit-prompt"` with `data-template-id="${s.templateId}"`
|
|
32
|
+
- Existing pencil tooltip clarified from "Edit" to "Edit schedule" to differentiate
|
|
33
|
+
- New `case 'schedule-edit-prompt'` handler reads `el.dataset.templateId`, calls `closePanel()`, then emits `automations:open` with `{templateId}` payload
|
|
34
|
+
- **`automations:open` event accepts `{templateId}` payload** — `initAutomationStudio()` listener in `neural-interface/public/shared/ui-automation-studio.js` now passes `opts` to `openPanel(opts = {})`. When payload contains `templateId`: if panel already open, finds template in `_templates` and calls `switchToDetail(tpl)`; on fresh open, after `loadData()` runs, looks up template and routes to detail view instead of `renderView()`. Falls back to default welcome view when no payload or template not found.
|
|
35
|
+
|
|
36
|
+
### Added — Sidepanel Tray Placeholder Pills
|
|
37
|
+
- **Persisted pills before panel bootstrap** — Added `neural-interface/public/shared/ui-sidepanel-tray.js` to render minimized placeholder pills for saved Claude, Codex, and OpenCode tabs before those provider panels have been lazy-loaded. The tray reads window-scoped tab storage, falls back to legacy single-session keys, filters out blank default sessions, and opens the matching provider via `claude-panel:show`, `codex-panel:show`, or `opencode-panel:show` with the selected `tabId`.
|
|
38
|
+
- **Provider handoff events** — Wired `initSidepanelTrayPlaceholders()` from `ui-navbar.js`, then dispatch `sidepanel-tray:provider-loaded` from `toggleClaudePanel()`, Codex `ensurePanel()`, and OpenCode `ensurePanel()` so placeholders disappear once real provider-owned pills are restored.
|
|
39
|
+
|
|
40
|
+
### Fixed — Right-Side Toolbar Reservation During Sidepanel Switches
|
|
41
|
+
- **Toolbar could overlap an open sidepanel** — `ui-sidepanel-layout.js` previously tracked one `_activeOwner`, so closing or switching one sidepanel could clear `--right-panel-width` and `--right-panel-gap` while another right panel was still active. Replaced that with a per-owner reservation map, applying the widest active reservation and returning `true` from `clearRightPanelLayout()` only when no reservations remain, preserving toolbar and adjacent editor spacing during multi-provider toggles.
|
|
42
|
+
|
|
43
|
+
### Changed — Claude Restored Tab Selection
|
|
44
|
+
- **Restored Claude tabs keep stable IDs** — Updated `createTab()` in `ui-claude-panel.js` to accept a restored `id`, and changed `restoreTabs()` to pass saved tab IDs through. Placeholder click-through can now target the restored Claude tab instead of losing selection because the panel generated a fresh `crypto.randomUUID()` during bootstrap.
|
|
45
|
+
|
|
46
|
+
### Changed — Backup System Coverage Expanded
|
|
47
|
+
- **Backup file lists extended** — Added `stored-plans.json`, `synabun-plugins.json`, `mcp-registry.json`, and `active-profile.json` to both manual `GET /api/system/backup` (`neural-interface/server.js:10457`) and auto-backup `buildBackupZipToFile()` (`neural-interface/server.js:10844`) file arrays. These cover the post-plan hook plan→memory tracker, plugin install registry, third-party MCP server registry + profile defs, and active MCP profile selection — all previously lost on restore.
|
|
48
|
+
- **Plan markdown history backed up recursively** — Added `addDirRecursive('data/plans', ...)` to both manual and auto backup paths (`neural-interface/server.js:10483`, `:10868`) so the date-sliced plan tree (`data/plans/YYYY-MM-DD/*.md`) rides along.
|
|
49
|
+
- **Restore creates `plans` subdir** — Added `'plans'` to the `mkdirSync` subdir list in `POST /api/system/restore` (`neural-interface/server.js:10617`). Existing recursive `data/` write loop already extracts plan files via `dirname(target)` mkdir.
|
|
50
|
+
- **Backward compatible with old backups** — Manifest version check still accepts `version 1 || 2`. Old backups restore exactly as before — missing new files skipped silently, no schema migration required.
|
|
51
|
+
|
|
52
|
+
### Added — CLI-Not-Installed Indicator in Sidepanels
|
|
53
|
+
- **Empty-state banner across Claude, Codex, OpenCode panels** — Detects when the corresponding CLI binary is missing and renders a warning banner inside the panel's empty state (`.cp-empty` / `.cxp-empty` / `.ocp-empty`) with the install command, an "Install guide" link to the official docs, and a "Re-check" button. Banner is removed automatically once the CLI is detected.
|
|
54
|
+
- **Shared status module `cli-status.js`** — New `neural-interface/public/shared/cli-status.js` wraps the existing `/api/system/tool-versions` endpoint with a subscriber + auto-poll layer. Exposes `subscribeCliStatus(toolKey, cb)`, `recheckCliStatus(toolKey)`, `getCliDocUrl(toolKey)`, `getCliInstallCommand(toolKey)`, `getCliLabel(toolKey)`. Polls every 30s while at least one subscribed tool reports `installed === null`; stops once detected or unsubscribed. 10s in-memory cache + single in-flight fetch dedup.
|
|
55
|
+
- **Send-button gating** — Each panel's send/input flow is now blocked while the CLI is missing. `ui-claude-panel.js` `send()` returns early with a notification. `cdx/cdx-tabs.js` `syncInputEnabled` reads the `.cxp-cli-blocked` panel class to force `send.disabled = true`, and `sendPrompt()` returns early with a system message. `ocp/ocp-panel.js` `syncSendButton` short-circuits to disabled with the "OpenCode CLI not installed" tooltip; `handleSend` returns early via `setStatus('offline', …)`.
|
|
56
|
+
- **Banner re-render hooks** — `cdx/cdx-tabs.js` `clearTranscript` now dispatches a `cdx-empty-rebuilt` CustomEvent and `ocp/ocp-render.js` `renderEmptyState` dispatches `ocp-empty-rebuilt`, so the banner re-injects itself after the empty state is rebuilt during session resets.
|
|
57
|
+
- **Official install URLs** — `claude-code` → `https://docs.claude.com/en/docs/claude-code/setup`, `codex` → `https://developers.openai.com/codex/cli/`, `opencode` → `https://opencode.ai/docs/`. (`gemini` constants defined for future use; no Gemini sidepanel currently exists.)
|
|
58
|
+
- **Banner styles** — Added `.cp-cli-missing*`, `.cxp-cli-missing*`, `.ocp-cli-missing*` rule sets to `ui-claude-panel.js` style block, `cdx/cdx-styles.js`, and `ocp/ocp-styles.js`. Includes `.cp-cli-blocked` / `.cxp-cli-blocked` / `.ocp-cli-blocked` panel classes for the dimmed input state.
|
|
59
|
+
|
|
60
|
+
## 2026-04-24
|
|
61
|
+
|
|
62
|
+
### Added — "Continue planning" in OpenCode, Codex, and Claude plan UIs
|
|
63
|
+
- **OpenCode** — Added `data-action="continue-planning"` button to the post-plan card in `ocp-render.js` and wired `onContinuePlanning` in `showPostPlanUI()` (`ocp-tabs.js`). Handler clears `showPostPlanActions` while keeping `mode='plan'`, so the user can keep prompting to refine the plan without exiting plan mode.
|
|
64
|
+
- **Codex** — Added `Continue planning` button to the post-plan card in `cdx-render.js`. Handler removes the card, then dispatches a new prompt with `forcePlanModePrefix: true` and the current plan text as context, allowing iterative plan refinement.
|
|
65
|
+
- **Claude** — Added `Continue planning` action entry in `ui-claude-panel.js`. Handler clears `showPostPlanActions` and saves tabs, keeping `tab.planMode = true` so the next user message continues in plan mode.
|
|
66
|
+
- **OpenCode guard message** — Updated the "choose before sending" error to include "Continue planning" alongside the existing three options.
|
|
67
|
+
|
|
68
|
+
### Fixed — OpenCode Sidepanel Post-Plan Card Resurrection
|
|
69
|
+
- **`setTabMode()` leaves `showPostPlanActions` flag true when leaving plan mode** — Mode button clicks, Tab-key mode cycling, and other `setTabMode()` callers bypassed `onContinue`'s flag clear, so `tab.showPostPlanActions` could persist into build/chat. Combined with the unguarded restore in `renderTabMessages()` at `ocp-tabs.js:2258`, any subsequent tab switch / session switch / revert / WS reconnect re-rendered the "PLAN COMPLETE" card in the message area even though the user had already moved on to implementation. Fixed in `neural-interface/public/shared/ocp/ocp-tabs.js` with two defenses:
|
|
70
|
+
- `setTabMode()` now clears `tab.showPostPlanActions` and calls `removePostPlanCards(container)` on any `plan → non-plan` transition, symmetric with the existing `_exitPlanDetected` reset on entry.
|
|
71
|
+
- `renderTabMessages()` restore guard tightened to `tab.mode === 'plan' && tab.showPostPlanActions && tab.planContent`, so a leaked flag cannot resurrect the card outside plan mode.
|
|
72
|
+
|
|
73
|
+
### Fixed — Loop Launch Silent Hang (Automation Studio Browser Loops)
|
|
74
|
+
- **Orphan Chrome holding `SingletonLock` froze `launchPersistentContext`** — When a previous loop-owned Chrome in `data/browser-profiles/<hash>/` stayed alive past its session, the OS-level singleton lock remained held by the live process. Existing stale-file cleanup at `neural-interface/server.js:20080` only removed unlocked lock files — useless when a running Chrome owned the lock. Subsequent `chromium.launchPersistentContext()` calls blocked indefinitely, so `POST /api/loop/launch` never returned. UI waited with no CLI window, no toast, no error surfaced. Fix in `neural-interface/server.js:20062` (`launchPersistentProfile`) adds a pre-launch orphan-kill pass: Unix uses `pgrep -f "user-data-dir=<root>"` + `SIGKILL`, Windows uses PowerShell `Get-CimInstance Win32_Process` filtered by `CommandLine -like "*user-data-dir=$root*"` (path passed via `SYNABUN_KILL_ROOT` env var to avoid backslash quoting) + `taskkill /F /PID`.
|
|
75
|
+
- **`launchPersistentContext` had no timeout** — Added `timeout: 45000` to `launchOpts` at `neural-interface/server.js:19938` as a platform-agnostic safety net. If any edge case still leaves the singleton locked, Playwright errors after 45s instead of hanging silently, letting the UI surface `Failed to launch loop`.
|
|
76
|
+
|
|
77
|
+
### Added — Diagnostic Alert Fallbacks for Silent Launch Failures
|
|
78
|
+
- **`attachDetached` catch in `neural-interface/public/shared/ui-terminal.js:5132`** — `showTermToast` appends its toast to `#term-container`; when the terminal panel is hidden (`offsetParent === null`), the toast renders invisibly and the failure gets swallowed. Added a post-toast container-visibility check that triggers a browser `alert()` with the error message plus a DevTools pointer when the container is hidden, surfacing attach failures that otherwise disappear.
|
|
79
|
+
- **`confirmLaunch` catch in `neural-interface/public/shared/ui-automation-studio.js:1491`** — Added an `alert()` alongside the existing `showToast` so errors thrown before the `terminal:attach-floating` emit (network failures, missing state, module-load errors) reach the user even when the toast host is offscreen.
|
|
80
|
+
|
|
81
|
+
### Fixed — Codex MCP Approval Gating
|
|
82
|
+
- **MCP approval prompts bypassed sidepanel Auto mode** — `handleCodexSkinWebSocket()` in `neural-interface/server.js` previously auto-accepted `mcpServer/elicitation/request` events for any SynaBun tool listed in `~/.claude/settings.json`, so tools like `mcp__SynaBun__remember` could run without a Codex approval card even when the tab Auto toggle was off. Added per-turn `activeTurnAutoAccept` tracking and gated the silent MCP accept path on both the current tab's Auto state and the permitted-tool list.
|
|
83
|
+
- **Auto state now travels with each Codex query** — `dispatchPrompt()` in `neural-interface/public/shared/cdx/cdx-tabs.js` includes `autoAccept: !!tab?.autoAccept` in the WebSocket `query` payload, and `server.js` passes it into `startTurn()` so approval behavior follows the visible tab state.
|
|
84
|
+
- **Per-turn auto-accept state is cleared after the turn** — `server.js` resets `activeTurnAutoAccept` on `turn/completed`, `thread/closed`, Codex child close, and `turn/start` failure to prevent a prior Auto-enabled turn from leaking into later tool approvals.
|
|
85
|
+
|
|
86
|
+
### Fixed — Codex Sidepanel Edit Plan Cancel Handoff
|
|
87
|
+
- **Post-plan card stayed disabled after closing the editor** — `renderPostPlanActions()` in `neural-interface/public/shared/cdx/cdx-render.js` intentionally marks the post-plan card busy when `Edit plan` opens the file editor, but the Codex `plan-edit-cancelled` path in `neural-interface/public/shared/cdx/cdx-panel.js` tried to recover through unregistered `_host.renderPostPlanActions` callbacks. The Codex `plan-saved` and `plan-edit-cancelled` listeners now import and call `appendAssistantMarkdownMessage()` / `renderPostPlanActions()` directly from `cdx-render.js`, so closing without saving re-renders a fresh clickable card and saving reopens the `PLAN UPDATED` handoff correctly.
|
|
88
|
+
|
|
89
|
+
### Fixed — Codex Loop Silent Failure (Sentinel Ignored Exit Code)
|
|
90
|
+
- **`attachExecLoopDriver` in `neural-interface/server.js:17230` advanced 162 ghost iterations after codex broke** — Iteration sentinel was built as `<codex exec ...>; echo SYNABUN_ITER_DONE_<N>`, so the echo fired regardless of `codex` exit status. After codex started failing fast at iter 4 (~4s vs ~7m for real runs), driver kept seeing the sentinel and looped 162 fake iters in 33 minutes until the PTY died with exit code 1. Fixed by:
|
|
91
|
+
- Sentinel now `<cmd>; rc=$?; echo SYNABUN_ITER_DONE_<N> rc=$rc` — captures exit code in the echo.
|
|
92
|
+
- Detection regex `new RegExp(\`${EXEC_SENTINEL_PREFIX}${session._execCurrentIter} rc=(-?\\d+)\`)` extracts `rc`.
|
|
93
|
+
- Per-driver `consecutiveNonZero` and `consecutiveFastFails` counters with `FAST_FAIL_THRESHOLD_MS=30000` and `FAIL_LIMIT=3`. On 3-streak (non-zero exit OR sub-30s duration), driver marks loop `active:false`, sets `stoppedReason: 'fast-fail-rc'` or `'fast-fail-duration'`, records `lastExitCode`, kills PTY, and clears the interval.
|
|
94
|
+
- Iteration start timestamp tracked via `session._execIterStartedAt = Date.now()` at iter-begin.
|
|
95
|
+
|
|
96
|
+
### Fixed — PTY Exit Left Loop File Active (Zombie Loops)
|
|
97
|
+
- **`createTerminalSession` PTY exit handler in `neural-interface/server.js:16718` did not update loop state** — When a loop-owned PTY died, `terminalSessions.delete()` ran and the driver interval cleared, but the on-disk loop file still showed `active:true`. UI/scheduler saw a phantom running loop forever. Fixed by adding a post-`pty:exit` block that calls `findLoopFileForTerminal(sessionId)`, marks the file `active:false`, sets `completedAt`, `stoppedReason: 'pty-exit-<code>'`, and records `lastExitCode`. Logs `pty:exit | marked loop inactive` for forensics.
|
|
98
|
+
|
|
99
|
+
### Changed — Loop Tab Isolation Hardening
|
|
100
|
+
- **Dropped `_switchSessionTab` calls during loop launch** — `neural-interface/server.js:8351` (manual launch) and `:8751` (scheduled launch) previously called `_switchSessionTab(claimedSession, ..., browserTabId)` after `createSessionTab`, flipping the shared browser session's `activeTabId`. With concurrent loops sharing one Chromium, this caused active-tab churn and risked any code path falling back to `session.activeTabId`. Each loop already pins its own tab via the `SYNABUN_BROWSER_TAB` env var, so the switch was unnecessary. Removed both calls; replaced with a comment explaining the constraint.
|
|
101
|
+
- **`SYNABUN_BROWSER_TAB` env now wins over caller-supplied `tabId`** — `mcp-server/src/services/neural-interface.ts:88` (`resolveSession`) reordered the resolution: pinned env value takes precedence over the explicit `tabId` argument. If the caller passes a different tabId, it's logged as `[MCP] tabId override ignored` and the pin is honored. Prevents an agent or model from accidentally leaking into another loop's tab by passing the wrong tabId. Rebuilt via `npm run build` in `mcp-server/`.
|
|
102
|
+
|
|
103
|
+
## 2026-04-23
|
|
104
|
+
|
|
105
|
+
### Added — Claude Code Plugin Install from MCP UI
|
|
106
|
+
- **CLI-delegated plugin installer** — Rewrote `neural-interface/lib/plugin-installer.js` to call `claude plugin marketplace add <url>` + `claude plugin install <plugin>@<marketplace>` instead of hand-writing `~/.claude/plugins/*.json`. Claude Code now owns its own marketplace + cache state; installs survive CC restarts. Legacy `synabun-symlink` source type removed.
|
|
107
|
+
- **Auto-repair on server startup** — `server.js` `app.listen` callback invokes `repairSynabunPlugins()`. Scans `~/.claude/plugins/known_marketplaces.json` for legacy `source: synabun-symlink` entries, purges dangling symlinks and stale `installed_plugins.json` rows, reinstalls via CLI from the remembered repo path.
|
|
108
|
+
- **`POST /api/plugins/repair`** — Manual trigger for the same repair flow.
|
|
109
|
+
- **`data/synabun-plugins.json` tracker** — New JSON file SynaBun owns, recording which plugins we installed (CC remains source of truth for install state). `setTrackerPath()` export pins the path to `DATA_HOME/data/synabun-plugins.json` on boot.
|
|
110
|
+
- **GitHub URL preferred over local clone** — `/api/mcp/install/github` claude-plugin branch passes `githubUrl` into the installer; CC re-clones fresh into its own cache so the temp clone at `data/mcp/<name>/repo` is only used to read the manifest.
|
|
111
|
+
|
|
112
|
+
### Added — Plugin Slash Commands in Sidepanel Autocomplete
|
|
113
|
+
- **`GET /api/claude-code/plugin-commands`** — New endpoint in `server.js`. Walks `~/.claude/plugins/cache/*/*/*/commands/*.{toml,md}`, parses `description` from TOML (`description = "..."`) or markdown frontmatter, returns `[{ name, description, source: 'plugin', plugin, marketplace }]`. No caching — fresh scan per request, so newly installed plugins appear without restart.
|
|
114
|
+
- **Autocomplete merge in `ui-claude-panel.js`** — `loadSkills()` adds a fourth merge step after SynaBun skills + CLI built-ins + extras. Plugin commands show in the `/` hint dropdown with `(plugin: <name>)` suffix.
|
|
115
|
+
|
|
116
|
+
### Fixed — Broken Legacy Caveman Install
|
|
117
|
+
- **CC marketplace loader errored with `Marketplace configuration file is corrupted`** — Caused by the old installer writing `"source": "synabun-symlink"` to `known_marketplaces.json`; CC prunes unknown source types and wiped the `cache/caveman/` symlink so `installPath` in `installed_plugins.json` pointed at a missing directory. Hooks never fired. Cleanup + CLI reinstall restored caveman — `claude plugin list` now shows `caveman@caveman ✔ enabled` with a valid `cache/caveman/caveman/<sha>` dir.
|
|
118
|
+
|
|
119
|
+
### Fixed — Sidepanel Slash-Hints Silent Bail
|
|
120
|
+
- **`showSlashHints()` returned early when `_skillsCache` null** — First `/` keystroke could fire before the async `loadSkills()` fetch resolved, leaving no dropdown. Made `showSlashHints` async; awaits `loadSkills()` when cache empty so the dropdown is guaranteed on first keystroke.
|
|
121
|
+
|
|
122
|
+
### Fixed — Sidepanel Slash-Hints Clipped by Input Wrapper
|
|
123
|
+
- **`.cp-input-wrap` had `overflow: hidden`** — Parent wrapper clipped the upward-expanding `.cp-slash-hints` dropdown (positioned `bottom: calc(100% + 4px)`). Removed the overflow rule — the wrapper's conic-gradient border already clips itself via `-webkit-mask-composite: xor`, so the overflow was redundant. Dropdown now expands unclipped and scrolls internally.
|
|
124
|
+
|
|
125
|
+
### Changed — Sidepanel Slash-Hints Dropdown Sizing
|
|
126
|
+
- **`.cp-slash-hints` — `max-height: 200px → 320px`** — Fits more rows before scroll kicks in.
|
|
127
|
+
- **Added `overscroll-behavior: contain`** — Mouse-wheel scroll inside the dropdown no longer bubbles to the message list beneath.
|
|
128
|
+
|
|
129
|
+
### Fixed — GitHub MCP Installer Discarded Servers on Install Failure
|
|
130
|
+
- **`/api/mcp/install/github` soft-fails local dep errors** — In `neural-interface/server.js`, the install endpoint returned HTTP 500 and aborted the whole flow when `pip install -r requirements.txt` or `npm install` errored, even though the clone + detection already succeeded. Reproducible with `AminForou/mcp-gsc`: launch command was `uvx mcp-search-console` (self-fetching) but `pip` missing on PATH made the endpoint bail before registry write. Now install failures attach an `installWarning` field to the 200 response — the server still registers, the user sees a `⚠` line in the GitHub install form, and deps can be fixed manually.
|
|
131
|
+
- **Self-fetcher runtimes skip local install** — Added `commandSelfFetches(detected)` in `neural-interface/lib/mcp-installer.js` that matches `uvx` / `uv run` / `npx` / `pipx` / `bunx` / `pnpm dlx` / `yarn dlx`. When true, `installDependencies` is never called — the server is registered with `install.skipped = true, reason: "command '<cmd>' self-fetches at runtime"` and the UI shows a `⚙ Skipped local deps` hint.
|
|
132
|
+
|
|
133
|
+
### Added — Cross-OS Preflight for Self-Fetching MCP Runtimes
|
|
134
|
+
- **`checkCommandOnPath(cmd)` in `neural-interface/lib/mcp-installer.js`** — OS-agnostic PATH probe. Uses `where <cmd>` on Windows (which resolves `.exe` via PATHEXT), `sh -c 'command -v <cmd>'` on Unix. 5s timeout guard. Returns a boolean.
|
|
135
|
+
- **`checkSelfFetcherAvailable(detected)` preflight** — Called by the install endpoint when `commandSelfFetches` is true. Returns `{ available, command, installHint }`. Missing binary attaches an OS-specific install command to `installWarning` (e.g. *"'uvx' not found on PATH. Install: brew install uv. Server registered anyway — will fail at launch until installed."*).
|
|
136
|
+
- **`SELF_FETCHER_INSTALL_HINTS` map** — Per-OS install commands for `uvx`/`uv`/`pipx`/`npx`/`bunx`/`bun`/`pnpm`/`yarn`. Mac uses `brew`, Linux uses official curl installers, Windows uses `winget` or PowerShell `irm`. `installHintFor(cmd)` picks the row based on `process.platform`. Ensures fresh SynaBun installs on Windows/Mac/Linux get actionable guidance instead of a silent launch failure.
|
|
137
|
+
|
|
138
|
+
### Changed — Codex Sidepanel Reasoning and Response Controls
|
|
139
|
+
- **Expanded Codex reasoning effort support** — Updated `neural-interface/public/shared/cdx/cdx-icons.js`, `cdx-panel.js`, `cdx-styles.js`, and `cdx-tabs.js` so the Codex footer effort toggle now supports the full current set of sidepanel overrides: `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`. The toolbar indicator now renders five effort dots, includes visual states for `minimal` and `xhigh`, and `/effort` validates input while treating `default` and `none` as no per-turn override.
|
|
140
|
+
- **Exposed response-control config keys** — Added `model_reasoning_summary` and `model_verbosity` to `CODEX_CONFIG_KEYS` in `neural-interface/server.js`, then surfaced both in the Codex settings form and `/status` output in `neural-interface/public/shared/cdx/cdx-tabs.js`. Settings now support reasoning summary choices `auto`, `concise`, `detailed`, and `none`, plus response verbosity choices `low`, `medium`, and `high`.
|
|
141
|
+
|
|
142
|
+
### Fixed — Codex Sidepanel Session Cross-Talk
|
|
143
|
+
- **Per-tab WebSocket identity** — `sendSocket()` in `neural-interface/public/shared/cdx/cdx-tabs.js` now stamps every Codex sidepanel WebSocket message with the bound tab's `sessionId`, so bootstrap, query, reattach, model, interrupt, and compact traffic is routed to the owning tab instead of only the shared `windowId`.
|
|
144
|
+
- **Strict orphan reattach keys** — `_codexOrphanKey()` in `neural-interface/server.js` now requires both `windowId` and `sessionId`, and the reattach handler returns `reattach_result: false` when the tab id is missing. The close handler refuses to create window-wide orphans and kills sessionless processes, preventing a newly opened tab from replaying another tab's buffered tool-call events.
|
|
145
|
+
- **Reconnect state cleanup** — `onClose()` sets `pendingReattach` only for abnormal Codex socket closes and `onReattachResult()` clears it, preserving correct orphan recovery for the original tab without leaking state to other sessions.
|
|
146
|
+
|
|
147
|
+
### Fixed — Codex Sidepanel Active-Tab Stop Scope
|
|
148
|
+
- **Stop button aborted every running Codex tab** — The shared composer stop state in `syncInputEnabled()` was keyed off `_tabs.some((t) => t?.running)`, and `cdx-panel.js` wired click/Escape to `interruptAllTabs()`, so stopping one visible tab sent `interrupt` and delayed `force_kill` to every running tab WebSocket. The handlers now call `interruptTurn()` only when `activeTab()?.running`, the stop icon is scoped to the active tab, and `interruptTab()` sends session-scoped stop messages for the bound tab.
|
|
149
|
+
- **Stale force-kill retries could outlive successful interrupts** — `onInterruptAck()` now clears each tab's `_abortRetry`, preventing the fallback timer from killing a Codex app-server after the server already acknowledged an interrupt.
|
|
150
|
+
- **Server accepted unscoped stop messages** — `validateCodexControlMessage()` in `server.js` rejects `interrupt`/`force_kill` without a matching `sessionId` or with a mismatched `threadId`, while `forceKillCodexChild()` logs pid/session/thread/reason before terminating the app-server.
|
|
151
|
+
|
|
152
|
+
### Fixed — Codex Plan Mode Handoff
|
|
153
|
+
- **Post-plan actions no longer get skipped** — `dispatchPrompt()` in `neural-interface/public/shared/cdx/cdx-tabs.js` now marks sent plan turns with `planMode`, `planTurnActive`, and `lastPlanTurnId`, so `handleNotify('turn/completed')` captures the completed plan even if the toolbar `planMode` flag has already changed by the time the turn finishes.
|
|
154
|
+
- **Approval gate persists across tab state** — Added `planApprovalPending` / `planTurnActive` persistence through `snapshotBoundState()`, `restoreBoundState()`, `commitBoundState()`, `bindTabState()`, `createTab()`, and `saveTabs()`, and blocks `sendPrompt()` / `advanceQueue()` until the user chooses Continue, Compact, or Edit.
|
|
155
|
+
- **Continue/Edit paths clear or restore the handoff correctly** — `renderPostPlanActions()` in `cdx-render.js` clears pending plan state before dispatching the non-plan implementation prompt and restores it if send fails, while `cdx-panel.js` marks edited plan saves as pending approval again.
|
|
156
|
+
- **Plan turns run read-only server-side** — `server.js` passes the client `planMode` flag into `startTurn()`, detects explicit `[PLAN MODE]` prompts with `isCodexPlanModePrompt()`, and forces `buildCodexSandboxPolicy(..., 'read-only')` for plan turns.
|
|
157
|
+
- **Prompt text now enforces the sidepanel handoff** — `CODEX_PLAN_MODE_PREFIXES` in `cdx-icons.js` and `server.js` now tells Codex to stop after producing the plan and wait for the sidepanel’s Edit, Compact, or Continue choice.
|
|
158
|
+
|
|
159
|
+
### Fixed — CLI/TUI Rendering Corruption (Terminal)
|
|
160
|
+
- **Stable xterm renderer path** — `loadXterm()` in `neural-interface/public/shared/ui-terminal.js` now intentionally skips `@xterm/addon-webgl` and `@xterm/addon-canvas`, leaving CLI/TUI windows on xterm's DOM renderer. This avoids WebGL glyph atlas/context corruption seen after multiple long-lived floating CLI sessions.
|
|
161
|
+
- **Serialized terminal writes** — Added `_createTerminalWriter()` with 64KB chunking and `term.write()` callback backpressure, then routed fresh session, reconnect, and WebSocket reconnect output through it. This prevents large PTY bursts from piling up unbounded parser/render work during long Codex/Claude/Gemini sessions.
|
|
162
|
+
- **Snapshot-based reconnects** — `createTerminalSession()` in `neural-interface/server.js` now maintains a capped `VTermBuffer` per PTY, updates it on output and resize, and `/ws/terminal/:id` sends a synthesized `snapshot` payload instead of replaying an arbitrary raw ANSI tail. The snapshot includes a plain-text companion so `_sendOnceReady()` can still detect ready prompts without waiting for its fallback timer.
|
|
163
|
+
- **Virtual terminal snapshot support** — `neural-interface/public/shared/vterm-buffer.js` now exposes `useAltBuffer` and accepts a per-instance scrollback cap, letting live PTY sessions keep only the rows needed for reconnect recovery while preserving the larger default for existing capture use cases.
|
|
164
|
+
|
|
165
|
+
## 2026-04-22
|
|
166
|
+
|
|
167
|
+
### Added — Smart "Install from GitHub" (MCP + Claude Code Plugins Auto-Route)
|
|
168
|
+
- **Repo classifier** — New `classifyRepo(repoPath)` in `neural-interface/lib/mcp-installer.js` runs BEFORE `detectServer()` and returns `{ kind: 'mcp' | 'claude-plugin' | 'unknown' }`. Prefers MCP when both markers exist, emits `alsoClaudePlugin` hint for mixed repos, scans for codex/gemini/cursor/windsurf adjacency signals.
|
|
169
|
+
- **Claude Code plugin installer** — New `neural-interface/lib/plugin-installer.js` with `installClaudePlugin()`, `uninstallClaudePlugin()`, `listSynabunPlugins()`. Symlinks the cloned repo into `~/.claude/plugins/marketplaces/<name>` and `~/.claude/plugins/cache/<marketplace>/<plugin>/unknown`, appends SynaBun-tagged entries to `known_marketplaces.json` + `installed_plugins.json` (dedupe by `scope:'user' + installPath`), captures `gitCommitSha` via `git rev-parse HEAD`.
|
|
170
|
+
- **`POST /api/mcp/install/github` branches on classification** — After clone, `kind: 'claude-plugin'` skips MCP detection/deps install and calls `installClaudePlugin`; `kind: 'unknown'` returns 422 with hints list; MCP path unchanged. New endpoints `GET /api/plugins/list` and `DELETE /api/plugins/:marketplace?pluginName=&purgeFiles=1`.
|
|
171
|
+
- **Settings UI rename + plugin list** — "+ Add Server" button renamed to "+ Install from GitHub" in `neural-interface/public/shared/ui-settings.js`. New "Claude Code Plugins" subsection with per-row Uninstall. Install handler swaps the form into "plugin mode" (hides Type/Command/Args/env/platforms/Save) when response is `kind: 'claude-plugin'` since the plugin is already activated server-side.
|
|
172
|
+
|
|
173
|
+
### Added — Installed MCPs Sub-Tab (Cross-CLI Unified View)
|
|
174
|
+
- **`GET /api/mcp/installed-all` endpoint** — Aggregates every externally-installed MCP across SynaBun registry (non-builtin), `~/.claude.json`, `~/.gemini/settings.json`, `~/.codex/config.toml` (regex `^\[mcp_servers\.([^\].]+)\]$` excludes nested `.tools.` sections), and OpenCode's `mcp.*` config. Merges by server name; `registeredWith` is a sorted array of CLI keys.
|
|
175
|
+
- **Sub-tab navigation inside Settings → MCP** — New `.stg-mcp-page-tabs` at top of the page swaps between **Profiles** (existing matrix + always-on + per-profile External Servers toggles) and **Installed** (new cross-CLI list + Claude Code Plugins + Install form). SynaBun's built-in tool groups are excluded from the Installed view.
|
|
176
|
+
- **Per-CLI badges** — Each row in the Installed list shows compact `SB/CC/OC/CX/GE` badges indicating which CLIs have the server registered. Styles added for `.stg-mcp-page-tabs`, `.stg-mcp-page-tab.active`, `.stg-mx-ext-cli-badge`.
|
|
177
|
+
|
|
178
|
+
### Added — Cross-CLI Uninstall Button on Installed-Tab Rows
|
|
179
|
+
- **One-click uninstall across all registered CLIs** — Each row in the Installed sub-tab carries a `×` button with `data-registered` containing its precise CLI list. Click handler confirms using the badge labels (e.g., "Remove foo from CC, OC, CX?"), then: if `registeredWith` includes `synabun` → `DELETE /api/mcp/registry/servers/:name`; for every remaining CLI key → `DELETE /api/mcp/sync` with `{ name, platforms: cliList }`. Refreshes both Installed list and the Profiles-tab External Servers list afterward. Avoids pointless 404 churn by only hitting CLIs that actually have the server.
|