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>
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>
- 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>
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>
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>
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>
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>
- 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>
- 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>
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>
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 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>
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>
- Add 60s timeout to pending device confirms to prevent Promise leaks
when client disconnects before user responds
- Add offDeviceConfirmRequest to preload and clean up IPC listener on
component unmount to prevent duplicate listener accumulation
- Extract duplicated parseUserAgent into shared lib/parse-user-agent.ts
- Clean up expired tokens in DeviceStore.registerToken to prevent
memory accumulation from unscanned QR codes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When an unverified device sends a non-RPC message, reply with an
"error" action containing UNAUTHORIZED code so the client knows
verification is required.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend DeviceEntry with optional DeviceMeta field. Verify handler
extracts meta from params and passes it through to onConfirmDevice
callback and deviceStore.allowDevice for persistence.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add DeviceStore for managing one-time tokens and persistent device
whitelist. Create async verify RPC handler that validates tokens and
awaits Desktop user confirmation for first-time connections. Whitelisted
devices (reconnections) pass through instantly. Add message guard to
reject unverified device traffic.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add getSoulContent/updateSoulContent to ProfileManager
- Create soul.md with agent name when updating profile name
- Add reloadSystemPrompt() method to Agent and AsyncAgent
- Include soul.md content in system prompt build
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add methods to ProfileManager, Agent, and AsyncAgent for:
- Getting/setting agent display name (stored in config.json)
- Getting/setting user.md content
This enables the desktop app to manage agent settings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Disable noUnusedLocals/noUnusedParameters in desktop tsconfig
(external src/ files have unused imports that fail strict checking)
- Add TSchema constraint to wrapTool generic to satisfy AgentTool type
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix originalToolsConfig assignment to handle undefined properly
- Fix devNull type cast for WritableStream compatibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Merge auth-profiles feature from main into runner.ts
- Merge closeCallbacks feature from main into async-agent.ts
- Regenerate pnpm-lock.yaml with new dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add profileId option to createAgent() method
- Default to "default" profile for all agents
- Ensures every agent has an associated profile for memory and config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add setToolStatus() to persist tool enable/disable to profile config
- Add getActiveTools() and reloadTools() methods
- Add getSkillsWithStatus(), getEligibleSkills(), reloadSkills() methods
- Add updateToolsConfig() and setToolEnabled() to ProfileManager
- Ensure profile directory is created on Agent initialization
- Fix reloadTools() to re-read profile config for latest changes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Unify locking strategy across the project by using a custom synchronous
file lock (exclusive-create based, with PID stale detection) instead of
the proper-lockfile dependency, matching the pattern in session-write-lock.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace single-retry with while loop that exhausts all candidate profiles
- Add "format" detection to classifyError (400, malformed, bad request, schema)
- Make timeout errors rotatable (some providers hang on rate limit)
- Export classifyError and isRotatableError for testing
- Add error-classification.test.ts with coverage for all failure reasons
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add proper-lockfile for concurrent-safe store updates with fallback
- Add "format" to AuthProfileFailureReason
- Two-level round-robin sort: credential type priority (OAuth > API key), then lastUsed
- Filter out profiles with missing/invalid credentials from candidates
- Add preferredProfile option to resolveAuthProfileOrder
- Export coerceStore and ensureAuthStoreFile for testing
- Add store.test.ts with coerceStore, load/save round-trip, corruption handling
- Update order.test.ts mocks for resolver and registry dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>