Commit graph

70 commits

Author SHA1 Message Date
Jiang Bohan
a8e7a803c9 fix(agent): separate display content from agent user turns 2026-02-09 15:54:10 +08:00
Jiang Bohan
d6993000ca Merge branch 'main' of https://github.com/multica-ai/super-multica
# Conflicts:
#	apps/desktop/electron/electron-env.d.ts
2026-02-09 14:34:48 +08:00
Bohan Jiang
94d6e15117
Merge pull request #101 from multica-ai/fix/credentials-reload
fix(agent): reload credentials dynamically
2026-02-09 14:33:40 +08:00
Jiang Bohan
5716932903 fix(agent): resolve merge conflict in runner.ts for credentials reload
Merge main's run mutex + soft error return with branch's refreshAuthState(),
keeping getApiKey defensive throw as defense-in-depth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:30:57 +08:00
Naiyuan Qing
0895d42d3b Merge remote-tracking branch 'origin/main' into feat/telegram-channel
# Conflicts:
#	apps/desktop/src/hooks/use-local-chat.ts
#	packages/sdk/src/actions/stream.ts
#	packages/ui/src/components/chat-view.tsx
#	src/agent/async-agent.ts
#	src/agent/events.ts
2026-02-09 14:28:06 +08:00
Jiang Bohan
22e225c6a8 refactor(profile): remove Communication Style UI and programmatic API
Style is now solely managed by the agent editing soul.md directly,
removing the need for UI controls, IPC handlers, and typed constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:25:38 +08:00
Bohan Jiang
b7085b2bf5
Merge pull request #107 from multica-ai/fix/new-user-onboarding
fix(desktop): new user onboarding — show errors and configure dialog in Chat
2026-02-09 13:54:48 +08:00
Jiang Bohan
8d32a06b5c fix(agent): validate API key before calling PiAgentCore.prompt()
getApiKey errors thrown inside PiAgentCore's internal async context
result in UnhandledPromiseRejection instead of propagating to the
caller. Return a graceful error early so AsyncAgent can emit it
through the subscriber mechanism to the UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:51:45 +08:00
Naiyuan Qing
23905daaa1 Merge remote-tracking branch 'origin/main' into feat/telegram-channel
# Conflicts:
#	apps/desktop/electron/electron-env.d.ts
#	apps/desktop/electron/ipc/index.ts
#	apps/desktop/electron/preload.ts
#	apps/desktop/src/App.tsx
#	apps/desktop/src/pages/layout.tsx
#	src/agent/async-agent.ts
#	src/agent/runner.ts
#	src/hub/hub.ts
2026-02-09 13:44:08 +08:00
Naiyuan Qing
23da5a35ff feat(channels): route media messages through agent
Add writeWithImages() to AsyncAgent for passing images directly to
the LLM via ImageContent. Extend Agent.run() to accept optional
images parameter. Update ChannelManager.routeIncoming() to download
media files and forward them: images as ImageContent to the LLM,
audio/video/document as file paths for agent-driven processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:44:39 +08:00
Jiang Bohan
9fe6b920c4 fix(agent): always provide getApiKey callback and emit error events
PiAgentCore was created with an empty object when no API key was
initially configured. This broke dynamic provider switching because
setProvider() updated currentApiKey but PiAgentCore had no getApiKey
callback to read it. Always provide the callback so it dynamically
reads the current key.

Also adds AgentErrorEvent to MulticaEvent and emits it from
AsyncAgent.write() catch handlers so errors flow through the
subscriber mechanism to IPC listeners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:23:44 +08:00
yushen
a7db286530 Merge remote-tracking branch 'origin/main' into sessions-spawn-parent-trigger 2026-02-06 19:44:17 +08:00
yushen
a3acd732e0 fix(subagent): persist LLM summary after internal announce to parent context
After child subagents complete, the coalesced announcement runs as an
internal turn which rolls back all messages from the parent's in-memory
context. This causes the parent LLM to lose findings in subsequent turns.

Add persistResponse option to writeInternal that re-injects the LLM's
summary as a non-internal assistant message after the internal run
completes. The internal prompt stays hidden while the summary persists
in both memory and session JSONL for future turns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 19:38:18 +08:00
Naiyuan Qing
4a503ecf4c feat(agent): add AgentErrorEvent for error propagation via subscribe
Add agent_error event type to MulticaEvent union so errors from
agent runs reach subscribe() consumers (Desktop IPC + Channel).
Make emitMulticaEvent public on Runner so AsyncAgent can emit errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 19:35:32 +08:00
yushen
d392238be3 feat(subagent): tag internal orchestration messages and filter from user-facing history
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>
2026-02-06 18:34:11 +08:00
Jiang Bohan
ffa5355a95 feat(heartbeat): add heartbeat core runner and profile integration 2026-02-06 16:41:56 +08:00
Jiang Bohan
24df8b02e7 fix(agent): reload credentials dynamically 2026-02-06 15:48:29 +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
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
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
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
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
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
Jiang Bohan
d7eb0da49b feat(agent): add provider switching and OAuth credential support
- 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>
2026-02-04 18:25:06 +08:00
Jiang Bohan
a098a78942 fix(agent): add getMessages method and fix TypeScript errors
- 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>
2026-02-04 16:51:07 +08:00
LinYushen
c70dd338c2
Merge pull request #84 from multica-ai/reasoning-mode-support
feat(agent): add reasoning mode for thinking content display
2026-02-04 16:15:20 +08:00
yushen
f649792118 fix(agent): allow output reinit 2026-02-04 16:09:36 +08:00
yushen
8fe2b5f010 fix(agent): resolve reasoningMode from profile config and session meta
- 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>
2026-02-04 16:00:53 +08:00
yushen
f585882a06 fix(agent): keep skills prompt without profile 2026-02-04 15:57:06 +08:00
yushen
953a29672a feat(agent): implement reasoning mode in engine and CLI
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>
2026-02-04 15:50:47 +08:00
yushen
f733f0bcc3 fix(agent): fix system prompt reload and tool casing issues
- 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>
2026-02-04 15:46:18 +08:00
yushen
8333615fb4 refactor(agent): integrate system prompt builder into profile, runner, and subagent
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>
2026-02-04 15:41:22 +08:00
Jiang Bohan
4df39448be feat(agent): add communication style setting to profile
- 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>
2026-02-04 14:42:23 +08:00
Jiang Bohan
fa03ae6e62 feat(agent): add soul.md support for agent identity
- 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>
2026-02-04 13:26:40 +08:00
Jiang Bohan
a80c858ce5 feat(agent): add profile name and user content management
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>
2026-02-04 13:26:40 +08:00
Jiang Bohan
4112d4511e fix(agent): resolve TypeScript errors with exactOptionalPropertyTypes
- Fix originalToolsConfig assignment to handle undefined properly
- Fix devNull type cast for WritableStream compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 18:58:45 +08:00
Jiang Bohan
dafbf856ac merge: resolve conflicts with main branch
- 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>
2026-02-03 18:57:05 +08:00
Jiang Bohan
6f6a4f82e4 feat(agent): add tools and skills management methods
- 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>
2026-02-03 18:25:43 +08:00
yushen
9ed4cdf2b0 fix(agent): handle auth profile failures and provider mismatch 2026-02-03 18:23:18 +08:00
yushen
4b10420324 Merge remote-tracking branch 'origin/main' into auth-profile-rotation
# Conflicts:
#	pnpm-lock.yaml
#	src/agent/runner.ts
2026-02-03 17:58:50 +08:00
yushen
e8815dbb97 fix(agent): loop-based rotation, timeout rotatable, error classification tests
- 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>
2026-02-03 17:26:16 +08:00
yushen
0c549f76e6 feat(session): integrate write lock and auto-repair into session flow
- storage.ts: acquire/release write lock in appendEntry and writeEntries
- session-manager.ts: add repairIfNeeded() method, apply transcript
  sanitization in loadMessages()
- runner.ts: move message loading to lazy async init in run(), call
  repair before loading messages (following OpenClaw's pattern)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:21:26 +08:00
yushen
dcc336f2d0 feat(agent): integrate auth profile rotation into runner and resolver
Extend credentials.ts with llm.order and listProfileIdsForProvider.
Add resolveApiKeyForProfile and resolveApiKeyForProvider to resolver.
Modify runner to support dynamic API key swapping and automatic
rotation on auth/rate_limit/billing errors with retry.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 16:56:04 +08:00
yushen
37ec8ff5e0 fix(streaming): use per-message stream ids and oauth resolver 2026-02-03 14:51:19 +08:00
yushen
273e49f678 feat(agent): stream raw AgentEvent from engine to Hub via Gateway
Expose PiAgentCore events through the full backend pipeline:
- Agent.subscribe() transparently forwards engine events
- AsyncAgent pushes all AgentEvent into Channel alongside error Messages
- Hub discriminates ChannelItem and forwards events via StreamAction

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:22:49 +08:00
Naiyuan Qing
7122b12ad5
Merge pull request #61 from multica-ai/feat/text-stream
feat: streaming AI message rendering
2026-02-02 17:45:30 +08:00
Jiang Bohan
6723aa8561 refactor(providers): extract provider management to dedicated module
- Create src/agent/providers/ with registry.ts and resolver.ts
- registry.ts: Provider metadata, status checking, login instructions
- resolver.ts: API key resolution, model resolution
- oauth/providers.ts now re-exports from providers/ (deprecated)
- tools.ts: Remove PROVIDER_ALIAS and DEFAULT_MODELS (moved to providers/)
- Update imports in runner.ts and chat.ts

This separates concerns:
- oauth/ only handles OAuth credential reading
- providers/ manages all provider metadata and resolution
2026-02-02 17:20:02 +08:00
Naiyuan Qing
d04bed8175 feat(agent): add streaming support for AI message generation
AsyncAgent now subscribes to pi-agent-core events (message_start,
message_update, message_end) and forwards incremental text deltas
through a stream callback. Hub registers the callback and sends
stream payloads to the requesting client via Gateway.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 17:18:10 +08:00