Action handlers now only see fields from the param table. Extra client
keys are still ignored. Garbage values on declared numbers fail instead
of becoming zero.
Phase 0 of the debug-console expansion. Introduces a closed `FieldRole`
union (group_id/user_id/member_id/message_id/file_id/file/image/record/
video/duration/timestamp/face_id) orthogonal to `type` (which governs
coercion), so the WebUI debug console can render semantic pickers instead
of forcing hand-written JSON.
- FieldDoc gains `role?`; Field gains a `.role()` modifier (survives
optional/default/describe). toJsonSchema emits it as `x-role`.
- Semantic constructors f.groupId/userId/memberId/fileId/file/image/
record/video/duration/timestamp/faceId wrap the coercion primitives and
stamp a role (messageId backfilled). Media sources reject '' to preserve
the empty-string guard they replace. faceId allows 0 (valid QQ face id).
- Extracted stringCore so f.string() and string-valued roles share it.
- ActionDoc gains `stream?`; defineStreamAction sets stream:true.
- Presets: groupAction injects f.groupId(); groupUserAction injects
f.groupId() + f.memberId() (a group action's user_id is a member of
that group). Backfills role onto 56 group_id / 7 member_id sites.
- Regenerated MCP catalog (79 x-role, 5 stream).
Adversarially reviewed: no coercion regression, member_id stamping has
zero false positives across all 7 groupUserAction sites, x-role is inert
to MCP consumers.
A new published package that exposes every OneBot action's docs + a ready-to-use
JSON Schema to LLM clients over stdio. Tools: list_actions / get_action /
search_actions / list_categories, plus the whole catalog as a resource.
The catalog is a build-time snapshot generated from @snowluma/onebot's live
action specs (collectActionDocs()) — so it auto-tracks action add/remove and the
published package carries zero runtime dependency on the private onebot package.
action-kit enrichment (the data source):
- Field.toJsonSchema() per coercer (uint→integer/minimum:1, messageId→integer/
not-const-0, int→min/max, string→minLength/maxLength, array→items, enum→enum).
- describe() composes a per-action inputSchema; ActionDoc gains inputSchema +
category. action-docs.ts tags each doc with its domain category.
Wiring: onebot exports ./action-kit + ./action-docs; root build:all includes mcp;
mcp-npm-publish.yml mirrors the sdk/ui publish workflows (triggers on changes
under packages/mcp/** or packages/onebot/**, since the catalog derives from it).
onebot typecheck + 318 tests green; mcp typecheck/build/smoke green; the 142
registered OneBot action names are unchanged.
Introduce packages/onebot/src/action-kit.ts — a zero-dependency,
type-inferring declarative layer for OneBot action params. Each action
declares its param contract (f.* coercers + optional cross-field rules)
and a `run` handler that receives typed, already-validated params.
Validation failures now return BAD_REQUEST naming the offending field
instead of falling through to ApiHandler's INTERNAL_ERROR catch.
- 141/142 actions migrated to defineAction/groupAction/groupUserAction
across info/message/friend/group-*/request/extended; only
.handle_quick_operation stays legacy (it needs the ApiHandler instance,
which run() does not receive).
- f.messageId() distinguishes signed/non-zero message ids from positive
group/user ids (f.uint()); int({min:0}).default(1800) fixes duration=0
(unmute), which the old `|| 1800` silently swallowed.
- run(p, ctx, raw) exposes the raw params as an escape hatch for the
irregular tail (alias-key fallbacks, arbitrary objects).
- action-docs.ts renders self-generated API docs from describe().
- ApiHandler dispatch / try-catch / response-wrapping unchanged (narrow seam).
Registered action name set is byte-identical before/after (142, verified
by runtime enumeration). Typecheck clean; onebot suite green (the
pre-existing websocket native-addon test failure is unrelated).