Replace the synchronous /edit (wait for full markdown response) with an
async + SSE flow per demo.html so the user sees text stream in real-time
and can interact with permission/question events the agent raises.
Backend
-------
- EditHandler now calls OpenCode /session/:id/prompt_async and returns
immediately with {ok, sessionId, notebookPath}; the LLM reply is no
longer embedded in this response.
- New GlobalEventHandler proxies OpenCode /global/event as
text/event-stream. Server forwards ALL events; the client filters.
A too-eager server-side ?session= filter was silently dropping events
the client would have accepted, so it was removed.
- New PermissionReplyHandler + QuestionReplyHandler forward user
replies (once/always/reject and freeform answer) back to OpenCode
Serve at /session/:sid/permissions/:permId and
/session/:sid/question/:qId/reply.
- OpenCodeClient gains send_message_async(), stream_global_events()
(async generator over the SSE feed via tornado streaming_callback),
reply_permission() and reply_question(). Legacy send_message_sync
removed.
Frontend
--------
- subscribeOpenCodeEvents() opens a fetch+reader SSE client (XSRF
token injected from serverSettings); AbortController-backed close()
is idempotent.
- OpenCodeInlinePrompt.applyEvent() routes events into the streaming
UI: text delta -> assistant message (re-rendered as markdown on
every delta so the user sees formatted <pre><code> blocks in
real-time, not raw fence source); reasoning/tool/permission/question
get collapsible details blocks. session.idle resets stream pointers
and fires onStreamEnd.
- Permission/question blocks render real interactive UI: three
buttons (once/always/reject) for permission, a text input + submit
for question. Click handlers post through the new API routes and
show success/failure status in place.
- Centralized idle detection (4 shapes: top-level + payload-nested
x {session.idle, session.status/idle}) inside the prompt; cell
action reacts via onStreamEnd callback rather than re-parsing
event types.
- System / workspace / pty / lsp / mcp / installation events AND
session-level control events (agent.switched, model.switched,
file.edited) are not rendered in the frontend.
Tests
-----
- 52 backend pytest (was 43)
- 58 frontend jest (was 46)
design.md section 3 updated for the new async /edit response shape
and the new GET /events SSE endpoint.
Co-Authored-By: Claude <noreply@anthropic.com>
When the user picks a provider+model in the inline prompt, remember
that choice across panel close/reopen AND across JupyterLab restarts,
keyed by notebookPath. If the stored provider (or the model under it)
is no longer present in the current providers payload, fall back to
the default (first provider / default[pid] model) - never silently
apply a stale value that would result in an empty or disabled select.
- New module src/components/model_selection.ts with
loadModelSelection / saveModelSelection / clearModelSelection.
Best-effort: localStorage may be disabled (private mode), quota
may be exhausted, value may be corrupt - all failure modes return
null / no-op rather than throw.
- IOpenCodeInlinePromptOptions gains notebookPath?: string.
- Constructor resolves the initial selection: prefer stored+valid,
else first provider / default[pid]. providerSelect.change and
modelSelect.change listeners call _persistSelection() to keep
storage in sync.
- _rebuildModelSelect now accepts a preferredModelId; if it exists
in the current provider's models it's used, else the default.
- OpenCodeCellActions._showPrompt passes notebookPath =
this._context?.notebookPath so the prompt can key the storage.
Tests
-----
- 10 unit tests in model_selection.spec.ts (load/save/clear +
malformed JSON + per-notebook independence).
- 4 prompt behavior tests in opencode_cell_actions.spec.ts
(valid stored applied, provider removed -> default, model removed
-> default, change -> saved).
- 72 frontend jest (was 58), 52 backend pytest (unchanged),
build green.
Co-Authored-By: Claude <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Replace the synchronous /edit (wait for full markdown response) with an async + SSE flow per demo.html so the user sees text stream in real-time and can interact with permission/question events the agent raises. Backend ------- - EditHandler now calls OpenCode /session/:id/prompt_async and returns immediately with {ok, sessionId, notebookPath}; the LLM reply is no longer embedded in this response. - New GlobalEventHandler proxies OpenCode /global/event as text/event-stream. Server forwards ALL events; the client filters. A too-eager server-side ?session= filter was silently dropping events the client would have accepted, so it was removed. - New PermissionReplyHandler + QuestionReplyHandler forward user replies (once/always/reject and freeform answer) back to OpenCode Serve at /session/:sid/permissions/:permId and /session/:sid/question/:qId/reply. - OpenCodeClient gains send_message_async(), stream_global_events() (async generator over the SSE feed via tornado streaming_callback), reply_permission() and reply_question(). Legacy send_message_sync removed. Frontend -------- - subscribeOpenCodeEvents() opens a fetch+reader SSE client (XSRF token injected from serverSettings); AbortController-backed close() is idempotent. - OpenCodeInlinePrompt.applyEvent() routes events into the streaming UI: text delta -> assistant message (re-rendered as markdown on every delta so the user sees formatted <pre><code> blocks in real-time, not raw fence source); reasoning/tool/permission/question get collapsible details blocks. session.idle resets stream pointers and fires onStreamEnd. - Permission/question blocks render real interactive UI: three buttons (once/always/reject) for permission, a text input + submit for question. Click handlers post through the new API routes and show success/failure status in place. - Centralized idle detection (4 shapes: top-level + payload-nested x {session.idle, session.status/idle}) inside the prompt; cell action reacts via onStreamEnd callback rather than re-parsing event types. - System / workspace / pty / lsp / mcp / installation events AND session-level control events (agent.switched, model.switched, file.edited) are not rendered in the frontend. Tests ----- - 52 backend pytest (was 43) - 58 frontend jest (was 46) design.md section 3 updated for the new async /edit response shape and the new GET /events SSE endpoint. Co-Authored-By: Claude <noreply@anthropic.com>