Replace tsc with esbuild for building @multica/sdk and @multica/store
in the Docker builder stage. esbuild resolves extensionless imports
that Node.js ESM cannot handle at runtime, producing self-contained
bundles for each workspace package.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy @multica/sdk and @multica/store workspace packages into both builder
and production stages. Build them with tsc in the builder stage, then
patch their package.json exports to point from ./src/*.ts to ./dist/*.js
for Node.js runtime compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update pnpm from 10.16.1 to 10.28.2 to match project packageManager,
and copy pnpm-workspace.yaml into both build stages so pnpm can resolve
the catalog configuration recorded in the lockfile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add clientName field to DeviceMeta so non-browser clients (Telegram,
Discord, etc.) can provide a human-readable label instead of relying on
userAgent parsing. Desktop UI now prioritizes clientName over parsed UA
string, fixing "Unknown on Unknown" display for Telegram connections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pack Telegram user info (userId, username, firstName) into the
userAgent field instead of passing custom fields that don't conform
to the SDK's DeviceMeta interface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move table DDL to scripts/telegram-users.sql for manual execution by
ops. Remove OnModuleInit and ensureTable() from TelegramUserStore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The old table had hub_url column; new schema uses hub_id + agent_id.
CREATE TABLE IF NOT EXISTS won't alter existing tables, so drop first.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of throwing and crashing the gateway when MySQL connection
fails, close the broken pool and continue with pool=null so the
isAvailable() guard handles it gracefully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the two-step Hub URL binding flow with a multica://connect
connection link flow that uses the same verify RPC handshake as the
SDK/web clients.
Changes:
- types.ts: replace hubUrl with hubId + agentId fields
- telegram-user.store.ts: update DB schema (hub_id, agent_id columns),
accept explicit deviceId in upsert
- telegram.service.ts: rewrite with parseConnectionCode validation,
verify RPC via routeFromVirtualDevice, pending request map for
RPC promise tracking, smart sendCallback for stream/message/RPC
- package.json: add @multica/store workspace dependency
Flow: user pastes multica://connect link -> parse & validate ->
check Hub online -> register virtual device -> verify RPC to Hub ->
Desktop approval -> save to DB -> route messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handleSend() only checked socket-based devices (deviceToSocket map),
causing DEVICE_NOT_FOUND errors when Hub sends responses to virtual
devices like Telegram. Now checks virtualDevices map as fallback.
Also adds routeFromVirtualDevice() to allow virtual devices to
initiate messages (e.g., verify RPC, chat messages) through the
Gateway routing infrastructure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The empty ~/.super-multica/channels.json5 file was accidentally added
during a merge conflict resolution. Remove it and add ~/.super-multica/
to .gitignore to prevent future accidents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Track bundled skill IDs in a .bundled-manifest.json file. On each
initialization, remove managed skills that were previously bundled
but are no longer present in the bundle directory. User-installed
skills are not affected since they are never recorded in the manifest.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The provider & model dropdown now scrolls internally instead of
causing the whole page to scroll when content exceeds viewport.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upgrade @mariozechner/pi-ai and @mariozechner/pi-agent-core from 0.50.3
to 0.52.9 to support latest models (claude-opus-4-6, o3, o3-mini).
Breaking type changes addressed:
- exactOptionalPropertyTypes: use conditional spread or `| undefined`
- TOOL_PROFILES removed: strip all profile references from CLI
- AgentMessage union requires timestamp: cast test fixtures
- AsyncAgent.id → sessionId
- Add explicit callback parameter types for SDK event handlers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clarify that local whisper is the primary provider (free, offline),
OpenAI API is the fallback, and the skill only activates when both
are unavailable. Add setup instructions noting no restart is required.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only cache successful whisper binary lookups. When whisper is not found,
leave the cache empty so subsequent calls re-check PATH. This allows
the app to detect a newly installed whisper without requiring a restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the new channel system design: FIFO pendingRoutes queue,
activeRoute/activeAcks state, agent_start/agent_end lifecycle,
InboundDebouncer, typing/reaction lifecycle, and UI metadata stripping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add stripUserMetadata() to remove timestamp envelopes and media type
labels ([Voice Message] Transcript:, [Image] Description:, etc.) that
are injected for LLM context but should not appear in the desktop UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace global lastRoute-based reply targeting with a FIFO route queue
(pendingRoutes) that snapshots route + ack targets at each debouncer flush.
Use agent_start/agent_end lifecycle instead of message_end to ensure stable
routing across multi-turn runs. Track per-message 👀 acks in ackBuffer →
activeAcks for precise cleanup. Two-gate typing stop: only stop when all
queued runs complete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add model selector section below the provider grid in the dropdown.
Shows available models for the current provider with active model
indicator. Clicking a model calls setProvider with the modelId.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously all agent errors (including 400 invalid_request_error) were
emitted as agent_error events, triggering the UI error banner and
interrupting the chat flow. Now only auth-related errors (401, no API key)
emit agent_error so the "Configure" banner appears. Runtime errors like
400 are still shown as chat messages but don't block the agent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setProvider() was saving the alias-resolved provider (e.g. "anthropic"
instead of "claude-code") to session metadata. On restart, this caused
the wrong provider to be selected. Now saves the original providerId
so the exact user selection is preserved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move session metadata loading before provider resolution so that
the stored provider from a previous setProvider() call is used
in the fallback chain (options > session meta > credentials > default)
instead of always falling back to "kimi-coding".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
grammy was not in rollupOptions.external, causing the channels IPC
handlers to fail at runtime with 'No handler registered'.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cron reminders were silently skipped when heartbeat.md had no actionable
content. Now cron: and exec-event reasons both bypass the empty-file
guard so scheduled reminders always reach the agent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>