Sync with sipeed/picoclaw upstream/main (~1095 commits since last merge),
including major restructures:
- Launcher migrated to web/backend (#1275): cmd/picoclaw-launcher/* removed.
Our launcher is dropped (MagicForm doesn't use it; web/backend is the
upstream replacement when needed).
- Agent loop split (#2585, 12d5421c): pkg/agent/loop.go deleted, replaced by
pkg/agent/agent_*.go files. Multi-tenancy customizations to be forward-ported
in follow-up commits on this branch.
- Tool packages reorganized (4c133dc2): pkg/tools/{filesystem,web}.go moved
to pkg/tools/{fs,integration}/. Customizations to be forward-ported.
- Channels switched to map+Settings model: MagicFormConfig → MagicFormSettings
registered in config_channel.go alongside other channels; channel constructor
updated to (channelName, channelType, *Config, *MessageBus) factory pattern.
- Config schema updates: removed deprecated AgentDefaults.Model field;
removed Config.Bindings (legacy migration in upstream); SessionConfig now
uses Dimensions instead of DMScope; ExecConfig adds AllowRemote.
Conflicts resolved:
- pkg/agent/loop.go + loop_test.go: accepted upstream deletion (split)
- pkg/tools/{filesystem,web}.go: accepted upstream deletion (moved)
- cmd/picoclaw-launcher/*: dropped (deps deleted upstream, unused by MagicForm)
- cmd/picoclaw/internal/gateway/helpers.go: accepted upstream deletion
- pkg/channels/manager.go: switched to upstream's map-driven channel init
- pkg/config/config.go: merged AgentDefaults fields, kept WorkspaceRoot,
removed deprecated Model
- pkg/bus/types.go: merged OutboundMessage with upstream's new fields
(Context, AgentID, SessionKey, Scope, ContextUsage) plus our magicform
callback fields (Type, Metrics, Progress, Escalation)
- pkg/tools/{cron,shell}.go: combined our customizations with upstream changes
- pkg/skills/installer.go: dropped our LimitReader path (upstream now uses
chunked DownloadToFile which is size-bounded)
The magicform channel was rebuilt to match upstream's new API:
- *config.MagicFormSettings (SecureString token) instead of MagicFormConfig
- Send returns ([]string, error) per upstream Channel interface
- InboundMessage built with Context (with Raw map for tenancy hints) instead
of bus.Peer + Metadata field which were removed.
Multi-tenancy hints (workspace_override, config_dir, allowed_tools,
allowed_skills) are still flowing via Context.Raw — the agent loop side of
the integration is forward-ported in a follow-up commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a context window usage indicator to the web chat UI and a /context
slash command that works across all channels.
Backend:
- Add computeContextUsage() estimating history + system + tool tokens
- Attach ContextUsage to outbound messages via the pico WebSocket protocol
- Add /context command showing context stats as formatted text
- Add EstimateSystemTokens() on ContextBuilder for system prompt estimation
Frontend:
- Add ContextUsageRing component (SVG ring + hover/tap popover)
- Show usage percentage, token counts, and compression threshold
- Hover on desktop (150ms leave delay), tap on mobile
- "View Details" sends /context with 1s cooldown
- i18n support (en/zh) for popover labels
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Introduced `workspace_root` configuration in global and MagicForm settings to enforce path security.
- Updated CLI commands to require relative paths for `--workspace` and `--config-dir` when `workspace_root` is set.
- Added detailed validation rules for workspace paths to prevent directory traversal and absolute paths.
- Enhanced agent processing to track execution metrics, including token usage and tool calls, and included these metrics in outbound messages.
- Updated callback payloads for MagicForm to support progress and escalation messages, including detailed metrics for final responses.
- Refactored agent loop to accumulate metrics across LLM iterations and publish progress updates during tool execution.
- Added tests to ensure proper handling of new metrics and workspace path validations.
Introduce SenderInfo struct and pkg/identity package to standardize user
identification across all channels. Each channel now constructs structured
sender info (platform, platformID, canonicalID, username, displayName)
instead of ad-hoc string IDs. Allow-list matching supports all legacy
formats (numeric ID, @username, id|username) plus the new canonical
"platform:id" format. Session key resolution also handles canonical
peerIDs for backward-compatible identity link matching.
Add outbound media sending capability so the agent can publish media
attachments (images, files, audio, video) through channels via the bus.
- Add MediaPart and OutboundMediaMessage types to bus
- Add PublishOutboundMedia/SubscribeOutboundMedia bus methods
- Add MediaSender interface discovered via type assertion by Manager
- Add media dispatch/worker in Manager with shared retry logic
- Extend ToolResult with Media field and MediaResult constructor
- Publish outbound media from agent loop on tool results
- Implement SendMedia for Telegram, Discord, Slack, LINE, OneBot, WeCom
PublishInbound/PublishOutbound held RLock during blocking channel sends,
deadlocking against Close() which needs a write lock when the buffer is
full. ConsumeInbound/SubscribeOutbound used bare receives instead of
comma-ok, causing zero-value processing or busy loops after close.
Replace sync.RWMutex+bool with atomic.Bool+done channel so Publish
methods use a lock-free 3-way select (send / done / ctx.Done). Add
context.Context parameter to both Publish methods so callers can cancel
or timeout blocked sends. Close() now only sets the atomic flag and
closes the done channel—never closes the data channels—eliminating
send-on-closed-channel panics.
- Remove dead code: RegisterHandler, GetHandler, handlers map,
MessageHandler type (zero callers across the whole repo)
- Add ErrBusClosed sentinel error
- Update all 10 caller sites to pass context
- Add msgBus.Close() to gateway and agent shutdown flows
- Add pkg/bus/bus_test.go with 11 test cases covering basic round-trip,
context cancellation, closed-bus behavior, concurrent publish+close,
full-buffer timeout, and idempotent Close
Channels previously deleted downloaded media files via defer os.Remove,
racing with the async Agent consumer. Introduce MediaStore to decouple
file ownership: channels register files on download, Agent releases them
after processing via ReleaseAll(scope).
- New pkg/media with MediaStore interface + FileMediaStore implementation
- InboundMessage gains MediaScope field for lifecycle tracking
- BaseChannel gains SetMediaStore/GetMediaStore + BuildMediaScope helper
- Manager injects MediaStore into channels; AgentLoop releases on completion
- Telegram, Discord, Slack, OneBot, LINE channels migrated from defer
os.Remove to store.Store() with media:// refs
Add bus.Peer struct and explicit Peer/MessageID fields to InboundMessage,
replacing the implicit peer_kind/peer_id/message_id metadata convention.
- Add Peer{Kind, ID} type to pkg/bus/types.go
- Extend InboundMessage with Peer and MessageID fields
- Change BaseChannel.HandleMessage signature to accept peer and messageID
- Adapt all 12 channel implementations to pass structured peer/messageID
- Simplify agent extractPeer() to read msg.Peer directly
- extractParentPeer unchanged (parent_peer still via metadata)