Skip to content

feat(web-react,assistant): shared ChatComposer + relocate the assistant into agent-app#155

Merged
Tjemmmic merged 4 commits into
mainfrom
feat/chat-composer
Jun 30, 2026
Merged

feat(web-react,assistant): shared ChatComposer + relocate the assistant into agent-app#155
Tjemmmic merged 4 commits into
mainfrom
feat/chat-composer

Conversation

@Tjemmmic

Copy link
Copy Markdown
Contributor

Part of a cross-repo effort to converge every chat surface onto agent-app's
chat components and delete the "Command Deck" chat (@tangle-network/ui/chat).
Addresses tangle-network/agent-dev-container#2715 (off-theme assistant composer)
and #1923 (four divergent chat inputs); those issues close in the downstream
agent-dev-container PR once this publishes.

What

web-react: add ChatComposer — the auto-resizing message input every app
hand-rolled (Enter sends / Shift+Enter newline, opt-in attach + drag-drop with
pending-file chips, streaming Stop/Send, a controls slot for the model/effort
pills, Cmd/Ctrl+L focus). It styles only against the shared design tokens — no
private --chat-* custom properties
— so it themes correctly in any shell (the
root cause of the off-theme assistant input). ModelPicker gains an optional
priorityGroup prop so a product can pin a labeled section (e.g. a tuner app's
fine-tuned models) above Recommended.

./assistant: relocate the in-app assistant out of sandbox-ui and rebuild its
chat surface on web-react's own components:

  • transcript → AssistantTranscript (web-react ChatMessages) via adaptTranscript,
    folding each turn's text/tool segments in emission order; the AgentTimeline
    build-timeline path is dropped
  • composer → ChatComposer with ModelPicker in its controls slot
  • provider marks → web-react ProviderLogo; timeAgo inlined
  • markdown + per-tool detail renderers are injected props

Net: @tangle-network/agent-app/assistant imports no @tangle-network/ui
(Command Deck) and no @tangle-network/sandbox-ui
— the dependency graph stays a
DAG (sandbox-ui continues to never import agent-app).

Verification

  • pnpm typecheck clean
  • pnpm test — 2147 pass (incl. 211 assistant tests); copied tests conformed to the
    stricter tsconfig + a shared RTL-cleanup test-setup
  • ChatComposer visually verified against the design mockups (light + dark) in the
    playground's new /composer route

Draft until the downstream sandbox-ui + agent-dev-container phases are validated
against this branch (linked locally), after which agent-app publishes.

Tjemmmic added 3 commits June 29, 2026 15:42
Add ChatComposer: the auto-resizing message input every agent app
hand-rolled (Enter sends / Shift+Enter newline, opt-in attach +
drag-and-drop with pending-file chips, streaming Stop/Send toggle, a
controls slot for the model/effort pills, Cmd/Ctrl+L focus). It styles
only against the shared design tokens — no private --chat-* custom
properties — so it themes correctly in any shell, replacing the
off-theme inputs across the products.

Add an optional priorityGroup to ModelPicker so a product can pin a
labeled section (e.g. a tuner app's own fine-tuned models) above
Recommended without duplicating those rows in the provider groups.
A /composer route renders ChatComposer across its states (model pill
above, typed, streaming, attachments, footer placement) for visual
review in light + dark. Alias web-react's lazy terminal import (and its
@xterm deps) to a stub so the playground dev server starts without the
terminal panel's runtime deps installed.
Relocate the portable assistant/copilot feature (dock, panel, history,
reducer, transport client, persistence, hooks, proposal card) and rebuild
its chat surface on web-react's own components, so the whole stack ships
from one package and the dependency graph stays a DAG.

- Transcript renders via AssistantTranscript (web-react ChatMessages) with
  adaptTranscript folding each turn's text/tool segments in emission order;
  the AgentTimeline build-timeline path is dropped.
- Composer is ChatComposer with the ModelPicker in its controls slot;
  the catalog maps onto CatalogModel.
- Provider marks render via web-react ProviderLogo; timeAgo is inlined.
- The markdown renderer and per-tool detail renderers are injected props,
  so this subpath imports no @tangle-network/ui (Command Deck) or
  sandbox-ui.

A shared test-setup registers @testing-library cleanup between tests
(this repo doesn't run with globals), and the copied tests are conformed
to the stricter tsconfig (noUncheckedIndexedAccess).
@Tjemmmic

Copy link
Copy Markdown
Contributor Author

🤖 AI Code Review (ensemble)

Summary

This PR introduces a portable @tangle-network/agent-app/assistant subpath, including a chat dock, state hooks, persistence, SSE parsing, and shared UI components. The code exhibits robust defensive practices including safe localStorage handling, focus trapping, and security tests against XSS; however, one functional data loss bug was identified in the SSE client parsing logic.

Issues Found

1 total — 0 P1 (blocking) · 1 P2 (should fix) · 0 P3 (nice to have)

⚠️ P2 — SSE client drops tool_call args despite the public contract preserving them

  • File: src/assistant/client.ts:L214-L218
  • Problem: ToolCallEventData includes optional args, and the reducer/transcript architecture expects them so AssistantTranscript can pass them to web-react tool cards and custom toolRenderers. However, the new toStreamEvent parser validates callId and name but returns only { callId, name }, so any server-emitted tool arguments are silently lost and custom detail renderers cannot show the exact tool invocation.
  • Fix: Preserve object-shaped args when parsing tool_call, and add a client test covering it. For example: const args = asObject(obj.args); return { type: "tool_call", data: { callId, name, ...(args ? { args } : {}) } };

✅ APPROVE

There are no P1 blocking issues and only one P2 issue identified from the available diff. The code generally includes scoped persistence, abort/cleanup handling, robust focus trapping, and targeted tests for higher-risk paths. The dropped tool_call.args should be fixed before relying on custom tool-detail rendering or invocation audit UI, but it does not block this draft PR from merging.

Quick Reference

  • P2: SSE client drops tool_call args despite the public contract preserving them

Synthesized by Sokuza AI from multiple independent reviewers

The chat components (ModelPicker, ChatMessages) consume CatalogModel, so a
web-react consumer should import the type from here rather than the package
root — avoids depending on the heavyweight root barrel just for the model type.
@Tjemmmic

Tjemmmic commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

🤖 AI Code Review (ensemble)

Resolution (190d748): 1 not an issue.

Summary

This PR introduces a portable, cross-host assistant surface (@tangle-network/agent-app/assistant) comprising a dock, panel, SSE client, history, and persistence layer, alongside a shared ChatComposer component in web-react. The code is densely written but features excellent documentation, robust security considerations, and comprehensive unit tests for the highest-risk areas. Reviewer A's concerns regarding SSR and SSE memory exhaustion are not supported by the diff, which explicitly handles non-browser environments and uses a standard, efficient parser.

Issues Found

1 total — 0 P1 (blocking) · 1 P2 (should fix) · 0 P3 (nice to have)

⚠️ P2 — Missing tests for useAssistantChat hookNot an issue

Note: src/assistant/useAssistantChat.ts is tested by src/assistant/useAssistantChat.test.tsx (480 lines, 13 cases) covering the exact scenarios called out: initial load (restores a persisted thread on mount; switchThread loads a past transcript), message sending (one request per same-tick send; send blocked while a proposal awaits confirm; confirm runs once on double-click), streaming updates (late stream events dropped after a user switch and after reset), and error handling (history-restore 404 clears the thread id; transcript-load failure drops the thread; MODEL_NOT_ALLOWED clears the selected model).

  • File: src/assistant/useAssistantChat.ts
  • Problem: The PR introduces significant new logic in useAssistantChat, including thread persistence, SSE event handling, and state management for streaming messages. While other components like ProposalCard and AssistantHistory have extensive tests, this critical hook lacks corresponding test coverage, increasing the risk of regressions in the core chat functionality.
  • Fix: Add unit tests for useAssistantChat covering scenarios like initial load, message sending, streaming updates, and error handling.

✅ APPROVE

Reviewers B and C approved, and the diff confirms there are no P1 blocking issues. The code explicitly defends against SSR/Node environments (e.g., typeof window === "undefined" guards) refuting the ReferenceError claims. The SSE chunk parser correctly trims processed lines and manages the buffer according to the standard SSE wire format, discarding keepalive comments line-by-line to prevent unbounded memory growth. Security is handled exceptionally well, particularly in the ProposalCard URL scheme validation. The only valid issue is the P2 missing test coverage for useAssistantChat, which is recommended but does not block this draft PR.

Quick Reference

  • P2: Missing tests for useAssistantChat hook

Synthesized by Sokuza AI from multiple independent reviewers

@Tjemmmic Tjemmmic marked this pull request as ready for review June 30, 2026 00:10
@Tjemmmic Tjemmmic merged commit 55e5ca7 into main Jun 30, 2026
1 check 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