grammy was not in rollupOptions.external, causing the channels IPC
handlers to fail at runtime with 'No handler registered'.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
- Fix double useChannels() instantiation: call once in ChannelsPage,
pass as props to TelegramCard
- Mask bot tokens in channels:getConfig before sending to renderer
- Add input validation (isValidId, token length) on all IPC handlers
- Fix stopAccount() to clean up typingTimer, lastRoute, aggregator,
and debouncer when stopping the account they belong to
- Add try/catch to stopChannel/startChannel in useChannels hook
- Consistent return type { ok, error? } on channels:stop handler
- Add tooltip hint on disabled Remove button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add IPC handlers, preload API, useChannels hook, and Channels page UI.
Users can save/remove Telegram bot tokens and start/stop bots directly
from the desktop app with immediate effect and persistence across restarts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use setWindowOpenHandler to intercept window.open calls and route
them through shell.openExternal instead of navigating inside Electron.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Forward agent_error as passthrough event to renderer. Add
clearLastRoute() calls in hub:sendMessage and localChat:send
handlers so channel replies stop when desktop sends a message.
Handle agent_error in use-local-chat to show error UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Announcement messages from subagent completion flows were persisted as
regular user messages, polluting conversation history and leaking
orchestration instructions to frontend/CLI.
- Add `internal?: boolean` to SessionEntry message variant
- SessionManager.saveMessage accepts { internal: true } option
- SessionManager.loadMessages filters internal by default
- Agent.runInternal() tags messages as internal, rolls back from memory
- Agent.withRunMutex() prevents concurrent run/runInternal mis-tagging
- AsyncAgent.writeInternal() suppresses event forwarding during internal runs
- Announce flows use writeInternal() instead of write()
- Desktop IPC getHistory reads from session storage (filtered)
- CLI session show parses SessionEntry, supports --show-internal flag
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Cron" tab to navigation bar with Time04Icon
- Add /crons route with CronsPage component
- Add cron IPC handlers (list, toggle, remove) in electron/ipc/cron.ts
- Expose cron API in preload.ts for renderer process
- Add useCronJobs hook for fetching and managing jobs
- Add CronJobList component with status badges, toggle switches,
delete buttons, relative time display, and empty state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 'group:cron' to TOOL_GROUPS in IPC handler and agent tools
- Add cron tool description and group name to use-tools hook
- Add Time04Icon for cron group in tool-list component
- Add subagent group icon (UserMultipleIcon) for completeness
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Use chatRef to hold stable reference to useChat return value, avoiding
stale closure issues and satisfying react-hooks/exhaustive-deps rule.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add LocalChat component using useLocalChat hook that communicates with
the Hub via IPC (no Gateway required). Fix streamId extraction to use
event.message.id matching Hub behavior. Fix history to return raw
AgentMessageItem[] instead of flattened strings. Add exec approval
forwarding over IPC. Use conditional rendering for LocalChat to prevent
event leaking from remote sessions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove "requires API key" wording and rebrand to Devv Search across
tool definition, desktop UI, system prompt, and README.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace Zustand-based message/connection stores with local state hooks.
useLocalChat now returns UseChatReturn shape with internal agentId discovery,
tool execution events, and error handling. Remote mode uses shared
useGatewayConnection + useChat + DevicePairing from packages.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Widen the LocalChatEvent.event.type union to include compaction_start
and compaction_end, fixing TS2367 comparison errors in use-local-chat.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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>
- 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>
Implements a simple memory_search tool for searching memory files:
- Searches memory.md and memory/*.md files by keyword
- Returns matching lines with context (2 lines before/after)
- Supports case-sensitive/insensitive search
- Respects maxResults limit
Tool is only available when a profile is active (has profileDir).
System prompt includes memory usage guidance when tool is present.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
The original icon PNGs had pre-baked rounded corners and transparent
backgrounds. When iOS applies its own superellipse mask on Add to Home
Screen, the transparent corners were filled with white, creating a
visible white border around the icon.
Changes:
- Regenerate all icon PNGs as full-bleed (no rounded corners, opaque
background filling entire canvas) from the existing logo SVG
- Add dedicated apple-touch-icon.png at 180x180 (iOS standard size)
- Add favicon.ico for universal browser fallback
- Replace oversized app/icon.png (540x540) with 192x192 full-bleed
- Update layout.tsx to reference apple-touch-icon.png
- Add explicit purpose: "any" to 192x192 manifest entry
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace Zustand global stores with hook-local state for the web app.
useGatewayConnection handles client lifecycle, identity persistence,
reconnection, and keyed reset. useChat handles message history, streaming
events, tool execution, Hub error action, and exec approval requests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Memory is now managed through profile files (memory.md, memory/*.md) using
standard read/edit tools, following OpenClaw's file-first approach.
Changes:
- Remove memory/ folder with KV-based memory tools
- Remove group:memory from tool groups
- Update system prompt to remove memory tool references
- Update README docs to reflect file-based memory approach
Agents use workspace.md instructions to manage memory via file operations.
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>
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>
- 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>
- 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>