Commit graph

108 commits

Author SHA1 Message Date
Naiyuan Qing
0895d42d3b Merge remote-tracking branch 'origin/main' into feat/telegram-channel
# Conflicts:
#	apps/desktop/src/hooks/use-local-chat.ts
#	packages/sdk/src/actions/stream.ts
#	packages/ui/src/components/chat-view.tsx
#	src/agent/async-agent.ts
#	src/agent/events.ts
2026-02-09 14:28:06 +08:00
Bohan Jiang
b7085b2bf5
Merge pull request #107 from multica-ai/fix/new-user-onboarding
fix(desktop): new user onboarding — show errors and configure dialog in Chat
2026-02-09 13:54:48 +08:00
Jiang Bohan
ed681a96bf feat(desktop): add Configure button in chat error banner
When the agent fails due to missing API key, the error banner now
shows a "Configure" button that opens the same ApiKeyDialog (or
OAuthDialog) used on the home page. After successful configuration
the error clears and the user can immediately start chatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:51:55 +08:00
Naiyuan Qing
23905daaa1 Merge remote-tracking branch 'origin/main' into feat/telegram-channel
# Conflicts:
#	apps/desktop/electron/electron-env.d.ts
#	apps/desktop/electron/ipc/index.ts
#	apps/desktop/electron/preload.ts
#	apps/desktop/src/App.tsx
#	apps/desktop/src/pages/layout.tsx
#	src/agent/async-agent.ts
#	src/agent/runner.ts
#	src/hub/hub.ts
2026-02-09 13:44:08 +08:00
Naiyuan Qing
6a02fd29be fix(ui): adjust chat input padding, icon, and button size
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 11:43:49 +08:00
Naiyuan Qing
54b3ebe9e9 feat(ui): render error messages with Markdown
Use MemoizedMarkdown for error messages so links are clickable
instead of plain text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 08:04:19 +08:00
Naiyuan Qing
1819f4196d fix(sdk): add AgentErrorEvent to StreamPayload type
StreamPayload.event was missing the agent_error event type, causing
TypeScript errors in useGatewayChat and useLocalChat where the
comparison payload.event.type === "agent_error" had no type overlap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 08:04:13 +08:00
Naiyuan Qing
56ebe613db fix(hooks): handle agent_error events in useGatewayChat
Desktop path already forwards agent_error to chat.setError() via
use-local-chat.ts, but the Web/Gateway path was missing this handling.
Add agent_error interception in the StreamAction branch so Web clients
render LLM errors the same way Desktop does.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 07:30:35 +08:00
Jiang Bohan
0e383f51ab feat(desktop): display agent errors in Chat UI instead of hanging
When the agent encounters an error (e.g. no API key configured),
the Chat UI now shows an error banner instead of silently hanging.
The user can still type and retry after fixing their configuration.

- Add AgentErrorEvent to SDK stream types
- Forward agent_error events through IPC to renderer
- Handle error events in useLocalChat hook
- Keep chat input enabled for AGENT_ERROR (retriable)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:23:53 +08:00
yushen
6ecdbc5783 fix(exec-approval): treat expiresAtMs=-1 as non-expiring 2026-02-06 18:09:01 +08:00
Naiyuan Qing
1e6da205df feat(ui): spin MulticaIcon on hover
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 20:22:29 +08:00
Naiyuan Qing
5e174b8fef fix(ui): responsive horizontal padding on message list
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 20:15:19 +08:00
Naiyuan Qing
21312abd01 feat: chat input padding 2026-02-05 19:02:38 +08:00
Naiyuan Qing
039a625dbd
Merge pull request #96 from multica-ai/feat/message-return
feat(chat): add message history pagination with scroll-up loading
2026-02-05 18:48:56 +08:00
Naiyuan Qing
02f3534f2e chore(chat): set DEFAULT_MESSAGES_LIMIT to 200 for production
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:45:15 +08:00
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
Naiyuan Qing
65c2fea1b6 feat(chat): add message history pagination with scroll-up loading
Return latest messages by default instead of oldest. Support paginated
loading of older messages when scrolling up via IntersectionObserver,
with scrollHeight compensation to preserve scroll position.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:40:15 +08:00
Naiyuan Qing
9bcb0993b1 Merge remote-tracking branch 'origin/main' into exec-approvals
# Conflicts:
#	apps/desktop/src/hooks/use-local-chat.ts
2026-02-05 17:54:19 +08:00
Naiyuan Qing
bece8f0545 refactor(web): adapt chat page for extracted shared hooks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:51:12 +08:00
Naiyuan Qing
7a21686505 refactor(hooks,ui): extract useGatewayChat hook and update shared components
Move gateway-specific chat logic into dedicated useGatewayChat hook.
useChat remains a pure state hook with no IO. Update ChatView props,
remove legacy chat.tsx and connect-prompt.tsx.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:50:47 +08:00
Naiyuan Qing
056b9abff6 refactor(ui): deduplicate device-pairing with StatusWrapper and PairingHeader
Extract StatusWrapper to replace duplicated fullscreen/inline wrapper in
ConnectionStatus and RejectedStatus. Extract PairingHeader to replace
repeated title+description blocks in mobile and desktop views. Make
desktop container mb responsive (mb-14 sm:mb-28).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:07:45 +08:00
Naiyuan Qing
b896ac402a refactor(ui): self-contain Loading/Spinner styles and clarify usage semantics
Move all CSS out of globals.css into inline styles within each component.
Loading (Apple-style radiating lines) for passive waiting states;
Spinner (3x3 grid pulse) for active processing/execution states.
Switch device-pairing ConnectionStatus from Loading to Spinner.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:38:39 +08:00
Naiyuan Qing
53c350ea33 refactor(ui,hooks): extract shared ChatView and DevicePairing to packages
- Extract ChatView from web chat-page into packages/ui as a prop-driven
  component (accepts UseChatReturn shape, no transport dependency)
- Move DevicePairing from apps/web to packages/ui with locally defined
  ConnectionIdentity type (no @multica/hooks dependency)
- Create @multica/hooks package with useGatewayConnection and useChat
  (moved from apps/web/hooks)
- Add isLoadingHistory state to useChat with skeleton loading in ChatView
- Add MulticaIcon (pure CSS asterisk via clip-path, adapts to theme)
- Slim web chat-page.tsx from 188 to 65 lines (just wires hooks to UI)

Desktop can now reuse ChatView and DevicePairing directly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:28:44 +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
7fdbf24c4e feat(ui): add exec approval card and refine tool/thinking expand style
Add ExecApprovalItem for human-in-the-loop command approval with uniform
outline buttons (Allow/Always/Deny), countdown timer, and command display.
Refine ToolCallItem and ThinkingItem: transparent by default, unified
bg-muted/30 wrapper on expand with seamless button+content integration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:44:16 +08:00
Naiyuan Qing
0a6e930c2b feat(ui): render thinking content blocks in message list
Add ThinkingItem component for displaying LLM extended thinking. Renders
as a collapsible row (matching ToolCallItem style) with expand to reveal
thinking text. MessageList now extracts and renders ThinkingContent blocks
before text content, matching the LLM output order.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:44:09 +08:00
Naiyuan Qing
348b12b532 fix(ui): prevent long unbreakable strings from overflowing markdown
Add break-words to markdown-content wrapper so URLs and file paths in
<p>, <li>, <a> etc. wrap at container boundary instead of overflowing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:44:04 +08:00
Naiyuan Qing
44fef52071 feat(ui): add container utility and loading spinner component
Add @utility container (w-full max-w-4xl mx-auto) to globals.css and
create a reusable Loading spinner component for consistent loading states.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:43:48 +08:00
Naiyuan Qing
3c303df8f1 Merge branch 'main' into exec-approvals 2026-02-05 11:06:52 +08:00
Naiyuan Qing
e8012a5580 feat(ui): use default font size for messages and larger send button
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 11:04:21 +08:00
Naiyuan Qing
22c957ee76 chore(web): enable experimental HTTPS and fix scanner active states
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 10:44:29 +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
cff9c004db feat(ui): add paste mode success/error feedback with state-based rendering
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>
2026-02-05 09:53:09 +08:00
Naiyuan Qing
544425ae40 feat(ui): improve mobile onboarding copy and chat input sizing
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>
2026-02-05 09:23:53 +08:00
Naiyuan Qing
8e8a0d58d1 feat(ui): redesign connect prompt with scan/paste tabs and fullscreen QR
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>
2026-02-05 09:23:46 +08:00
Naiyuan Qing
845e503899 feat(ui): redesign QR scanner with state machine and mobile sheet
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>
2026-02-05 08:22:49 +08:00
Naiyuan Qing
d0681ef46d feat(ui): extend QR scanner hook with lifecycle control and flash API
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>
2026-02-05 08:22:42 +08:00
Naiyuan Qing
b74a5ea1a7 feat(ui): replace ChatInput textarea with Tiptap editor
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>
2026-02-05 07:56:11 +08:00
Naiyuan Qing
83e1210ec0 merge: resolve conflicts with main, keep pi-ai types + add extractThinkingFromEvent
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 18:44:23 +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
1f7951df1b fix(ui): render tool calls immediately from assistant toolCall blocks
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>
2026-02-04 18:12:34 +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
d930040258 feat(sdk): add exec approval action types for client integration
- ExecApprovalRequestAction: Hub-to-Client approval request payload
- ResolveExecApprovalParams/Result: Client-to-Hub RPC types
- ApprovalDecision type: allow-once, allow-always, deny
- Export from SDK actions index for frontend consumption

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:07:22 +08:00
LinYushen
c70dd338c2
Merge pull request #84 from multica-ai/reasoning-mode-support
feat(agent): add reasoning mode for thinking content display
2026-02-04 16:15:20 +08:00
yushen
0340358a9b feat(sdk): add thinking content extraction for stream events
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>
2026-02-04 15:50:41 +08:00
Naiyuan Qing
096b284cef
Merge pull request #80 from multica-ai/feat/web-qr-scanner
feat(ui): add QR code scanner to ConnectPrompt
2026-02-04 15:44:02 +08:00