Skip to content

feat(web): Canvas chat, message send, and hide-completed default - #64

Merged
4ndreello merged 2 commits into
mainfrom
feat/web-send-chat
Sep 8, 2026
Merged

feat(web): Canvas chat, message send, and hide-completed default#64
4ndreello merged 2 commits into
mainfrom
feat/web-send-chat

Conversation

@4ndreello

Copy link
Copy Markdown
Owner

What

Chat transcript in the detail drawer. Clicking any node now opens a conversation view instead of a flat activity list:

  • user messages come from turn.started.prompt (the daemon attaches it on the initial turn and on every session.send)
  • assistant answers render in full from message events
  • tool, permission, and file events stay as compact gray lines so the conversation remains readable
  • reads GET /api/sessions/:id/logs (up to 1000 events, endpoint already existed but was unused by the canvas) instead of the 10-event session.get slice; renders the last 200 entries and autoscrolls to the latest

Send messages from the web. New POST /api/sessions/:id/send proxies to the existing daemon session.send method (resume turn, same path codedeck send uses):

  • body capped at 64 KiB → 413; invalid/empty JSON → 400
  • daemon error codes map to statuses: SESSION_NOT_FOUND → 404, SESSION_BUSY → 409, CAPABILITY_NOT_SUPPORTED → 400
  • the send box locks itself with an inline reason while the session is working/starting ("aguarde o turno atual terminar") and for origin=open interactive heads ("sessão interativa não aceita mensagens"); the poll keeps the lock state fresh as turns start and end

Hide-completed is now the default. A clean URL is the filtered state; ?hide=0 opts back in and ?hide=1 keeps working for existing bookmarks. The eye button starts active.

Why

The orchestrator nodes only ever showed "Trabalhando agora · processando..." — there was no way to see what a session actually said, and no way to answer it from the browser. The daemon already persisted everything needed and already implemented session.send; the web layer was the only gap.

Verification

  • npx vitest run: 74 files / 885 tests green (web-command suite covers the send route end-to-end: proxy, validation, 413, error mapping, method guards)
  • tsc --noEmit clean
  • Live check against a running daemon on an isolated port: POST .../zzfake/send → 404, broken JSON → 400; drawer verified in a real browser — 94-entry transcript rendered with autoscroll, input locked on an open-origin orchestrator and enabled on a completed run session; default filter shows "97 ocultas" with 3 visible nodes and no URL params

Detail drawer becomes a conversation view: the user's prompt
(turn.started.prompt, attached by the daemon on the initial turn and on
every send) renders as a user bubble, assistant message events as full
answer bubbles, and tool/permission/file events as compact lines. The
transcript reads GET /api/sessions/:id/logs (up to 1000 events) instead
of the 10-event session.get slice, and autoscrolls to the latest entry.

Sending is wired through a new POST /api/sessions/:id/send, proxying to
the existing daemon session.send method (resume turn). The body is
capped at 64 KiB (413), validated (400), and daemon error codes map to
HTTP statuses: SESSION_NOT_FOUND 404, SESSION_BUSY 409,
CAPABILITY_NOT_SUPPORTED 400. The send box locks itself while the
session is working/starting and for origin=open interactive heads, with
the reason spelled out next to the input.

Hiding completed sessions is now the default: a clean URL is the
filtered state and ?hide=0 opts back in (?hide=1 keeps working).
Four tests repeated the expect(CANVAS_PAGE).toContain(...) shape; Sonar's
CPD counted the new lines as duplicated blocks (11.8% > 3% on
web-command.test.ts). One table + one assertion pattern replaces all
four without losing coverage.
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

@4ndreello
4ndreello merged commit f75b087 into main Sep 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant