@ycodium-ai/plugin-im 0.2.2634 → 0.2.2658
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -1
- package/dist/index.js +1291 -283
- package/locales/en.json +20 -1
- package/locales/zh-CN.json +20 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -83,6 +83,56 @@ written as plugin facts (`ctx.events.append`) and folded back by a pure function
|
|
|
83
83
|
(`ctx.derivedState`, see `src/chatState.ts`). A saved thread that was deleted,
|
|
84
84
|
archived or moved to another project is dropped on the next message.
|
|
85
85
|
|
|
86
|
+
Inbound attachments. An adapter reports a message's resources as `resources`
|
|
87
|
+
(`ImInboundResource`, `src/adapter.ts`) and implements `fetchResource`, which must stop reading
|
|
88
|
+
at `maxBytes` (`readBodyLimited`, `src/attachments.ts`) and throw `ImResourceTooLargeError`.
|
|
89
|
+
An adapter whose platform caps bot downloads tighter than the host sets `downloadLimitBytes`
|
|
90
|
+
(Telegram: 20 MB); a size the platform reports up front is checked before any download. The
|
|
91
|
+
core (`collectResources` in `src/index.ts`) cleans the name (`sanitizeAttachmentName`: the host
|
|
92
|
+
splices it into the `[Attached … "<name>" …]` line the agent reads, so newlines and double
|
|
93
|
+
quotes must not survive), takes the type from the bytes (`resolveMime`: platforms self-report,
|
|
94
|
+
Lark says PNG for every image), checks the host's limits (10 MiB image, 25 MiB other, 20 per
|
|
95
|
+
turn, mirrored from the host because a plugin cannot import it) and stores each one with
|
|
96
|
+
`ctx.attachments.store`; an image the host can inline reaches the agent as an image, anything
|
|
97
|
+
else as a path. A failed one is reported in the chat and left as a line in the turn text.
|
|
98
|
+
A message with attachments and no text starts no turn: the thread is created if the group has
|
|
99
|
+
none (attachments are stored per thread), the attachments wait in memory for that thread, and
|
|
100
|
+
the next message with text carries them. Waiting attachments are dropped when the group's
|
|
101
|
+
thread changes and are not persisted. Lark does not require a mention for a message that is only
|
|
102
|
+
a resource (`RESOURCE_MESSAGE_TYPES` in `src/larkInbound.ts`); the sender allow-list still
|
|
103
|
+
applies. Lark rich text is read by `src/larkPost.ts` (every image, `<image N>` placeholders in
|
|
104
|
+
order, the client's `md` field preferred). Known types we do not handle are reported with
|
|
105
|
+
`unsupportedType` and answered with one line, rate-limited per group. A group pointed at a team
|
|
106
|
+
cannot carry attachments. WeCom reports `image`, `file`, `video` and the images of `mixed` (a group "mention plus picture"; text items and `<image N>` placeholders keep their order, and a mixed with only images still has content, so it starts a turn) in `resources`, with the download URL and `aeskey` packed into the resource key. `fetchResource` (`src/wecomMedia.ts`) GETs the URL (valid five minutes) and decrypts: `aeskey` is base64 for a 32-byte key, the IV is its first 16 bytes, AES-256-CBC with auto padding off, then PKCS#7 removed with a **32-byte block** (pad value 1 to 32, every pad byte equal, else the media is rejected). Without an `aeskey` the bytes are used as they are. The ciphertext is capped at limit + 32, the plaintext at the limit, and the file name comes from `Content-Disposition` (`filename*=UTF-8''…` first, then `filename="…"`) through `ImFetchedResource`. A WeCom `voice` message is its transcript as text, not an attachment. `quote` is not read on any platform.
|
|
107
|
+
|
|
108
|
+
Outbound files. The plugin contributes one MCP tool, `im_send_file` (`src/sendFile.ts`;
|
|
109
|
+
`ctx.toolkit` name `im`), with `path` and an optional `description` (the caption). It sends only
|
|
110
|
+
to the group whose thread is the calling conversation (`chatOf(...).threadId`), never by
|
|
111
|
+
project, and fails with `not-in-chat` otherwise. The bytes come from `invocation.workspace`
|
|
112
|
+
(`workspace.read`: realpath-bounded to the thread's folders, regular files, 50 MiB cap) and must
|
|
113
|
+
be read before the handler returns, because every method rejects `invocation-ended` afterwards.
|
|
114
|
+
Order: `stat` (size against the adapter's `outboundLimits`, before reading), `readFile` with
|
|
115
|
+
`maxBytes` = the file limit, type from the bytes (`resolveMime`), then the caption line, then
|
|
116
|
+
`sendImage` (a picture within the image limit) or the optional adapter method `sendFile`. Audio
|
|
117
|
+
and video go as files. Lark uploads `file_type=stream` to `im/v1/files`; Telegram uses
|
|
118
|
+
`sendDocument` and falls back to it when `sendPhoto` answers 400. Failures return `isError` with
|
|
119
|
+
`errorCode` (`not-in-chat`, `no-workspace`, `outside-workspace`, `not-a-file`, `not-found`,
|
|
120
|
+
`empty`, `too-large`, `unsupported-platform`, `upload-failed`); the text for the agent is plain
|
|
121
|
+
English, only the caption goes through the catalog. The limits are Lark 10 MB image / 30 MB file
|
|
122
|
+
(Lark docs), WeCom 10 MB image (PNG, JPEG, GIF) / 20 MB file / at least 5 bytes (WeCom docs), and
|
|
123
|
+
Telegram 10 MB / 50 MB (from memory of the Bot API docs, not checked in this repository).
|
|
124
|
+
|
|
125
|
+
WeCom uploads over the existing long connection (`src/wecomUpload.ts`, `request` in
|
|
126
|
+
`src/wecomApi.ts`): `aibot_upload_media_init` (type, filename, total_size, total_chunks, md5) gives
|
|
127
|
+
an `upload_id`; `aibot_upload_media_chunk` sends each chunk of at most 512 KB as base64 (all chunks
|
|
128
|
+
in parallel up to 4, three at a time up to 10, else two; two retries per chunk after 500 ms and
|
|
129
|
+
1000 ms); `aibot_upload_media_finish` gives the `media_id`; then `aibot_send_msg` carries
|
|
130
|
+
`{ chatid, chat_type, msgtype: "image" | "file", <msgtype>: { media_id } }`. Every request waits for
|
|
131
|
+
its ack by `req_id` (15 s); an `errcode` other than 0 or a missing ack fails the upload, and nothing
|
|
132
|
+
is sent to the chat. `chat_type` (1 single, 2 group) comes from the last inbound message of that
|
|
133
|
+
conversation and is left out when none has arrived since the plugin started, as plain text does.
|
|
134
|
+
A picture goes as `image` only when it is PNG, JPEG or GIF within 10 MB (`imageMimeTypes`).
|
|
135
|
+
|
|
86
136
|
Chat commands: `/stop`, `/continue`, `/status`, `/new`, `/sessions`, `/resume`, `/model`,
|
|
87
137
|
`/plan`, `/team`, `/thread`, `/help`, `/panel`. `/sessions` and `/resume` only list and
|
|
88
138
|
switch threads this plugin opened, since the host lets a plugin send only into
|
|
@@ -107,7 +157,8 @@ interface language instead. The copy lives in `locales/en.json` and
|
|
|
107
157
|
|
|
108
158
|
## Requirements
|
|
109
159
|
|
|
110
|
-
A Ycodium server. The plugin asks to
|
|
160
|
+
A Ycodium server. The plugin asks to read files in the project of the conversation that
|
|
161
|
+
calls its tool (`workspace.read`, used only by `im_send_file`) and to dispatch `thread.create`,
|
|
111
162
|
`thread.meta.update`, `thread.turn.start`, `thread.turn.interrupt`,
|
|
112
163
|
`thread.approval.respond`, `thread.user-input.respond`, `thread.turn-intent.run`,
|
|
113
164
|
`thread.turn-intent.cancel` and `thread.turn-queue.resume`. Those commands only land
|