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>
Comprehensive single-source-of-truth document covering all product
modules, features, and technical details. Serves as the foundation
for subsequent UI design, copywriting, and user journey documents.
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>
The sessions_spawn tool was not showing in the desktop tools UI because
TOOL_GROUPS and ALL_KNOWN_TOOLS were missing the subagent group.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update workspace.md template to prevent agents from claiming to
"remember" things without actually editing files. The new guidance:
- Uses "No Mental Notes!" as a strong warning
- Lists specific trigger phrases (记住, Remember, I prefer, etc.)
- Maps each trigger to the appropriate file to edit
- Explicitly forbids saying "I'll remember" without file edits
This addresses the issue where agents would acknowledge user
preferences verbally but not persist them to profile files.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change `multica dev` default from gateway+console+web to desktop app
- Remove console from dev command options (use embedded Hub in desktop)
- Update package.json scripts to reflect new defaults
- Update README.md and CLAUDE.md architecture documentation
The desktop app has an embedded Hub, so no separate console/gateway is
needed for local development. Gateway is now optional, only needed for
remote client access.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Simplify 4-layer policy to 3-layer:
- Layer 1: Global allow/deny (user config)
- Layer 2: Provider-specific rules
- Layer 3: Subagent restrictions
Removed:
- ToolProfileId type (minimal/coding/web/full)
- TOOL_PROFILES constant
- getProfilePolicy function
- profile field from ToolsConfig
Users can achieve the same effect using allow/deny with group:* syntax.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resolve tools before building subagent system prompt so the
"## Tooling" section is included, matching OpenClaw's pattern.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Only inject workspace.md into system prompt (not soul, user, memory)
- Add profile directory path to workspace section for on-demand file access
- Agent now reads soul.md, user.md, memory.md on first session using tools
- Reduces system prompt size and improves token efficiency
- Aligns with workspace.md template instructions ("Read soul.md first")
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Message.content changed from string to ContentBlock[] — update desktop's
local chat hook to match: extractContentFromAgentEvent returns ContentBlock[],
and history messages are normalized from string to ContentBlock[].
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>
- Add useProvider hook for provider state management
- Add ApiKeyDialog for configuring API key providers
- Add OAuthDialog for importing OAuth credentials from CLI tools
- Update home page with provider dropdown selector
- Support switching providers mid-conversation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add provider.ts with handlers for list, current, set, saveApiKey, importOAuth
- Import OAuth credentials from CLI tools (Claude Code, Codex)
- Register provider handlers in IPC index
- Expose provider API in preload.ts with TypeScript types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add getProviderInfo() and setProvider() methods to Agent class
- Expose provider methods via AsyncAgent
- Add setLlmProviderOAuthToken() for storing OAuth credentials
- Extend ProviderConfig type with OAuth fields (oauthToken, oauthRefreshToken, oauthExpiresAt)
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>
The createExecApprovalCallback was using profileId as the agentId for
approval requests, but agentSenders map is keyed by agent.sessionId.
This caused sendToClient lookups to fail, silently denying all Hub-mode
approvals. Now generates sessionId upfront and passes it separately
from profileId to the callback.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ExecApprovalManager: tracks pending approvals, sends to clients via
Gateway, resolves on RPC response, auto-denies on timeout (fail-closed)
- RPC handler: resolveExecApproval for client decision delivery
- Hub integration: creates approval callback per agent, injects into
AsyncAgent, registers RPC handler, cancels pending on agent close
- Reads/writes exec approval config and allowlist from agent profile
- Test coverage for manager: request/resolve, timeout, cancel, errors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add optional onApprovalNeeded callback to exec tool (backward compatible)
- Thread callback through CreateToolsOptions → AgentOptions → resolveTools
- Add ExecApprovalConfig to ProfileConfig for per-profile configuration
- Create CLI terminal approval callback (readline-based) for non-Hub mode
- Export all exec approval types and functions from tools index
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused AgentEvent import from hub.ts
- Change @typescript-eslint/no-unused-vars to warn instead of error
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add getMessages() to Agent and AsyncAgent for retrieving session history
- Fix type annotations in hub.ts for AgentMessage handling
- Remove duplicate type export in preload.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document the dual-mode chat architecture showing how IPC and Gateway
paths share the same UI components, Store, and Agent layer.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Chat page now supports two modes:
- Local: Direct IPC to agent in same Electron process
- Remote: WebSocket via Gateway to external Hub
Both modes reuse the same UI components and useMessagesStore.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add IPC handlers for direct local agent communication:
- localChat:subscribe - Subscribe to agent events
- localChat:unsubscribe - Unsubscribe from events
- localChat:send - Send message to local agent
- localChat:getHistory - Get message history
This enables chat without Gateway connection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>