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>
- 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>
Add AsyncAgent.subscribe() that allows multiple subscribers to receive
the same agent events, enabling local IPC chat to coexist with other
event consumers.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Read reasoningMode from profile config and storedMeta when not
explicitly set via options (matching thinkingLevel pattern)
- Skip extractThinking() call when reasoningMode is "off"
- Clean up redundant ?? undefined casts in CLI entry points
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wire reasoningMode through Agent runner, CLI output, and all CLI
entry points (run, interactive, non-interactive). In stream mode,
thinking content is printed to stderr in real-time. In on mode,
thinking is shown after message completion. Default is stream.
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 ReasoningMode type (off/on/stream) to AgentOptions and related
config types. Add extractThinking() for extracting thinking content
blocks from LLM responses, mirroring the existing extractText() pattern.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- reloadSystemPrompt() now updates both agent and session prompt
- reloadSystemPrompt() uses buildSkillsPrompt() consistently (not buildModelSkillsPrompt)
- Extract rebuildSystemPrompt() to eliminate duplicated builder logic
- Preserve original tool name casing in tooling summary (matching OpenClaw)
- Fix report line count to use actual newline count
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
ProfileManager.buildSystemPrompt() now delegates to the structured builder.
Runner assembles prompt after tool resolution with safety, tooling summary,
and runtime info. Subagent prompts use minimal mode with safety constitution.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Structured, mode-aware system prompt builder inspired by OpenClaw.
Supports three modes (full/minimal/none), safety constitution,
conditional tool sections, runtime info, and prompt telemetry.
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>
- Add IPC handlers for profile:getStyle and profile:setStyle
- Add style dropdown with 6 preset options in agent settings UI
- Display style descriptions to help users choose
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AgentStyle type with 6 preset options (professional, friendly, etc.)
- Add getStyle/setStyle methods to ProfileManager
- Update soul.md template to include style section
- Add reloadSystemPrompt support for style changes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename storage path from ~/.super-multica/devices/ to
~/.super-multica/client-devices/ for clarity
- Change JSON format from bare array to { version, devices[] }
dict for future extensibility
- Auto-migrate legacy array format on load
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Radix UI version in this project doesn't support asChild on
AlertDialogDescription. Use inline spans with block display instead.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The import was redundant since it's already re-exported directly.
Fixes eslint no-unused-vars error in CI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Merge remote main branch, keeping both device verification (ws-auth-handshake)
and agent settings/profile features from main.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>