Remove content-shape and message-type attachment filtering, recover forwarded unknown attachments, and preserve original media across all agent backends.
Two connector improvements for the linked-bot chat experience, modelled on
codex / OpenClaw connect:
1. Built-in slash commands. A user can type /new (aliases /start, /reset) to
open a fresh session or /clear to wipe the current one, instead of the
message being forwarded to the agent. The command set is fixed (never
dynamically extended) so behaviour stays predictable. Matching requires the
whole message to be the bare command, so a normal question that merely starts
with a slash is forwarded untouched. Costs no agent turn / tokens.
- connect_command.go: channel-agnostic parser + action table (+ tests).
- sessionResetter optional interface; execForwarder (Claude family) and the
codex app-server forwarder both implement it. The main loop intercepts a
command right after the owner-decision interceptor and acks via webhook.
2. Persist codex per-conversation context. codexThreadSessions previously kept
the convID->threadID map in memory only, so a codex connector restart lost
every conversation's context — unlike the Claude-family convSessions store
which already persisted. The thread identity is now the authoritative,
mutex-guarded map persisted to <config>/connect/<clientId>/codex-threads.json
(best-effort, atomic write), restored on startup. An empty clientId keeps it
in memory only, preserving the original behaviour. This also removes a data
race on threadID by moving it off the per-conversation state into the
sessions store.
convSessions held the DingTalk-conversation to agent-session map purely
in memory, so a connector restart dropped every chat's multi-turn
context - the core weakness of an always-on digital employee.
Persist the map to ~/.dws/connect/<clientId>/sessions.json (scoped per
clientId so multiple bots on one machine stay isolated), reusing the
existing config dir, 0600 FilePerm and lock-id sanitizer conventions:
- newConvSessions restores the map on startup; a missing or corrupt
file degrades to an empty map with a warning, never a panic or a
blocked connection.
- args/reset persist after every mutation, under the existing mutex so
the snapshot is race-free, via a temp-file + rename atomic write.
- saves are best-effort: a write failure only logs a warning and never
blocks message handling.
- an empty path (clientId missing or --agent-memory off) keeps the map
in memory only, preserving the original behaviour exactly.
forwarderForChannel now takes clientId to derive the per-bot store path.
Adds unit tests for reload, corrupt-file degradation, in-memory mode and
concurrent access (race-clean).