Commit graph

232 commits

Author SHA1 Message Date
yushen
1a9e915d49 fix(hub): skip text processing after MessageAggregator reset
Add an `active` flag so that message_update/message_end events are
ignored after reset() until the next message_start, preventing stale
accumulated text from being re-emitted as a block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 07:49:20 +08:00
yushen
b8aa5ad7e6 refactor(core): convert MULTICA_API_URL constant to getApiBaseUrl() function
Lazy-read process.env at call time instead of module import time.
This ensures the env bridge in the Electron main process has time to
set process.env.MULTICA_API_URL before the first API request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 07:30:36 +08:00
Jiayuan Zhang
ef8b38899a fix(core): check for "toolCall" type in hasToolUse() to match pi-ai types
The hasToolUse() function was checking for "tool_use" (raw Anthropic format)
but pi-ai normalizes tool call blocks to type "toolCall". This made tool
narration non-functional in the ChannelManager (Desktop/embedded) path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 03:32:16 +08:00
Jiayuan Zhang
3c911807e1 chore(channels): remove unused Desktop Telegram channel plugin
The Telegram bot is handled entirely by the Gateway
(apps/gateway/telegram/). The Desktop channel plugin was never
registered (commented out in initChannels) and is dead code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 02:28:00 +08:00
Jiayuan Zhang
dde8cc542a feat(telegram): show editable status message during tool execution
Instead of silently discarding tool narration or spamming separate
messages, send a single editable Telegram message that updates in-place
as the agent works. First tool narration sends a reply; subsequent
narrations edit the same message. The final answer is sent as a new
message.

- Add replyTextEditable/editText to ChannelOutboundAdapter interface
- Implement editFormatted + editable methods in Telegram plugin
- Track statusMessageId in ChannelManager, clear on agent_end/error
- Add sendOrEditStatus to Gateway TelegramService with same behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 02:14:42 +08:00
Jiayuan Zhang
81998e6309 fix(telegram): skip tool narration messages, only send final answer
When the agent uses tools (web search, etc.), it generates intermediate
narration text like "Let me search..." before each tool call. These were
being sent as separate Telegram messages, causing message spam. Now we
detect tool_use blocks in the message content and skip sending those
intermediate messages — only the final answer reaches the user.

Applied to both Desktop channel plugin and Gateway Telegram service.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 02:04:17 +08:00
Jiayuan Zhang
8270762d66 feat(telegram): convert Markdown tables to vertical list format
Telegram doesn't support HTML tables, so pipe-delimited Markdown tables
were rendered as hard-to-read plain text on mobile. This converts tables
to a vertical "Header: Value" format with bold first column before
sending to Telegram.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 01:56:21 +08:00
Jiayuan Zhang
9189883710
Merge pull request #175 from multica-ai/forrestchang/telegram-arch
feat(gateway): Telegram QR-based connection flow with centralized bot
2026-02-14 01:45:46 +08:00
Jiayuan Zhang
c2a2c75b24
Merge pull request #174 from multica-ai/forrestchang/arch-analysis
test(core): migrate tests to strict mock policy with real implementations
2026-02-14 00:13:24 +08:00
Jiang Bohan
a86709a4cd chore: switch API host from api-dev.copilothub.ai to api.multica.ai
Update all backend API base URLs to use the production multica.ai domain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:25:00 +08:00
Jiayuan Zhang
4cb8b93f93 refactor(desktop): hide manual Telegram bot creation, use official Gateway bot only
Remove the user-facing ability to create custom Telegram bots via BotFather.
Non-technical users should only need to message @multica_bot on Telegram.
- Disable telegramChannel plugin registration in initChannels()
- Remove ConnectStep from onboarding flow (Privacy → Provider → Start)
- Replace TelegramCard with simple text pointing to @multica_bot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:20:39 +08:00
Jiayuan Zhang
f6b6c2909b test(core): migrate 5 tests from internal mocks to real implementations
- store.test.ts: use baseDir option instead of mocking paths.js
- session-file-repair.test.ts: remove write-lock mock, assert behavior
- announce-findings.test.ts: use real storage with temp dirs
- sessions-list.test.ts: use real registry with seed helper
- compaction.test.ts: mock only third-party pi-coding-agent, use real
  context-window internals

All tests exercise real code paths, improving confidence in actual
behavior per the strict mock policy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:17:52 +08:00
Jiayuan Zhang
37e550200c refactor(core): add baseDir DI and test helpers for mock-free testing
Add AuthStoreOptions with baseDir to auth-profiles/store.ts functions,
add baseDir option to announce.ts readLatestAssistantReply, and add
seedSubagentRunForTests helper to registry.ts. These enable tests to
use real implementations with temp directories instead of mocking
internal modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:17:41 +08:00
Jiayuan Zhang
b6db23c4b3 feat(agent): add structured run logging for debugging
Introduce a RunLog system that records agent execution events as
structured JSONL to ~/.super-multica/sessions/{id}/run-log.jsonl.
Enable via MULTICA_RUN_LOG=1 env var or AgentOptions.enableRunLog.

Logs: run lifecycle, LLM calls, tool execution timing, context
overflow recovery, auth profile rotation, error classification,
and compaction events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:38:02 +08:00
Jiayuan Zhang
27c3ba5682 feat(web): add Accept: text/markdown header for Cloudflare Markdown for Agents
Prefer markdown responses from servers that support Cloudflare's Markdown
for Agents feature, reducing token usage by ~80% when available. Non-supporting
servers fall back to HTML as before.

- Update Accept header to prefer text/markdown in web_fetch requests
- Add text/markdown content-type handling to skip HTML parsing pipeline
- Capture x-markdown-tokens response header in WebFetchResult
- Add extractMarkdownTitle() helper for native markdown title extraction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:47:17 +08:00
LinYushen
882dc8592b
Merge pull request #157 from multica-ai/feat/improve-compaction
feat(compaction): improve summary mode with fallback, split-turn, and metadata
2026-02-13 19:15:38 +08:00
yushen
882e34c491 fix(core): preserve context-specific auth error messages
Add optional context parameter to getAuthHeaders() so callers can
provide feature-specific suffixes (e.g. "to use web search") in the
not-logged-in error message, restoring the original behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:58:27 +08:00
yushen
53cffe923b refactor(tools): use shared api-client for auth headers
Replace duplicated getLocalAuth() + manual header construction in
finance/api.ts and web-search.ts with the shared getAuthHeaders()
and API_BASE_URL from hub/api-client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:56:26 +08:00
yushen
f76312511d feat(core): add shared api-client module for auth headers
Extract API_BASE_URL and getAuthHeaders() into a reusable module
so that tools don't duplicate base URL and auth header construction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:56:20 +08:00
yushen
77953a611d feat(finance): replace API key auth with auth headers
Route all financial data requests through api-dev.copilothub.ai/api/v1/financial
proxy and authenticate via sid/device-id/os-type headers instead of X-API-KEY.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:52:46 +08:00
yushen
92a137414d feat(web-search): replace HMAC signing with auth headers
Migrate web_search tool from HMAC-SHA256 reqId signing to
sid/device-id/os-type auth headers, matching the desktop API client
pattern. Update endpoint to /api/v1/web-search.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:25:43 +08:00
yushen
ec413effb9 feat(core): add auth-store module to read local auth data
Reads sid and deviceId from ~/.super-multica/auth.json for use by
tools that need authenticated API access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:25:37 +08:00
Naiyuan Qing
9877a4f97e
Merge pull request #167 from multica-ai/feat/desktop-device-id
feat: unify encrypted Device ID across all platforms
2026-02-13 17:47:54 +08:00
Naiyuan Qing
242be23876 feat(utils): unify encrypted Device ID across all platforms
- Add common generateEncryptedId() utility in @multica/utils
- All Device IDs now use same encryption algorithm (40 hex chars)
- Web: store encrypted format directly in localStorage
- Desktop: use shared utility, accept encrypted ID from Web
- Hub: use shared utility for hub-id generation
- Telegram: use shared utility for device ID generation
- Gateway hook: use encrypted format for client connections

Algorithm: sha256(sha256(uuid).slice(0,32)).slice(0,8) + sha256(uuid).slice(0,32)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 17:34:23 +08:00
Jiayuan Zhang
fe6fdd4ddc
Merge pull request #165 from multica-ai/forrestchang/analyze-run-logs
feat(session): optimize image storage with externalization and resize
2026-02-13 17:13:40 +08:00
Jiayuan Zhang
7ac90a2ce7 feat(tools): add image resize wrapper for read tool
Wraps the read tool from pi-coding-agent to automatically downscale
oversized images (>1MB or >2000px) before they enter the session.
Uses macOS sips for resize with no extra dependencies, following the
same pattern as OpenClaw. Falls back gracefully on non-macOS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:12:37 +08:00
Jiayuan Zhang
278334474f feat(session): externalize base64 images from session JSONL
Session JSONL files were bloated because base64 image data was stored
inline (a real session had 6.7MB of images in a 9.8MB file). Images
are now extracted to per-session media/ directories as binary files,
with compact $ref references stored in the JSONL. Images are restored
transparently on read. Old sessions with inline base64 remain
backward compatible and auto-migrate on next compaction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:12:30 +08:00
Jiang Bohan
13fbeaf4d2 fix(providers): use dedicated openai-codex provider instead of aliasing to openai
Codex OAuth tokens use the ChatGPT backend API (chatgpt.com), not the
standard OpenAI API (api.openai.com). pi-ai already has a dedicated
openai-codex provider with the correct API format (openai-codex-responses)
and base URL. Remove the alias that was incorrectly mapping openai-codex
to openai, which caused 401 errors due to missing scopes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:01:57 +08:00
Naiyuan Qing
e0f80b0054 merge: resolve conflict with main (keep both auth and subagents handlers) 2026-02-13 14:41:15 +08:00
Naiyuan Qing
7faf421996 merge: resolve conflict with main branch (build scripts) 2026-02-13 14:36:42 +08:00
Jiang Bohan
499c573544 feat(desktop): add subagent dashboard with real-time status tracking
Adds a status bar and slide-out dashboard panel to the chat view that
shows subagent progress in real-time. Polls at 2s when active, 10s when
idle. Completed runs auto-hide after 5 minutes. Includes dismiss button
and 30s auto-dismiss for the status bar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:46:58 +08:00
yushen
d7e85d0c25 fix(compaction): prevent double-counting in split-turn detection
- detectSplitTurn now returns adjustedToSummarize (truncated at
  assistantIndex) so split prefix messages are not summarized twice
  and removedCount is not inflated
- Remove unused contextWindow parameter from computeAdaptiveChunkRatio
- Remove duplicated single-chunk fast path in compactMessagesWithChunkedSummary
  (the multi-chunk loop handles 1 chunk naturally)
- Fix triple-newline formatting in buildPlainTextFallback by concatenating
  metadata sections directly instead of joining through parts array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:33:31 +08:00
yushen
9e7907a814 feat(compaction): switch default to summary mode with split-turn and adaptive chunking
- Default compaction mode changed from "tokens" to "summary" (LLM-based)
- Split turn detection: handles orphaned tool_result messages at compaction
  boundary by summarizing the split turn prefix separately
- Adaptive chunk sizing: dynamically adjusts chunk ratio (0.15-0.4) based on
  average message token count instead of fixed 50k tokens
- Fallback chain: summary compaction wraps in try/catch, falling back to
  tokens mode on complete failure; summarizeWithFallback provides 3-level
  degradation within summary mode itself
- Metadata appended to summaries: file operations + tool failures sections
- CompactionResult extended with fileOperations and toolFailures fields
- CompactionEndEvent gains optional summary field
- API key resolution improved: reuses agent's own key for summary generation

Backward compatible: explicit "count" or "tokens" mode behaves identically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:26:34 +08:00
yushen
ba44de89b7 feat(compaction): add metadata extraction and multi-level summary fallback
New modules for improved compaction:
- compaction-metadata.ts: extracts file operations (read/modified) and tool
  failures from compacted messages, appended to summaries for context retention
- summary-fallback.ts: 3-level degradation chain (full LLM summary → filtered
  summary excluding oversized messages → plain-text fallback with metadata)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:26:22 +08:00
Naiyuan Qing
ca1c260735 feat(ui): add size prop to MulticaIcon bordered variant
Supports sm, md, lg sizes for flexible usage in different contexts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 12:24:48 +08:00
Naiyuan Qing
35ab158775 feat(types): add AuthUser type for auth integration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 12:24:43 +08:00
Jiang Bohan
a490c737cc feat(providers): add gpt-5.3-codex and update OpenAI model lists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:55:16 +08:00
Jiang Bohan
16c325c808 fix(oauth): don't expire OAuth credentials that have refresh_token
Codex and Claude Code OAuth tokens with refresh_token can auto-renew,
so they should not be considered expired based on last_refresh time or
file mtime. Previously, credentials were hardcoded to expire after 1
hour, causing valid Codex sessions to show as unconfigured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:42:24 +08:00
Naiyuan Qing
e87e39f13b docs: add monorepo development workflow documentation
- Update root dev script to build packages before watching
- Add --no-dts flag to package dev scripts to avoid DTS race conditions
- Document monorepo development workflow in README

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 09:21:47 +08:00
Naiyuan Qing
374ce68b84 fix(ui): remove math plugins and disable single-tilde strikethrough
- Remove remarkMath and rehypeKatex plugins that caused text wrapping
  issues by interpreting $ signs as LaTeX delimiters
- Configure remarkGfm with singleTilde: false to prevent single ~ from
  being parsed as strikethrough (e.g., ~1000 should not become <del>)
- Only double tilde ~~text~~ now renders as strikethrough

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 08:33:56 +08:00
Jiayuan Zhang
0c6cdb0d3a fix(hub): initialize channelManager before restoreAgents
Move channel plugin initialization (initChannels + ChannelManager
constructor) before restoreAgents() to prevent TypeError when
createAgent() calls channelManager.listChannelInfos() during restore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:40:16 +08:00
Jiayuan Zhang
c64616512a feat(hub): wire send_file callback with dual-path routing
Create onChannelSendFile callback in Hub.createAgent() that tries the
channel plugin path first (local file), then falls back to the gateway
path (base64 over RoutedMessage). Also pass channel info to agent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:37:35 +08:00
Jiayuan Zhang
0ebd9d6b22 feat(agent): add connected channels section to system prompt
Add ChannelInfo type and buildChannelsSection() that informs the LLM
about connected messaging channels and their capabilities (e.g. send
files). Wire through SystemPromptOptions and runner.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:37:29 +08:00
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