- 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>
Add createCronTool to the agent's default tools list so agents
can use the cron tool to manage scheduled tasks.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Initialize CronService on Hub startup
- Set executeCronJob as the job executor
- Shutdown CronService on Hub cleanup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- cron-job-tool.md: Implementation plan for scheduled task system
- auto-memory-refresh.md: Design for pre-compaction memory flush (future)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds CLI support for managing scheduled tasks:
- multica cron status: Show service status
- multica cron list: List all scheduled jobs
- multica cron add: Create new jobs with --at/--every/--cron
- multica cron run: Execute jobs immediately
- multica cron enable/disable: Toggle job status
- multica cron remove: Delete jobs
- multica cron logs: View run history
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a cron tool that allows agents to create, manage,
and execute scheduled tasks. Supports:
- status: Get service status
- list: List all jobs with optional filters
- add: Create one-shot, interval, or cron jobs
- update: Modify existing jobs
- remove: Delete jobs
- run: Execute jobs immediately
- logs: View run history
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements a timer-based cron job system:
- types.ts: Job types (at, every, cron schedules)
- schedule.ts: Next run computation using croner
- store.ts: Persistent JSON storage with JSONL run logs
- service.ts: CronService with timer management
- execute.ts: Job execution (system-event, agent-turn)
Based on OpenClaw's implementation (MIT License).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The closing fence regex was not checking for an empty info string,
allowing e.g. ```python to incorrectly close an open fence. Also
adds missing test for tool_execution_update passthrough.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Message aggregation should not be managed by the Hub. The aggregator
is a standalone utility for client adapters to use internally — the
Hub stays clean and only does event forwarding. Third-party messaging
integrations (Discord, Telegram) will use MessageAggregator in their
own adapter layer.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
Add enableAggregation/disableAggregation methods to Hub for per-agent
aggregation control. When enabled, streaming text deltas are buffered
and emitted as block-reply events instead of raw stream events. The
existing streaming mode remains the default for own clients.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BlockChunker splits streaming text at natural boundaries (paragraph,
newline, sentence, word) with configurable min/max chars and markdown
fence-safe splitting. MessageAggregator consumes AgentEvents, buffers
text deltas, and emits complete BlockReply objects via callbacks.
Enables future third-party messaging integrations (Discord, Telegram)
that cannot consume raw streaming deltas.
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>
Sign each search request with HMAC-SHA256 using Hub ID, a per-request
nonce (UUIDv7), and unix timestamp. The signed reqId is sent in the
request body to prevent unauthorized API usage.
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>
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>
Add localApprovalHandlers map so exec approval requests can be routed
to the Electron renderer via IPC instead of requiring a Gateway
connection. Expose setLocalApprovalHandler/removeLocalApprovalHandler
and resolveExecApproval on Hub for the IPC layer to use.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rename CopilotHub references to Devv Search (constants, types, function
names, error message). Remove unused resolveTimeoutSeconds/resolveCacheTtlMs
imports and use constants directly.
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>
Remove Brave and Perplexity providers in favor of a single CopilotHub
search endpoint (api-dev.copilothub.ai/web-search). Simplifies schema
to query-only, removes credential dependencies, retains caching.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
- Keep needsCompaction() method from compaction-feedback PR
- Keep tool result pruning from this branch
- Add "pruning" to CompactionEndEvent.reason type in events.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adding explicit return type to help TypeScript resolve the type
correctly across different build configurations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The desktop app imports from the root src directory, but turbo wasn't
tracking those files for cache hash calculation. This caused CI builds
to use stale hashes when types changed in the root src directory.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The CompactionResult type includes "pruning" as a reason but SessionEntry
did not, causing type errors when writing compaction entries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two-phase pruning approach based on OpenClaw's microcompact-style:
- Soft Trim (30% utilization): Keep head 1500 + tail 1500 chars of large tool results
- Hard Clear (50% utilization): Replace old tool results with placeholder
Protections:
- Never prunes before first real user message (bootstrap protection)
- Protects last 3 assistant messages and their corresponding tool results
- Skips image-containing tool results
- Respects tool allow/deny lists
Enabled by default in tokens/summary compaction modes.
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>
Document how clients receive real-time events via WebSocket and IPC,
covering message lifecycle, tool execution, and compaction events with
full data structure definitions and routing pseudocode.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>