* feat(im): support rich-text message attachment zone in send/reply/mget/edit
Support the post message attachment zone (top-level files array) end to end:
- +messages-send / +messages-reply: repeatable --attachment file_key flags
merged into the post content's files array (deduplicated).
- +messages-mget: render attachment-zone files/folders as <file>/<folder>
tags in content, extract file keys for --download-resources.
- +messages-edit: new shortcut (PUT /open-apis/im/v1/messages/:id) with
--set-attachments / --clear-attachments; body-only edits preserve the
attachment zone by default.
- Attachment flags are mutually exclusive with --content carrying a files
array (declare the zone via one or the other, not both).
- bot-only identity, matching server behavior (user token rejected).
- Fixes from review: attachments no longer bypass content mutual-exclusion
validation (P1); merge dedups by key.
- Docs (SKILL.md, references, affordance) and unit tests updated.
* fix(im): address design-review findings (auto-infer post, dedup set, doc routing)
- --attachment/--set-attachments/--clear-attachments now infer msg_type=post
automatically; only an explicit incompatible --msg-type conflicts.
--text is rejected with attachments (text is a standalone message, not a
post body) with a hint to use --markdown or --content.
- --set-attachments deduplicates repeated keys (docs promised this; the
replace helper now enforces it).
- Shortcut Description no longer leaks the HTTP path or the raw server
error phrase; it describes the command semantically.
- affordance/im.md +messages-edit now routes WHEN: interactive cards go to
messages.patch, corrected messages go to +messages-send, and attachment
tri-state tips are listed.
- mget doc no longer claims --format json exposes raw wire fields (the
output is the rendered content); download eligibility clarified.
#2194 extended `im +messages-search` to `AuthTypes: {user, bot}` but left the
affordance example and the skill reference asserting user-only, so the
dual-identity guard added by #2199 fails on main.
* fix(schema): fall back to runtime catalog when no embedded metadata
Binaries built from the bare Go module (plugin builds) embed only the
empty meta_data_default.json stub because meta_data.json is gitignored
and fetched at build time. The schema command, its completion, and the
affordance command-form resolver read the embedded-only catalog, so
every schema lookup failed with "Unknown service" even though the
runtime registry had already sync-fetched full metadata.
Add registry.SchemaCatalog(): embedded when compiled in (official
builds unchanged, still deterministic), otherwise the merged runtime
catalog seeded from cache or remote fetch. When neither source has
data (offline plugin build with a cold cache), schema now returns a
failed_precondition error with an actionable hint instead of
"Unknown service" with an empty candidate list.
* fix(registry): gate cached meta overlay on version newer than embedded
The cached remote meta was overlaid onto the embedded meta_data.json
unconditionally, so after a CLI upgrade an equal- or older-version
cache kept shadowing the freshly shipped embedded definitions until a
later refresh happened to rewrite it.
Only overlay when the cache version is strictly newer than the
embedded baseline. The bare-module stub baseline is "0.0.0", so plugin
builds without compiled metadata still take any real cached version
(TestOverlayGate_StubEmbedded_OverlaysRealCache) and the schema
runtime fallback keeps working offline from a warm cache.
Ports #1376 onto the typed meta model.
---------
Co-authored-by: liangshuo-1 <266696938+liangshuo-1@users.noreply.github.com>