synabun 2026.5.13-beta → 2026.5.14-1
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 +113 -96
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +65 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/src/services/neural-interface.ts +59 -2
- package/neural-interface/public/shared/styles.css +45 -0
- package/neural-interface/public/shared/ui-update.js +159 -51
- package/neural-interface/server.js +142 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
# Project Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## OpenCode Sidepanel V2
|
|
4
4
|
|
|
5
|
-
###
|
|
6
|
-
- **
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
### Added — OpenCode Sidepanel V2 (Clean-Slate Rebuild)
|
|
6
|
+
- **Fresh SDK wrapper `lib/opencode-v2-client.js`** — New ~190-line wrapper around `@opencode-ai/sdk/v2`'s `createOpencodeClient`. Pass-through philosophy: NO `normalizeV2Event` translation, NO `buildSystemForPlanMode` injection, NO legacy shape massaging. `connect({ port })` runs an `event.subscribe()` loop with 1s→10s exponential-backoff auto-reconnect via `AbortController`, emitting `{ eventType, event }` envelopes verbatim through an internal `event.onEvent(listener)` registry. Surfaces `session.list/create/get/delete/abort/messages/prompt` and `permission.reply`; `unwrap()` translates hey-api `{ data, error, response }` to `{ status, data }` and throws on error; `clean()` strips `undefined` keys so the URL builder doesn't append `?foo=undefined`. Verbose log gated on `OCPV2_VERBOSE=1`.
|
|
7
|
+
- **New WS route `/ws/opencode-v2` + REST endpoints** — `neural-interface/server.js` adds `_ocpV2WsClients` Set, `broadcastToOpencodeV2Clients()`, `installOpencodeV2Listener()` (independent bridge from legacy `installOpencodeClientListener`), and `handleOpencodeV2Ws(ws)`. The handler implements `init / session:list / session:create / session:get / session:delete / session:messages / message:send / message:abort / permission:reply` over id-correlated JSON frames, with per-session `AbortController` map so a fresh send cancels the previous in-flight prompt cleanly. New REST: `GET /api/opencode-v2/status`, `POST /api/opencode-v2/serve/start`, `POST /api/opencode-v2/serve/stop`. Wired `opencodeV2Client.connect/disconnect` into both `ensureOpencodeServer` boot points and `stopOpencodeServer`, sharing the same `opencode serve` process on port 4096 as the legacy panel. `/ws/opencode-v2` added to the upgrade allowlist and connection router; legacy `/ws/opencode-skin` route remains registered (orphaned).
|
|
8
|
+
- **Browser modules under `public/shared/ocp-v2/`** — Six fresh modules totaling under 1500 lines:
|
|
9
|
+
- `ocp-v2-styles.js` — `.ocpv2-*` CSS injected once, slide-in panel, message bubbles, tool cards with status badges, reasoning collapsibles, permission cards, error banners, compose footer with autosizing textarea.
|
|
10
|
+
- `ocp-v2-state.js` — pub/sub state store: `serverStatus`, `sessionId`/`sessionInfo`, `messageOrder[]`, `messages: Map<id,{parts:Map}>`, `running`, `pendingPermission`, `errors[]`. Setters fire `notify()` to all `subscribe()` listeners.
|
|
11
|
+
- `ocp-v2-ws.js` — WebSocket client. `api.{init,sessionList,sessionCreate,sessionGet,sessionDelete,sessionMessages,send,abort,permissionReply}` returns id-correlated promises. `handleEvent()` dispatches `message.updated` / `message.removed` / `message.part.updated` / `session.idle` / `session.error` / `permission.asked` / `permission.replied` into state mutations; auto-reconnect after 1.5s on close.
|
|
12
|
+
- `ocp-v2-render.js` — full CLI parity renderers: text (minimal Markdown — paragraphs, fences, inline code, **bold**, *italic*, lists, links — no external deps), reasoning (collapsible `<details>`, open while streaming), tool (header with name + status badge, input JSON, output `<pre>`, error variant), file chip, step markers, permission card with `Allow once / Always / Reject` buttons that lock on click and call `api.permissionReply()`, error banner. Mounts on the messages container and re-renders on every state notification.
|
|
13
|
+
- `ocp-v2-send.js` — compose textarea + Send/Abort button. Enter sends, Shift+Enter newline, optimistic user message rendered before SDK echoes back, button toggles to Abort while `running`. `doAbort()` calls `api.abort(sessionId)` and force-clears `running`.
|
|
14
|
+
- `ocp-v2-panel.js` — slide-in panel container. Lazy boot on first toggle: `api.init()` → `setServerStatus` → `startFreshSession()` (auto-creates one fresh session per panel open, matches CLI single-session model). Header shows `OpenCode · {sid8}` + status pill (`ready` / `running` / `error`) + a `New` button to spawn a new session. Emits `opencode-panel:visibility` so the existing navbar binding still highlights the topright button.
|
|
15
|
+
- **Barrel + navbar swap** — New `public/shared/ui-opencode-panel-v2.js` re-exports `toggleOpencodePanel` / `isOpencodePanelOpen` / `openOpencodeWithPrompt` from `ocp-v2/ocp-v2-panel.js`. Single-line edit in `public/shared/ui-navbar.js` repoints the import from `./ui-opencode-panel.js` → `./ui-opencode-panel-v2.js`. Topright OpenCode button now opens the V2 panel; old `public/shared/ocp/**` (13 files, ~14000 lines), `public/shared/ui-opencode-panel.js`, and `lib/opencode-client.js` remain unmodified on disk as a rollback safety net.
|
|
9
16
|
|
|
10
|
-
|
|
17
|
+
### Added — Reasoning Effort Picker in OpenCode V2 Sidepanel
|
|
18
|
+
- **Variant picker in compose footer** — New `neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js` module renders a small dropdown beside the model picker that lets users pick a reasoning/thinking effort (`low` / `medium` / `high` / `max` / `xhigh` / `minimal` / `none`) for any OpenCode model that exposes a `variants` map. Auto-hides when the active model has no variants. Includes a "default" entry that clears the variant so the SDK falls back to its built-in default.
|
|
19
|
+
- Persists per-model selection in `localStorage` under `ocp-v2-model-variants` so each model remembers its preferred effort across sessions.
|
|
20
|
+
- Subscribes to `config:model` and restores the saved variant whenever the user switches models.
|
|
21
|
+
- Variants ordered by `PREFERRED_ORDER` (`none, minimal, low, medium, high, max, xhigh`); unknown variant names appended alphabetically; entries with `disabled: true` filtered out.
|
|
22
|
+
- **State plumbing** — Added `variant: null` field plus `setVariant()` setter to `neural-interface/public/shared/ocp-v2/ocp-v2-state.js` (emits `config:variant` event).
|
|
23
|
+
- **Send pipeline** — `neural-interface/public/shared/ocp-v2/ocp-v2-send.js` mounts the variant picker before the model picker in `.ocpv2-footer-right` and forwards `variant: s.variant || undefined` on every `api.send()` call. WS API in `neural-interface/public/shared/ocp-v2/ocp-v2-ws.js` carries the field through to the server, and `neural-interface/server.js` forwards it to `opencodeV2Client.session.prompt(...)` (the v2 client already passes through arbitrary fields via `...rest`).
|
|
24
|
+
- **Styles** — Added `#ocpv2-variant-dd` rules to `neural-interface/public/shared/ocp-v2/ocp-v2-styles.js`: brain icon, capitalised variant names, smaller right-anchored menu, reused `ocpv2-model-menu-in` animation.
|
|
11
25
|
|
|
12
26
|
### Fixed — OpenCode V2 Sidepanel Error Auto-Heal
|
|
13
27
|
- **Stale header error pill recovery** — `ocp-v2-state.js` now has explicit `healing`, `clearErrors()`, and `setHealing()` state so transient OpenCode/WebSocket failures no longer leave the sidepanel header stuck on `error` after the connection recovers.
|
|
@@ -32,10 +46,6 @@
|
|
|
32
46
|
- **`hasOpenCodeState` short-circuits on `sessionId`** — V2 plain entries have no `draft`/`plan`/`title` fields; predicate now treats any entry with a `sessionId` as meaningful so placeholder pills render correctly during the pre-boot window.
|
|
33
47
|
- **`tabLabel` fallback for unbooted v2 placeholders** — Placeholder pills show `OpenCode · {sid8}` when no title is persisted yet (v2 plain payload doesn't store titles), instead of a generic "New session" label.
|
|
34
48
|
|
|
35
|
-
### Changed — Sidepanel Header & Tray Pill Polish
|
|
36
|
-
- **`stopPropagation` on OCP header buttons** — `+` (new session), Minimize, Close, and Slide buttons in `ocp-v2-panel.js` now `event.stopPropagation()` so a click no longer bubbles into the session-menu open/close handler. Removes a race where a stray header click could toggle the session dropdown.
|
|
37
|
-
- **`data-tooltip-pos` on every sidepanel pill close button** — Tray pill X buttons in `ui-claude-panel.js` (`.cp-session-pill`), `cdx/cdx-tabs.js` (`.cxp-session-pill`), `ocp-v2-panel.js` (`.ocpv2-session-pill`), and the placeholder pills in `ui-sidepanel-tray.js` all got `data-tooltip-pos="top"` so the "Close" tooltip stops clipping at the bottom edge of the tray. OCP header buttons got `data-tooltip-pos="bottom"` for symmetry.
|
|
38
|
-
|
|
39
49
|
### Fixed — OpenCode V2 Sub-Agent Sidepanel Reliability
|
|
40
50
|
- **Sub-agent tray pill no longer disappears on spawn** — Sub-agent sessions (e.g. the `Task` / `Explore` tool) used to fire server-side without producing a tray pill, leaving the user to dig through the Resume header. Multiple detection gaps fixed in `ocp-v2-manager.js`: new `readParentID()` helper reads `parentID` / `parentId` / `parent_id` / `session.parentID` variants; `AGENT_TOOL_NAMES` expanded with `invoke` / `subagent` / `spawn` / `explore` / `plan` casing variants; new `scanForOrphanChildren()` calls `api.sessionList()` on `registerPrimaryPanel` and `bindPrimarySession` to find children whose `parentID` matches the active session; a new `session.updated` listener catches sub-agents whose `parentID` arrives after creation; the fallback tool-event path no longer requires strict parent-equality so a mid-spawn tab switch still attaches.
|
|
41
51
|
- **Late `session.created` events buffered** — `ocp-v2-ws.js` now keeps a 32-entry / 60 s TTL ring of `session.created` events and replays them to every new `subscribeSessionCreated` subscriber. The panel manager registers only after `boot()` finishes, so child spawns that fired during boot used to be dropped silently — now they are seen.
|
|
@@ -52,44 +62,9 @@
|
|
|
52
62
|
- **Header status shows the task, not "idle"** — `ocp-v2-childpanel.js` accepts a new `taskInfo` option and exposes `instance.setTaskInfo()`. New `currentTaskLabel(s)` builds the badge from (priority) the currently-running tool name → `${subagentType} · ${summary}` → summary alone → subagent type alone. `syncHeader()` displays the label both while running and while idle, with the same string as `data-tooltip`. `ocp-v2-manager.js` added `describeAgentTask(part, ev, toolName)` to extract `{subagentType, summary}` from the parent's tool input (`state.input.description` / `state.input.prompt` / `subagent_type` / `agent`) and `lookupParentTaskInfo(childSid)` which scans the parent store's recent tool parts when no replay part is available (session.created / session.updated / orphan-scan paths). Task info is threaded through `ensureChildSpawn` → `spawnChildPanel` → `createChildPanel`.
|
|
53
63
|
- **Child pill tooltip pops left, not above** — `ocp-v2-childpanel.js` sets `data-tooltip-pos="left"` on both the child tray pill and its close button. The tray is right-anchored, so `above` tooltips were getting clipped at the screen edge; left placement uses `ui-tooltip.js`'s existing left path. The tray pill tooltip also appends ` — ${taskLabel}` so the minimized state still surfaces the agent's task.
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
### Fixed — Long-Session Memory Leaks
|
|
58
|
-
- **Bounded `_orphanedProcs` map** — Claude-skin orphan processes accumulated in `server.js` when a kill timer never fired (crash, kill -9, sleep) and an entry was never reattached. Added a 60-second sweep (`setInterval(...).unref()`) that deletes entries whose `proc` is null/killed/exited and clears the stale `killTimer`.
|
|
59
|
-
- **Pruned `_ocpSessionOwners` / `_ocpSessionActivity`** — `_ocpStartSilenceWatchdog` in `server.js` now removes session entries with no live clients and `lastEventAt` older than 30 minutes. Watchdog scan no longer iterates dead sessions for the lifetime of the process.
|
|
60
|
-
- **Idle screencast frame waste** — `onScreencastFrame` in `server.js` now bails out early (still ack'ing `Page.screencastFrameAck` to keep the CDP pipeline alive) when `session.clients` is empty, skipping JPEG buffer allocation and broadcast. Removed the per-frame `frameCount++` and conditional `console.log` that allocated even when not logging.
|
|
61
|
-
|
|
62
|
-
### Changed — Whiteboard Drag Hot Path
|
|
63
|
-
- **rAF-batched `renderAll()`** — `ui-whiteboard.js` now schedules renders through `requestAnimationFrame` via `_renderRafId`. Multiple mousemove handlers used to call `renderAll()` synchronously per pointer event; renders are coalesced to one per frame. Added `renderAllImmediate()` for explicit flushes.
|
|
64
|
-
- **`_elementMap` O(1) lookups** — Added `_findEl(id)` Map cache. Rebuilt lazily when `_elements.length` changes; `_invalidateElementMap()` is called from `undo()`/`redo()` to cover same-length object replacement. Replaced 8 `_elements.find(el2 => el2.id === ...)` calls inside `onMouseMove` (move-multi, shape-create, section-create, move, move-arrow, move-pen, rotate, resize).
|
|
65
|
-
- **Cached root rect** — Added `_rootRectCached` + `_getRootRect()`/`_invalidateRootRect()`. `clientToCanvas()` and the pen-drawing path reuse the cached rect for the duration of a gesture; cache is cleared at mouseup and on window resize. Eliminates `_root.getBoundingClientRect()` layout flushes inside the 60 Hz mousemove loop.
|
|
66
|
-
|
|
67
|
-
### Changed — Sidepanel Markdown Rendering
|
|
68
|
-
- **OCP v2 incremental render** — Rewrote `render()` in `ocp-v2-render.js` to use a key/sig reconciler (`_reconcile`, `_partSig`, `_msgSig`) instead of `_container.innerHTML = ''` followed by a full rebuild. User messages, assistant part bubbles, error banners, permission cards, question cards, and the post-plan card now have stable composite keys and content signatures so unchanged nodes are reused. Scroll position is far more stable across streaming deltas.
|
|
69
|
-
- **LRU markdown memoization** — Added a 512-entry insertion-order LRU cache around `renderMarkdown()` in `ocp-v2-render.js`, `md()` in `cdx/cdx-render.js`, and `md()` in `ocp/ocp-render.js`. Identical text re-rendered during streaming/state-change passes now returns cached HTML instead of re-invoking `marked.parse()`.
|
|
70
|
-
|
|
71
|
-
### Changed — Storage Layer
|
|
72
|
-
- **Deferred `JSON.parse` to flush time** — `storage.js` introduces `STR_SENTINEL`. `setItem()` no longer parses the caller-provided string on every call; `_dirty[key] = STR_SENTINEL` records "parse the current cache string at flush time". `_flushToServer()` and the `beforeunload` `sendBeacon` path resolve sentinels by parsing the cached string once when actually flushing. Big win for callers that store large snapshots (whiteboard, codex transcripts) where every keystroke previously triggered a redundant `JSON.parse`.
|
|
73
|
-
|
|
74
|
-
### Changed — Floating Terminal Drag Compositing
|
|
75
|
-
- **Simplified `.term-float-tab.float-dragging` shadow** — In `styles.css`, replaced the multi-layer `box-shadow: var(--shadow-2xl), 0 0 0 1px rgba(0,0,0,0.4)` with a single `0 8px 24px rgba(0,0,0,0.45), 0 0 0 1px rgba(0,0,0,0.4)`, dropped the `box-shadow` transition (kept `border-radius`), and added `will-change: transform, left, top` so the dragging window stays on its own composited layer. Removes per-frame GPU compositing thrash during drag.
|
|
76
|
-
|
|
77
|
-
## 2026-05-09
|
|
78
|
-
|
|
79
|
-
### Added — Reasoning Effort Picker in OpenCode V2 Sidepanel
|
|
80
|
-
|
|
81
|
-
- **Variant picker in compose footer** — New `neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js` module renders a small dropdown beside the model picker that lets users pick a reasoning/thinking effort (`low` / `medium` / `high` / `max` / `xhigh` / `minimal` / `none`) for any OpenCode model that exposes a `variants` map. Auto-hides when the active model has no variants. Includes a "default" entry that clears the variant so the SDK falls back to its built-in default.
|
|
82
|
-
- Persists per-model selection in `localStorage` under `ocp-v2-model-variants` so each model remembers its preferred effort across sessions.
|
|
83
|
-
- Subscribes to `config:model` and restores the saved variant whenever the user switches models.
|
|
84
|
-
- Variants ordered by `PREFERRED_ORDER` (`none, minimal, low, medium, high, max, xhigh`); unknown variant names appended alphabetically; entries with `disabled: true` filtered out.
|
|
85
|
-
- **State plumbing** — Added `variant: null` field plus `setVariant()` setter to `neural-interface/public/shared/ocp-v2/ocp-v2-state.js` (emits `config:variant` event).
|
|
86
|
-
- **Send pipeline** — `neural-interface/public/shared/ocp-v2/ocp-v2-send.js` mounts the variant picker before the model picker in `.ocpv2-footer-right` and forwards `variant: s.variant || undefined` on every `api.send()` call. WS API in `neural-interface/public/shared/ocp-v2/ocp-v2-ws.js` carries the field through to the server, and `neural-interface/server.js` forwards it to `opencodeV2Client.session.prompt(...)` (the v2 client already passes through arbitrary fields via `...rest`).
|
|
87
|
-
- **Styles** — Added `#ocpv2-variant-dd` rules to `neural-interface/public/shared/ocp-v2/ocp-v2-styles.js`: brain icon, capitalised variant names, smaller right-anchored menu, reused `ocpv2-model-menu-in` animation.
|
|
65
|
+
---
|
|
88
66
|
|
|
89
|
-
##
|
|
90
|
-
|
|
91
|
-
### Changed — Empty-Plan Re-Prompt Button (Claude Panel)
|
|
92
|
-
- **Replaced "Plan text was empty" status text with an interactive button** — When the user clicks `Edit plan` in the post-plan card but the plan extractor and `/api/latest-plan` disk fallback both fail to surface a plan file, `neural-interface/public/shared/ui-claude-panel.js` previously printed a static `appendStatus()` line ("Plan text was empty — ask Claude to re-output the plan, or click Continue with implementation to proceed."). The `noFile()` callback in the `'plan'` action handler now renders an inline `msg-status` row containing the short text "Plan text was empty." plus a primary `post-plan-action pp-primary` button labeled "Ask Claude to re-output the plan". Clicking the button populates `#cp-input` with a re-prompt instruction (`Please re-output the full plan you were preparing. Use ExitPlanMode with the complete plan text...`) and calls `send()`. Button disables on click, and falls back to a status if the WebSocket is closed. Scoped to the Claude panel only — `ocp-panel.js` retains the existing `renderErrorMessage` pattern.
|
|
67
|
+
## OpenCode Sidepanel (Legacy)
|
|
93
68
|
|
|
94
69
|
### Added — OpenCode Sidepanel Trace System
|
|
95
70
|
- **`ocp-trace.js` client trace bus** — New `neural-interface/public/shared/ocp/ocp-trace.js` exporting `trace()`, `traceTimer()`, `setTraceLevel()`, `setTraceContext()`, `onTrace()`, and `exportTraceLog()`. 5000-event ring buffer, level-gated routing (silent/errors/normal/verbose, persisted in `localStorage:ocp.traceLevel`), CSS-styled `[ocp-trace]` `console.debug` mirror, 250ms batched POST to `/api/ocp/trace/ingest`, `keepalive: true` flush on `pagehide`/`visibilitychange`.
|
|
@@ -101,37 +76,16 @@
|
|
|
101
76
|
- **4th recovery button** — `renderProseQuestionRecoveryCard()` in `neural-interface/public/shared/ocp/ocp-render.js` now renders a "Force question tool now" button alongside the existing Reply directly / Re-ask / Dismiss buttons. Wired in `ocp-plan.js` to send a hard prompt with a literal `question({ questions: [...] })` one-shot example so models like Kimi K2.6 can pattern-match the tool-call shape.
|
|
102
77
|
- **`question:list` polling fallback** — `startQuestionPolling()` / `stopQuestionPolling()` in `neural-interface/public/shared/ocp/ocp-questions.js`. While a plan turn is active and no question card is visible, polls `question.list()` every 2.5s and surfaces any pending question via `backfillPendingQuestions()`. Wired from `ocp-send.js` to start on plan-mode turn start and stop on resolve / abort / error.
|
|
103
78
|
|
|
104
|
-
### Fixed — OpenCode
|
|
105
|
-
- **
|
|
106
|
-
- **
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- **`
|
|
79
|
+
### Fixed — OpenCode Plan Mode Forces `question` Tool + Prose-Question Recovery
|
|
80
|
+
- **Plan-mode prompt-prefix injection** — `neural-interface/server.js` previously sent `body.mode='plan'` and `body.tools={...}` without any prompt instruction, so models like Kimi K2.6 wrote clarification questions as prose ("Before I proceed, a couple of quick questions:") instead of calling the OpenCode `question` tool — the sidepanel only renders interactive cards for actual `question` tool calls, so prose questions stranded the user. New `OPENCODE_PLAN_MODE_INSTRUCTION` constant (mirroring the existing `CODEX_PLAN_MODE_PREFIXES` at ~line 2828) is now prepended to the first text part in every `body.mode === 'plan'` send: it explicitly states `You MUST call the \`question\` tool`, forbids prose questions, and reminds the model to call `ExitPlanMode` when ready. Idempotent via a `startsWith('[PLAN MODE]')` guard so the prefix isn't double-injected on multi-turn plan refinement.
|
|
81
|
+
- **Per-turn question-tool usage tracking** — `neural-interface/public/shared/ocp/ocp-tabs.js` adds a `tab._questionToolUsedThisTurn` flag set to `true` in all three question-detection sites (`message.part.updated` tool branch, `tool.start` handler, and the dedicated `question.asked` SSE event), reset to `false` at the start of `sendMessage()`, when entering plan mode in `setTabMode()`, and on tab clear.
|
|
82
|
+
- **Prose-question detector** — New `looksLikeProseQuestions(content)` helper scans the last 400 chars of captured plan text for trailing patterns: `quick/a couple of/two/before i proceed … questions:`, `Question N:` markers, or 2+ trailing `?` per line.
|
|
83
|
+
- **`maybeShowPostPlanUI` recovery reroute** — When `!force && !_exitPlanDetected && !_questionToolUsedThisTurn && looksLikeProseQuestions(planContent)`, the function now calls `showProseQuestionRecoveryUI(tab)` and returns `false` instead of stamping the misleading PLAN COMPLETE card. Fixes the long-standing "press STOP after prose questions → PLAN COMPLETE for half-finished plan" trap.
|
|
84
|
+
- **`MODEL ASKED QUESTIONS IN PROSE` recovery card** — New `renderProseQuestionRecoveryCard()` / `removeProseQuestionRecoveryCards()` exports in `neural-interface/public/shared/ocp/ocp-render.js`, plus a new `.ocp-prose-question-card` block in `neural-interface/public/shared/ocp/ocp-styles.js` (amber accent so it's visually distinct from the green-ish PLAN COMPLETE). Three actions: **Reply directly** focuses the OCP input field; **Re-ask using question tool** sends a follow-up prompt instructing the model to re-ask using the `question` tool; **Dismiss** removes the card. Cards are also cleared on next `sendMessage()` and on `setTabMode()` exit-plan.
|
|
110
85
|
|
|
111
86
|
### Changed — OpenCode Plan-Mode System Instruction
|
|
112
87
|
- **One-shot `question` tool example** — `PLAN_MODE_INSTRUCTION` in `neural-interface/lib/opencode-client.js` and the duplicate `OPENCODE_PLAN_MODE_INSTRUCTION` in `neural-interface/server.js` now include a literal `question({ questions: [{ question, header, options: [{label, description}], multiSelect }] })` example block plus a "If you find yourself writing 'let me ask a few questions'… STOP and call the `question` tool" reinforcement. Kimi K2.6 ignored the abstract directive but pattern-matches the concrete example.
|
|
113
88
|
|
|
114
|
-
### Added — OpenCode Sidepanel V2 (Clean-Slate Rebuild)
|
|
115
|
-
- **Fresh SDK wrapper `lib/opencode-v2-client.js`** — New ~190-line wrapper around `@opencode-ai/sdk/v2`'s `createOpencodeClient`. Pass-through philosophy: NO `normalizeV2Event` translation, NO `buildSystemForPlanMode` injection, NO legacy shape massaging. `connect({ port })` runs an `event.subscribe()` loop with 1s→10s exponential-backoff auto-reconnect via `AbortController`, emitting `{ eventType, event }` envelopes verbatim through an internal `event.onEvent(listener)` registry. Surfaces `session.list/create/get/delete/abort/messages/prompt` and `permission.reply`; `unwrap()` translates hey-api `{ data, error, response }` to `{ status, data }` and throws on error; `clean()` strips `undefined` keys so the URL builder doesn't append `?foo=undefined`. Verbose log gated on `OCPV2_VERBOSE=1`.
|
|
116
|
-
- **New WS route `/ws/opencode-v2` + REST endpoints** — `neural-interface/server.js` adds `_ocpV2WsClients` Set, `broadcastToOpencodeV2Clients()`, `installOpencodeV2Listener()` (independent bridge from legacy `installOpencodeClientListener`), and `handleOpencodeV2Ws(ws)`. The handler implements `init / session:list / session:create / session:get / session:delete / session:messages / message:send / message:abort / permission:reply` over id-correlated JSON frames, with per-session `AbortController` map so a fresh send cancels the previous in-flight prompt cleanly. New REST: `GET /api/opencode-v2/status`, `POST /api/opencode-v2/serve/start`, `POST /api/opencode-v2/serve/stop`. Wired `opencodeV2Client.connect/disconnect` into both `ensureOpencodeServer` boot points and `stopOpencodeServer`, sharing the same `opencode serve` process on port 4096 as the legacy panel. `/ws/opencode-v2` added to the upgrade allowlist and connection router; legacy `/ws/opencode-skin` route remains registered (orphaned).
|
|
117
|
-
- **Browser modules under `public/shared/ocp-v2/`** — Six fresh modules totaling under 1500 lines:
|
|
118
|
-
- `ocp-v2-styles.js` — `.ocpv2-*` CSS injected once, slide-in panel, message bubbles, tool cards with status badges, reasoning collapsibles, permission cards, error banners, compose footer with autosizing textarea.
|
|
119
|
-
- `ocp-v2-state.js` — pub/sub state store: `serverStatus`, `sessionId`/`sessionInfo`, `messageOrder[]`, `messages: Map<id,{parts:Map}>`, `running`, `pendingPermission`, `errors[]`. Setters fire `notify()` to all `subscribe()` listeners.
|
|
120
|
-
- `ocp-v2-ws.js` — WebSocket client. `api.{init,sessionList,sessionCreate,sessionGet,sessionDelete,sessionMessages,send,abort,permissionReply}` returns id-correlated promises. `handleEvent()` dispatches `message.updated` / `message.removed` / `message.part.updated` / `session.idle` / `session.error` / `permission.asked` / `permission.replied` into state mutations; auto-reconnect after 1.5s on close.
|
|
121
|
-
- `ocp-v2-render.js` — full CLI parity renderers: text (minimal Markdown — paragraphs, fences, inline code, **bold**, *italic*, lists, links — no external deps), reasoning (collapsible `<details>`, open while streaming), tool (header with name + status badge, input JSON, output `<pre>`, error variant), file chip, step markers, permission card with `Allow once / Always / Reject` buttons that lock on click and call `api.permissionReply()`, error banner. Mounts on the messages container and re-renders on every state notification.
|
|
122
|
-
- `ocp-v2-send.js` — compose textarea + Send/Abort button. Enter sends, Shift+Enter newline, optimistic user message rendered before SDK echoes back, button toggles to Abort while `running`. `doAbort()` calls `api.abort(sessionId)` and force-clears `running`.
|
|
123
|
-
- `ocp-v2-panel.js` — slide-in panel container. Lazy boot on first toggle: `api.init()` → `setServerStatus` → `startFreshSession()` (auto-creates one fresh session per panel open, matches CLI single-session model). Header shows `OpenCode · {sid8}` + status pill (`ready` / `running` / `error`) + a `New` button to spawn a new session. Emits `opencode-panel:visibility` so the existing navbar binding still highlights the topright button.
|
|
124
|
-
- **Barrel + navbar swap** — New `public/shared/ui-opencode-panel-v2.js` re-exports `toggleOpencodePanel` / `isOpencodePanelOpen` / `openOpencodeWithPrompt` from `ocp-v2/ocp-v2-panel.js`. Single-line edit in `public/shared/ui-navbar.js` repoints the import from `./ui-opencode-panel.js` → `./ui-opencode-panel-v2.js`. Topright OpenCode button now opens the V2 panel; old `public/shared/ocp/**` (13 files, ~14000 lines), `public/shared/ui-opencode-panel.js`, and `lib/opencode-client.js` remain unmodified on disk as a rollback safety net.
|
|
125
|
-
|
|
126
|
-
## 2026-05-06
|
|
127
|
-
|
|
128
|
-
### Fixed — Terminal ESC Key Now Aborts CLI TUIs On First Press (Floating + Docked)
|
|
129
|
-
- **Single ESC reaches the PTY instead of `\x1b\x1b`** — In `neural-interface/public/shared/ui-terminal.js`, the global window-capture ESC handler in `initTerminal()` sent `\x1b` over the WS without `e.preventDefault()` / `e.stopImmediatePropagation()`, so xterm.js's own keydown listener on `.xterm-helper-textarea` fired afterwards and re-emitted ESC via `term.onData(...)` (line 1915). The PTY received `\x1b\x1b`, which Bubbletea-based TUIs (OpenCode, Claude Code, Codex) interpret as a Meta-prefix and ignore — so the running task never aborted. Handler now sends one `\x1b`, calls `e.preventDefault()` + `e.stopImmediatePropagation()` to block the duplicate, and re-focuses via `requestAnimationFrame(() => session.term?.focus())` instead of the old `term?.blur()` / `ta.blur()` pair (focus loss had forced the user to click back into the terminal between keystrokes).
|
|
130
|
-
- **Docked terminal now has ESC coverage** — Previous early return `if (!isXterm) return;` left the docked panel without any ESC fallback when focus drifted to a tab title or panel chrome. Handler now resolves the target session by DOM ancestry: xterm helper textarea → its viewport's `dataset.sessionId`, else `.term-float-tab` → that tab's session, else inside `_panel` (`#terminal-panel`) → `_sessions[_activeIdx]`. Skips non-terminal `input/textarea/select/[contenteditable]` so search/rename/path inputs still consume ESC normally.
|
|
131
|
-
- **Removed redundant per-floating-window ESC branch** — `attachTab()`'s `win.addEventListener('keydown', ...)` had a duplicate ESC sender that was unreachable for the common case (skipped when target was a textarea). Stripped the ESC branch; the Ctrl+C copy-on-selection branch stays as the floating-window-only fallback.
|
|
132
|
-
|
|
133
|
-
## 2026-05-05
|
|
134
|
-
|
|
135
89
|
### Fixed — OpenCode Plan-Mode Stall Recovery Now Always Shows PLAN COMPLETE (Followup)
|
|
136
90
|
- **PLAN COMPLETE always renders after a stall** — In `neural-interface/public/shared/ocp/ocp-tabs.js`, the `executePlanStallRecovery()` added in the prior fix took the wrong fallback when `tab.planContent` ended up shorter than 40 chars: it called `showProseQuestionRecoveryUI(tab)` (which renders a card titled `MODEL ASKED QUESTIONS IN PROSE`), instead of the user-expected `PLAN COMPLETE`. Reproduced on `opencode-go/kimi-k2.6` plan mode where the user reported "the agent asked a few questions … then automatically returned an error … but why isn't it presenting the plan complete normally?". Recovery now unconditionally calls `maybeShowPostPlanUI(tab, { source: \`stall:${reason}\`, force: true })` — `force: true` skips both the 40-char gate AND the prose-question guard. The prose-question recovery card is for a different bug class (model wrote questions as text instead of using the `question` tool) and should never be the stall fallback.
|
|
137
91
|
- **Broader content extraction via `extractPlanTextLoose`** — The previous recovery called `capturePlanContent(tab)` which only adopts DOM text > 80 chars from the *last* `.ocp-msg-assistant` bubble, missing two key paths used by kimi-k2.6: (1) the live thinking block content (rendered into `.ocp-think-content-live` by the new inline `<think>` parser), and (2) `tab.planContent` not being populated by `message.part.delta` events (deltas update the DOM only — only `message.part.updated` updates `tab.planContent`). Recovery now calls `extractPlanTextLoose(tab)` which already covers all six extraction sources: existing `tab.planContent` → `ExitPlanMode` tool args → `todowrite`/`tasks` todos → final assistant text bubble (>40 chars) → standalone `.ocp-think-block .ocp-think-content` (matches both finalized AND live-streaming thinking blocks since the live block carries both classes) → subagent (Agent/task) tool result → last-resort any assistant bubble.
|
|
@@ -144,6 +98,18 @@
|
|
|
144
98
|
- **Couples with finalization watchdog clearing** — `clearPlanFinalizationWatchdog(tab)` now also calls `clearPlanStallTimers(tab)`, so all 16 existing terminal/abort sites (message.completed, session.status:idle, session.idle, session.error, abortTab, sendMessage start/resolved/catch, setTabMode, etc.) clear stall timers without per-site edits.
|
|
145
99
|
- **Soft-prompt card** — New `renderPlanStallSoftCard()` in `neural-interface/public/shared/ocp/ocp-render.js` mirrors the prose-question recovery card with a cool-blue accent (vs. amber) so users can visually distinguish "stuck" from "asked questions in prose". CSS in `neural-interface/public/shared/ocp/ocp-styles.js` (`.ocp-plan-stall-card` and friends).
|
|
146
100
|
|
|
101
|
+
### Fixed — OpenCode Plan Mode Auto-Finalizes After ExitPlanMode
|
|
102
|
+
- **PLAN COMPLETE without manual STOP** — When a plan-mode model emitted `ExitPlanMode` but the OpenCode server never delivered a terminal `session.idle` / `message.completed`, the post-plan card stayed buried until the user pressed STOP (which triggered `message:abort` → server-emitted `session.idle` → post-plan funnel). The `schedulePlanFinalizationWatchdog()` in `neural-interface/public/shared/ocp/ocp-tabs.js` was previously armed only inside `recordAgentChildEvent` for the Deepseek subagent-idle case, never for parent-level `ExitPlanMode` without subagents (e.g., Anthropic plan mode). The watchdog is now armed (with a 5 s timeout) in BOTH ExitPlanMode detection sites — the legacy `tool.start` handler and the modern `message.part.updated` handler — replacing the lone `clearPlanFinalizationWatchdog(tab)` call with a clear-then-schedule pair.
|
|
103
|
+
- **Parameterized timeout** — `schedulePlanFinalizationWatchdog(tab, { timeoutMs = 8000 } = {})` accepts an optional `timeoutMs`; the subagent-idle case keeps the 8 s default while ExitPlanMode-detected uses 5 s (the tool already returned, terminal event should arrive in 1–3 s).
|
|
104
|
+
- **Relaxed min-length guard on watchdog fire** — When `_exitPlanDetected = true`, the 40-char `tab.planContent` minimum is bypassed so plans whose text only lives on `toolInput.plan` (no separate assistant prose) still render. The existing `maybeShowPostPlanUI(tab, { force: tab._exitPlanDetected })` already passes `force: true` for the tool-detected path.
|
|
105
|
+
|
|
106
|
+
### Fixed — OpenCode Question Reply "Fetch Failed" Frozen UI
|
|
107
|
+
- **Non-terminal reply error path** — In `neural-interface/public/shared/ocp/ocp-questions.js renderOpencodeQuestion`, the `requestWs('question:reply')` catch handler no longer flips `tab.running=false` / `endTurn(tab)`. Instead it re-enables the question card (`locked=''`, buttons re-enabled) and shows "Click an answer again to retry." Previously a transient SDK fetch error caused the entire turn to be marked complete, freezing the UI behind a stale card.
|
|
108
|
+
- **Composer unblocking on prose-recovery dismiss** — `onReplyDirectly` and `onDismiss` callbacks in `ocp-plan.js showProseQuestionRecoveryUI` now defensively clear `tab.showPostPlanActions = false` so the composer always re-enables when the user closes the prose-recovery card.
|
|
109
|
+
|
|
110
|
+
### Fixed — OpenCode Sidepanel Question Card Multi-Question Routing Trace
|
|
111
|
+
- **`event:in` + `q:queued` diagnostics** — `neural-interface/public/shared/ocp/ocp-events.js` now traces every routed SDK event with `eventType`, `sid`, `partType`, `toolName`, plus `q:asked` (with question count) and `q:queued` (with `reason: no-container | active-question-busy`). Makes it possible to determine — from the trace alone — whether the model emitted `question.asked`, whether the router rendered or queued it, and why.
|
|
112
|
+
|
|
147
113
|
### Fixed — OpenCode Sidepanel Live Thinking Stream for Inline `<think>` Tag Models
|
|
148
114
|
- **CLI-style typing for Kimi K2 / DeepSeek R1 / Qwen QwQ via opencode-go** — In `neural-interface/public/shared/ocp/ocp-render.js`, models that emit reasoning as inline `<think>…</think>` tags (rather than as a structured `partType === 'reasoning'` part) previously had their reasoning render as raw `<think>` text in the live bubble, with `<think>` parsing only happening at `finalizeStreamingMessage()`. The user complaint: thinking should "type into" the sidepanel as it does in the OpenCode CLI. New stateful parser `_routeStreamingChunk(el, container, chunk)` walks each delta, splits at `<think>` / `</think>` boundaries, and routes inside-tag content to the live thinking block via `_appendThinkTextOnly` and outside-tag content to the live text node via `_appendRawTextOnly`.
|
|
149
115
|
- **Element-level state** — `el._thinkInside` tracks whether subsequent deltas continue inside an unclosed `<think>` block. `el._thinkPendingTag` buffers a partial tag that lands at a chunk boundary (e.g., delta ending with `<thi` or `</thin`); `_matchTrailingTagPrefix(s, target)` finds the longest suffix of `s` that prefixes `target`, so the partial flushes correctly when the rest of the tag arrives in the next delta.
|
|
@@ -157,10 +123,19 @@
|
|
|
157
123
|
- **Removed obsolete render path** — Deleted `renderStreamingElement()` and `scheduleStreamingRender()` (the 16 ms `innerHTML`-rebuild path) since the live append path replaces it.
|
|
158
124
|
- **Live styling cues** — Added `.ocp-think-block-live` (slightly brighter border) and a pulsing `Thinking…` summary label via `@keyframes ocp-think-pulse` in `neural-interface/public/shared/ocp/ocp-styles.js`, plus `scroll-behavior: smooth` on `.ocp-think-content-live`.
|
|
159
125
|
|
|
160
|
-
### Fixed — OpenCode
|
|
161
|
-
- **
|
|
162
|
-
|
|
163
|
-
|
|
126
|
+
### Fixed — OpenCode Sidepanel Project Selector Lost the Project Path
|
|
127
|
+
- **Session created in the right directory** — The `session:create` WS handler in `neural-interface/server.js` now forwards the panel-supplied cwd to OpenCode as `POST /session?directory=<encoded path>`. OpenCode binds each session to the directory passed in this query parameter (`session.directory`); previously the call omitted it, so every OpenCode session inherited the serve cwd (`PACKAGE_ROOT` = SynaBun) and ALL agent tool calls (Read, Bash, Edit, Write) resolved relative to SynaBun even when the user had picked a different project in the footer. With the query param wired through, OpenCode's tools natively resolve at the project root the user selected.
|
|
128
|
+
- **Fresh-session cwd routing** — Switching the project via the OpenCode footer dropdown previously mutated only `tab.project` in `neural-interface/public/shared/ocp/ocp-panel.js`, leaving the existing OpenCode session bound to the cwd it was first created with. Added `setActiveProject()` in `neural-interface/public/shared/ocp/ocp-tabs.js` that updates `tab.project` + `STOR.project`, drops the bound `tab.sessionId` (along with title, token usage, plan/post-plan, watchdogs, tool activity, question/permission queues, and snapshot flush), refreshes the project chip label, and clears the messages container so the next `sendMessage()` creates a fresh OpenCode session at the new cwd. The projectbar dropdown click handler and the name-modal commit now both route through `setActiveProject()` instead of touching `tab.project` directly.
|
|
129
|
+
- **Greeting carries the absolute project path** — Updated `buildOpencodeGreetingUserPrompt()` in `neural-interface/server.js` to spell out the absolute project working directory (`Project working directory: \`/abs/path\``) and instruct the agent to use it for all file operations whenever the selected project differs from `PACKAGE_ROOT`. Belt-and-suspenders alongside the new `?directory=` query param, so the agent's first turn explicitly states the cwd it should be operating in.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Claude Sidepanel
|
|
134
|
+
|
|
135
|
+
### Fixed — Sidepanel Pills Open Blank Session After Server Restart
|
|
136
|
+
- **Pill click landed on "New chat" instead of resuming the underlying Claude session** — `loadConfig()` in `neural-interface/public/shared/ui-claude-panel.js` detected a server-bootId mismatch and `removeItem`'d `STOR.tabs` / `STOR.tabsLegacy` / `STOR.session` outright. Pills (rendered by `ui-sidepanel-tray.js`) read those same keys but render before the panel inits, so they survived the restart visually — but the first pill click triggered `toggleClaudePanel` → `loadConfig` → wipe → `restoreTabs` found nothing → fell through to `createTab(null, 'New chat')`.
|
|
137
|
+
- Replaced the wipe with a new `_scrubStaleTabState(key)` helper added above `restoreTabs()`. On bootId mismatch it now **preserves** resumable identity (`id`, `sessionId`, `label`, `project`, `model`, `effort`) and **drops** runtime-only state that would lie post-restart (`running` / `_wasRunning`, `queue` / `queuePaused`, `planMode` / `planFilePath`, `sessionCost` — re-synced via `syncSessionCosts()`). Tabs with no `sessionId` and only a default label ("New chat", "New session", "Claude") are pruned; the legacy `STOR.session` single-sessionId key is left intact.
|
|
138
|
+
- Codex and OpenCode panels never had a bootId wipe, so their pills already worked across restarts — Claude was the lone outlier.
|
|
164
139
|
|
|
165
140
|
### Fixed — Claude Sidepanel Plan-Mode "Plan Text Was Empty" Edit Flow
|
|
166
141
|
- **Server-side plan file authoring before `killProc()`** — `neural-interface/server.js` ExitPlanMode hook (`handleClaudeSkinWebSocket`, ~line 6616) now calls `writePlanFile(inputPlan || planText, { cwd: workDir })` before the kill. Previously the kill (intentional, prevents `--print`-mode auto-implementation) ran before `PostToolUse` could fire, so `post-plan.mjs` never authored a plan file from `tool_input.plan`. Result: clicking "Edit Plan" hit `noFile()` with "Plan text was empty — ask Claude to re-output the plan…". Server now writes `data/plans/YYYY-MM-DD/<slug>.md` directly from `ExitPlanMode.input.plan` (or the preceding text block as fallback).
|
|
@@ -170,34 +145,76 @@
|
|
|
170
145
|
- **Removed redundant `/api/create-plan` POST from 3 eager-capture sites** — `renderAssistant()` (ExitPlanMode-detection block, partial-message dedup branch, new-message branch) no longer POSTs the captured plan content. Capture still stashes `tab._planContent` from `exitBlock.input.plan` for the implementation-prompt flow, but file authoring is exclusively server-side.
|
|
171
146
|
- **Removed `extractPlanText`/`materialize` DOM-scrape fallback from Edit Plan click handler** — `renderPostPlanActions()`'s `a.action === 'plan'` branch no longer scrapes `.msg-body` rendered prose and POSTs it to `/api/create-plan`. The DOM only ever held rendered prose narration, never authoritative plan markdown — that path was the source of the garbage-named plan files. Click flow simplified to: `tab.planFilePath` → `openPlan` immediately; else one `requestAnimationFrame` retry for in-flight `plan_file_written`; else `diskFallback` via `GET /api/latest-plan`; else `noFile()`.
|
|
172
147
|
|
|
173
|
-
###
|
|
174
|
-
- **Plan
|
|
175
|
-
- **Per-turn question-tool usage tracking** — `neural-interface/public/shared/ocp/ocp-tabs.js` adds a `tab._questionToolUsedThisTurn` flag set to `true` in all three question-detection sites (`message.part.updated` tool branch, `tool.start` handler, and the dedicated `question.asked` SSE event), reset to `false` at the start of `sendMessage()`, when entering plan mode in `setTabMode()`, and on tab clear.
|
|
176
|
-
- **Prose-question detector** — New `looksLikeProseQuestions(content)` helper scans the last 400 chars of captured plan text for trailing patterns: `quick/a couple of/two/before i proceed … questions:`, `Question N:` markers, or 2+ trailing `?` per line.
|
|
177
|
-
- **`maybeShowPostPlanUI` recovery reroute** — When `!force && !_exitPlanDetected && !_questionToolUsedThisTurn && looksLikeProseQuestions(planContent)`, the function now calls `showProseQuestionRecoveryUI(tab)` and returns `false` instead of stamping the misleading PLAN COMPLETE card. Fixes the long-standing "press STOP after prose questions → PLAN COMPLETE for half-finished plan" trap.
|
|
178
|
-
- **`MODEL ASKED QUESTIONS IN PROSE` recovery card** — New `renderProseQuestionRecoveryCard()` / `removeProseQuestionRecoveryCards()` exports in `neural-interface/public/shared/ocp/ocp-render.js`, plus a new `.ocp-prose-question-card` block in `neural-interface/public/shared/ocp/ocp-styles.js` (amber accent so it's visually distinct from the green-ish PLAN COMPLETE). Three actions: **Reply directly** focuses the OCP input field; **Re-ask using question tool** sends a follow-up prompt instructing the model to re-ask using the `question` tool; **Dismiss** removes the card. Cards are also cleared on next `sendMessage()` and on `setTabMode()` exit-plan.
|
|
179
|
-
|
|
180
|
-
### Added — AskUserQuestion Manual QA Test Suite
|
|
181
|
-
- **Test suite at `tests/ask-user-question/`** — Manual QA suite for the `AskUserQuestion` interactive question card across Claude, Codex, and OpenCode sidepanels. Each scenario is a directive copy-paste prompt that forces the panel CLI to invoke its `AskUserQuestion` equivalent (Claude `control_request`, Codex `request_user_input`, OpenCode `tool.start` / `question.asked`) with a specific shape, so visual and functional rendering can be verified across all three panels.
|
|
182
|
-
- **`README.md`** — Master entry with the panel-to-render-function map (`renderAskUserQuestion()` in `neural-interface/public/shared/ui-claude-panel.js`, `renderUserInputRequest()` in `neural-interface/public/shared/cdx/cdx-requests.js`, `renderQuestionCard()` in `neural-interface/public/shared/ocp/ocp-render.js`), pre-flight steps, and recommended run order.
|
|
183
|
-
- **`checklist.md`** — Per-panel verification checklist covering visual checks (header chip, options layout, `Submit (x/y)` progress, locked state, inter-block spacing) and functional checks (selection toggling, multi-select, "Other" / custom-input path, submit transmission, queue behavior). Includes a scenario × panel scoreboard.
|
|
184
|
-
- **`master-prompt.md`** — Single 4-question stress prompt (mixed multi/single-select, long labels, descriptions) for end-to-end smoke testing after granular scenarios.
|
|
185
|
-
- **`scenarios/01-12-*.md`** — 12 numbered scenarios covering single-select min/max, multi-select, batched 2/4-question cards with mixed types, `Other` / custom typed answer, sequential queue (`_questionQueue` / `processQuestionQueue`), plan-mode integration, long-content overflow, header-only cards, no-options free-text fallback, and cancel-mid-answer queue clearing.
|
|
148
|
+
### Changed — Empty-Plan Re-Prompt Button (Claude Panel)
|
|
149
|
+
- **Replaced "Plan text was empty" status text with an interactive button** — When the user clicks `Edit plan` in the post-plan card but the plan extractor and `/api/latest-plan` disk fallback both fail to surface a plan file, `neural-interface/public/shared/ui-claude-panel.js` previously printed a static `appendStatus()` line ("Plan text was empty — ask Claude to re-output the plan, or click Continue with implementation to proceed."). The `noFile()` callback in the `'plan'` action handler now renders an inline `msg-status` row containing the short text "Plan text was empty." plus a primary `post-plan-action pp-primary` button labeled "Ask Claude to re-output the plan". Clicking the button populates `#cp-input` with a re-prompt instruction (`Please re-output the full plan you were preparing. Use ExitPlanMode with the complete plan text...`) and calls `send()`. Button disables on click, and falls back to a status if the WebSocket is closed. Scoped to the Claude panel only — `ocp-panel.js` retains the existing `renderErrorMessage` pattern.
|
|
186
150
|
|
|
187
|
-
|
|
151
|
+
---
|
|
188
152
|
|
|
189
|
-
|
|
190
|
-
- **Session created in the right directory** — The `session:create` WS handler in `neural-interface/server.js` now forwards the panel-supplied cwd to OpenCode as `POST /session?directory=<encoded path>`. OpenCode binds each session to the directory passed in this query parameter (`session.directory`); previously the call omitted it, so every OpenCode session inherited the serve cwd (`PACKAGE_ROOT` = SynaBun) and ALL agent tool calls (Read, Bash, Edit, Write) resolved relative to SynaBun even when the user had picked a different project in the footer. With the query param wired through, OpenCode's tools natively resolve at the project root the user selected.
|
|
191
|
-
- **Fresh-session cwd routing** — Switching the project via the OpenCode footer dropdown previously mutated only `tab.project` in `neural-interface/public/shared/ocp/ocp-panel.js`, leaving the existing OpenCode session bound to the cwd it was first created with. Added `setActiveProject()` in `neural-interface/public/shared/ocp/ocp-tabs.js` that updates `tab.project` + `STOR.project`, drops the bound `tab.sessionId` (along with title, token usage, plan/post-plan, watchdogs, tool activity, question/permission queues, and snapshot flush), refreshes the project chip label, and clears the messages container so the next `sendMessage()` creates a fresh OpenCode session at the new cwd. The projectbar dropdown click handler and the name-modal commit now both route through `setActiveProject()` instead of touching `tab.project` directly.
|
|
192
|
-
- **Greeting carries the absolute project path** — Updated `buildOpencodeGreetingUserPrompt()` in `neural-interface/server.js` to spell out the absolute project working directory (`Project working directory: \`/abs/path\``) and instruct the agent to use it for all file operations whenever the selected project differs from `PACKAGE_ROOT`. Belt-and-suspenders alongside the new `?directory=` query param, so the agent's first turn explicitly states the cwd it should be operating in.
|
|
153
|
+
## Codex Sidepanel
|
|
193
154
|
|
|
194
155
|
### Fixed — Codex Sidepanel Project Switching
|
|
195
156
|
- **Fresh-session cwd routing** — Fixed the Codex footer project selector so changing projects updates the bound `_project` state in `neural-interface/public/shared/cdx/cdx-tabs.js` instead of only mutating `tab.project` in `neural-interface/public/shared/cdx/cdx-panel.js`; the new `setActiveProject()` keeps `tab.project`, `STOR.project`, branch UI, and fresh thread reset state in sync before `dispatchPrompt()` sends `query.cwd`.
|
|
196
157
|
- **Sandbox root refresh** — Sends `reset_thread` to the Codex websocket after a project change and clears `_threadId` when the server explicitly returns `threadId: null`, preventing stale app-server thread IDs from leaking into fresh sessions and causing workspace-write permissions to target the old folder.
|
|
197
158
|
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Sidepanel Header & Tray
|
|
162
|
+
|
|
163
|
+
### Changed — Sidepanel Header & Tray Pill Polish
|
|
164
|
+
- **`stopPropagation` on OCP header buttons** — `+` (new session), Minimize, Close, and Slide buttons in `ocp-v2-panel.js` now `event.stopPropagation()` so a click no longer bubbles into the session-menu open/close handler. Removes a race where a stray header click could toggle the session dropdown.
|
|
165
|
+
- **`data-tooltip-pos` on every sidepanel pill close button** — Tray pill X buttons in `ui-claude-panel.js` (`.cp-session-pill`), `cdx/cdx-tabs.js` (`.cxp-session-pill`), `ocp-v2-panel.js` (`.ocpv2-session-pill`), and the placeholder pills in `ui-sidepanel-tray.js` all got `data-tooltip-pos="top"` so the "Close" tooltip stops clipping at the bottom edge of the tray. OCP header buttons got `data-tooltip-pos="bottom"` for symmetry.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Terminal
|
|
170
|
+
|
|
171
|
+
### Fixed — Terminal ESC Key Now Aborts CLI TUIs On First Press (Floating + Docked)
|
|
172
|
+
- **Single ESC reaches the PTY instead of `\x1b\x1b`** — In `neural-interface/public/shared/ui-terminal.js`, the global window-capture ESC handler in `initTerminal()` sent `\x1b` over the WS without `e.preventDefault()` / `e.stopImmediatePropagation()`, so xterm.js's own keydown listener on `.xterm-helper-textarea` fired afterwards and re-emitted ESC via `term.onData(...)` (line 1915). The PTY received `\x1b\x1b`, which Bubbletea-based TUIs (OpenCode, Claude Code, Codex) interpret as a Meta-prefix and ignore — so the running task never aborted. Handler now sends one `\x1b`, calls `e.preventDefault()` + `e.stopImmediatePropagation()` to block the duplicate, and re-focuses via `requestAnimationFrame(() => session.term?.focus())` instead of the old `term?.blur()` / `ta.blur()` pair (focus loss had forced the user to click back into the terminal between keystrokes).
|
|
173
|
+
- **Docked terminal now has ESC coverage** — Previous early return `if (!isXterm) return;` left the docked panel without any ESC fallback when focus drifted to a tab title or panel chrome. Handler now resolves the target session by DOM ancestry: xterm helper textarea → its viewport's `dataset.sessionId`, else `.term-float-tab` → that tab's session, else inside `_panel` (`#terminal-panel`) → `_sessions[_activeIdx]`. Skips non-terminal `input/textarea/select/[contenteditable]` so search/rename/path inputs still consume ESC normally.
|
|
174
|
+
- **Removed redundant per-floating-window ESC branch** — `attachTab()`'s `win.addEventListener('keydown', ...)` had a duplicate ESC sender that was unreachable for the common case (skipped when target was a textarea). Stripped the ESC branch; the Ctrl+C copy-on-selection branch stays as the floating-window-only fallback.
|
|
175
|
+
|
|
176
|
+
### Changed — Floating Terminal Drag Compositing
|
|
177
|
+
- **Simplified `.term-float-tab.float-dragging` shadow** — In `styles.css`, replaced the multi-layer `box-shadow: var(--shadow-2xl), 0 0 0 1px rgba(0,0,0,0.4)` with a single `0 8px 24px rgba(0,0,0,0.45), 0 0 0 1px rgba(0,0,0,0.4)`, dropped the `box-shadow` transition (kept `border-radius`), and added `will-change: transform, left, top` so the dragging window stays on its own composited layer. Removes per-frame GPU compositing thrash during drag.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Performance & Stability
|
|
182
|
+
|
|
183
|
+
### Fixed — Long-Session Memory Leaks
|
|
184
|
+
- **Bounded `_orphanedProcs` map** — Claude-skin orphan processes accumulated in `server.js` when a kill timer never fired (crash, kill -9, sleep) and an entry was never reattached. Added a 60-second sweep (`setInterval(...).unref()`) that deletes entries whose `proc` is null/killed/exited and clears the stale `killTimer`.
|
|
185
|
+
- **Pruned `_ocpSessionOwners` / `_ocpSessionActivity`** — `_ocpStartSilenceWatchdog` in `server.js` now removes session entries with no live clients and `lastEventAt` older than 30 minutes. Watchdog scan no longer iterates dead sessions for the lifetime of the process.
|
|
186
|
+
- **Idle screencast frame waste** — `onScreencastFrame` in `server.js` now bails out early (still ack'ing `Page.screencastFrameAck` to keep the CDP pipeline alive) when `session.clients` is empty, skipping JPEG buffer allocation and broadcast. Removed the per-frame `frameCount++` and conditional `console.log` that allocated even when not logging.
|
|
187
|
+
|
|
188
|
+
### Changed — Whiteboard Drag Hot Path
|
|
189
|
+
- **rAF-batched `renderAll()`** — `ui-whiteboard.js` now schedules renders through `requestAnimationFrame` via `_renderRafId`. Multiple mousemove handlers used to call `renderAll()` synchronously per pointer event; renders are coalesced to one per frame. Added `renderAllImmediate()` for explicit flushes.
|
|
190
|
+
- **`_elementMap` O(1) lookups** — Added `_findEl(id)` Map cache. Rebuilt lazily when `_elements.length` changes; `_invalidateElementMap()` is called from `undo()`/`redo()` to cover same-length object replacement. Replaced 8 `_elements.find(el2 => el2.id === ...)` calls inside `onMouseMove` (move-multi, shape-create, section-create, move, move-arrow, move-pen, rotate, resize).
|
|
191
|
+
- **Cached root rect** — Added `_rootRectCached` + `_getRootRect()`/`_invalidateRootRect()`. `clientToCanvas()` and the pen-drawing path reuse the cached rect for the duration of a gesture; cache is cleared at mouseup and on window resize. Eliminates `_root.getBoundingClientRect()` layout flushes inside the 60 Hz mousemove loop.
|
|
192
|
+
|
|
193
|
+
### Changed — Sidepanel Markdown Rendering
|
|
194
|
+
- **OCP v2 incremental render** — Rewrote `render()` in `ocp-v2-render.js` to use a key/sig reconciler (`_reconcile`, `_partSig`, `_msgSig`) instead of `_container.innerHTML = ''` followed by a full rebuild. User messages, assistant part bubbles, error banners, permission cards, question cards, and the post-plan card now have stable composite keys and content signatures so unchanged nodes are reused. Scroll position is far more stable across streaming deltas.
|
|
195
|
+
- **LRU markdown memoization** — Added a 512-entry insertion-order LRU cache around `renderMarkdown()` in `ocp-v2-render.js`, `md()` in `cdx/cdx-render.js`, and `md()` in `ocp/ocp-render.js`. Identical text re-rendered during streaming/state-change passes now returns cached HTML instead of re-invoking `marked.parse()`.
|
|
196
|
+
|
|
197
|
+
### Changed — Storage Layer
|
|
198
|
+
- **Deferred `JSON.parse` to flush time** — `storage.js` introduces `STR_SENTINEL`. `setItem()` no longer parses the caller-provided string on every call; `_dirty[key] = STR_SENTINEL` records "parse the current cache string at flush time". `_flushToServer()` and the `beforeunload` `sendBeacon` path resolve sentinels by parsing the cached string once when actually flushing. Big win for callers that store large snapshots (whiteboard, codex transcripts) where every keystroke previously triggered a redundant `JSON.parse`.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Notifications
|
|
203
|
+
|
|
198
204
|
### Added — Pending Remember Picker on Notifications Health Tab
|
|
199
205
|
- **Inline provider/model picker** — New "Run Pending Remembers" button on the Notifications drawer Health tab in `neural-interface/public/shared/ui-sessions.js`. Clicking expands an inline picker with provider buttons (Claude Code / Codex / OpenCode), a per-provider model dropdown, and Cancel/Run actions, so each orphaned `pending-remember` flag can be summarized into a real memory by the user's chosen sidepanel CLI instead of an isolated background agent.
|
|
200
206
|
- **Per-provider model loading** — Claude models pulled from `/api/claude/config`, OpenCode models flattened from `/api/opencode/providers/full` as `provider/model` ids, Codex falls back to a static list (`gpt-5-codex`, `gpt-5`, `o4`, `o4-mini`) since its model list is WebSocket-only.
|
|
201
207
|
- **Sidepanel routing** — Run dispatches the prompt to the chosen sidepanel: Claude via `sendToPanel({ newTab: true, autoSubmit: true })`, OpenCode via `openOpencodeWithPrompt({ autoSend: true })`, Codex by toggling the panel and pre-filling `#cxp-input` (auto-send is unreliable from outside the Codex panel).
|
|
202
208
|
- **Server endpoint** — New `GET /api/sessions/pending-remember-prompt` in `neural-interface/server.js` returns `{ ok, count, prompt }`; resolves each orphaned session id to its `~/.claude/projects/<projKey>/<sessionId>.jsonl` transcript and embeds those paths in the prompt so the executing CLI can read them.
|
|
203
209
|
- **Picker styling** — Added `.sm-pr-picker` / `.sm-pr-row` / `.sm-pr-providers` / `.sm-pr-select` rules in `neural-interface/public/shared/styles.css`, including an `active` pill state for the selected provider.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Testing
|
|
214
|
+
|
|
215
|
+
### Added — AskUserQuestion Manual QA Test Suite
|
|
216
|
+
- **Test suite at `tests/ask-user-question/`** — Manual QA suite for the `AskUserQuestion` interactive question card across Claude, Codex, and OpenCode sidepanels. Each scenario is a directive copy-paste prompt that forces the panel CLI to invoke its `AskUserQuestion` equivalent (Claude `control_request`, Codex `request_user_input`, OpenCode `tool.start` / `question.asked`) with a specific shape, so visual and functional rendering can be verified across all three panels.
|
|
217
|
+
- **`README.md`** — Master entry with the panel-to-render-function map (`renderAskUserQuestion()` in `neural-interface/public/shared/ui-claude-panel.js`, `renderUserInputRequest()` in `neural-interface/public/shared/cdx/cdx-requests.js`, `renderQuestionCard()` in `neural-interface/public/shared/ocp/ocp-render.js`), pre-flight steps, and recommended run order.
|
|
218
|
+
- **`checklist.md`** — Per-panel verification checklist covering visual checks (header chip, options layout, `Submit (x/y)` progress, locked state, inter-block spacing) and functional checks (selection toggling, multi-select, "Other" / custom-input path, submit transmission, queue behavior). Includes a scenario × panel scoreboard.
|
|
219
|
+
- **`master-prompt.md`** — Single 4-question stress prompt (mixed multi/single-select, long labels, descriptions) for end-to-end smoke testing after granular scenarios.
|
|
220
|
+
- **`scenarios/01-12-*.md`** — 12 numbered scenarios covering single-select min/max, multi-select, batched 2/4-question cards with mixed types, `Other` / custom typed answer, sequential queue (`_questionQueue` / `processQuestionQueue`), plan-mode integration, long-content overflow, header-only cards, no-options free-text fallback, and cancel-mid-answer queue clearing.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neural-interface.d.ts","sourceRoot":"","sources":["../../src/services/neural-interface.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,UAAU;IAClB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"neural-interface.d.ts","sourceRoot":"","sources":["../../src/services/neural-interface.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,UAAU;IAClB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAkGD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAmHpE;AAID,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnE;AAID,wBAAsB,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,CAExD;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIrE;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEzE;AAID,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,cAAc,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACxE,OAAO,CAAC,UAAU,CAAC,CAOrB;AAED,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEnF;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtF;AAED,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEnF;AAID,wBAAsB,KAAK,CACzB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACxE,OAAO,CAAC,UAAU,CAAC,CASrB;AAED,wBAAsB,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAExJ;AAED,wBAAsB,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAG9L;AAED,wBAAsB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAE1I;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAE7I;AAED,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAElG;AAED,wBAAsB,MAAM,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,EAC5I,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,wBAAsB,MAAM,CAC1B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC,CAErB;AAID,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3C,OAAO,CAAC,UAAU,CAAC,CAarB;AAED,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGvF;AAED,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGxF;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtF;AAED,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGvF;AAID,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAErG;AAED,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC,CAErB;AAID,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAEzD;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACnC,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAE7E;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAE3D;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CAEhE;AAID,wBAAsB,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,CAEpD;AAED,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5E,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,wBAAsB,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtE;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAE3D;AAID,wBAAsB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAExE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAErE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,CAE1D;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,CAExD;AAID,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEjE;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGlF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAEpG;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAE9E;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEnE;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,CAEtD;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEvE"}
|
|
@@ -51,6 +51,64 @@ let _recoveredSessionId = null;
|
|
|
51
51
|
// Prevents a second Claude Code instance (e.g. sidepanel) from grabbing the CLI's session
|
|
52
52
|
// when both are running without explicit sessionId or SYNABUN_BROWSER_SESSION pinning.
|
|
53
53
|
let _affinitySessionId = null;
|
|
54
|
+
// Ancestor-PID fallback (Layer B). Walks the process tree once and caches the
|
|
55
|
+
// resolved loop pins. Used when codex/opencode strip SYNABUN_BROWSER_* env on
|
|
56
|
+
// MCP child spawn — we map our PID chain up to a known PTY and pull the loop's
|
|
57
|
+
// session/tab IDs from that loop's state file.
|
|
58
|
+
let _ancestorLookupTried = false;
|
|
59
|
+
let _ancestorPinnedSession = null;
|
|
60
|
+
let _ancestorPinnedTab = null;
|
|
61
|
+
async function getAncestorPids() {
|
|
62
|
+
const ppid = typeof process.ppid === 'number' ? process.ppid : 0;
|
|
63
|
+
if (!ppid)
|
|
64
|
+
return [];
|
|
65
|
+
const pids = [ppid];
|
|
66
|
+
const isWin = process.platform === 'win32';
|
|
67
|
+
const { exec } = await import('node:child_process');
|
|
68
|
+
const runCmd = (cmd) => new Promise((resolve) => {
|
|
69
|
+
exec(cmd, { timeout: 1500, windowsHide: true }, (_err, stdout) => resolve(stdout || ''));
|
|
70
|
+
});
|
|
71
|
+
let current = ppid;
|
|
72
|
+
for (let i = 0; i < 6; i++) {
|
|
73
|
+
let parent = 0;
|
|
74
|
+
try {
|
|
75
|
+
if (isWin) {
|
|
76
|
+
const out = await runCmd(`wmic process where ProcessId=${current} get ParentProcessId /value`);
|
|
77
|
+
const m = /ParentProcessId=(\d+)/i.exec(out);
|
|
78
|
+
parent = m ? Number(m[1]) : 0;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const out = await runCmd(`ps -o ppid= -p ${current}`);
|
|
82
|
+
parent = Number(out.trim().split(/\s+/)[0]) || 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
parent = 0;
|
|
87
|
+
}
|
|
88
|
+
if (!parent || parent === 1 || parent === current)
|
|
89
|
+
break;
|
|
90
|
+
pids.push(parent);
|
|
91
|
+
current = parent;
|
|
92
|
+
}
|
|
93
|
+
return pids;
|
|
94
|
+
}
|
|
95
|
+
async function resolveFromAncestors() {
|
|
96
|
+
if (_ancestorLookupTried)
|
|
97
|
+
return;
|
|
98
|
+
_ancestorLookupTried = true;
|
|
99
|
+
try {
|
|
100
|
+
const pids = await getAncestorPids();
|
|
101
|
+
if (pids.length === 0)
|
|
102
|
+
return;
|
|
103
|
+
const resp = await request('POST', '/api/loop/resolve-from-ancestors', { pids }, 2000);
|
|
104
|
+
if (resp.matched) {
|
|
105
|
+
_ancestorPinnedSession = resp.browserSessionId || null;
|
|
106
|
+
_ancestorPinnedTab = resp.browserTabId || null;
|
|
107
|
+
console.error(`[MCP] ancestor lookup matched: session=${_ancestorPinnedSession} tab=${_ancestorPinnedTab}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch { /* best-effort */ }
|
|
111
|
+
}
|
|
54
112
|
/**
|
|
55
113
|
* Resolve which session ID to use.
|
|
56
114
|
* - If sessionId provided, return it immediately (server returns 404 if invalid).
|
|
@@ -62,7 +120,12 @@ export async function resolveSession(sessionId, autoCreate, tabId) {
|
|
|
62
120
|
// Resolve tab ID from explicit param or environment variable.
|
|
63
121
|
// When SYNABUN_BROWSER_TAB is pinned (loop/agent context), the env value wins
|
|
64
122
|
// even if caller passes a different tabId — prevents tab-leak across loops.
|
|
65
|
-
|
|
123
|
+
// If env pins are missing (codex/opencode env-strip case), try ancestor-PID
|
|
124
|
+
// lookup once and use whatever the Neural Interface reports for our PTY.
|
|
125
|
+
if (!process.env.SYNABUN_BROWSER_SESSION && !process.env.SYNABUN_BROWSER_TAB && !sessionId) {
|
|
126
|
+
await resolveFromAncestors();
|
|
127
|
+
}
|
|
128
|
+
const pinnedTab = process.env.SYNABUN_BROWSER_TAB || _ancestorPinnedTab || undefined;
|
|
66
129
|
const resolvedTabId = pinnedTab || tabId || undefined;
|
|
67
130
|
if (pinnedTab && tabId && tabId !== pinnedTab) {
|
|
68
131
|
console.error(`[MCP] tabId override ignored: pinned=${pinnedTab} requested=${tabId}`);
|
|
@@ -70,7 +133,7 @@ export async function resolveSession(sessionId, autoCreate, tabId) {
|
|
|
70
133
|
// Agent/loop-scoped browser session — set by the orchestrator to pin
|
|
71
134
|
// this MCP instance to a specific browser session (multi-session isolation).
|
|
72
135
|
// If pinned session died, check recovery cache first, then auto-create.
|
|
73
|
-
const pinnedSession = process.env.SYNABUN_BROWSER_SESSION;
|
|
136
|
+
const pinnedSession = process.env.SYNABUN_BROWSER_SESSION || _ancestorPinnedSession || undefined;
|
|
74
137
|
if (pinnedSession && !sessionId) {
|
|
75
138
|
// Check recovery cache first — avoids re-creating on every call after recovery
|
|
76
139
|
if (_recoveredSessionId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neural-interface.js","sourceRoot":"","sources":["../../src/services/neural-interface.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"neural-interface.js","sourceRoot":"","sources":["../../src/services/neural-interface.ts"],"names":[],"mappings":"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"}
|
|
@@ -78,6 +78,58 @@ let _recoveredSessionId: string | null = null;
|
|
|
78
78
|
// when both are running without explicit sessionId or SYNABUN_BROWSER_SESSION pinning.
|
|
79
79
|
let _affinitySessionId: string | null = null;
|
|
80
80
|
|
|
81
|
+
// Ancestor-PID fallback (Layer B). Walks the process tree once and caches the
|
|
82
|
+
// resolved loop pins. Used when codex/opencode strip SYNABUN_BROWSER_* env on
|
|
83
|
+
// MCP child spawn — we map our PID chain up to a known PTY and pull the loop's
|
|
84
|
+
// session/tab IDs from that loop's state file.
|
|
85
|
+
let _ancestorLookupTried = false;
|
|
86
|
+
let _ancestorPinnedSession: string | null = null;
|
|
87
|
+
let _ancestorPinnedTab: string | null = null;
|
|
88
|
+
|
|
89
|
+
async function getAncestorPids(): Promise<number[]> {
|
|
90
|
+
const ppid = typeof process.ppid === 'number' ? process.ppid : 0;
|
|
91
|
+
if (!ppid) return [];
|
|
92
|
+
const pids: number[] = [ppid];
|
|
93
|
+
const isWin = process.platform === 'win32';
|
|
94
|
+
const { exec } = await import('node:child_process');
|
|
95
|
+
const runCmd = (cmd: string): Promise<string> => new Promise((resolve) => {
|
|
96
|
+
exec(cmd, { timeout: 1500, windowsHide: true }, (_err: unknown, stdout: string) => resolve(stdout || ''));
|
|
97
|
+
});
|
|
98
|
+
let current = ppid;
|
|
99
|
+
for (let i = 0; i < 6; i++) {
|
|
100
|
+
let parent = 0;
|
|
101
|
+
try {
|
|
102
|
+
if (isWin) {
|
|
103
|
+
const out = await runCmd(`wmic process where ProcessId=${current} get ParentProcessId /value`);
|
|
104
|
+
const m = /ParentProcessId=(\d+)/i.exec(out);
|
|
105
|
+
parent = m ? Number(m[1]) : 0;
|
|
106
|
+
} else {
|
|
107
|
+
const out = await runCmd(`ps -o ppid= -p ${current}`);
|
|
108
|
+
parent = Number(out.trim().split(/\s+/)[0]) || 0;
|
|
109
|
+
}
|
|
110
|
+
} catch { parent = 0; }
|
|
111
|
+
if (!parent || parent === 1 || parent === current) break;
|
|
112
|
+
pids.push(parent);
|
|
113
|
+
current = parent;
|
|
114
|
+
}
|
|
115
|
+
return pids;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function resolveFromAncestors(): Promise<void> {
|
|
119
|
+
if (_ancestorLookupTried) return;
|
|
120
|
+
_ancestorLookupTried = true;
|
|
121
|
+
try {
|
|
122
|
+
const pids = await getAncestorPids();
|
|
123
|
+
if (pids.length === 0) return;
|
|
124
|
+
const resp = await request('POST', '/api/loop/resolve-from-ancestors', { pids }, 2000);
|
|
125
|
+
if (resp.matched) {
|
|
126
|
+
_ancestorPinnedSession = (resp.browserSessionId as string) || null;
|
|
127
|
+
_ancestorPinnedTab = (resp.browserTabId as string) || null;
|
|
128
|
+
console.error(`[MCP] ancestor lookup matched: session=${_ancestorPinnedSession} tab=${_ancestorPinnedTab}`);
|
|
129
|
+
}
|
|
130
|
+
} catch { /* best-effort */ }
|
|
131
|
+
}
|
|
132
|
+
|
|
81
133
|
/**
|
|
82
134
|
* Resolve which session ID to use.
|
|
83
135
|
* - If sessionId provided, return it immediately (server returns 404 if invalid).
|
|
@@ -93,7 +145,12 @@ export async function resolveSession(
|
|
|
93
145
|
// Resolve tab ID from explicit param or environment variable.
|
|
94
146
|
// When SYNABUN_BROWSER_TAB is pinned (loop/agent context), the env value wins
|
|
95
147
|
// even if caller passes a different tabId — prevents tab-leak across loops.
|
|
96
|
-
|
|
148
|
+
// If env pins are missing (codex/opencode env-strip case), try ancestor-PID
|
|
149
|
+
// lookup once and use whatever the Neural Interface reports for our PTY.
|
|
150
|
+
if (!process.env.SYNABUN_BROWSER_SESSION && !process.env.SYNABUN_BROWSER_TAB && !sessionId) {
|
|
151
|
+
await resolveFromAncestors();
|
|
152
|
+
}
|
|
153
|
+
const pinnedTab = process.env.SYNABUN_BROWSER_TAB || _ancestorPinnedTab || undefined;
|
|
97
154
|
const resolvedTabId = pinnedTab || tabId || undefined;
|
|
98
155
|
if (pinnedTab && tabId && tabId !== pinnedTab) {
|
|
99
156
|
console.error(`[MCP] tabId override ignored: pinned=${pinnedTab} requested=${tabId}`);
|
|
@@ -102,7 +159,7 @@ export async function resolveSession(
|
|
|
102
159
|
// Agent/loop-scoped browser session — set by the orchestrator to pin
|
|
103
160
|
// this MCP instance to a specific browser session (multi-session isolation).
|
|
104
161
|
// If pinned session died, check recovery cache first, then auto-create.
|
|
105
|
-
const pinnedSession = process.env.SYNABUN_BROWSER_SESSION;
|
|
162
|
+
const pinnedSession = process.env.SYNABUN_BROWSER_SESSION || _ancestorPinnedSession || undefined;
|
|
106
163
|
if (pinnedSession && !sessionId) {
|
|
107
164
|
// Check recovery cache first — avoids re-creating on every call after recovery
|
|
108
165
|
if (_recoveredSessionId) {
|
|
@@ -5182,6 +5182,51 @@
|
|
|
5182
5182
|
transition: opacity 0.15s ease;
|
|
5183
5183
|
}
|
|
5184
5184
|
|
|
5185
|
+
/* Liquid step transition for the update wizard. The shell stays mounted;
|
|
5186
|
+
only the inner .update-step swaps with a measured height tween +
|
|
5187
|
+
keyframe-driven crossfade. Keyframe animations are used (not CSS
|
|
5188
|
+
transitions) because freshly-appended elements have unreliable initial-
|
|
5189
|
+
state observation under Chromium — animations run their declared
|
|
5190
|
+
keyframes deterministically. `isolation: isolate` on the wrapper keeps
|
|
5191
|
+
repaints from propagating to the modal's `backdrop-filter` ancestor. */
|
|
5192
|
+
.update-step-content {
|
|
5193
|
+
position: relative;
|
|
5194
|
+
overflow: hidden;
|
|
5195
|
+
isolation: isolate;
|
|
5196
|
+
transition: height 340ms cubic-bezier(.4, 0, .2, 1);
|
|
5197
|
+
will-change: height;
|
|
5198
|
+
}
|
|
5199
|
+
.update-step {
|
|
5200
|
+
width: 100%;
|
|
5201
|
+
transform: translate3d(0, 0, 0);
|
|
5202
|
+
}
|
|
5203
|
+
@keyframes synabunStepIn {
|
|
5204
|
+
from { opacity: 0; transform: translate3d(0, 10px, 0); }
|
|
5205
|
+
to { opacity: 1; transform: translate3d(0, 0, 0); }
|
|
5206
|
+
}
|
|
5207
|
+
@keyframes synabunStepOut {
|
|
5208
|
+
from { opacity: 1; transform: translate3d(0, 0, 0); }
|
|
5209
|
+
to { opacity: 0; transform: translate3d(0, -8px, 0); }
|
|
5210
|
+
}
|
|
5211
|
+
.update-step.is-entering {
|
|
5212
|
+
animation: synabunStepIn 360ms cubic-bezier(.22, 1, .36, 1) forwards;
|
|
5213
|
+
will-change: opacity, transform;
|
|
5214
|
+
}
|
|
5215
|
+
.update-step.is-leaving {
|
|
5216
|
+
pointer-events: none;
|
|
5217
|
+
animation: synabunStepOut 260ms cubic-bezier(.4, 0, .2, 1) forwards;
|
|
5218
|
+
will-change: opacity, transform;
|
|
5219
|
+
}
|
|
5220
|
+
@media (prefers-reduced-motion: reduce) {
|
|
5221
|
+
.update-step-content,
|
|
5222
|
+
.update-step.is-entering,
|
|
5223
|
+
.update-step.is-leaving {
|
|
5224
|
+
animation: none !important;
|
|
5225
|
+
transition: none !important;
|
|
5226
|
+
transform: none !important;
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
|
|
5185
5230
|
/* ── Dropdown panels (shared) ── */
|
|
5186
5231
|
.dropdown-panel {
|
|
5187
5232
|
position: absolute;
|
|
@@ -35,14 +35,15 @@ function applyTopRightButton(data) {
|
|
|
35
35
|
}
|
|
36
36
|
if (has) {
|
|
37
37
|
const srcLabel = sourceLabel(data);
|
|
38
|
-
btn.dataset.tooltip = `SynaBun update available: v${data.current} → v${data.latest} (${srcLabel})
|
|
38
|
+
btn.dataset.tooltip = `SynaBun update available: v${data.current} → v${data.latest} (${srcLabel})`;
|
|
39
39
|
}
|
|
40
40
|
if (!_btnWired) {
|
|
41
41
|
_btnWired = true;
|
|
42
|
+
// Whole button opens the update flow — it's a dedicated SynaBun-update
|
|
43
|
+
// button (display:none when no update available), so any click on it
|
|
44
|
+
// should prompt the wizard, not just the green badge.
|
|
42
45
|
btn.addEventListener('click', (e) => {
|
|
43
|
-
|
|
44
|
-
if (!currentBadge?.textContent) return;
|
|
45
|
-
if (e.target !== currentBadge && !currentBadge.contains(e.target)) return;
|
|
46
|
+
if (!_versionData?.updateAvailable) return;
|
|
46
47
|
e.stopPropagation();
|
|
47
48
|
e.preventDefault();
|
|
48
49
|
openUpdateModal();
|
|
@@ -118,25 +119,131 @@ function openUpdateModal() {
|
|
|
118
119
|
|
|
119
120
|
let step = 1;
|
|
120
121
|
let backupDone = false;
|
|
122
|
+
let isAnimating = false;
|
|
121
123
|
|
|
122
124
|
const overlay = document.createElement('div');
|
|
123
125
|
overlay.className = 'tag-delete-overlay';
|
|
124
126
|
overlay.style.zIndex = '300100';
|
|
125
127
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
const modal = document.createElement('div');
|
|
129
|
+
modal.className = 'tag-delete-modal update-modal';
|
|
130
|
+
modal.style.maxWidth = '460px';
|
|
131
|
+
modal.style.textAlign = 'left';
|
|
132
|
+
|
|
133
|
+
const dotsRow = document.createElement('div');
|
|
134
|
+
dotsRow.className = 'update-steps';
|
|
135
|
+
dotsRow.innerHTML = [1, 2, 3].map(() => `<span class="update-step-dot"></span>`).join('');
|
|
136
|
+
|
|
137
|
+
const stepWrap = document.createElement('div');
|
|
138
|
+
stepWrap.className = 'update-step-content';
|
|
139
|
+
|
|
140
|
+
modal.appendChild(dotsRow);
|
|
141
|
+
modal.appendChild(stepWrap);
|
|
142
|
+
overlay.appendChild(modal);
|
|
143
|
+
|
|
144
|
+
function buildStepNode(n) {
|
|
145
|
+
const node = document.createElement('div');
|
|
146
|
+
node.className = 'update-step';
|
|
147
|
+
node.dataset.step = String(n);
|
|
148
|
+
node.innerHTML = n === 1 ? renderStep1() : n === 2 ? renderStep2() : renderStep3();
|
|
149
|
+
return node;
|
|
134
150
|
}
|
|
135
151
|
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
152
|
+
function syncStepDots() {
|
|
153
|
+
const dots = dotsRow.querySelectorAll('.update-step-dot');
|
|
154
|
+
dots.forEach((dot, i) => {
|
|
155
|
+
const idx = i + 1;
|
|
156
|
+
dot.classList.toggle('active', idx === step);
|
|
157
|
+
dot.classList.toggle('done', idx < step);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function prefersReducedMotion() {
|
|
162
|
+
try { return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true; }
|
|
163
|
+
catch { return false; }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function setStep(n) {
|
|
167
|
+
if (isAnimating) return;
|
|
168
|
+
step = n;
|
|
169
|
+
syncStepDots();
|
|
170
|
+
|
|
171
|
+
const outgoing = stepWrap.firstElementChild;
|
|
172
|
+
const incoming = buildStepNode(n);
|
|
173
|
+
|
|
174
|
+
if (!outgoing) {
|
|
175
|
+
stepWrap.appendChild(incoming);
|
|
176
|
+
bindStepEvents(incoming);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (prefersReducedMotion()) {
|
|
181
|
+
stepWrap.replaceChild(incoming, outgoing);
|
|
182
|
+
bindStepEvents(incoming);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
isAnimating = true;
|
|
187
|
+
|
|
188
|
+
// 1. Pin BOTH wrapper height AND modal width before any positioning
|
|
189
|
+
// changes. The modal is a flex item whose width derives from its
|
|
190
|
+
// in-flow content; when both step nodes go absolute, the only
|
|
191
|
+
// remaining in-flow child is the tiny .update-steps dots row,
|
|
192
|
+
// causing the modal to shrink horizontally to its min-width during
|
|
193
|
+
// the transition (visible as a horizontal snap).
|
|
194
|
+
const oldH = outgoing.offsetHeight;
|
|
195
|
+
const pinW = modal.offsetWidth;
|
|
196
|
+
stepWrap.style.height = oldH + 'px';
|
|
197
|
+
modal.style.width = pinW + 'px';
|
|
198
|
+
|
|
199
|
+
// 2. Stage incoming absolutely positioned, hidden, to measure its
|
|
200
|
+
// natural height at the pinned modal width.
|
|
201
|
+
incoming.style.position = 'absolute';
|
|
202
|
+
incoming.style.left = '0';
|
|
203
|
+
incoming.style.right = '0';
|
|
204
|
+
incoming.style.top = '0';
|
|
205
|
+
incoming.style.visibility = 'hidden';
|
|
206
|
+
stepWrap.appendChild(incoming);
|
|
207
|
+
bindStepEvents(incoming);
|
|
208
|
+
const newH = incoming.offsetHeight;
|
|
209
|
+
|
|
210
|
+
// 3. Position outgoing absolutely so the height tween isn't fought.
|
|
211
|
+
outgoing.style.position = 'absolute';
|
|
212
|
+
outgoing.style.left = '0';
|
|
213
|
+
outgoing.style.right = '0';
|
|
214
|
+
outgoing.style.top = '0';
|
|
215
|
+
outgoing.classList.add('is-leaving');
|
|
216
|
+
|
|
217
|
+
// 4. Reveal incoming and trigger its enter animation.
|
|
218
|
+
incoming.style.visibility = '';
|
|
219
|
+
incoming.classList.add('is-entering');
|
|
220
|
+
|
|
221
|
+
// 5. Tween wrapper height. Next frame so the pinned values commit first.
|
|
222
|
+
requestAnimationFrame(() => {
|
|
223
|
+
stepWrap.style.height = newH + 'px';
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
let settled = false;
|
|
227
|
+
const finalize = () => {
|
|
228
|
+
if (settled) return;
|
|
229
|
+
settled = true;
|
|
230
|
+
incoming.removeEventListener('animationend', onIncomingEnd);
|
|
231
|
+
if (outgoing.parentNode === stepWrap) stepWrap.removeChild(outgoing);
|
|
232
|
+
incoming.classList.remove('is-entering');
|
|
233
|
+
incoming.style.position = '';
|
|
234
|
+
incoming.style.left = '';
|
|
235
|
+
incoming.style.right = '';
|
|
236
|
+
incoming.style.top = '';
|
|
237
|
+
incoming.style.visibility = '';
|
|
238
|
+
stepWrap.style.height = '';
|
|
239
|
+
modal.style.width = '';
|
|
240
|
+
isAnimating = false;
|
|
241
|
+
};
|
|
242
|
+
const onIncomingEnd = (ev) => {
|
|
243
|
+
if (ev.target === incoming && ev.animationName === 'synabunStepIn') finalize();
|
|
244
|
+
};
|
|
245
|
+
incoming.addEventListener('animationend', onIncomingEnd);
|
|
246
|
+
setTimeout(finalize, 480);
|
|
140
247
|
}
|
|
141
248
|
|
|
142
249
|
function renderStep1() {
|
|
@@ -261,30 +368,23 @@ function openUpdateModal() {
|
|
|
261
368
|
</p>`;
|
|
262
369
|
}
|
|
263
370
|
|
|
264
|
-
function
|
|
265
|
-
// Close on overlay click
|
|
266
|
-
overlay.addEventListener('click', (e) => {
|
|
267
|
-
if (e.target === overlay) close();
|
|
268
|
-
});
|
|
269
|
-
|
|
371
|
+
function bindStepEvents(scope) {
|
|
270
372
|
// Cancel / Done
|
|
271
|
-
|
|
272
|
-
|
|
373
|
+
scope.querySelector('#update-cancel')?.addEventListener('click', close);
|
|
374
|
+
scope.querySelector('#update-done')?.addEventListener('click', close);
|
|
273
375
|
|
|
274
376
|
// Next
|
|
275
|
-
|
|
276
|
-
step
|
|
277
|
-
render();
|
|
377
|
+
scope.querySelector('#update-next')?.addEventListener('click', () => {
|
|
378
|
+
setStep(step + 1);
|
|
278
379
|
});
|
|
279
380
|
|
|
280
381
|
// Back
|
|
281
|
-
|
|
282
|
-
step
|
|
283
|
-
render();
|
|
382
|
+
scope.querySelector('#update-back')?.addEventListener('click', () => {
|
|
383
|
+
setStep(step - 1);
|
|
284
384
|
});
|
|
285
385
|
|
|
286
386
|
// Copy buttons
|
|
287
|
-
|
|
387
|
+
scope.querySelectorAll('.update-copy-btn').forEach(btn => {
|
|
288
388
|
btn.addEventListener('click', () => {
|
|
289
389
|
const text = btn.dataset.copy;
|
|
290
390
|
navigator.clipboard.writeText(text).then(() => {
|
|
@@ -297,24 +397,24 @@ function openUpdateModal() {
|
|
|
297
397
|
});
|
|
298
398
|
|
|
299
399
|
// Backup download
|
|
300
|
-
|
|
400
|
+
scope.querySelector('#update-backup-download')?.addEventListener('click', handleBackupDownload);
|
|
301
401
|
|
|
302
402
|
// Update Now — kick off click-to-update flow
|
|
303
|
-
|
|
403
|
+
scope.querySelector('#update-run-now')?.addEventListener('click', handleRunUpdate);
|
|
304
404
|
|
|
305
405
|
// Manual — surface the command + relaunch hint, hide auto-update controls
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const runNow =
|
|
406
|
+
scope.querySelector('#update-manual')?.addEventListener('click', () => {
|
|
407
|
+
scope.querySelector('#update-manual-note').style.display = '';
|
|
408
|
+
const runNow = scope.querySelector('#update-run-now');
|
|
309
409
|
if (runNow) runNow.style.display = 'none';
|
|
310
410
|
});
|
|
311
411
|
|
|
312
412
|
// Skip checkbox
|
|
313
|
-
const skipCb =
|
|
413
|
+
const skipCb = scope.querySelector('#update-skip-backup');
|
|
314
414
|
if (skipCb) {
|
|
315
415
|
skipCb.addEventListener('change', () => {
|
|
316
416
|
backupDone = skipCb.checked;
|
|
317
|
-
const nextBtn =
|
|
417
|
+
const nextBtn = scope.querySelector('#update-next');
|
|
318
418
|
if (nextBtn) nextBtn.disabled = !backupDone;
|
|
319
419
|
});
|
|
320
420
|
}
|
|
@@ -328,12 +428,13 @@ function openUpdateModal() {
|
|
|
328
428
|
// Auto-restart is always on; server forces it true regardless of payload.
|
|
329
429
|
const autoRestart = true;
|
|
330
430
|
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
const
|
|
431
|
+
const liveStep = stepWrap.querySelector('.update-step:not(.is-leaving)') || stepWrap;
|
|
432
|
+
const runBtn = liveStep.querySelector('#update-run-now');
|
|
433
|
+
const backBtn = liveStep.querySelector('#update-back');
|
|
434
|
+
const manualBtn = liveStep.querySelector('#update-manual');
|
|
435
|
+
const status = liveStep.querySelector('#update-run-status');
|
|
436
|
+
const icon = liveStep.querySelector('#update-run-icon');
|
|
437
|
+
const text = liveStep.querySelector('#update-run-text');
|
|
337
438
|
|
|
338
439
|
if (!runBtn || !backBtn || !status || !icon || !text) return;
|
|
339
440
|
|
|
@@ -393,10 +494,11 @@ function openUpdateModal() {
|
|
|
393
494
|
}
|
|
394
495
|
|
|
395
496
|
async function handleBackupDownload() {
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
const
|
|
399
|
-
const
|
|
497
|
+
const liveStep = stepWrap.querySelector('.update-step:not(.is-leaving)') || stepWrap;
|
|
498
|
+
const btn = liveStep.querySelector('#update-backup-download');
|
|
499
|
+
const status = liveStep.querySelector('#update-backup-status');
|
|
500
|
+
const icon = liveStep.querySelector('#update-backup-icon');
|
|
501
|
+
const text = liveStep.querySelector('#update-backup-text');
|
|
400
502
|
if (!btn) return;
|
|
401
503
|
|
|
402
504
|
btn.disabled = true;
|
|
@@ -431,9 +533,9 @@ function openUpdateModal() {
|
|
|
431
533
|
text.textContent = `${filename} (${(blob.size / 1024 / 1024).toFixed(1)} MB)`;
|
|
432
534
|
|
|
433
535
|
backupDone = true;
|
|
434
|
-
const nextBtn =
|
|
536
|
+
const nextBtn = liveStep.querySelector('#update-next');
|
|
435
537
|
if (nextBtn) nextBtn.disabled = false;
|
|
436
|
-
const skipCb =
|
|
538
|
+
const skipCb = liveStep.querySelector('#update-skip-backup');
|
|
437
539
|
if (skipCb) skipCb.checked = true;
|
|
438
540
|
} catch (err) {
|
|
439
541
|
icon.className = 'update-backup-icon';
|
|
@@ -451,7 +553,13 @@ function openUpdateModal() {
|
|
|
451
553
|
setTimeout(() => overlay.remove(), 150);
|
|
452
554
|
}
|
|
453
555
|
|
|
454
|
-
render
|
|
556
|
+
// Boot: attach shell once, wire overlay-click-to-close, render initial step.
|
|
557
|
+
overlay.addEventListener('click', (e) => {
|
|
558
|
+
if (e.target === overlay) close();
|
|
559
|
+
});
|
|
560
|
+
document.body.appendChild(overlay);
|
|
561
|
+
syncStepDots();
|
|
562
|
+
setStep(1);
|
|
455
563
|
}
|
|
456
564
|
|
|
457
565
|
function esc(str) {
|
|
@@ -10051,6 +10051,22 @@ app.post('/api/loop/launch', async (req, res) => {
|
|
|
10051
10051
|
// Claude Code uses ToolSearch for deferred loading — always give it the full profile
|
|
10052
10052
|
if (cliProfile === 'claude-code') loopExtraEnv.SYNABUN_PROFILE = 'full';
|
|
10053
10053
|
|
|
10054
|
+
// Per-loop opencode config dir: opencode reads ~/.config/opencode/config.json (or
|
|
10055
|
+
// $XDG_CONFIG_HOME/opencode/config.json) at startup and uses the MCP env block
|
|
10056
|
+
// verbatim. Spawn each opencode loop with its own XDG_CONFIG_HOME so the MCP
|
|
10057
|
+
// child inherits this loop's browser pins.
|
|
10058
|
+
if (cliProfile === 'opencode' && (browserSessionId || browserTabId)) {
|
|
10059
|
+
try {
|
|
10060
|
+
const xdgRoot = setupOpencodeLoopConfig(terminalSessionId, browserSessionId, browserTabId);
|
|
10061
|
+
if (xdgRoot) {
|
|
10062
|
+
loopExtraEnv.XDG_CONFIG_HOME = xdgRoot;
|
|
10063
|
+
loopLog(terminalSessionId, 'launch:opencode-config', 'per-loop XDG_CONFIG_HOME wired', { xdgRoot });
|
|
10064
|
+
}
|
|
10065
|
+
} catch (err) {
|
|
10066
|
+
loopLog(terminalSessionId, 'launch:opencode-config', 'setup FAILED (falling back to global config)', { err: err.message });
|
|
10067
|
+
}
|
|
10068
|
+
}
|
|
10069
|
+
|
|
10054
10070
|
loopLog(terminalSessionId, 'launch:env', 'extraEnv prepared for spawned PTY', {
|
|
10055
10071
|
keys: Object.keys(loopExtraEnv),
|
|
10056
10072
|
SYNABUN_TERMINAL_SESSION: loopExtraEnv.SYNABUN_TERMINAL_SESSION,
|
|
@@ -10125,6 +10141,10 @@ app.post('/api/loop/stop', async (req, res) => {
|
|
|
10125
10141
|
}
|
|
10126
10142
|
}
|
|
10127
10143
|
}
|
|
10144
|
+
// Per-CLI per-loop config dir cleanup (opencode XDG_CONFIG_HOME, etc.)
|
|
10145
|
+
if (data.profile === 'opencode' && data.terminalSessionId) {
|
|
10146
|
+
cleanupOpencodeLoopConfig(data.terminalSessionId);
|
|
10147
|
+
}
|
|
10128
10148
|
// Delete the loop file — no history keeping
|
|
10129
10149
|
try { unlinkSync(filePath); } catch { /* ok */ }
|
|
10130
10150
|
stopped++;
|
|
@@ -10137,6 +10157,40 @@ app.post('/api/loop/stop', async (req, res) => {
|
|
|
10137
10157
|
}
|
|
10138
10158
|
});
|
|
10139
10159
|
|
|
10160
|
+
// POST /api/loop/resolve-from-ancestors — process-tree fallback for MCP env loss.
|
|
10161
|
+
// The MCP server walks its ancestor PID chain (codex/opencode/gemini → shell PTY)
|
|
10162
|
+
// and POSTs the chain here. We match any PID against terminalSessions[*].pty.pid
|
|
10163
|
+
// and return that loop's browserSessionId/browserTabId. Layer B of the
|
|
10164
|
+
// defense-in-depth browser tab isolation fix.
|
|
10165
|
+
app.post('/api/loop/resolve-from-ancestors', (req, res) => {
|
|
10166
|
+
try {
|
|
10167
|
+
const pids = Array.isArray(req.body?.pids) ? req.body.pids.map(Number).filter(Number.isFinite) : [];
|
|
10168
|
+
if (pids.length === 0) return res.json({ matched: false });
|
|
10169
|
+
const pidSet = new Set(pids);
|
|
10170
|
+
let matchedTerminal = null;
|
|
10171
|
+
for (const [tsid, session] of terminalSessions.entries()) {
|
|
10172
|
+
const pid = session?.pty?.pid;
|
|
10173
|
+
if (pid && pidSet.has(pid)) { matchedTerminal = tsid; break; }
|
|
10174
|
+
}
|
|
10175
|
+
if (!matchedTerminal) return res.json({ matched: false });
|
|
10176
|
+
const loopFile = findLoopFileForTerminal(matchedTerminal);
|
|
10177
|
+
if (!loopFile) return res.json({ matched: false, terminalSessionId: matchedTerminal });
|
|
10178
|
+
try {
|
|
10179
|
+
const data = JSON.parse(readFileSync(resolve(LOOP_DIR, loopFile), 'utf-8'));
|
|
10180
|
+
return res.json({
|
|
10181
|
+
matched: true,
|
|
10182
|
+
terminalSessionId: matchedTerminal,
|
|
10183
|
+
browserSessionId: data.browserSessionId || null,
|
|
10184
|
+
browserTabId: data.browserTabId || null,
|
|
10185
|
+
});
|
|
10186
|
+
} catch {
|
|
10187
|
+
return res.json({ matched: false, terminalSessionId: matchedTerminal });
|
|
10188
|
+
}
|
|
10189
|
+
} catch (err) {
|
|
10190
|
+
res.status(500).json({ error: err.message });
|
|
10191
|
+
}
|
|
10192
|
+
});
|
|
10193
|
+
|
|
10140
10194
|
// GET /api/loop/history — no history keeping, always empty
|
|
10141
10195
|
app.get('/api/loop/history', (_req, res) => { res.json([]); });
|
|
10142
10196
|
|
|
@@ -10458,6 +10512,15 @@ async function launchScheduledLoop(schedule) {
|
|
|
10458
10512
|
if (scheduledBrowserTabId) schedExtraEnv.SYNABUN_BROWSER_TAB = scheduledBrowserTabId;
|
|
10459
10513
|
}
|
|
10460
10514
|
|
|
10515
|
+
if (cliProfile === 'opencode' && (scheduledBrowserSessionId || scheduledBrowserTabId)) {
|
|
10516
|
+
try {
|
|
10517
|
+
const xdgRoot = setupOpencodeLoopConfig(terminalSessionId, scheduledBrowserSessionId, scheduledBrowserTabId);
|
|
10518
|
+
if (xdgRoot) schedExtraEnv.XDG_CONFIG_HOME = xdgRoot;
|
|
10519
|
+
} catch (err) {
|
|
10520
|
+
console.warn(`[schedule] opencode per-loop config setup failed: ${err.message}`);
|
|
10521
|
+
}
|
|
10522
|
+
}
|
|
10523
|
+
|
|
10461
10524
|
const pendingId = 'pending-' + randomBytes(8).toString('hex');
|
|
10462
10525
|
const loopState = {
|
|
10463
10526
|
active: true,
|
|
@@ -19900,6 +19963,68 @@ function prepareExecTaskFile(terminalSessionId, loopState) {
|
|
|
19900
19963
|
return taskFile;
|
|
19901
19964
|
}
|
|
19902
19965
|
|
|
19966
|
+
/**
|
|
19967
|
+
* Build a per-loop XDG_CONFIG_HOME with an opencode config.json whose MCP env
|
|
19968
|
+
* block is augmented with this loop's SYNABUN_BROWSER_SESSION/TAB pins.
|
|
19969
|
+
* OpenCode's `opencode run` reads MCP server env verbatim from config.json; the
|
|
19970
|
+
* pins set on the PTY don't propagate to the MCP child, so we have to bake them
|
|
19971
|
+
* into the config file. Returns the XDG_CONFIG_HOME root (parent of opencode/).
|
|
19972
|
+
*/
|
|
19973
|
+
function setupOpencodeLoopConfig(terminalSessionId, browserSessionId, browserTabId) {
|
|
19974
|
+
const xdgRoot = resolve(DATA_HOME, 'data', 'loop-configs', `opencode-${terminalSessionId}`);
|
|
19975
|
+
const targetDir = resolve(xdgRoot, 'opencode');
|
|
19976
|
+
if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
|
|
19977
|
+
|
|
19978
|
+
let baseCfg = {};
|
|
19979
|
+
try {
|
|
19980
|
+
const userPath = getOpencodeConfigPath();
|
|
19981
|
+
if (existsSync(userPath)) baseCfg = JSON.parse(readFileSync(userPath, 'utf8'));
|
|
19982
|
+
} catch { /* start from blank if user's config is missing/broken */ }
|
|
19983
|
+
if (!baseCfg.mcp) baseCfg.mcp = {};
|
|
19984
|
+
const existingEntry = baseCfg.mcp.SynaBun;
|
|
19985
|
+
if (existingEntry && typeof existingEntry === 'object') {
|
|
19986
|
+
const env = { ...(existingEntry.environment || existingEntry.env || {}) };
|
|
19987
|
+
if (browserSessionId) env.SYNABUN_BROWSER_SESSION = browserSessionId;
|
|
19988
|
+
if (browserTabId) env.SYNABUN_BROWSER_TAB = browserTabId;
|
|
19989
|
+
env.SYNABUN_TERMINAL_SESSION = terminalSessionId;
|
|
19990
|
+
// config.json uses "environment" (per persistMcpToOpencodeConfig). Preserve both
|
|
19991
|
+
// keys defensively in case different opencode versions read one or the other.
|
|
19992
|
+
baseCfg.mcp.SynaBun = { ...existingEntry, environment: env, env };
|
|
19993
|
+
} else {
|
|
19994
|
+
// No SynaBun entry — fall back to canonical MCP install pointing at our preload
|
|
19995
|
+
const { mcpIndexPath, envPath } = getMcpPaths();
|
|
19996
|
+
const env = {
|
|
19997
|
+
DOTENV_PATH: envPath,
|
|
19998
|
+
SYNABUN_DATA_HOME: DATA_HOME,
|
|
19999
|
+
MEMORY_DATA_DIR: resolve(DATA_HOME, 'mcp-data'),
|
|
20000
|
+
SYNABUN_TERMINAL_SESSION: terminalSessionId,
|
|
20001
|
+
};
|
|
20002
|
+
if (browserSessionId) env.SYNABUN_BROWSER_SESSION = browserSessionId;
|
|
20003
|
+
if (browserTabId) env.SYNABUN_BROWSER_TAB = browserTabId;
|
|
20004
|
+
baseCfg.mcp.SynaBun = {
|
|
20005
|
+
type: 'local',
|
|
20006
|
+
command: ['node', mcpIndexPath],
|
|
20007
|
+
environment: env,
|
|
20008
|
+
env,
|
|
20009
|
+
};
|
|
20010
|
+
}
|
|
20011
|
+
const target = resolve(targetDir, 'config.json');
|
|
20012
|
+
writeFileSync(target, JSON.stringify(baseCfg, null, 2) + '\n', 'utf8');
|
|
20013
|
+
return xdgRoot;
|
|
20014
|
+
}
|
|
20015
|
+
|
|
20016
|
+
/**
|
|
20017
|
+
* Best-effort cleanup of a per-loop opencode XDG_CONFIG_HOME dir on loop stop.
|
|
20018
|
+
*/
|
|
20019
|
+
function cleanupOpencodeLoopConfig(terminalSessionId) {
|
|
20020
|
+
const xdgRoot = resolve(DATA_HOME, 'data', 'loop-configs', `opencode-${terminalSessionId}`);
|
|
20021
|
+
try {
|
|
20022
|
+
if (existsSync(xdgRoot)) rmSync(xdgRoot, { recursive: true, force: true });
|
|
20023
|
+
} catch (err) {
|
|
20024
|
+
console.warn(`[loop/stop] Failed to remove opencode loop config dir: ${err.message}`);
|
|
20025
|
+
}
|
|
20026
|
+
}
|
|
20027
|
+
|
|
19903
20028
|
/**
|
|
19904
20029
|
* Build a CLI exec command for non-Claude loop iterations.
|
|
19905
20030
|
* Task is read from a temp file via stdin (`cat <file> | <cli> exec -`)
|
|
@@ -19915,11 +20040,28 @@ function buildExecCommand(profile, loopState, taskFile) {
|
|
|
19915
20040
|
// Shell-escape a string for safe embedding in single quotes
|
|
19916
20041
|
const esc = (s) => "'" + s.replace(/'/g, "'\\''") + "'";
|
|
19917
20042
|
|
|
20043
|
+
// Per-loop MCP env overrides. Codex's shell_environment_policy filters parent env,
|
|
20044
|
+
// so PTY-level SYNABUN_BROWSER_TAB/SESSION pins are stripped before reaching the
|
|
20045
|
+
// SynaBun MCP child. Inject them directly into the MCP env via `-c` overrides.
|
|
20046
|
+
const mcpEnvOverrides = [];
|
|
20047
|
+
if (loopState.browserSessionId) {
|
|
20048
|
+
mcpEnvOverrides.push(['SYNABUN_BROWSER_SESSION', loopState.browserSessionId]);
|
|
20049
|
+
}
|
|
20050
|
+
if (loopState.browserTabId) {
|
|
20051
|
+
mcpEnvOverrides.push(['SYNABUN_BROWSER_TAB', loopState.browserTabId]);
|
|
20052
|
+
}
|
|
20053
|
+
if (loopState.terminalSessionId) {
|
|
20054
|
+
mcpEnvOverrides.push(['SYNABUN_TERMINAL_SESSION', loopState.terminalSessionId]);
|
|
20055
|
+
}
|
|
20056
|
+
|
|
19918
20057
|
switch (profile) {
|
|
19919
20058
|
case 'codex': {
|
|
19920
20059
|
// --dangerously-bypass-approvals-and-sandbox: SynaBun Automation Studio
|
|
19921
20060
|
// provides its own sandboxed, user-supervised environment.
|
|
19922
20061
|
const parts = ['cat', esc(taskFile), '|', bin, 'exec', '--dangerously-bypass-approvals-and-sandbox'];
|
|
20062
|
+
for (const [k, v] of mcpEnvOverrides) {
|
|
20063
|
+
parts.push('-c', esc(`mcp_servers.SynaBun.env.${k}="${v}"`));
|
|
20064
|
+
}
|
|
19923
20065
|
if (model) parts.push('--model', model);
|
|
19924
20066
|
parts.push('-C', esc(cwd), '-');
|
|
19925
20067
|
return parts.join(' ');
|