Add a "verifying" connection state between "connected" and "registered"
so collaborators see clear feedback while waiting for the device owner
to approve their connection on Desktop.
Changes across the stack:
- Hub: verify RPC returns isNewDevice flag to distinguish new vs whitelisted
- SDK: emit "verifying" state before verify RPC, pass isNewDevice through
- Store: capture isNewDevice via onVerified, capture rejection via onError
- UI: ConnectionStatus (waiting), RejectedStatus (declined), and
verify success overlay (approved) replace the stuck scanner screen
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace overlay approach with conditional rendering for paste validation
states. Success shows checkmark icon with 600ms delay before connecting,
error shows alert with message and auto-resets after 2s. Improves
Connecting text visibility.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rewrite user-facing text to be source-agnostic and result-focused:
- Connect prompt: "Scan to start" / "Scan a QR code to use an Agent"
- Empty chat: "Your Agent is ready"
- Input placeholder: "Ask your Agent..." / "Scan QR code to get started"
- Bump chat input font to text-base for mobile readability
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace sheet-based scanner with inline QrScannerView, add scan/paste
mode toggle for desktop, and fullscreen camera overlay for mobile.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rewrite QrScannerView with full state machine (idle/requesting/scanning/
detected/success/error), corner bracket animations snapping to QR
cornerPoints, flash toggle, and haptic feedback.
Add QrScannerSheet as bottom sheet wrapper for mobile. Simplify
ConnectPrompt to use useIsMobile() — mobile shows scan button + sheet,
desktop shows paste-only UI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add cornerPoints, hasFlash/toggleFlash, and manual start/stop/pause
to useQrScanner. Change enabled default to false for click-to-start.
Add scoped CSS animations for scanner bracket breathing and error shake.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Swap the plain <textarea> for a minimal Tiptap (ProseMirror) editor
with IME-safe Enter-to-submit, Shift+Enter newline, placeholder
support, and an optional imperative ref (getText/setText/focus/clear).
All rich-text extensions are disabled — only plain text is allowed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Handle `action: "error"` messages in connection-store (e.g. UNAUTHORIZED)
- Widen lastError type to `{code, message}` to support all error codes
- Display dismissible error banner in Chat with role="alert" and aria-live
- Add accessible close button with focus-visible ring
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tools were only visible after tool_execution_end because the UI relied
solely on toolResult messages (created at tool_execution_start, ~8ms
before end). Now MessageList detects toolCall blocks in the streaming
assistant message and renders them as "running" ToolCallItems immediately.
Once a real toolResult message arrives, the synthetic one is replaced.
- Add resolvedToolCallIds set to deduplicate assistant vs toolResult renders
- Extract getTextContent to shared utils to avoid duplication
- Wrap MessageList and ToolCallItem in memo for performance
- Add accessible region/tabIndex to expanded result panel
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove backward-compatible aliases (TextBlock, ThinkingBlock, ToolCallBlock)
and extractTextFromEvent from SDK — unused after prior refactors
- Add explicit ContentBlock doc comment explaining the wider union tradeoff
- Scope endStream tool interruption to the same agentId (prevents
cross-agent interference in multi-agent scenarios)
- Handle tool_execution_update event (no-op for now, avoids unhandled case)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace hugeicons status icons with CSS colored dots + glow-pulse
animation for running state (honors prefers-reduced-motion)
- Add smart subtitles from toolArgs: file basename, command preview,
search pattern, URL hostname
- Add right-aligned stats: line count, match count, file count
- Add hover chevron for expand/collapse affordance
- Fix accessibility: aria-label, aria-expanded, focus-visible ring
- Store toolArgs in Message (was received but discarded)
- Extract toolArgs from assistant ToolCall blocks during fetchHistory
- Add --tool-running/success/error CSS variables with dark mode support
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Message.content is now ContentBlock[] (was string), supporting
text, thinking, toolCall, and image blocks
- Add toolResult role with toolCallId, toolName, toolStatus, isError
- Add startToolExecution/endToolExecution to MessagesStore
- MessageList renders toolResult messages via ToolCallItem
- Extract text from ContentBlock[] for markdown rendering
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace hand-written message/content types in @multica/sdk with
`import type` from @mariozechner/pi-ai and @mariozechner/pi-agent-core.
This ensures compile-time correctness and eliminates type drift between
backend and frontend (e.g. "tool_use" vs "toolCall", "tool_result" vs
"toolResult").
- stream.ts: re-export TextContent, ThinkingContent, ToolCall,
ImageContent from pi-ai; use AgentEvent from pi-agent-core
- rpc.ts: AgentMessageItem = pi-ai Message (no more manual mirroring)
- connection-store.ts: use SDK types instead of inline hand-written ones
- ContentBlock now includes ImageContent to match backend reality
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update StreamMessageEvent content type to include thinking blocks.
Add extractThinkingFromEvent() helper and export it, enabling clients
to access reasoning content from streamed agent responses.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add browser-based QR scanning as an alternative to paste mode in the
ConnectPrompt component. Mobile users can scan the Desktop QR code
directly instead of manually copying connection codes.
- Add qr-scanner dependency (WebWorker-based decoding, BarcodeDetector support)
- Create use-qr-scanner hook wrapping camera lifecycle and cleanup
- Create QrScannerView component with viewfinder overlay
- ConnectPrompt auto-detects mobile (touch + narrow viewport) and defaults to scan mode
- Lazy-load scanner component for zero initial bundle impact
- Graceful fallback to paste mode on permission denial or no camera
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Auto-collect navigator.userAgent, platform, and language in the SDK
verify RPC payload. Add DeviceMeta type. Hub receives this metadata
for display in the Desktop UI device list and confirmation dialog.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Embed transparent verification logic in GatewayClient that automatically
sends an RPC "verify" request to the Hub after REGISTERED event. Adds
hubId, token, and verifyTimeout options. Upper-layer callers see no change
— "registered" state means both gateway registration and Hub verification
are complete. Failures surface via onError callback.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace useGatewayStore, useHubStore, useDeviceId, and useHubInit with:
- ConnectionStore: WebSocket lifecycle, deviceId persistence via Zustand persist,
fetch message history on registration via getAgentMessages RPC
- MessagesStore: simplified to current-agent-only, sendMessage accepts SendContext
to break circular import with ConnectionStore
- useAutoConnect: returns { loading } for skeleton UI, skips connect if already
connected (fixes Electron tab-switch reconnect), no cleanup disconnect
Removes: gateway.ts, hub.ts, hub-init.ts, device-id.ts, sonner dep from store
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove all props from Chat (showHeader, headerActions) making it a
zero-config pure chat component with only connection input, messages,
and send functionality
- Create AppHeader client component for web app with brand, theme
toggle, disconnect button, and useHubInit
- Add disconnect button to desktop layout header
- Add reset() action to hub store to eliminate duplicated state reset
- Remove unused token field from gateway store
- Remove dead code: connection-bar.tsx
- Guard handleConnect against empty deviceId race condition
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The URL constructor parses non-standard protocols (multica://)
differently across engines — Chromium may return empty hostname
while Node.js returns "connect". Replace new URL() with simple
startsWith check and URLSearchParams to ensure consistent behavior
in both web and Electron environments.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add @multica/store and zustand to desktop dependencies. Replace
placeholder chat page with the shared Chat component. Add Toaster
for toast notifications and remove padding on the chat route.
Change Chat root from h-dvh to h-full for container adaptability.
Add showHeader prop to Chat; desktop passes showHeader={false}
since it has its own layout header.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Chat component no longer depends on next-themes, making it safe to use
in the desktop app. Theme toggle is now a fixed button in the web layout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Merge auth-profiles feature from main into runner.ts
- Merge closeCallbacks feature from main into async-agent.ts
- Regenerate pnpm-lock.yaml with new dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add connection code parsing (JSON + base64) with localStorage persistence
- Add connectWithCode() to gateway store for code-driven connections
- Replace Sidebar with simple ConnectionBar (paste code / status / disconnect)
- Simplify Chat component by removing header clutter
- Auto-restore connection on page refresh from saved code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create tool-list.tsx with collapsible groups and toggle switches
- Create skill-list.tsx with status badges and action dialogs
- Create qr-code.tsx for connection QR code display
- Add dialog.tsx component to @multica/ui
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Show a spinner with "Generating..." label while assistant messages are
being streamed. The indicator appears immediately when content is empty
and persists below the content until streaming completes.
Also fix duplicate React key warning by including block index in the key
to handle blocks with identical content.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend Hub now sends raw AgentEvent in stream payloads instead of the
old delta/final/error state machine. Update StreamPayload type to use
event field, add extractTextFromEvent helper, and rewrite gateway store
handler to dispatch on event.type (message_start/update/end).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove template boilerplate (sample SVGs, test IPC message, comments),
add react-router-dom v7 with createHashRouter, scaffold home and chat
pages using @multica/ui components, and add READMEs for desktop, ui,
and store packages documenting import conventions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Messages store gains streamingIds set and startStream/appendStream/
endStream actions. Gateway store routes stream action payloads to
these new actions. Chat component switches to StreamingMarkdown
for in-progress messages, providing incremental block-level rendering.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AsyncAgent now subscribes to pi-agent-core events (message_start,
message_update, message_end) and forwards incremental text deltas
through a stream callback. Hub registers the callback and sends
stream payloads to the requesting client via Gateway.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change package exports from dist/ to src/ (consistent with @multica/store and @multica/ui)
- Add private: true since SDK is an internal monorepo package
- Switch tsconfig from NodeNext to bundler moduleResolution
- Remove .js suffixes from all internal imports for Turbopack compatibility
Eliminates the need to build SDK before running dev server or Next.js build.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move gatewayUrl into gateway store state with setGatewayUrl action
- layout.tsx uses useGatewayStore.getState().setGatewayUrl() directly
- Remove @multica/fetch from store and web package.json dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add useAutoScroll hook using ResizeObserver + MutationObserver
- Observes content children for size changes (streaming, images)
- Watches for new DOM nodes (new messages, history load)
- Respects user scroll position: no force-scroll when reading above
- Integrate in Chat component alongside existing useScrollFade
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add typed AgentMessageItem to SDK with proper content block types
- Add fetchAgentMessages action to hub store using getAgentMessages RPC
- Extract text from complex content blocks (user string, assistant array)
- Auto-load history when selecting an agent with no local messages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add LIST_DEVICES event and "hub" device type to SDK
- Add listDevices() method to GatewayClient
- Add handleListDevices handler in Gateway
- Change Hub deviceType from "client" to "hub"
- Refactor gateway store: auto-connect WS, separate hubId selection
- Hub-init: auto-connect on mount, discover hubs on registered
- Hub-sidebar: show discovered hubs list with connect/disconnect UI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add 5 new RPC methods (getHubInfo, listAgents, createAgent, deleteAgent,
updateGateway) mirroring the existing Console HTTP API, enabling pure
WebSocket communication from the frontend.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add request<T>() method that handles full request/response lifecycle
with auto-generated requestId, timeout handling, and pending request
cleanup on disconnect. Also add GetAgentMessagesParams and
GetAgentMessagesResult type definitions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sync latest code from src/shared/gateway-sdk/ into packages/sdk/,
update all backend imports to use @multica/sdk, and remove the
duplicate src/shared/gateway-sdk/ directory.
- Translate Chinese comments to English in SDK source
- Fix package.json exports with default condition
- Add @multica/sdk as workspace dependency for backend
- Update imports in gateway, test-client, and hub
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>