Commit graph

26 commits

Author SHA1 Message Date
Naiyuan Qing
1fe27a59d0 chore(store): remove unused Zustand stores and slim down package
After the chat refactoring moved state management to @multica/hooks,
the Zustand stores (useConnectionStore, useMessagesStore, useAutoConnect)
are no longer imported by any application code. This removes them along
with their unused dependencies (zustand, uuid, react).

- Delete connection-store.ts, messages.ts, use-auto-connect.ts
- Extract Message/ToolStatus types into types.ts (preserves UI imports)
- Remove saveConnection/loadConnection/clearConnection from connection.ts
- Drop zustand, uuid, react deps from package.json

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:41:31 +08:00
yushen
8f9dcbf7e5 fix(agent): address compaction feedback review findings
- Wrap maybeCompact() in try/catch to ensure compaction_end always fires
- Widen multicaListeners type to match subscribeAll() callback signature
- Import CompactionEndEvent from SDK instead of inline type casts
- Add doc comment explaining reason field type difference (agent vs SDK)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:27:08 +08:00
yushen
1316d329ee feat(hub): propagate compaction events to all frontend transports
Forward compaction_start/compaction_end events through Hub (Gateway path)
and Desktop IPC (local path) to the Zustand messages store. Adds
CompactionEvent types to the SDK, compacting/lastCompaction state to
useMessagesStore, and event routing in both connection-store and
use-local-chat.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:55:00 +08:00
Naiyuan Qing
037908cf8d feat(connection): add device verification status feedback for collaborators
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>
2026-02-05 10:44:15 +08:00
Naiyuan Qing
e4f1d51453 feat(store,ui): handle Hub error messages and display error banner
- 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>
2026-02-04 18:39:32 +08:00
Naiyuan Qing
cfd46ee602 refactor(sdk,store): clean up SDK exports and scope tool interruption
- 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>
2026-02-04 18:12:24 +08:00
Naiyuan Qing
703c4686d9 feat(ui): improve tool-call-item with status dots, smart subtitles, and accessibility
- 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>
2026-02-04 17:30:46 +08:00
Naiyuan Qing
583242baba feat(store,ui): add tool execution lifecycle and ContentBlock rendering
- 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>
2026-02-04 17:30:37 +08:00
Naiyuan Qing
ef4e57ffdd refactor(sdk): unify message types with pi-ai source of truth
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>
2026-02-04 17:13:01 +08:00
yushen
3d13c28cfc feat(store): pass hubId and token to GatewayClient for verification
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:27:33 +08:00
Naiyuan Qing
162a86dff4 refactor(store): consolidate 3 stores into ConnectionStore + MessagesStore
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>
2026-02-04 10:12:45 +08:00
Naiyuan Qing
dacbfa9e3d refactor(ui): purify Chat component and move header to app layouts
- 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>
2026-02-03 20:31:24 +08:00
Naiyuan Qing
b6d7626239 fix(store): use string prefix + URLSearchParams for connection URL parsing
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>
2026-02-03 20:13:51 +08:00
Naiyuan Qing
e7521b4cb0 feat(web): replace sidebar with connection code flow
- 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>
2026-02-03 18:53:49 +08:00
Naiyuan Qing
d5b31eeddb fix(sdk,store): align frontend streaming protocol with new AgentEvent format
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>
2026-02-03 16:54:34 +08:00
Naiyuan Qing
86d00bb134 feat(ui): render AI messages with streaming markdown
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>
2026-02-02 17:18:35 +08:00
Naiyuan Qing
e491949749 refactor(store): remove @multica/fetch dependency
- 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>
2026-02-02 16:23:06 +08:00
Naiyuan Qing
4d6d57187c feat(store): load agent message history via RPC
- 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>
2026-02-02 16:13:59 +08:00
Naiyuan Qing
3622f82a7b feat(gateway): add auto-connect, hub discovery via list-devices
- 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>
2026-02-02 16:13:40 +08:00
Naiyuan Qing
150c87a496 refactor(frontend): replace HTTP API with pure WebSocket RPC
- gateway store: add hubId state and request() RPC method
- hub store: replace consoleApi calls with gateway.request() RPC
- hub-init: simplify to react on gwState === "registered"
- hub-sidebar: add hub-id input + connect/disconnect flow
- chat: use gateway hubId for message routing
- layout: remove consoleUrl config (no longer needed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 15:27:07 +08:00
Naiyuan Qing
4a7a9800f2 fix(frontend): apply code review fixes across store and UI
- Move setConfig() after imports in layout.tsx
- Add gateway disconnect cleanup in hub-init useEffect
- Replace silent catches with toast error notifications in hub store
- Optimize chat selectors with useMemo and useCallback/getState()
- Remove unused getMessagesByAgent from messages store
- Add clipboard try/catch with error toast
- Add skeleton loading states for hub sidebar and chat header

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 11:52:42 +08:00
Naiyuan Qing
eef26d8675 refactor(frontend): complete migration of all hooks and components to packages
- Create gateway store in @multica/store (WS connection independent of components)
- Gateway auto-connects when hub is ready, messages handled internally
- Move scroll-fade hook to @multica/ui/hooks
- Move Chat component to @multica/ui/components
- Add setConfig() call in web layout for URL injection
- Delete all web-local hooks, components, and lib/config
- Web app is now a pure shell: layout.tsx + page.tsx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 11:07:57 +08:00
Naiyuan Qing
97fce5b113 refactor(store): migrate messages hook to Zustand store
- Move useMessages from apps/web to packages/store/src/messages.ts
- Convert useState to Zustand store for global message persistence
- Add reserved interfaces: updateMessage, loadMessages, getMessagesByAgent
- Update chat.tsx imports to use @multica/store

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:49:52 +08:00
Naiyuan Qing
5275bc55fc refactor(store): migrate device-id hook to @multica/store
- Move useDeviceId from apps/web to packages/store/src/device-id.ts
- Update imports in chat.tsx and use-gateway.ts to use @multica/store
- Add uuid dependency to @multica/store

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:45:20 +08:00
Naiyuan Qing
63861d03c6 refactor(frontend): extract shared stores and components into packages
- Create @multica/fetch package for HTTP client and URL config
- Migrate hub store and hub-init hook to @multica/store
- Move HubSidebar component to @multica/ui for web/desktop reuse
- Update web app imports to use shared packages
- Remove counter store example and its component-example usage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:37:17 +08:00
Naiyuan Qing
d7d2861a79 feat(store): add shared Zustand store package with counter example
- Create packages/store with @multica/store package
- Add zustand to pnpm catalog for version consistency
- Add counter store as cross-platform state example
- Integrate counter into ComponentExample for verification
- Add tsconfig path mappings for web and desktop
- Add @multica/store to Next.js transpilePackages
- Add @multica/store dependency to packages/ui

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 11:34:09 +08:00