@rynx-ai/plugin-channel-lark 0.1.9

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.
Files changed (41) hide show
  1. package/dist/cli-mirror/mirror-index.d.ts +46 -0
  2. package/dist/cli-mirror/mirror-index.js +48 -0
  3. package/dist/cli-mirror/rollout-mirror.d.ts +85 -0
  4. package/dist/cli-mirror/rollout-mirror.js +333 -0
  5. package/dist/cli-mirror/rollout-watcher.d.ts +109 -0
  6. package/dist/cli-mirror/rollout-watcher.js +347 -0
  7. package/dist/host-adapters.d.ts +82 -0
  8. package/dist/host-adapters.js +404 -0
  9. package/dist/index.d.ts +7 -0
  10. package/dist/index.js +7 -0
  11. package/dist/lark-card-stream.d.ts +363 -0
  12. package/dist/lark-card-stream.js +1335 -0
  13. package/dist/lark-content.d.ts +30 -0
  14. package/dist/lark-content.js +138 -0
  15. package/dist/lark-conversation-directory.d.ts +65 -0
  16. package/dist/lark-conversation-directory.js +106 -0
  17. package/dist/lark-diff-card.d.ts +25 -0
  18. package/dist/lark-diff-card.js +58 -0
  19. package/dist/lark-fork-transcript.d.ts +16 -0
  20. package/dist/lark-fork-transcript.js +141 -0
  21. package/dist/lark-resume-card.d.ts +31 -0
  22. package/dist/lark-resume-card.js +105 -0
  23. package/dist/lark-sdk/client.d.ts +8 -0
  24. package/dist/lark-sdk/client.js +32 -0
  25. package/dist/lark-sdk/index.d.ts +1 -0
  26. package/dist/lark-sdk/index.js +1 -0
  27. package/dist/lark-sender.d.ts +114 -0
  28. package/dist/lark-sender.js +323 -0
  29. package/dist/lark-session-store.d.ts +13 -0
  30. package/dist/lark-session-store.js +14 -0
  31. package/dist/lark-settings-card.d.ts +55 -0
  32. package/dist/lark-settings-card.js +158 -0
  33. package/dist/lark-setup.d.ts +17 -0
  34. package/dist/lark-setup.js +86 -0
  35. package/dist/lark.d.ts +500 -0
  36. package/dist/lark.js +2010 -0
  37. package/dist/runtime.d.ts +16 -0
  38. package/dist/runtime.js +157 -0
  39. package/dist/runtime.mjs +130863 -0
  40. package/package.json +35 -0
  41. package/rynx-plugin.json +42 -0
package/dist/lark.d.ts ADDED
@@ -0,0 +1,500 @@
1
+ import { type CodexCapabilities } from "@rynx-ai/core";
2
+ import type { PluginAgentSummary, PluginExecutionDefaults, PluginResolvedSessionExecution, PluginSessionExecutionSnapshot } from "@rynx-ai/plugin-sdk";
3
+ import { chunkLarkReplyText, extractLarkTextPrompt, isLarkNewSessionCommand, isLarkStopCommand } from "./lark-content.js";
4
+ import { type LarkConversationDirectory } from "./lark-conversation-directory.js";
5
+ import { LarkClientProvider, LarkSdkMessageSender } from "./lark-sender.js";
6
+ import { type LarkActiveSessionStore } from "./lark-session-store.js";
7
+ import type { ConversationProvider, ConversationRuntime } from "@rynx-ai/core";
8
+ export { chunkLarkReplyText, extractLarkTextPrompt, isLarkNewSessionCommand, isLarkStopCommand, };
9
+ export { LarkClientProvider, LarkSdkMessageSender };
10
+ export interface LarkUserIdentifier {
11
+ union_id?: string;
12
+ user_id?: string;
13
+ open_id?: string;
14
+ }
15
+ export interface LarkMessageMention {
16
+ key: string;
17
+ id: LarkUserIdentifier;
18
+ name: string;
19
+ tenant_key?: string;
20
+ }
21
+ export interface LarkMessageReceiveEvent {
22
+ tenant_key?: string;
23
+ sender: {
24
+ sender_id?: LarkUserIdentifier;
25
+ sender_type: string;
26
+ tenant_key?: string;
27
+ };
28
+ message: {
29
+ message_id: string;
30
+ root_id?: string;
31
+ parent_id?: string;
32
+ create_time: string;
33
+ update_time?: string;
34
+ chat_id: string;
35
+ thread_id?: string;
36
+ chat_type: string;
37
+ message_type: string;
38
+ content: string;
39
+ mentions?: LarkMessageMention[];
40
+ user_agent?: string;
41
+ };
42
+ }
43
+ export interface LarkBotStatus {
44
+ connected: boolean;
45
+ mode: "ws";
46
+ last_event_at: string | null;
47
+ last_error: string | null;
48
+ }
49
+ export interface LarkBotRuntime {
50
+ start(): Promise<void>;
51
+ stop(): Promise<void>;
52
+ getStatus(): LarkBotStatus;
53
+ }
54
+ interface LarkLogEntry {
55
+ event: string;
56
+ tenant_key?: string;
57
+ chat_id?: string;
58
+ thread_id?: string;
59
+ message_id?: string;
60
+ session_key?: string;
61
+ session_id?: string;
62
+ previous_session_id?: string;
63
+ provider?: ConversationProvider;
64
+ model?: string;
65
+ error?: string;
66
+ emoji_type?: string;
67
+ reason?: string;
68
+ kind?: unknown;
69
+ source?: string;
70
+ command?: string;
71
+ reasoning_effort?: string | null;
72
+ rotated?: boolean;
73
+ }
74
+ interface LarkLogger {
75
+ log(entry: LarkLogEntry): void;
76
+ }
77
+ export declare class LarkConversationKeyResolver {
78
+ resolve(event: LarkMessageReceiveEvent): string;
79
+ }
80
+ export declare class PerThreadQueue {
81
+ private readonly tails;
82
+ enqueue(key: string, task: () => Promise<void>): Promise<void>;
83
+ }
84
+ export declare class LarkMessageDeduper {
85
+ private readonly ttlMs;
86
+ private readonly entries;
87
+ constructor(ttlMs?: number);
88
+ markIfNew(messageId: string, now?: number): boolean;
89
+ private prune;
90
+ }
91
+ export interface LarkMessageSender {
92
+ createText(args: {
93
+ chatId: string;
94
+ text: string;
95
+ uuid: string;
96
+ }): Promise<void>;
97
+ getMessage?(args: {
98
+ messageId: string;
99
+ }): Promise<LarkFetchedMessage | null>;
100
+ createReaction(args: {
101
+ messageId: string;
102
+ emojiType: string;
103
+ }): Promise<string | undefined>;
104
+ deleteReaction(args: {
105
+ messageId: string;
106
+ reactionId: string;
107
+ }): Promise<void>;
108
+ replyText(args: {
109
+ messageId: string;
110
+ text: string;
111
+ uuid: string;
112
+ replyInThread: boolean;
113
+ }): Promise<void>;
114
+ createCard?(args: {
115
+ cardJson: string;
116
+ }): Promise<{
117
+ cardId: string;
118
+ }>;
119
+ batchUpdateCard?(args: {
120
+ cardId: string;
121
+ sequence: number;
122
+ actions: string;
123
+ uuid?: string;
124
+ }): Promise<void>;
125
+ sendInteractive?(args: {
126
+ chatId: string;
127
+ cardId: string;
128
+ uuid: string;
129
+ }): Promise<{
130
+ messageId: string;
131
+ }>;
132
+ replyInteractive?(args: {
133
+ messageId: string;
134
+ cardId: string;
135
+ uuid: string;
136
+ replyInThread: boolean;
137
+ }): Promise<{
138
+ messageId: string;
139
+ }>;
140
+ forwardThread?(args: {
141
+ threadId: string;
142
+ receiveId: string;
143
+ receiveIdType: "chat_id" | "thread_id";
144
+ uuid: string;
145
+ }): Promise<void>;
146
+ sendText?(args: {
147
+ chatId: string;
148
+ text: string;
149
+ uuid: string;
150
+ }): Promise<{
151
+ messageId: string;
152
+ threadId?: string;
153
+ }>;
154
+ createChat?(args: {
155
+ name: string;
156
+ userIdList: string[];
157
+ uuid: string;
158
+ }): Promise<{
159
+ chatId: string;
160
+ }>;
161
+ getChat?(args: {
162
+ chatId: string;
163
+ }): Promise<{
164
+ name: string;
165
+ }>;
166
+ listChatMembers?(args: {
167
+ chatId: string;
168
+ }): Promise<Array<{
169
+ memberId: string;
170
+ name: string;
171
+ }>>;
172
+ listMessages?(args: {
173
+ containerIdType: "chat" | "thread";
174
+ containerId: string;
175
+ pageSize?: number;
176
+ }): Promise<Array<{
177
+ msgType: string;
178
+ senderId?: string;
179
+ senderType?: string;
180
+ content: string;
181
+ }>>;
182
+ }
183
+ export interface LarkFetchedMessage {
184
+ messageId?: string;
185
+ msgType?: string;
186
+ content?: string;
187
+ body?: {
188
+ content?: string;
189
+ };
190
+ }
191
+ export interface LarkCardActionEvent {
192
+ /** Whatever we put in the button's/select's `behaviors[0].value` payload. */
193
+ value: Record<string, unknown> | null;
194
+ /** Selected option value for a `select_static` change (Feishu `action.option`). */
195
+ option?: string | null;
196
+ /** Form field values for a `form_submit` button (Feishu `action.form_value`). */
197
+ formValue?: Record<string, unknown> | null;
198
+ /** open_message_id of the card carrying the control, if Lark surfaced it. */
199
+ messageId?: string;
200
+ }
201
+ /**
202
+ * Shape Feishu expects in response to a card.action.trigger callback. At
203
+ * minimum a `toast` is enough to acknowledge the click and surface UI
204
+ * feedback to the user without a partial card update.
205
+ */
206
+ export interface LarkCardActionResponse {
207
+ toast?: {
208
+ type: "info" | "success" | "warning" | "error";
209
+ content: string;
210
+ };
211
+ /**
212
+ * Replace the card that triggered the action, atomically, as part of the
213
+ * callback response. For raw schema-2.0 cards: `{ type: "raw", data: <card> }`.
214
+ * Preferred over an out-of-band `card.update` (which can briefly flash the new
215
+ * card and then revert to the original form).
216
+ */
217
+ card?: {
218
+ type: "raw";
219
+ data: Record<string, unknown>;
220
+ };
221
+ }
222
+ export interface LarkWsClientStartOptions {
223
+ onMessage: (event: LarkMessageReceiveEvent) => void;
224
+ onCardAction?: (event: LarkCardActionEvent) => LarkCardActionResponse | void | Promise<LarkCardActionResponse | void>;
225
+ }
226
+ export interface LarkWsClientProvider {
227
+ getMessageSender(): LarkMessageSender;
228
+ /**
229
+ * Resolve this bot's own `open_id` (via the bot info API). Used to tell, in a
230
+ * group with several bots, whether an incoming `@` actually targets us.
231
+ * Returns `null` when it cannot be determined.
232
+ */
233
+ getBotOpenId?(): Promise<string | null>;
234
+ start(options: LarkWsClientStartOptions): Promise<void>;
235
+ stop(): Promise<void>;
236
+ }
237
+ type LarkConversationRuntime = Pick<ConversationRuntime, "runLive">;
238
+ interface LarkMessageIngestorOptions {
239
+ conversationRuntime: LarkConversationRuntime;
240
+ messageSender: LarkMessageSender;
241
+ cwd?: string;
242
+ /** Default runtime for conversations without a `/runtime` choice. */
243
+ defaultRuntime?: ConversationProvider;
244
+ /** Default declarative agent for conversations without an `/agent` choice. */
245
+ defaultAgent?: string;
246
+ /** Host-backed agent catalog and first-turn execution resolver. */
247
+ agentCatalog?: LarkAgentCatalog;
248
+ activeSessionStore: LarkActiveSessionStore;
249
+ conversationKeyResolver?: LarkConversationKeyResolver;
250
+ deduper?: LarkMessageDeduper;
251
+ queue?: PerThreadQueue;
252
+ logger?: LarkLogger;
253
+ cardThrottleMs?: number;
254
+ /** Read-only Codex capabilities powering `/models` and `/search`. */
255
+ capabilities?: CodexCapabilities;
256
+ /** Conversation directory powering `/resume`. */
257
+ conversationDirectory?: LarkConversationDirectory;
258
+ /** Allowlist of roots a `/cwd` target must live under. */
259
+ allowedRoots?: string[];
260
+ /**
261
+ * This bot's own `open_id`. In a group hosting several bots, only a message
262
+ * that `@`-mentions this `open_id` triggers a run. When unset, any mention
263
+ * triggers (legacy single-bot behavior).
264
+ */
265
+ botOpenId?: string | null;
266
+ /** When true, private (p2p) chats are rejected — the bot is group-only. */
267
+ disableP2p?: boolean;
268
+ /** When true, only groups in `groupAllowlist` may use the bot. */
269
+ groupAllowlistEnabled?: boolean;
270
+ /** Group `chat_id`s allowed when `groupAllowlistEnabled` is true. */
271
+ groupAllowlist?: string[];
272
+ /** Optional CLI-takeover mirror; receives Codex-thread bindings and turn
273
+ * completions so it can mirror terminal-driven turns back into Feishu. */
274
+ mirror?: LarkCliMirror;
275
+ /** Host-RPC backed title updater for daemon-owned machine sessions. */
276
+ sessionTitleWriter?: (sessionId: string, title: string) => Promise<void>;
277
+ /** Host-RPC backed allocator used by `/fork` before binding the new chat. */
278
+ sessionProvisioner?: (input: {
279
+ sessionKey: string;
280
+ fromSessionId: string;
281
+ }) => Promise<string>;
282
+ }
283
+ export interface LarkAgentCatalog {
284
+ list(): Promise<PluginAgentSummary[]>;
285
+ get(id: string): Promise<PluginAgentSummary | null>;
286
+ resolveExecution(input: {
287
+ defaultAgent?: string;
288
+ session: PluginSessionExecutionSnapshot;
289
+ }): Promise<PluginResolvedSessionExecution>;
290
+ }
291
+ /**
292
+ * Collaborator that mirrors CLI-driven Codex turns back into Feishu. The
293
+ * ingestor only needs to (a) register a session's Codex thread as it becomes
294
+ * bound, and (b) let the mirror claim the lines the bridge itself wrote so they
295
+ * aren't mirrored twice.
296
+ */
297
+ export interface LarkCliMirror {
298
+ noteBoundSession(codexSessionId: string): Promise<void>;
299
+ claimToEof(codexSessionId: string): Promise<void>;
300
+ }
301
+ export declare class LarkMessageIngestor {
302
+ private readonly conversationRuntime;
303
+ private readonly messageSender;
304
+ /** Default runtime for this bot; per-conversation choices override it. */
305
+ private readonly provider;
306
+ /** Default declarative agent for this bot; per-conversation `/agent` overrides it. */
307
+ private readonly defaultAgent?;
308
+ /** Host-backed agent catalog for `/agent` and execution freezing. */
309
+ private readonly agentCatalog?;
310
+ /** Transient per-conversation settings-card selections, keyed by sessionKey. */
311
+ private readonly pendingSettings;
312
+ private readonly cwd?;
313
+ private readonly activeSessionStore;
314
+ private readonly conversationKeyResolver;
315
+ private readonly deduper;
316
+ private readonly queue;
317
+ private readonly logger;
318
+ private readonly cardThrottleMs;
319
+ private readonly capabilities?;
320
+ private readonly conversationDirectory;
321
+ private readonly allowedRoots;
322
+ private botOpenId;
323
+ private readonly disableP2p;
324
+ private readonly groupAllowlistEnabled;
325
+ private readonly groupAllowlist;
326
+ private readonly mirror?;
327
+ private readonly sessionTitleWriter?;
328
+ private readonly sessionProvisioner?;
329
+ private readonly inFlight;
330
+ constructor({ conversationRuntime, messageSender, cwd, defaultRuntime, defaultAgent, agentCatalog, activeSessionStore, conversationKeyResolver, deduper, queue, logger, cardThrottleMs, capabilities, conversationDirectory, allowedRoots, botOpenId, disableP2p, groupAllowlistEnabled, groupAllowlist, mirror, sessionTitleWriter, sessionProvisioner, }: LarkMessageIngestorOptions);
331
+ /** Best-effort title update for the daemon-owned machine-session record. */
332
+ private registerSession;
333
+ /**
334
+ * Set this bot's own `open_id` once it has been resolved asynchronously
335
+ * (the runtime fetches it from the bot info API at startup).
336
+ */
337
+ setBotOpenId(openId: string | null): void;
338
+ /** Whether a bridge-driven turn is currently running for `sessionKey`. The
339
+ * CLI mirror reads this to skip turns already shown live in Feishu. */
340
+ isInFlight(sessionKey: string): boolean;
341
+ /**
342
+ * Whether a group message is addressed to *this* bot. With a known
343
+ * `botOpenId`, that means one of the `@`-mentions carries our `open_id` —
344
+ * so in a group hosting several bots, only the one actually mentioned runs.
345
+ * When `botOpenId` is unknown, fall back to "any mention" (legacy single-bot
346
+ * behavior) rather than going silent.
347
+ */
348
+ private isAddressedToBot;
349
+ /**
350
+ * Handle a Feishu CardKit button click. Supported actions:
351
+ * - `{ kind: "stop_turn", sessionKey }` — the in-card "停止" button.
352
+ * - `{ kind: "resume_forward", threadId, chatId }` — forward a topic thread
353
+ * into `chatId` (from the `/resume` card).
354
+ * Anything else is logged and ignored.
355
+ */
356
+ ingestCardAction(event: LarkCardActionEvent): Promise<LarkCardActionResponse>;
357
+ private handleResumeForwardAction;
358
+ /** Where a forward should land for the conversation `event` arrived in:
359
+ * inside the current topic thread when present, else the chat. */
360
+ private forwardTargetOf;
361
+ /**
362
+ * Cancel the in-flight turn for `sessionKey`, if any. Returns true if a
363
+ * turn was actually aborted. Safe to call from outside the per-session
364
+ * queue (that's the whole point — the queue is serialized but interrupt
365
+ * must bypass it).
366
+ */
367
+ interruptSession(sessionKey: string): boolean;
368
+ private registerInFlight;
369
+ private clearInFlight;
370
+ ingest(event: LarkMessageReceiveEvent): Promise<void>;
371
+ private handleStopCommand;
372
+ private handleInfoCommand;
373
+ private handleActionCommand;
374
+ /**
375
+ * Render the working-tree diff as a CardKit card (summary + per-file stats +
376
+ * a collapsed full diff). Falls back to a plain-text reply when the sender
377
+ * has no card capability or the card send fails.
378
+ */
379
+ private handleDiffCommand;
380
+ private collectGitDiff;
381
+ private renderGoalReply;
382
+ private capabilityErrorText;
383
+ private conversationKindOf;
384
+ private recordConversation;
385
+ private handleCwdCommand;
386
+ private handleResumeCommand;
387
+ private resumeList;
388
+ private resumeByShortId;
389
+ private sendStandaloneCard;
390
+ private handleForkCommand;
391
+ /**
392
+ * Send the `/runtime` (or `/model`) settings card and seed the pending
393
+ * selection state with the conversation's current settings.
394
+ */
395
+ private handleSettingsCommand;
396
+ /**
397
+ * `/agent` — list declarative agents, or select one for this conversation.
398
+ * Selecting rotates to a fresh session (so sandbox/persona/tools don't change
399
+ * mid-thread) and drops explicit runtime/model overrides so the agent drives them.
400
+ */
401
+ private handleAgentCommand;
402
+ /**
403
+ * Label shown for the model in the turn card. An empty model means the
404
+ * runtime follows its own configured default (e.g. traex with no
405
+ * `TRAEX_MODEL`); show a placeholder so the status row isn't blank (the
406
+ * runtime itself is shown separately).
407
+ */
408
+ private displayModel;
409
+ private renderSettingsSummary;
410
+ /** Assemble the settings-card input from the current pending selection. */
411
+ private settingsCardInput;
412
+ private fetchModelOptions;
413
+ /**
414
+ * Handle a settings-card dropdown change. A runtime change re-scopes the
415
+ * model list (and resets model/effort); a model change re-scopes the effort
416
+ * list — both re-render the card in place via the callback response so the
417
+ * dependent dropdowns update. Effort changes just update pending state.
418
+ */
419
+ private handleSettingsSelect;
420
+ /** Card-action response that re-renders the open settings card in place. */
421
+ private settingsCardResponse;
422
+ /** Apply the pending settings-card selections to the conversation. */
423
+ private handleSettingsApply;
424
+ /** Cancel an open settings card without changing anything. */
425
+ private handleSettingsCancel;
426
+ /**
427
+ * Build a card-action response that swaps the settings card for a static
428
+ * result card. Returning the card in the callback response replaces it
429
+ * atomically; an out-of-band `card.update` would briefly show the result and
430
+ * then revert to the form.
431
+ */
432
+ private settingsResultCard;
433
+ private renderModelsReply;
434
+ private handleNewSessionCommand;
435
+ private logMessageReceived;
436
+ private tryCreateReaction;
437
+ private tryDeleteReaction;
438
+ /**
439
+ * If this turn replies to a Lark message card, fetch the parent card and wrap
440
+ * the prompt so the agent receives the card content as context. Generic: it
441
+ * forwards any interactive (card) parent and names no specific skill — the
442
+ * agent decides how to act on the card. Returns null when the parent is not a
443
+ * card (or cannot be fetched), so the caller falls back to the raw prompt.
444
+ */
445
+ private tryBuildCardReplyPrompt;
446
+ /**
447
+ * Map a session to the {@link TurnSettings} a turn runs with. Already-frozen
448
+ * sessions are resolved locally; first-turn defaults and named-agent lookup
449
+ * are delegated to the capability-gated host and then persisted exactly once.
450
+ */
451
+ private resolveTurnExecution;
452
+ private buildReply;
453
+ private canUseCardSender;
454
+ private tryBuildReplyWithCard;
455
+ /**
456
+ * On the turn's first `session.status` carrying a runtime session id, bind it
457
+ * to the in-flight turn and register it with the CLI mirror (so the mirror
458
+ * watches that rollout file, and the bridge's own lines are later claimed).
459
+ */
460
+ private noteRuntimeSession;
461
+ private buildReplyDrain;
462
+ private sendReplyText;
463
+ }
464
+ interface LocalLarkBotRuntimeOptions {
465
+ /** Host-computed defaults carried on the explicit contribution start request. */
466
+ executionDefaults?: PluginExecutionDefaults;
467
+ /** Capability-gated host view of agent metadata and execution resolution. */
468
+ agentCatalog?: LarkAgentCatalog;
469
+ conversationRuntime: LarkConversationRuntime;
470
+ clientProvider?: LarkWsClientProvider;
471
+ messageIngestor?: LarkMessageIngestor;
472
+ logger?: LarkLogger;
473
+ /** Read-only agent capabilities for `/models` and `/search`. */
474
+ capabilities?: CodexCapabilities;
475
+ /** Per-instance options (credentials + knobs) overriding global config. */
476
+ options?: Record<string, unknown>;
477
+ /** Per-instance default declarative agent. */
478
+ defaultAgent?: string;
479
+ /** Explicit plugin-scoped state files. Runtime entries always supply both. */
480
+ activeSessionStore?: LarkActiveSessionStore;
481
+ conversationDirectory?: LarkConversationDirectory;
482
+ /** Host RPC seams; no daemon object crosses into this process. */
483
+ sessionTitleWriter?: (sessionId: string, title: string) => Promise<void>;
484
+ sessionProvisioner?: (input: {
485
+ sessionKey: string;
486
+ fromSessionId: string;
487
+ }) => Promise<string>;
488
+ }
489
+ export declare class LocalLarkBotRuntime implements LarkBotRuntime {
490
+ private readonly clientProvider?;
491
+ private readonly messageIngestor?;
492
+ private readonly logger;
493
+ private connected;
494
+ private lastEventAt;
495
+ private lastError;
496
+ constructor({ executionDefaults, agentCatalog, conversationRuntime, options, defaultAgent, logger, clientProvider, messageIngestor, capabilities, activeSessionStore: injectedActiveSessionStore, conversationDirectory: injectedConversationDirectory, sessionTitleWriter, sessionProvisioner, }: LocalLarkBotRuntimeOptions);
497
+ getStatus(): LarkBotStatus;
498
+ start(): Promise<void>;
499
+ stop(): Promise<void>;
500
+ }