Commit graph

256 commits

Author SHA1 Message Date
yushen
6ecdbc5783 fix(exec-approval): treat expiresAtMs=-1 as non-expiring 2026-02-06 18:09:01 +08:00
yushen
a36cbac3fd feat(exec-approval): default to full/off security and support no-timeout
- Change default security from "allowlist" to "full" (allow all commands)
- Change default ask from "on-miss" to "off" (never prompt)
- Change DEFAULT_APPROVAL_TIMEOUT_MS from 60s to -1 (no timeout)
- Support timeoutMs=-1 to wait indefinitely for user decision
- Update CLI and Hub approval flows to skip setTimeout when timeout<0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 18:02:07 +08:00
Jiang Bohan
b5eca456ab fix(cron): clarify agent-turn can use all tools in description
The model was confused about payload capabilities. Explicitly state
that agent-turn spawns an isolated agent with ALL tools (exec, write,
web_fetch, etc.) and add usage guidance for choosing between
system-event and agent-turn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:31:29 +08:00
Jiang Bohan
2129feaf3a fix(cron): replace require("fs") with ESM renameSync import
The bundled ESM environment does not support require(). Use the
already-imported fs functions instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:21:12 +08:00
Jiang Bohan
9bd18472b8 refactor(cron): align tool schema with OpenClaw patterns
- Replace Type.Union(Type.Literal(...)) with flat stringEnum helper
  to avoid anyOf in JSON schema (better LLM provider compatibility)
- Rewrite description to structured ACTIONS/SCHEDULE/PAYLOAD/CONSTRAINTS
  format without markdown headers or code blocks
- Add CRITICAL CONSTRAINTS section for sessionTarget+payload rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:08:15 +08:00
Jiang Bohan
ab85a01431 fix(cron): improve action parameter description for better model understanding
- Add explicit list of valid actions in parameter description
- Add IMPORTANT note at start of tool description

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 14:59:35 +08:00
Jiang Bohan
ea7a2c837b feat(desktop): add cron tool to Tools page UI
- 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>
2026-02-06 14:47:54 +08:00
Jiang Bohan
321b48a1cf feat(agent): register cron tool in agent toolset
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>
2026-02-06 14:47:54 +08:00
Jiang Bohan
b82409c7c0 feat(hub): integrate cron service lifecycle
- 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>
2026-02-06 14:47:54 +08:00
Jiang Bohan
f15a28e14a feat(cli): add multica cron commands
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>
2026-02-06 14:47:31 +08:00
Jiang Bohan
9d0cc6fdf6 feat(tools): add cron tool for agent job management
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>
2026-02-06 14:47:31 +08:00
Jiang Bohan
de355cace3 feat(cron): add cron job scheduling module
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>
2026-02-06 14:47:31 +08:00
yushen
f6fab68873 fix(hub): include message_end text in aggregator 2026-02-06 12:44:15 +08:00
yushen
1e71341997 Merge remote-tracking branch 'origin/main' into message-aggregation 2026-02-06 11:55:58 +08:00
yushen
ec6dbff61c Merge remote-tracking branch 'origin/main' into copilothub-web-search 2026-02-06 11:43:40 +08:00
yushen
efb1495326 fix(hub): fence close must reject info strings per CommonMark spec
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>
2026-02-05 22:23:55 +08:00
yushen
fc0c1781c3 refactor(hub): remove Hub-level aggregation integration
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>
2026-02-05 20:24:25 +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
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
yushen
c03a60753e feat(hub): integrate message aggregator into Hub event pipeline
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>
2026-02-05 18:28:14 +08:00
yushen
1859e32a30 feat(hub): add message aggregation module for block-level reply chunking
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>
2026-02-05 18:28:08 +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
yushen
3817e2d8a2 feat(tools): add HMAC-SHA256 request signing to web search
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>
2026-02-05 17:50:57 +08:00
Naiyuan Qing
874de766ec feat(hub): add local exec approval routing for desktop IPC chat
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>
2026-02-05 17:50:41 +08:00
yushen
51ba86a8eb refactor(tools): clean up web search naming and remove dead code
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>
2026-02-05 17:31:53 +08:00
yushen
630f06eddb chore(tools): update web_search descriptions to Devv Search
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>
2026-02-05 17:21:39 +08:00
yushen
8ea2f2a1bf feat(tools): replace web search with CopilotHub API
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>
2026-02-05 17:19:12 +08:00
Jiang Bohan
6a21a51766 merge: resolve conflict with main branch
- 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>
2026-02-05 16:26:11 +08:00
Jiang Bohan
71e44bebc0 fix(session): add explicit return type to maybeCompact method
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>
2026-02-05 16:20:40 +08:00
Jiang Bohan
d2827ae948 fix(session): add pruning to SessionEntry reason type
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>
2026-02-05 16:01:10 +08:00
Jiang Bohan
a7f1c56e09 fix(profile): resolve exactOptionalPropertyTypes type error in updateStyle 2026-02-05 15:55:30 +08:00
Jiang Bohan
3dd7ff52d6 feat(context): add tool result pruning for smarter context management
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.
2026-02-05 15:50:58 +08:00
LinYushen
6ebd610ffe
Merge pull request #94 from multica-ai/compaction-feedback
feat: add compaction feedback events
2026-02-05 15:41:38 +08:00
yushen
f0a359ba64 fix(agent): emit compaction events only when compacted 2026-02-05 15:32:41 +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
Jiang Bohan
79e1dc0acd refactor(prompt): update profile and memory sections
- Profile: add usage hint for base path
- Memory Recall: clear step-by-step instructions for search and update

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:14:09 +08:00
Jiang Bohan
7547e563fc refactor(prompt): simplify profile and memory sections
- Profile: concise directory path with single usage hint
- Memory Recall: one-liner instruction matching OpenClaw style

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:05:54 +08:00
Jiang Bohan
6bfe836559 feat(tools): add keyword-based memory_search tool
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>
2026-02-05 14:57:40 +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
yushen
70f3755552 feat(agent): emit compaction lifecycle events
Introduce MulticaEvent type system parallel to pi-agent-core's AgentEvent,
with compaction_start and compaction_end events. Agent.subscribeAll() merges
both event streams. maybeCompact() now emits events bracketing the compaction
work, gated by a new SessionManager.needsCompaction() pre-check.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:54:52 +08:00
Jiang Bohan
1e1fa410c3 refactor(tools): remove KV memory tools in favor of file-based memory
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>
2026-02-05 14:41:23 +08:00
yushen
e13bc30841 fix(agent): propagate session write errors 2026-02-05 14:24:24 +08:00
yushen
e23462dc72 fix(session): add error logging and remove TOCTOU in ensureSessionDir
- Log storage write errors in SessionManager.enqueue() instead of
  silently swallowing them
- Remove existsSync check in ensureSessionDir to avoid TOCTOU race;
  mkdirSync with recursive is idempotent when dir already exists

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:18:41 +08:00
yushen
2ec25cb32a fix(session): flush session writes before subagent result delivery
The subagent announce flow reads the child's session file to extract
results, but saveMessage() is fire-and-forget (void enqueue). When
agent.run() completes, pending appendEntry writes may not have flushed
to disk yet, causing readLatestAssistantReply() to find no entries.

Add SessionManager.flush() and call it in AsyncAgent.write() after
agent.run() returns. This ensures waitForIdle() won't resolve until
all session data is on disk, fixing the "no return content" issue.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:12:53 +08:00
yushen
3039e3b34c fix(session): handle transient ENOENT in session directory creation
The mkdir call with { recursive: true } can throw ENOENT on macOS APFS
due to filesystem race conditions. This caused SpawnSession child agents
to silently fail — the session data was never written, so the parent
read back empty results ("子任务完成了,但没有返回内容").

Two fixes:
- Add retry logic to mkdir calls in acquireSessionWriteLock and
  ensureSessionDir to handle transient ENOENT
- Add .catch() to SessionManager.enqueue() to prevent unhandled
  promise rejections from fire-and-forget storage operations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 13:52:47 +08:00
Naiyuan Qing
3c303df8f1 Merge branch 'main' into exec-approvals 2026-02-05 11:06:52 +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
Jiang Bohan
cdc64f9c83 feat(profile): enforce file-based memory with stronger guidance
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>
2026-02-05 03:32:53 +08:00
Jiang Bohan
a3e639f8f5 refactor(cli): make desktop app the default dev target
- 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>
2026-02-05 03:14:44 +08:00
Jiang Bohan
087d1a8653 refactor(tools): remove tool profile layer from policy system
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>
2026-02-05 02:58:15 +08:00