@ycodium-ai/plugin-im 0.2.2678 → 0.2.2694

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 CHANGED
@@ -1,25 +1,43 @@
1
1
  # @ycodium-ai/plugin-im
2
2
 
3
- Talk to your agents from Telegram, WeCom, or Lark. Mention the bot in a group
4
- and it starts a turn on this machine; the conclusion comes back to the group,
5
- and anything the agent needs approved lands there too.
3
+ Talk to your agents from Telegram, WeCom, or Lark. Message the bot (or mention
4
+ it in a group) and it starts a turn on this machine; the conclusion comes back
5
+ to the chat, and anything the agent needs approved lands there too.
6
6
 
7
7
  ## What it does
8
8
 
9
- Each settings-card row is one group:
10
-
11
- - Pick a project and that group can start work there, and only hears that project.
12
- - Leave the project empty and the row only reports, on every project.
13
- - Archived projects are read-only. The project picker hides them unless a row is
14
- already bound to one (shown as "(archived)"), and a message to a group bound
15
- to an archived project gets a one-line reply that the project is archived
16
- instead of starting work, at most once every ten minutes per group. Chat
17
- commands such as help and status still work.
18
- Restore the project to resume.
9
+ Each settings-card row is one bot (platform, credentials, who may use it); the
10
+ chats it talks in (private chats, groups) are not configured, they pick their own
11
+ project:
12
+
13
+ - **Allowed people** is an allow-list of that platform's user ids. Empty means
14
+ nobody can make the agent work. Someone who is not on it gets one reply with
15
+ their own user id and where to put it, at most once every ten minutes per
16
+ person (the table of who was told is bounded in memory).
17
+ - A chat with no project starts nothing. The first message from an allowed
18
+ person gets a project picker (a card on Lark, an inline keyboard on Telegram,
19
+ a numbered text list on WeCom, which has no buttons: reply with the number or
20
+ `/repo <number>`). The pick is written as a plugin fact and survives restarts.
21
+ `/repo` switches the project: the old thread is kept (still under that
22
+ project's `/sessions`), the chat forgets it, and a running turn blocks the
23
+ switch. The panel has a Pick project button.
24
+ - A chat only hears the project it picked: one conclusion per finished turn and
25
+ thread failures go to the chats that picked that project; a chat with no
26
+ project hears nothing.
27
+ - `/group <name>` (Lark only): the bot creates a group, adds the sender as owner
28
+ and posts the project picker in it. Telegram and WeCom bots cannot create
29
+ groups and answer so.
30
+ - **In groups, answer** (per bot): only when mentioned (default), every message,
31
+ or every message except ones aimed at someone else. WeCom only delivers
32
+ mentions, so it has no effect there. Under the default, a group that holds only the
33
+ sender and the bot needs no mention (`soloGroup.ts`; Lark `im.v1.chat.get`, Telegram
34
+ `getChatMemberCount`, answer cached five minutes; WeCom unsupported).
35
+ - Archived projects are read-only and are not offered by the picker. A message
36
+ to a chat whose project was archived or deleted gets a one-line reply that
37
+ says so and points at `/repo`, at most once every ten minutes per chat. Chat
38
+ commands such as help and status still work. Restore the project to resume.
19
39
  - Secrets (bot tokens, app credentials) stay in the environment secret store.
20
40
  The patch file keeps a reference, not the value.
21
- - Who may command the bot is an allow-list of that platform's user ids. Empty
22
- means anyone in the group can make the agent run things on this machine.
23
41
  - A row can pick its own proxy. On a given machine one platform may be
24
42
  unreachable while another is fine.
25
43
 
@@ -62,7 +80,7 @@ optional `choiceCards` surface (`ImChoiceCard`, `src/adapter.ts`): Lark patches
62
80
  card, Telegram edits a message and its inline keyboard. A request is posted on
63
81
  `thread.user-input-pending` (read with `listPendingUserInputs`), one card per
64
82
  question, and answered with one `thread.user-input.respond` once every question
65
- has an answer; a free-form answer is the next ordinary message in the group, and
83
+ has an answer; a free-form answer is the next ordinary message in the chat, and
66
84
  `thread.request-resolved` freezes the cards of a question answered elsewhere or
67
85
  closed unanswered. WeCom has no buttons: it writes the options as numbers and the
68
86
  reply is parsed in `src/questionCards.ts`. A message that lands behind a running
@@ -75,39 +93,41 @@ and `thread.turn-queue.resume`, and the card is the user's own action, so no
75
93
  and withdrawing need buttons (resuming is the `/continue` command). Editing a queued message is not offered:
76
94
  the host does not let a plugin dispatch `thread.turn-intent.edit`.
77
95
 
78
- A group keeps its place across restarts. Which thread a group is talking in, the
79
- team it is pointed at, the model and plan mode set from the group, the threads
80
- this plugin has opened, and the approval, question and queued-message buttons still
81
- waiting in the group are
96
+ A chat keeps its place across restarts. Which project a chat picked, which thread
97
+ it is talking in, the team it is pointed at, the model and plan mode set from the
98
+ chat, the threads this plugin has opened, and the approval, question and
99
+ queued-message buttons still waiting in the chat are
82
100
  written as plugin facts (`ctx.events.append`) and folded back by a pure function
83
101
  (`ctx.derivedState`, see `src/chatState.ts`). A saved thread that was deleted,
84
- archived or moved to another project is dropped on the next message.
102
+ archived or no longer on the chat's project is dropped on the next message.
103
+ The chat key is `<bot row id>:<chat id>`: two bots in the same person's private chat share
104
+ the chat id, so the key has to carry the bot.
85
105
 
86
106
  Inbound attachments. An adapter reports a message's resources as `resources`
87
107
  (`ImInboundResource`, `src/adapter.ts`) and implements `fetchResource`, which must stop reading
88
108
  at `maxBytes` (`readBodyLimited`, `src/attachments.ts`) and throw `ImResourceTooLargeError`.
89
109
  An adapter whose platform caps bot downloads tighter than the host sets `downloadLimitBytes`
90
110
  (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
111
+ core (`collectResources` in `src/inboundAttachments.ts`) cleans the name (`sanitizeAttachmentName`: the host
92
112
  splices it into the `[Attached … "<name>" …]` line the agent reads, so newlines and double
93
113
  quotes must not survive), takes the type from the bytes (`resolveMime`: platforms self-report,
94
114
  Lark says PNG for every image), checks the host's limits (10 MiB image, 25 MiB other, 20 per
95
115
  turn, mirrored from the host because a plugin cannot import it) and stores each one with
96
116
  `ctx.attachments.store`; an image the host can inline reaches the agent as an image, anything
97
117
  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
118
+ A message with attachments and no text starts no turn: the thread is created if the chat has
99
119
  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
120
+ the next message with text carries them. Waiting attachments are dropped when the chat's
101
121
  thread changes and are not persisted. Lark does not require a mention for a message that is only
102
122
  a resource (`RESOURCE_MESSAGE_TYPES` in `src/larkInbound.ts`); the sender allow-list still
103
123
  applies. Lark rich text is read by `src/larkPost.ts` (every image, `<image N>` placeholders in
104
124
  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
125
+ `unsupportedType` and answered with one line, rate-limited per chat. A chat pointed at a team
106
126
  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
127
 
108
128
  Outbound files. The plugin contributes one MCP tool, `im_send_file` (`src/sendFile.ts`;
109
129
  `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
130
+ to the chat whose thread is the calling conversation (`chatOf(...).threadId`), never by
111
131
  project, and fails with `not-in-chat` otherwise. The bytes come from `invocation.workspace`
112
132
  (`workspace.read`: realpath-bounded to the thread's folders, regular files, 50 MiB cap) and must
113
133
  be read before the handler returns, because every method rejects `invocation-ended` afterwards.
@@ -134,9 +154,10 @@ conversation and is left out when none has arrived since the plugin started, as
134
154
  A picture goes as `image` only when it is PNG, JPEG or GIF within 10 MB (`imageMimeTypes`).
135
155
 
136
156
  Chat commands: `/stop`, `/continue`, `/status`, `/new`, `/sessions`, `/resume`, `/model`,
137
- `/plan`, `/team`, `/thread`, `/help`, `/panel`. `/sessions` and `/resume` only list and
157
+ `/plan`, `/repo`, `/group`, `/team`, `/thread`, `/help`, `/panel`. In a chat with no project
158
+ `/sessions`, `/resume` and the team commands answer with the project picker. `/sessions` and `/resume` only list and
138
159
  switch threads this plugin opened, since the host lets a plugin send only into
139
- its own threads. `/continue` dispatches `thread.turn-queue.resume` for the group's thread
160
+ its own threads. `/continue` dispatches `thread.turn-queue.resume` for the chat's thread
140
161
  when its queue is paused (a `/stop` or a failure pauses it), which is how WeCom, with
141
162
  no buttons, resumes; it is not `/resume`, which already switches threads.
142
163
 
@@ -147,7 +168,7 @@ a short `pn:<version>:<index>` token (`callback_data` is capped at 64 bytes);
147
168
  the action stays in memory and a tap on an older keyboard version counts as expired. Each card keeps its own view
148
169
  stack in memory (`src/panelController.ts`), so after a restart an old card only
149
170
  answers that it has expired. A tap runs the same handler as the typed command and
150
- waits in the same per-group queue as typed messages. Another platform opts in by
171
+ waits in the same per-chat queue as typed messages. Another platform opts in by
151
172
  implementing the optional `panel` surface on its adapter (`src/adapter.ts`).
152
173
 
153
174
  The **Reply language** setting (Chinese by default, or English) picks the
@@ -173,18 +194,33 @@ ycodium plugin add @ycodium-ai/plugin-im
173
194
  The plugin ships enabled with Ycodium as `core.im`. Disabling it closes every
174
195
  chat connection. Uninstalling it writes a removal onto the home patch, so a
175
196
  restart or upgrade does not bring it back. Reinstalling the unversioned bundled
176
- package restores the same entry without an npm fetch. Group configuration and
197
+ package restores the same entry without an npm fetch. Bot configuration and
177
198
  secrets stay until you clear them.
178
199
 
179
200
  ## Surfaces
180
201
 
181
- - **Web / Desktop** — Settings → Plugins → Chat. The groups table is the
202
+ - **Web / Desktop** — Settings → Plugins → Chat. The bots table is the
182
203
  settings card this package registers.
183
204
  - **Mobile** — the same settings card, without platform option icons (mobile
184
205
  has no plugin icon pipeline).
185
206
  - Chat replies themselves go to Telegram, WeCom, or Lark, not into a Ycodium
186
207
  page.
187
208
 
209
+ ## Source layout
210
+
211
+ `src/index.ts` only assembles: it builds the adapters, registers the settings card, builds the
212
+ shared core and wires the feature modules together. Everything else is one module per
213
+ responsibility, each receiving a small `core` object (a few host capabilities, the reply
214
+ language, the saved-state mirror) plus an explicit `deps` object, never the whole `ctx`:
215
+
216
+ - `botConfig` (settings card, reading bots, chat keys, allow-list), `core` (state mirror and
217
+ persistence, delivery, bot and chat lookup), `connections` (one long connection per bot)
218
+ - `inbound` (per-bot message handlers), `chatQueue` (per-chat serial queue), `commands`,
219
+ `chatSetup` (picker, `/repo`, `/group`), `projectRows`/`projectList` (the project list),
220
+ `sessions`, `modelPlan`, `turns`, `inboundAttachments`, `teamRuns`
221
+ - `approvals`, `interactions` (question and queue cards, button routing), `replyCards`,
222
+ `notifications`, `acknowledgements`, `panelWiring` (with `panelController`)
223
+
188
224
  ## Developing
189
225
 
190
226
  The package builds its own code and the Lark SDK into `dist/index.js`. The host