Commit graph

60 commits

Author SHA1 Message Date
Jiayuan Zhang
01b89ed488 feat(channels): add message source prefix, sendFile, and channel info
Prepend [ChannelName · private/group] prefix to debounced messages so
the LLM knows the message source. Add sendFile() for outbound media
routing and listChannelInfos() for system prompt channel awareness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:37:22 +08:00
Jiayuan Zhang
a29366d7cf feat(agent): add send_file tool for channel file sending
New send_file tool with TypeBox schema, auto-detect media type from
file extension, and file validation. Wired through AgentOptions and
resolveTools with conditional registration when callback is provided.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:37:15 +08:00
Jiayuan Zhang
6e96fd1306 feat(channels): add outbound media types and sendMedia to channel adapter
Add OutboundMedia interface and OutboundMediaType to the channel type
system. Implement sendMedia in the Telegram plugin using grammy's
InputFile API with HTML caption formatting and plain-text fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:37:08 +08:00
Jiayuan Zhang
ade7f2b056
feat(agent): add search-then-fetch guidance to web tools system prompt (#150)
Guide the LLM to evaluate snippet quality after web_search and
follow up with web_fetch on the most relevant URLs when deeper
content is needed for accurate answers.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:50:13 +08:00
Bohan Jiang
a986456e72
Merge pull request #148 from multica-ai/refactor/workspace-documents
refactor(agent): move workspace to ~/Documents/Multica
2026-02-12 23:08:57 +08:00
Bohan Jiang
d385915f95
Merge pull request #149 from multica-ai/feat/default-agent-name-multica
feat(profile): default new agent name to "Multica"
2026-02-12 23:08:33 +08:00
Jiang Bohan
02b480dc47 feat(profile): default new agent name to "Multica" instead of "Assistant"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:48:40 +08:00
Jiang Bohan
cb2dfd246f refactor(agent): move workspace to ~/Documents/Multica
Hidden directory ~/.super-multica is not user-friendly for a working
directory. Move default workspace base to ~/Documents/Multica so
users can easily find agent-created files in Finder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:04:20 +08:00
Naiyuan Qing
71433fc4ed
Merge pull request #146 from multica-ai/feat/message-source-tracking
feat(core): add message source tracking and persistence
2026-02-12 18:51:23 +08:00
Naiyuan Qing
8199dde1b6 feat(core): add message source tracking and persistence
- Add MessageSource type to track where user messages originate (local, gateway, channel)
- Broadcast inbound messages from all channels to local Desktop UI via Hub.onInboundMessage()
- Persist source field in JSONL session storage so it survives page refresh
- Display source icon (Monitor/Smartphone/Send) with tooltip for non-local user messages

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 18:45:15 +08:00
Jiang Bohan
18ab2da8c4 test(agent): add workspace resolution and initialization tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:08:04 +08:00
Jiang Bohan
ca0b4624fd feat(agent): add default workspace directory per profile
Each agent profile now gets a dedicated workspace directory
(~/.super-multica/workspace/{profileId}) used as the default CWD
for tool operations. Supports override via MULTICA_WORKSPACE_DIR
env var or config.json workspaceDir field. The workspace path is
injected into system prompt and runtime info.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:07:59 +08:00
Jiang Bohan
5b0cbd4d7d feat(subagent): increase default timeout from 10 min to 30 min
A subagent that times out loses all its work, so a generous default
reduces wasted compute. Update the constant, tool description, system
prompt guidelines, and documentation to reflect the new 1800s default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:54:53 +08:00
Bohan Jiang
28c83af0d3
Merge pull request #141 from multica-ai/fix/writeinternal-retry
fix(agent): retry writeInternal once on transient LLM errors
2026-02-12 17:31:03 +08:00
LinYushen
8bc36a9cc9
Merge pull request #136 from multica-ai/fix/agent-compaction
fix(agent): prevent context window overflow with 3-layer compaction defense
2026-02-12 17:23:12 +08:00
Jiang Bohan
d627fb4ff4 fix(agent): retry writeInternal once on transient LLM errors
When writeInternal fails with a transient error (e.g. undici
"terminated", connection reset, 502/503), wait 5 seconds and retry
once. This prevents continuation tasks from silently failing when
the LLM streaming connection is interrupted mid-generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:19:52 +08:00
Jiang Bohan
6b9e5d5f40 refactor(subagent): improve system prompt with anti-hallucination and group guidance
Rewrite Sub-Agents section: add explicit anti-hallucination rules (never
fabricate completion status), make group/next the recommended pattern for
multi-agent tasks, remove sessions_list polling restrictions, add usage
examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:11:38 +08:00
Jiang Bohan
48185c3e77 feat(subagent): add groupId/next params to spawn and group display to list
sessions_spawn now accepts groupId and next parameters. First spawn with
next auto-creates a group; subsequent spawns join via groupId. sessions_list
groups runs by groupId with completion progress display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:11:31 +08:00
Jiang Bohan
f46c00e902 feat(subagent): update announce flow for group continuation
When all runs in a group complete, deliver combined findings plus the
`next` continuation prompt to the parent agent via writeInternal. The
parent can then act on the collected data (summarize, write files, etc).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:11:25 +08:00
Jiang Bohan
12fb12b895 feat(subagent): add SubagentGroup type and registry support
Add SubagentGroup for "collect all, then act" workflows where multiple
subagents complete before a continuation task runs. Groups are persisted
alongside run records and support a `next` continuation prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:11:20 +08:00
yushen
9c52dc0124 fix(agent): 3-layer defense against context window overflow
Root cause: compaction only ran after successful assistant response,
never before calling the LLM API. Internal runs skipped it entirely,
and the fire-and-forget void swallowed errors with race conditions.

Layer 1 - Pre-flight compaction in transformContext:
  Runs before EVERY LLM call (including internal runs). Prunes tool
  results and drops oldest messages when utilization exceeds 80%.
  Pure in-memory, no disk writes.

Layer 2 - Fix post-hoc compaction:
  Store compaction promise instead of voiding it, await at _run() start
  to prevent race conditions. Emit compaction_start before the actual
  compaction. Remove useless catch-rethrow block.

Layer 3 - Context overflow recovery:
  Catch context overflow 400 errors → auto-compact → retry (up to 2
  attempts). Runs before classifyError/auth rotation so overflow errors
  are handled by compaction, not profile switching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:52:19 +08:00
Jiang Bohan
ece00cac2b merge: resolve conflict with main (lucide-react migration)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:47:00 +08:00
yushen
b13d40c6da fix(agent): increase token estimation safety margins
- ESTIMATION_SAFETY_MARGIN: 1.2 → 1.5 (50% buffer covers CJK text)
- estimateSystemPromptTokens: /3 → /2 (conservative for mixed content)

This makes the 80% compaction trigger fire earlier, reducing the gap
between estimated and actual token counts that caused overflow errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:43:43 +08:00
yushen
f8ca4ca95e feat(agent): add isContextOverflowError utility
Detects context overflow 400 errors from various LLM providers
(prompt too long, context length exceeded, request too large, etc.)
for use in auto-compaction recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:43:33 +08:00
Jiang Bohan
a33df19bef feat(desktop): add chat abort support with partial content preservation
Add stop button to interrupt agent generation mid-stream. The send button
toggles to a stop icon during loading. Abort propagates from UI through
IPC to the Agent layer (PiAgentCore.abort()), preserving all partial
content in the agent's context so users can follow up immediately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:33:06 +08:00
Naiyuan Qing
b310b57ce9 merge: integrate origin/main into feat/onboarding-check
Resolved conflicts:
- Keep Lucide icons (replaced Hugeicons) in desktop and ui
- Keep new Sidebar layout design
- Merge new dependencies (electron-updater, lucide-react, katex)
- Add new 'data' tool with Lucide BarChart3 icon
- Keep UpdateNotification component (not integrated into UI yet)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 11:16:49 +08:00
Naiyuan Qing
eb4e1f57b1 feat(desktop): persist onboarding state to file system
- Add AppState module in core for managing app state persistence
- Add app-state IPC handlers for reading/writing onboarding state
- Hydrate onboarding state from file system on app startup
- Prevent flash by showing blank screen during hydration
- Update onboarding store to sync with file system
- Improve MulticaIcon with enhanced animation states
- Minor UI fixes in chat and device list components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 10:53:04 +08:00
Jiayuan Zhang
b5f63f7f7c
Merge pull request #130 from multica-ai/forrestchang/finance-pr
feat(finance): add internal evidence decision engine
2026-02-11 20:39:29 +08:00
Jiayuan Zhang
24435cdba4 refactor(agent): remove hasWebTools conditional in data section
Web tools are always available, so the conditional branching was
unnecessary. Data section now always includes the dynamic evidence
decision guidance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 20:31:40 +08:00
Jiayuan Zhang
0330b8b7ca refactor(agent): remove keyword-based finance decisioner
The finance-decisioner used hardcoded keyword matching and arbitrary
scoring weights to decide evidence plans — the LLM reading the
improved SKILL.md already makes this decision more intelligently.

Removes: finance-decisioner.ts, its tests, runner.ts integration
(applyFinanceResearchGuidance, saveFinanceDecisionMeta,
rebuildSystemPromptWithExtra shim), and researchDecision from
session meta.

Keeps: SKILL.md improvements, sections.ts dynamic data/web guidance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 20:29:14 +08:00
Jiayuan Zhang
4c4f8989ca feat(agent): add internal finance evidence decisioner 2026-02-11 20:02:30 +08:00
Jiang Bohan
c49ab1fee4 fix(heartbeat): use internal run to hide heartbeat from chat history
The heartbeat runner used agent.write() (normal write), which persisted
both the heartbeat prompt and HEARTBEAT_OK response as regular messages
visible in the UI chat history.

Switch to runInternalForResult() — a new AsyncAgent method that runs
via runInternal() (messages marked internal: true, rolled back from
in-memory state). This hides both the heartbeat prompt and response
from the UI entirely, while still persisting to JSONL for diagnostics.

The previous commit's event-stream heartbeat ACK filter remains as a
defense-in-depth layer for edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 19:41:57 +08:00
Jiang Bohan
8d6a803739 fix(agent): filter heartbeat ACK messages from desktop event stream
The heartbeat runner uses agent.write() (normal write), so heartbeat
ACK responses like "HEARTBEAT_OK" were not suppressed by the internal
run filter and leaked into the desktop UI chat. The Gateway path was
already fixed (Hub has delayed-start + isHeartbeatAckEvent filtering),
but the local Desktop path through AsyncAgent had no such filtering.

Add createFilteredHandler() to AsyncAgent that buffers message_start
for assistant messages and checks subsequent events with
isHeartbeatAckEvent(). Pure heartbeat ACKs are suppressed end-to-end;
all other messages are forwarded normally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 19:34:23 +08:00
Bohan Jiang
16cd5d0aaf
Merge pull request #126 from multica-ai/feat/subagent-announce-delivery
feat(subagent): two-tier announce delivery, provider inheritance, error propagation
2026-02-11 19:04:39 +08:00
Jiang Bohan
af3a42a00e fix(subagent): forward announcement summary to UI stream
Changed forwardAssistant from false to true in sendAnnounceDirect() so
the assistant's summary response is streamed to the desktop UI in
real-time. The announcement prompt stays internal (hidden from UI), but
the user now sees the completion notification.

Previously, persistAssistantSummary saved the response to JSONL but
never emitted events to the UI subscriber, leaving users with no
visible feedback after subagent tasks completed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 18:58:32 +08:00
Jiang Bohan
47cbf60687 fix(agent): reload tools after setProvider to fix stale closure
setProvider() updated toolsOptions.provider but didn't reload tool
instances. The sessions_spawn tool captured the old provider in its
closure at creation time, causing subagents to inherit a stale provider.

Now calls resolveTools() + setTools() after updating toolsOptions so
sessions_spawn gets a fresh closure with the correct provider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 18:58:24 +08:00
Jiayuan Zhang
5cccb066f1 refactor(agent): keep finance tool decisions internal 2026-02-11 18:56:31 +08:00
Jiayuan Zhang
c5db9bf232 refactor(agent): make finance web usage dynamic 2026-02-11 18:48:31 +08:00
Jiayuan Zhang
64d8427ca7 fix(agent): enforce data-web evidence fusion 2026-02-11 18:30:20 +08:00
Jiang Bohan
d7a02182ab feat(subagent): add announce:"silent" mode for deferred coalesced announcements
Add an `announce` parameter to sessions_spawn that controls when findings
are delivered to the parent agent:

- "immediate" (default): announce per-completion (existing behavior)
- "silent": defer until ALL silent runs from the same requester complete,
  then deliver ONE coalesced announcement with all findings

This enables workflows like "spawn 10 parallel subagents to collect data,
then summarize everything at once" without intermediate results.

Changes:
- types.ts: add `announce` field to SubagentRunRecord & RegisterSubagentRunParams
- sessions-spawn.ts: add `announce` parameter to tool schema
- registry.ts: split checkAndAnnounce into immediate/silent groups,
  extract announceGroup helper, use count-match guard for silent readiness
- sections.ts: add announce mode guidance to system prompt
- registry.test.ts: add silent mode tests (field storage, group isolation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:40:00 +08:00
Jiang Bohan
de928cfe2b fix(agent): add NO_REPLY detection utility for filtering silent replies
Extract SILENT_REPLY_TOKEN and isSilentReplyText() into a shared
module. Detects NO_REPLY at the start or end of text (with optional
whitespace/punctuation) to filter out silent announcement responses
that should not be forwarded to the user.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:16:22 +08:00
Jiang Bohan
d01fcffe32 docs(subagent): add architecture flowchart and README
Document the full subagent lifecycle: spawn, concurrency queue,
execution, completion handling, two-tier announcement delivery,
and record archival. Include provider inheritance chain and
error propagation diagrams.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:10:12 +08:00
Jiang Bohan
58ec56234c feat(subagent): improve system prompts for error reporting and timeout
Add error reporting rule to subagent system prompt: subagents must
explicitly report tool failures and missing credentials in their
final message. Add timeout guidelines to parent system prompt with
recommended values by task complexity (10-30 min).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:10:05 +08:00
Jiang Bohan
716e69ac39 feat(subagent): add anti-polling guards to sessions_list
When subagents are still running, sessions_list now returns an
explicit instruction telling the LLM not to poll again and wait
for automatic result delivery. Normalizes status display to
uppercase ([RUNNING], [OK], [ERROR]).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:09:56 +08:00
Jiang Bohan
121b644df4 fix(subagent): inherit parent provider in spawned subagents
Pass parent's resolvedProvider through the tool chain (tools.ts →
sessions-spawn.ts → hub.createSubagent) so subagents use the same
LLM provider as the parent. Previously subagents fell back to the
hardcoded default provider, causing API key errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:09:49 +08:00
Jiang Bohan
2e6d419c27 feat(subagent): deferred record cleanup and error propagation
Replace immediate record deletion with archiveAtMs-based deferred
cleanup (60min retention). This keeps records queryable via
sessions_list after completion. Add sweeper to clean expired records.
Check childAgent.lastRunError after waitForIdle to detect failed runs
that resolve the promise without throwing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:09:42 +08:00
Jiang Bohan
299c947893 feat(agent): expose isRunning and lastRunError on Agent and AsyncAgent
Add isRunning flag to Agent (runner.ts) for detecting active runs.
Add lastRunError to AsyncAgent for propagating child run errors to
the registry. Fix duplicate message emission in writeInternal by
resetting forwardInternalAssistant before persistAssistantSummary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:09:33 +08:00
Jiang Bohan
45db13cbdd feat(subagent): add two-tier announce delivery with debounced queue
Add announce-queue.ts for batching nearby subagent completions with
debounce (1s) and collect mode. Implement two-tier delivery in
announce.ts: queue when parent is busy, writeInternal when idle.
All delivery uses writeInternal() to mark messages as internal,
preventing announcement bubbles from appearing in the UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:09:26 +08:00
Jiayuan Zhang
d80e97648c fix(core): read data tool API key from credentials.json5
Use credentialManager.getToolConfig("data") as the primary source for
the Financial Datasets API key, with FINANCIAL_DATASETS_API_KEY env var
as fallback. Also add data tool entry to the credentials template.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:45:46 +08:00
Jiayuan Zhang
d47594d7b2 feat(core): add unified data tool with finance domain support
Introduces a `data` tool that provides structured access to the Financial
Datasets API (financialdatasets.ai). Supports 18 finance actions covering
stock prices, financial statements, key metrics, SEC filings, analyst
estimates, insider trades, news, and crypto data.

Designed as a stable interface — the backend can be swapped from direct
API calls to a Multica Data Service without changing the tool schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:43:58 +08:00