eve-lark 0.5.0 → 0.5.1

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/dist/index.js CHANGED
@@ -108,8 +108,10 @@ var LarkClient = class {
108
108
  async sendText(args) {
109
109
  const content = JSON.stringify({ text: args.content });
110
110
  if (args.rootId) {
111
+ console.log(`[eve-lark] sendText \u2192 reply rootId=${args.rootId}`);
111
112
  return this.#replyMessage(args.rootId, "text", content);
112
113
  }
114
+ console.log(`[eve-lark] sendText \u2192 send chatId=${args.chatId}`);
113
115
  return this.#sendMessage({
114
116
  receive_id: args.chatId,
115
117
  msg_type: "text",
@@ -119,8 +121,10 @@ var LarkClient = class {
119
121
  async sendCard(args) {
120
122
  const content = JSON.stringify(args.card);
121
123
  if (args.rootId) {
124
+ console.log(`[eve-lark] sendCard \u2192 reply rootId=${args.rootId}`);
122
125
  return this.#replyMessage(args.rootId, "interactive", content);
123
126
  }
127
+ console.log(`[eve-lark] sendCard \u2192 send chatId=${args.chatId}`);
124
128
  return this.#sendMessage({
125
129
  receive_id: args.chatId,
126
130
  msg_type: "interactive",
@@ -151,6 +155,7 @@ var LarkClient = class {
151
155
  const path = `/open-apis/im/v1/messages/${encodeURIComponent(replyToMessageId)}/reply`;
152
156
  const json = await this.#request("POST", path, { msg_type: msgType, content });
153
157
  const messageId = json.data?.message_id;
158
+ console.log(`[eve-lark] reply API msgType=${msgType} replyTo=${replyToMessageId} \u2192 messageId=${messageId ?? "?"}`);
154
159
  if (!messageId) {
155
160
  throw new LarkApiError("eve-lark: reply missing message_id in response", {
156
161
  body: json
@@ -829,6 +834,7 @@ var StreamingCardController = class {
829
834
  createTimer = null;
830
835
  patchTimer = null;
831
836
  patchInFlight = null;
837
+ createInFlight = null;
832
838
  patchScheduled = false;
833
839
  lastPatchAt = 0;
834
840
  constructor(client, deps) {
@@ -986,6 +992,13 @@ var StreamingCardController = class {
986
992
  this.cancelCreateTimer();
987
993
  this.cancelPatchTimer();
988
994
  this.buffer = fullText;
995
+ if (this.createInFlight) {
996
+ try {
997
+ await this.createInFlight;
998
+ } catch {
999
+ }
1000
+ this.createInFlight = null;
1001
+ }
989
1002
  if (this.fallbackToText) {
990
1003
  await this.client.sendText({
991
1004
  chatId: this.deps.chatId,
@@ -1065,7 +1078,9 @@ var StreamingCardController = class {
1065
1078
  const rootIdSnapshot = this.deps.rootId;
1066
1079
  this.createTimer = setTimeout(() => {
1067
1080
  this.createTimer = null;
1068
- void this.doCreate(rootIdSnapshot);
1081
+ this.createInFlight = this.doCreate(rootIdSnapshot).then(() => {
1082
+ this.createInFlight = null;
1083
+ });
1069
1084
  }, this.deps.createThresholdMs);
1070
1085
  }
1071
1086
  cancelCreateTimer() {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/channel.ts","../src/errors.ts","../src/lark-client.ts","../src/dedup.ts","../src/crypto.ts","../src/parse.ts","../src/card.ts","../src/streaming-controller.ts","../src/options.ts","../src/long-connection.ts","../src/feishu-emoji.ts"],"sourcesContent":["import {\n defineChannel,\n POST,\n type Channel,\n type RouteHandlerArgs,\n} from \"eve/channels\";\n\nimport { LarkClient } from \"./lark-client.js\";\nimport { DedupMap } from \"./dedup.js\";\nimport { decryptPayload, verifySignature } from \"./crypto.js\";\nimport { parseInbound } from \"./parse.js\";\nimport { StreamingCardController } from \"./streaming-controller.js\";\nimport {\n ASK_BUTTON_VALUE_MARKER,\n buildAskAnsweredCard,\n buildAskCard,\n buildAuthCard,\n buildAuthCompletedCard,\n buildTextCard,\n} from \"./card.js\";\nimport { resolveOptions } from \"./options.js\";\nimport { isEveStartLauncher, startLongConnection } from \"./long-connection.js\";\nimport { isValidFeishuEmojiType } from \"./feishu-emoji.js\";\nimport type {\n LarkCardActionTriggerEvent,\n LarkChannelOptions,\n LarkContinuationToken,\n LarkEncryptedBody,\n LarkEventBody,\n LarkInboundEvent,\n LarkInboundFile,\n LarkInputRequest,\n LarkInputResponse,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\n/** Hard cap on inbound webhook body size. Feishu payloads are <10 KB; this\n * is purely defense against a malicious or buggy peer OOMing the process. */\nconst MAX_BODY_BYTES = 1_000_000;\n\n/** Drop a session's controller if it's been inactive this long. Bounds the\n * closure-scoped `controllers`/`sessionMeta` Maps against crashes that\n * prevent `message.completed`/`turn.failed` from firing. */\nconst STALE_SESSION_MS = 30 * 60 * 1000;\n\n/** How often to sweep stale controllers. */\nconst SWEEP_INTERVAL_MS = 5 * 60 * 1000;\n\n/** Reply text used when the model returns null/empty — guarantees the user\n * always sees *something* so they're not left looking at a typing emoji. */\nconst EMPTY_REPLY_TEXT = \"(model returned no content)\";\n\n/**\n * Continuation token format: `${chatId}:${rootMessageId ?? \"_\"}`.\n * The framework prepends the channel file stem before handing the token to\n * the runtime; consumers should call this helper rather than concatenate.\n */\nexport function larkContinuationToken(\n chatId: string,\n rootMessageId: string | null,\n): LarkContinuationToken {\n return `${chatId}:${rootMessageId ?? \"_\"}` as LarkContinuationToken;\n}\n\ninterface LarkSessionMeta {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n /** When the controller was last touched. Used by the stale-sweep. */\n touchedAt: number;\n}\n\ninterface ResolvedSessionInfo {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n messageId?: string | undefined;\n}\n\n/**\n * Extract chat + message metadata stashed on `auth.initiator.attributes` at\n * session start. This is the canonical place to read it: closure state\n * doesn't reliably cross eve's process/worker boundary, but auth attributes\n * are persisted with the session.\n */\nfunction sessionInfoFromCtx(ctx: { session?: { auth?: { initiator?: { attributes?: unknown } | null } | null } | null }): ResolvedSessionInfo | null {\n const attrs = (ctx.session?.auth?.initiator?.attributes ?? {}) as {\n chatId?: unknown;\n rootMessageId?: unknown;\n parentId?: unknown;\n messageId?: unknown;\n };\n if (typeof attrs.chatId !== \"string\" || !attrs.chatId) return null;\n return {\n chatId: attrs.chatId,\n rootId: typeof attrs.rootMessageId === \"string\" ? attrs.rootMessageId : undefined,\n parentId: typeof attrs.parentId === \"string\" ? attrs.parentId : undefined,\n messageId: typeof attrs.messageId === \"string\" ? attrs.messageId : undefined,\n };\n}\n\nfunction ackOk(): Response {\n return Response.json({ code: 0 });\n}\n\n/**\n * Resolve the configured `ackReaction` to a single valid emoji type for this\n * event, or `false` if reactions are disabled (or the configured value is\n * invalid). Picks randomly when given an array.\n *\n * Validates against {@link VALID_FEISHU_EMOJI_TYPES} because Feishu rejects\n * unknown emoji types with HTTP 400 code=231001. The validation is\n * case-sensitive — `TYPING` is invalid but `Typing` is. Without this check a\n * typo in the default or a user-supplied value fails silently on every\n * inbound message.\n */\nfunction pickAckEmoji(reaction: string | readonly string[] | false): string | false {\n if (reaction === false) return false;\n if (typeof reaction === \"string\") {\n if (!isValidFeishuEmojiType(reaction)) {\n console.warn(\n `[eve-lark] ackReaction \"${reaction}\" is not a valid Feishu emoji type ` +\n `(case-sensitive; e.g. \"Typing\" not \"TYPING\"). Skipping ack reaction. ` +\n `See VALID_FEISHU_EMOJI_TYPES for the full list.`,\n );\n return false;\n }\n return reaction;\n }\n if (Array.isArray(reaction)) {\n const valid = reaction.filter(isValidFeishuEmojiType);\n if (valid.length === 0) {\n const sample = reaction.slice(0, 3).join(\", \");\n console.warn(\n `[eve-lark] ackReaction array contains no valid Feishu emoji types ` +\n `(got [${sample}${reaction.length > 3 ? \", …\" : \"\"}]). Skipping ack reaction.`,\n );\n return false;\n }\n if (valid.length < reaction.length) {\n const dropped = reaction.filter((e) => !isValidFeishuEmojiType(e));\n console.warn(\n `[eve-lark] ackReaction array dropped ${dropped.length} invalid emoji type(s): ` +\n `${dropped.slice(0, 3).join(\", \")}`,\n );\n }\n const idx = Math.floor(Math.random() * valid.length);\n return valid[idx] ?? false;\n }\n return false;\n}\n\nfunction resourceUrl(\n options: ResolvedLarkOptions,\n file: LarkInboundFile,\n messageId: string,\n): string {\n const type = file.kind === \"image\" ? \"image\" : \"file\";\n return `${options.baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/resources/${encodeURIComponent(file.fileKey)}?type=${type}`;\n}\n\n/**\n * Build the eve UserContent payload from a parsed inbound event. Text comes\n * first; each inbound image/file becomes a `file` part carrying a URL pointing\n * at the Lark resource endpoint. The channel's `fetchFile` hook will stage\n * those URLs to bytes when the model runs.\n */\nfunction buildUserContent(\n text: string,\n files: LarkInboundFile[],\n options: ResolvedLarkOptions,\n messageId: string,\n): unknown[] {\n const parts: unknown[] = [];\n if (text.length > 0) parts.push({ type: \"text\", text });\n for (const f of files) {\n parts.push({\n type: \"file\",\n data: new URL(resourceUrl(options, f, messageId)),\n mediaType: f.mediaType,\n });\n }\n return parts;\n}\n\n/**\n * Run a diagnostic check and reply with the results in the given chat.\n * Tests config validity, fetches a tenant_access_token, and reports.\n */\nasync function runDiagnostics(\n client: LarkClient,\n opts: ResolvedLarkOptions,\n chatId: string,\n): Promise<void> {\n const lines: string[] = [\"**eve-lark diagnostics**\", \"\"];\n lines.push(`appId: \\`${opts.appId}\\``);\n lines.push(`baseUrl: \\`${opts.baseUrl}\\``);\n lines.push(`mode: \\`${opts.mode}\\``);\n lines.push(`replyMode: \\`${opts.replyMode}\\``);\n lines.push(`encryptKey: ${opts.encryptKey ? \"✓ set\" : \"✗ not set\"}`);\n lines.push(`ackReaction: \\`${opts.ackReaction === false ? \"disabled\" : opts.ackReaction}\\``);\n\n lines.push(\"\");\n lines.push(\"**Token fetch:**\");\n try {\n const token = await client.getTenantAccessToken();\n lines.push(`✓ tenant_access_token: ${token.slice(0, 8)}…`);\n } catch (e) {\n lines.push(`✗ failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n\n const report = lines.join(\"\\n\");\n try {\n await client.sendPost({ chatId, content: report });\n } catch (e) {\n console.error(\"[eve-lark] diagnostic report delivery failed:\", e);\n }\n}\n\n/**\n * Port of eve's `formatErrorHint` from `#internal/logging.js`.\n *\n * Builds a ` (name: message)` hint from a turn.failed/session.failed event's\n * data payload. Reads `data.details.name` (error class, e.g. \"AI_APICallError\")\n * and `data.message` (the actual reason, e.g. a rate-limit string). Both are\n * optional; the hint is empty when neither is present so callers can\n * interpolate unconditionally: `` `I hit an error${hint}.` ``\n *\n * Truncated to 160 chars (matching eve) to keep one-line Feishu replies\n * readable.\n */\nfunction formatErrorHint(data: unknown): string {\n if (typeof data !== \"object\" || data === null) return \"\";\n const d = data as { details?: unknown; message?: unknown };\n const detailsName =\n typeof d.details === \"object\" && d.details !== null\n ? (d.details as { name?: unknown }).name\n : undefined;\n const name =\n typeof detailsName === \"string\" && detailsName.length > 0 ? detailsName : undefined;\n const message = typeof d.message === \"string\" ? d.message.trim() : \"\";\n if (name && message.length > 0) return ` (${name}: ${truncateForDisplay(message)})`;\n if (name) return ` (${name})`;\n if (message.length > 0) return ` (${truncateForDisplay(message)})`;\n return \"\";\n}\n\n/**\n * Port of eve's `extractErrorId`. Reads `data.details.errorId` if present —\n * a UUID users can quote to support. Returns undefined when absent.\n */\nfunction extractErrorId(details: unknown): string | undefined {\n if (typeof details === \"object\" && details !== null) {\n const id = (details as { errorId?: unknown }).errorId;\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n }\n return undefined;\n}\n\nfunction truncateForDisplay(s: string, max = 160): string {\n return s.length <= max ? s : `${s.slice(0, max - 1).trimEnd()}…`;\n}\n\n/**\n * Compose a user-facing error message from a turn.failed/session.failed\n * event. Mirrors eve's official channel output:\n *\n * `⚠ I hit an error while handling your request (AI_APICallError: <reason>). (Error id: <uuid>)`\n *\n * Always returns a non-empty string. If `data` has no useful info, falls back\n * to `⚠ <fallbackReason>` (e.g. \"turn failed\").\n */\nfunction formatFailureMessage(\n data: unknown,\n fallback: string,\n opts: { sentence: \"turn\" | \"session\" } = { sentence: \"turn\" },\n): string {\n const hint = formatErrorHint(data);\n const errorId = extractErrorId((data as { details?: unknown } | null)?.details);\n const lead =\n opts.sentence === \"session\"\n ? \"This session couldn't recover from an error\"\n : \"I hit an error while handling your request\";\n const idSuffix = errorId ? ` (Error id: ${errorId})` : \"\";\n // If we have neither hint nor errorId, prefer the explicit fallback so\n // users get \"turn failed\" rather than the same string every time. With\n // hint or errorId present, the lead-in alone is informative enough.\n if (!hint && !errorId) return `⚠ ${fallback}`;\n return `⚠ ${lead}${hint}.${idSuffix}`;\n}\n\n/**\n * Create a Lark/Feishu channel for the eve agent framework.\n *\n * The channel mounts a single POST webhook that verifies the request,\n * decrypts the body when an encrypt key is configured, deduplicates events\n * by id, parses the inbound message, and starts or resumes an eve session.\n *\n * Streaming happens via eve's native channel events: `message.appended`\n * drives live card patches, `message.completed` finalizes the card, and\n * `turn.failed` aborts it. In `replyMode: \"static\"` the controller is\n * skipped and `message.completed` delivers a single card.\n *\n * **Delivery guarantee**: every terminal event (`message.completed` or\n * `turn.failed`) delivers *something* to the user. If the streaming card\n * path fails, we fall back to a fresh card; if that fails, plain text; if\n * even that fails, the error is logged. The user is never left looking at\n * a typing-emoji reaction with no reply.\n */\nexport function createLarkChannel(\n optionsInput: LarkChannelOptions,\n): Channel<undefined, Record<string, unknown>, Record<string, unknown>> {\n const options = resolveOptions(optionsInput);\n const client = new LarkClient(options);\n const dedup = new DedupMap(options.dedupTtlMs, options.dedupMaxEntries);\n\n // Long-connection side effect: when mode is \"long-connection\" (the\n // default), start a Feishu WSClient in the background. Each inbound event\n // is re-signed and POSTed to this channel's webhook on localhost, where\n // the standard handler runs with full access to send() etc.\n //\n // Skip when running inside the `eve start` launcher process: eve forks\n // a nitro server child to actually serve HTTP, and both processes load\n // the channel module. Without this guard each process spawns its own\n // WSClient and Feishu delivers every event twice.\n if (options.mode === \"long-connection\" && !isEveStartLauncher()) {\n const eveWebhookUrl = `http://127.0.0.1:${options.port}${options.webhookPath}`;\n void startLongConnection({ resolved: options, eveWebhookUrl }).catch((e) => {\n console.error(\"[eve-lark] long-connection startup failed:\", e);\n });\n } else if (options.mode === \"long-connection\" && isEveStartLauncher()) {\n console.log(\"[eve-lark] skipping WSClient start in eve-start launcher process; the spawned server will start it\");\n }\n\n // Channel-scoped (closure) state.\n //\n // **Per-turn** (keyed by eve turnId): each turn owns its own controller so\n // interleaved turns in conversation-mode don't overwrite each other's\n // reply rootId or share streaming-card state. `turnSources` maps a turnId\n // to the inbound messageId that started it — set by turn.started (which\n // shifts the head of the session's FIFO queue), consumed by\n // message.appended (for reply target) and message.completed/turn.failed/\n // turn.completed (for ack cleanup). `turnTouchedAt` feeds the stale-sweep.\n //\n // **Per-session** (keyed by sessionId): sessionMeta, the inbound-msgId FIFO\n // (awaiting turn.started), the last-chat-message id (diagnostic), the ack\n // reactions (keyed by INBOUND messageId), and per-`${sessionId}:${name}`\n // auth cards. These are session-level concerns that don't split per turn.\n const controllers = new Map<string, StreamingCardController>();\n const turnSources = new Map<string, string>();\n const turnTouchedAt = new Map<string, number>();\n const sessionMeta = new Map<string, LarkSessionMeta>();\n\n // Pending ask_question input requests, keyed by eve's requestId. Used to\n // resolve card.action.trigger callbacks back to the originating session.\n // Also keyed by chat-continuation-token for freeform interception.\n interface PendingInput {\n requestId: string;\n sessionId: string;\n /** eve turnId that emitted the input.requested (so handleCardAction can\n * find the per-turn controller to preserve the streaming buffer above\n * the answered ask UI). */\n turnId: string;\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n /** The card message id we sent (so we can patch it after the user answers). */\n cardMessageId?: string | undefined;\n /** Full request, so the post-click renderer can show selected label. */\n request: LarkInputRequest;\n /** When the pending input was registered (for stale-sweep). */\n createdAt: number;\n /** Whether to intercept the next inbound chat message as the response. */\n awaitingFreeform: boolean;\n touchedAt: number;\n }\n const pendingInputsByRequestId = new Map<string, PendingInput>();\n const pendingInputsByChatToken = new Map<string, PendingInput>();\n\n // Auth cards keyed by `${sessionId}:${connectionName}`. Populated by\n // authorization.required, consumed + deleted by authorization.completed.\n const authCards = new Map<string, string>();\n\n // Ack reaction cleanup — keyed by INBOUND messageId (not sessionId) so\n // multiple messages in the same session don't overwrite each other's\n // cleanup data. eve reuses the same sessionId for conversation-mode\n // continuation; a sessionId-keyed map would lose the first message's\n // reactionId when the second arrives.\n const ackReactions = new Map<string, { reactionId: string; createdAt: number }>();\n\n // FIFO queue of inbound messageIds per session, awaiting turn.started.\n // turn.started shifts the head to map the incoming turnId → source message\n // (stored in turnSources). eve conversation mode reuses the same sessionId\n // for same-chat continuation, so a single-value map would lose the first\n // message's slot when the second arrives before the first turn starts.\n const currentInboundMsgIds = new Map<string, string[]>();\n\n // messageId of the last inbound message per chat session. The per-turn\n // controller now does quote-reply directly via turnSources (turnId → source),\n // so lastChatMessage is no longer used for \"follow/interleaved\" detection.\n // Kept mainly for future diagnostics/visualization; webhook still updates it.\n const lastChatMessage = new Map<string, string>();\n\n // Turn ids whose ack reaction has already been cleaned. eve fires both\n // message.completed AND turn.completed for turns with visible text (only\n // turn.completed for tool-call-only turns); without dedup the second event\n // would re-run cleanupAckForTurn on the same source message.\n const cleanedTurns = new Set<string>();\n\n function getController(turnId: string, meta: ResolvedSessionInfo): StreamingCardController {\n let ctrl = controllers.get(turnId);\n if (!ctrl) {\n ctrl = new StreamingCardController(client, {\n chatId: meta.chatId,\n rootId: meta.rootId,\n parentId: meta.parentId,\n patchIntervalMs: options.streamPatchIntervalMs,\n createThresholdMs: options.streamCreateThresholdMs,\n useCardKitV2: options.replyMode === \"streaming-v2\",\n });\n controllers.set(turnId, ctrl);\n }\n turnTouchedAt.set(turnId, Date.now());\n return ctrl;\n }\n\n /** Tear down all per-turn state for `turnId`. Idempotent — safe to call\n * from every terminal handler (message.completed, turn.failed,\n * turn.completed) without ordering assumptions. */\n function dropTurn(turnId: string): void {\n controllers.delete(turnId);\n turnSources.delete(turnId);\n turnTouchedAt.delete(turnId);\n }\n\n\n /** Best-effort ack-reaction cleanup. Takes the INBOUND messageId (not\n * sessionId) so multiple messages in the same session don't clean up\n * each other's reactions. */\n async function cleanupAckReaction(messageId: string): Promise<void> {\n const ack = ackReactions.get(messageId);\n if (!ack) return;\n try {\n await client.removeReaction({ messageId, reactionId: ack.reactionId });\n } catch (e) {\n console.warn(\n \"[eve-lark] ack reaction cleanup failed:\",\n e instanceof Error ? e.message : e,\n );\n }\n ackReactions.delete(messageId);\n }\n\n /** Drain the oldest inbound messageId from the session's FIFO queue and\n * clean up its ack reaction. Used when a terminal event arrives WITHOUT\n * a turnId (so we can't look up the per-turn source). Terminal events\n * that DO carry a turnId use {@link cleanupAckForTurn} instead. */\n async function cleanupAckForSession(sessionId: string): Promise<void> {\n const queue = currentInboundMsgIds.get(sessionId);\n if (!queue || queue.length === 0) return;\n const msgId = queue.shift();\n if (!msgId) return;\n if (queue.length === 0) currentInboundMsgIds.delete(sessionId);\n await cleanupAckReaction(msgId);\n }\n\n /** Clean up the ack reaction for the inbound message that started this\n * turn (looked up via the turnId → source map populated by turn.started).\n * Per-turn equivalent of {@link cleanupAckForSession}. */\n async function cleanupAckForTurn(turnId: string): Promise<void> {\n const sourceMsgId = turnSources.get(turnId);\n if (!sourceMsgId) return;\n await cleanupAckReaction(sourceMsgId);\n }\n\n /**\n * Cascade-deliver a reply to the user. Tries (in order):\n *\n * post mode (default — native chat size + markdown):\n * 1. sendPost (msg_type: \"post\", renders at native size)\n * 2. sendText (post POST rejected)\n *\n * streaming mode (live card patches during the turn):\n * 1. streaming finalize (patches existing card OR creates one)\n * 2. sendCard (finalize failed)\n * 3. sendText (card POST rejected)\n *\n * static mode (one-shot card):\n * 1. sendCard (single card with the full text)\n * 2. sendText (card POST rejected)\n *\n * `turnId` selects the per-turn controller in streaming mode; in post /\n * static mode it's ignored. Callers MUST pre-check `controllers.has(turnId)`\n * for streaming mode if they need to dedupe — deliverReply itself will\n * fall through to a fresh sendCard when no controller exists.\n *\n * Each failure logs; we never throw out of here.\n */\n async function deliverReply(turnId: string | undefined, info: ResolvedSessionInfo, text: string): Promise<void> {\n if (options.replyMode === \"post\") {\n try {\n await client.sendPost({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendPost (turnId=${turnId ?? \"?\"})`);\n return;\n } catch (postErr) {\n console.warn(\n `[eve-lark] sendPost failed; falling back to plain text (turnId=${turnId ?? \"?\"}):`,\n postErr instanceof Error ? postErr.message : postErr,\n );\n // Fall through to sendText.\n }\n // post-specific fallback (skip the card cascade below).\n try {\n await client.sendText({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendText fallback (turnId=${turnId ?? \"?\"})`);\n } catch (textErr) {\n console.error(\n `[eve-lark] sendText fallback ALSO failed; the user will not see this reply (turnId=${turnId ?? \"?\"}):`,\n textErr instanceof Error ? textErr.message : textErr,\n );\n }\n return;\n }\n\n if (options.replyMode === \"streaming\" || options.replyMode === \"streaming-v2\") {\n const ctrl = turnId ? controllers.get(turnId) : undefined;\n if (ctrl) {\n try {\n await ctrl.finalize(text);\n console.log(`[eve-lark] delivered via streaming finalize (turnId=${turnId})`);\n return;\n } catch (e) {\n console.warn(\n `[eve-lark] streaming finalize failed; falling back to fresh card (turnId=${turnId}):`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n // No controller (turn had no message.appended, e.g. a short final reply)\n // or finalize threw — fall through to a one-shot sendCard.\n }\n\n try {\n const res = await client.sendCard({\n chatId: info.chatId,\n card: buildTextCard(text),\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendCard (turnId=${turnId ?? \"?\"})`);\n void res;\n return;\n } catch (cardErr) {\n console.warn(\n `[eve-lark] sendCard failed; falling back to plain text (turnId=${turnId ?? \"?\"}):`,\n cardErr instanceof Error ? cardErr.message : cardErr,\n );\n }\n\n try {\n await client.sendText({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendText fallback (turnId=${turnId ?? \"?\"})`);\n } catch (textErr) {\n console.error(\n `[eve-lark] sendText fallback ALSO failed; the user will not see this reply (turnId=${turnId ?? \"?\"}):`,\n textErr instanceof Error ? textErr.message : textErr,\n );\n }\n }\n\n // Lazy sweep: drop per-turn controllers/sources that haven't been touched\n // in STALE_SESSION_MS, plus stale sessionMeta and pending input requests.\n // Guards against the case where eve crashes mid-turn (no terminal event).\n let lastSweepAt = 0;\n function maybeSweep(): void {\n const now = Date.now();\n if (now - lastSweepAt < SWEEP_INTERVAL_MS) return;\n lastSweepAt = now;\n const cutoff = now - STALE_SESSION_MS;\n for (const [turnId, touchedAt] of turnTouchedAt) {\n if (touchedAt < cutoff) {\n dropTurn(turnId);\n }\n }\n for (const [id, meta] of sessionMeta) {\n if (meta.touchedAt < cutoff) {\n sessionMeta.delete(id);\n }\n }\n for (const [reqId, p] of pendingInputsByRequestId) {\n if (p.touchedAt < cutoff) {\n pendingInputsByRequestId.delete(reqId);\n const tokenKey = chatTokenKey(p.chatId, p.rootId, p.parentId);\n if (pendingInputsByChatToken.get(tokenKey)?.requestId === reqId) {\n pendingInputsByChatToken.delete(tokenKey);\n }\n }\n }\n }\n\n /** Compose the chat-scoped key used for freeform interception. */\n function chatTokenKey(chatId: string, rootId?: string, parentId?: string): string {\n return `${chatId}:${parentId ?? rootId ?? \"_\"}`;\n }\n\n function dropPendingInput(p: PendingInput): void {\n pendingInputsByRequestId.delete(p.requestId);\n const tokenKey = chatTokenKey(p.chatId, p.rootId, p.parentId);\n if (pendingInputsByChatToken.get(tokenKey)?.requestId === p.requestId) {\n pendingInputsByChatToken.delete(tokenKey);\n }\n }\n\n const webhookHandler = async (\n req: Request,\n helpers: RouteHandlerArgs[\"send\"] extends never ? never : RouteHandlerArgs,\n ): Promise<Response> => {\n maybeSweep();\n\n // 0) Body size cap — refuse gigantic bodies before allocating.\n const contentLength = Number(req.headers.get(\"content-length\") ?? \"0\");\n if (Number.isFinite(contentLength) && contentLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n const rawBody = Buffer.from(await req.arrayBuffer());\n if (rawBody.byteLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n\n // 1) Skew check (only enforced when a real timestamp header is present)\n const tsHeader = req.headers.get(\"x-lark-request-timestamp\") ?? \"\";\n const ts = Number(tsHeader);\n if (\n tsHeader &&\n Number.isFinite(ts) &&\n ts > 0 &&\n Math.abs(Date.now() / 1000 - ts) > options.signatureSkewMs / 1000\n ) {\n return new Response(\"request timestamp out of skew window\", { status: 408 });\n }\n\n // 2) Signature verify + AES decrypt when encryptKey configured\n let workingBody: Buffer = rawBody;\n if (options.encryptKey) {\n const nonce = req.headers.get(\"x-lark-request-nonce\") ?? \"\";\n const sigHeader = req.headers.get(\"x-lark-signature\");\n if (!sigHeader) return new Response(\"missing signature\", { status: 401 });\n const ok = verifySignature({\n timestamp: tsHeader,\n nonce,\n encryptKey: options.encryptKey,\n rawBody,\n signatureHeader: sigHeader,\n });\n if (!ok) return new Response(\"bad signature\", { status: 401 });\n\n try {\n const envelope = JSON.parse(rawBody.toString(\"utf8\")) as LarkEncryptedBody;\n if (envelope.encrypt) {\n workingBody = decryptPayload(envelope.encrypt, options.encryptKey) as Buffer;\n }\n } catch {\n return new Response(\"decrypt failed\", { status: 400 });\n }\n }\n\n // 3) Parse body\n let body: LarkEventBody;\n try {\n body = JSON.parse(workingBody.toString(\"utf8\")) as LarkEventBody;\n } catch {\n return new Response(\"invalid json\", { status: 400 });\n }\n\n // 4) url_verification short-circuit\n if (body.type === \"url_verification\") {\n return Response.json({ challenge: body.challenge ?? \"\" });\n }\n\n // 5) Schema check\n if (body.schema !== \"2.0\") {\n return ackOk();\n }\n\n // 6) Verification-token check\n if (body.header?.token !== options.verificationToken) {\n return new Response(\"verification token mismatch\", { status: 401 });\n }\n\n // 7) Dedup. Card-action callbacks dedup on open_message_id (one click\n // per render — re-clicks after patch are no-ops because we replaced\n // the buttons with text).\n const evtMsg = body.event as { message?: { message_id?: string }; open_message_id?: string } | undefined;\n const dedupKey = body.header?.event_id ?? evtMsg?.message?.message_id ?? evtMsg?.open_message_id;\n if (dedupKey) {\n if (dedup.has(dedupKey)) return ackOk();\n dedup.set(dedupKey);\n }\n\n // 8) Event-type dispatch. Two event types we own:\n // - \"im.message.receive_v1\" — normal inbound message (existing flow).\n // - \"card.action.trigger\" — user clicked a button on an ask_card.\n // Anything else is ack-and-skip.\n const eventType = body.header?.event_type;\n if (eventType === \"card.action.trigger\") {\n return handleCardAction(body.event as LarkCardActionTriggerEvent, helpers);\n }\n if (eventType !== \"im.message.receive_v1\") {\n return ackOk();\n }\n if (!body.event) return ackOk();\n\n // 9) Parse — body.event is now narrowed to the message-event branch.\n const parsed = parseInbound(body.event as LarkInboundEvent, options.botOpenId);\n\n // 10) Self-message suppression\n if (parsed.senderType === \"app\") {\n return ackOk();\n }\n\n // 10.5) Allowlist. Ack-and-drop so the user sees the message \"delivered\"\n // (Feishu's perspective) but the agent never wakes up. DM allowlist\n // keys on sender open_id; group allowlist keys on chat_id. Both are\n // independent — setting one doesn't restrict the other surface.\n if (parsed.chatType === \"p2p\" && options.allowFrom) {\n if (!options.allowFrom.includes(parsed.senderOpenId)) {\n console.log(\n `[eve-lark] dropping DM from non-allowlisted sender ${parsed.senderOpenId}`,\n );\n return ackOk();\n }\n }\n if (parsed.chatType === \"group\" && options.groupAllowFrom) {\n if (!options.groupAllowFrom.includes(parsed.chatId)) {\n console.log(\n `[eve-lark] dropping group message from non-allowlisted chat ${parsed.chatId}`,\n );\n return ackOk();\n }\n }\n\n // 10.8) Audio/media transcription. If the message has no text (audio/\n // media/sticker) and an ASR provider is configured, download the audio\n // bytes and transcribe. The transcript replaces the empty text so the\n // normal flow picks it up. If ASR fails, we fall through to step 11\n // (ack-and-skip).\n if (options.asrProvider && parsed.text === \"\" && parsed.files.length === 0) {\n const rawEvent = body.event as LarkInboundEvent;\n const msgType = rawEvent.message?.message_type;\n if (msgType === \"audio\" || msgType === \"media\") {\n try {\n const content = JSON.parse(rawEvent.message.content) as { file_key?: string };\n if (content.file_key) {\n const bytes = await client.downloadResource({\n messageId: parsed.messageId,\n fileKey: content.file_key,\n type: \"file\",\n });\n const mediaType = msgType === \"audio\" ? \"audio/mpeg\" : \"video/mp4\";\n const transcript = await options.asrProvider.transcribe(bytes, mediaType);\n if (transcript) {\n parsed.text = transcript;\n }\n }\n } catch (e) {\n console.warn(\n \"[eve-lark] audio transcription failed, skipping message:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n }\n\n // 11) Skip unsupported message types\n if (parsed.text === \"\" && parsed.files.length === 0) {\n return ackOk();\n }\n\n // 11.1) Built-in slash command: /lark-diagnose. Run diagnostics\n // (token fetch + config summary) and reply directly via LarkClient.\n // Does NOT forward to the agent.\n if (parsed.text.trim().toLowerCase() === \"/lark-diagnose\") {\n helpers.waitUntil(runDiagnostics(client, options, parsed.chatId));\n return ackOk();\n }\n\n // 11.5) Freeform-input interception. If this chat has a pending\n // ask_question awaiting a freeform text reply, treat this inbound\n // message as the answer instead of starting a new turn. eve resumes\n // the parked session with the user's text as InputResponse.text.\n const tokenKey = chatTokenKey(parsed.chatId, parsed.rootId ?? undefined, parsed.parentId ?? undefined);\n const pending = pendingInputsByChatToken.get(tokenKey);\n if (pending && pending.awaitingFreeform && parsed.text.length > 0) {\n const resp: LarkInputResponse = { requestId: pending.requestId, text: parsed.text };\n const resumeAttributes: Record<string, string> = {\n chatId: parsed.chatId,\n messageId: parsed.messageId,\n chatType: parsed.chatType,\n };\n if (parsed.rootId) resumeAttributes.rootMessageId = parsed.rootId;\n if (parsed.parentId) resumeAttributes.parentMessageId = parsed.parentId;\n const resumeAuth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: parsed.senderOpenId,\n attributes: resumeAttributes,\n };\n const resumeToken = larkContinuationToken(parsed.chatId, parsed.parentId ?? parsed.rootId);\n try {\n await helpers.send(\n { inputResponses: [resp] } as never,\n { auth: resumeAuth as never, continuationToken: resumeToken },\n );\n // Update the card (if any) to show the typed answer.\n if (pending.cardMessageId) {\n try {\n await client.patchCard({\n messageId: pending.cardMessageId,\n card: buildAskAnsweredCard(pending.request, { kind: \"freeform\", text: parsed.text }),\n });\n } catch (e) {\n console.warn(\"[eve-lark] patchCard after freeform answer failed:\", e instanceof Error ? e.message : e);\n }\n }\n } catch (e) {\n console.error(\"[eve-lark] freeform input-response send failed:\", e instanceof Error ? e.message : e);\n } finally {\n dropPendingInput(pending);\n }\n return ackOk();\n }\n\n // 12) Build session inputs\n const userContent = buildUserContent(parsed.text, parsed.files, options, parsed.messageId);\n const continuationToken = larkContinuationToken(parsed.chatId, parsed.parentId ?? parsed.rootId);\n // Build auth.attributes, OMITTING null/undefined values. eve's\n // SessionAuthContext contract requires `Record<string, string | readonly\n // string[]>` — null values violate it and the `as never` cast below\n // silences TS without protecting runtime. A null `rootMessageId` (typical\n // for top-level chats where parsed.rootId is null) used to sneak through\n // and silently break helpers.send: the call returned a Session object\n // but no workflow actually ran — no [eve:harness] log, no model call,\n // no reply. Sanitize to strings-only so eve's runtime accepts the auth.\n const attributes: Record<string, string> = {\n chatId: parsed.chatId,\n messageId: parsed.messageId,\n chatType: parsed.chatType,\n };\n if (parsed.rootId) attributes.rootMessageId = parsed.rootId;\n if (parsed.parentId) attributes.parentMessageId = parsed.parentId;\n const auth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: parsed.senderOpenId,\n attributes,\n };\n\n // 13) Start/resume session.\n // For group chats, surface any matching per-group systemPrompt as\n // `context` — eve prepends each context entry as a role:\"user\" message\n // before the delivery message. DMs ignore this.\n const groupConfig =\n parsed.chatType === \"group\"\n ? options.groupConfigs?.find((g) => g.chatId === parsed.chatId)\n : undefined;\n const sendPayload: { auth: unknown; continuationToken: string; context?: readonly string[] } = {\n auth: auth as never,\n continuationToken,\n };\n if (groupConfig?.systemPrompt) {\n sendPayload.context = [groupConfig.systemPrompt];\n }\n console.log(\n `[eve-lark] invoking helpers.send chatId=${parsed.chatId} continuationToken=${continuationToken}` +\n ` textLen=${parsed.text.length} files=${parsed.files.length}`,\n );\n let session: { id: string };\n try {\n session = await helpers.send(userContent as never, sendPayload as never);\n } catch (e) {\n console.error(\n `[eve-lark] helpers.send threw for chatId=${parsed.chatId} continuationToken=${continuationToken}:`,\n e instanceof Error ? e.message : e,\n );\n throw e;\n }\n console.log(\n `[eve-lark] helpers.send returned sessionId=${session.id} for chatId=${parsed.chatId}`,\n );\n\n // Enqueue this inbound messageId so turn.started can shift it off the\n // head to map the incoming turnId → source message. FIFO (not\n // single-value) because a second message arriving before the first\n // turn's turn.started must not overwrite the first message's slot.\n const queue = currentInboundMsgIds.get(session.id);\n if (queue) queue.push(parsed.messageId);\n else currentInboundMsgIds.set(session.id, [parsed.messageId]);\n\n // This user message is the current last inbound in the chat → record it\n // (for diagnostics/visualization). Quote-reply now locates the source\n // directly via turnSources (turnId → source); this map is not used for it.\n lastChatMessage.set(session.id, parsed.messageId);\n\n // 14) Remember chat metadata keyed by session.id so terminal handlers\n // can look up where to deliver replies.\n sessionMeta.set(session.id, {\n chatId: parsed.chatId,\n rootId: parsed.rootId ?? undefined,\n parentId: parsed.parentId ?? undefined,\n touchedAt: Date.now(),\n });\n\n // 15) Ack reaction — fire-and-forget. Stash the resulting reaction id\n // keyed by messageId (NOT sessionId) so multiple messages in the same\n // session don't overwrite each other's cleanup data.\n const emoji = pickAckEmoji(options.ackReaction);\n if (emoji) {\n const inboundMsgId = parsed.messageId;\n helpers.waitUntil(\n client\n .addReaction({ messageId: inboundMsgId, emojiType: emoji })\n .then(({ reactionId }) => {\n ackReactions.set(inboundMsgId, { reactionId, createdAt: Date.now() });\n })\n .catch((e) => {\n console.warn(\n \"[eve-lark] ack reaction failed:\",\n e instanceof Error ? e.message : e,\n );\n }),\n );\n }\n\n return ackOk();\n };\n\n /**\n * Handle a `card.action.trigger` callback from Feishu. The user clicked a\n * button on an ask_card we rendered earlier. Extract the requestId +\n * optionId from `action.value`, resume the parked eve session with an\n * InputResponse, and patch the card to show the selection.\n *\n * Buttons we created carry `{__eveLarkAsk, requestId, optionId}` in their\n * `value`. Buttons from any other source are ignored.\n */\n async function handleCardAction(\n evt: LarkCardActionTriggerEvent,\n helpers: RouteHandlerArgs,\n ): Promise<Response> {\n const value = evt.action?.value;\n if (!value || value[ASK_BUTTON_VALUE_MARKER] !== true) {\n // Not our button/select — ignore (could be from another integration).\n return ackOk();\n }\n const requestId = typeof value.requestId === \"string\" ? value.requestId : \"\";\n // optionId location depends on the source element:\n // button: action.value.optionId (we put it there at render time)\n // select_static: action.option (Feishu returns the selected option's value string here)\n const optionId =\n (typeof value.optionId === \"string\" ? value.optionId : \"\") ||\n (typeof evt.action?.option === \"string\" ? evt.action.option : \"\");\n if (!requestId) return ackOk();\n\n const pending = pendingInputsByRequestId.get(requestId);\n if (!pending) {\n console.warn(\n `[eve-lark] card action for unknown requestId=${requestId} (already answered or expired)`,\n );\n return ackOk();\n }\n\n // ACK-FIRST: return ackOk() immediately so Feishu doesn't time out the\n // card.action.trigger callback (~3s) and revert the optimistic UI. The\n // actual work (patch the card to \"answered\" + resume the parked eve\n // session with the InputResponse) runs in the background via\n // helpers.waitUntil. Order within the background task matters: patch\n // FIRST so the user sees the \"✓ selected\" state as fast as possible,\n // THEN resume eve so model execution latency doesn't delay the visual\n // confirmation.\n const selectedOpt = pending.request.options?.find((o) => o.id === optionId);\n helpers.waitUntil(\n (async () => {\n if (pending.cardMessageId && selectedOpt) {\n // If the ask was rendered inline on a streaming card, preserve\n // the controller's accumulated buffer above the answered prompt\n // so the user doesn't lose what the agent said before asking.\n const ctrlForBuffer = controllers.get(pending.turnId);\n const priorBuffer = ctrlForBuffer?.getBuffer() ?? undefined;\n try {\n await client.patchCard({\n messageId: pending.cardMessageId,\n card: buildAskAnsweredCard(\n pending.request,\n { kind: \"option\", label: selectedOpt.label },\n priorBuffer,\n ),\n });\n // Clear the inline ask so the controller's next patch (turn 2's\n // streaming text after the user's answer resumes the session)\n // doesn't re-render the now-answered buttons.\n ctrlForBuffer?.clearAskRequest();\n } catch (e) {\n console.warn(\n \"[eve-lark] patchCard after ask-answer failed:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n\n const resp: LarkInputResponse = { requestId, optionId: optionId || undefined };\n const resumeToken = larkContinuationToken(\n pending.chatId,\n pending.parentId ?? pending.rootId ?? null,\n );\n const resumeAttributes: Record<string, string> = {\n chatId: pending.chatId,\n messageId: evt.open_message_id,\n chatType: pending.request.display === \"confirmation\" ? \"p2p\" : \"group\",\n };\n if (pending.rootId) resumeAttributes.rootMessageId = pending.rootId;\n if (pending.parentId) resumeAttributes.parentMessageId = pending.parentId;\n const resumeAuth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: evt.open_id,\n attributes: resumeAttributes,\n };\n try {\n await helpers.send(\n { inputResponses: [resp] } as never,\n { auth: resumeAuth as never, continuationToken: resumeToken },\n );\n console.log(\n `[eve-lark] ask answered via card action requestId=${requestId} optionId=${optionId}`,\n );\n } catch (e) {\n console.error(\n `[eve-lark] ask input-response send failed (requestId=${requestId}):`,\n e instanceof Error ? e.message : e,\n );\n }\n\n dropPendingInput(pending);\n })().catch((e) => {\n console.error(\"[eve-lark] card action background work failed:\", e);\n }),\n );\n\n return ackOk();\n }\n\n // Channel event handlers — declared as a standalone const so tests can\n // invoke them directly (eve's defineChannel hides events on the returned\n // Channel object). createLarkChannel attaches them as `__testEvents` on\n // the returned channel for that purpose; production code never reads it.\n const channelEvents = {\n // Streaming delta — patch the card.\n \"message.appended\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n if (options.replyMode !== \"streaming\" && options.replyMode !== \"streaming-v2\") return;\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) return;\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n const d = data as { messageDelta?: string };\n if (typeof d.messageDelta !== \"string\") return;\n const ctrl = getController(turnId, info);\n // Quote-reply: aim this turn's card at the inbound message that\n // started it. turn.started already mapped turnId → source (shifted off\n // the session's FIFO). Each turn owns its own controller, so each card\n // quotes its OWN source — interleaved replies don't overwrite each\n // other's rootId.\n const source = turnSources.get(turnId);\n if (source) {\n console.log(`[eve-lark] quote-reply turnId=${turnId} sessionId=${sessionId} → REPLY ${source}`);\n ctrl.setReplyTarget(source);\n }\n ctrl.appendDelta(d.messageDelta);\n },\n\n // Model is about to call tools. Record each call on the streaming\n // controller so it shows up in the card as ⏳ name. The controller\n // creates the card immediately if it doesn't exist yet, so the user\n // sees the tool call even before any text has streamed (which is the\n // common case — model often calls tools before producing visible\n // output). Only fires for streaming modes; post/static have no live\n // surface to update.\n async \"actions.requested\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n if (options.replyMode !== \"streaming\" && options.replyMode !== \"streaming-v2\") return;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) return;\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n const d = data as { actions?: Array<{ kind?: string; toolName?: string }> };\n const names = (d.actions ?? [])\n .map((a) => a.toolName)\n .filter((n): n is string => typeof n === \"string\");\n if (names.length === 0) return;\n // getController (not just .get) so we create the controller if it\n // doesn't exist yet — tools can fire before any message.appended.\n const ctrl = getController(turnId, info);\n for (const name of names) {\n ctrl.addToolCall(name);\n }\n },\n\n // A tool finished. Mark its entry ✓ (or ✗ on failure). Stays visible —\n // the user keeps the tool history at the top of the card through end\n // of turn. Best-effort: if we can't find the controller (e.g. post\n // mode, or session already cleaned up), no-op.\n async \"action.result\"(data: unknown, _channel: unknown, _ctx: { session: { id: string } }) {\n if (options.replyMode !== \"streaming\" && options.replyMode !== \"streaming-v2\") return;\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n const ctrl = controllers.get(turnId);\n if (!ctrl) return;\n const d = data as { result?: { toolName?: string; status?: string } };\n const name = d.result?.toolName;\n if (!name) return;\n ctrl.completeToolCall(name, d.result?.status === \"failed\");\n },\n\n // eve's ask_question (and similar HITL tools) fire this event with a\n // list of input requests. Each request becomes a Feishu card with\n // buttons (one per option) plus optional freeform hint.\n async \"input.requested\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) {\n console.warn(`[eve-lark] input.requested: no session info (sessionId=${sessionId})`);\n return;\n }\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) {\n console.warn(`[eve-lark] input.requested: no turnId (sessionId=${sessionId})`);\n return;\n }\n const d = data as { requests?: readonly LarkInputRequest[] };\n const requests = d.requests ?? [];\n if (requests.length === 0) return;\n\n console.log(\n `[eve-lark] input.requested sessionId=${sessionId} turnId=${turnId} chatId=${info.chatId} count=${requests.length}`,\n );\n\n for (const req of requests) {\n // Inline ask: if a streaming card already exists for this turn,\n // patch IT with the ask UI (prompt + option buttons appended below\n // the streaming text). This keeps the whole turn on one card — no\n // separate ask-card, no separate reply-card. Falls back to creating\n // a fresh ask-card when there's no streaming controller (post/static\n // reply modes, or the very first ask before any text streamed).\n const existingCtrl = controllers.get(turnId);\n const canPatchExisting =\n existingCtrl &&\n existingCtrl.getMessageId() &&\n (options.replyMode === \"streaming\" || options.replyMode === \"streaming-v2\");\n\n let cardMessageId: string | undefined;\n if (canPatchExisting && existingCtrl) {\n existingCtrl.setAskRequest(req);\n cardMessageId = existingCtrl.getMessageId();\n } else {\n const card = buildAskCard(req);\n try {\n const res = await client.sendCard({\n chatId: info.chatId,\n card,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n cardMessageId = res.messageId;\n } catch (e) {\n console.error(\n `[eve-lark] ask card send failed (requestId=${req.requestId}):`,\n e instanceof Error ? e.message : e,\n );\n continue;\n }\n }\n\n const pending: PendingInput = {\n requestId: req.requestId,\n sessionId,\n turnId,\n chatId: info.chatId,\n rootId: info.rootId,\n parentId: info.parentId,\n cardMessageId,\n request: req,\n createdAt: Date.now(),\n touchedAt: Date.now(),\n awaitingFreeform: req.allowFreeform === true,\n };\n pendingInputsByRequestId.set(req.requestId, pending);\n if (pending.awaitingFreeform) {\n const tokenKey = chatTokenKey(info.chatId, info.rootId, info.parentId);\n pendingInputsByChatToken.set(tokenKey, pending);\n }\n }\n },\n\n // Terminal — deliver the final reply, then clean up the ack reaction.\n // The per-turn controller is NOT destroyed here: in HITL flows eve fires\n // input.requested AFTER message.completed (still in the same turn), and\n // that handler needs the controller to patch the streaming card with the\n // ask UI inline. The controller is torn down by turn.completed (the true\n // end of turn). Duplicate message.completed events on the same turn are\n // deduped by the controller's finalize() state guard (state→completed on\n // first call, no-op thereafter).\n async \"message.completed\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) {\n console.warn(`[eve-lark] message.completed: no session info, cannot deliver (sessionId=${sessionId})`);\n return;\n }\n const d = data as { message?: string | null };\n const rawText = typeof d.message === \"string\" ? d.message : \"\";\n const mcTurnId = (data as { turnId?: string } | null)?.turnId;\n console.log(\n `[eve-lark] message.completed sessionId=${sessionId} chatId=${info.chatId} msgLen=${rawText.length}` + (mcTurnId ? ` turnId=${mcTurnId}` : \"\"),\n );\n const text = rawText.length > 0 ? rawText : EMPTY_REPLY_TEXT;\n\n const isStreaming = options.replyMode === \"streaming\" || options.replyMode === \"streaming-v2\";\n // Streaming dedup: if the controller is already gone (turn.completed or\n // turn.failed already tore it down), skip delivery. Otherwise the\n // controller's finalize() state guard handles same-turn duplicates.\n if (isStreaming && mcTurnId && !controllers.has(mcTurnId)) {\n if (!cleanedTurns.has(mcTurnId)) {\n cleanedTurns.add(mcTurnId);\n await cleanupAckForTurn(mcTurnId);\n }\n return;\n }\n\n try {\n await deliverReply(mcTurnId, info, text);\n } finally {\n if (mcTurnId && !cleanedTurns.has(mcTurnId)) {\n cleanedTurns.add(mcTurnId);\n await cleanupAckForTurn(mcTurnId);\n } else if (!mcTurnId) {\n await cleanupAckForSession(sessionId);\n }\n // NOTE: do NOT dropTurn here — see the comment at the top of this\n // handler. turn.completed / turn.failed own the controller teardown.\n }\n },\n\n async \"turn.failed\"(data: unknown, _channel: unknown, ctx: { session?: { id: string } } | null) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) {\n console.warn(\"[eve-lark] turn.failed: no sessionId on ctx\");\n return;\n }\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) {\n console.warn(`[eve-lark] turn.failed: no session info (sessionId=${sessionId})`);\n return;\n }\n const failedTurnId = (data as { turnId?: string } | null)?.turnId;\n const userText = formatFailureMessage(data, \"turn failed\", { sentence: \"turn\" });\n const errorId = extractErrorId((data as { details?: unknown } | null)?.details);\n console.warn(\n `[eve-lark] turn.failed sessionId=${sessionId} chatId=${info.chatId}` +\n ` err=\"${userText.slice(0, 200)}\"` +\n (failedTurnId ? ` turnId=${failedTurnId}` : \"\") +\n (errorId ? ` errorId=${errorId}` : \"\"),\n );\n\n const ctrl = failedTurnId ? controllers.get(failedTurnId) : undefined;\n if (ctrl) {\n try {\n await ctrl.abort(userText);\n console.log(`[eve-lark] error shown via streaming abort (turnId=${failedTurnId})`);\n } catch (e) {\n console.warn(\n `[eve-lark] turn.failed: streaming abort failed, will deliver fresh error (turnId=${failedTurnId}):`,\n e instanceof Error ? e.message : e,\n );\n try {\n await deliverReply(failedTurnId, info, userText);\n } catch {\n // unreachable\n }\n }\n } else {\n try {\n await deliverReply(failedTurnId, info, userText);\n } catch {\n // unreachable\n }\n }\n\n if (!failedTurnId || !cleanedTurns.has(failedTurnId)) {\n if (failedTurnId) cleanedTurns.add(failedTurnId);\n if (failedTurnId) await cleanupAckForTurn(failedTurnId);\n else await cleanupAckForSession(sessionId);\n }\n // Turn failed — destroy the per-turn controller so a later\n // message.completed/turn.completed on this turnId doesn't reuse it.\n // (message.completed itself doesn't drop, to keep the controller alive\n // for a follow-up input.requested in HITL flows.)\n if (failedTurnId) dropTurn(failedTurnId);\n },\n\n async \"session.failed\"(data: unknown) {\n const userText = formatFailureMessage(data, \"session failed\", { sentence: \"session\" });\n const errorId = extractErrorId((data as { details?: unknown } | null)?.details);\n console.error(\n `[eve-lark] session.failed: ${userText}` + (errorId ? ` (errorId=${errorId})` : \"\"),\n );\n },\n\n // A new turn is starting. Per-turn controllers mean we no longer reset\n // existing controllers here — each turn gets a fresh one (created lazily\n // in message.appended / actions.requested). The only side effect left is\n // mapping turnId → inbound source message: shift the head of the\n // session's FIFO (populated by the webhook handler) so message.appended\n // can aim this turn's card at the right user message and terminal events\n // can clean up the right ack reaction.\n async \"turn.started\"(_data: unknown, _channel: unknown, ctx: { session?: { id: string } } | null) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) return;\n const tsTurnId = (_data as { turnId?: string } | null)?.turnId;\n console.log(`[eve-lark] turn.started sessionId=${sessionId}` + (tsTurnId ? ` turnId=${tsTurnId}` : \"\"));\n if (!tsTurnId) return;\n const queue = currentInboundMsgIds.get(sessionId);\n if (queue && queue.length > 0) {\n const source = queue.shift()!;\n if (queue.length === 0) currentInboundMsgIds.delete(sessionId);\n turnSources.set(tsTurnId, source);\n }\n },\n\n // Turn ended cleanly. eve fires this after the final message.completed\n // (or instead of it when the assistant step ended in tool-calls with no\n // visible text). Clean up the ack reaction and destroy the per-turn\n // controller (idempotent — message.completed/turn.failed may have done\n // it already).\n async \"turn.completed\"(data: unknown, _channel: unknown, _ctx: { session?: { id: string } } | null) {\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n if (cleanedTurns.has(turnId)) {\n // message.completed already drained this turn — but we still drop\n // the per-turn controller (turn.completed is the LAST event for a\n // tool-only turn that never fires message.completed).\n dropTurn(turnId);\n return;\n }\n cleanedTurns.add(turnId);\n try {\n await cleanupAckForTurn(turnId);\n } catch {\n // best-effort\n }\n dropTurn(turnId);\n },\n\n // The agent needs the user to sign in to an external service (e.g.\n // GitHub, Slack, Linear). Render a card with a \"Sign in with <X>\"\n // URL button so the user can complete the flow in their browser.\n // The card message id is tracked so `authorization.completed` can\n // patch it with the outcome.\n async \"authorization.required\"(data: unknown, _channel: unknown, ctx: { session?: { id: string } }) {\n const sessionId = ctx?.session?.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info || !sessionId) return;\n const d = data as {\n name?: string;\n authorization?: { displayName?: string; url?: string; userCode?: string };\n };\n const name = d.name ?? \"service\";\n const displayName = d.authorization?.displayName ?? name;\n const url = d.authorization?.url;\n if (!url) {\n console.warn(`[eve-lark] authorization.required for ${name}: no url, skipping card`);\n return;\n }\n const card = buildAuthCard({ displayName, url, userCode: d.authorization?.userCode });\n try {\n const res = await client.sendCard({ chatId: info.chatId, card, rootId: info.rootId, parentId: info.parentId });\n authCards.set(`${sessionId}:${name}`, res.messageId);\n } catch (e) {\n console.error(`[eve-lark] auth card send failed (${name}):`, e instanceof Error ? e.message : e);\n }\n },\n\n // The user completed (or declined) the external auth. Patch the card\n // we rendered in `authorization.required` to show the outcome.\n async \"authorization.completed\"(data: unknown, _channel: unknown, ctx: { session?: { id: string } }) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) return;\n const d = data as {\n name?: string;\n outcome?: string;\n reason?: string;\n authorization?: { displayName?: string };\n };\n const name = d.name ?? \"service\";\n const cardMessageId = authCards.get(`${sessionId}:${name}`);\n if (!cardMessageId) return; // no prior required card (or already cleaned up)\n const displayName = d.authorization?.displayName ?? name;\n const card = buildAuthCompletedCard({\n displayName,\n outcome: d.outcome ?? \"completed\",\n reason: d.reason,\n });\n try {\n await client.patchCard({ messageId: cardMessageId, card });\n } catch (e) {\n console.warn(`[eve-lark] auth card patch failed (${name}):`, e instanceof Error ? e.message : e);\n }\n authCards.delete(`${sessionId}:${name}`);\n },\n };\n\n const channel = defineChannel({\n routes: [POST(options.webhookPath, webhookHandler as never)],\n\n fetchFile: async (url: string) => {\n if (!url.startsWith(options.baseUrl)) return null;\n const m = url.match(/\\/messages\\/([^/]+)\\/resources\\/([^?]+)\\?type=(image|file)/);\n if (!m || !m[1] || !m[2] || !m[3]) return null;\n return client.downloadResource({\n messageId: m[1],\n fileKey: m[2],\n type: m[3] as \"image\" | \"file\",\n });\n },\n\n events: channelEvents,\n });\n\n // Test seam: expose the events map so tests can drive handlers directly\n // (eve's defineChannel hides them on the returned Channel). Production\n // code MUST NOT read this — it's typing-loose and not part of the API.\n (channel as Channel<undefined, Record<string, unknown>, Record<string, unknown>> & {\n __testEvents?: typeof channelEvents;\n }).__testEvents = channelEvents;\n\n return channel;\n}\n","/**\n * Typed error hierarchy for eve-lark.\n *\n * All errors extend a common base so consumers can `instanceof LarkChannelError`\n * to catch anything thrown by the channel.\n */\n\nexport class LarkChannelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\nexport class LarkConfigError extends LarkChannelError {}\n\nexport class LarkSignatureError extends LarkChannelError {}\n\nexport class LarkDecryptError extends LarkChannelError {}\n\nexport interface LarkApiErrorBody {\n code?: number | undefined;\n msg?: string | undefined;\n}\n\nexport class LarkApiError extends LarkChannelError {\n readonly code: number | undefined;\n readonly body: LarkApiErrorBody | undefined;\n readonly status: number | undefined;\n\n constructor(\n message: string,\n opts?: {\n code?: number | undefined;\n body?: LarkApiErrorBody | undefined;\n status?: number | undefined;\n cause?: unknown;\n },\n ) {\n super(message, { cause: opts?.cause });\n this.code = opts?.code;\n this.body = opts?.body;\n this.status = opts?.status;\n }\n}\n","import { LarkApiError, type LarkApiErrorBody } from \"./errors.js\";\nimport type { LarkCard, ResolvedLarkOptions } from \"./types.js\";\n\ninterface TokenState {\n value: string;\n expiresAt: number;\n}\n\nconst TOKEN_INVALID_CODES = new Set<number>([99991663, 99991664, 99991661]);\n\ninterface RequestResult {\n status: number;\n body: unknown;\n retryAfter: number | null;\n}\n\nexport class LarkClient {\n private readonly options: ResolvedLarkOptions;\n private token: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(options: ResolvedLarkOptions) {\n this.options = options;\n }\n\n async getTenantAccessToken(): Promise<string> {\n if (\n this.token &&\n Date.now() + this.options.tokenRefreshBufferMs < this.token.expiresAt\n ) {\n return this.token.value;\n }\n if (this.refreshPromise) return this.refreshPromise;\n this.refreshPromise = this.#refresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n async #refresh(): Promise<string> {\n const body = {\n app_id: this.options.appId,\n app_secret: this.options.appSecret,\n };\n const res = await this.options.fetch(\n `${this.options.baseUrl}/open-apis/auth/v3/tenant_access_token/internal`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n },\n );\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: token refresh failed (HTTP ${res.status})`,\n { status: res.status },\n );\n }\n const json = (await res.json()) as { code?: number; tenant_access_token?: string; expire?: number; msg?: string };\n if (json.code !== 0 || !json.tenant_access_token) {\n throw new LarkApiError(\n `eve-lark: token refresh returned code=${json.code ?? \"?\"} msg=${json.msg ?? \"?\"}`,\n { body: json, code: json.code },\n );\n }\n const expireSec = typeof json.expire === \"number\" ? json.expire : 7200;\n this.token = {\n value: json.tenant_access_token,\n expiresAt: Date.now() + expireSec * 1000,\n };\n return this.token.value;\n }\n\n async sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify({ text: args.content });\n // Quote-reply: Feishu sendMessage's root_id only accepts threads,\n // not generic message quotes. A generic quote must use the reply API\n // (POST /messages/{id}/reply).\n if (args.rootId) {\n return this.#replyMessage(args.rootId, \"text\", content);\n }\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"text\",\n content,\n });\n }\n\n async sendCard(args: {\n chatId: string;\n card: LarkCard;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify(args.card);\n if (args.rootId) {\n return this.#replyMessage(args.rootId, \"interactive\", content);\n }\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"interactive\",\n content,\n });\n }\n\n async sendPost(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n // `msg_type: \"post\"` renders at native chat-message size with full\n // markdown support (bold, links, code, <font> color tags) via the\n // inner `{tag: \"md\"}` element. Cards render noticeably smaller because\n // Feishu treats them as \"structured content\"; post does not.\n //\n // The content schema is post > zh_cn > content > lines > inline nodes.\n // We put the whole reply in one md node — the md tag honors embedded\n // newlines, so multi-paragraph replies work as a single text string.\n const post = {\n zh_cn: {\n content: [[{ tag: \"md\", text: args.content }]],\n },\n };\n const content = JSON.stringify(post);\n if (args.rootId) {\n return this.#replyMessage(args.rootId, \"post\", content);\n }\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"post\",\n content,\n });\n }\n\n /** Quote-reply to a specific message via Feishu's reply API.\n * POST /open-apis/im/v1/messages/{message_id}/reply — this is the only\n * way to quote-reply to a normal (non-thread) message; sendMessage's\n * root_id field only works inside threads. */\n async #replyMessage(replyToMessageId: string, msgType: string, content: string): Promise<{ messageId: string }> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(replyToMessageId)}/reply`;\n const json = await this.#request(\"POST\", path, { msg_type: msgType, content });\n const messageId = (json as { data?: { message_id?: string } }).data?.message_id;\n if (!messageId) {\n throw new LarkApiError(\"eve-lark: reply missing message_id in response\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { messageId };\n }\n\n async #sendMessage(body: Record<string, unknown>): Promise<{ messageId: string }> {\n const payload = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n const json = await this.#request(\"POST\", \"/open-apis/im/v1/messages?receive_id_type=chat_id\", payload);\n const messageId = (json as { data?: { message_id?: string } }).data?.message_id;\n if (!messageId) {\n throw new LarkApiError(\"eve-lark: send missing message_id in response\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { messageId };\n }\n\n async patchCard(args: { messageId: string; card: LarkCard }): Promise<void> {\n await this.#request(\n \"PATCH\",\n `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}`,\n { content: JSON.stringify(args.card) },\n );\n }\n\n async downloadResource(args: {\n messageId: string;\n fileKey: string;\n type: \"image\" | \"file\";\n }): Promise<Buffer> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/resources/${encodeURIComponent(args.fileKey)}?type=${args.type}`;\n const token = await this.getTenantAccessToken();\n const res = await this.options.fetch(`${this.options.baseUrl}${path}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: downloadResource HTTP ${res.status}`,\n { status: res.status },\n );\n }\n return Buffer.from(await res.arrayBuffer());\n }\n\n async addReaction(args: {\n messageId: string;\n emojiType: string;\n }): Promise<{ reactionId: string }> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions`;\n const json = (await this.#request(\"POST\", path, {\n reaction_type: { emoji_type: args.emojiType },\n })) as { data?: { reaction_id?: string } };\n const reactionId = json.data?.reaction_id;\n if (!reactionId) {\n throw new LarkApiError(\"eve-lark: addReaction missing reaction_id\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { reactionId };\n }\n\n async removeReaction(args: { messageId: string; reactionId: string }): Promise<void> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions/${encodeURIComponent(args.reactionId)}`;\n await this.#request(\"DELETE\", path, undefined);\n }\n\n /**\n * Central request wrapper with auth, retry, and Feishu error decoding.\n *\n * Retry policy:\n * - 429 (rate limit): always retry with `Retry-After` backoff. Safe —\n * server rejected the request before processing.\n * - 5xx: retry ONLY for idempotent methods (GET / PATCH / DELETE). POST\n * is NOT retried on 5xx because Feishu's POST /messages and POST\n * /reactions are non-idempotent — the server may have created the\n * resource before returning the error, and retrying would silently\n * double-send.\n * - 401 / token-invalid code: refresh and retry once.\n * - Other 4xx: throw LarkApiError with the Feishu code/msg.\n */\n async #request(method: string, path: string, body: unknown): Promise<unknown> {\n const url = `${this.options.baseUrl}${path}`;\n let token = await this.getTenantAccessToken();\n let tokenRefreshed = false;\n const methodNorm = method.toUpperCase();\n const retryableMethod = methodNorm !== \"POST\";\n\n for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {\n const res = await this.options.fetch(url, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n\n const result = await this.#consumeResponse(res);\n const status = result.status;\n\n if (status >= 200 && status < 300) {\n const jsonBody = result.body as { code?: number; msg?: string };\n if (jsonBody && typeof jsonBody.code === \"number\" && jsonBody.code !== 0) {\n if (TOKEN_INVALID_CODES.has(jsonBody.code) && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed code=${jsonBody.code} msg=${jsonBody.msg ?? \"?\"}`,\n { code: jsonBody.code, body: jsonBody as LarkApiErrorBody, status },\n );\n }\n return result.body;\n }\n\n if (status === 401 && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n\n const isRateLimited = status === 429;\n const isServerErr = status >= 500 && status < 600;\n const retryable = isRateLimited || (isServerErr && retryableMethod);\n if (retryable && attempt < this.options.maxRetries) {\n const delayMs = this.#computeBackoff(status, result.retryAfter, attempt);\n await sleep(delayMs);\n continue;\n }\n\n const bodyObj = result.body as LarkApiErrorBody | undefined;\n const code = bodyObj?.code;\n const msg = bodyObj?.msg;\n const detail = msg ? ` code=${code ?? \"?\"} msg=${msg}` : \"\";\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed HTTP ${status}${detail}`,\n { status, body: bodyObj, code },\n );\n }\n throw new LarkApiError(`eve-lark: ${method} ${path} exhausted retries`);\n }\n\n async #consumeResponse(res: Response): Promise<RequestResult> {\n const retryAfterRaw = res.headers.get(\"retry-after\");\n const retryAfter = retryAfterRaw ? parseRetryAfter(retryAfterRaw) : null;\n const text = await res.text();\n if (!text) {\n return { status: res.status, body: undefined, retryAfter };\n }\n try {\n return { status: res.status, body: JSON.parse(text), retryAfter };\n } catch {\n return { status: res.status, body: { raw: text }, retryAfter };\n }\n }\n\n #computeBackoff(status: number, retryAfter: number | null, attempt: number): number {\n if (status === 429 && retryAfter !== null) {\n return Math.min(retryAfter * 1000, 10_000);\n }\n const base = 300 * Math.pow(2, attempt);\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\n return Math.max(0, Math.round(base + jitter));\n }\n}\n\nfunction parseRetryAfter(raw: string): number | null {\n const sec = Number(raw);\n if (Number.isFinite(sec) && sec >= 0) return sec;\n const date = Date.parse(raw);\n if (Number.isFinite(date)) return Math.max(0, (date - Date.now()) / 1000);\n return null;\n}\n\nfunction sleep(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * In-process deduplication for Feishu webhook events.\n *\n * Feishu retries delivery on non-2xx responses and during brief outage windows,\n * so consumers must idempotently ack events they've already seen. We key by\n * `header.event_id` (or `message.message_id` as a fallback) and remember each\n * key for the TTL window.\n *\n * Backed by an insertion-ordered Map so FIFO eviction is O(1) at the front.\n * Lazy sweep on every insert prevents unbounded growth of expired entries;\n * no `setInterval` so this is safe in serverless.\n */\nexport class DedupMap {\n private readonly entries = new Map<string, number>();\n private readonly ttlMs: number;\n private readonly maxEntries: number;\n private insertsSinceSweep = 0;\n\n constructor(ttlMs: number, maxEntries: number) {\n this.ttlMs = ttlMs;\n this.maxEntries = maxEntries;\n }\n\n has(key: string): boolean {\n const at = this.entries.get(key);\n if (at === undefined) return false;\n if (Date.now() - at > this.ttlMs) {\n this.entries.delete(key);\n return false;\n }\n return true;\n }\n\n set(key: string): void {\n this.maybeSweep();\n // Refresh timestamp by re-inserting at the tail of insertion order.\n this.entries.delete(key);\n this.entries.set(key, Date.now());\n\n while (this.entries.size > this.maxEntries) {\n const oldestKey = this.entries.keys().next().value;\n if (oldestKey === undefined) break;\n this.entries.delete(oldestKey);\n }\n }\n\n /**\n * Walk the insertion-ordered map from the front and drop expired entries.\n * Stops at the first non-expired entry since events arrive roughly in time\n * order. Called on every set, so cost is amortized.\n */\n private maybeSweep(): void {\n this.insertsSinceSweep += 1;\n if (this.insertsSinceSweep < 64 && this.entries.size < this.maxEntries) {\n return;\n }\n this.insertsSinceSweep = 0;\n const now = Date.now();\n for (const [key, at] of this.entries) {\n if (now - at <= this.ttlMs) break;\n this.entries.delete(key);\n }\n }\n}\n","import { createDecipheriv, createHash, timingSafeEqual } from \"node:crypto\";\nimport { LarkDecryptError } from \"./errors.js\";\n\n/**\n * Verify an `X-Lark-Signature` header against the raw webhook body.\n *\n * Feishu computes: `sha256(timestamp + nonce + encrypt_key + body)` and ships\n * the hex digest (optionally prefixed with `sha256=`) in `X-Lark-Signature`.\n * We concatenate the string parts first, then the raw bytes of the body, to\n * avoid a UTF-8 round-trip on the request body.\n *\n * Constant-time compare. Returns false on length mismatch instead of throwing.\n */\nexport function verifySignature(opts: {\n timestamp: string;\n nonce: string;\n encryptKey: string;\n rawBody: Buffer;\n signatureHeader: string;\n}): boolean {\n const expected = opts.signatureHeader.replace(/^sha256=/, \"\");\n const computed = createHash(\"sha256\")\n .update(opts.timestamp + opts.nonce + opts.encryptKey)\n .update(opts.rawBody)\n .digest(\"hex\");\n const a = Buffer.from(computed, \"hex\");\n const b = Buffer.from(expected, \"hex\");\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\n/**\n * Decrypt the `encrypt` field from a Feishu webhook body.\n *\n * Layout:\n * key = SHA256(encrypt_key) // 32 bytes → AES-256\n * buf = base64decode(encrypt_field)\n * iv = buf[0:16]\n * ct = buf[16:] // AES-256-CBC ciphertext\n * plaintext = AES_256_CBC_decrypt(key, iv, ct) // PKCS#7 unpadded\n *\n * Returns the raw plaintext bytes. The caller is expected to JSON.parse them.\n */\nexport function decryptPayload(encryptB64: string, encryptKey: string): Buffer {\n const key = createHash(\"sha256\").update(encryptKey).digest();\n const buf = Buffer.from(encryptB64, \"base64\");\n\n if (buf.length < 32) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext too short (${buf.length} bytes; need >= 32 for IV + one block)`,\n );\n }\n if ((buf.length - 16) % 16 !== 0) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext length ${buf.length} is not 16 + N*16`,\n );\n }\n\n const iv = buf.subarray(0, 16);\n const ct = buf.subarray(16);\n const dec = createDecipheriv(\"aes-256-cbc\", key, iv);\n\n try {\n return Buffer.concat([dec.update(ct), dec.final()]);\n } catch (e) {\n throw new LarkDecryptError(\"eve-lark: AES decrypt failed (bad padding or wrong key)\", {\n cause: e,\n });\n }\n}\n","import type {\n LarkInboundEvent,\n LarkInboundFile,\n LarkInboundResult,\n LarkMention,\n LarkRawMention,\n} from \"./types.js\";\n\nconst MIME_BY_EXT: Record<string, string> = {\n pdf: \"application/pdf\",\n zip: \"application/zip\",\n gz: \"application/gzip\",\n tar: \"application/x-tar\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n csv: \"text/csv\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n json: \"application/json\",\n xml: \"application/xml\",\n html: \"text/html\",\n htm: \"text/html\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n};\n\nfunction mimeFromExt(filename: string | undefined): string {\n if (!filename) return \"application/octet-stream\";\n const dot = filename.lastIndexOf(\".\");\n if (dot < 0) return \"application/octet-stream\";\n return MIME_BY_EXT[filename.slice(dot + 1).toLowerCase()] ?? \"application/octet-stream\";\n}\n\nfunction mentionFromRaw(m: LarkRawMention, botOpenId: string | undefined): LarkMention {\n const isOpenIdOfBot =\n !!botOpenId && !!m.id.open_id && m.id.open_id === botOpenId;\n const isAll = !!m.id.open_id && m.id.open_id === \"all\";\n return {\n key: m.key,\n id: {\n openId: m.id.open_id,\n userId: m.id.user_id,\n unionId: m.id.union_id,\n },\n name: m.name,\n idType: m.id_type ?? \"open_id\",\n isOpenIdOfBot,\n isAll,\n };\n}\n\nfunction stripBotMentions(text: string, mentions: LarkMention[]): string {\n // Feishu ships mentions as opaque placeholders (e.g. \"@_user_1\") in the text\n // body alongside a structured mentions array. Rewrite them to something the\n // model can read:\n // - the bot itself: dropped (the model already knows it's being addressed)\n // - @all: replaced with a literal \"@all\" token\n // - other users: replaced with \"@<display name>\"\n let out = text;\n for (const m of mentions) {\n if (!m.key) continue;\n if (m.isOpenIdOfBot) {\n out = out.split(m.key).join(\"\");\n } else if (m.isAll) {\n out = out.split(m.key).join(\"@all\");\n } else {\n out = out.split(m.key).join(`@${m.name}`);\n }\n }\n return out.replace(/\\s+/g, \" \").trim();\n}\n\ninterface ParsedContent {\n text: string;\n files: LarkInboundFile[];\n}\n\nfunction parseContent(messageType: string, rawContent: string): ParsedContent {\n if (!rawContent) return { text: \"\", files: [] };\n let content: Record<string, unknown>;\n try {\n content = JSON.parse(rawContent) as Record<string, unknown>;\n } catch {\n return { text: \"\", files: [] };\n }\n\n switch (messageType) {\n case \"text\": {\n const text = typeof content.text === \"string\" ? content.text : \"\";\n return { text, files: [] };\n }\n case \"image\": {\n const imageKey = typeof content.image_key === \"string\" ? content.image_key : \"\";\n if (!imageKey) return { text: \"\", files: [] };\n return {\n text: \"\",\n files: [{ fileKey: imageKey, mediaType: \"image/png\", kind: \"image\" }],\n };\n }\n case \"file\": {\n const fileKey = typeof content.file_key === \"string\" ? content.file_key : \"\";\n if (!fileKey) return { text: \"\", files: [] };\n const fileName = typeof content.file_name === \"string\" ? content.file_name : undefined;\n return {\n text: \"\",\n files: [{ fileKey, mediaType: mimeFromExt(fileName), kind: \"file\" }],\n };\n }\n case \"post\": {\n const locale = (content.zh_cn ?? content.en_us ?? content.ja_jp ?? null) as\n | { content?: unknown[][] }\n | null;\n if (!locale?.content) return { text: \"\", files: [] };\n const text = locale.content\n .flatMap((line) =>\n (line ?? [])\n .filter((node): node is { tag: string; text?: unknown } => {\n if (typeof node !== \"object\" || node === null) return false;\n const tag = (node as { tag?: unknown }).tag;\n const text = (node as { text?: unknown }).text;\n return tag === \"text\" && typeof text === \"string\";\n })\n .map((node) => node.text as string),\n )\n .join(\" \");\n return { text, files: [] };\n }\n default:\n // audio, media, sticker, share_chat, share_user, interactive — not in v1 scope.\n return { text: \"\", files: [] };\n }\n}\n\nexport function parseInbound(\n event: LarkInboundEvent,\n botOpenId?: string,\n): LarkInboundResult {\n const messageType = event.message.message_type;\n const parsed = parseContent(messageType, event.message.content);\n const rawMentions = event.message.mentions ?? [];\n const mentions = rawMentions.map((m) => mentionFromRaw(m, botOpenId));\n\n const senderOpenId =\n event.sender.sender_id.open_id ??\n event.sender.sender_id.user_id ??\n event.sender.sender_id.union_id ??\n \"\";\n\n const text =\n messageType === \"text\"\n ? stripBotMentions(parsed.text, mentions)\n : parsed.text;\n\n const chatType = event.chat_type === \"group\" ? \"group\" : \"p2p\";\n const senderType = event.sender.sender_type === \"app\" ? \"app\" : \"user\";\n\n return {\n text,\n files: parsed.files,\n chatId: event.message.chat_id,\n rootId: event.message.root_id ?? null,\n parentId: event.message.parent_id ?? null,\n messageId: event.message.message_id,\n senderOpenId,\n senderType,\n chatType,\n mentions,\n };\n}\n","import type { LarkCard, LarkCardButton, LarkInputRequest } from \"./types.js\";\n\n/**\n * One tool call's renderable state. Mirror of the same name in\n * streaming-controller.ts to avoid a circular import (controller imports\n * card builders, not vice versa).\n */\nexport interface ToolCallEntry {\n name: string;\n state: \"running\" | \"done\" | \"failed\";\n}\n\n/**\n * Render tool calls as a single grey-on-grey lark_md block above the answer\n * buffer. Running tools get `⏳`, completed get `✓` (green), failed get `✗`\n * (red). One line per tool so the user can see the full call history even\n * after the turn ends. Returns undefined when there are no tool calls so\n * callers can skip pushing an empty line.\n */\nexport function renderToolCalls(calls: readonly ToolCallEntry[]): string | undefined {\n if (calls.length === 0) return undefined;\n return calls\n .map((c) => {\n if (c.state === \"running\") return `<font color='blue'>⏳ ${c.name}</font>`;\n if (c.state === \"failed\") return `<font color='red'>✗ ${c.name}</font>`;\n return `<font color='green'>✓ ${c.name}</font>`;\n })\n .join(\"\\n\");\n}\n\nconst BASE_CONFIG = {\n wide_screen_mode: true,\n update_multi: true,\n} as const;\n\n/**\n * Build a Feishu interactive card that surfaces an eve `authorization.required`\n * event: the agent needs the user to sign in to an external service. Renders\n * the connection's display name, a URL button that opens the auth URL, and\n * the user code (if present) so the user can copy-paste it.\n */\nexport function buildAuthCard(opts: {\n displayName: string;\n url: string;\n userCode?: string | undefined;\n}): LarkCard {\n const elements: LarkCard[\"elements\"] = [\n { tag: \"div\", text: { tag: \"lark_md\", content: `Sign in to **${escapeMarkdown(opts.displayName)}** to continue.` } },\n {\n tag: \"action\",\n actions: [\n {\n tag: \"button\",\n text: { tag: \"plain_text\", content: `Sign in with ${opts.displayName}` },\n type: \"primary\",\n url: opts.url,\n },\n ],\n },\n ];\n if (opts.userCode) {\n elements.push({\n tag: \"div\",\n text: { tag: \"lark_md\", content: `Verification code: \\`${escapeMarkdown(opts.userCode)}\\`` },\n });\n }\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/**\n * Build the \"post-authorization\" card that replaces the auth card once the\n * user completes (or declines, or fails) the external sign-in.\n */\nexport function buildAuthCompletedCard(opts: {\n displayName: string;\n outcome: \"authorized\" | \"declined\" | \"failed\" | \"timed-out\" | string;\n reason?: string | undefined;\n}): LarkCard {\n const outcomeLabel: Record<string, string> = {\n authorized: \"✓\",\n declined: \"✗\",\n failed: \"⚠\",\n \"timed-out\": \"⏱\",\n };\n const glyph = outcomeLabel[opts.outcome] ?? \"•\";\n const outcomeText: Record<string, string> = {\n authorized: \"connected\",\n declined: \"declined\",\n failed: \"failed\",\n \"timed-out\": \"timed out\",\n };\n const label = outcomeText[opts.outcome] ?? opts.outcome;\n const suffix = opts.reason ? ` — ${escapeMarkdown(opts.reason)}` : \"\";\n return {\n config: { ...BASE_CONFIG },\n elements: [\n {\n tag: \"div\",\n text: { tag: \"lark_md\", content: `**${escapeMarkdown(opts.displayName)}**: ${glyph} ${label}${suffix}` },\n },\n ],\n };\n}\n\n/**\n * Build a simple single-shot card with the given markdown text. Renders via\n * `div` + `lark_md` so the font size is close to a native chat message\n * (the bare `markdown` element renders noticeably smaller).\n */\nexport function buildTextCard(text: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [{ tag: \"div\", text: { tag: \"lark_md\", content: text } }],\n };\n}\n\n/**\n * Build a streaming card with optional status prefix, tool-call history,\n * answer buffer, and inline ask UI. Tool calls render first (so the user\n * sees what the agent is doing / has done), then status, then the streamed\n * text. If `askRequest` is set, the prompt renders below the text and an\n * `action` row of option buttons is appended so the user can answer inline\n * on the SAME card — no separate ask-card, no separate reply card.\n */\nexport function buildStreamingCard(opts: {\n buffer: string;\n status?: string | undefined;\n toolCalls?: readonly ToolCallEntry[] | undefined;\n askRequest?: LarkInputRequest | null | undefined;\n}): LarkCard {\n const lines: string[] = [];\n const toolLine = renderToolCalls(opts.toolCalls ?? []);\n if (toolLine) lines.push(toolLine);\n if (opts.status) {\n lines.push(`<font color='grey'>${opts.status}</font>`);\n }\n lines.push(opts.buffer.length > 0 ? opts.buffer : \"_…_\");\n if (opts.askRequest) {\n lines.push(`**${opts.askRequest.prompt}**`);\n if (opts.askRequest.allowFreeform && (opts.askRequest.options?.length ?? 0) === 0) {\n lines.push(`<font color='grey'>_Reply to this chat with your answer_</font>`);\n }\n }\n const elements: LarkCard[\"elements\"] = [\n { tag: \"div\", text: { tag: \"lark_md\", content: lines.join(\"\\n\\n\") } },\n ];\n // Append option buttons as an action row when the ask has selectable options.\n // (select_static threshold is handled in buildAskCard; for the inline case\n // we always render buttons since the card already exists and we want\n // one-tap answering.)\n if (opts.askRequest?.options && opts.askRequest.options.length > 0) {\n const buttons: LarkCardButton[] = opts.askRequest.options.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: opts.askRequest!.requestId,\n optionId: opt.id,\n },\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/**\n * Build an error card displayed when a turn fails.\n */\nexport function buildErrorCard(message: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [\n { tag: \"div\", text: { tag: \"lark_md\", content: `<font color='red'>⚠ ${message}</font>` } },\n ],\n };\n}\n\n/** Marker placed in every ask-card button value so the card-action handler\n * can recognise our own callbacks (and ignore card actions from other\n * sources on the same message). */\nexport const ASK_BUTTON_VALUE_MARKER = \"__eveLarkAsk\";\n\n/** Above this many options, switch from buttons to a dropdown menu so the\n * card stays readable. Below it, buttons give one-tap answering. */\nconst ASK_OPTIONS_BUTTON_MAX = 3;\n\n/**\n * Build a Feishu interactive card that surfaces an eve `ask_question`\n * input request.\n *\n * Render choice:\n * - `display === \"select\"` OR options > {@link ASK_OPTIONS_BUTTON_MAX}:\n * a single `select_static` dropdown. The selected optionId comes back in\n * `action.option` (Feishu's select callback shape).\n * - Otherwise (display \"confirmation\" or short option list): one button per\n * option. Each button's `value` carries `{__eveLarkAsk, requestId,\n * optionId}`; Feishu returns it via `action.value` on click.\n *\n * For `allowFreeform: true` with no options, renders just the prompt (the\n * user replies with a normal chat message, which the channel intercepts).\n * For `allowFreeform: true` WITH options, renders the picker AND a footer\n * hint that the user can also type a reply.\n */\nexport function buildAskCard(request: LarkInputRequest): LarkCard {\n const elements: LarkCard[\"elements\"] = [\n { tag: \"div\", text: { tag: \"lark_md\", content: request.prompt } },\n ];\n\n const optionCount = request.options?.length ?? 0;\n if (optionCount > 0) {\n const useSelect =\n request.display === \"select\" || optionCount > ASK_OPTIONS_BUTTON_MAX;\n if (useSelect) {\n elements.push({\n tag: \"action\",\n actions: [\n {\n tag: \"select_static\",\n placeholder: { tag: \"plain_text\", content: \"Select an option…\" },\n options: request.options!.map((opt) => ({\n text: { tag: \"plain_text\", content: opt.label },\n value: opt.id,\n })),\n // Marker carries requestId; optionId is returned via action.option.\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: request.requestId,\n __larkSelect: true,\n },\n },\n ],\n });\n } else {\n const buttons: LarkCardButton[] = request.options!.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: request.requestId,\n optionId: opt.id,\n },\n ...(opt.description\n ? {\n confirm: {\n title: { tag: \"plain_text\", content: opt.label },\n text: { tag: \"plain_text\", content: opt.description },\n },\n }\n : {}),\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n }\n\n if (request.allowFreeform) {\n const hint =\n optionCount > 0\n ? \"_…or reply to this chat with your own answer_\"\n : \"_Reply to this chat with your answer_\";\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: hint } });\n }\n\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/**\n * Build the \"post-click\" card body that replaces the ask-card once the user\n * has answered. Disables further clicks by removing the action row and\n * appending a \"✓ <selected label>\" line.\n *\n * `priorBuffer` is optional streaming text from the controller when the ask\n * was rendered inline on a streaming card — preserves the prior turn's text\n * above the answered prompt instead of wiping it.\n */\nexport function buildAskAnsweredCard(\n request: LarkInputRequest,\n selected: { kind: \"option\"; label: string } | { kind: \"freeform\"; text: string },\n priorBuffer?: string | undefined,\n): LarkCard {\n const elements: LarkCard[\"elements\"] = [];\n if (priorBuffer && priorBuffer.length > 0) {\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: priorBuffer } });\n }\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: request.prompt } });\n const summary =\n selected.kind === \"option\"\n ? `<font color='green'>✓ ${escapeMarkdown(selected.label)}</font>`\n : `<font color='green'>✓ ${escapeMarkdown(selected.text)}</font>`;\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: summary } });\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/** Escape characters that have special meaning in lark_md so user-controlled\n * strings can't inject formatting. */\nfunction escapeMarkdown(s: string): string {\n return s.replace(/[*_`~\\[\\]]/g, (m) => `\\\\${m}`);\n}\n\n// ---------------------------------------------------------------------------\n// CardKit v2 (schema 2.0) card builders. Used by replyMode: \"streaming-v2\".\n// The CardKit v2 schema wraps elements in `body.elements` (vs v1's bare\n// `elements`) and supports `config.streaming_mode` for live-patched cards\n// that render at near-native font size.\n// ---------------------------------------------------------------------------\n\nexport interface CardKitV2Card {\n schema: \"2.0\";\n config: {\n streaming_mode: boolean;\n wide_screen_mode?: boolean;\n update_multi?: boolean;\n };\n body: {\n elements: Array<{ tag: string; text?: { tag: string; content: string }; [k: string]: unknown }>;\n };\n}\n\n/**\n * Build a CardKit v2 card body for streaming. `streamingMode: true` for\n * intermediate patches, `false` for the final card.\n *\n * Uses the v2 `markdown` element (not `div+lark_md`) because CardKit v2\n * renders `markdown` at native chat-message size, while `div+lark_md` in v2\n * defaults to a larger / \"card-like\" size that looks unnatural in a chat\n * thread. (v1 interactive cards render `div+lark_md` at native size; v2\n * flips the defaults — pick the element that gives the look you want.)\n */\nexport function buildCardKitStreamingCard(opts: {\n buffer: string;\n status?: string | undefined;\n streamingMode: boolean;\n toolCalls?: readonly ToolCallEntry[] | undefined;\n askRequest?: LarkInputRequest | null | undefined;\n}): CardKitV2Card {\n const lines: string[] = [];\n const toolLine = renderToolCalls(opts.toolCalls ?? []);\n if (toolLine) lines.push(toolLine);\n if (opts.status) {\n lines.push(`<font color='grey'>${opts.status}</font>`);\n }\n lines.push(opts.buffer.length > 0 ? opts.buffer : \"_…_\");\n if (opts.askRequest) {\n lines.push(`**${opts.askRequest.prompt}**`);\n if (opts.askRequest.allowFreeform && (opts.askRequest.options?.length ?? 0) === 0) {\n lines.push(`<font color='grey'>_Reply to this chat with your answer_</font>`);\n }\n }\n const elements: CardKitV2Card[\"body\"][\"elements\"] = [\n { tag: \"markdown\", content: lines.join(\"\\n\\n\") },\n ];\n if (opts.askRequest?.options && opts.askRequest.options.length > 0) {\n const buttons = opts.askRequest.options.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: opts.askRequest!.requestId,\n optionId: opt.id,\n },\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n return {\n schema: \"2.0\",\n config: {\n streaming_mode: opts.streamingMode,\n wide_screen_mode: true,\n update_multi: true,\n },\n body: { elements },\n };\n}\n\n/**\n * Build a non-streaming CardKit v2 card with the final text. Used as the\n * terminal patch when the turn completes.\n */\nexport function buildCardKitFinalCard(\n text: string,\n toolCalls?: readonly ToolCallEntry[] | undefined,\n askRequest?: LarkInputRequest | null | undefined,\n): CardKitV2Card {\n const lines: string[] = [];\n const toolLine = renderToolCalls(toolCalls ?? []);\n if (toolLine) lines.push(toolLine);\n lines.push(text);\n if (askRequest) {\n lines.push(`**${askRequest.prompt}**`);\n }\n const elements: CardKitV2Card[\"body\"][\"elements\"] = [\n { tag: \"markdown\", content: lines.join(\"\\n\\n\") },\n ];\n if (askRequest?.options && askRequest.options.length > 0) {\n const buttons = askRequest.options.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: askRequest!.requestId,\n optionId: opt.id,\n },\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n return {\n schema: \"2.0\",\n config: { streaming_mode: false, wide_screen_mode: true, update_multi: true },\n body: { elements },\n };\n}\n\n","import {\n buildCardKitFinalCard,\n buildCardKitStreamingCard,\n buildErrorCard,\n buildStreamingCard,\n buildTextCard,\n} from \"./card.js\";\nimport type { LarkInputRequest } from \"./types.js\";\n\ntype State = \"idle\" | \"creating\" | \"streaming\" | \"completed\" | \"aborted\";\n\n/**\n * One tool call's renderable state. Tracked across the turn so the user can\n * see what the agent did, not just the final text. Persistent: a fast tool\n * that completes between patches still shows up because we accumulate rather\n * than overwrite.\n */\nexport interface ToolCallEntry {\n name: string;\n state: \"running\" | \"done\" | \"failed\";\n}\n\ninterface ControllerDeps {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n patchIntervalMs: number;\n createThresholdMs: number;\n /** When true, use CardKit v2 schema (schema 2.0 + streaming_mode) instead\n * of v1 interactive cards. Better font size, slightly different API path. */\n useCardKitV2?: boolean | undefined;\n}\n\ninterface LarkClientLike {\n sendCard(args: {\n chatId: string;\n card: unknown;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n patchCard(args: { messageId: string; card: unknown }): Promise<void>;\n sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n}\n\n/**\n * Streaming interactive-card state machine.\n *\n * idle ──first delta──> creating ──sendCard ok──> streaming ──finalize──> completed\n * │\n * └──sendCard fail──> aborted (flag; static fallback on message.completed)\n *\n * The card is created lazily after `createThresholdMs` of the first delta, so\n * short turns can short-circuit straight to `finalize` (which sends the card\n * with the full answer in one shot). Once streaming, patches are throttled to\n * `patchIntervalMs`.\n *\n * If `sendCard` fails on creation, the controller flips to `fallbackToText`\n * and `finalize`/`ensureFinalized` deliver via `sendText` instead.\n */\nexport class StreamingCardController {\n private deps: ControllerDeps;\n private readonly client: LarkClientLike;\n\n private state: State = \"idle\";\n private buffer = \"\";\n private status: string | undefined;\n private messageId: string | undefined;\n private fallbackToText = false;\n /** Active HITL input request, when set via `setAskRequest`. The card\n * builder appends prompt + buttons to the same streaming card so the\n * user can answer inline instead of getting a separate ask-card. */\n private askRequest: LarkInputRequest | null = null;\n /** Tool calls made during this turn, in order. Rendered above the buffer\n * so users can see what the agent is doing / has done, Claude-Code-style. */\n private toolCalls: ToolCallEntry[] = [];\n\n private createTimer: ReturnType<typeof setTimeout> | null = null;\n private patchTimer: ReturnType<typeof setTimeout> | null = null;\n private patchInFlight: Promise<void> | null = null;\n private patchScheduled = false;\n private lastPatchAt = 0;\n\n constructor(client: LarkClientLike, deps: ControllerDeps) {\n this.client = client;\n this.deps = deps;\n }\n\n appendDelta(text: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.buffer += text;\n if (this.state === \"idle\") {\n this.scheduleCreate();\n } else if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n setStatus(status: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.status = status;\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /**\n * Record the start of a tool call. Renders as `🔧 name` (or `⏳ name` while\n * running). Persistent across patches — won't be lost if the tool completes\n * between throttled patches.\n *\n * Schedules a patch regardless of state (idle/creating/streaming) so the\n * user sees the call even when no text has streamed yet, which is the\n * common case (model often calls a tool before any visible output).\n */\n addToolCall(name: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n // Don't add a duplicate running entry for the same name (some tools\n // batch-call with the same name in one actions.requested event).\n if (this.toolCalls.some((t) => t.name === name && t.state === \"running\")) {\n return;\n }\n this.toolCalls.push({ name, state: \"running\" });\n // Kick the create if we're idle (so the card appears showing the tool\n // call before any text delta). Otherwise patch.\n if (this.state === \"idle\") {\n this.scheduleCreate();\n } else if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /**\n * Mark the most-recently-started running entry for `name` as done/failed.\n * Rendered as `✓ name` (green) or `✗ name` (red). Stays visible — the\n * entry is not removed, so the user sees the full tool history at the\n * end of the turn.\n */\n completeToolCall(name: string, failed = false): void {\n for (let i = this.toolCalls.length - 1; i >= 0; i--) {\n const entry = this.toolCalls[i];\n if (entry && entry.name === name && entry.state === \"running\") {\n entry.state = failed ? \"failed\" : \"done\";\n break;\n }\n }\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /** Read-only view of tool calls (for card builders). */\n getToolCalls(): readonly ToolCallEntry[] {\n return this.toolCalls;\n }\n\n /** Current streaming buffer (for handleCardAction to preserve when patching\n * the \"answered\" state of an inline ask card). */\n getBuffer(): string {\n return this.buffer;\n }\n\n /** Card message id (so input.requested can reuse the existing card instead\n * of creating a separate ask-card). */\n getMessageId(): string | undefined {\n return this.messageId;\n }\n\n /** Aim the next card we create at this user message (quote-reply). Used\n * when messages interleave so the user can tell which reply answers which\n * message. Must be called before the first sendCard (i.e. in the webhook\n * handler right after helpers.send, before any streaming delta).\n *\n * Sets rootId (not parentId) because Feishu's sendMessage API uses root_id\n * for quote-reply; parent_id is ignored. */\n setReplyTarget(messageId: string): void {\n this.deps.rootId = messageId;\n }\n\n /** Active HITL request being rendered inline on this card, or null. */\n getAskRequest(): LarkInputRequest | null {\n return this.askRequest;\n }\n\n /**\n * Render an `ask_question` request inline on this card by appending prompt\n * + option buttons below the streaming text. Patches immediately so the\n * user sees the buttons as soon as the agent asks.\n *\n * If the prior turn already finalized (state=\"completed\"), transition back\n * to \"streaming\" so the next patch can update the same card. The card\n * keeps its messageId — no new card is sent.\n */\n setAskRequest(req: LarkInputRequest): void {\n if (this.state === \"aborted\") return;\n this.askRequest = req;\n if (this.state === \"completed\" && this.messageId) {\n this.state = \"streaming\";\n }\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /** Clear the inline ask request (e.g. after the user clicked an option). */\n clearAskRequest(): void {\n this.askRequest = null;\n }\n\n /**\n * Reset per-turn state for the next turn within the same session. Clears\n * the streaming buffer, tool-call history, status, and any inline ask —\n * but keeps the card messageId and transitions back to \"streaming\" so the\n * next message.appended patches the SAME card. Without this, the second\n * turn's message.completed would create a brand-new card and the user\n * would see N cards for N turns within one logical conversation.\n *\n * Called from the channel's `turn.started` event handler.\n */\n resetForNewTurn(): void {\n this.buffer = \"\";\n this.status = undefined;\n this.toolCalls = [];\n this.askRequest = null;\n this.fallbackToText = false;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n this.patchInFlight = null;\n this.patchScheduled = false;\n // Keep messageId; transition back to \"streaming\" so the next delta patches.\n if (this.messageId) {\n this.state = \"streaming\";\n } else {\n this.state = \"idle\";\n }\n }\n\n /**\n * Full reset for a brand-new inbound user message (not a continuation\n * of the same conversation flow). Same as {@link resetForNewTurn} but\n * ALSO clears the card messageId so the next `sendCard` creates a fresh\n * card. Without this, all top-level messages in the same chat would\n * patch the first card — the user would only see the latest reply\n * overwritten onto one card instead of N independent cards.\n */\n resetForNewMessage(replyTargetMessageId?: string): void {\n this.resetForNewTurn();\n this.messageId = undefined;\n this.state = \"idle\";\n // Quote-reply target for THIS turn's card. Passed in from turn.started\n // (the inbound messageId at the head of the channel's FIFO queue) so each\n // message's card quotes its OWN user message, not whichever arrived last.\n if (replyTargetMessageId) this.deps.rootId = replyTargetMessageId;\n }\n\n async finalize(fullText: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n this.buffer = fullText;\n\n if (this.fallbackToText) {\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n return;\n }\n\n if (this.messageId === undefined) {\n // Never managed to create a card. Send one with the full text in a\n // single shot so the user still gets a card reply.\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: this.deps.useCardKitV2\n ? buildCardKitFinalCard(fullText, this.toolCalls, this.askRequest)\n : buildTextCard(fullText),\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"completed\";\n } catch {\n // Last-resort fallback: plain text.\n this.fallbackToText = true;\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n }\n return;\n }\n\n // Card already exists; flush the final state.\n if (this.patchInFlight) {\n try {\n await this.patchInFlight;\n } catch {\n // swallow; we'll attempt the final patch below\n }\n }\n await this.client.patchCard({\n messageId: this.messageId,\n card: this.deps.useCardKitV2\n ? buildCardKitStreamingCard({ buffer: fullText, streamingMode: false, toolCalls: this.toolCalls, askRequest: this.askRequest })\n : buildStreamingCard({ buffer: fullText, status: undefined, toolCalls: this.toolCalls, askRequest: this.askRequest }),\n });\n this.state = \"completed\";\n }\n\n async abort(error: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n if (this.messageId === undefined) {\n // No card to patch; mark fallback and let finalize/ensureFinalized\n // deliver a plain-text error if asked.\n this.fallbackToText = true;\n this.state = \"aborted\";\n return;\n }\n try {\n await this.client.patchCard({\n messageId: this.messageId,\n card: buildErrorCard(error),\n });\n } finally {\n this.state = \"aborted\";\n }\n }\n\n async ensureFinalized(): Promise<void> {\n if (this.state !== \"completed\" && this.state !== \"aborted\") {\n await this.finalize(this.buffer);\n }\n }\n\n isStreaming(): boolean {\n return this.state === \"streaming\" || this.state === \"creating\";\n }\n\n isCompleted(): boolean {\n return this.state === \"completed\" || this.state === \"aborted\";\n }\n\n private scheduleCreate(): void {\n if (this.createTimer) return;\n this.state = \"creating\";\n // Snapshot rootId: card creation has a createThresholdMs delay, during\n // which a subsequent turn's resetForNewMessage may overwrite deps.rootId.\n // The snapshot ensures the card quotes the user message from the turn\n // that called scheduleCreate (this turn), not a later one.\n const rootIdSnapshot = this.deps.rootId;\n this.createTimer = setTimeout(() => {\n this.createTimer = null;\n void this.doCreate(rootIdSnapshot);\n }, this.deps.createThresholdMs);\n }\n\n private cancelCreateTimer(): void {\n if (this.createTimer) {\n clearTimeout(this.createTimer);\n this.createTimer = null;\n }\n }\n\n private async doCreate(rootIdSnapshot?: string): Promise<void> {\n if (this.state !== \"creating\") return;\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: this.deps.useCardKitV2\n ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls, askRequest: this.askRequest })\n : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls, askRequest: this.askRequest }),\n rootId: rootIdSnapshot ?? this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"streaming\";\n this.lastPatchAt = Date.now();\n } catch (e) {\n console.warn(\n \"[eve-lark] streaming card create failed; will deliver via plain text on finalize:\",\n e instanceof Error ? e.message : e,\n );\n this.fallbackToText = true;\n this.state = \"streaming\"; // keep accepting deltas; finalize will deliver as text\n }\n }\n\n private schedulePatch(): void {\n if (this.patchScheduled) return;\n this.patchScheduled = true;\n const elapsed = Date.now() - this.lastPatchAt;\n const wait = Math.max(0, this.deps.patchIntervalMs - elapsed);\n this.patchTimer = setTimeout(() => {\n this.patchTimer = null;\n this.patchScheduled = false;\n void this.maybeFlushPatch();\n }, wait);\n }\n\n private cancelPatchTimer(): void {\n if (this.patchTimer) {\n clearTimeout(this.patchTimer);\n this.patchTimer = null;\n }\n this.patchScheduled = false;\n }\n\n private async maybeFlushPatch(): Promise<void> {\n if (this.state !== \"streaming\") return;\n if (this.patchInFlight) return;\n if (this.messageId === undefined) return;\n const card = this.deps.useCardKitV2\n ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls, askRequest: this.askRequest })\n : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls, askRequest: this.askRequest });\n this.patchInFlight = this.client\n .patchCard({ messageId: this.messageId, card })\n .catch((e) => {\n // Best-effort: the next delta will retry. Log so operators can see\n // when the card stream is degraded.\n console.warn(\n \"[eve-lark] streaming card patch failed:\",\n e instanceof Error ? e.message : e,\n );\n })\n .finally(() => {\n this.patchInFlight = null;\n this.lastPatchAt = Date.now();\n });\n await this.patchInFlight;\n }\n}\n","import { LarkConfigError } from \"./errors.js\";\nimport type {\n LarkChannelOptions,\n LarkReplyMode,\n LarkTransportMode,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\nconst DEFAULTS = {\n baseUrl: \"https://open.feishu.cn\",\n webhookPath: \"/lark/webhook\",\n // \"streaming-v2\" uses Feishu CardKit v2 (schema 2.0 + streaming_mode) for\n // live-patched interactive cards — the best live UX this channel can offer\n // today. Users who prefer native chat-message size (with markdown) at the\n // cost of no streaming should opt into replyMode: \"post\".\n replyMode: \"streaming-v2\" as LarkReplyMode,\n streamPatchIntervalMs: 1000,\n streamCreateThresholdMs: 400,\n dedupTtlMs: 30 * 60 * 1000,\n dedupMaxEntries: 5000,\n requestTimeoutMs: 15000,\n maxRetries: 2,\n tokenRefreshBufferMs: 5 * 60 * 1000,\n signatureSkewMs: 5 * 60 * 1000,\n ackReaction: \"Typing\" as string | false,\n mode: \"long-connection\" as LarkTransportMode,\n};\n\nconst ENV_KEYS = {\n appId: \"LARK_APP_ID\",\n appSecret: \"LARK_APP_SECRET\",\n verificationToken: \"LARK_VERIFICATION_TOKEN\",\n encryptKey: \"LARK_ENCRYPT_KEY\",\n baseUrl: \"LARK_BASE_URL\",\n botOpenId: \"LARK_BOT_OPEN_ID\",\n replyMode: \"LARK_REPLY_MODE\",\n mode: \"LARK_MODE\",\n} as const;\n\nexport type ResolveEnv = Record<string, string | undefined>;\n\nfunction defaultEnv(): ResolveEnv {\n if (typeof process !== \"undefined\" && process.env) {\n return process.env as ResolveEnv;\n }\n return {};\n}\n\nfunction pick(input: string | undefined, envValue: string | undefined): string | undefined {\n return input ?? envValue;\n}\n\nexport function resolveOptions(\n options: LarkChannelOptions,\n env: ResolveEnv = defaultEnv(),\n): ResolvedLarkOptions {\n const appId = pick(options.appId, env[ENV_KEYS.appId]);\n const appSecret = pick(options.appSecret, env[ENV_KEYS.appSecret]);\n const verificationToken = pick(\n options.verificationToken,\n env[ENV_KEYS.verificationToken],\n );\n\n if (!appId) {\n throw new LarkConfigError(\n `eve-lark: appId is required (option \\`appId\\` or env \\`${ENV_KEYS.appId}\\`)`,\n );\n }\n if (!appSecret) {\n throw new LarkConfigError(\n `eve-lark: appSecret is required (option \\`appSecret\\` or env \\`${ENV_KEYS.appSecret}\\`)`,\n );\n }\n if (!verificationToken) {\n throw new LarkConfigError(\n `eve-lark: verificationToken is required (option \\`verificationToken\\` or env \\`${ENV_KEYS.verificationToken}\\`)`,\n );\n }\n\n const rawBaseUrl = pick(options.baseUrl, env[ENV_KEYS.baseUrl]) ?? DEFAULTS.baseUrl;\n const baseUrl = rawBaseUrl.replace(/\\/+$/, \"\");\n\n const replyModeEnv = env[ENV_KEYS.replyMode];\n const replyMode: LarkReplyMode =\n options.replyMode ??\n (replyModeEnv === \"post\" || replyModeEnv === \"static\" || replyModeEnv === \"streaming\" || replyModeEnv === \"streaming-v2\"\n ? replyModeEnv\n : DEFAULTS.replyMode);\n\n const modeEnv = env[ENV_KEYS.mode];\n const mode: LarkTransportMode =\n options.mode ??\n (modeEnv === \"webhook\" || modeEnv === \"long-connection\" ? modeEnv : DEFAULTS.mode);\n\n return {\n appId,\n appSecret,\n verificationToken,\n encryptKey: pick(options.encryptKey, env[ENV_KEYS.encryptKey]),\n baseUrl,\n botOpenId: pick(options.botOpenId, env[ENV_KEYS.botOpenId]),\n webhookPath: options.webhookPath ?? DEFAULTS.webhookPath,\n replyMode,\n streamPatchIntervalMs: options.streamPatchIntervalMs ?? DEFAULTS.streamPatchIntervalMs,\n streamCreateThresholdMs: options.streamCreateThresholdMs ?? DEFAULTS.streamCreateThresholdMs,\n dedupTtlMs: options.dedupTtlMs ?? DEFAULTS.dedupTtlMs,\n dedupMaxEntries: options.dedupMaxEntries ?? DEFAULTS.dedupMaxEntries,\n requestTimeoutMs: options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs,\n maxRetries: options.maxRetries ?? DEFAULTS.maxRetries,\n tokenRefreshBufferMs: options.tokenRefreshBufferMs ?? DEFAULTS.tokenRefreshBufferMs,\n signatureSkewMs: options.signatureSkewMs ?? DEFAULTS.signatureSkewMs,\n fetch: options.fetch ?? globalThis.fetch,\n ackReaction: options.ackReaction ?? DEFAULTS.ackReaction,\n mode,\n port:\n options.port ??\n (process.env.PORT ? Number(process.env.PORT) : 2000),\n allowFrom: options.allowFrom,\n groupAllowFrom: options.groupAllowFrom,\n groupConfigs: options.groupConfigs,\n asrProvider: options.asrProvider,\n };\n}\n","/**\n * Long-connection transport: when `mode: \"long-connection\"` is set on\n * {@link createLarkChannel} (the default), the channel starts a Feishu\n * `@larksuiteoapi/node-sdk` WSClient as a side effect of construction. Each\n * inbound event is re-encrypted + re-signed and POSTed to the channel's own\n * webhook on localhost, where the standard webhook handler runs (with full\n * access to `send()` etc.). This lets users run the bot against a real Feishu\n * app from `eve dev` alone — no public webhook URL, no second process.\n *\n * The SDK is a hard runtime dependency of this package (declared in\n * `dependencies`), so `pnpm add eve-lark` brings it in automatically. The\n * `import()` below is dynamic only so `mode: \"webhook\"` code paths don't\n * eagerly load the SDK at module import time.\n */\n\nimport {\n createCipheriv,\n createHash,\n randomBytes,\n} from \"node:crypto\";\nimport type { ResolvedLarkOptions } from \"./types.js\";\n\n/** A Feishu v2 envelope we forward to the channel webhook. */\nexport type LarkEvent = {\n schema?: string;\n type?: string;\n challenge?: string;\n token?: string;\n header?: Record<string, unknown>;\n event?: unknown;\n [k: string]: unknown;\n};\n\nexport interface PostEventOptions {\n eveWebhookUrl: string;\n /** When set, the body is AES-encrypted and the request is signed. */\n encryptKey?: string | undefined;\n /** Override for tests. Defaults to globalThis.fetch. */\n fetch?: typeof fetch | undefined;\n}\n\nfunction aesEncrypt(plaintext: Buffer, key: string): Buffer {\n const keyBuf = createHash(\"sha256\").update(key).digest();\n const iv = randomBytes(16);\n const cipher = createCipheriv(\"aes-256-cbc\", keyBuf, iv);\n return Buffer.concat([iv, cipher.update(plaintext), cipher.final()]);\n}\n\nfunction signBody(timestamp: string, nonce: string, body: Buffer, key: string): string {\n return createHash(\"sha256\")\n .update(timestamp + nonce + key)\n .update(body)\n .digest(\"hex\");\n}\n\n/**\n * Forward one Feishu event to the local eve webhook. Re-encrypts and signs\n * when an `encryptKey` is set, so the channel handler exercises its own\n * signature + AES pipeline on every event.\n */\nexport async function postEventToWebhook(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n const plain = Buffer.from(JSON.stringify(event), \"utf8\");\n let body: Buffer;\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n\n if (opts.encryptKey) {\n const encrypted = aesEncrypt(plain, opts.encryptKey).toString(\"base64\");\n body = Buffer.from(JSON.stringify({ encrypt: encrypted }), \"utf8\");\n const ts = Math.floor(Date.now() / 1000).toString();\n const nonce = randomBytes(8).toString(\"hex\");\n const sig = signBody(ts, nonce, body, opts.encryptKey);\n headers[\"x-lark-request-timestamp\"] = ts;\n headers[\"x-lark-request-nonce\"] = nonce;\n headers[\"x-lark-signature\"] = `sha256=${sig}`;\n } else {\n body = plain;\n }\n\n const res = await fetchImpl(opts.eveWebhookUrl, {\n method: \"POST\",\n headers,\n body: new Uint8Array(body.buffer, body.byteOffset, body.byteLength) as never,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `eve-lark: forward to ${opts.eveWebhookUrl} failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : \"\"}`,\n );\n }\n}\n\n/**\n * Post-event wrapper with a single exponential-backoff retry. Catches the\n * common case where `eve dev` is momentarily unavailable (between HMR\n * reloads, mid-restart, GC pause). Without this the event would be dropped\n * on the floor with just a log line — bad UX during dev.\n */\nexport async function postEventToWebhookRetry(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n try {\n await postEventToWebhook(event, opts);\n } catch (firstErr) {\n await new Promise((r) => setTimeout(r, 300));\n // Regenerate signature: timestamp/nonce moved on by ~300ms, and the\n // skew check at the channel handler would reject a stale signature.\n await postEventToWebhook(event, opts).catch((retryErr) => {\n throw retryErr instanceof Error\n ? retryErr\n : new Error(String(retryErr), { cause: firstErr });\n });\n }\n}\n\n/**\n * The Feishu SDK's EventDispatcher passes handlers a payload that may or may\n * not include the outer envelope. Rebuild a v2-shaped envelope so the channel\n * webhook can parse it the same way it parses a raw Feishu POST.\n */\nexport function rebuildEnvelopeFromSdkEvent(\n eventType: string,\n data: unknown,\n ctx: { appId: string; verificationToken: string },\n): LarkEvent {\n const maybeHeader = (data as { header?: Record<string, unknown> })?.header;\n const header =\n maybeHeader && typeof maybeHeader === \"object\"\n ? maybeHeader\n : {\n event_id: `lc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n event_type: eventType,\n create_time: String(Math.floor(Date.now() / 1000)),\n token: ctx.verificationToken,\n app_id: ctx.appId,\n };\n const maybeEvent = (data as { event?: unknown })?.event;\n return {\n schema: \"2.0\",\n header,\n event: maybeEvent ?? data,\n };\n}\n\nexport interface StartLongConnectionArgs {\n resolved: ResolvedLarkOptions;\n eveWebhookUrl: string;\n /** Override logger. */\n log?: ((msg: string) => void) | undefined;\n logError?: ((msg: string, err?: unknown) => void) | undefined;\n /** Test seam: inject a custom SDK module. */\n sdk?: unknown;\n}\n\n/**\n * Detect whether this process is the `eve start` launcher (NOT the nitro\n * server it spawns). The launcher loads the channel module for build\n * discovery but never serves HTTP; the spawned `.output/server/index.mjs`\n * child is what actually runs the server. Without this check, both\n * processes would start a WSClient and Feishu would deliver every event\n * twice.\n *\n * Discriminator: the launcher's `argv[1]` is the eve CLI binary\n * (`.../eve/bin/eve.js` or `.mjs`/`.cjs`/no-ext shim) and `argv[2]` is\n * `start`. The spawned server child has `argv[1] = .output/server/index.mjs`\n * and `argv[2]` unset. `eve dev` runs nitro in-process (no fork), so we\n * DON'T treat it as a launcher — the WSClient must start there.\n *\n * Override: set `EVE_LARK_FORCE_WS=1` to always start (escape hatch in\n * case the heuristic breaks for some package-manager layout).\n */\nexport function isEveStartLauncher(): boolean {\n if (process.env.EVE_LARK_FORCE_WS === \"1\") return false;\n const arg1 = process.argv[1] ?? \"\";\n const isEveBinary = /[/\\\\]eve[/\\\\]bin[/\\\\]eve(?:\\.[cm]?js)?$/.test(arg1);\n return isEveBinary && process.argv[2] === \"start\";\n}\n\n/**\n * Active connections keyed by `${appId}:${eveWebhookUrl}`.\n *\n * Eve's lifecycle can construct the channel module more than once (e.g.,\n * build-time scan + serve-time import, or HMR reload). Each construction\n * would naively start a fresh WSClient — Feishu then delivers every event\n * to BOTH connections, and the user sees double replies.\n *\n * Guard: if a connection for the same key is already running (or starting),\n * the second call resolves immediately without touching the SDK. On\n * failure, the slot is cleared so a retry can succeed.\n *\n * The map lives on `globalThis` so it survives module reloads (HMR, build\n * then serve) — otherwise a reloaded module instance would have its own\n * fresh map and the guard would silently fail.\n *\n * Single-process, so no lock is needed — `Map.has` + `Map.set` from the\n * same synchronous block is atomic in JS's single-threaded runtime.\n */\nconst GLOBAL_KEY = \"__eveLarkActiveConnections\";\ntype GlobalWithLark = typeof globalThis & {\n [GLOBAL_KEY]?: Map<string, Promise<void>>;\n};\nfunction getActiveConnections(): Map<string, Promise<void>> {\n const g = globalThis as GlobalWithLark;\n if (!g[GLOBAL_KEY]) g[GLOBAL_KEY] = new Map();\n return g[GLOBAL_KEY]!;\n}\n\n/** @internal — test-only seam for resetting module state between cases. */\nexport function __resetLongConnectionSingletonsForTests(): void {\n getActiveConnections().clear();\n}\n\n/**\n * Start the Feishu WSClient side effect. Connects via the official SDK,\n * registers a handler that re-signs each event and POSTs it to the local eve\n * webhook. Resolves once the connection is established; the WSClient then\n * runs in the background for the lifetime of the process.\n *\n * Idempotent: a second call with the same `appId` + `eveWebhookUrl` is a\n * no-op (see {@link getActiveConnections}). Different keys (different app,\n * or different webhook URL — e.g. different `--port`) get separate WSClients.\n *\n * @throws if @larksuiteoapi/node-sdk is not installed, or the WSClient\n * fails to establish its first connection.\n */\nexport async function startLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const key = `${args.resolved.appId}:${args.eveWebhookUrl}`;\n const activeConnections = getActiveConnections();\n const existing = activeConnections.get(key);\n if (existing) {\n console.log(\n `[eve-lark] startLongConnection: skip (already running) key=${key} pid=${process.pid}`,\n );\n return;\n }\n console.log(\n `[eve-lark] startLongConnection: start new WSClient key=${key} pid=${process.pid}`,\n );\n\n const promise = (async () => {\n await doStartLongConnection(args);\n })().catch((e) => {\n // On failure, clear the slot so a future call can retry.\n activeConnections.delete(key);\n throw e;\n });\n\n // Synchronous set after the synchronous has-check above — atomic in JS's\n // single-threaded runtime. No race with another concurrent caller.\n activeConnections.set(key, promise);\n await promise;\n}\n\nasync function doStartLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const log = args.log ?? ((m: string) => console.log(`[eve-lark] ${m}`));\n const logError = args.logError ?? ((m: string, e?: unknown) => console.error(`[eve-lark] ${m}`, e ?? \"\"));\n\n const sdk = (args.sdk ?? (await loadLarkSdk())) as LarkSdk;\n\n const dispatcher = new sdk.EventDispatcher({\n verificationToken: args.resolved.verificationToken,\n encryptKey: args.resolved.encryptKey,\n });\n\n dispatcher.register({\n \"im.message.receive_v1\": async (data: unknown) => {\n try {\n const envelope = rebuildEnvelopeFromSdkEvent(\"im.message.receive_v1\", data, {\n appId: args.resolved.appId,\n verificationToken: args.resolved.verificationToken,\n });\n await postEventToWebhookRetry(envelope, {\n eveWebhookUrl: args.eveWebhookUrl,\n encryptKey: args.resolved.encryptKey,\n });\n } catch (e) {\n logError(`forward failed (event dropped)`, e);\n }\n },\n\n // Card-button clicks. Feishu's card.action.trigger fires when a user\n // taps a button on a card we rendered. Forward to the channel webhook\n // — the webhook handler dispatches by event_type and feeds the click\n // back into eve as an InputResponse.\n \"card.action.trigger\": async (data: unknown) => {\n try {\n const envelope = rebuildEnvelopeFromSdkEvent(\"card.action.trigger\", data, {\n appId: args.resolved.appId,\n verificationToken: args.resolved.verificationToken,\n });\n await postEventToWebhookRetry(envelope, {\n eveWebhookUrl: args.eveWebhookUrl,\n encryptKey: args.resolved.encryptKey,\n });\n } catch (e) {\n logError(`card action forward failed (event dropped)`, e);\n }\n },\n });\n\n const domain = args.resolved.baseUrl.includes(\"larksuite.com\")\n ? sdk.Domain.Lark\n : sdk.Domain.Feishu;\n\n const wsClient = new sdk.WSClient({\n appId: args.resolved.appId,\n appSecret: args.resolved.appSecret,\n domain,\n onReady: () => log(`WS connected to Feishu (${args.resolved.baseUrl})`),\n onError: (err: Error) => logError(`WS error`, err),\n onReconnecting: () => log(`WS reconnecting…`),\n onReconnected: () => log(`WS reconnected`),\n autoReconnect: true,\n });\n\n await wsClient.start({ eventDispatcher: dispatcher });\n}\n\n/* eslint-disable @typescript-eslint/consistent-type-imports */\n// The `import()` type query is the canonical way to express \"the runtime\n// namespace of this dynamic import\" without making the SDK a hard dep.\ntype LarkSdk = typeof import(\"@larksuiteoapi/node-sdk\");\n/* eslint-enable @typescript-eslint/consistent-type-imports */\n\nasync function loadLarkSdk(): Promise<LarkSdk> {\n try {\n return await import(\"@larksuiteoapi/node-sdk\");\n } catch {\n throw new Error(\n \"eve-lark: mode:\\\"long-connection\\\" requires @larksuiteoapi/node-sdk. Install it: pnpm add @larksuiteoapi/node-sdk (or npm/yarn equivalent).\",\n );\n }\n}\n","/**\n * Valid Feishu emoji type strings for the message-reactions API.\n *\n * Feishu emoji types are case-sensitive and inconsistent: most are uppercase\n * (`THUMBSUP`, `OK`), but a meaningful subset is CamelCase (`Typing`,\n * `CrossMark`, `EatingFood`, `Drumstick`, …). Passing the wrong case fails\n * with HTTP 400 code=231001 \"reaction type is invalid\" — silent unless the\n * caller is watching logs.\n *\n * Source: openclaw-lark's `VALID_FEISHU_EMOJI_TYPES` (which references the\n * official Feishu emoji doc).\n */\nexport const VALID_FEISHU_EMOJI_TYPES: ReadonlySet<string> = new Set([\n \"OK\", \"THUMBSUP\", \"THANKS\", \"MUSCLE\", \"FINGERHEART\", \"APPLAUSE\", \"FISTBUMP\",\n \"JIAYI\", \"DONE\", \"SMILE\", \"BLUSH\", \"LAUGH\", \"SMIRK\", \"LOL\", \"FACEPALM\",\n \"LOVE\", \"WINK\", \"PROUD\", \"WITTY\", \"SMART\", \"SCOWL\", \"THINKING\", \"SOB\",\n \"CRY\", \"ERROR\", \"NOSEPICK\", \"HAUGHTY\", \"SLAP\", \"SPITBLOOD\", \"TOASTED\",\n \"GLANCE\", \"DULL\", \"INNOCENTSMILE\", \"JOYFUL\", \"WOW\", \"TRICK\", \"YEAH\",\n \"ENOUGH\", \"TEARS\", \"EMBARRASSED\", \"KISS\", \"SMOOCH\", \"DROOL\", \"OBSESSED\",\n \"MONEY\", \"TEASE\", \"SHOWOFF\", \"COMFORT\", \"CLAP\", \"PRAISE\", \"STRIVE\",\n \"XBLUSH\", \"SILENT\", \"WAVE\", \"WHAT\", \"FROWN\", \"SHY\", \"DIZZY\", \"LOOKDOWN\",\n \"CHUCKLE\", \"WAIL\", \"CRAZY\", \"WHIMPER\", \"HUG\", \"BLUBBER\", \"WRONGED\",\n \"HUSKY\", \"SHHH\", \"SMUG\", \"ANGRY\", \"HAMMER\", \"SHOCKED\", \"TERROR\",\n \"PETRIFIED\", \"SKULL\", \"SWEAT\", \"SPEECHLESS\", \"SLEEP\", \"DROWSY\", \"YAWN\",\n \"SICK\", \"PUKE\", \"BETRAYED\", \"HEADSET\", \"EatingFood\", \"MeMeMe\", \"Sigh\",\n \"Typing\", \"SLIGHT\", \"TONGUE\", \"EYESCLOSED\", \"RoarForYou\", \"CALF\", \"BEAR\",\n \"BULL\", \"RAINBOWPUKE\", \"Lemon\", \"ROSE\", \"HEART\", \"PARTY\", \"LIPS\", \"BEER\",\n \"CAKE\", \"GIFT\", \"CUCUMBER\", \"Drumstick\", \"Pepper\", \"CANDIEDHAWS\",\n \"BubbleTea\", \"Coffee\", \"Get\", \"LGTM\", \"OnIt\", \"OneSecond\", \"VRHeadset\",\n \"YouAreTheBest\", \"SALUTE\", \"SHAKE\", \"HIGHFIVE\", \"UPPERLEFT\", \"ThumbsDown\",\n \"Yes\", \"No\", \"OKR\", \"CheckMark\", \"CrossMark\", \"MinusOne\", \"Hundred\",\n \"AWESOMEN\", \"Pin\", \"Alarm\", \"Loudspeaker\", \"Trophy\", \"Fire\", \"BOMB\",\n \"Music\", \"XmasTree\", \"Snowman\", \"XmasHat\", \"FIREWORKS\", \"2022\",\n \"REDPACKET\", \"FORTUNE\", \"LUCK\", \"FIRECRACKER\", \"StickyRiceBalls\",\n \"HEARTBROKEN\", \"POOP\", \"StatusFlashOfInspiration\", \"18X\", \"CLEAVER\",\n \"Soccer\", \"Basketball\", \"GeneralDoNotDisturb\", \"Status_PrivateMessage\",\n \"GeneralInMeetingBusy\", \"StatusReading\", \"StatusInFlight\",\n \"GeneralBusinessTrip\", \"GeneralWorkFromHome\", \"StatusEnjoyLife\",\n \"GeneralTravellingCar\", \"StatusBus\", \"GeneralSun\", \"GeneralMoonRest\",\n \"MoonRabbit\", \"Mooncake\", \"JubilantRabbit\", \"TV\", \"Movie\", \"Pumpkin\",\n \"BeamingFace\", \"Delighted\", \"ColdSweat\", \"FullMoonFace\", \"Partying\",\n \"GoGoGo\", \"ThanksFace\", \"SaluteFace\", \"Shrug\", \"ClownFace\", \"HappyDragon\",\n]);\n\n/** Returns true iff `s` is a valid Feishu emoji type string (case-sensitive). */\nexport function isValidFeishuEmojiType(s: string): boolean {\n return VALID_FEISHU_EMOJI_TYPES.has(s);\n}\n"],"mappings":";;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;;;ACEA,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAP5C,OAO4C;AAAA;AAAA;AAAA,EAC1C,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;AAEO,IAAM,kBAAN,cAA8B,iBAAiB;AAAA,EAdtD,OAcsD;AAAA;AAAA;AAAC;AAEhD,IAAM,qBAAN,cAAiC,iBAAiB;AAAA,EAhBzD,OAgByD;AAAA;AAAA;AAAC;AAEnD,IAAM,mBAAN,cAA+B,iBAAiB;AAAA,EAlBvD,OAkBuD;AAAA;AAAA;AAAC;AAOjD,IAAM,eAAN,cAA2B,iBAAiB;AAAA,EAzBnD,OAyBmD;AAAA;AAAA;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,SACA,MAMA;AACA,UAAM,SAAS,EAAE,OAAO,MAAM,MAAM,CAAC;AACrC,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO,MAAM;AAClB,SAAK,SAAS,MAAM;AAAA,EACtB;AACF;;;ACpCA,IAAM,sBAAsB,oBAAI,IAAY,CAAC,UAAU,UAAU,QAAQ,CAAC;AAQnE,IAAM,aAAN,MAAiB;AAAA,EAhBxB,OAgBwB;AAAA;AAAA;AAAA,EACL;AAAA,EACT,QAA2B;AAAA,EAC3B,iBAAyC;AAAA,EAEjD,YAAY,SAA8B;AACxC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,uBAAwC;AAC5C,QACE,KAAK,SACL,KAAK,IAAI,IAAI,KAAK,QAAQ,uBAAuB,KAAK,MAAM,WAC5D;AACA,aAAO,KAAK,MAAM;AAAA,IACpB;AACA,QAAI,KAAK,eAAgB,QAAO,KAAK;AACrC,SAAK,iBAAiB,KAAK,SAAS;AACpC,QAAI;AACF,aAAO,MAAM,KAAK;AAAA,IACpB,UAAE;AACA,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAM,WAA4B;AAChC,UAAM,OAAO;AAAA,MACX,QAAQ,KAAK,QAAQ;AAAA,MACrB,YAAY,KAAK,QAAQ;AAAA,IAC3B;AACA,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,GAAG,KAAK,QAAQ,OAAO;AAAA,MACvB;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,YAAY,QAAQ,KAAK,QAAQ,gBAAgB;AAAA,MAC3D;AAAA,IACF;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,wCAAwC,IAAI,MAAM;AAAA,QAClD,EAAE,QAAQ,IAAI,OAAO;AAAA,MACvB;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAI,KAAK,SAAS,KAAK,CAAC,KAAK,qBAAqB;AAChD,YAAM,IAAI;AAAA,QACR,yCAAyC,KAAK,QAAQ,GAAG,QAAQ,KAAK,OAAO,GAAG;AAAA,QAChF,EAAE,MAAM,MAAM,MAAM,KAAK,KAAK;AAAA,MAChC;AAAA,IACF;AACA,UAAM,YAAY,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AAClE,SAAK,QAAQ;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,WAAW,KAAK,IAAI,IAAI,YAAY;AAAA,IACtC;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,MAAM,SAAS,MAKoB;AACjC,UAAM,UAAU,KAAK,UAAU,EAAE,MAAM,KAAK,QAAQ,CAAC;AAIrD,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,cAAc,KAAK,QAAQ,QAAQ,OAAO;AAAA,IACxD;AACA,WAAO,KAAK,aAAa;AAAA,MACvB,YAAY,KAAK;AAAA,MACjB,UAAU;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,MAKoB;AACjC,UAAM,UAAU,KAAK,UAAU,KAAK,IAAI;AACxC,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,cAAc,KAAK,QAAQ,eAAe,OAAO;AAAA,IAC/D;AACA,WAAO,KAAK,aAAa;AAAA,MACvB,YAAY,KAAK;AAAA,MACjB,UAAU;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,MAKoB;AASjC,UAAM,OAAO;AAAA,MACX,OAAO;AAAA,QACL,SAAS,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,KAAK,QAAQ,CAAC,CAAC;AAAA,MAC/C;AAAA,IACF;AACA,UAAM,UAAU,KAAK,UAAU,IAAI;AACnC,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK,cAAc,KAAK,QAAQ,QAAQ,OAAO;AAAA,IACxD;AACA,WAAO,KAAK,aAAa;AAAA,MACvB,YAAY,KAAK;AAAA,MACjB,UAAU;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,kBAA0B,SAAiB,SAAiD;AAC9G,UAAM,OAAO,6BAA6B,mBAAmB,gBAAgB,CAAC;AAC9E,UAAM,OAAO,MAAM,KAAK,SAAS,QAAQ,MAAM,EAAE,UAAU,SAAS,QAAQ,CAAC;AAC7E,UAAM,YAAa,KAA4C,MAAM;AACrE,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,aAAa,kDAAkD;AAAA,QACvE,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,EAAE,UAAU;AAAA,EACrB;AAAA,EAEA,MAAM,aAAa,MAA+D;AAChF,UAAM,UAAU,OAAO;AAAA,MACrB,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,MAAS;AAAA,IACxD;AACA,UAAM,OAAO,MAAM,KAAK,SAAS,QAAQ,qDAAqD,OAAO;AACrG,UAAM,YAAa,KAA4C,MAAM;AACrE,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,aAAa,iDAAiD;AAAA,QACtE,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,EAAE,UAAU;AAAA,EACrB;AAAA,EAEA,MAAM,UAAU,MAA4D;AAC1E,UAAM,KAAK;AAAA,MACT;AAAA,MACA,6BAA6B,mBAAmB,KAAK,SAAS,CAAC;AAAA,MAC/D,EAAE,SAAS,KAAK,UAAU,KAAK,IAAI,EAAE;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiB,MAIH;AAClB,UAAM,OAAO,6BAA6B,mBAAmB,KAAK,SAAS,CAAC,cAAc,mBAAmB,KAAK,OAAO,CAAC,SAAS,KAAK,IAAI;AAC5I,UAAM,QAAQ,MAAM,KAAK,qBAAqB;AAC9C,UAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,GAAG,KAAK,QAAQ,OAAO,GAAG,IAAI,IAAI;AAAA,MACrE,QAAQ;AAAA,MACR,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,MAC5C,QAAQ,YAAY,QAAQ,KAAK,QAAQ,gBAAgB;AAAA,IAC3D,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mCAAmC,IAAI,MAAM;AAAA,QAC7C,EAAE,QAAQ,IAAI,OAAO;AAAA,MACvB;AAAA,IACF;AACA,WAAO,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC;AAAA,EAC5C;AAAA,EAEA,MAAM,YAAY,MAGkB;AAClC,UAAM,OAAO,6BAA6B,mBAAmB,KAAK,SAAS,CAAC;AAC5E,UAAM,OAAQ,MAAM,KAAK,SAAS,QAAQ,MAAM;AAAA,MAC9C,eAAe,EAAE,YAAY,KAAK,UAAU;AAAA,IAC9C,CAAC;AACD,UAAM,aAAa,KAAK,MAAM;AAC9B,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,aAAa,6CAA6C;AAAA,QAClE,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,EAAE,WAAW;AAAA,EACtB;AAAA,EAEA,MAAM,eAAe,MAAgE;AACnF,UAAM,OAAO,6BAA6B,mBAAmB,KAAK,SAAS,CAAC,cAAc,mBAAmB,KAAK,UAAU,CAAC;AAC7H,UAAM,KAAK,SAAS,UAAU,MAAM,MAAS;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,SAAS,QAAgB,MAAc,MAAiC;AAC5E,UAAM,MAAM,GAAG,KAAK,QAAQ,OAAO,GAAG,IAAI;AAC1C,QAAI,QAAQ,MAAM,KAAK,qBAAqB;AAC5C,QAAI,iBAAiB;AACrB,UAAM,aAAa,OAAO,YAAY;AACtC,UAAM,kBAAkB,eAAe;AAEvC,aAAS,UAAU,GAAG,WAAW,KAAK,QAAQ,YAAY,WAAW;AACnE,YAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,KAAK;AAAA,QACxC;AAAA,QACA,SAAS;AAAA,UACP,eAAe,UAAU,KAAK;AAAA,UAC9B,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,QAC1D,QAAQ,YAAY,QAAQ,KAAK,QAAQ,gBAAgB;AAAA,MAC3D,CAAC;AAED,YAAM,SAAS,MAAM,KAAK,iBAAiB,GAAG;AAC9C,YAAM,SAAS,OAAO;AAEtB,UAAI,UAAU,OAAO,SAAS,KAAK;AACjC,cAAM,WAAW,OAAO;AACxB,YAAI,YAAY,OAAO,SAAS,SAAS,YAAY,SAAS,SAAS,GAAG;AACxE,cAAI,oBAAoB,IAAI,SAAS,IAAI,KAAK,CAAC,gBAAgB;AAC7D,iBAAK,QAAQ;AACb,oBAAQ,MAAM,KAAK,qBAAqB;AACxC,6BAAiB;AACjB,uBAAW;AACX;AAAA,UACF;AACA,gBAAM,IAAI;AAAA,YACR,aAAa,MAAM,IAAI,IAAI,gBAAgB,SAAS,IAAI,QAAQ,SAAS,OAAO,GAAG;AAAA,YACnF,EAAE,MAAM,SAAS,MAAM,MAAM,UAA8B,OAAO;AAAA,UACpE;AAAA,QACF;AACA,eAAO,OAAO;AAAA,MAChB;AAEA,UAAI,WAAW,OAAO,CAAC,gBAAgB;AACrC,aAAK,QAAQ;AACb,gBAAQ,MAAM,KAAK,qBAAqB;AACxC,yBAAiB;AACjB,mBAAW;AACX;AAAA,MACF;AAEA,YAAM,gBAAgB,WAAW;AACjC,YAAM,cAAc,UAAU,OAAO,SAAS;AAC9C,YAAM,YAAY,iBAAkB,eAAe;AACnD,UAAI,aAAa,UAAU,KAAK,QAAQ,YAAY;AAClD,cAAM,UAAU,KAAK,gBAAgB,QAAQ,OAAO,YAAY,OAAO;AACvE,cAAM,MAAM,OAAO;AACnB;AAAA,MACF;AAEA,YAAM,UAAU,OAAO;AACvB,YAAM,OAAO,SAAS;AACtB,YAAM,MAAM,SAAS;AACrB,YAAM,SAAS,MAAM,SAAS,QAAQ,GAAG,QAAQ,GAAG,KAAK;AACzD,YAAM,IAAI;AAAA,QACR,aAAa,MAAM,IAAI,IAAI,gBAAgB,MAAM,GAAG,MAAM;AAAA,QAC1D,EAAE,QAAQ,MAAM,SAAS,KAAK;AAAA,MAChC;AAAA,IACF;AACA,UAAM,IAAI,aAAa,aAAa,MAAM,IAAI,IAAI,oBAAoB;AAAA,EACxE;AAAA,EAEA,MAAM,iBAAiB,KAAuC;AAC5D,UAAM,gBAAgB,IAAI,QAAQ,IAAI,aAAa;AACnD,UAAM,aAAa,gBAAgB,gBAAgB,aAAa,IAAI;AACpE,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,CAAC,MAAM;AACT,aAAO,EAAE,QAAQ,IAAI,QAAQ,MAAM,QAAW,WAAW;AAAA,IAC3D;AACA,QAAI;AACF,aAAO,EAAE,QAAQ,IAAI,QAAQ,MAAM,KAAK,MAAM,IAAI,GAAG,WAAW;AAAA,IAClE,QAAQ;AACN,aAAO,EAAE,QAAQ,IAAI,QAAQ,MAAM,EAAE,KAAK,KAAK,GAAG,WAAW;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,gBAAgB,QAAgB,YAA2B,SAAyB;AAClF,QAAI,WAAW,OAAO,eAAe,MAAM;AACzC,aAAO,KAAK,IAAI,aAAa,KAAM,GAAM;AAAA,IAC3C;AACA,UAAM,OAAO,MAAM,KAAK,IAAI,GAAG,OAAO;AACtC,UAAM,SAAS,OAAO,OAAO,KAAK,OAAO,IAAI,IAAI;AACjD,WAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM,CAAC;AAAA,EAC9C;AACF;AAEA,SAAS,gBAAgB,KAA4B;AACnD,QAAM,MAAM,OAAO,GAAG;AACtB,MAAI,OAAO,SAAS,GAAG,KAAK,OAAO,EAAG,QAAO;AAC7C,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,KAAK,GAAI;AACxE,SAAO;AACT;AANS;AAQT,SAAS,MAAM,IAA2B;AACxC,MAAI,MAAM,EAAG,QAAO,QAAQ,QAAQ;AACpC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAHS;;;ACtUF,IAAM,WAAN,MAAe;AAAA,EAZtB,OAYsB;AAAA;AAAA;AAAA,EACH,UAAU,oBAAI,IAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACT,oBAAoB;AAAA,EAE5B,YAAY,OAAe,YAAoB;AAC7C,SAAK,QAAQ;AACb,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,IAAI,KAAsB;AACxB,UAAM,KAAK,KAAK,QAAQ,IAAI,GAAG;AAC/B,QAAI,OAAO,OAAW,QAAO;AAC7B,QAAI,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO;AAChC,WAAK,QAAQ,OAAO,GAAG;AACvB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,KAAmB;AACrB,SAAK,WAAW;AAEhB,SAAK,QAAQ,OAAO,GAAG;AACvB,SAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AAEhC,WAAO,KAAK,QAAQ,OAAO,KAAK,YAAY;AAC1C,YAAM,YAAY,KAAK,QAAQ,KAAK,EAAE,KAAK,EAAE;AAC7C,UAAI,cAAc,OAAW;AAC7B,WAAK,QAAQ,OAAO,SAAS;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,aAAmB;AACzB,SAAK,qBAAqB;AAC1B,QAAI,KAAK,oBAAoB,MAAM,KAAK,QAAQ,OAAO,KAAK,YAAY;AACtE;AAAA,IACF;AACA,SAAK,oBAAoB;AACzB,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS;AACpC,UAAI,MAAM,MAAM,KAAK,MAAO;AAC5B,WAAK,QAAQ,OAAO,GAAG;AAAA,IACzB;AAAA,EACF;AACF;;;AC/DA,SAAS,kBAAkB,YAAY,uBAAuB;AAavD,SAAS,gBAAgB,MAMpB;AACV,QAAM,WAAW,KAAK,gBAAgB,QAAQ,YAAY,EAAE;AAC5D,QAAM,WAAW,WAAW,QAAQ,EACjC,OAAO,KAAK,YAAY,KAAK,QAAQ,KAAK,UAAU,EACpD,OAAO,KAAK,OAAO,EACnB,OAAO,KAAK;AACf,QAAM,IAAI,OAAO,KAAK,UAAU,KAAK;AACrC,QAAM,IAAI,OAAO,KAAK,UAAU,KAAK;AACrC,SAAO,EAAE,WAAW,EAAE,UAAU,gBAAgB,GAAG,CAAC;AACtD;AAfgB;AA6BT,SAAS,eAAe,YAAoB,YAA4B;AAC7E,QAAM,MAAM,WAAW,QAAQ,EAAE,OAAO,UAAU,EAAE,OAAO;AAC3D,QAAM,MAAM,OAAO,KAAK,YAAY,QAAQ;AAE5C,MAAI,IAAI,SAAS,IAAI;AACnB,UAAM,IAAI;AAAA,MACR,mCAAmC,IAAI,MAAM;AAAA,IAC/C;AAAA,EACF;AACA,OAAK,IAAI,SAAS,MAAM,OAAO,GAAG;AAChC,UAAM,IAAI;AAAA,MACR,+BAA+B,IAAI,MAAM;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,SAAS,GAAG,EAAE;AAC7B,QAAM,KAAK,IAAI,SAAS,EAAE;AAC1B,QAAM,MAAM,iBAAiB,eAAe,KAAK,EAAE;AAEnD,MAAI;AACF,WAAO,OAAO,OAAO,CAAC,IAAI,OAAO,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;AAAA,EACpD,SAAS,GAAG;AACV,UAAM,IAAI,iBAAiB,2DAA2D;AAAA,MACpF,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACF;AA1BgB;;;AClChB,IAAM,cAAsC;AAAA,EAC1C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEA,SAAS,YAAY,UAAsC;AACzD,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,SAAS,YAAY,GAAG;AACpC,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO,YAAY,SAAS,MAAM,MAAM,CAAC,EAAE,YAAY,CAAC,KAAK;AAC/D;AALS;AAOT,SAAS,eAAe,GAAmB,WAA4C;AACrF,QAAM,gBACJ,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,GAAG,YAAY;AACpD,QAAM,QAAQ,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,GAAG,YAAY;AACjD,SAAO;AAAA,IACL,KAAK,EAAE;AAAA,IACP,IAAI;AAAA,MACF,QAAQ,EAAE,GAAG;AAAA,MACb,QAAQ,EAAE,GAAG;AAAA,MACb,SAAS,EAAE,GAAG;AAAA,IAChB;AAAA,IACA,MAAM,EAAE;AAAA,IACR,QAAQ,EAAE,WAAW;AAAA,IACrB;AAAA,IACA;AAAA,EACF;AACF;AAhBS;AAkBT,SAAS,iBAAiB,MAAc,UAAiC;AAOvE,MAAI,MAAM;AACV,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,EAAE,IAAK;AACZ,QAAI,EAAE,eAAe;AACnB,YAAM,IAAI,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE;AAAA,IAChC,WAAW,EAAE,OAAO;AAClB,YAAM,IAAI,MAAM,EAAE,GAAG,EAAE,KAAK,MAAM;AAAA,IACpC,OAAO;AACL,YAAM,IAAI,MAAM,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE;AAAA,IAC1C;AAAA,EACF;AACA,SAAO,IAAI,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACvC;AAnBS;AA0BT,SAAS,aAAa,aAAqB,YAAmC;AAC5E,MAAI,CAAC,WAAY,QAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAE;AAC9C,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,UAAU;AAAA,EACjC,QAAQ;AACN,WAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAE;AAAA,EAC/B;AAEA,UAAQ,aAAa;AAAA,IACnB,KAAK,QAAQ;AACX,YAAM,OAAO,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAC/D,aAAO,EAAE,MAAM,OAAO,CAAC,EAAE;AAAA,IAC3B;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,WAAW,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC7E,UAAI,CAAC,SAAU,QAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAE;AAC5C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO,CAAC,EAAE,SAAS,UAAU,WAAW,aAAa,MAAM,QAAQ,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,UAAU,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAC1E,UAAI,CAAC,QAAS,QAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAE;AAC3C,YAAM,WAAW,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC7E,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO,CAAC,EAAE,SAAS,WAAW,YAAY,QAAQ,GAAG,MAAM,OAAO,CAAC;AAAA,MACrE;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,SAAU,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS;AAGnE,UAAI,CAAC,QAAQ,QAAS,QAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAE;AACnD,YAAM,OAAO,OAAO,QACjB;AAAA,QAAQ,CAAC,UACP,QAAQ,CAAC,GACP,OAAO,CAAC,SAAkD;AACzD,cAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,gBAAM,MAAO,KAA2B;AACxC,gBAAMA,QAAQ,KAA4B;AAC1C,iBAAO,QAAQ,UAAU,OAAOA,UAAS;AAAA,QAC3C,CAAC,EACA,IAAI,CAAC,SAAS,KAAK,IAAc;AAAA,MACtC,EACC,KAAK,GAAG;AACX,aAAO,EAAE,MAAM,OAAO,CAAC,EAAE;AAAA,IAC3B;AAAA,IACA;AAEE,aAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAE;AAAA,EACjC;AACF;AAtDS;AAwDF,SAAS,aACd,OACA,WACmB;AACnB,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,SAAS,aAAa,aAAa,MAAM,QAAQ,OAAO;AAC9D,QAAM,cAAc,MAAM,QAAQ,YAAY,CAAC;AAC/C,QAAM,WAAW,YAAY,IAAI,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AAEpE,QAAM,eACJ,MAAM,OAAO,UAAU,WACvB,MAAM,OAAO,UAAU,WACvB,MAAM,OAAO,UAAU,YACvB;AAEF,QAAM,OACJ,gBAAgB,SACZ,iBAAiB,OAAO,MAAM,QAAQ,IACtC,OAAO;AAEb,QAAM,WAAW,MAAM,cAAc,UAAU,UAAU;AACzD,QAAM,aAAa,MAAM,OAAO,gBAAgB,QAAQ,QAAQ;AAEhE,SAAO;AAAA,IACL;AAAA,IACA,OAAO,OAAO;AAAA,IACd,QAAQ,MAAM,QAAQ;AAAA,IACtB,QAAQ,MAAM,QAAQ,WAAW;AAAA,IACjC,UAAU,MAAM,QAAQ,aAAa;AAAA,IACrC,WAAW,MAAM,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAnCgB;;;AC7HT,SAAS,gBAAgB,OAAqD;AACnF,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MACJ,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,UAAU,UAAW,QAAO,6BAAwB,EAAE,IAAI;AAChE,QAAI,EAAE,UAAU,SAAU,QAAO,4BAAuB,EAAE,IAAI;AAC9D,WAAO,8BAAyB,EAAE,IAAI;AAAA,EACxC,CAAC,EACA,KAAK,IAAI;AACd;AATgB;AAWhB,IAAM,cAAc;AAAA,EAClB,kBAAkB;AAAA,EAClB,cAAc;AAChB;AAQO,SAAS,cAAc,MAIjB;AACX,QAAM,WAAiC;AAAA,IACrC,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,gBAAgB,eAAe,KAAK,WAAW,CAAC,kBAAkB,EAAE;AAAA,IACnH;AAAA,MACE,KAAK;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,KAAK;AAAA,UACL,MAAM,EAAE,KAAK,cAAc,SAAS,gBAAgB,KAAK,WAAW,GAAG;AAAA,UACvE,MAAM;AAAA,UACN,KAAK,KAAK;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,UAAU;AACjB,aAAS,KAAK;AAAA,MACZ,KAAK;AAAA,MACL,MAAM,EAAE,KAAK,WAAW,SAAS,wBAAwB,eAAe,KAAK,QAAQ,CAAC,KAAK;AAAA,IAC7F,CAAC;AAAA,EACH;AACA,SAAO,EAAE,QAAQ,EAAE,GAAG,YAAY,GAAG,SAAS;AAChD;AA1BgB;AAgCT,SAAS,uBAAuB,MAI1B;AACX,QAAM,eAAuC;AAAA,IAC3C,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AACA,QAAM,QAAQ,aAAa,KAAK,OAAO,KAAK;AAC5C,QAAM,cAAsC;AAAA,IAC1C,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AACA,QAAM,QAAQ,YAAY,KAAK,OAAO,KAAK,KAAK;AAChD,QAAM,SAAS,KAAK,SAAS,WAAM,eAAe,KAAK,MAAM,CAAC,KAAK;AACnE,SAAO;AAAA,IACL,QAAQ,EAAE,GAAG,YAAY;AAAA,IACzB,UAAU;AAAA,MACR;AAAA,QACE,KAAK;AAAA,QACL,MAAM,EAAE,KAAK,WAAW,SAAS,KAAK,eAAe,KAAK,WAAW,CAAC,OAAO,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG;AAAA,MACzG;AAAA,IACF;AAAA,EACF;AACF;AA7BgB;AAoCT,SAAS,cAAc,MAAwB;AACpD,SAAO;AAAA,IACL,QAAQ,EAAE,GAAG,YAAY;AAAA,IACzB,UAAU,CAAC,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,KAAK,EAAE,CAAC;AAAA,EACpE;AACF;AALgB;AAeT,SAAS,mBAAmB,MAKtB;AACX,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAW,gBAAgB,KAAK,aAAa,CAAC,CAAC;AACrD,MAAI,SAAU,OAAM,KAAK,QAAQ;AACjC,MAAI,KAAK,QAAQ;AACf,UAAM,KAAK,sBAAsB,KAAK,MAAM,SAAS;AAAA,EACvD;AACA,QAAM,KAAK,KAAK,OAAO,SAAS,IAAI,KAAK,SAAS,UAAK;AACvD,MAAI,KAAK,YAAY;AACnB,UAAM,KAAK,KAAK,KAAK,WAAW,MAAM,IAAI;AAC1C,QAAI,KAAK,WAAW,kBAAkB,KAAK,WAAW,SAAS,UAAU,OAAO,GAAG;AACjF,YAAM,KAAK,iEAAiE;AAAA,IAC9E;AAAA,EACF;AACA,QAAM,WAAiC;AAAA,IACrC,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,MAAM,KAAK,MAAM,EAAE,EAAE;AAAA,EACtE;AAKA,MAAI,KAAK,YAAY,WAAW,KAAK,WAAW,QAAQ,SAAS,GAAG;AAClE,UAAM,UAA4B,KAAK,WAAW,QAAQ,IAAI,CAAC,SAAS;AAAA,MACtE,KAAK;AAAA,MACL,MAAM,EAAE,KAAK,cAAc,SAAS,IAAI,MAAM;AAAA,MAC9C,MAAM,IAAI,SAAS;AAAA,MACnB,OAAO;AAAA,QACL,CAAC,uBAAuB,GAAG;AAAA,QAC3B,WAAW,KAAK,WAAY;AAAA,QAC5B,UAAU,IAAI;AAAA,MAChB;AAAA,IACF,EAAE;AACF,aAAS,KAAK,EAAE,KAAK,UAAU,SAAS,QAAQ,CAAC;AAAA,EACnD;AACA,SAAO,EAAE,QAAQ,EAAE,GAAG,YAAY,GAAG,SAAS;AAChD;AAxCgB;AA6CT,SAAS,eAAe,SAA2B;AACxD,SAAO;AAAA,IACL,QAAQ,EAAE,GAAG,YAAY;AAAA,IACzB,UAAU;AAAA,MACR,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,4BAAuB,OAAO,UAAU,EAAE;AAAA,IAC3F;AAAA,EACF;AACF;AAPgB;AAYT,IAAM,0BAA0B;AAIvC,IAAM,yBAAyB;AAmBxB,SAAS,aAAa,SAAqC;AAChE,QAAM,WAAiC;AAAA,IACrC,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,QAAQ,OAAO,EAAE;AAAA,EAClE;AAEA,QAAM,cAAc,QAAQ,SAAS,UAAU;AAC/C,MAAI,cAAc,GAAG;AACnB,UAAM,YACJ,QAAQ,YAAY,YAAY,cAAc;AAChD,QAAI,WAAW;AACb,eAAS,KAAK;AAAA,QACZ,KAAK;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,KAAK;AAAA,YACL,aAAa,EAAE,KAAK,cAAc,SAAS,yBAAoB;AAAA,YAC/D,SAAS,QAAQ,QAAS,IAAI,CAAC,SAAS;AAAA,cACtC,MAAM,EAAE,KAAK,cAAc,SAAS,IAAI,MAAM;AAAA,cAC9C,OAAO,IAAI;AAAA,YACb,EAAE;AAAA;AAAA,YAEF,OAAO;AAAA,cACL,CAAC,uBAAuB,GAAG;AAAA,cAC3B,WAAW,QAAQ;AAAA,cACnB,cAAc;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,YAAM,UAA4B,QAAQ,QAAS,IAAI,CAAC,SAAS;AAAA,QAC/D,KAAK;AAAA,QACL,MAAM,EAAE,KAAK,cAAc,SAAS,IAAI,MAAM;AAAA,QAC9C,MAAM,IAAI,SAAS;AAAA,QACnB,OAAO;AAAA,UACL,CAAC,uBAAuB,GAAG;AAAA,UAC3B,WAAW,QAAQ;AAAA,UACnB,UAAU,IAAI;AAAA,QAChB;AAAA,QACA,GAAI,IAAI,cACJ;AAAA,UACE,SAAS;AAAA,YACP,OAAO,EAAE,KAAK,cAAc,SAAS,IAAI,MAAM;AAAA,YAC/C,MAAM,EAAE,KAAK,cAAc,SAAS,IAAI,YAAY;AAAA,UACtD;AAAA,QACF,IACA,CAAC;AAAA,MACP,EAAE;AACF,eAAS,KAAK,EAAE,KAAK,UAAU,SAAS,QAAQ,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,MAAI,QAAQ,eAAe;AACzB,UAAM,OACJ,cAAc,IACV,uDACA;AACN,aAAS,KAAK,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,KAAK,EAAE,CAAC;AAAA,EACvE;AAEA,SAAO,EAAE,QAAQ,EAAE,GAAG,YAAY,GAAG,SAAS;AAChD;AA7DgB;AAwET,SAAS,qBACd,SACA,UACA,aACU;AACV,QAAM,WAAiC,CAAC;AACxC,MAAI,eAAe,YAAY,SAAS,GAAG;AACzC,aAAS,KAAK,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,YAAY,EAAE,CAAC;AAAA,EAC9E;AACA,WAAS,KAAK,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,QAAQ,OAAO,EAAE,CAAC;AAC/E,QAAM,UACJ,SAAS,SAAS,WACd,8BAAyB,eAAe,SAAS,KAAK,CAAC,YACvD,8BAAyB,eAAe,SAAS,IAAI,CAAC;AAC5D,WAAS,KAAK,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,WAAW,SAAS,QAAQ,EAAE,CAAC;AACxE,SAAO,EAAE,QAAQ,EAAE,GAAG,YAAY,GAAG,SAAS;AAChD;AAhBgB;AAoBhB,SAAS,eAAe,GAAmB;AACzC,SAAO,EAAE,QAAQ,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE;AACjD;AAFS;AAiCF,SAAS,0BAA0B,MAMxB;AAChB,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAW,gBAAgB,KAAK,aAAa,CAAC,CAAC;AACrD,MAAI,SAAU,OAAM,KAAK,QAAQ;AACjC,MAAI,KAAK,QAAQ;AACf,UAAM,KAAK,sBAAsB,KAAK,MAAM,SAAS;AAAA,EACvD;AACA,QAAM,KAAK,KAAK,OAAO,SAAS,IAAI,KAAK,SAAS,UAAK;AACvD,MAAI,KAAK,YAAY;AACnB,UAAM,KAAK,KAAK,KAAK,WAAW,MAAM,IAAI;AAC1C,QAAI,KAAK,WAAW,kBAAkB,KAAK,WAAW,SAAS,UAAU,OAAO,GAAG;AACjF,YAAM,KAAK,iEAAiE;AAAA,IAC9E;AAAA,EACF;AACA,QAAM,WAA8C;AAAA,IAClD,EAAE,KAAK,YAAY,SAAS,MAAM,KAAK,MAAM,EAAE;AAAA,EACjD;AACA,MAAI,KAAK,YAAY,WAAW,KAAK,WAAW,QAAQ,SAAS,GAAG;AAClE,UAAM,UAAU,KAAK,WAAW,QAAQ,IAAI,CAAC,SAAS;AAAA,MACpD,KAAK;AAAA,MACL,MAAM,EAAE,KAAK,cAAc,SAAS,IAAI,MAAM;AAAA,MAC9C,MAAM,IAAI,SAAS;AAAA,MACnB,OAAO;AAAA,QACL,CAAC,uBAAuB,GAAG;AAAA,QAC3B,WAAW,KAAK,WAAY;AAAA,QAC5B,UAAU,IAAI;AAAA,MAChB;AAAA,IACF,EAAE;AACF,aAAS,KAAK,EAAE,KAAK,UAAU,SAAS,QAAQ,CAAC;AAAA,EACnD;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN,gBAAgB,KAAK;AAAA,MACrB,kBAAkB;AAAA,MAClB,cAAc;AAAA,IAChB;AAAA,IACA,MAAM,EAAE,SAAS;AAAA,EACnB;AACF;AA7CgB;AAmDT,SAAS,sBACd,MACA,WACA,YACe;AACf,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAW,gBAAgB,aAAa,CAAC,CAAC;AAChD,MAAI,SAAU,OAAM,KAAK,QAAQ;AACjC,QAAM,KAAK,IAAI;AACf,MAAI,YAAY;AACd,UAAM,KAAK,KAAK,WAAW,MAAM,IAAI;AAAA,EACvC;AACA,QAAM,WAA8C;AAAA,IAClD,EAAE,KAAK,YAAY,SAAS,MAAM,KAAK,MAAM,EAAE;AAAA,EACjD;AACA,MAAI,YAAY,WAAW,WAAW,QAAQ,SAAS,GAAG;AACxD,UAAM,UAAU,WAAW,QAAQ,IAAI,CAAC,SAAS;AAAA,MAC/C,KAAK;AAAA,MACL,MAAM,EAAE,KAAK,cAAc,SAAS,IAAI,MAAM;AAAA,MAC9C,MAAM,IAAI,SAAS;AAAA,MACnB,OAAO;AAAA,QACL,CAAC,uBAAuB,GAAG;AAAA,QAC3B,WAAW,WAAY;AAAA,QACvB,UAAU,IAAI;AAAA,MAChB;AAAA,IACF,EAAE;AACF,aAAS,KAAK,EAAE,KAAK,UAAU,SAAS,QAAQ,CAAC;AAAA,EACnD;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ,EAAE,gBAAgB,OAAO,kBAAkB,MAAM,cAAc,KAAK;AAAA,IAC5E,MAAM,EAAE,SAAS;AAAA,EACnB;AACF;AAjCgB;;;AC5TT,IAAM,0BAAN,MAA8B;AAAA,EAhErC,OAgEqC;AAAA;AAAA;AAAA,EAC3B;AAAA,EACS;AAAA,EAET,QAAe;AAAA,EACf,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAIjB,aAAsC;AAAA;AAAA;AAAA,EAGtC,YAA6B,CAAC;AAAA,EAE9B,cAAoD;AAAA,EACpD,aAAmD;AAAA,EACnD,gBAAsC;AAAA,EACtC,iBAAiB;AAAA,EACjB,cAAc;AAAA,EAEtB,YAAY,QAAwB,MAAsB;AACxD,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,YAAY,MAAoB;AAC9B,QAAI,KAAK,UAAU,eAAe,KAAK,UAAU,UAAW;AAC5D,SAAK,UAAU;AACf,QAAI,KAAK,UAAU,QAAQ;AACzB,WAAK,eAAe;AAAA,IACtB,WAAW,KAAK,UAAU,aAAa;AACrC,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,UAAU,QAAsB;AAC9B,QAAI,KAAK,UAAU,eAAe,KAAK,UAAU,UAAW;AAC5D,SAAK,SAAS;AACd,QAAI,KAAK,UAAU,aAAa;AAC9B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,YAAY,MAAoB;AAC9B,QAAI,KAAK,UAAU,eAAe,KAAK,UAAU,UAAW;AAG5D,QAAI,KAAK,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,SAAS,GAAG;AACxE;AAAA,IACF;AACA,SAAK,UAAU,KAAK,EAAE,MAAM,OAAO,UAAU,CAAC;AAG9C,QAAI,KAAK,UAAU,QAAQ;AACzB,WAAK,eAAe;AAAA,IACtB,WAAW,KAAK,UAAU,aAAa;AACrC,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,iBAAiB,MAAc,SAAS,OAAa;AACnD,aAAS,IAAI,KAAK,UAAU,SAAS,GAAG,KAAK,GAAG,KAAK;AACnD,YAAM,QAAQ,KAAK,UAAU,CAAC;AAC9B,UAAI,SAAS,MAAM,SAAS,QAAQ,MAAM,UAAU,WAAW;AAC7D,cAAM,QAAQ,SAAS,WAAW;AAClC;AAAA,MACF;AAAA,IACF;AACA,QAAI,KAAK,UAAU,aAAa;AAC9B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGA,eAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA,EAIA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA,EAIA,eAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAe,WAAyB;AACtC,SAAK,KAAK,SAAS;AAAA,EACrB;AAAA;AAAA,EAGA,gBAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,cAAc,KAA6B;AACzC,QAAI,KAAK,UAAU,UAAW;AAC9B,SAAK,aAAa;AAClB,QAAI,KAAK,UAAU,eAAe,KAAK,WAAW;AAChD,WAAK,QAAQ;AAAA,IACf;AACA,QAAI,KAAK,UAAU,aAAa;AAC9B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGA,kBAAwB;AACtB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,kBAAwB;AACtB,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,YAAY,CAAC;AAClB,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,kBAAkB;AACvB,SAAK,iBAAiB;AACtB,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AAEtB,QAAI,KAAK,WAAW;AAClB,WAAK,QAAQ;AAAA,IACf,OAAO;AACL,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,mBAAmB,sBAAqC;AACtD,SAAK,gBAAgB;AACrB,SAAK,YAAY;AACjB,SAAK,QAAQ;AAIb,QAAI,qBAAsB,MAAK,KAAK,SAAS;AAAA,EAC/C;AAAA,EAEA,MAAM,SAAS,UAAiC;AAC9C,QAAI,KAAK,UAAU,eAAe,KAAK,UAAU,UAAW;AAC5D,SAAK,kBAAkB;AACvB,SAAK,iBAAiB;AACtB,SAAK,SAAS;AAEd,QAAI,KAAK,gBAAgB;AACvB,YAAM,KAAK,OAAO,SAAS;AAAA,QACzB,QAAQ,KAAK,KAAK;AAAA,QAClB,SAAS;AAAA,QACT,QAAQ,KAAK,KAAK;AAAA,QAClB,UAAU,KAAK,KAAK;AAAA,MACtB,CAAC;AACD,WAAK,QAAQ;AACb;AAAA,IACF;AAEA,QAAI,KAAK,cAAc,QAAW;AAGhC,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,OAAO,SAAS;AAAA,UACrC,QAAQ,KAAK,KAAK;AAAA,UAClB,MAAM,KAAK,KAAK,eACZ,sBAAsB,UAAU,KAAK,WAAW,KAAK,UAAU,IAC/D,cAAc,QAAQ;AAAA,UAC1B,QAAQ,KAAK,KAAK;AAAA,UAClB,UAAU,KAAK,KAAK;AAAA,QACtB,CAAC;AACD,aAAK,YAAY,IAAI;AACrB,aAAK,QAAQ;AAAA,MACf,QAAQ;AAEN,aAAK,iBAAiB;AACtB,cAAM,KAAK,OAAO,SAAS;AAAA,UACzB,QAAQ,KAAK,KAAK;AAAA,UAClB,SAAS;AAAA,UACT,QAAQ,KAAK,KAAK;AAAA,UAClB,UAAU,KAAK,KAAK;AAAA,QACtB,CAAC;AACD,aAAK,QAAQ;AAAA,MACf;AACA;AAAA,IACF;AAGA,QAAI,KAAK,eAAe;AACtB,UAAI;AACF,cAAM,KAAK;AAAA,MACb,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,KAAK,OAAO,UAAU;AAAA,MAC1B,WAAW,KAAK;AAAA,MAChB,MAAM,KAAK,KAAK,eACZ,0BAA0B,EAAE,QAAQ,UAAU,eAAe,OAAO,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,CAAC,IAC5H,mBAAmB,EAAE,QAAQ,UAAU,QAAQ,QAAW,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,CAAC;AAAA,IACxH,CAAC;AACD,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,MAAM,OAA8B;AACxC,QAAI,KAAK,UAAU,eAAe,KAAK,UAAU,UAAW;AAC5D,SAAK,kBAAkB;AACvB,SAAK,iBAAiB;AACtB,QAAI,KAAK,cAAc,QAAW;AAGhC,WAAK,iBAAiB;AACtB,WAAK,QAAQ;AACb;AAAA,IACF;AACA,QAAI;AACF,YAAM,KAAK,OAAO,UAAU;AAAA,QAC1B,WAAW,KAAK;AAAA,QAChB,MAAM,eAAe,KAAK;AAAA,MAC5B,CAAC;AAAA,IACH,UAAE;AACA,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEA,MAAM,kBAAiC;AACrC,QAAI,KAAK,UAAU,eAAe,KAAK,UAAU,WAAW;AAC1D,YAAM,KAAK,SAAS,KAAK,MAAM;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,UAAU,eAAe,KAAK,UAAU;AAAA,EACtD;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,UAAU,eAAe,KAAK,UAAU;AAAA,EACtD;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,KAAK,YAAa;AACtB,SAAK,QAAQ;AAKb,UAAM,iBAAiB,KAAK,KAAK;AACjC,SAAK,cAAc,WAAW,MAAM;AAClC,WAAK,cAAc;AACnB,WAAK,KAAK,SAAS,cAAc;AAAA,IACnC,GAAG,KAAK,KAAK,iBAAiB;AAAA,EAChC;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK,aAAa;AACpB,mBAAa,KAAK,WAAW;AAC7B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,gBAAwC;AAC7D,QAAI,KAAK,UAAU,WAAY;AAC/B,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,OAAO,SAAS;AAAA,QACrC,QAAQ,KAAK,KAAK;AAAA,QAClB,MAAM,KAAK,KAAK,eACZ,0BAA0B,EAAE,QAAQ,KAAK,QAAQ,QAAQ,KAAK,QAAQ,eAAe,MAAM,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,CAAC,IACnJ,mBAAmB,EAAE,QAAQ,KAAK,QAAQ,QAAQ,KAAK,QAAQ,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,CAAC;AAAA,QAC3H,QAAQ,kBAAkB,KAAK,KAAK;AAAA,QACpC,UAAU,KAAK,KAAK;AAAA,MACtB,CAAC;AACD,WAAK,YAAY,IAAI;AACrB,WAAK,QAAQ;AACb,WAAK,cAAc,KAAK,IAAI;AAAA,IAC9B,SAAS,GAAG;AACV,cAAQ;AAAA,QACN;AAAA,QACA,aAAa,QAAQ,EAAE,UAAU;AAAA,MACnC;AACA,WAAK,iBAAiB;AACtB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,eAAgB;AACzB,SAAK,iBAAiB;AACtB,UAAM,UAAU,KAAK,IAAI,IAAI,KAAK;AAClC,UAAM,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,kBAAkB,OAAO;AAC5D,SAAK,aAAa,WAAW,MAAM;AACjC,WAAK,aAAa;AAClB,WAAK,iBAAiB;AACtB,WAAK,KAAK,gBAAgB;AAAA,IAC5B,GAAG,IAAI;AAAA,EACT;AAAA,EAEQ,mBAAyB;AAC/B,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAAA,IACpB;AACA,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEA,MAAc,kBAAiC;AAC7C,QAAI,KAAK,UAAU,YAAa;AAChC,QAAI,KAAK,cAAe;AACxB,QAAI,KAAK,cAAc,OAAW;AAClC,UAAM,OAAO,KAAK,KAAK,eACnB,0BAA0B,EAAE,QAAQ,KAAK,QAAQ,QAAQ,KAAK,QAAQ,eAAe,MAAM,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,CAAC,IACnJ,mBAAmB,EAAE,QAAQ,KAAK,QAAQ,QAAQ,KAAK,QAAQ,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW,CAAC;AAC3H,SAAK,gBAAgB,KAAK,OACvB,UAAU,EAAE,WAAW,KAAK,WAAW,KAAK,CAAC,EAC7C,MAAM,CAAC,MAAM;AAGZ,cAAQ;AAAA,QACN;AAAA,QACA,aAAa,QAAQ,EAAE,UAAU;AAAA,MACnC;AAAA,IACF,CAAC,EACA,QAAQ,MAAM;AACb,WAAK,gBAAgB;AACrB,WAAK,cAAc,KAAK,IAAI;AAAA,IAC9B,CAAC;AACH,UAAM,KAAK;AAAA,EACb;AACF;;;ACpbA,IAAM,WAAW;AAAA,EACf,SAAS;AAAA,EACT,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKb,WAAW;AAAA,EACX,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,YAAY,KAAK,KAAK;AAAA,EACtB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,sBAAsB,IAAI,KAAK;AAAA,EAC/B,iBAAiB,IAAI,KAAK;AAAA,EAC1B,aAAa;AAAA,EACb,MAAM;AACR;AAEA,IAAM,WAAW;AAAA,EACf,OAAO;AAAA,EACP,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AAAA,EACX,MAAM;AACR;AAIA,SAAS,aAAyB;AAChC,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,WAAO,QAAQ;AAAA,EACjB;AACA,SAAO,CAAC;AACV;AALS;AAOT,SAAS,KAAK,OAA2B,UAAkD;AACzF,SAAO,SAAS;AAClB;AAFS;AAIF,SAAS,eACd,SACA,MAAkB,WAAW,GACR;AACrB,QAAM,QAAQ,KAAK,QAAQ,OAAO,IAAI,SAAS,KAAK,CAAC;AACrD,QAAM,YAAY,KAAK,QAAQ,WAAW,IAAI,SAAS,SAAS,CAAC;AACjE,QAAM,oBAAoB;AAAA,IACxB,QAAQ;AAAA,IACR,IAAI,SAAS,iBAAiB;AAAA,EAChC;AAEA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,0DAA0D,SAAS,KAAK;AAAA,IAC1E;AAAA,EACF;AACA,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,kEAAkE,SAAS,SAAS;AAAA,IACtF;AAAA,EACF;AACA,MAAI,CAAC,mBAAmB;AACtB,UAAM,IAAI;AAAA,MACR,kFAAkF,SAAS,iBAAiB;AAAA,IAC9G;AAAA,EACF;AAEA,QAAM,aAAa,KAAK,QAAQ,SAAS,IAAI,SAAS,OAAO,CAAC,KAAK,SAAS;AAC5E,QAAM,UAAU,WAAW,QAAQ,QAAQ,EAAE;AAE7C,QAAM,eAAe,IAAI,SAAS,SAAS;AAC3C,QAAM,YACJ,QAAQ,cACP,iBAAiB,UAAU,iBAAiB,YAAY,iBAAiB,eAAe,iBAAiB,iBACtG,eACA,SAAS;AAEf,QAAM,UAAU,IAAI,SAAS,IAAI;AACjC,QAAM,OACJ,QAAQ,SACP,YAAY,aAAa,YAAY,oBAAoB,UAAU,SAAS;AAE/E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,KAAK,QAAQ,YAAY,IAAI,SAAS,UAAU,CAAC;AAAA,IAC7D;AAAA,IACA,WAAW,KAAK,QAAQ,WAAW,IAAI,SAAS,SAAS,CAAC;AAAA,IAC1D,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C;AAAA,IACA,uBAAuB,QAAQ,yBAAyB,SAAS;AAAA,IACjE,yBAAyB,QAAQ,2BAA2B,SAAS;AAAA,IACrE,YAAY,QAAQ,cAAc,SAAS;AAAA,IAC3C,iBAAiB,QAAQ,mBAAmB,SAAS;AAAA,IACrD,kBAAkB,QAAQ,oBAAoB,SAAS;AAAA,IACvD,YAAY,QAAQ,cAAc,SAAS;AAAA,IAC3C,sBAAsB,QAAQ,wBAAwB,SAAS;AAAA,IAC/D,iBAAiB,QAAQ,mBAAmB,SAAS;AAAA,IACrD,OAAO,QAAQ,SAAS,WAAW;AAAA,IACnC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C;AAAA,IACA,MACE,QAAQ,SACP,QAAQ,IAAI,OAAO,OAAO,QAAQ,IAAI,IAAI,IAAI;AAAA,IACjD,WAAW,QAAQ;AAAA,IACnB,gBAAgB,QAAQ;AAAA,IACxB,cAAc,QAAQ;AAAA,IACtB,aAAa,QAAQ;AAAA,EACvB;AACF;AAtEgB;;;ACrChB;AAAA,EACE;AAAA,EACA,cAAAC;AAAA,EACA;AAAA,OACK;AAsBP,SAAS,WAAW,WAAmB,KAAqB;AAC1D,QAAM,SAASC,YAAW,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO;AACvD,QAAM,KAAK,YAAY,EAAE;AACzB,QAAM,SAAS,eAAe,eAAe,QAAQ,EAAE;AACvD,SAAO,OAAO,OAAO,CAAC,IAAI,OAAO,OAAO,SAAS,GAAG,OAAO,MAAM,CAAC,CAAC;AACrE;AALS;AAOT,SAAS,SAAS,WAAmB,OAAe,MAAc,KAAqB;AACrF,SAAOA,YAAW,QAAQ,EACvB,OAAO,YAAY,QAAQ,GAAG,EAC9B,OAAO,IAAI,EACX,OAAO,KAAK;AACjB;AALS;AAYT,eAAsB,mBACpB,OACA,MACe;AACf,QAAM,YAAY,KAAK,SAAS,WAAW;AAC3C,QAAM,QAAQ,OAAO,KAAK,KAAK,UAAU,KAAK,GAAG,MAAM;AACvD,MAAI;AACJ,QAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAE7E,MAAI,KAAK,YAAY;AACnB,UAAM,YAAY,WAAW,OAAO,KAAK,UAAU,EAAE,SAAS,QAAQ;AACtE,WAAO,OAAO,KAAK,KAAK,UAAU,EAAE,SAAS,UAAU,CAAC,GAAG,MAAM;AACjE,UAAM,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,EAAE,SAAS;AAClD,UAAM,QAAQ,YAAY,CAAC,EAAE,SAAS,KAAK;AAC3C,UAAM,MAAM,SAAS,IAAI,OAAO,MAAM,KAAK,UAAU;AACrD,YAAQ,0BAA0B,IAAI;AACtC,YAAQ,sBAAsB,IAAI;AAClC,YAAQ,kBAAkB,IAAI,UAAU,GAAG;AAAA,EAC7C,OAAO;AACL,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,MAAM,UAAU,KAAK,eAAe;AAAA,IAC9C,QAAQ;AAAA,IACR;AAAA,IACA,MAAM,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU;AAAA,EACpE,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,UAAM,IAAI;AAAA,MACR,wBAAwB,KAAK,aAAa,iBAAiB,IAAI,MAAM,IAAI,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE;AAAA,IAChH;AAAA,EACF;AACF;AAjCsB;AAyCtB,eAAsB,wBACpB,OACA,MACe;AACf,MAAI;AACF,UAAM,mBAAmB,OAAO,IAAI;AAAA,EACtC,SAAS,UAAU;AACjB,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAG3C,UAAM,mBAAmB,OAAO,IAAI,EAAE,MAAM,CAAC,aAAa;AACxD,YAAM,oBAAoB,QACtB,WACA,IAAI,MAAM,OAAO,QAAQ,GAAG,EAAE,OAAO,SAAS,CAAC;AAAA,IACrD,CAAC;AAAA,EACH;AACF;AAhBsB;AAuBf,SAAS,4BACd,WACA,MACA,KACW;AACX,QAAM,cAAe,MAA+C;AACpE,QAAM,SACJ,eAAe,OAAO,gBAAgB,WAClC,cACA;AAAA,IACE,UAAU,MAAM,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,IACpE,YAAY;AAAA,IACZ,aAAa,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,CAAC;AAAA,IACjD,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI;AAAA,EACd;AACN,QAAM,aAAc,MAA8B;AAClD,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,OAAO,cAAc;AAAA,EACvB;AACF;AAtBgB;AAmDT,SAAS,qBAA8B;AAC5C,MAAI,QAAQ,IAAI,sBAAsB,IAAK,QAAO;AAClD,QAAM,OAAO,QAAQ,KAAK,CAAC,KAAK;AAChC,QAAM,cAAc,0CAA0C,KAAK,IAAI;AACvE,SAAO,eAAe,QAAQ,KAAK,CAAC,MAAM;AAC5C;AALgB;AA0BhB,IAAM,aAAa;AAInB,SAAS,uBAAmD;AAC1D,QAAM,IAAI;AACV,MAAI,CAAC,EAAE,UAAU,EAAG,GAAE,UAAU,IAAI,oBAAI,IAAI;AAC5C,SAAO,EAAE,UAAU;AACrB;AAJS;AAwBT,eAAsB,oBAAoB,MAA8C;AACtF,QAAM,MAAM,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK,aAAa;AACxD,QAAM,oBAAoB,qBAAqB;AAC/C,QAAM,WAAW,kBAAkB,IAAI,GAAG;AAC1C,MAAI,UAAU;AACZ,YAAQ;AAAA,MACN,8DAA8D,GAAG,QAAQ,QAAQ,GAAG;AAAA,IACtF;AACA;AAAA,EACF;AACA,UAAQ;AAAA,IACN,0DAA0D,GAAG,QAAQ,QAAQ,GAAG;AAAA,EAClF;AAEA,QAAM,WAAW,YAAY;AAC3B,UAAM,sBAAsB,IAAI;AAAA,EAClC,GAAG,EAAE,MAAM,CAAC,MAAM;AAEhB,sBAAkB,OAAO,GAAG;AAC5B,UAAM;AAAA,EACR,CAAC;AAID,oBAAkB,IAAI,KAAK,OAAO;AAClC,QAAM;AACR;AA1BsB;AA4BtB,eAAe,sBAAsB,MAA8C;AACjF,QAAM,MAAM,KAAK,QAAQ,CAAC,MAAc,QAAQ,IAAI,cAAc,CAAC,EAAE;AACrE,QAAM,WAAW,KAAK,aAAa,CAAC,GAAW,MAAgB,QAAQ,MAAM,cAAc,CAAC,IAAI,KAAK,EAAE;AAEvG,QAAM,MAAO,KAAK,OAAQ,MAAM,YAAY;AAE5C,QAAM,aAAa,IAAI,IAAI,gBAAgB;AAAA,IACzC,mBAAmB,KAAK,SAAS;AAAA,IACjC,YAAY,KAAK,SAAS;AAAA,EAC5B,CAAC;AAED,aAAW,SAAS;AAAA,IAClB,yBAAyB,8BAAO,SAAkB;AAChD,UAAI;AACF,cAAM,WAAW,4BAA4B,yBAAyB,MAAM;AAAA,UAC1E,OAAO,KAAK,SAAS;AAAA,UACrB,mBAAmB,KAAK,SAAS;AAAA,QACnC,CAAC;AACD,cAAM,wBAAwB,UAAU;AAAA,UACtC,eAAe,KAAK;AAAA,UACpB,YAAY,KAAK,SAAS;AAAA,QAC5B,CAAC;AAAA,MACH,SAAS,GAAG;AACV,iBAAS,kCAAkC,CAAC;AAAA,MAC9C;AAAA,IACF,GAbyB;AAAA;AAAA;AAAA;AAAA;AAAA,IAmBzB,uBAAuB,8BAAO,SAAkB;AAC9C,UAAI;AACF,cAAM,WAAW,4BAA4B,uBAAuB,MAAM;AAAA,UACxE,OAAO,KAAK,SAAS;AAAA,UACrB,mBAAmB,KAAK,SAAS;AAAA,QACnC,CAAC;AACD,cAAM,wBAAwB,UAAU;AAAA,UACtC,eAAe,KAAK;AAAA,UACpB,YAAY,KAAK,SAAS;AAAA,QAC5B,CAAC;AAAA,MACH,SAAS,GAAG;AACV,iBAAS,8CAA8C,CAAC;AAAA,MAC1D;AAAA,IACF,GAbuB;AAAA,EAczB,CAAC;AAED,QAAM,SAAS,KAAK,SAAS,QAAQ,SAAS,eAAe,IACzD,IAAI,OAAO,OACX,IAAI,OAAO;AAEf,QAAM,WAAW,IAAI,IAAI,SAAS;AAAA,IAChC,OAAO,KAAK,SAAS;AAAA,IACrB,WAAW,KAAK,SAAS;AAAA,IACzB;AAAA,IACA,SAAS,6BAAM,IAAI,2BAA2B,KAAK,SAAS,OAAO,GAAG,GAA7D;AAAA,IACT,SAAS,wBAAC,QAAe,SAAS,YAAY,GAAG,GAAxC;AAAA,IACT,gBAAgB,6BAAM,IAAI,uBAAkB,GAA5B;AAAA,IAChB,eAAe,6BAAM,IAAI,gBAAgB,GAA1B;AAAA,IACf,eAAe;AAAA,EACjB,CAAC;AAED,QAAM,SAAS,MAAM,EAAE,iBAAiB,WAAW,CAAC;AACtD;AA/De;AAuEf,eAAe,cAAgC;AAC7C,MAAI;AACF,WAAO,MAAM,OAAO,yBAAyB;AAAA,EAC/C,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AARe;;;AC5TR,IAAM,2BAAgD,oBAAI,IAAI;AAAA,EACnE;AAAA,EAAM;AAAA,EAAY;AAAA,EAAU;AAAA,EAAU;AAAA,EAAe;AAAA,EAAY;AAAA,EACjE;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAO;AAAA,EAC5D;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAY;AAAA,EAChE;AAAA,EAAO;AAAA,EAAS;AAAA,EAAY;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAa;AAAA,EAC5D;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAiB;AAAA,EAAU;AAAA,EAAO;AAAA,EAAS;AAAA,EAC7D;AAAA,EAAU;AAAA,EAAS;AAAA,EAAe;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAC7D;AAAA,EAAS;AAAA,EAAS;AAAA,EAAW;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAU;AAAA,EAC1D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAO;AAAA,EAAS;AAAA,EAC7D;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAW;AAAA,EAAO;AAAA,EAAW;AAAA,EACzD;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAU;AAAA,EAAW;AAAA,EACvD;AAAA,EAAa;AAAA,EAAS;AAAA,EAAS;AAAA,EAAc;AAAA,EAAS;AAAA,EAAU;AAAA,EAChE;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAW;AAAA,EAAc;AAAA,EAAU;AAAA,EAC/D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAc;AAAA,EAAc;AAAA,EAAQ;AAAA,EAClE;AAAA,EAAQ;AAAA,EAAe;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAClE;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAa;AAAA,EAAU;AAAA,EACnD;AAAA,EAAa;AAAA,EAAU;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAa;AAAA,EAC3D;AAAA,EAAiB;AAAA,EAAU;AAAA,EAAS;AAAA,EAAY;AAAA,EAAa;AAAA,EAC7D;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAa;AAAA,EAAa;AAAA,EAAY;AAAA,EAC1D;AAAA,EAAY;AAAA,EAAO;AAAA,EAAS;AAAA,EAAe;AAAA,EAAU;AAAA,EAAQ;AAAA,EAC7D;AAAA,EAAS;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAa;AAAA,EACxD;AAAA,EAAa;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAe;AAAA,EAC/C;AAAA,EAAe;AAAA,EAAQ;AAAA,EAA4B;AAAA,EAAO;AAAA,EAC1D;AAAA,EAAU;AAAA,EAAc;AAAA,EAAuB;AAAA,EAC/C;AAAA,EAAwB;AAAA,EAAiB;AAAA,EACzC;AAAA,EAAuB;AAAA,EAAuB;AAAA,EAC9C;AAAA,EAAwB;AAAA,EAAa;AAAA,EAAc;AAAA,EACnD;AAAA,EAAc;AAAA,EAAY;AAAA,EAAkB;AAAA,EAAM;AAAA,EAAS;AAAA,EAC3D;AAAA,EAAe;AAAA,EAAa;AAAA,EAAa;AAAA,EAAgB;AAAA,EACzD;AAAA,EAAU;AAAA,EAAc;AAAA,EAAc;AAAA,EAAS;AAAA,EAAa;AAC9D,CAAC;AAGM,SAAS,uBAAuB,GAAoB;AACzD,SAAO,yBAAyB,IAAI,CAAC;AACvC;AAFgB;;;AVPhB,IAAM,iBAAiB;AAKvB,IAAM,mBAAmB,KAAK,KAAK;AAGnC,IAAM,oBAAoB,IAAI,KAAK;AAInC,IAAM,mBAAmB;AAOlB,SAAS,sBACd,QACA,eACuB;AACvB,SAAO,GAAG,MAAM,IAAI,iBAAiB,GAAG;AAC1C;AALgB;AA4BhB,SAAS,mBAAmB,KAAyH;AACnJ,QAAM,QAAS,IAAI,SAAS,MAAM,WAAW,cAAc,CAAC;AAM5D,MAAI,OAAO,MAAM,WAAW,YAAY,CAAC,MAAM,OAAQ,QAAO;AAC9D,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,QAAQ,OAAO,MAAM,kBAAkB,WAAW,MAAM,gBAAgB;AAAA,IACxE,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;AAAA,IAChE,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAAA,EACrE;AACF;AAdS;AAgBT,SAAS,QAAkB;AACzB,SAAO,SAAS,KAAK,EAAE,MAAM,EAAE,CAAC;AAClC;AAFS;AAeT,SAAS,aAAa,UAA8D;AAClF,MAAI,aAAa,MAAO,QAAO;AAC/B,MAAI,OAAO,aAAa,UAAU;AAChC,QAAI,CAAC,uBAAuB,QAAQ,GAAG;AACrC,cAAQ;AAAA,QACN,2BAA2B,QAAQ;AAAA,MAGrC;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,UAAM,QAAQ,SAAS,OAAO,sBAAsB;AACpD,QAAI,MAAM,WAAW,GAAG;AACtB,YAAM,SAAS,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAC7C,cAAQ;AAAA,QACN,2EACW,MAAM,GAAG,SAAS,SAAS,IAAI,aAAQ,EAAE;AAAA,MACtD;AACA,aAAO;AAAA,IACT;AACA,QAAI,MAAM,SAAS,SAAS,QAAQ;AAClC,YAAM,UAAU,SAAS,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;AACjE,cAAQ;AAAA,QACN,wCAAwC,QAAQ,MAAM,2BACjD,QAAQ,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MACrC;AAAA,IACF;AACA,UAAM,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,MAAM,MAAM;AACnD,WAAO,MAAM,GAAG,KAAK;AAAA,EACvB;AACA,SAAO;AACT;AAlCS;AAoCT,SAAS,YACP,SACA,MACA,WACQ;AACR,QAAM,OAAO,KAAK,SAAS,UAAU,UAAU;AAC/C,SAAO,GAAG,QAAQ,OAAO,6BAA6B,mBAAmB,SAAS,CAAC,cAAc,mBAAmB,KAAK,OAAO,CAAC,SAAS,IAAI;AAChJ;AAPS;AAeT,SAAS,iBACP,MACA,OACA,SACA,WACW;AACX,QAAM,QAAmB,CAAC;AAC1B,MAAI,KAAK,SAAS,EAAG,OAAM,KAAK,EAAE,MAAM,QAAQ,KAAK,CAAC;AACtD,aAAW,KAAK,OAAO;AACrB,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,MAAM,IAAI,IAAI,YAAY,SAAS,GAAG,SAAS,CAAC;AAAA,MAChD,WAAW,EAAE;AAAA,IACf,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAhBS;AAsBT,eAAe,eACb,QACA,MACA,QACe;AACf,QAAM,QAAkB,CAAC,4BAA4B,EAAE;AACvD,QAAM,KAAK,YAAY,KAAK,KAAK,IAAI;AACrC,QAAM,KAAK,cAAc,KAAK,OAAO,IAAI;AACzC,QAAM,KAAK,WAAW,KAAK,IAAI,IAAI;AACnC,QAAM,KAAK,gBAAgB,KAAK,SAAS,IAAI;AAC7C,QAAM,KAAK,eAAe,KAAK,aAAa,eAAU,gBAAW,EAAE;AACnE,QAAM,KAAK,kBAAkB,KAAK,gBAAgB,QAAQ,aAAa,KAAK,WAAW,IAAI;AAE3F,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,kBAAkB;AAC7B,MAAI;AACF,UAAM,QAAQ,MAAM,OAAO,qBAAqB;AAChD,UAAM,KAAK,+BAA0B,MAAM,MAAM,GAAG,CAAC,CAAC,QAAG;AAAA,EAC3D,SAAS,GAAG;AACV,UAAM,KAAK,kBAAa,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,EACtE;AAEA,QAAM,SAAS,MAAM,KAAK,IAAI;AAC9B,MAAI;AACF,UAAM,OAAO,SAAS,EAAE,QAAQ,SAAS,OAAO,CAAC;AAAA,EACnD,SAAS,GAAG;AACV,YAAQ,MAAM,iDAAiD,CAAC;AAAA,EAClE;AACF;AA5Be;AA0Cf,SAAS,gBAAgB,MAAuB;AAC9C,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,IAAI;AACV,QAAM,cACJ,OAAO,EAAE,YAAY,YAAY,EAAE,YAAY,OAC1C,EAAE,QAA+B,OAClC;AACN,QAAM,OACJ,OAAO,gBAAgB,YAAY,YAAY,SAAS,IAAI,cAAc;AAC5E,QAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,KAAK,IAAI;AACnE,MAAI,QAAQ,QAAQ,SAAS,EAAG,QAAO,KAAK,IAAI,KAAK,mBAAmB,OAAO,CAAC;AAChF,MAAI,KAAM,QAAO,KAAK,IAAI;AAC1B,MAAI,QAAQ,SAAS,EAAG,QAAO,KAAK,mBAAmB,OAAO,CAAC;AAC/D,SAAO;AACT;AAdS;AAoBT,SAAS,eAAe,SAAsC;AAC5D,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,UAAM,KAAM,QAAkC;AAC9C,WAAO,OAAO,OAAO,YAAY,GAAG,SAAS,IAAI,KAAK;AAAA,EACxD;AACA,SAAO;AACT;AANS;AAQT,SAAS,mBAAmB,GAAW,MAAM,KAAa;AACxD,SAAO,EAAE,UAAU,MAAM,IAAI,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,QAAQ,CAAC;AAC/D;AAFS;AAaT,SAAS,qBACP,MACA,UACA,OAAyC,EAAE,UAAU,OAAO,GACpD;AACR,QAAM,OAAO,gBAAgB,IAAI;AACjC,QAAM,UAAU,eAAgB,MAAuC,OAAO;AAC9E,QAAM,OACJ,KAAK,aAAa,YACd,gDACA;AACN,QAAM,WAAW,UAAU,eAAe,OAAO,MAAM;AAIvD,MAAI,CAAC,QAAQ,CAAC,QAAS,QAAO,UAAK,QAAQ;AAC3C,SAAO,UAAK,IAAI,GAAG,IAAI,IAAI,QAAQ;AACrC;AAjBS;AAqCF,SAAS,kBACd,cACsE;AACtE,QAAM,UAAU,eAAe,YAAY;AAC3C,QAAM,SAAS,IAAI,WAAW,OAAO;AACrC,QAAM,QAAQ,IAAI,SAAS,QAAQ,YAAY,QAAQ,eAAe;AAWtE,MAAI,QAAQ,SAAS,qBAAqB,CAAC,mBAAmB,GAAG;AAC/D,UAAM,gBAAgB,oBAAoB,QAAQ,IAAI,GAAG,QAAQ,WAAW;AAC5E,SAAK,oBAAoB,EAAE,UAAU,SAAS,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM;AAC1E,cAAQ,MAAM,8CAA8C,CAAC;AAAA,IAC/D,CAAC;AAAA,EACH,WAAW,QAAQ,SAAS,qBAAqB,mBAAmB,GAAG;AACrE,YAAQ,IAAI,oGAAoG;AAAA,EAClH;AAgBA,QAAM,cAAc,oBAAI,IAAqC;AAC7D,QAAM,cAAc,oBAAI,IAAoB;AAC5C,QAAM,gBAAgB,oBAAI,IAAoB;AAC9C,QAAM,cAAc,oBAAI,IAA6B;AAyBrD,QAAM,2BAA2B,oBAAI,IAA0B;AAC/D,QAAM,2BAA2B,oBAAI,IAA0B;AAI/D,QAAM,YAAY,oBAAI,IAAoB;AAO1C,QAAM,eAAe,oBAAI,IAAuD;AAOhF,QAAM,uBAAuB,oBAAI,IAAsB;AAMvD,QAAM,kBAAkB,oBAAI,IAAoB;AAMhD,QAAM,eAAe,oBAAI,IAAY;AAErC,WAAS,cAAc,QAAgB,MAAoD;AACzF,QAAI,OAAO,YAAY,IAAI,MAAM;AACjC,QAAI,CAAC,MAAM;AACT,aAAO,IAAI,wBAAwB,QAAQ;AAAA,QACzC,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf,iBAAiB,QAAQ;AAAA,QACzB,mBAAmB,QAAQ;AAAA,QAC3B,cAAc,QAAQ,cAAc;AAAA,MACtC,CAAC;AACD,kBAAY,IAAI,QAAQ,IAAI;AAAA,IAC9B;AACA,kBAAc,IAAI,QAAQ,KAAK,IAAI,CAAC;AACpC,WAAO;AAAA,EACT;AAfS;AAoBT,WAAS,SAAS,QAAsB;AACtC,gBAAY,OAAO,MAAM;AACzB,gBAAY,OAAO,MAAM;AACzB,kBAAc,OAAO,MAAM;AAAA,EAC7B;AAJS;AAUT,iBAAe,mBAAmB,WAAkC;AAClE,UAAM,MAAM,aAAa,IAAI,SAAS;AACtC,QAAI,CAAC,IAAK;AACV,QAAI;AACF,YAAM,OAAO,eAAe,EAAE,WAAW,YAAY,IAAI,WAAW,CAAC;AAAA,IACvE,SAAS,GAAG;AACV,cAAQ;AAAA,QACN;AAAA,QACA,aAAa,QAAQ,EAAE,UAAU;AAAA,MACnC;AAAA,IACF;AACA,iBAAa,OAAO,SAAS;AAAA,EAC/B;AAZe;AAkBf,iBAAe,qBAAqB,WAAkC;AACpE,UAAM,QAAQ,qBAAqB,IAAI,SAAS;AAChD,QAAI,CAAC,SAAS,MAAM,WAAW,EAAG;AAClC,UAAM,QAAQ,MAAM,MAAM;AAC1B,QAAI,CAAC,MAAO;AACZ,QAAI,MAAM,WAAW,EAAG,sBAAqB,OAAO,SAAS;AAC7D,UAAM,mBAAmB,KAAK;AAAA,EAChC;AAPe;AAYf,iBAAe,kBAAkB,QAA+B;AAC9D,UAAM,cAAc,YAAY,IAAI,MAAM;AAC1C,QAAI,CAAC,YAAa;AAClB,UAAM,mBAAmB,WAAW;AAAA,EACtC;AAJe;AA6Bf,iBAAe,aAAa,QAA4B,MAA2B,MAA6B;AAC9G,QAAI,QAAQ,cAAc,QAAQ;AAChC,UAAI;AACF,cAAM,OAAO,SAAS;AAAA,UACpB,QAAQ,KAAK;AAAA,UACb,SAAS;AAAA,UACT,QAAQ,KAAK;AAAA,UACb,UAAU,KAAK;AAAA,QACjB,CAAC;AACD,gBAAQ,IAAI,6CAA6C,UAAU,GAAG,GAAG;AACzE;AAAA,MACF,SAAS,SAAS;AAChB,gBAAQ;AAAA,UACN,kEAAkE,UAAU,GAAG;AAAA,UAC/E,mBAAmB,QAAQ,QAAQ,UAAU;AAAA,QAC/C;AAAA,MAEF;AAEA,UAAI;AACF,cAAM,OAAO,SAAS;AAAA,UACpB,QAAQ,KAAK;AAAA,UACb,SAAS;AAAA,UACT,QAAQ,KAAK;AAAA,UACb,UAAU,KAAK;AAAA,QACjB,CAAC;AACD,gBAAQ,IAAI,sDAAsD,UAAU,GAAG,GAAG;AAAA,MACpF,SAAS,SAAS;AAChB,gBAAQ;AAAA,UACN,sFAAsF,UAAU,GAAG;AAAA,UACnG,mBAAmB,QAAQ,QAAQ,UAAU;AAAA,QAC/C;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,QAAQ,cAAc,eAAe,QAAQ,cAAc,gBAAgB;AAC7E,YAAM,OAAO,SAAS,YAAY,IAAI,MAAM,IAAI;AAChD,UAAI,MAAM;AACR,YAAI;AACF,gBAAM,KAAK,SAAS,IAAI;AACxB,kBAAQ,IAAI,uDAAuD,MAAM,GAAG;AAC5E;AAAA,QACF,SAAS,GAAG;AACV,kBAAQ;AAAA,YACN,4EAA4E,MAAM;AAAA,YAClF,aAAa,QAAQ,EAAE,UAAU;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IAGF;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,OAAO,SAAS;AAAA,QAChC,QAAQ,KAAK;AAAA,QACb,MAAM,cAAc,IAAI;AAAA,QACxB,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,MACjB,CAAC;AACD,cAAQ,IAAI,6CAA6C,UAAU,GAAG,GAAG;AACzE,WAAK;AACL;AAAA,IACF,SAAS,SAAS;AAChB,cAAQ;AAAA,QACN,kEAAkE,UAAU,GAAG;AAAA,QAC/E,mBAAmB,QAAQ,QAAQ,UAAU;AAAA,MAC/C;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,SAAS;AAAA,QACpB,QAAQ,KAAK;AAAA,QACb,SAAS;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,MACjB,CAAC;AACD,cAAQ,IAAI,sDAAsD,UAAU,GAAG,GAAG;AAAA,IACpF,SAAS,SAAS;AAChB,cAAQ;AAAA,QACN,sFAAsF,UAAU,GAAG;AAAA,QACnG,mBAAmB,QAAQ,QAAQ,UAAU;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AArFe;AA0Ff,MAAI,cAAc;AAClB,WAAS,aAAmB;AAC1B,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,cAAc,kBAAmB;AAC3C,kBAAc;AACd,UAAM,SAAS,MAAM;AACrB,eAAW,CAAC,QAAQ,SAAS,KAAK,eAAe;AAC/C,UAAI,YAAY,QAAQ;AACtB,iBAAS,MAAM;AAAA,MACjB;AAAA,IACF;AACA,eAAW,CAAC,IAAI,IAAI,KAAK,aAAa;AACpC,UAAI,KAAK,YAAY,QAAQ;AAC3B,oBAAY,OAAO,EAAE;AAAA,MACvB;AAAA,IACF;AACA,eAAW,CAAC,OAAO,CAAC,KAAK,0BAA0B;AACjD,UAAI,EAAE,YAAY,QAAQ;AACxB,iCAAyB,OAAO,KAAK;AACrC,cAAM,WAAW,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;AAC5D,YAAI,yBAAyB,IAAI,QAAQ,GAAG,cAAc,OAAO;AAC/D,mCAAyB,OAAO,QAAQ;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAxBS;AA2BT,WAAS,aAAa,QAAgB,QAAiB,UAA2B;AAChF,WAAO,GAAG,MAAM,IAAI,YAAY,UAAU,GAAG;AAAA,EAC/C;AAFS;AAIT,WAAS,iBAAiB,GAAuB;AAC/C,6BAAyB,OAAO,EAAE,SAAS;AAC3C,UAAM,WAAW,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;AAC5D,QAAI,yBAAyB,IAAI,QAAQ,GAAG,cAAc,EAAE,WAAW;AACrE,+BAAyB,OAAO,QAAQ;AAAA,IAC1C;AAAA,EACF;AANS;AAQT,QAAM,iBAAiB,8BACrB,KACA,YACsB;AACtB,eAAW;AAGX,UAAM,gBAAgB,OAAO,IAAI,QAAQ,IAAI,gBAAgB,KAAK,GAAG;AACrE,QAAI,OAAO,SAAS,aAAa,KAAK,gBAAgB,gBAAgB;AACpE,aAAO,IAAI,SAAS,0BAA0B,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/D;AACA,UAAM,UAAU,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC;AACnD,QAAI,QAAQ,aAAa,gBAAgB;AACvC,aAAO,IAAI,SAAS,0BAA0B,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/D;AAGA,UAAM,WAAW,IAAI,QAAQ,IAAI,0BAA0B,KAAK;AAChE,UAAM,KAAK,OAAO,QAAQ;AAC1B,QACE,YACA,OAAO,SAAS,EAAE,KAClB,KAAK,KACL,KAAK,IAAI,KAAK,IAAI,IAAI,MAAO,EAAE,IAAI,QAAQ,kBAAkB,KAC7D;AACA,aAAO,IAAI,SAAS,wCAAwC,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7E;AAGA,QAAI,cAAsB;AAC1B,QAAI,QAAQ,YAAY;AACtB,YAAM,QAAQ,IAAI,QAAQ,IAAI,sBAAsB,KAAK;AACzD,YAAM,YAAY,IAAI,QAAQ,IAAI,kBAAkB;AACpD,UAAI,CAAC,UAAW,QAAO,IAAI,SAAS,qBAAqB,EAAE,QAAQ,IAAI,CAAC;AACxE,YAAM,KAAK,gBAAgB;AAAA,QACzB,WAAW;AAAA,QACX;AAAA,QACA,YAAY,QAAQ;AAAA,QACpB;AAAA,QACA,iBAAiB;AAAA,MACnB,CAAC;AACD,UAAI,CAAC,GAAI,QAAO,IAAI,SAAS,iBAAiB,EAAE,QAAQ,IAAI,CAAC;AAE7D,UAAI;AACF,cAAM,WAAW,KAAK,MAAM,QAAQ,SAAS,MAAM,CAAC;AACpD,YAAI,SAAS,SAAS;AACpB,wBAAc,eAAe,SAAS,SAAS,QAAQ,UAAU;AAAA,QACnE;AAAA,MACF,QAAQ;AACN,eAAO,IAAI,SAAS,kBAAkB,EAAE,QAAQ,IAAI,CAAC;AAAA,MACvD;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,MAAM,YAAY,SAAS,MAAM,CAAC;AAAA,IAChD,QAAQ;AACN,aAAO,IAAI,SAAS,gBAAgB,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrD;AAGA,QAAI,KAAK,SAAS,oBAAoB;AACpC,aAAO,SAAS,KAAK,EAAE,WAAW,KAAK,aAAa,GAAG,CAAC;AAAA,IAC1D;AAGA,QAAI,KAAK,WAAW,OAAO;AACzB,aAAO,MAAM;AAAA,IACf;AAGA,QAAI,KAAK,QAAQ,UAAU,QAAQ,mBAAmB;AACpD,aAAO,IAAI,SAAS,+BAA+B,EAAE,QAAQ,IAAI,CAAC;AAAA,IACpE;AAKA,UAAM,SAAS,KAAK;AACpB,UAAM,WAAW,KAAK,QAAQ,YAAY,QAAQ,SAAS,cAAc,QAAQ;AACjF,QAAI,UAAU;AACZ,UAAI,MAAM,IAAI,QAAQ,EAAG,QAAO,MAAM;AACtC,YAAM,IAAI,QAAQ;AAAA,IACpB;AAMA,UAAM,YAAY,KAAK,QAAQ;AAC/B,QAAI,cAAc,uBAAuB;AACvC,aAAO,iBAAiB,KAAK,OAAqC,OAAO;AAAA,IAC3E;AACA,QAAI,cAAc,yBAAyB;AACzC,aAAO,MAAM;AAAA,IACf;AACA,QAAI,CAAC,KAAK,MAAO,QAAO,MAAM;AAG9B,UAAM,SAAS,aAAa,KAAK,OAA2B,QAAQ,SAAS;AAG7E,QAAI,OAAO,eAAe,OAAO;AAC/B,aAAO,MAAM;AAAA,IACf;AAMA,QAAI,OAAO,aAAa,SAAS,QAAQ,WAAW;AAClD,UAAI,CAAC,QAAQ,UAAU,SAAS,OAAO,YAAY,GAAG;AACpD,gBAAQ;AAAA,UACN,sDAAsD,OAAO,YAAY;AAAA,QAC3E;AACA,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AACA,QAAI,OAAO,aAAa,WAAW,QAAQ,gBAAgB;AACzD,UAAI,CAAC,QAAQ,eAAe,SAAS,OAAO,MAAM,GAAG;AACnD,gBAAQ;AAAA,UACN,+DAA+D,OAAO,MAAM;AAAA,QAC9E;AACA,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAOA,QAAI,QAAQ,eAAe,OAAO,SAAS,MAAM,OAAO,MAAM,WAAW,GAAG;AAC1E,YAAM,WAAW,KAAK;AACtB,YAAM,UAAU,SAAS,SAAS;AAClC,UAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,YAAI;AACF,gBAAM,UAAU,KAAK,MAAM,SAAS,QAAQ,OAAO;AACnD,cAAI,QAAQ,UAAU;AACpB,kBAAM,QAAQ,MAAM,OAAO,iBAAiB;AAAA,cAC1C,WAAW,OAAO;AAAA,cAClB,SAAS,QAAQ;AAAA,cACjB,MAAM;AAAA,YACR,CAAC;AACD,kBAAM,YAAY,YAAY,UAAU,eAAe;AACvD,kBAAM,aAAa,MAAM,QAAQ,YAAY,WAAW,OAAO,SAAS;AACxE,gBAAI,YAAY;AACd,qBAAO,OAAO;AAAA,YAChB;AAAA,UACF;AAAA,QACF,SAAS,GAAG;AACV,kBAAQ;AAAA,YACN;AAAA,YACA,aAAa,QAAQ,EAAE,UAAU;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,MAAM,OAAO,MAAM,WAAW,GAAG;AACnD,aAAO,MAAM;AAAA,IACf;AAKA,QAAI,OAAO,KAAK,KAAK,EAAE,YAAY,MAAM,kBAAkB;AACzD,cAAQ,UAAU,eAAe,QAAQ,SAAS,OAAO,MAAM,CAAC;AAChE,aAAO,MAAM;AAAA,IACf;AAMA,UAAM,WAAW,aAAa,OAAO,QAAQ,OAAO,UAAU,QAAW,OAAO,YAAY,MAAS;AACrG,UAAM,UAAU,yBAAyB,IAAI,QAAQ;AACrD,QAAI,WAAW,QAAQ,oBAAoB,OAAO,KAAK,SAAS,GAAG;AACjE,YAAM,OAA0B,EAAE,WAAW,QAAQ,WAAW,MAAM,OAAO,KAAK;AAClF,YAAM,mBAA2C;AAAA,QAC/C,QAAQ,OAAO;AAAA,QACf,WAAW,OAAO;AAAA,QAClB,UAAU,OAAO;AAAA,MACnB;AACA,UAAI,OAAO,OAAQ,kBAAiB,gBAAgB,OAAO;AAC3D,UAAI,OAAO,SAAU,kBAAiB,kBAAkB,OAAO;AAC/D,YAAM,aAAa;AAAA,QACjB,eAAe;AAAA,QACf,eAAe;AAAA,QACf,aAAa,OAAO;AAAA,QACpB,YAAY;AAAA,MACd;AACA,YAAM,cAAc,sBAAsB,OAAO,QAAQ,OAAO,YAAY,OAAO,MAAM;AACzF,UAAI;AACF,cAAM,QAAQ;AAAA,UACZ,EAAE,gBAAgB,CAAC,IAAI,EAAE;AAAA,UACzB,EAAE,MAAM,YAAqB,mBAAmB,YAAY;AAAA,QAC9D;AAEA,YAAI,QAAQ,eAAe;AACzB,cAAI;AACF,kBAAM,OAAO,UAAU;AAAA,cACrB,WAAW,QAAQ;AAAA,cACnB,MAAM,qBAAqB,QAAQ,SAAS,EAAE,MAAM,YAAY,MAAM,OAAO,KAAK,CAAC;AAAA,YACrF,CAAC;AAAA,UACH,SAAS,GAAG;AACV,oBAAQ,KAAK,sDAAsD,aAAa,QAAQ,EAAE,UAAU,CAAC;AAAA,UACvG;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AACV,gBAAQ,MAAM,mDAAmD,aAAa,QAAQ,EAAE,UAAU,CAAC;AAAA,MACrG,UAAE;AACA,yBAAiB,OAAO;AAAA,MAC1B;AACA,aAAO,MAAM;AAAA,IACf;AAGA,UAAM,cAAc,iBAAiB,OAAO,MAAM,OAAO,OAAO,SAAS,OAAO,SAAS;AACzF,UAAM,oBAAoB,sBAAsB,OAAO,QAAQ,OAAO,YAAY,OAAO,MAAM;AAS/F,UAAM,aAAqC;AAAA,MACzC,QAAQ,OAAO;AAAA,MACf,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,IACnB;AACA,QAAI,OAAO,OAAQ,YAAW,gBAAgB,OAAO;AACrD,QAAI,OAAO,SAAU,YAAW,kBAAkB,OAAO;AACzD,UAAM,OAAO;AAAA,MACX,eAAe;AAAA,MACf,eAAe;AAAA,MACf,aAAa,OAAO;AAAA,MACpB;AAAA,IACF;AAMA,UAAM,cACJ,OAAO,aAAa,UAChB,QAAQ,cAAc,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO,MAAM,IAC5D;AACN,UAAM,cAAyF;AAAA,MAC7F;AAAA,MACA;AAAA,IACF;AACA,QAAI,aAAa,cAAc;AAC7B,kBAAY,UAAU,CAAC,YAAY,YAAY;AAAA,IACjD;AACA,YAAQ;AAAA,MACN,2CAA2C,OAAO,MAAM,sBAAsB,iBAAiB,YACjF,OAAO,KAAK,MAAM,UAAU,OAAO,MAAM,MAAM;AAAA,IAC/D;AACA,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,KAAK,aAAsB,WAAoB;AAAA,IACzE,SAAS,GAAG;AACV,cAAQ;AAAA,QACN,4CAA4C,OAAO,MAAM,sBAAsB,iBAAiB;AAAA,QAChG,aAAa,QAAQ,EAAE,UAAU;AAAA,MACnC;AACA,YAAM;AAAA,IACR;AACA,YAAQ;AAAA,MACN,8CAA8C,QAAQ,EAAE,eAAe,OAAO,MAAM;AAAA,IACtF;AAMA,UAAM,QAAQ,qBAAqB,IAAI,QAAQ,EAAE;AACjD,QAAI,MAAO,OAAM,KAAK,OAAO,SAAS;AAAA,QACjC,sBAAqB,IAAI,QAAQ,IAAI,CAAC,OAAO,SAAS,CAAC;AAK5D,oBAAgB,IAAI,QAAQ,IAAI,OAAO,SAAS;AAIhD,gBAAY,IAAI,QAAQ,IAAI;AAAA,MAC1B,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO,UAAU;AAAA,MACzB,UAAU,OAAO,YAAY;AAAA,MAC7B,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAKD,UAAM,QAAQ,aAAa,QAAQ,WAAW;AAC9C,QAAI,OAAO;AACT,YAAM,eAAe,OAAO;AAC5B,cAAQ;AAAA,QACN,OACG,YAAY,EAAE,WAAW,cAAc,WAAW,MAAM,CAAC,EACzD,KAAK,CAAC,EAAE,WAAW,MAAM;AACxB,uBAAa,IAAI,cAAc,EAAE,YAAY,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,QACtE,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,kBAAQ;AAAA,YACN;AAAA,YACA,aAAa,QAAQ,EAAE,UAAU;AAAA,UACnC;AAAA,QACF,CAAC;AAAA,MACL;AAAA,IACF;AAEA,WAAO,MAAM;AAAA,EACf,GAjUuB;AA4UvB,iBAAe,iBACb,KACA,SACmB;AACnB,UAAM,QAAQ,IAAI,QAAQ;AAC1B,QAAI,CAAC,SAAS,MAAM,uBAAuB,MAAM,MAAM;AAErD,aAAO,MAAM;AAAA,IACf;AACA,UAAM,YAAY,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAI1E,UAAM,YACH,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW,QACtD,OAAO,IAAI,QAAQ,WAAW,WAAW,IAAI,OAAO,SAAS;AAChE,QAAI,CAAC,UAAW,QAAO,MAAM;AAE7B,UAAM,UAAU,yBAAyB,IAAI,SAAS;AACtD,QAAI,CAAC,SAAS;AACZ,cAAQ;AAAA,QACN,gDAAgD,SAAS;AAAA,MAC3D;AACA,aAAO,MAAM;AAAA,IACf;AAUA,UAAM,cAAc,QAAQ,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAC1E,YAAQ;AAAA,OACL,YAAY;AACX,YAAI,QAAQ,iBAAiB,aAAa;AAIxC,gBAAM,gBAAgB,YAAY,IAAI,QAAQ,MAAM;AACpD,gBAAM,cAAc,eAAe,UAAU,KAAK;AAClD,cAAI;AACF,kBAAM,OAAO,UAAU;AAAA,cACrB,WAAW,QAAQ;AAAA,cACnB,MAAM;AAAA,gBACJ,QAAQ;AAAA,gBACR,EAAE,MAAM,UAAU,OAAO,YAAY,MAAM;AAAA,gBAC3C;AAAA,cACF;AAAA,YACF,CAAC;AAID,2BAAe,gBAAgB;AAAA,UACjC,SAAS,GAAG;AACV,oBAAQ;AAAA,cACN;AAAA,cACA,aAAa,QAAQ,EAAE,UAAU;AAAA,YACnC;AAAA,UACF;AAAA,QACF;AAEA,cAAM,OAA0B,EAAE,WAAW,UAAU,YAAY,OAAU;AAC7E,cAAM,cAAc;AAAA,UAClB,QAAQ;AAAA,UACR,QAAQ,YAAY,QAAQ,UAAU;AAAA,QACxC;AACA,cAAM,mBAA2C;AAAA,UAC/C,QAAQ,QAAQ;AAAA,UAChB,WAAW,IAAI;AAAA,UACf,UAAU,QAAQ,QAAQ,YAAY,iBAAiB,QAAQ;AAAA,QACjE;AACA,YAAI,QAAQ,OAAQ,kBAAiB,gBAAgB,QAAQ;AAC7D,YAAI,QAAQ,SAAU,kBAAiB,kBAAkB,QAAQ;AACjE,cAAM,aAAa;AAAA,UACjB,eAAe;AAAA,UACf,eAAe;AAAA,UACf,aAAa,IAAI;AAAA,UACjB,YAAY;AAAA,QACd;AACA,YAAI;AACF,gBAAM,QAAQ;AAAA,YACZ,EAAE,gBAAgB,CAAC,IAAI,EAAE;AAAA,YACzB,EAAE,MAAM,YAAqB,mBAAmB,YAAY;AAAA,UAC9D;AACA,kBAAQ;AAAA,YACN,qDAAqD,SAAS,aAAa,QAAQ;AAAA,UACrF;AAAA,QACF,SAAS,GAAG;AACV,kBAAQ;AAAA,YACN,wDAAwD,SAAS;AAAA,YACjE,aAAa,QAAQ,EAAE,UAAU;AAAA,UACnC;AAAA,QACF;AAEA,yBAAiB,OAAO;AAAA,MAC1B,GAAG,EAAE,MAAM,CAAC,MAAM;AAChB,gBAAQ,MAAM,kDAAkD,CAAC;AAAA,MACnE,CAAC;AAAA,IACH;AAEA,WAAO,MAAM;AAAA,EACf;AAxGe;AA8Gf,QAAM,gBAAgB;AAAA;AAAA,IAEpB,mBAAmB,MAAe,UAAmB,KAAkC;AACrF,UAAI,QAAQ,cAAc,eAAe,QAAQ,cAAc,eAAgB;AAC/E,YAAM,YAAY,IAAI,QAAQ;AAC9B,YAAM,OAAO,mBAAmB,GAAY;AAC5C,UAAI,CAAC,KAAM;AACX,YAAM,SAAU,MAAqC;AACrD,UAAI,CAAC,OAAQ;AACb,YAAM,IAAI;AACV,UAAI,OAAO,EAAE,iBAAiB,SAAU;AACxC,YAAM,OAAO,cAAc,QAAQ,IAAI;AAMvC,YAAM,SAAS,YAAY,IAAI,MAAM;AACrC,UAAI,QAAQ;AACV,gBAAQ,IAAI,iCAAiC,MAAM,cAAc,SAAS,iBAAY,MAAM,EAAE;AAC9F,aAAK,eAAe,MAAM;AAAA,MAC5B;AACA,WAAK,YAAY,EAAE,YAAY;AAAA,IACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM,oBAAoB,MAAe,UAAmB,KAAkC;AAC5F,UAAI,QAAQ,cAAc,eAAe,QAAQ,cAAc,eAAgB;AAC/E,YAAM,OAAO,mBAAmB,GAAY;AAC5C,UAAI,CAAC,KAAM;AACX,YAAM,SAAU,MAAqC;AACrD,UAAI,CAAC,OAAQ;AACb,YAAM,IAAI;AACV,YAAM,SAAS,EAAE,WAAW,CAAC,GAC1B,IAAI,CAAC,MAAM,EAAE,QAAQ,EACrB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AACnD,UAAI,MAAM,WAAW,EAAG;AAGxB,YAAM,OAAO,cAAc,QAAQ,IAAI;AACvC,iBAAW,QAAQ,OAAO;AACxB,aAAK,YAAY,IAAI;AAAA,MACvB;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,gBAAgB,MAAe,UAAmB,MAAmC;AACzF,UAAI,QAAQ,cAAc,eAAe,QAAQ,cAAc,eAAgB;AAC/E,YAAM,SAAU,MAAqC;AACrD,UAAI,CAAC,OAAQ;AACb,YAAM,OAAO,YAAY,IAAI,MAAM;AACnC,UAAI,CAAC,KAAM;AACX,YAAM,IAAI;AACV,YAAM,OAAO,EAAE,QAAQ;AACvB,UAAI,CAAC,KAAM;AACX,WAAK,iBAAiB,MAAM,EAAE,QAAQ,WAAW,QAAQ;AAAA,IAC3D;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,kBAAkB,MAAe,UAAmB,KAAkC;AAC1F,YAAM,YAAY,IAAI,QAAQ;AAC9B,YAAM,OAAO,mBAAmB,GAAY;AAC5C,UAAI,CAAC,MAAM;AACT,gBAAQ,KAAK,0DAA0D,SAAS,GAAG;AACnF;AAAA,MACF;AACA,YAAM,SAAU,MAAqC;AACrD,UAAI,CAAC,QAAQ;AACX,gBAAQ,KAAK,oDAAoD,SAAS,GAAG;AAC7E;AAAA,MACF;AACA,YAAM,IAAI;AACV,YAAM,WAAW,EAAE,YAAY,CAAC;AAChC,UAAI,SAAS,WAAW,EAAG;AAE3B,cAAQ;AAAA,QACN,wCAAwC,SAAS,WAAW,MAAM,WAAW,KAAK,MAAM,UAAU,SAAS,MAAM;AAAA,MACnH;AAEA,iBAAW,OAAO,UAAU;AAO1B,cAAM,eAAe,YAAY,IAAI,MAAM;AAC3C,cAAM,mBACJ,gBACA,aAAa,aAAa,MACzB,QAAQ,cAAc,eAAe,QAAQ,cAAc;AAE9D,YAAI;AACJ,YAAI,oBAAoB,cAAc;AACpC,uBAAa,cAAc,GAAG;AAC9B,0BAAgB,aAAa,aAAa;AAAA,QAC5C,OAAO;AACL,gBAAM,OAAO,aAAa,GAAG;AAC7B,cAAI;AACF,kBAAM,MAAM,MAAM,OAAO,SAAS;AAAA,cAChC,QAAQ,KAAK;AAAA,cACb;AAAA,cACA,QAAQ,KAAK;AAAA,cACb,UAAU,KAAK;AAAA,YACjB,CAAC;AACD,4BAAgB,IAAI;AAAA,UACtB,SAAS,GAAG;AACV,oBAAQ;AAAA,cACN,8CAA8C,IAAI,SAAS;AAAA,cAC3D,aAAa,QAAQ,EAAE,UAAU;AAAA,YACnC;AACA;AAAA,UACF;AAAA,QACF;AAEA,cAAM,UAAwB;AAAA,UAC5B,WAAW,IAAI;AAAA,UACf;AAAA,UACA;AAAA,UACA,QAAQ,KAAK;AAAA,UACb,QAAQ,KAAK;AAAA,UACb,UAAU,KAAK;AAAA,UACf;AAAA,UACA,SAAS;AAAA,UACT,WAAW,KAAK,IAAI;AAAA,UACpB,WAAW,KAAK,IAAI;AAAA,UACpB,kBAAkB,IAAI,kBAAkB;AAAA,QAC1C;AACA,iCAAyB,IAAI,IAAI,WAAW,OAAO;AACnD,YAAI,QAAQ,kBAAkB;AAC5B,gBAAM,WAAW,aAAa,KAAK,QAAQ,KAAK,QAAQ,KAAK,QAAQ;AACrE,mCAAyB,IAAI,UAAU,OAAO;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,MAAM,oBAAoB,MAAe,UAAmB,KAAkC;AAC5F,YAAM,YAAY,IAAI,QAAQ;AAC9B,YAAM,OAAO,mBAAmB,GAAY;AAC5C,UAAI,CAAC,MAAM;AACT,gBAAQ,KAAK,4EAA4E,SAAS,GAAG;AACrG;AAAA,MACF;AACA,YAAM,IAAI;AACV,YAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC5D,YAAM,WAAY,MAAqC;AACvD,cAAQ;AAAA,QACN,0CAA0C,SAAS,WAAW,KAAK,MAAM,WAAW,QAAQ,MAAM,MAAM,WAAW,WAAW,QAAQ,KAAK;AAAA,MAC7I;AACA,YAAM,OAAO,QAAQ,SAAS,IAAI,UAAU;AAE5C,YAAM,cAAc,QAAQ,cAAc,eAAe,QAAQ,cAAc;AAI/E,UAAI,eAAe,YAAY,CAAC,YAAY,IAAI,QAAQ,GAAG;AACzD,YAAI,CAAC,aAAa,IAAI,QAAQ,GAAG;AAC/B,uBAAa,IAAI,QAAQ;AACzB,gBAAM,kBAAkB,QAAQ;AAAA,QAClC;AACA;AAAA,MACF;AAEA,UAAI;AACF,cAAM,aAAa,UAAU,MAAM,IAAI;AAAA,MACzC,UAAE;AACA,YAAI,YAAY,CAAC,aAAa,IAAI,QAAQ,GAAG;AAC3C,uBAAa,IAAI,QAAQ;AACzB,gBAAM,kBAAkB,QAAQ;AAAA,QAClC,WAAW,CAAC,UAAU;AACpB,gBAAM,qBAAqB,SAAS;AAAA,QACtC;AAAA,MAGF;AAAA,IACF;AAAA,IAEA,MAAM,cAAc,MAAe,UAAmB,KAA0C;AAC9F,YAAM,YAAY,KAAK,SAAS;AAChC,UAAI,CAAC,WAAW;AACd,gBAAQ,KAAK,6CAA6C;AAC1D;AAAA,MACF;AACA,YAAM,OAAO,mBAAmB,GAAY;AAC5C,UAAI,CAAC,MAAM;AACT,gBAAQ,KAAK,sDAAsD,SAAS,GAAG;AAC/E;AAAA,MACF;AACA,YAAM,eAAgB,MAAqC;AAC3D,YAAM,WAAW,qBAAqB,MAAM,eAAe,EAAE,UAAU,OAAO,CAAC;AAC/E,YAAM,UAAU,eAAgB,MAAuC,OAAO;AAC9E,cAAQ;AAAA,QACN,oCAAoC,SAAS,WAAW,KAAK,MAAM,SACxD,SAAS,MAAM,GAAG,GAAG,CAAC,OAC9B,eAAe,WAAW,YAAY,KAAK,OAC3C,UAAU,YAAY,OAAO,KAAK;AAAA,MACvC;AAEA,YAAM,OAAO,eAAe,YAAY,IAAI,YAAY,IAAI;AAC5D,UAAI,MAAM;AACR,YAAI;AACF,gBAAM,KAAK,MAAM,QAAQ;AACzB,kBAAQ,IAAI,sDAAsD,YAAY,GAAG;AAAA,QACnF,SAAS,GAAG;AACV,kBAAQ;AAAA,YACN,oFAAoF,YAAY;AAAA,YAChG,aAAa,QAAQ,EAAE,UAAU;AAAA,UACnC;AACA,cAAI;AACF,kBAAM,aAAa,cAAc,MAAM,QAAQ;AAAA,UACjD,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF,OAAO;AACL,YAAI;AACF,gBAAM,aAAa,cAAc,MAAM,QAAQ;AAAA,QACjD,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI,CAAC,gBAAgB,CAAC,aAAa,IAAI,YAAY,GAAG;AACpD,YAAI,aAAc,cAAa,IAAI,YAAY;AAC/C,YAAI,aAAc,OAAM,kBAAkB,YAAY;AAAA,YACjD,OAAM,qBAAqB,SAAS;AAAA,MAC3C;AAKA,UAAI,aAAc,UAAS,YAAY;AAAA,IACzC;AAAA,IAEA,MAAM,iBAAiB,MAAe;AACpC,YAAM,WAAW,qBAAqB,MAAM,kBAAkB,EAAE,UAAU,UAAU,CAAC;AACrF,YAAM,UAAU,eAAgB,MAAuC,OAAO;AAC9E,cAAQ;AAAA,QACN,8BAA8B,QAAQ,MAAM,UAAU,aAAa,OAAO,MAAM;AAAA,MAClF;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM,eAAe,OAAgB,UAAmB,KAA0C;AAChG,YAAM,YAAY,KAAK,SAAS;AAChC,UAAI,CAAC,UAAW;AAChB,YAAM,WAAY,OAAsC;AACxD,cAAQ,IAAI,qCAAqC,SAAS,MAAM,WAAW,WAAW,QAAQ,KAAK,GAAG;AACtG,UAAI,CAAC,SAAU;AACf,YAAM,QAAQ,qBAAqB,IAAI,SAAS;AAChD,UAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,cAAM,SAAS,MAAM,MAAM;AAC3B,YAAI,MAAM,WAAW,EAAG,sBAAqB,OAAO,SAAS;AAC7D,oBAAY,IAAI,UAAU,MAAM;AAAA,MAClC;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM,iBAAiB,MAAe,UAAmB,MAA2C;AAClG,YAAM,SAAU,MAAqC;AACrD,UAAI,CAAC,OAAQ;AACb,UAAI,aAAa,IAAI,MAAM,GAAG;AAI5B,iBAAS,MAAM;AACf;AAAA,MACF;AACA,mBAAa,IAAI,MAAM;AACvB,UAAI;AACF,cAAM,kBAAkB,MAAM;AAAA,MAChC,QAAQ;AAAA,MAER;AACA,eAAS,MAAM;AAAA,IACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM,yBAAyB,MAAe,UAAmB,KAAmC;AAClG,YAAM,YAAY,KAAK,SAAS;AAChC,YAAM,OAAO,mBAAmB,GAAY;AAC5C,UAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,YAAM,IAAI;AAIV,YAAM,OAAO,EAAE,QAAQ;AACvB,YAAM,cAAc,EAAE,eAAe,eAAe;AACpD,YAAM,MAAM,EAAE,eAAe;AAC7B,UAAI,CAAC,KAAK;AACR,gBAAQ,KAAK,yCAAyC,IAAI,yBAAyB;AACnF;AAAA,MACF;AACA,YAAM,OAAO,cAAc,EAAE,aAAa,KAAK,UAAU,EAAE,eAAe,SAAS,CAAC;AACpF,UAAI;AACF,cAAM,MAAM,MAAM,OAAO,SAAS,EAAE,QAAQ,KAAK,QAAQ,MAAM,QAAQ,KAAK,QAAQ,UAAU,KAAK,SAAS,CAAC;AAC7G,kBAAU,IAAI,GAAG,SAAS,IAAI,IAAI,IAAI,IAAI,SAAS;AAAA,MACrD,SAAS,GAAG;AACV,gBAAQ,MAAM,qCAAqC,IAAI,MAAM,aAAa,QAAQ,EAAE,UAAU,CAAC;AAAA,MACjG;AAAA,IACF;AAAA;AAAA;AAAA,IAIA,MAAM,0BAA0B,MAAe,UAAmB,KAAmC;AACnG,YAAM,YAAY,KAAK,SAAS;AAChC,UAAI,CAAC,UAAW;AAChB,YAAM,IAAI;AAMV,YAAM,OAAO,EAAE,QAAQ;AACvB,YAAM,gBAAgB,UAAU,IAAI,GAAG,SAAS,IAAI,IAAI,EAAE;AAC1D,UAAI,CAAC,cAAe;AACpB,YAAM,cAAc,EAAE,eAAe,eAAe;AACpD,YAAM,OAAO,uBAAuB;AAAA,QAClC;AAAA,QACA,SAAS,EAAE,WAAW;AAAA,QACtB,QAAQ,EAAE;AAAA,MACZ,CAAC;AACD,UAAI;AACF,cAAM,OAAO,UAAU,EAAE,WAAW,eAAe,KAAK,CAAC;AAAA,MAC3D,SAAS,GAAG;AACV,gBAAQ,KAAK,sCAAsC,IAAI,MAAM,aAAa,QAAQ,EAAE,UAAU,CAAC;AAAA,MACjG;AACA,gBAAU,OAAO,GAAG,SAAS,IAAI,IAAI,EAAE;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,UAAU,cAAc;AAAA,IAC5B,QAAQ,CAAC,KAAK,QAAQ,aAAa,cAAuB,CAAC;AAAA,IAE3D,WAAW,8BAAO,QAAgB;AAChC,UAAI,CAAC,IAAI,WAAW,QAAQ,OAAO,EAAG,QAAO;AAC7C,YAAM,IAAI,IAAI,MAAM,4DAA4D;AAChF,UAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAG,QAAO;AAC1C,aAAO,OAAO,iBAAiB;AAAA,QAC7B,WAAW,EAAE,CAAC;AAAA,QACd,SAAS,EAAE,CAAC;AAAA,QACZ,MAAM,EAAE,CAAC;AAAA,MACX,CAAC;AAAA,IACH,GATW;AAAA,IAWX,QAAQ;AAAA,EACV,CAAC;AAKD,EAAC,QAEE,eAAe;AAElB,SAAO;AACT;AA9nCgB;","names":["text","createHash","createHash"]}
1
+ {"version":3,"sources":["../src/channel.ts","../src/errors.ts","../src/lark-client.ts","../src/dedup.ts","../src/crypto.ts","../src/parse.ts","../src/card.ts","../src/streaming-controller.ts","../src/options.ts","../src/long-connection.ts","../src/feishu-emoji.ts"],"sourcesContent":["import {\n defineChannel,\n POST,\n type Channel,\n type RouteHandlerArgs,\n} from \"eve/channels\";\n\nimport { LarkClient } from \"./lark-client.js\";\nimport { DedupMap } from \"./dedup.js\";\nimport { decryptPayload, verifySignature } from \"./crypto.js\";\nimport { parseInbound } from \"./parse.js\";\nimport { StreamingCardController } from \"./streaming-controller.js\";\nimport {\n ASK_BUTTON_VALUE_MARKER,\n buildAskAnsweredCard,\n buildAskCard,\n buildAuthCard,\n buildAuthCompletedCard,\n buildTextCard,\n} from \"./card.js\";\nimport { resolveOptions } from \"./options.js\";\nimport { isEveStartLauncher, startLongConnection } from \"./long-connection.js\";\nimport { isValidFeishuEmojiType } from \"./feishu-emoji.js\";\nimport type {\n LarkCardActionTriggerEvent,\n LarkChannelOptions,\n LarkContinuationToken,\n LarkEncryptedBody,\n LarkEventBody,\n LarkInboundEvent,\n LarkInboundFile,\n LarkInputRequest,\n LarkInputResponse,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\n/** Hard cap on inbound webhook body size. Feishu payloads are <10 KB; this\n * is purely defense against a malicious or buggy peer OOMing the process. */\nconst MAX_BODY_BYTES = 1_000_000;\n\n/** Drop a session's controller if it's been inactive this long. Bounds the\n * closure-scoped `controllers`/`sessionMeta` Maps against crashes that\n * prevent `message.completed`/`turn.failed` from firing. */\nconst STALE_SESSION_MS = 30 * 60 * 1000;\n\n/** How often to sweep stale controllers. */\nconst SWEEP_INTERVAL_MS = 5 * 60 * 1000;\n\n/** Reply text used when the model returns null/empty — guarantees the user\n * always sees *something* so they're not left looking at a typing emoji. */\nconst EMPTY_REPLY_TEXT = \"(model returned no content)\";\n\n/**\n * Continuation token format: `${chatId}:${rootMessageId ?? \"_\"}`.\n * The framework prepends the channel file stem before handing the token to\n * the runtime; consumers should call this helper rather than concatenate.\n */\nexport function larkContinuationToken(\n chatId: string,\n rootMessageId: string | null,\n): LarkContinuationToken {\n return `${chatId}:${rootMessageId ?? \"_\"}` as LarkContinuationToken;\n}\n\ninterface LarkSessionMeta {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n /** When the controller was last touched. Used by the stale-sweep. */\n touchedAt: number;\n}\n\ninterface ResolvedSessionInfo {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n messageId?: string | undefined;\n}\n\n/**\n * Extract chat + message metadata stashed on `auth.initiator.attributes` at\n * session start. This is the canonical place to read it: closure state\n * doesn't reliably cross eve's process/worker boundary, but auth attributes\n * are persisted with the session.\n */\nfunction sessionInfoFromCtx(ctx: { session?: { auth?: { initiator?: { attributes?: unknown } | null } | null } | null }): ResolvedSessionInfo | null {\n const attrs = (ctx.session?.auth?.initiator?.attributes ?? {}) as {\n chatId?: unknown;\n rootMessageId?: unknown;\n parentId?: unknown;\n messageId?: unknown;\n };\n if (typeof attrs.chatId !== \"string\" || !attrs.chatId) return null;\n return {\n chatId: attrs.chatId,\n rootId: typeof attrs.rootMessageId === \"string\" ? attrs.rootMessageId : undefined,\n parentId: typeof attrs.parentId === \"string\" ? attrs.parentId : undefined,\n messageId: typeof attrs.messageId === \"string\" ? attrs.messageId : undefined,\n };\n}\n\nfunction ackOk(): Response {\n return Response.json({ code: 0 });\n}\n\n/**\n * Resolve the configured `ackReaction` to a single valid emoji type for this\n * event, or `false` if reactions are disabled (or the configured value is\n * invalid). Picks randomly when given an array.\n *\n * Validates against {@link VALID_FEISHU_EMOJI_TYPES} because Feishu rejects\n * unknown emoji types with HTTP 400 code=231001. The validation is\n * case-sensitive — `TYPING` is invalid but `Typing` is. Without this check a\n * typo in the default or a user-supplied value fails silently on every\n * inbound message.\n */\nfunction pickAckEmoji(reaction: string | readonly string[] | false): string | false {\n if (reaction === false) return false;\n if (typeof reaction === \"string\") {\n if (!isValidFeishuEmojiType(reaction)) {\n console.warn(\n `[eve-lark] ackReaction \"${reaction}\" is not a valid Feishu emoji type ` +\n `(case-sensitive; e.g. \"Typing\" not \"TYPING\"). Skipping ack reaction. ` +\n `See VALID_FEISHU_EMOJI_TYPES for the full list.`,\n );\n return false;\n }\n return reaction;\n }\n if (Array.isArray(reaction)) {\n const valid = reaction.filter(isValidFeishuEmojiType);\n if (valid.length === 0) {\n const sample = reaction.slice(0, 3).join(\", \");\n console.warn(\n `[eve-lark] ackReaction array contains no valid Feishu emoji types ` +\n `(got [${sample}${reaction.length > 3 ? \", …\" : \"\"}]). Skipping ack reaction.`,\n );\n return false;\n }\n if (valid.length < reaction.length) {\n const dropped = reaction.filter((e) => !isValidFeishuEmojiType(e));\n console.warn(\n `[eve-lark] ackReaction array dropped ${dropped.length} invalid emoji type(s): ` +\n `${dropped.slice(0, 3).join(\", \")}`,\n );\n }\n const idx = Math.floor(Math.random() * valid.length);\n return valid[idx] ?? false;\n }\n return false;\n}\n\nfunction resourceUrl(\n options: ResolvedLarkOptions,\n file: LarkInboundFile,\n messageId: string,\n): string {\n const type = file.kind === \"image\" ? \"image\" : \"file\";\n return `${options.baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/resources/${encodeURIComponent(file.fileKey)}?type=${type}`;\n}\n\n/**\n * Build the eve UserContent payload from a parsed inbound event. Text comes\n * first; each inbound image/file becomes a `file` part carrying a URL pointing\n * at the Lark resource endpoint. The channel's `fetchFile` hook will stage\n * those URLs to bytes when the model runs.\n */\nfunction buildUserContent(\n text: string,\n files: LarkInboundFile[],\n options: ResolvedLarkOptions,\n messageId: string,\n): unknown[] {\n const parts: unknown[] = [];\n if (text.length > 0) parts.push({ type: \"text\", text });\n for (const f of files) {\n parts.push({\n type: \"file\",\n data: new URL(resourceUrl(options, f, messageId)),\n mediaType: f.mediaType,\n });\n }\n return parts;\n}\n\n/**\n * Run a diagnostic check and reply with the results in the given chat.\n * Tests config validity, fetches a tenant_access_token, and reports.\n */\nasync function runDiagnostics(\n client: LarkClient,\n opts: ResolvedLarkOptions,\n chatId: string,\n): Promise<void> {\n const lines: string[] = [\"**eve-lark diagnostics**\", \"\"];\n lines.push(`appId: \\`${opts.appId}\\``);\n lines.push(`baseUrl: \\`${opts.baseUrl}\\``);\n lines.push(`mode: \\`${opts.mode}\\``);\n lines.push(`replyMode: \\`${opts.replyMode}\\``);\n lines.push(`encryptKey: ${opts.encryptKey ? \"✓ set\" : \"✗ not set\"}`);\n lines.push(`ackReaction: \\`${opts.ackReaction === false ? \"disabled\" : opts.ackReaction}\\``);\n\n lines.push(\"\");\n lines.push(\"**Token fetch:**\");\n try {\n const token = await client.getTenantAccessToken();\n lines.push(`✓ tenant_access_token: ${token.slice(0, 8)}…`);\n } catch (e) {\n lines.push(`✗ failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n\n const report = lines.join(\"\\n\");\n try {\n await client.sendPost({ chatId, content: report });\n } catch (e) {\n console.error(\"[eve-lark] diagnostic report delivery failed:\", e);\n }\n}\n\n/**\n * Port of eve's `formatErrorHint` from `#internal/logging.js`.\n *\n * Builds a ` (name: message)` hint from a turn.failed/session.failed event's\n * data payload. Reads `data.details.name` (error class, e.g. \"AI_APICallError\")\n * and `data.message` (the actual reason, e.g. a rate-limit string). Both are\n * optional; the hint is empty when neither is present so callers can\n * interpolate unconditionally: `` `I hit an error${hint}.` ``\n *\n * Truncated to 160 chars (matching eve) to keep one-line Feishu replies\n * readable.\n */\nfunction formatErrorHint(data: unknown): string {\n if (typeof data !== \"object\" || data === null) return \"\";\n const d = data as { details?: unknown; message?: unknown };\n const detailsName =\n typeof d.details === \"object\" && d.details !== null\n ? (d.details as { name?: unknown }).name\n : undefined;\n const name =\n typeof detailsName === \"string\" && detailsName.length > 0 ? detailsName : undefined;\n const message = typeof d.message === \"string\" ? d.message.trim() : \"\";\n if (name && message.length > 0) return ` (${name}: ${truncateForDisplay(message)})`;\n if (name) return ` (${name})`;\n if (message.length > 0) return ` (${truncateForDisplay(message)})`;\n return \"\";\n}\n\n/**\n * Port of eve's `extractErrorId`. Reads `data.details.errorId` if present —\n * a UUID users can quote to support. Returns undefined when absent.\n */\nfunction extractErrorId(details: unknown): string | undefined {\n if (typeof details === \"object\" && details !== null) {\n const id = (details as { errorId?: unknown }).errorId;\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n }\n return undefined;\n}\n\nfunction truncateForDisplay(s: string, max = 160): string {\n return s.length <= max ? s : `${s.slice(0, max - 1).trimEnd()}…`;\n}\n\n/**\n * Compose a user-facing error message from a turn.failed/session.failed\n * event. Mirrors eve's official channel output:\n *\n * `⚠ I hit an error while handling your request (AI_APICallError: <reason>). (Error id: <uuid>)`\n *\n * Always returns a non-empty string. If `data` has no useful info, falls back\n * to `⚠ <fallbackReason>` (e.g. \"turn failed\").\n */\nfunction formatFailureMessage(\n data: unknown,\n fallback: string,\n opts: { sentence: \"turn\" | \"session\" } = { sentence: \"turn\" },\n): string {\n const hint = formatErrorHint(data);\n const errorId = extractErrorId((data as { details?: unknown } | null)?.details);\n const lead =\n opts.sentence === \"session\"\n ? \"This session couldn't recover from an error\"\n : \"I hit an error while handling your request\";\n const idSuffix = errorId ? ` (Error id: ${errorId})` : \"\";\n // If we have neither hint nor errorId, prefer the explicit fallback so\n // users get \"turn failed\" rather than the same string every time. With\n // hint or errorId present, the lead-in alone is informative enough.\n if (!hint && !errorId) return `⚠ ${fallback}`;\n return `⚠ ${lead}${hint}.${idSuffix}`;\n}\n\n/**\n * Create a Lark/Feishu channel for the eve agent framework.\n *\n * The channel mounts a single POST webhook that verifies the request,\n * decrypts the body when an encrypt key is configured, deduplicates events\n * by id, parses the inbound message, and starts or resumes an eve session.\n *\n * Streaming happens via eve's native channel events: `message.appended`\n * drives live card patches, `message.completed` finalizes the card, and\n * `turn.failed` aborts it. In `replyMode: \"static\"` the controller is\n * skipped and `message.completed` delivers a single card.\n *\n * **Delivery guarantee**: every terminal event (`message.completed` or\n * `turn.failed`) delivers *something* to the user. If the streaming card\n * path fails, we fall back to a fresh card; if that fails, plain text; if\n * even that fails, the error is logged. The user is never left looking at\n * a typing-emoji reaction with no reply.\n */\nexport function createLarkChannel(\n optionsInput: LarkChannelOptions,\n): Channel<undefined, Record<string, unknown>, Record<string, unknown>> {\n const options = resolveOptions(optionsInput);\n const client = new LarkClient(options);\n const dedup = new DedupMap(options.dedupTtlMs, options.dedupMaxEntries);\n\n // Long-connection side effect: when mode is \"long-connection\" (the\n // default), start a Feishu WSClient in the background. Each inbound event\n // is re-signed and POSTed to this channel's webhook on localhost, where\n // the standard handler runs with full access to send() etc.\n //\n // Skip when running inside the `eve start` launcher process: eve forks\n // a nitro server child to actually serve HTTP, and both processes load\n // the channel module. Without this guard each process spawns its own\n // WSClient and Feishu delivers every event twice.\n if (options.mode === \"long-connection\" && !isEveStartLauncher()) {\n const eveWebhookUrl = `http://127.0.0.1:${options.port}${options.webhookPath}`;\n void startLongConnection({ resolved: options, eveWebhookUrl }).catch((e) => {\n console.error(\"[eve-lark] long-connection startup failed:\", e);\n });\n } else if (options.mode === \"long-connection\" && isEveStartLauncher()) {\n console.log(\"[eve-lark] skipping WSClient start in eve-start launcher process; the spawned server will start it\");\n }\n\n // Channel-scoped (closure) state.\n //\n // **Per-turn** (keyed by eve turnId): each turn owns its own controller so\n // interleaved turns in conversation-mode don't overwrite each other's\n // reply rootId or share streaming-card state. `turnSources` maps a turnId\n // to the inbound messageId that started it — set by turn.started (which\n // shifts the head of the session's FIFO queue), consumed by\n // message.appended (for reply target) and message.completed/turn.failed/\n // turn.completed (for ack cleanup). `turnTouchedAt` feeds the stale-sweep.\n //\n // **Per-session** (keyed by sessionId): sessionMeta, the inbound-msgId FIFO\n // (awaiting turn.started), the last-chat-message id (diagnostic), the ack\n // reactions (keyed by INBOUND messageId), and per-`${sessionId}:${name}`\n // auth cards. These are session-level concerns that don't split per turn.\n const controllers = new Map<string, StreamingCardController>();\n const turnSources = new Map<string, string>();\n const turnTouchedAt = new Map<string, number>();\n const sessionMeta = new Map<string, LarkSessionMeta>();\n\n // Pending ask_question input requests, keyed by eve's requestId. Used to\n // resolve card.action.trigger callbacks back to the originating session.\n // Also keyed by chat-continuation-token for freeform interception.\n interface PendingInput {\n requestId: string;\n sessionId: string;\n /** eve turnId that emitted the input.requested (so handleCardAction can\n * find the per-turn controller to preserve the streaming buffer above\n * the answered ask UI). */\n turnId: string;\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n /** The card message id we sent (so we can patch it after the user answers). */\n cardMessageId?: string | undefined;\n /** Full request, so the post-click renderer can show selected label. */\n request: LarkInputRequest;\n /** When the pending input was registered (for stale-sweep). */\n createdAt: number;\n /** Whether to intercept the next inbound chat message as the response. */\n awaitingFreeform: boolean;\n touchedAt: number;\n }\n const pendingInputsByRequestId = new Map<string, PendingInput>();\n const pendingInputsByChatToken = new Map<string, PendingInput>();\n\n // Auth cards keyed by `${sessionId}:${connectionName}`. Populated by\n // authorization.required, consumed + deleted by authorization.completed.\n const authCards = new Map<string, string>();\n\n // Ack reaction cleanup — keyed by INBOUND messageId (not sessionId) so\n // multiple messages in the same session don't overwrite each other's\n // cleanup data. eve reuses the same sessionId for conversation-mode\n // continuation; a sessionId-keyed map would lose the first message's\n // reactionId when the second arrives.\n const ackReactions = new Map<string, { reactionId: string; createdAt: number }>();\n\n // FIFO queue of inbound messageIds per session, awaiting turn.started.\n // turn.started shifts the head to map the incoming turnId → source message\n // (stored in turnSources). eve conversation mode reuses the same sessionId\n // for same-chat continuation, so a single-value map would lose the first\n // message's slot when the second arrives before the first turn starts.\n const currentInboundMsgIds = new Map<string, string[]>();\n\n // messageId of the last inbound message per chat session. The per-turn\n // controller now does quote-reply directly via turnSources (turnId → source),\n // so lastChatMessage is no longer used for \"follow/interleaved\" detection.\n // Kept mainly for future diagnostics/visualization; webhook still updates it.\n const lastChatMessage = new Map<string, string>();\n\n // Turn ids whose ack reaction has already been cleaned. eve fires both\n // message.completed AND turn.completed for turns with visible text (only\n // turn.completed for tool-call-only turns); without dedup the second event\n // would re-run cleanupAckForTurn on the same source message.\n const cleanedTurns = new Set<string>();\n\n function getController(turnId: string, meta: ResolvedSessionInfo): StreamingCardController {\n let ctrl = controllers.get(turnId);\n if (!ctrl) {\n ctrl = new StreamingCardController(client, {\n chatId: meta.chatId,\n rootId: meta.rootId,\n parentId: meta.parentId,\n patchIntervalMs: options.streamPatchIntervalMs,\n createThresholdMs: options.streamCreateThresholdMs,\n useCardKitV2: options.replyMode === \"streaming-v2\",\n });\n controllers.set(turnId, ctrl);\n }\n turnTouchedAt.set(turnId, Date.now());\n return ctrl;\n }\n\n /** Tear down all per-turn state for `turnId`. Idempotent — safe to call\n * from every terminal handler (message.completed, turn.failed,\n * turn.completed) without ordering assumptions. */\n function dropTurn(turnId: string): void {\n controllers.delete(turnId);\n turnSources.delete(turnId);\n turnTouchedAt.delete(turnId);\n }\n\n\n /** Best-effort ack-reaction cleanup. Takes the INBOUND messageId (not\n * sessionId) so multiple messages in the same session don't clean up\n * each other's reactions. */\n async function cleanupAckReaction(messageId: string): Promise<void> {\n const ack = ackReactions.get(messageId);\n if (!ack) return;\n try {\n await client.removeReaction({ messageId, reactionId: ack.reactionId });\n } catch (e) {\n console.warn(\n \"[eve-lark] ack reaction cleanup failed:\",\n e instanceof Error ? e.message : e,\n );\n }\n ackReactions.delete(messageId);\n }\n\n /** Drain the oldest inbound messageId from the session's FIFO queue and\n * clean up its ack reaction. Used when a terminal event arrives WITHOUT\n * a turnId (so we can't look up the per-turn source). Terminal events\n * that DO carry a turnId use {@link cleanupAckForTurn} instead. */\n async function cleanupAckForSession(sessionId: string): Promise<void> {\n const queue = currentInboundMsgIds.get(sessionId);\n if (!queue || queue.length === 0) return;\n const msgId = queue.shift();\n if (!msgId) return;\n if (queue.length === 0) currentInboundMsgIds.delete(sessionId);\n await cleanupAckReaction(msgId);\n }\n\n /** Clean up the ack reaction for the inbound message that started this\n * turn (looked up via the turnId → source map populated by turn.started).\n * Per-turn equivalent of {@link cleanupAckForSession}. */\n async function cleanupAckForTurn(turnId: string): Promise<void> {\n const sourceMsgId = turnSources.get(turnId);\n if (!sourceMsgId) return;\n await cleanupAckReaction(sourceMsgId);\n }\n\n /**\n * Cascade-deliver a reply to the user. Tries (in order):\n *\n * post mode (default — native chat size + markdown):\n * 1. sendPost (msg_type: \"post\", renders at native size)\n * 2. sendText (post POST rejected)\n *\n * streaming mode (live card patches during the turn):\n * 1. streaming finalize (patches existing card OR creates one)\n * 2. sendCard (finalize failed)\n * 3. sendText (card POST rejected)\n *\n * static mode (one-shot card):\n * 1. sendCard (single card with the full text)\n * 2. sendText (card POST rejected)\n *\n * `turnId` selects the per-turn controller in streaming mode; in post /\n * static mode it's ignored. Callers MUST pre-check `controllers.has(turnId)`\n * for streaming mode if they need to dedupe — deliverReply itself will\n * fall through to a fresh sendCard when no controller exists.\n *\n * Each failure logs; we never throw out of here.\n */\n async function deliverReply(turnId: string | undefined, info: ResolvedSessionInfo, text: string): Promise<void> {\n if (options.replyMode === \"post\") {\n try {\n await client.sendPost({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendPost (turnId=${turnId ?? \"?\"})`);\n return;\n } catch (postErr) {\n console.warn(\n `[eve-lark] sendPost failed; falling back to plain text (turnId=${turnId ?? \"?\"}):`,\n postErr instanceof Error ? postErr.message : postErr,\n );\n // Fall through to sendText.\n }\n // post-specific fallback (skip the card cascade below).\n try {\n await client.sendText({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendText fallback (turnId=${turnId ?? \"?\"})`);\n } catch (textErr) {\n console.error(\n `[eve-lark] sendText fallback ALSO failed; the user will not see this reply (turnId=${turnId ?? \"?\"}):`,\n textErr instanceof Error ? textErr.message : textErr,\n );\n }\n return;\n }\n\n if (options.replyMode === \"streaming\" || options.replyMode === \"streaming-v2\") {\n const ctrl = turnId ? controllers.get(turnId) : undefined;\n if (ctrl) {\n try {\n await ctrl.finalize(text);\n console.log(`[eve-lark] delivered via streaming finalize (turnId=${turnId})`);\n return;\n } catch (e) {\n console.warn(\n `[eve-lark] streaming finalize failed; falling back to fresh card (turnId=${turnId}):`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n // No controller (turn had no message.appended, e.g. a short final reply)\n // or finalize threw — fall through to a one-shot sendCard.\n }\n\n try {\n const res = await client.sendCard({\n chatId: info.chatId,\n card: buildTextCard(text),\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendCard (turnId=${turnId ?? \"?\"})`);\n void res;\n return;\n } catch (cardErr) {\n console.warn(\n `[eve-lark] sendCard failed; falling back to plain text (turnId=${turnId ?? \"?\"}):`,\n cardErr instanceof Error ? cardErr.message : cardErr,\n );\n }\n\n try {\n await client.sendText({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendText fallback (turnId=${turnId ?? \"?\"})`);\n } catch (textErr) {\n console.error(\n `[eve-lark] sendText fallback ALSO failed; the user will not see this reply (turnId=${turnId ?? \"?\"}):`,\n textErr instanceof Error ? textErr.message : textErr,\n );\n }\n }\n\n // Lazy sweep: drop per-turn controllers/sources that haven't been touched\n // in STALE_SESSION_MS, plus stale sessionMeta and pending input requests.\n // Guards against the case where eve crashes mid-turn (no terminal event).\n let lastSweepAt = 0;\n function maybeSweep(): void {\n const now = Date.now();\n if (now - lastSweepAt < SWEEP_INTERVAL_MS) return;\n lastSweepAt = now;\n const cutoff = now - STALE_SESSION_MS;\n for (const [turnId, touchedAt] of turnTouchedAt) {\n if (touchedAt < cutoff) {\n dropTurn(turnId);\n }\n }\n for (const [id, meta] of sessionMeta) {\n if (meta.touchedAt < cutoff) {\n sessionMeta.delete(id);\n }\n }\n for (const [reqId, p] of pendingInputsByRequestId) {\n if (p.touchedAt < cutoff) {\n pendingInputsByRequestId.delete(reqId);\n const tokenKey = chatTokenKey(p.chatId, p.rootId, p.parentId);\n if (pendingInputsByChatToken.get(tokenKey)?.requestId === reqId) {\n pendingInputsByChatToken.delete(tokenKey);\n }\n }\n }\n }\n\n /** Compose the chat-scoped key used for freeform interception. */\n function chatTokenKey(chatId: string, rootId?: string, parentId?: string): string {\n return `${chatId}:${parentId ?? rootId ?? \"_\"}`;\n }\n\n function dropPendingInput(p: PendingInput): void {\n pendingInputsByRequestId.delete(p.requestId);\n const tokenKey = chatTokenKey(p.chatId, p.rootId, p.parentId);\n if (pendingInputsByChatToken.get(tokenKey)?.requestId === p.requestId) {\n pendingInputsByChatToken.delete(tokenKey);\n }\n }\n\n const webhookHandler = async (\n req: Request,\n helpers: RouteHandlerArgs[\"send\"] extends never ? never : RouteHandlerArgs,\n ): Promise<Response> => {\n maybeSweep();\n\n // 0) Body size cap — refuse gigantic bodies before allocating.\n const contentLength = Number(req.headers.get(\"content-length\") ?? \"0\");\n if (Number.isFinite(contentLength) && contentLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n const rawBody = Buffer.from(await req.arrayBuffer());\n if (rawBody.byteLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n\n // 1) Skew check (only enforced when a real timestamp header is present)\n const tsHeader = req.headers.get(\"x-lark-request-timestamp\") ?? \"\";\n const ts = Number(tsHeader);\n if (\n tsHeader &&\n Number.isFinite(ts) &&\n ts > 0 &&\n Math.abs(Date.now() / 1000 - ts) > options.signatureSkewMs / 1000\n ) {\n return new Response(\"request timestamp out of skew window\", { status: 408 });\n }\n\n // 2) Signature verify + AES decrypt when encryptKey configured\n let workingBody: Buffer = rawBody;\n if (options.encryptKey) {\n const nonce = req.headers.get(\"x-lark-request-nonce\") ?? \"\";\n const sigHeader = req.headers.get(\"x-lark-signature\");\n if (!sigHeader) return new Response(\"missing signature\", { status: 401 });\n const ok = verifySignature({\n timestamp: tsHeader,\n nonce,\n encryptKey: options.encryptKey,\n rawBody,\n signatureHeader: sigHeader,\n });\n if (!ok) return new Response(\"bad signature\", { status: 401 });\n\n try {\n const envelope = JSON.parse(rawBody.toString(\"utf8\")) as LarkEncryptedBody;\n if (envelope.encrypt) {\n workingBody = decryptPayload(envelope.encrypt, options.encryptKey) as Buffer;\n }\n } catch {\n return new Response(\"decrypt failed\", { status: 400 });\n }\n }\n\n // 3) Parse body\n let body: LarkEventBody;\n try {\n body = JSON.parse(workingBody.toString(\"utf8\")) as LarkEventBody;\n } catch {\n return new Response(\"invalid json\", { status: 400 });\n }\n\n // 4) url_verification short-circuit\n if (body.type === \"url_verification\") {\n return Response.json({ challenge: body.challenge ?? \"\" });\n }\n\n // 5) Schema check\n if (body.schema !== \"2.0\") {\n return ackOk();\n }\n\n // 6) Verification-token check\n if (body.header?.token !== options.verificationToken) {\n return new Response(\"verification token mismatch\", { status: 401 });\n }\n\n // 7) Dedup. Card-action callbacks dedup on open_message_id (one click\n // per render — re-clicks after patch are no-ops because we replaced\n // the buttons with text).\n const evtMsg = body.event as { message?: { message_id?: string }; open_message_id?: string } | undefined;\n const dedupKey = body.header?.event_id ?? evtMsg?.message?.message_id ?? evtMsg?.open_message_id;\n if (dedupKey) {\n if (dedup.has(dedupKey)) return ackOk();\n dedup.set(dedupKey);\n }\n\n // 8) Event-type dispatch. Two event types we own:\n // - \"im.message.receive_v1\" — normal inbound message (existing flow).\n // - \"card.action.trigger\" — user clicked a button on an ask_card.\n // Anything else is ack-and-skip.\n const eventType = body.header?.event_type;\n if (eventType === \"card.action.trigger\") {\n return handleCardAction(body.event as LarkCardActionTriggerEvent, helpers);\n }\n if (eventType !== \"im.message.receive_v1\") {\n return ackOk();\n }\n if (!body.event) return ackOk();\n\n // 9) Parse — body.event is now narrowed to the message-event branch.\n const parsed = parseInbound(body.event as LarkInboundEvent, options.botOpenId);\n\n // 10) Self-message suppression\n if (parsed.senderType === \"app\") {\n return ackOk();\n }\n\n // 10.5) Allowlist. Ack-and-drop so the user sees the message \"delivered\"\n // (Feishu's perspective) but the agent never wakes up. DM allowlist\n // keys on sender open_id; group allowlist keys on chat_id. Both are\n // independent — setting one doesn't restrict the other surface.\n if (parsed.chatType === \"p2p\" && options.allowFrom) {\n if (!options.allowFrom.includes(parsed.senderOpenId)) {\n console.log(\n `[eve-lark] dropping DM from non-allowlisted sender ${parsed.senderOpenId}`,\n );\n return ackOk();\n }\n }\n if (parsed.chatType === \"group\" && options.groupAllowFrom) {\n if (!options.groupAllowFrom.includes(parsed.chatId)) {\n console.log(\n `[eve-lark] dropping group message from non-allowlisted chat ${parsed.chatId}`,\n );\n return ackOk();\n }\n }\n\n // 10.8) Audio/media transcription. If the message has no text (audio/\n // media/sticker) and an ASR provider is configured, download the audio\n // bytes and transcribe. The transcript replaces the empty text so the\n // normal flow picks it up. If ASR fails, we fall through to step 11\n // (ack-and-skip).\n if (options.asrProvider && parsed.text === \"\" && parsed.files.length === 0) {\n const rawEvent = body.event as LarkInboundEvent;\n const msgType = rawEvent.message?.message_type;\n if (msgType === \"audio\" || msgType === \"media\") {\n try {\n const content = JSON.parse(rawEvent.message.content) as { file_key?: string };\n if (content.file_key) {\n const bytes = await client.downloadResource({\n messageId: parsed.messageId,\n fileKey: content.file_key,\n type: \"file\",\n });\n const mediaType = msgType === \"audio\" ? \"audio/mpeg\" : \"video/mp4\";\n const transcript = await options.asrProvider.transcribe(bytes, mediaType);\n if (transcript) {\n parsed.text = transcript;\n }\n }\n } catch (e) {\n console.warn(\n \"[eve-lark] audio transcription failed, skipping message:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n }\n\n // 11) Skip unsupported message types\n if (parsed.text === \"\" && parsed.files.length === 0) {\n return ackOk();\n }\n\n // 11.1) Built-in slash command: /lark-diagnose. Run diagnostics\n // (token fetch + config summary) and reply directly via LarkClient.\n // Does NOT forward to the agent.\n if (parsed.text.trim().toLowerCase() === \"/lark-diagnose\") {\n helpers.waitUntil(runDiagnostics(client, options, parsed.chatId));\n return ackOk();\n }\n\n // 11.5) Freeform-input interception. If this chat has a pending\n // ask_question awaiting a freeform text reply, treat this inbound\n // message as the answer instead of starting a new turn. eve resumes\n // the parked session with the user's text as InputResponse.text.\n const tokenKey = chatTokenKey(parsed.chatId, parsed.rootId ?? undefined, parsed.parentId ?? undefined);\n const pending = pendingInputsByChatToken.get(tokenKey);\n if (pending && pending.awaitingFreeform && parsed.text.length > 0) {\n const resp: LarkInputResponse = { requestId: pending.requestId, text: parsed.text };\n const resumeAttributes: Record<string, string> = {\n chatId: parsed.chatId,\n messageId: parsed.messageId,\n chatType: parsed.chatType,\n };\n if (parsed.rootId) resumeAttributes.rootMessageId = parsed.rootId;\n if (parsed.parentId) resumeAttributes.parentMessageId = parsed.parentId;\n const resumeAuth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: parsed.senderOpenId,\n attributes: resumeAttributes,\n };\n const resumeToken = larkContinuationToken(parsed.chatId, parsed.parentId ?? parsed.rootId);\n try {\n await helpers.send(\n { inputResponses: [resp] } as never,\n { auth: resumeAuth as never, continuationToken: resumeToken },\n );\n // Update the card (if any) to show the typed answer.\n if (pending.cardMessageId) {\n try {\n await client.patchCard({\n messageId: pending.cardMessageId,\n card: buildAskAnsweredCard(pending.request, { kind: \"freeform\", text: parsed.text }),\n });\n } catch (e) {\n console.warn(\"[eve-lark] patchCard after freeform answer failed:\", e instanceof Error ? e.message : e);\n }\n }\n } catch (e) {\n console.error(\"[eve-lark] freeform input-response send failed:\", e instanceof Error ? e.message : e);\n } finally {\n dropPendingInput(pending);\n }\n return ackOk();\n }\n\n // 12) Build session inputs\n const userContent = buildUserContent(parsed.text, parsed.files, options, parsed.messageId);\n const continuationToken = larkContinuationToken(parsed.chatId, parsed.parentId ?? parsed.rootId);\n // Build auth.attributes, OMITTING null/undefined values. eve's\n // SessionAuthContext contract requires `Record<string, string | readonly\n // string[]>` — null values violate it and the `as never` cast below\n // silences TS without protecting runtime. A null `rootMessageId` (typical\n // for top-level chats where parsed.rootId is null) used to sneak through\n // and silently break helpers.send: the call returned a Session object\n // but no workflow actually ran — no [eve:harness] log, no model call,\n // no reply. Sanitize to strings-only so eve's runtime accepts the auth.\n const attributes: Record<string, string> = {\n chatId: parsed.chatId,\n messageId: parsed.messageId,\n chatType: parsed.chatType,\n };\n if (parsed.rootId) attributes.rootMessageId = parsed.rootId;\n if (parsed.parentId) attributes.parentMessageId = parsed.parentId;\n const auth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: parsed.senderOpenId,\n attributes,\n };\n\n // 13) Start/resume session.\n // For group chats, surface any matching per-group systemPrompt as\n // `context` — eve prepends each context entry as a role:\"user\" message\n // before the delivery message. DMs ignore this.\n const groupConfig =\n parsed.chatType === \"group\"\n ? options.groupConfigs?.find((g) => g.chatId === parsed.chatId)\n : undefined;\n const sendPayload: { auth: unknown; continuationToken: string; context?: readonly string[] } = {\n auth: auth as never,\n continuationToken,\n };\n if (groupConfig?.systemPrompt) {\n sendPayload.context = [groupConfig.systemPrompt];\n }\n console.log(\n `[eve-lark] invoking helpers.send chatId=${parsed.chatId} continuationToken=${continuationToken}` +\n ` textLen=${parsed.text.length} files=${parsed.files.length}`,\n );\n let session: { id: string };\n try {\n session = await helpers.send(userContent as never, sendPayload as never);\n } catch (e) {\n console.error(\n `[eve-lark] helpers.send threw for chatId=${parsed.chatId} continuationToken=${continuationToken}:`,\n e instanceof Error ? e.message : e,\n );\n throw e;\n }\n console.log(\n `[eve-lark] helpers.send returned sessionId=${session.id} for chatId=${parsed.chatId}`,\n );\n\n // Enqueue this inbound messageId so turn.started can shift it off the\n // head to map the incoming turnId → source message. FIFO (not\n // single-value) because a second message arriving before the first\n // turn's turn.started must not overwrite the first message's slot.\n const queue = currentInboundMsgIds.get(session.id);\n if (queue) queue.push(parsed.messageId);\n else currentInboundMsgIds.set(session.id, [parsed.messageId]);\n\n // This user message is the current last inbound in the chat → record it\n // (for diagnostics/visualization). Quote-reply now locates the source\n // directly via turnSources (turnId → source); this map is not used for it.\n lastChatMessage.set(session.id, parsed.messageId);\n\n // 14) Remember chat metadata keyed by session.id so terminal handlers\n // can look up where to deliver replies.\n sessionMeta.set(session.id, {\n chatId: parsed.chatId,\n rootId: parsed.rootId ?? undefined,\n parentId: parsed.parentId ?? undefined,\n touchedAt: Date.now(),\n });\n\n // 15) Ack reaction — fire-and-forget. Stash the resulting reaction id\n // keyed by messageId (NOT sessionId) so multiple messages in the same\n // session don't overwrite each other's cleanup data.\n const emoji = pickAckEmoji(options.ackReaction);\n if (emoji) {\n const inboundMsgId = parsed.messageId;\n helpers.waitUntil(\n client\n .addReaction({ messageId: inboundMsgId, emojiType: emoji })\n .then(({ reactionId }) => {\n ackReactions.set(inboundMsgId, { reactionId, createdAt: Date.now() });\n })\n .catch((e) => {\n console.warn(\n \"[eve-lark] ack reaction failed:\",\n e instanceof Error ? e.message : e,\n );\n }),\n );\n }\n\n return ackOk();\n };\n\n /**\n * Handle a `card.action.trigger` callback from Feishu. The user clicked a\n * button on an ask_card we rendered earlier. Extract the requestId +\n * optionId from `action.value`, resume the parked eve session with an\n * InputResponse, and patch the card to show the selection.\n *\n * Buttons we created carry `{__eveLarkAsk, requestId, optionId}` in their\n * `value`. Buttons from any other source are ignored.\n */\n async function handleCardAction(\n evt: LarkCardActionTriggerEvent,\n helpers: RouteHandlerArgs,\n ): Promise<Response> {\n const value = evt.action?.value;\n if (!value || value[ASK_BUTTON_VALUE_MARKER] !== true) {\n // Not our button/select — ignore (could be from another integration).\n return ackOk();\n }\n const requestId = typeof value.requestId === \"string\" ? value.requestId : \"\";\n // optionId location depends on the source element:\n // button: action.value.optionId (we put it there at render time)\n // select_static: action.option (Feishu returns the selected option's value string here)\n const optionId =\n (typeof value.optionId === \"string\" ? value.optionId : \"\") ||\n (typeof evt.action?.option === \"string\" ? evt.action.option : \"\");\n if (!requestId) return ackOk();\n\n const pending = pendingInputsByRequestId.get(requestId);\n if (!pending) {\n console.warn(\n `[eve-lark] card action for unknown requestId=${requestId} (already answered or expired)`,\n );\n return ackOk();\n }\n\n // ACK-FIRST: return ackOk() immediately so Feishu doesn't time out the\n // card.action.trigger callback (~3s) and revert the optimistic UI. The\n // actual work (patch the card to \"answered\" + resume the parked eve\n // session with the InputResponse) runs in the background via\n // helpers.waitUntil. Order within the background task matters: patch\n // FIRST so the user sees the \"✓ selected\" state as fast as possible,\n // THEN resume eve so model execution latency doesn't delay the visual\n // confirmation.\n const selectedOpt = pending.request.options?.find((o) => o.id === optionId);\n helpers.waitUntil(\n (async () => {\n if (pending.cardMessageId && selectedOpt) {\n // If the ask was rendered inline on a streaming card, preserve\n // the controller's accumulated buffer above the answered prompt\n // so the user doesn't lose what the agent said before asking.\n const ctrlForBuffer = controllers.get(pending.turnId);\n const priorBuffer = ctrlForBuffer?.getBuffer() ?? undefined;\n try {\n await client.patchCard({\n messageId: pending.cardMessageId,\n card: buildAskAnsweredCard(\n pending.request,\n { kind: \"option\", label: selectedOpt.label },\n priorBuffer,\n ),\n });\n // Clear the inline ask so the controller's next patch (turn 2's\n // streaming text after the user's answer resumes the session)\n // doesn't re-render the now-answered buttons.\n ctrlForBuffer?.clearAskRequest();\n } catch (e) {\n console.warn(\n \"[eve-lark] patchCard after ask-answer failed:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n\n const resp: LarkInputResponse = { requestId, optionId: optionId || undefined };\n const resumeToken = larkContinuationToken(\n pending.chatId,\n pending.parentId ?? pending.rootId ?? null,\n );\n const resumeAttributes: Record<string, string> = {\n chatId: pending.chatId,\n messageId: evt.open_message_id,\n chatType: pending.request.display === \"confirmation\" ? \"p2p\" : \"group\",\n };\n if (pending.rootId) resumeAttributes.rootMessageId = pending.rootId;\n if (pending.parentId) resumeAttributes.parentMessageId = pending.parentId;\n const resumeAuth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: evt.open_id,\n attributes: resumeAttributes,\n };\n try {\n await helpers.send(\n { inputResponses: [resp] } as never,\n { auth: resumeAuth as never, continuationToken: resumeToken },\n );\n console.log(\n `[eve-lark] ask answered via card action requestId=${requestId} optionId=${optionId}`,\n );\n } catch (e) {\n console.error(\n `[eve-lark] ask input-response send failed (requestId=${requestId}):`,\n e instanceof Error ? e.message : e,\n );\n }\n\n dropPendingInput(pending);\n })().catch((e) => {\n console.error(\"[eve-lark] card action background work failed:\", e);\n }),\n );\n\n return ackOk();\n }\n\n // Channel event handlers — declared as a standalone const so tests can\n // invoke them directly (eve's defineChannel hides events on the returned\n // Channel object). createLarkChannel attaches them as `__testEvents` on\n // the returned channel for that purpose; production code never reads it.\n const channelEvents = {\n // Streaming delta — patch the card.\n \"message.appended\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n if (options.replyMode !== \"streaming\" && options.replyMode !== \"streaming-v2\") return;\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) return;\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n const d = data as { messageDelta?: string };\n if (typeof d.messageDelta !== \"string\") return;\n const ctrl = getController(turnId, info);\n // Quote-reply: aim this turn's card at the inbound message that\n // started it. turn.started already mapped turnId → source (shifted off\n // the session's FIFO). Each turn owns its own controller, so each card\n // quotes its OWN source — interleaved replies don't overwrite each\n // other's rootId.\n const source = turnSources.get(turnId);\n if (source) {\n console.log(`[eve-lark] quote-reply turnId=${turnId} sessionId=${sessionId} → REPLY ${source}`);\n ctrl.setReplyTarget(source);\n }\n ctrl.appendDelta(d.messageDelta);\n },\n\n // Model is about to call tools. Record each call on the streaming\n // controller so it shows up in the card as ⏳ name. The controller\n // creates the card immediately if it doesn't exist yet, so the user\n // sees the tool call even before any text has streamed (which is the\n // common case — model often calls tools before producing visible\n // output). Only fires for streaming modes; post/static have no live\n // surface to update.\n async \"actions.requested\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n if (options.replyMode !== \"streaming\" && options.replyMode !== \"streaming-v2\") return;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) return;\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n const d = data as { actions?: Array<{ kind?: string; toolName?: string }> };\n const names = (d.actions ?? [])\n .map((a) => a.toolName)\n .filter((n): n is string => typeof n === \"string\");\n if (names.length === 0) return;\n // getController (not just .get) so we create the controller if it\n // doesn't exist yet — tools can fire before any message.appended.\n const ctrl = getController(turnId, info);\n for (const name of names) {\n ctrl.addToolCall(name);\n }\n },\n\n // A tool finished. Mark its entry ✓ (or ✗ on failure). Stays visible —\n // the user keeps the tool history at the top of the card through end\n // of turn. Best-effort: if we can't find the controller (e.g. post\n // mode, or session already cleaned up), no-op.\n async \"action.result\"(data: unknown, _channel: unknown, _ctx: { session: { id: string } }) {\n if (options.replyMode !== \"streaming\" && options.replyMode !== \"streaming-v2\") return;\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n const ctrl = controllers.get(turnId);\n if (!ctrl) return;\n const d = data as { result?: { toolName?: string; status?: string } };\n const name = d.result?.toolName;\n if (!name) return;\n ctrl.completeToolCall(name, d.result?.status === \"failed\");\n },\n\n // eve's ask_question (and similar HITL tools) fire this event with a\n // list of input requests. Each request becomes a Feishu card with\n // buttons (one per option) plus optional freeform hint.\n async \"input.requested\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) {\n console.warn(`[eve-lark] input.requested: no session info (sessionId=${sessionId})`);\n return;\n }\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) {\n console.warn(`[eve-lark] input.requested: no turnId (sessionId=${sessionId})`);\n return;\n }\n const d = data as { requests?: readonly LarkInputRequest[] };\n const requests = d.requests ?? [];\n if (requests.length === 0) return;\n\n console.log(\n `[eve-lark] input.requested sessionId=${sessionId} turnId=${turnId} chatId=${info.chatId} count=${requests.length}`,\n );\n\n for (const req of requests) {\n // Inline ask: if a streaming card already exists for this turn,\n // patch IT with the ask UI (prompt + option buttons appended below\n // the streaming text). This keeps the whole turn on one card — no\n // separate ask-card, no separate reply-card. Falls back to creating\n // a fresh ask-card when there's no streaming controller (post/static\n // reply modes, or the very first ask before any text streamed).\n const existingCtrl = controllers.get(turnId);\n const canPatchExisting =\n existingCtrl &&\n existingCtrl.getMessageId() &&\n (options.replyMode === \"streaming\" || options.replyMode === \"streaming-v2\");\n\n let cardMessageId: string | undefined;\n if (canPatchExisting && existingCtrl) {\n existingCtrl.setAskRequest(req);\n cardMessageId = existingCtrl.getMessageId();\n } else {\n const card = buildAskCard(req);\n try {\n const res = await client.sendCard({\n chatId: info.chatId,\n card,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n cardMessageId = res.messageId;\n } catch (e) {\n console.error(\n `[eve-lark] ask card send failed (requestId=${req.requestId}):`,\n e instanceof Error ? e.message : e,\n );\n continue;\n }\n }\n\n const pending: PendingInput = {\n requestId: req.requestId,\n sessionId,\n turnId,\n chatId: info.chatId,\n rootId: info.rootId,\n parentId: info.parentId,\n cardMessageId,\n request: req,\n createdAt: Date.now(),\n touchedAt: Date.now(),\n awaitingFreeform: req.allowFreeform === true,\n };\n pendingInputsByRequestId.set(req.requestId, pending);\n if (pending.awaitingFreeform) {\n const tokenKey = chatTokenKey(info.chatId, info.rootId, info.parentId);\n pendingInputsByChatToken.set(tokenKey, pending);\n }\n }\n },\n\n // Terminal — deliver the final reply, then clean up the ack reaction.\n // The per-turn controller is NOT destroyed here: in HITL flows eve fires\n // input.requested AFTER message.completed (still in the same turn), and\n // that handler needs the controller to patch the streaming card with the\n // ask UI inline. The controller is torn down by turn.completed (the true\n // end of turn). Duplicate message.completed events on the same turn are\n // deduped by the controller's finalize() state guard (state→completed on\n // first call, no-op thereafter).\n async \"message.completed\"(data: unknown, _channel: unknown, ctx: { session: { id: string } }) {\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) {\n console.warn(`[eve-lark] message.completed: no session info, cannot deliver (sessionId=${sessionId})`);\n return;\n }\n const d = data as { message?: string | null };\n const rawText = typeof d.message === \"string\" ? d.message : \"\";\n const mcTurnId = (data as { turnId?: string } | null)?.turnId;\n console.log(\n `[eve-lark] message.completed sessionId=${sessionId} chatId=${info.chatId} msgLen=${rawText.length}` + (mcTurnId ? ` turnId=${mcTurnId}` : \"\"),\n );\n const text = rawText.length > 0 ? rawText : EMPTY_REPLY_TEXT;\n\n const isStreaming = options.replyMode === \"streaming\" || options.replyMode === \"streaming-v2\";\n // Streaming dedup: if the controller is already gone (turn.completed or\n // turn.failed already tore it down), skip delivery. Otherwise the\n // controller's finalize() state guard handles same-turn duplicates.\n if (isStreaming && mcTurnId && !controllers.has(mcTurnId)) {\n if (!cleanedTurns.has(mcTurnId)) {\n cleanedTurns.add(mcTurnId);\n await cleanupAckForTurn(mcTurnId);\n }\n return;\n }\n\n try {\n await deliverReply(mcTurnId, info, text);\n } finally {\n if (mcTurnId && !cleanedTurns.has(mcTurnId)) {\n cleanedTurns.add(mcTurnId);\n await cleanupAckForTurn(mcTurnId);\n } else if (!mcTurnId) {\n await cleanupAckForSession(sessionId);\n }\n // NOTE: do NOT dropTurn here — see the comment at the top of this\n // handler. turn.completed / turn.failed own the controller teardown.\n }\n },\n\n async \"turn.failed\"(data: unknown, _channel: unknown, ctx: { session?: { id: string } } | null) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) {\n console.warn(\"[eve-lark] turn.failed: no sessionId on ctx\");\n return;\n }\n const info = sessionInfoFromCtx(ctx as never);\n if (!info) {\n console.warn(`[eve-lark] turn.failed: no session info (sessionId=${sessionId})`);\n return;\n }\n const failedTurnId = (data as { turnId?: string } | null)?.turnId;\n const userText = formatFailureMessage(data, \"turn failed\", { sentence: \"turn\" });\n const errorId = extractErrorId((data as { details?: unknown } | null)?.details);\n console.warn(\n `[eve-lark] turn.failed sessionId=${sessionId} chatId=${info.chatId}` +\n ` err=\"${userText.slice(0, 200)}\"` +\n (failedTurnId ? ` turnId=${failedTurnId}` : \"\") +\n (errorId ? ` errorId=${errorId}` : \"\"),\n );\n\n const ctrl = failedTurnId ? controllers.get(failedTurnId) : undefined;\n if (ctrl) {\n try {\n await ctrl.abort(userText);\n console.log(`[eve-lark] error shown via streaming abort (turnId=${failedTurnId})`);\n } catch (e) {\n console.warn(\n `[eve-lark] turn.failed: streaming abort failed, will deliver fresh error (turnId=${failedTurnId}):`,\n e instanceof Error ? e.message : e,\n );\n try {\n await deliverReply(failedTurnId, info, userText);\n } catch {\n // unreachable\n }\n }\n } else {\n try {\n await deliverReply(failedTurnId, info, userText);\n } catch {\n // unreachable\n }\n }\n\n if (!failedTurnId || !cleanedTurns.has(failedTurnId)) {\n if (failedTurnId) cleanedTurns.add(failedTurnId);\n if (failedTurnId) await cleanupAckForTurn(failedTurnId);\n else await cleanupAckForSession(sessionId);\n }\n // Turn failed — destroy the per-turn controller so a later\n // message.completed/turn.completed on this turnId doesn't reuse it.\n // (message.completed itself doesn't drop, to keep the controller alive\n // for a follow-up input.requested in HITL flows.)\n if (failedTurnId) dropTurn(failedTurnId);\n },\n\n async \"session.failed\"(data: unknown) {\n const userText = formatFailureMessage(data, \"session failed\", { sentence: \"session\" });\n const errorId = extractErrorId((data as { details?: unknown } | null)?.details);\n console.error(\n `[eve-lark] session.failed: ${userText}` + (errorId ? ` (errorId=${errorId})` : \"\"),\n );\n },\n\n // A new turn is starting. Per-turn controllers mean we no longer reset\n // existing controllers here — each turn gets a fresh one (created lazily\n // in message.appended / actions.requested). The only side effect left is\n // mapping turnId → inbound source message: shift the head of the\n // session's FIFO (populated by the webhook handler) so message.appended\n // can aim this turn's card at the right user message and terminal events\n // can clean up the right ack reaction.\n async \"turn.started\"(_data: unknown, _channel: unknown, ctx: { session?: { id: string } } | null) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) return;\n const tsTurnId = (_data as { turnId?: string } | null)?.turnId;\n console.log(`[eve-lark] turn.started sessionId=${sessionId}` + (tsTurnId ? ` turnId=${tsTurnId}` : \"\"));\n if (!tsTurnId) return;\n const queue = currentInboundMsgIds.get(sessionId);\n if (queue && queue.length > 0) {\n const source = queue.shift()!;\n if (queue.length === 0) currentInboundMsgIds.delete(sessionId);\n turnSources.set(tsTurnId, source);\n }\n },\n\n // Turn ended cleanly. eve fires this after the final message.completed\n // (or instead of it when the assistant step ended in tool-calls with no\n // visible text). Clean up the ack reaction and destroy the per-turn\n // controller (idempotent — message.completed/turn.failed may have done\n // it already).\n async \"turn.completed\"(data: unknown, _channel: unknown, _ctx: { session?: { id: string } } | null) {\n const turnId = (data as { turnId?: string } | null)?.turnId;\n if (!turnId) return;\n if (cleanedTurns.has(turnId)) {\n // message.completed already drained this turn — but we still drop\n // the per-turn controller (turn.completed is the LAST event for a\n // tool-only turn that never fires message.completed).\n dropTurn(turnId);\n return;\n }\n cleanedTurns.add(turnId);\n try {\n await cleanupAckForTurn(turnId);\n } catch {\n // best-effort\n }\n dropTurn(turnId);\n },\n\n // The agent needs the user to sign in to an external service (e.g.\n // GitHub, Slack, Linear). Render a card with a \"Sign in with <X>\"\n // URL button so the user can complete the flow in their browser.\n // The card message id is tracked so `authorization.completed` can\n // patch it with the outcome.\n async \"authorization.required\"(data: unknown, _channel: unknown, ctx: { session?: { id: string } }) {\n const sessionId = ctx?.session?.id;\n const info = sessionInfoFromCtx(ctx as never);\n if (!info || !sessionId) return;\n const d = data as {\n name?: string;\n authorization?: { displayName?: string; url?: string; userCode?: string };\n };\n const name = d.name ?? \"service\";\n const displayName = d.authorization?.displayName ?? name;\n const url = d.authorization?.url;\n if (!url) {\n console.warn(`[eve-lark] authorization.required for ${name}: no url, skipping card`);\n return;\n }\n const card = buildAuthCard({ displayName, url, userCode: d.authorization?.userCode });\n try {\n const res = await client.sendCard({ chatId: info.chatId, card, rootId: info.rootId, parentId: info.parentId });\n authCards.set(`${sessionId}:${name}`, res.messageId);\n } catch (e) {\n console.error(`[eve-lark] auth card send failed (${name}):`, e instanceof Error ? e.message : e);\n }\n },\n\n // The user completed (or declined) the external auth. Patch the card\n // we rendered in `authorization.required` to show the outcome.\n async \"authorization.completed\"(data: unknown, _channel: unknown, ctx: { session?: { id: string } }) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) return;\n const d = data as {\n name?: string;\n outcome?: string;\n reason?: string;\n authorization?: { displayName?: string };\n };\n const name = d.name ?? \"service\";\n const cardMessageId = authCards.get(`${sessionId}:${name}`);\n if (!cardMessageId) return; // no prior required card (or already cleaned up)\n const displayName = d.authorization?.displayName ?? name;\n const card = buildAuthCompletedCard({\n displayName,\n outcome: d.outcome ?? \"completed\",\n reason: d.reason,\n });\n try {\n await client.patchCard({ messageId: cardMessageId, card });\n } catch (e) {\n console.warn(`[eve-lark] auth card patch failed (${name}):`, e instanceof Error ? e.message : e);\n }\n authCards.delete(`${sessionId}:${name}`);\n },\n };\n\n const channel = defineChannel({\n routes: [POST(options.webhookPath, webhookHandler as never)],\n\n fetchFile: async (url: string) => {\n if (!url.startsWith(options.baseUrl)) return null;\n const m = url.match(/\\/messages\\/([^/]+)\\/resources\\/([^?]+)\\?type=(image|file)/);\n if (!m || !m[1] || !m[2] || !m[3]) return null;\n return client.downloadResource({\n messageId: m[1],\n fileKey: m[2],\n type: m[3] as \"image\" | \"file\",\n });\n },\n\n events: channelEvents,\n });\n\n // Test seam: expose the events map so tests can drive handlers directly\n // (eve's defineChannel hides them on the returned Channel). Production\n // code MUST NOT read this — it's typing-loose and not part of the API.\n (channel as Channel<undefined, Record<string, unknown>, Record<string, unknown>> & {\n __testEvents?: typeof channelEvents;\n }).__testEvents = channelEvents;\n\n return channel;\n}\n","/**\n * Typed error hierarchy for eve-lark.\n *\n * All errors extend a common base so consumers can `instanceof LarkChannelError`\n * to catch anything thrown by the channel.\n */\n\nexport class LarkChannelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\nexport class LarkConfigError extends LarkChannelError {}\n\nexport class LarkSignatureError extends LarkChannelError {}\n\nexport class LarkDecryptError extends LarkChannelError {}\n\nexport interface LarkApiErrorBody {\n code?: number | undefined;\n msg?: string | undefined;\n}\n\nexport class LarkApiError extends LarkChannelError {\n readonly code: number | undefined;\n readonly body: LarkApiErrorBody | undefined;\n readonly status: number | undefined;\n\n constructor(\n message: string,\n opts?: {\n code?: number | undefined;\n body?: LarkApiErrorBody | undefined;\n status?: number | undefined;\n cause?: unknown;\n },\n ) {\n super(message, { cause: opts?.cause });\n this.code = opts?.code;\n this.body = opts?.body;\n this.status = opts?.status;\n }\n}\n","import { LarkApiError, type LarkApiErrorBody } from \"./errors.js\";\nimport type { LarkCard, ResolvedLarkOptions } from \"./types.js\";\n\ninterface TokenState {\n value: string;\n expiresAt: number;\n}\n\nconst TOKEN_INVALID_CODES = new Set<number>([99991663, 99991664, 99991661]);\n\ninterface RequestResult {\n status: number;\n body: unknown;\n retryAfter: number | null;\n}\n\nexport class LarkClient {\n private readonly options: ResolvedLarkOptions;\n private token: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(options: ResolvedLarkOptions) {\n this.options = options;\n }\n\n async getTenantAccessToken(): Promise<string> {\n if (\n this.token &&\n Date.now() + this.options.tokenRefreshBufferMs < this.token.expiresAt\n ) {\n return this.token.value;\n }\n if (this.refreshPromise) return this.refreshPromise;\n this.refreshPromise = this.#refresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n async #refresh(): Promise<string> {\n const body = {\n app_id: this.options.appId,\n app_secret: this.options.appSecret,\n };\n const res = await this.options.fetch(\n `${this.options.baseUrl}/open-apis/auth/v3/tenant_access_token/internal`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n },\n );\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: token refresh failed (HTTP ${res.status})`,\n { status: res.status },\n );\n }\n const json = (await res.json()) as { code?: number; tenant_access_token?: string; expire?: number; msg?: string };\n if (json.code !== 0 || !json.tenant_access_token) {\n throw new LarkApiError(\n `eve-lark: token refresh returned code=${json.code ?? \"?\"} msg=${json.msg ?? \"?\"}`,\n { body: json, code: json.code },\n );\n }\n const expireSec = typeof json.expire === \"number\" ? json.expire : 7200;\n this.token = {\n value: json.tenant_access_token,\n expiresAt: Date.now() + expireSec * 1000,\n };\n return this.token.value;\n }\n\n async sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify({ text: args.content });\n if (args.rootId) {\n console.log(`[eve-lark] sendText → reply rootId=${args.rootId}`);\n return this.#replyMessage(args.rootId, \"text\", content);\n }\n console.log(`[eve-lark] sendText → send chatId=${args.chatId}`);\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"text\",\n content,\n });\n }\n\n async sendCard(args: {\n chatId: string;\n card: LarkCard;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify(args.card);\n if (args.rootId) {\n console.log(`[eve-lark] sendCard → reply rootId=${args.rootId}`);\n return this.#replyMessage(args.rootId, \"interactive\", content);\n }\n console.log(`[eve-lark] sendCard → send chatId=${args.chatId}`);\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"interactive\",\n content,\n });\n }\n\n async sendPost(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n // `msg_type: \"post\"` renders at native chat-message size with full\n // markdown support (bold, links, code, <font> color tags) via the\n // inner `{tag: \"md\"}` element. Cards render noticeably smaller because\n // Feishu treats them as \"structured content\"; post does not.\n //\n // The content schema is post > zh_cn > content > lines > inline nodes.\n // We put the whole reply in one md node — the md tag honors embedded\n // newlines, so multi-paragraph replies work as a single text string.\n const post = {\n zh_cn: {\n content: [[{ tag: \"md\", text: args.content }]],\n },\n };\n const content = JSON.stringify(post);\n if (args.rootId) {\n return this.#replyMessage(args.rootId, \"post\", content);\n }\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"post\",\n content,\n });\n }\n\n /** Quote-reply to a specific message via Feishu's reply API.\n * POST /open-apis/im/v1/messages/{message_id}/reply — this is the only\n * way to quote-reply to a normal (non-thread) message; sendMessage's\n * root_id field only works inside threads. */\n async #replyMessage(replyToMessageId: string, msgType: string, content: string): Promise<{ messageId: string }> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(replyToMessageId)}/reply`;\n const json = await this.#request(\"POST\", path, { msg_type: msgType, content });\n const messageId = (json as { data?: { message_id?: string } }).data?.message_id;\n console.log(`[eve-lark] reply API msgType=${msgType} replyTo=${replyToMessageId} → messageId=${messageId ?? \"?\"}`);\n if (!messageId) {\n throw new LarkApiError(\"eve-lark: reply missing message_id in response\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { messageId };\n }\n\n async #sendMessage(body: Record<string, unknown>): Promise<{ messageId: string }> {\n const payload = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n const json = await this.#request(\"POST\", \"/open-apis/im/v1/messages?receive_id_type=chat_id\", payload);\n const messageId = (json as { data?: { message_id?: string } }).data?.message_id;\n if (!messageId) {\n throw new LarkApiError(\"eve-lark: send missing message_id in response\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { messageId };\n }\n\n async patchCard(args: { messageId: string; card: LarkCard }): Promise<void> {\n await this.#request(\n \"PATCH\",\n `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}`,\n { content: JSON.stringify(args.card) },\n );\n }\n\n async downloadResource(args: {\n messageId: string;\n fileKey: string;\n type: \"image\" | \"file\";\n }): Promise<Buffer> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/resources/${encodeURIComponent(args.fileKey)}?type=${args.type}`;\n const token = await this.getTenantAccessToken();\n const res = await this.options.fetch(`${this.options.baseUrl}${path}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: downloadResource HTTP ${res.status}`,\n { status: res.status },\n );\n }\n return Buffer.from(await res.arrayBuffer());\n }\n\n async addReaction(args: {\n messageId: string;\n emojiType: string;\n }): Promise<{ reactionId: string }> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions`;\n const json = (await this.#request(\"POST\", path, {\n reaction_type: { emoji_type: args.emojiType },\n })) as { data?: { reaction_id?: string } };\n const reactionId = json.data?.reaction_id;\n if (!reactionId) {\n throw new LarkApiError(\"eve-lark: addReaction missing reaction_id\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { reactionId };\n }\n\n async removeReaction(args: { messageId: string; reactionId: string }): Promise<void> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions/${encodeURIComponent(args.reactionId)}`;\n await this.#request(\"DELETE\", path, undefined);\n }\n\n /**\n * Central request wrapper with auth, retry, and Feishu error decoding.\n *\n * Retry policy:\n * - 429 (rate limit): always retry with `Retry-After` backoff. Safe —\n * server rejected the request before processing.\n * - 5xx: retry ONLY for idempotent methods (GET / PATCH / DELETE). POST\n * is NOT retried on 5xx because Feishu's POST /messages and POST\n * /reactions are non-idempotent — the server may have created the\n * resource before returning the error, and retrying would silently\n * double-send.\n * - 401 / token-invalid code: refresh and retry once.\n * - Other 4xx: throw LarkApiError with the Feishu code/msg.\n */\n async #request(method: string, path: string, body: unknown): Promise<unknown> {\n const url = `${this.options.baseUrl}${path}`;\n let token = await this.getTenantAccessToken();\n let tokenRefreshed = false;\n const methodNorm = method.toUpperCase();\n const retryableMethod = methodNorm !== \"POST\";\n\n for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {\n const res = await this.options.fetch(url, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n\n const result = await this.#consumeResponse(res);\n const status = result.status;\n\n if (status >= 200 && status < 300) {\n const jsonBody = result.body as { code?: number; msg?: string };\n if (jsonBody && typeof jsonBody.code === \"number\" && jsonBody.code !== 0) {\n if (TOKEN_INVALID_CODES.has(jsonBody.code) && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed code=${jsonBody.code} msg=${jsonBody.msg ?? \"?\"}`,\n { code: jsonBody.code, body: jsonBody as LarkApiErrorBody, status },\n );\n }\n return result.body;\n }\n\n if (status === 401 && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n\n const isRateLimited = status === 429;\n const isServerErr = status >= 500 && status < 600;\n const retryable = isRateLimited || (isServerErr && retryableMethod);\n if (retryable && attempt < this.options.maxRetries) {\n const delayMs = this.#computeBackoff(status, result.retryAfter, attempt);\n await sleep(delayMs);\n continue;\n }\n\n const bodyObj = result.body as LarkApiErrorBody | undefined;\n const code = bodyObj?.code;\n const msg = bodyObj?.msg;\n const detail = msg ? ` code=${code ?? \"?\"} msg=${msg}` : \"\";\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed HTTP ${status}${detail}`,\n { status, body: bodyObj, code },\n );\n }\n throw new LarkApiError(`eve-lark: ${method} ${path} exhausted retries`);\n }\n\n async #consumeResponse(res: Response): Promise<RequestResult> {\n const retryAfterRaw = res.headers.get(\"retry-after\");\n const retryAfter = retryAfterRaw ? parseRetryAfter(retryAfterRaw) : null;\n const text = await res.text();\n if (!text) {\n return { status: res.status, body: undefined, retryAfter };\n }\n try {\n return { status: res.status, body: JSON.parse(text), retryAfter };\n } catch {\n return { status: res.status, body: { raw: text }, retryAfter };\n }\n }\n\n #computeBackoff(status: number, retryAfter: number | null, attempt: number): number {\n if (status === 429 && retryAfter !== null) {\n return Math.min(retryAfter * 1000, 10_000);\n }\n const base = 300 * Math.pow(2, attempt);\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\n return Math.max(0, Math.round(base + jitter));\n }\n}\n\nfunction parseRetryAfter(raw: string): number | null {\n const sec = Number(raw);\n if (Number.isFinite(sec) && sec >= 0) return sec;\n const date = Date.parse(raw);\n if (Number.isFinite(date)) return Math.max(0, (date - Date.now()) / 1000);\n return null;\n}\n\nfunction sleep(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * In-process deduplication for Feishu webhook events.\n *\n * Feishu retries delivery on non-2xx responses and during brief outage windows,\n * so consumers must idempotently ack events they've already seen. We key by\n * `header.event_id` (or `message.message_id` as a fallback) and remember each\n * key for the TTL window.\n *\n * Backed by an insertion-ordered Map so FIFO eviction is O(1) at the front.\n * Lazy sweep on every insert prevents unbounded growth of expired entries;\n * no `setInterval` so this is safe in serverless.\n */\nexport class DedupMap {\n private readonly entries = new Map<string, number>();\n private readonly ttlMs: number;\n private readonly maxEntries: number;\n private insertsSinceSweep = 0;\n\n constructor(ttlMs: number, maxEntries: number) {\n this.ttlMs = ttlMs;\n this.maxEntries = maxEntries;\n }\n\n has(key: string): boolean {\n const at = this.entries.get(key);\n if (at === undefined) return false;\n if (Date.now() - at > this.ttlMs) {\n this.entries.delete(key);\n return false;\n }\n return true;\n }\n\n set(key: string): void {\n this.maybeSweep();\n // Refresh timestamp by re-inserting at the tail of insertion order.\n this.entries.delete(key);\n this.entries.set(key, Date.now());\n\n while (this.entries.size > this.maxEntries) {\n const oldestKey = this.entries.keys().next().value;\n if (oldestKey === undefined) break;\n this.entries.delete(oldestKey);\n }\n }\n\n /**\n * Walk the insertion-ordered map from the front and drop expired entries.\n * Stops at the first non-expired entry since events arrive roughly in time\n * order. Called on every set, so cost is amortized.\n */\n private maybeSweep(): void {\n this.insertsSinceSweep += 1;\n if (this.insertsSinceSweep < 64 && this.entries.size < this.maxEntries) {\n return;\n }\n this.insertsSinceSweep = 0;\n const now = Date.now();\n for (const [key, at] of this.entries) {\n if (now - at <= this.ttlMs) break;\n this.entries.delete(key);\n }\n }\n}\n","import { createDecipheriv, createHash, timingSafeEqual } from \"node:crypto\";\nimport { LarkDecryptError } from \"./errors.js\";\n\n/**\n * Verify an `X-Lark-Signature` header against the raw webhook body.\n *\n * Feishu computes: `sha256(timestamp + nonce + encrypt_key + body)` and ships\n * the hex digest (optionally prefixed with `sha256=`) in `X-Lark-Signature`.\n * We concatenate the string parts first, then the raw bytes of the body, to\n * avoid a UTF-8 round-trip on the request body.\n *\n * Constant-time compare. Returns false on length mismatch instead of throwing.\n */\nexport function verifySignature(opts: {\n timestamp: string;\n nonce: string;\n encryptKey: string;\n rawBody: Buffer;\n signatureHeader: string;\n}): boolean {\n const expected = opts.signatureHeader.replace(/^sha256=/, \"\");\n const computed = createHash(\"sha256\")\n .update(opts.timestamp + opts.nonce + opts.encryptKey)\n .update(opts.rawBody)\n .digest(\"hex\");\n const a = Buffer.from(computed, \"hex\");\n const b = Buffer.from(expected, \"hex\");\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\n/**\n * Decrypt the `encrypt` field from a Feishu webhook body.\n *\n * Layout:\n * key = SHA256(encrypt_key) // 32 bytes → AES-256\n * buf = base64decode(encrypt_field)\n * iv = buf[0:16]\n * ct = buf[16:] // AES-256-CBC ciphertext\n * plaintext = AES_256_CBC_decrypt(key, iv, ct) // PKCS#7 unpadded\n *\n * Returns the raw plaintext bytes. The caller is expected to JSON.parse them.\n */\nexport function decryptPayload(encryptB64: string, encryptKey: string): Buffer {\n const key = createHash(\"sha256\").update(encryptKey).digest();\n const buf = Buffer.from(encryptB64, \"base64\");\n\n if (buf.length < 32) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext too short (${buf.length} bytes; need >= 32 for IV + one block)`,\n );\n }\n if ((buf.length - 16) % 16 !== 0) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext length ${buf.length} is not 16 + N*16`,\n );\n }\n\n const iv = buf.subarray(0, 16);\n const ct = buf.subarray(16);\n const dec = createDecipheriv(\"aes-256-cbc\", key, iv);\n\n try {\n return Buffer.concat([dec.update(ct), dec.final()]);\n } catch (e) {\n throw new LarkDecryptError(\"eve-lark: AES decrypt failed (bad padding or wrong key)\", {\n cause: e,\n });\n }\n}\n","import type {\n LarkInboundEvent,\n LarkInboundFile,\n LarkInboundResult,\n LarkMention,\n LarkRawMention,\n} from \"./types.js\";\n\nconst MIME_BY_EXT: Record<string, string> = {\n pdf: \"application/pdf\",\n zip: \"application/zip\",\n gz: \"application/gzip\",\n tar: \"application/x-tar\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n csv: \"text/csv\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n json: \"application/json\",\n xml: \"application/xml\",\n html: \"text/html\",\n htm: \"text/html\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n};\n\nfunction mimeFromExt(filename: string | undefined): string {\n if (!filename) return \"application/octet-stream\";\n const dot = filename.lastIndexOf(\".\");\n if (dot < 0) return \"application/octet-stream\";\n return MIME_BY_EXT[filename.slice(dot + 1).toLowerCase()] ?? \"application/octet-stream\";\n}\n\nfunction mentionFromRaw(m: LarkRawMention, botOpenId: string | undefined): LarkMention {\n const isOpenIdOfBot =\n !!botOpenId && !!m.id.open_id && m.id.open_id === botOpenId;\n const isAll = !!m.id.open_id && m.id.open_id === \"all\";\n return {\n key: m.key,\n id: {\n openId: m.id.open_id,\n userId: m.id.user_id,\n unionId: m.id.union_id,\n },\n name: m.name,\n idType: m.id_type ?? \"open_id\",\n isOpenIdOfBot,\n isAll,\n };\n}\n\nfunction stripBotMentions(text: string, mentions: LarkMention[]): string {\n // Feishu ships mentions as opaque placeholders (e.g. \"@_user_1\") in the text\n // body alongside a structured mentions array. Rewrite them to something the\n // model can read:\n // - the bot itself: dropped (the model already knows it's being addressed)\n // - @all: replaced with a literal \"@all\" token\n // - other users: replaced with \"@<display name>\"\n let out = text;\n for (const m of mentions) {\n if (!m.key) continue;\n if (m.isOpenIdOfBot) {\n out = out.split(m.key).join(\"\");\n } else if (m.isAll) {\n out = out.split(m.key).join(\"@all\");\n } else {\n out = out.split(m.key).join(`@${m.name}`);\n }\n }\n return out.replace(/\\s+/g, \" \").trim();\n}\n\ninterface ParsedContent {\n text: string;\n files: LarkInboundFile[];\n}\n\nfunction parseContent(messageType: string, rawContent: string): ParsedContent {\n if (!rawContent) return { text: \"\", files: [] };\n let content: Record<string, unknown>;\n try {\n content = JSON.parse(rawContent) as Record<string, unknown>;\n } catch {\n return { text: \"\", files: [] };\n }\n\n switch (messageType) {\n case \"text\": {\n const text = typeof content.text === \"string\" ? content.text : \"\";\n return { text, files: [] };\n }\n case \"image\": {\n const imageKey = typeof content.image_key === \"string\" ? content.image_key : \"\";\n if (!imageKey) return { text: \"\", files: [] };\n return {\n text: \"\",\n files: [{ fileKey: imageKey, mediaType: \"image/png\", kind: \"image\" }],\n };\n }\n case \"file\": {\n const fileKey = typeof content.file_key === \"string\" ? content.file_key : \"\";\n if (!fileKey) return { text: \"\", files: [] };\n const fileName = typeof content.file_name === \"string\" ? content.file_name : undefined;\n return {\n text: \"\",\n files: [{ fileKey, mediaType: mimeFromExt(fileName), kind: \"file\" }],\n };\n }\n case \"post\": {\n const locale = (content.zh_cn ?? content.en_us ?? content.ja_jp ?? null) as\n | { content?: unknown[][] }\n | null;\n if (!locale?.content) return { text: \"\", files: [] };\n const text = locale.content\n .flatMap((line) =>\n (line ?? [])\n .filter((node): node is { tag: string; text?: unknown } => {\n if (typeof node !== \"object\" || node === null) return false;\n const tag = (node as { tag?: unknown }).tag;\n const text = (node as { text?: unknown }).text;\n return tag === \"text\" && typeof text === \"string\";\n })\n .map((node) => node.text as string),\n )\n .join(\" \");\n return { text, files: [] };\n }\n default:\n // audio, media, sticker, share_chat, share_user, interactive — not in v1 scope.\n return { text: \"\", files: [] };\n }\n}\n\nexport function parseInbound(\n event: LarkInboundEvent,\n botOpenId?: string,\n): LarkInboundResult {\n const messageType = event.message.message_type;\n const parsed = parseContent(messageType, event.message.content);\n const rawMentions = event.message.mentions ?? [];\n const mentions = rawMentions.map((m) => mentionFromRaw(m, botOpenId));\n\n const senderOpenId =\n event.sender.sender_id.open_id ??\n event.sender.sender_id.user_id ??\n event.sender.sender_id.union_id ??\n \"\";\n\n const text =\n messageType === \"text\"\n ? stripBotMentions(parsed.text, mentions)\n : parsed.text;\n\n const chatType = event.chat_type === \"group\" ? \"group\" : \"p2p\";\n const senderType = event.sender.sender_type === \"app\" ? \"app\" : \"user\";\n\n return {\n text,\n files: parsed.files,\n chatId: event.message.chat_id,\n rootId: event.message.root_id ?? null,\n parentId: event.message.parent_id ?? null,\n messageId: event.message.message_id,\n senderOpenId,\n senderType,\n chatType,\n mentions,\n };\n}\n","import type { LarkCard, LarkCardButton, LarkInputRequest } from \"./types.js\";\n\n/**\n * One tool call's renderable state. Mirror of the same name in\n * streaming-controller.ts to avoid a circular import (controller imports\n * card builders, not vice versa).\n */\nexport interface ToolCallEntry {\n name: string;\n state: \"running\" | \"done\" | \"failed\";\n}\n\n/**\n * Render tool calls as a single grey-on-grey lark_md block above the answer\n * buffer. Running tools get `⏳`, completed get `✓` (green), failed get `✗`\n * (red). One line per tool so the user can see the full call history even\n * after the turn ends. Returns undefined when there are no tool calls so\n * callers can skip pushing an empty line.\n */\nexport function renderToolCalls(calls: readonly ToolCallEntry[]): string | undefined {\n if (calls.length === 0) return undefined;\n return calls\n .map((c) => {\n if (c.state === \"running\") return `<font color='blue'>⏳ ${c.name}</font>`;\n if (c.state === \"failed\") return `<font color='red'>✗ ${c.name}</font>`;\n return `<font color='green'>✓ ${c.name}</font>`;\n })\n .join(\"\\n\");\n}\n\nconst BASE_CONFIG = {\n wide_screen_mode: true,\n update_multi: true,\n} as const;\n\n/**\n * Build a Feishu interactive card that surfaces an eve `authorization.required`\n * event: the agent needs the user to sign in to an external service. Renders\n * the connection's display name, a URL button that opens the auth URL, and\n * the user code (if present) so the user can copy-paste it.\n */\nexport function buildAuthCard(opts: {\n displayName: string;\n url: string;\n userCode?: string | undefined;\n}): LarkCard {\n const elements: LarkCard[\"elements\"] = [\n { tag: \"div\", text: { tag: \"lark_md\", content: `Sign in to **${escapeMarkdown(opts.displayName)}** to continue.` } },\n {\n tag: \"action\",\n actions: [\n {\n tag: \"button\",\n text: { tag: \"plain_text\", content: `Sign in with ${opts.displayName}` },\n type: \"primary\",\n url: opts.url,\n },\n ],\n },\n ];\n if (opts.userCode) {\n elements.push({\n tag: \"div\",\n text: { tag: \"lark_md\", content: `Verification code: \\`${escapeMarkdown(opts.userCode)}\\`` },\n });\n }\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/**\n * Build the \"post-authorization\" card that replaces the auth card once the\n * user completes (or declines, or fails) the external sign-in.\n */\nexport function buildAuthCompletedCard(opts: {\n displayName: string;\n outcome: \"authorized\" | \"declined\" | \"failed\" | \"timed-out\" | string;\n reason?: string | undefined;\n}): LarkCard {\n const outcomeLabel: Record<string, string> = {\n authorized: \"✓\",\n declined: \"✗\",\n failed: \"⚠\",\n \"timed-out\": \"⏱\",\n };\n const glyph = outcomeLabel[opts.outcome] ?? \"•\";\n const outcomeText: Record<string, string> = {\n authorized: \"connected\",\n declined: \"declined\",\n failed: \"failed\",\n \"timed-out\": \"timed out\",\n };\n const label = outcomeText[opts.outcome] ?? opts.outcome;\n const suffix = opts.reason ? ` — ${escapeMarkdown(opts.reason)}` : \"\";\n return {\n config: { ...BASE_CONFIG },\n elements: [\n {\n tag: \"div\",\n text: { tag: \"lark_md\", content: `**${escapeMarkdown(opts.displayName)}**: ${glyph} ${label}${suffix}` },\n },\n ],\n };\n}\n\n/**\n * Build a simple single-shot card with the given markdown text. Renders via\n * `div` + `lark_md` so the font size is close to a native chat message\n * (the bare `markdown` element renders noticeably smaller).\n */\nexport function buildTextCard(text: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [{ tag: \"div\", text: { tag: \"lark_md\", content: text } }],\n };\n}\n\n/**\n * Build a streaming card with optional status prefix, tool-call history,\n * answer buffer, and inline ask UI. Tool calls render first (so the user\n * sees what the agent is doing / has done), then status, then the streamed\n * text. If `askRequest` is set, the prompt renders below the text and an\n * `action` row of option buttons is appended so the user can answer inline\n * on the SAME card — no separate ask-card, no separate reply card.\n */\nexport function buildStreamingCard(opts: {\n buffer: string;\n status?: string | undefined;\n toolCalls?: readonly ToolCallEntry[] | undefined;\n askRequest?: LarkInputRequest | null | undefined;\n}): LarkCard {\n const lines: string[] = [];\n const toolLine = renderToolCalls(opts.toolCalls ?? []);\n if (toolLine) lines.push(toolLine);\n if (opts.status) {\n lines.push(`<font color='grey'>${opts.status}</font>`);\n }\n lines.push(opts.buffer.length > 0 ? opts.buffer : \"_…_\");\n if (opts.askRequest) {\n lines.push(`**${opts.askRequest.prompt}**`);\n if (opts.askRequest.allowFreeform && (opts.askRequest.options?.length ?? 0) === 0) {\n lines.push(`<font color='grey'>_Reply to this chat with your answer_</font>`);\n }\n }\n const elements: LarkCard[\"elements\"] = [\n { tag: \"div\", text: { tag: \"lark_md\", content: lines.join(\"\\n\\n\") } },\n ];\n // Append option buttons as an action row when the ask has selectable options.\n // (select_static threshold is handled in buildAskCard; for the inline case\n // we always render buttons since the card already exists and we want\n // one-tap answering.)\n if (opts.askRequest?.options && opts.askRequest.options.length > 0) {\n const buttons: LarkCardButton[] = opts.askRequest.options.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: opts.askRequest!.requestId,\n optionId: opt.id,\n },\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/**\n * Build an error card displayed when a turn fails.\n */\nexport function buildErrorCard(message: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [\n { tag: \"div\", text: { tag: \"lark_md\", content: `<font color='red'>⚠ ${message}</font>` } },\n ],\n };\n}\n\n/** Marker placed in every ask-card button value so the card-action handler\n * can recognise our own callbacks (and ignore card actions from other\n * sources on the same message). */\nexport const ASK_BUTTON_VALUE_MARKER = \"__eveLarkAsk\";\n\n/** Above this many options, switch from buttons to a dropdown menu so the\n * card stays readable. Below it, buttons give one-tap answering. */\nconst ASK_OPTIONS_BUTTON_MAX = 3;\n\n/**\n * Build a Feishu interactive card that surfaces an eve `ask_question`\n * input request.\n *\n * Render choice:\n * - `display === \"select\"` OR options > {@link ASK_OPTIONS_BUTTON_MAX}:\n * a single `select_static` dropdown. The selected optionId comes back in\n * `action.option` (Feishu's select callback shape).\n * - Otherwise (display \"confirmation\" or short option list): one button per\n * option. Each button's `value` carries `{__eveLarkAsk, requestId,\n * optionId}`; Feishu returns it via `action.value` on click.\n *\n * For `allowFreeform: true` with no options, renders just the prompt (the\n * user replies with a normal chat message, which the channel intercepts).\n * For `allowFreeform: true` WITH options, renders the picker AND a footer\n * hint that the user can also type a reply.\n */\nexport function buildAskCard(request: LarkInputRequest): LarkCard {\n const elements: LarkCard[\"elements\"] = [\n { tag: \"div\", text: { tag: \"lark_md\", content: request.prompt } },\n ];\n\n const optionCount = request.options?.length ?? 0;\n if (optionCount > 0) {\n const useSelect =\n request.display === \"select\" || optionCount > ASK_OPTIONS_BUTTON_MAX;\n if (useSelect) {\n elements.push({\n tag: \"action\",\n actions: [\n {\n tag: \"select_static\",\n placeholder: { tag: \"plain_text\", content: \"Select an option…\" },\n options: request.options!.map((opt) => ({\n text: { tag: \"plain_text\", content: opt.label },\n value: opt.id,\n })),\n // Marker carries requestId; optionId is returned via action.option.\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: request.requestId,\n __larkSelect: true,\n },\n },\n ],\n });\n } else {\n const buttons: LarkCardButton[] = request.options!.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: request.requestId,\n optionId: opt.id,\n },\n ...(opt.description\n ? {\n confirm: {\n title: { tag: \"plain_text\", content: opt.label },\n text: { tag: \"plain_text\", content: opt.description },\n },\n }\n : {}),\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n }\n\n if (request.allowFreeform) {\n const hint =\n optionCount > 0\n ? \"_…or reply to this chat with your own answer_\"\n : \"_Reply to this chat with your answer_\";\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: hint } });\n }\n\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/**\n * Build the \"post-click\" card body that replaces the ask-card once the user\n * has answered. Disables further clicks by removing the action row and\n * appending a \"✓ <selected label>\" line.\n *\n * `priorBuffer` is optional streaming text from the controller when the ask\n * was rendered inline on a streaming card — preserves the prior turn's text\n * above the answered prompt instead of wiping it.\n */\nexport function buildAskAnsweredCard(\n request: LarkInputRequest,\n selected: { kind: \"option\"; label: string } | { kind: \"freeform\"; text: string },\n priorBuffer?: string | undefined,\n): LarkCard {\n const elements: LarkCard[\"elements\"] = [];\n if (priorBuffer && priorBuffer.length > 0) {\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: priorBuffer } });\n }\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: request.prompt } });\n const summary =\n selected.kind === \"option\"\n ? `<font color='green'>✓ ${escapeMarkdown(selected.label)}</font>`\n : `<font color='green'>✓ ${escapeMarkdown(selected.text)}</font>`;\n elements.push({ tag: \"div\", text: { tag: \"lark_md\", content: summary } });\n return { config: { ...BASE_CONFIG }, elements };\n}\n\n/** Escape characters that have special meaning in lark_md so user-controlled\n * strings can't inject formatting. */\nfunction escapeMarkdown(s: string): string {\n return s.replace(/[*_`~\\[\\]]/g, (m) => `\\\\${m}`);\n}\n\n// ---------------------------------------------------------------------------\n// CardKit v2 (schema 2.0) card builders. Used by replyMode: \"streaming-v2\".\n// The CardKit v2 schema wraps elements in `body.elements` (vs v1's bare\n// `elements`) and supports `config.streaming_mode` for live-patched cards\n// that render at near-native font size.\n// ---------------------------------------------------------------------------\n\nexport interface CardKitV2Card {\n schema: \"2.0\";\n config: {\n streaming_mode: boolean;\n wide_screen_mode?: boolean;\n update_multi?: boolean;\n };\n body: {\n elements: Array<{ tag: string; text?: { tag: string; content: string }; [k: string]: unknown }>;\n };\n}\n\n/**\n * Build a CardKit v2 card body for streaming. `streamingMode: true` for\n * intermediate patches, `false` for the final card.\n *\n * Uses the v2 `markdown` element (not `div+lark_md`) because CardKit v2\n * renders `markdown` at native chat-message size, while `div+lark_md` in v2\n * defaults to a larger / \"card-like\" size that looks unnatural in a chat\n * thread. (v1 interactive cards render `div+lark_md` at native size; v2\n * flips the defaults — pick the element that gives the look you want.)\n */\nexport function buildCardKitStreamingCard(opts: {\n buffer: string;\n status?: string | undefined;\n streamingMode: boolean;\n toolCalls?: readonly ToolCallEntry[] | undefined;\n askRequest?: LarkInputRequest | null | undefined;\n}): CardKitV2Card {\n const lines: string[] = [];\n const toolLine = renderToolCalls(opts.toolCalls ?? []);\n if (toolLine) lines.push(toolLine);\n if (opts.status) {\n lines.push(`<font color='grey'>${opts.status}</font>`);\n }\n lines.push(opts.buffer.length > 0 ? opts.buffer : \"_…_\");\n if (opts.askRequest) {\n lines.push(`**${opts.askRequest.prompt}**`);\n if (opts.askRequest.allowFreeform && (opts.askRequest.options?.length ?? 0) === 0) {\n lines.push(`<font color='grey'>_Reply to this chat with your answer_</font>`);\n }\n }\n const elements: CardKitV2Card[\"body\"][\"elements\"] = [\n { tag: \"markdown\", content: lines.join(\"\\n\\n\") },\n ];\n if (opts.askRequest?.options && opts.askRequest.options.length > 0) {\n const buttons = opts.askRequest.options.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: opts.askRequest!.requestId,\n optionId: opt.id,\n },\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n return {\n schema: \"2.0\",\n config: {\n streaming_mode: opts.streamingMode,\n wide_screen_mode: true,\n update_multi: true,\n },\n body: { elements },\n };\n}\n\n/**\n * Build a non-streaming CardKit v2 card with the final text. Used as the\n * terminal patch when the turn completes.\n */\nexport function buildCardKitFinalCard(\n text: string,\n toolCalls?: readonly ToolCallEntry[] | undefined,\n askRequest?: LarkInputRequest | null | undefined,\n): CardKitV2Card {\n const lines: string[] = [];\n const toolLine = renderToolCalls(toolCalls ?? []);\n if (toolLine) lines.push(toolLine);\n lines.push(text);\n if (askRequest) {\n lines.push(`**${askRequest.prompt}**`);\n }\n const elements: CardKitV2Card[\"body\"][\"elements\"] = [\n { tag: \"markdown\", content: lines.join(\"\\n\\n\") },\n ];\n if (askRequest?.options && askRequest.options.length > 0) {\n const buttons = askRequest.options.map((opt) => ({\n tag: \"button\",\n text: { tag: \"plain_text\", content: opt.label },\n type: opt.style ?? \"default\",\n value: {\n [ASK_BUTTON_VALUE_MARKER]: true,\n requestId: askRequest!.requestId,\n optionId: opt.id,\n },\n }));\n elements.push({ tag: \"action\", actions: buttons });\n }\n return {\n schema: \"2.0\",\n config: { streaming_mode: false, wide_screen_mode: true, update_multi: true },\n body: { elements },\n };\n}\n\n","import {\n buildCardKitFinalCard,\n buildCardKitStreamingCard,\n buildErrorCard,\n buildStreamingCard,\n buildTextCard,\n} from \"./card.js\";\nimport type { LarkInputRequest } from \"./types.js\";\n\ntype State = \"idle\" | \"creating\" | \"streaming\" | \"completed\" | \"aborted\";\n\n/**\n * One tool call's renderable state. Tracked across the turn so the user can\n * see what the agent did, not just the final text. Persistent: a fast tool\n * that completes between patches still shows up because we accumulate rather\n * than overwrite.\n */\nexport interface ToolCallEntry {\n name: string;\n state: \"running\" | \"done\" | \"failed\";\n}\n\ninterface ControllerDeps {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n patchIntervalMs: number;\n createThresholdMs: number;\n /** When true, use CardKit v2 schema (schema 2.0 + streaming_mode) instead\n * of v1 interactive cards. Better font size, slightly different API path. */\n useCardKitV2?: boolean | undefined;\n}\n\ninterface LarkClientLike {\n sendCard(args: {\n chatId: string;\n card: unknown;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n patchCard(args: { messageId: string; card: unknown }): Promise<void>;\n sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n}\n\n/**\n * Streaming interactive-card state machine.\n *\n * idle ──first delta──> creating ──sendCard ok──> streaming ──finalize──> completed\n * │\n * └──sendCard fail──> aborted (flag; static fallback on message.completed)\n *\n * The card is created lazily after `createThresholdMs` of the first delta, so\n * short turns can short-circuit straight to `finalize` (which sends the card\n * with the full answer in one shot). Once streaming, patches are throttled to\n * `patchIntervalMs`.\n *\n * If `sendCard` fails on creation, the controller flips to `fallbackToText`\n * and `finalize`/`ensureFinalized` deliver via `sendText` instead.\n */\nexport class StreamingCardController {\n private deps: ControllerDeps;\n private readonly client: LarkClientLike;\n\n private state: State = \"idle\";\n private buffer = \"\";\n private status: string | undefined;\n private messageId: string | undefined;\n private fallbackToText = false;\n /** Active HITL input request, when set via `setAskRequest`. The card\n * builder appends prompt + buttons to the same streaming card so the\n * user can answer inline instead of getting a separate ask-card. */\n private askRequest: LarkInputRequest | null = null;\n /** Tool calls made during this turn, in order. Rendered above the buffer\n * so users can see what the agent is doing / has done, Claude-Code-style. */\n private toolCalls: ToolCallEntry[] = [];\n\n private createTimer: ReturnType<typeof setTimeout> | null = null;\n private patchTimer: ReturnType<typeof setTimeout> | null = null;\n private patchInFlight: Promise<void> | null = null;\n private createInFlight: Promise<void> | null = null;\n private patchScheduled = false;\n private lastPatchAt = 0;\n\n constructor(client: LarkClientLike, deps: ControllerDeps) {\n this.client = client;\n this.deps = deps;\n }\n\n appendDelta(text: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.buffer += text;\n if (this.state === \"idle\") {\n this.scheduleCreate();\n } else if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n setStatus(status: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.status = status;\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /**\n * Record the start of a tool call. Renders as `🔧 name` (or `⏳ name` while\n * running). Persistent across patches — won't be lost if the tool completes\n * between throttled patches.\n *\n * Schedules a patch regardless of state (idle/creating/streaming) so the\n * user sees the call even when no text has streamed yet, which is the\n * common case (model often calls a tool before any visible output).\n */\n addToolCall(name: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n // Don't add a duplicate running entry for the same name (some tools\n // batch-call with the same name in one actions.requested event).\n if (this.toolCalls.some((t) => t.name === name && t.state === \"running\")) {\n return;\n }\n this.toolCalls.push({ name, state: \"running\" });\n // Kick the create if we're idle (so the card appears showing the tool\n // call before any text delta). Otherwise patch.\n if (this.state === \"idle\") {\n this.scheduleCreate();\n } else if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /**\n * Mark the most-recently-started running entry for `name` as done/failed.\n * Rendered as `✓ name` (green) or `✗ name` (red). Stays visible — the\n * entry is not removed, so the user sees the full tool history at the\n * end of the turn.\n */\n completeToolCall(name: string, failed = false): void {\n for (let i = this.toolCalls.length - 1; i >= 0; i--) {\n const entry = this.toolCalls[i];\n if (entry && entry.name === name && entry.state === \"running\") {\n entry.state = failed ? \"failed\" : \"done\";\n break;\n }\n }\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /** Read-only view of tool calls (for card builders). */\n getToolCalls(): readonly ToolCallEntry[] {\n return this.toolCalls;\n }\n\n /** Current streaming buffer (for handleCardAction to preserve when patching\n * the \"answered\" state of an inline ask card). */\n getBuffer(): string {\n return this.buffer;\n }\n\n /** Card message id (so input.requested can reuse the existing card instead\n * of creating a separate ask-card). */\n getMessageId(): string | undefined {\n return this.messageId;\n }\n\n /** Aim the next card we create at this user message (quote-reply). Used\n * when messages interleave so the user can tell which reply answers which\n * message. Must be called before the first sendCard (i.e. in the webhook\n * handler right after helpers.send, before any streaming delta).\n *\n * Sets rootId (not parentId) because Feishu's sendMessage API uses root_id\n * for quote-reply; parent_id is ignored. */\n setReplyTarget(messageId: string): void {\n this.deps.rootId = messageId;\n }\n\n /** Active HITL request being rendered inline on this card, or null. */\n getAskRequest(): LarkInputRequest | null {\n return this.askRequest;\n }\n\n /**\n * Render an `ask_question` request inline on this card by appending prompt\n * + option buttons below the streaming text. Patches immediately so the\n * user sees the buttons as soon as the agent asks.\n *\n * If the prior turn already finalized (state=\"completed\"), transition back\n * to \"streaming\" so the next patch can update the same card. The card\n * keeps its messageId — no new card is sent.\n */\n setAskRequest(req: LarkInputRequest): void {\n if (this.state === \"aborted\") return;\n this.askRequest = req;\n if (this.state === \"completed\" && this.messageId) {\n this.state = \"streaming\";\n }\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n /** Clear the inline ask request (e.g. after the user clicked an option). */\n clearAskRequest(): void {\n this.askRequest = null;\n }\n\n /**\n * Reset per-turn state for the next turn within the same session. Clears\n * the streaming buffer, tool-call history, status, and any inline ask —\n * but keeps the card messageId and transitions back to \"streaming\" so the\n * next message.appended patches the SAME card. Without this, the second\n * turn's message.completed would create a brand-new card and the user\n * would see N cards for N turns within one logical conversation.\n *\n * Called from the channel's `turn.started` event handler.\n */\n resetForNewTurn(): void {\n this.buffer = \"\";\n this.status = undefined;\n this.toolCalls = [];\n this.askRequest = null;\n this.fallbackToText = false;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n this.patchInFlight = null;\n this.patchScheduled = false;\n // Keep messageId; transition back to \"streaming\" so the next delta patches.\n if (this.messageId) {\n this.state = \"streaming\";\n } else {\n this.state = \"idle\";\n }\n }\n\n /**\n * Full reset for a brand-new inbound user message (not a continuation\n * of the same conversation flow). Same as {@link resetForNewTurn} but\n * ALSO clears the card messageId so the next `sendCard` creates a fresh\n * card. Without this, all top-level messages in the same chat would\n * patch the first card — the user would only see the latest reply\n * overwritten onto one card instead of N independent cards.\n */\n resetForNewMessage(replyTargetMessageId?: string): void {\n this.resetForNewTurn();\n this.messageId = undefined;\n this.state = \"idle\";\n // Quote-reply target for THIS turn's card. Passed in from turn.started\n // (the inbound messageId at the head of the channel's FIFO queue) so each\n // message's card quotes its OWN user message, not whichever arrived last.\n if (replyTargetMessageId) this.deps.rootId = replyTargetMessageId;\n }\n\n async finalize(fullText: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n this.buffer = fullText;\n\n // Wait for any in-flight doCreate before checking messageId.\n // Without this, finalize can see messageId=undefined (doCreate's sendCard\n // hasn't resolved yet) and fire a 2nd sendCard — producing duplicate cards.\n if (this.createInFlight) {\n try {\n await this.createInFlight;\n } catch {\n // doCreate failure is handled by the fallbackToText flag below.\n }\n this.createInFlight = null;\n }\n\n if (this.fallbackToText) {\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n return;\n }\n\n if (this.messageId === undefined) {\n // Never managed to create a card. Send one with the full text in a\n // single shot so the user still gets a card reply.\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: this.deps.useCardKitV2\n ? buildCardKitFinalCard(fullText, this.toolCalls, this.askRequest)\n : buildTextCard(fullText),\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"completed\";\n } catch {\n // Last-resort fallback: plain text.\n this.fallbackToText = true;\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n }\n return;\n }\n\n // Card already exists; flush the final state.\n if (this.patchInFlight) {\n try {\n await this.patchInFlight;\n } catch {\n // swallow; we'll attempt the final patch below\n }\n }\n await this.client.patchCard({\n messageId: this.messageId,\n card: this.deps.useCardKitV2\n ? buildCardKitStreamingCard({ buffer: fullText, streamingMode: false, toolCalls: this.toolCalls, askRequest: this.askRequest })\n : buildStreamingCard({ buffer: fullText, status: undefined, toolCalls: this.toolCalls, askRequest: this.askRequest }),\n });\n this.state = \"completed\";\n }\n\n async abort(error: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n if (this.messageId === undefined) {\n // No card to patch; mark fallback and let finalize/ensureFinalized\n // deliver a plain-text error if asked.\n this.fallbackToText = true;\n this.state = \"aborted\";\n return;\n }\n try {\n await this.client.patchCard({\n messageId: this.messageId,\n card: buildErrorCard(error),\n });\n } finally {\n this.state = \"aborted\";\n }\n }\n\n async ensureFinalized(): Promise<void> {\n if (this.state !== \"completed\" && this.state !== \"aborted\") {\n await this.finalize(this.buffer);\n }\n }\n\n isStreaming(): boolean {\n return this.state === \"streaming\" || this.state === \"creating\";\n }\n\n isCompleted(): boolean {\n return this.state === \"completed\" || this.state === \"aborted\";\n }\n\n private scheduleCreate(): void {\n if (this.createTimer) return;\n this.state = \"creating\";\n // Snapshot rootId: card creation has a createThresholdMs delay, during\n // which a subsequent turn's resetForNewMessage may overwrite deps.rootId.\n // The snapshot ensures the card quotes the user message from the turn\n // that called scheduleCreate (this turn), not a later one.\n const rootIdSnapshot = this.deps.rootId;\n this.createTimer = setTimeout(() => {\n this.createTimer = null;\n this.createInFlight = this.doCreate(rootIdSnapshot).then(() => {\n this.createInFlight = null;\n });\n }, this.deps.createThresholdMs);\n }\n\n private cancelCreateTimer(): void {\n if (this.createTimer) {\n clearTimeout(this.createTimer);\n this.createTimer = null;\n }\n }\n\n private async doCreate(rootIdSnapshot?: string): Promise<void> {\n if (this.state !== \"creating\") return;\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: this.deps.useCardKitV2\n ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls, askRequest: this.askRequest })\n : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls, askRequest: this.askRequest }),\n rootId: rootIdSnapshot ?? this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"streaming\";\n this.lastPatchAt = Date.now();\n } catch (e) {\n console.warn(\n \"[eve-lark] streaming card create failed; will deliver via plain text on finalize:\",\n e instanceof Error ? e.message : e,\n );\n this.fallbackToText = true;\n this.state = \"streaming\"; // keep accepting deltas; finalize will deliver as text\n }\n }\n\n private schedulePatch(): void {\n if (this.patchScheduled) return;\n this.patchScheduled = true;\n const elapsed = Date.now() - this.lastPatchAt;\n const wait = Math.max(0, this.deps.patchIntervalMs - elapsed);\n this.patchTimer = setTimeout(() => {\n this.patchTimer = null;\n this.patchScheduled = false;\n void this.maybeFlushPatch();\n }, wait);\n }\n\n private cancelPatchTimer(): void {\n if (this.patchTimer) {\n clearTimeout(this.patchTimer);\n this.patchTimer = null;\n }\n this.patchScheduled = false;\n }\n\n private async maybeFlushPatch(): Promise<void> {\n if (this.state !== \"streaming\") return;\n if (this.patchInFlight) return;\n if (this.messageId === undefined) return;\n const card = this.deps.useCardKitV2\n ? buildCardKitStreamingCard({ buffer: this.buffer, status: this.status, streamingMode: true, toolCalls: this.toolCalls, askRequest: this.askRequest })\n : buildStreamingCard({ buffer: this.buffer, status: this.status, toolCalls: this.toolCalls, askRequest: this.askRequest });\n this.patchInFlight = this.client\n .patchCard({ messageId: this.messageId, card })\n .catch((e) => {\n // Best-effort: the next delta will retry. Log so operators can see\n // when the card stream is degraded.\n console.warn(\n \"[eve-lark] streaming card patch failed:\",\n e instanceof Error ? e.message : e,\n );\n })\n .finally(() => {\n this.patchInFlight = null;\n this.lastPatchAt = Date.now();\n });\n await this.patchInFlight;\n }\n}\n","import { LarkConfigError } from \"./errors.js\";\nimport type {\n LarkChannelOptions,\n LarkReplyMode,\n LarkTransportMode,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\nconst DEFAULTS = {\n baseUrl: \"https://open.feishu.cn\",\n webhookPath: \"/lark/webhook\",\n // \"streaming-v2\" uses Feishu CardKit v2 (schema 2.0 + streaming_mode) for\n // live-patched interactive cards — the best live UX this channel can offer\n // today. Users who prefer native chat-message size (with markdown) at the\n // cost of no streaming should opt into replyMode: \"post\".\n replyMode: \"streaming-v2\" as LarkReplyMode,\n streamPatchIntervalMs: 1000,\n streamCreateThresholdMs: 400,\n dedupTtlMs: 30 * 60 * 1000,\n dedupMaxEntries: 5000,\n requestTimeoutMs: 15000,\n maxRetries: 2,\n tokenRefreshBufferMs: 5 * 60 * 1000,\n signatureSkewMs: 5 * 60 * 1000,\n ackReaction: \"Typing\" as string | false,\n mode: \"long-connection\" as LarkTransportMode,\n};\n\nconst ENV_KEYS = {\n appId: \"LARK_APP_ID\",\n appSecret: \"LARK_APP_SECRET\",\n verificationToken: \"LARK_VERIFICATION_TOKEN\",\n encryptKey: \"LARK_ENCRYPT_KEY\",\n baseUrl: \"LARK_BASE_URL\",\n botOpenId: \"LARK_BOT_OPEN_ID\",\n replyMode: \"LARK_REPLY_MODE\",\n mode: \"LARK_MODE\",\n} as const;\n\nexport type ResolveEnv = Record<string, string | undefined>;\n\nfunction defaultEnv(): ResolveEnv {\n if (typeof process !== \"undefined\" && process.env) {\n return process.env as ResolveEnv;\n }\n return {};\n}\n\nfunction pick(input: string | undefined, envValue: string | undefined): string | undefined {\n return input ?? envValue;\n}\n\nexport function resolveOptions(\n options: LarkChannelOptions,\n env: ResolveEnv = defaultEnv(),\n): ResolvedLarkOptions {\n const appId = pick(options.appId, env[ENV_KEYS.appId]);\n const appSecret = pick(options.appSecret, env[ENV_KEYS.appSecret]);\n const verificationToken = pick(\n options.verificationToken,\n env[ENV_KEYS.verificationToken],\n );\n\n if (!appId) {\n throw new LarkConfigError(\n `eve-lark: appId is required (option \\`appId\\` or env \\`${ENV_KEYS.appId}\\`)`,\n );\n }\n if (!appSecret) {\n throw new LarkConfigError(\n `eve-lark: appSecret is required (option \\`appSecret\\` or env \\`${ENV_KEYS.appSecret}\\`)`,\n );\n }\n if (!verificationToken) {\n throw new LarkConfigError(\n `eve-lark: verificationToken is required (option \\`verificationToken\\` or env \\`${ENV_KEYS.verificationToken}\\`)`,\n );\n }\n\n const rawBaseUrl = pick(options.baseUrl, env[ENV_KEYS.baseUrl]) ?? DEFAULTS.baseUrl;\n const baseUrl = rawBaseUrl.replace(/\\/+$/, \"\");\n\n const replyModeEnv = env[ENV_KEYS.replyMode];\n const replyMode: LarkReplyMode =\n options.replyMode ??\n (replyModeEnv === \"post\" || replyModeEnv === \"static\" || replyModeEnv === \"streaming\" || replyModeEnv === \"streaming-v2\"\n ? replyModeEnv\n : DEFAULTS.replyMode);\n\n const modeEnv = env[ENV_KEYS.mode];\n const mode: LarkTransportMode =\n options.mode ??\n (modeEnv === \"webhook\" || modeEnv === \"long-connection\" ? modeEnv : DEFAULTS.mode);\n\n return {\n appId,\n appSecret,\n verificationToken,\n encryptKey: pick(options.encryptKey, env[ENV_KEYS.encryptKey]),\n baseUrl,\n botOpenId: pick(options.botOpenId, env[ENV_KEYS.botOpenId]),\n webhookPath: options.webhookPath ?? DEFAULTS.webhookPath,\n replyMode,\n streamPatchIntervalMs: options.streamPatchIntervalMs ?? DEFAULTS.streamPatchIntervalMs,\n streamCreateThresholdMs: options.streamCreateThresholdMs ?? DEFAULTS.streamCreateThresholdMs,\n dedupTtlMs: options.dedupTtlMs ?? DEFAULTS.dedupTtlMs,\n dedupMaxEntries: options.dedupMaxEntries ?? DEFAULTS.dedupMaxEntries,\n requestTimeoutMs: options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs,\n maxRetries: options.maxRetries ?? DEFAULTS.maxRetries,\n tokenRefreshBufferMs: options.tokenRefreshBufferMs ?? DEFAULTS.tokenRefreshBufferMs,\n signatureSkewMs: options.signatureSkewMs ?? DEFAULTS.signatureSkewMs,\n fetch: options.fetch ?? globalThis.fetch,\n ackReaction: options.ackReaction ?? DEFAULTS.ackReaction,\n mode,\n port:\n options.port ??\n (process.env.PORT ? Number(process.env.PORT) : 2000),\n allowFrom: options.allowFrom,\n groupAllowFrom: options.groupAllowFrom,\n groupConfigs: options.groupConfigs,\n asrProvider: options.asrProvider,\n };\n}\n","/**\n * Long-connection transport: when `mode: \"long-connection\"` is set on\n * {@link createLarkChannel} (the default), the channel starts a Feishu\n * `@larksuiteoapi/node-sdk` WSClient as a side effect of construction. Each\n * inbound event is re-encrypted + re-signed and POSTed to the channel's own\n * webhook on localhost, where the standard webhook handler runs (with full\n * access to `send()` etc.). This lets users run the bot against a real Feishu\n * app from `eve dev` alone — no public webhook URL, no second process.\n *\n * The SDK is a hard runtime dependency of this package (declared in\n * `dependencies`), so `pnpm add eve-lark` brings it in automatically. The\n * `import()` below is dynamic only so `mode: \"webhook\"` code paths don't\n * eagerly load the SDK at module import time.\n */\n\nimport {\n createCipheriv,\n createHash,\n randomBytes,\n} from \"node:crypto\";\nimport type { ResolvedLarkOptions } from \"./types.js\";\n\n/** A Feishu v2 envelope we forward to the channel webhook. */\nexport type LarkEvent = {\n schema?: string;\n type?: string;\n challenge?: string;\n token?: string;\n header?: Record<string, unknown>;\n event?: unknown;\n [k: string]: unknown;\n};\n\nexport interface PostEventOptions {\n eveWebhookUrl: string;\n /** When set, the body is AES-encrypted and the request is signed. */\n encryptKey?: string | undefined;\n /** Override for tests. Defaults to globalThis.fetch. */\n fetch?: typeof fetch | undefined;\n}\n\nfunction aesEncrypt(plaintext: Buffer, key: string): Buffer {\n const keyBuf = createHash(\"sha256\").update(key).digest();\n const iv = randomBytes(16);\n const cipher = createCipheriv(\"aes-256-cbc\", keyBuf, iv);\n return Buffer.concat([iv, cipher.update(plaintext), cipher.final()]);\n}\n\nfunction signBody(timestamp: string, nonce: string, body: Buffer, key: string): string {\n return createHash(\"sha256\")\n .update(timestamp + nonce + key)\n .update(body)\n .digest(\"hex\");\n}\n\n/**\n * Forward one Feishu event to the local eve webhook. Re-encrypts and signs\n * when an `encryptKey` is set, so the channel handler exercises its own\n * signature + AES pipeline on every event.\n */\nexport async function postEventToWebhook(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n const plain = Buffer.from(JSON.stringify(event), \"utf8\");\n let body: Buffer;\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n\n if (opts.encryptKey) {\n const encrypted = aesEncrypt(plain, opts.encryptKey).toString(\"base64\");\n body = Buffer.from(JSON.stringify({ encrypt: encrypted }), \"utf8\");\n const ts = Math.floor(Date.now() / 1000).toString();\n const nonce = randomBytes(8).toString(\"hex\");\n const sig = signBody(ts, nonce, body, opts.encryptKey);\n headers[\"x-lark-request-timestamp\"] = ts;\n headers[\"x-lark-request-nonce\"] = nonce;\n headers[\"x-lark-signature\"] = `sha256=${sig}`;\n } else {\n body = plain;\n }\n\n const res = await fetchImpl(opts.eveWebhookUrl, {\n method: \"POST\",\n headers,\n body: new Uint8Array(body.buffer, body.byteOffset, body.byteLength) as never,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `eve-lark: forward to ${opts.eveWebhookUrl} failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : \"\"}`,\n );\n }\n}\n\n/**\n * Post-event wrapper with a single exponential-backoff retry. Catches the\n * common case where `eve dev` is momentarily unavailable (between HMR\n * reloads, mid-restart, GC pause). Without this the event would be dropped\n * on the floor with just a log line — bad UX during dev.\n */\nexport async function postEventToWebhookRetry(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n try {\n await postEventToWebhook(event, opts);\n } catch (firstErr) {\n await new Promise((r) => setTimeout(r, 300));\n // Regenerate signature: timestamp/nonce moved on by ~300ms, and the\n // skew check at the channel handler would reject a stale signature.\n await postEventToWebhook(event, opts).catch((retryErr) => {\n throw retryErr instanceof Error\n ? retryErr\n : new Error(String(retryErr), { cause: firstErr });\n });\n }\n}\n\n/**\n * The Feishu SDK's EventDispatcher passes handlers a payload that may or may\n * not include the outer envelope. Rebuild a v2-shaped envelope so the channel\n * webhook can parse it the same way it parses a raw Feishu POST.\n */\nexport function rebuildEnvelopeFromSdkEvent(\n eventType: string,\n data: unknown,\n ctx: { appId: string; verificationToken: string },\n): LarkEvent {\n const maybeHeader = (data as { header?: Record<string, unknown> })?.header;\n const header =\n maybeHeader && typeof maybeHeader === \"object\"\n ? maybeHeader\n : {\n event_id: `lc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n event_type: eventType,\n create_time: String(Math.floor(Date.now() / 1000)),\n token: ctx.verificationToken,\n app_id: ctx.appId,\n };\n const maybeEvent = (data as { event?: unknown })?.event;\n return {\n schema: \"2.0\",\n header,\n event: maybeEvent ?? data,\n };\n}\n\nexport interface StartLongConnectionArgs {\n resolved: ResolvedLarkOptions;\n eveWebhookUrl: string;\n /** Override logger. */\n log?: ((msg: string) => void) | undefined;\n logError?: ((msg: string, err?: unknown) => void) | undefined;\n /** Test seam: inject a custom SDK module. */\n sdk?: unknown;\n}\n\n/**\n * Detect whether this process is the `eve start` launcher (NOT the nitro\n * server it spawns). The launcher loads the channel module for build\n * discovery but never serves HTTP; the spawned `.output/server/index.mjs`\n * child is what actually runs the server. Without this check, both\n * processes would start a WSClient and Feishu would deliver every event\n * twice.\n *\n * Discriminator: the launcher's `argv[1]` is the eve CLI binary\n * (`.../eve/bin/eve.js` or `.mjs`/`.cjs`/no-ext shim) and `argv[2]` is\n * `start`. The spawned server child has `argv[1] = .output/server/index.mjs`\n * and `argv[2]` unset. `eve dev` runs nitro in-process (no fork), so we\n * DON'T treat it as a launcher — the WSClient must start there.\n *\n * Override: set `EVE_LARK_FORCE_WS=1` to always start (escape hatch in\n * case the heuristic breaks for some package-manager layout).\n */\nexport function isEveStartLauncher(): boolean {\n if (process.env.EVE_LARK_FORCE_WS === \"1\") return false;\n const arg1 = process.argv[1] ?? \"\";\n const isEveBinary = /[/\\\\]eve[/\\\\]bin[/\\\\]eve(?:\\.[cm]?js)?$/.test(arg1);\n return isEveBinary && process.argv[2] === \"start\";\n}\n\n/**\n * Active connections keyed by `${appId}:${eveWebhookUrl}`.\n *\n * Eve's lifecycle can construct the channel module more than once (e.g.,\n * build-time scan + serve-time import, or HMR reload). Each construction\n * would naively start a fresh WSClient — Feishu then delivers every event\n * to BOTH connections, and the user sees double replies.\n *\n * Guard: if a connection for the same key is already running (or starting),\n * the second call resolves immediately without touching the SDK. On\n * failure, the slot is cleared so a retry can succeed.\n *\n * The map lives on `globalThis` so it survives module reloads (HMR, build\n * then serve) — otherwise a reloaded module instance would have its own\n * fresh map and the guard would silently fail.\n *\n * Single-process, so no lock is needed — `Map.has` + `Map.set` from the\n * same synchronous block is atomic in JS's single-threaded runtime.\n */\nconst GLOBAL_KEY = \"__eveLarkActiveConnections\";\ntype GlobalWithLark = typeof globalThis & {\n [GLOBAL_KEY]?: Map<string, Promise<void>>;\n};\nfunction getActiveConnections(): Map<string, Promise<void>> {\n const g = globalThis as GlobalWithLark;\n if (!g[GLOBAL_KEY]) g[GLOBAL_KEY] = new Map();\n return g[GLOBAL_KEY]!;\n}\n\n/** @internal — test-only seam for resetting module state between cases. */\nexport function __resetLongConnectionSingletonsForTests(): void {\n getActiveConnections().clear();\n}\n\n/**\n * Start the Feishu WSClient side effect. Connects via the official SDK,\n * registers a handler that re-signs each event and POSTs it to the local eve\n * webhook. Resolves once the connection is established; the WSClient then\n * runs in the background for the lifetime of the process.\n *\n * Idempotent: a second call with the same `appId` + `eveWebhookUrl` is a\n * no-op (see {@link getActiveConnections}). Different keys (different app,\n * or different webhook URL — e.g. different `--port`) get separate WSClients.\n *\n * @throws if @larksuiteoapi/node-sdk is not installed, or the WSClient\n * fails to establish its first connection.\n */\nexport async function startLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const key = `${args.resolved.appId}:${args.eveWebhookUrl}`;\n const activeConnections = getActiveConnections();\n const existing = activeConnections.get(key);\n if (existing) {\n console.log(\n `[eve-lark] startLongConnection: skip (already running) key=${key} pid=${process.pid}`,\n );\n return;\n }\n console.log(\n `[eve-lark] startLongConnection: start new WSClient key=${key} pid=${process.pid}`,\n );\n\n const promise = (async () => {\n await doStartLongConnection(args);\n })().catch((e) => {\n // On failure, clear the slot so a future call can retry.\n activeConnections.delete(key);\n throw e;\n });\n\n // Synchronous set after the synchronous has-check above — atomic in JS's\n // single-threaded runtime. No race with another concurrent caller.\n activeConnections.set(key, promise);\n await promise;\n}\n\nasync function doStartLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const log = args.log ?? ((m: string) => console.log(`[eve-lark] ${m}`));\n const logError = args.logError ?? ((m: string, e?: unknown) => console.error(`[eve-lark] ${m}`, e ?? \"\"));\n\n const sdk = (args.sdk ?? (await loadLarkSdk())) as LarkSdk;\n\n const dispatcher = new sdk.EventDispatcher({\n verificationToken: args.resolved.verificationToken,\n encryptKey: args.resolved.encryptKey,\n });\n\n dispatcher.register({\n \"im.message.receive_v1\": async (data: unknown) => {\n try {\n const envelope = rebuildEnvelopeFromSdkEvent(\"im.message.receive_v1\", data, {\n appId: args.resolved.appId,\n verificationToken: args.resolved.verificationToken,\n });\n await postEventToWebhookRetry(envelope, {\n eveWebhookUrl: args.eveWebhookUrl,\n encryptKey: args.resolved.encryptKey,\n });\n } catch (e) {\n logError(`forward failed (event dropped)`, e);\n }\n },\n\n // Card-button clicks. Feishu's card.action.trigger fires when a user\n // taps a button on a card we rendered. Forward to the channel webhook\n // — the webhook handler dispatches by event_type and feeds the click\n // back into eve as an InputResponse.\n \"card.action.trigger\": async (data: unknown) => {\n try {\n const envelope = rebuildEnvelopeFromSdkEvent(\"card.action.trigger\", data, {\n appId: args.resolved.appId,\n verificationToken: args.resolved.verificationToken,\n });\n await postEventToWebhookRetry(envelope, {\n eveWebhookUrl: args.eveWebhookUrl,\n encryptKey: args.resolved.encryptKey,\n });\n } catch (e) {\n logError(`card action forward failed (event dropped)`, e);\n }\n },\n });\n\n const domain = args.resolved.baseUrl.includes(\"larksuite.com\")\n ? sdk.Domain.Lark\n : sdk.Domain.Feishu;\n\n const wsClient = new sdk.WSClient({\n appId: args.resolved.appId,\n appSecret: args.resolved.appSecret,\n domain,\n onReady: () => log(`WS connected to Feishu (${args.resolved.baseUrl})`),\n onError: (err: Error) => logError(`WS error`, err),\n onReconnecting: () => log(`WS reconnecting…`),\n onReconnected: () => log(`WS reconnected`),\n autoReconnect: true,\n });\n\n await wsClient.start({ eventDispatcher: dispatcher });\n}\n\n/* eslint-disable @typescript-eslint/consistent-type-imports */\n// The `import()` type query is the canonical way to express \"the runtime\n// namespace of this dynamic import\" without making the SDK a hard dep.\ntype LarkSdk = typeof import(\"@larksuiteoapi/node-sdk\");\n/* eslint-enable @typescript-eslint/consistent-type-imports */\n\nasync function loadLarkSdk(): Promise<LarkSdk> {\n try {\n return await import(\"@larksuiteoapi/node-sdk\");\n } catch {\n throw new Error(\n \"eve-lark: mode:\\\"long-connection\\\" requires @larksuiteoapi/node-sdk. Install it: pnpm add @larksuiteoapi/node-sdk (or npm/yarn equivalent).\",\n );\n }\n}\n","/**\n * Valid Feishu emoji type strings for the message-reactions API.\n *\n * Feishu emoji types are case-sensitive and inconsistent: most are uppercase\n * (`THUMBSUP`, `OK`), but a meaningful subset is CamelCase (`Typing`,\n * `CrossMark`, `EatingFood`, `Drumstick`, …). Passing the wrong case fails\n * with HTTP 400 code=231001 \"reaction type is invalid\" — silent unless the\n * caller is watching logs.\n *\n * Source: openclaw-lark's `VALID_FEISHU_EMOJI_TYPES` (which references the\n * official Feishu emoji doc).\n */\nexport const VALID_FEISHU_EMOJI_TYPES: ReadonlySet<string> = new Set([\n \"OK\", \"THUMBSUP\", \"THANKS\", \"MUSCLE\", \"FINGERHEART\", \"APPLAUSE\", \"FISTBUMP\",\n \"JIAYI\", \"DONE\", \"SMILE\", \"BLUSH\", \"LAUGH\", \"SMIRK\", \"LOL\", \"FACEPALM\",\n \"LOVE\", \"WINK\", \"PROUD\", \"WITTY\", \"SMART\", \"SCOWL\", \"THINKING\", \"SOB\",\n \"CRY\", \"ERROR\", \"NOSEPICK\", \"HAUGHTY\", \"SLAP\", \"SPITBLOOD\", \"TOASTED\",\n \"GLANCE\", \"DULL\", \"INNOCENTSMILE\", \"JOYFUL\", \"WOW\", \"TRICK\", \"YEAH\",\n \"ENOUGH\", \"TEARS\", \"EMBARRASSED\", \"KISS\", \"SMOOCH\", \"DROOL\", \"OBSESSED\",\n \"MONEY\", \"TEASE\", \"SHOWOFF\", \"COMFORT\", \"CLAP\", \"PRAISE\", \"STRIVE\",\n \"XBLUSH\", \"SILENT\", \"WAVE\", \"WHAT\", \"FROWN\", \"SHY\", \"DIZZY\", \"LOOKDOWN\",\n \"CHUCKLE\", \"WAIL\", \"CRAZY\", \"WHIMPER\", \"HUG\", \"BLUBBER\", \"WRONGED\",\n \"HUSKY\", \"SHHH\", \"SMUG\", \"ANGRY\", \"HAMMER\", \"SHOCKED\", \"TERROR\",\n \"PETRIFIED\", \"SKULL\", \"SWEAT\", \"SPEECHLESS\", \"SLEEP\", \"DROWSY\", \"YAWN\",\n \"SICK\", \"PUKE\", \"BETRAYED\", \"HEADSET\", \"EatingFood\", \"MeMeMe\", \"Sigh\",\n \"Typing\", \"SLIGHT\", \"TONGUE\", \"EYESCLOSED\", \"RoarForYou\", \"CALF\", \"BEAR\",\n \"BULL\", \"RAINBOWPUKE\", \"Lemon\", \"ROSE\", \"HEART\", \"PARTY\", \"LIPS\", \"BEER\",\n \"CAKE\", \"GIFT\", \"CUCUMBER\", \"Drumstick\", \"Pepper\", \"CANDIEDHAWS\",\n \"BubbleTea\", \"Coffee\", \"Get\", \"LGTM\", \"OnIt\", \"OneSecond\", \"VRHeadset\",\n \"YouAreTheBest\", \"SALUTE\", \"SHAKE\", \"HIGHFIVE\", \"UPPERLEFT\", \"ThumbsDown\",\n \"Yes\", \"No\", \"OKR\", \"CheckMark\", \"CrossMark\", \"MinusOne\", \"Hundred\",\n \"AWESOMEN\", \"Pin\", \"Alarm\", \"Loudspeaker\", \"Trophy\", \"Fire\", \"BOMB\",\n \"Music\", \"XmasTree\", \"Snowman\", \"XmasHat\", \"FIREWORKS\", \"2022\",\n \"REDPACKET\", \"FORTUNE\", \"LUCK\", \"FIRECRACKER\", \"StickyRiceBalls\",\n \"HEARTBROKEN\", \"POOP\", \"StatusFlashOfInspiration\", \"18X\", \"CLEAVER\",\n \"Soccer\", \"Basketball\", \"GeneralDoNotDisturb\", \"Status_PrivateMessage\",\n \"GeneralInMeetingBusy\", \"StatusReading\", \"StatusInFlight\",\n \"GeneralBusinessTrip\", \"GeneralWorkFromHome\", \"StatusEnjoyLife\",\n \"GeneralTravellingCar\", \"StatusBus\", \"GeneralSun\", \"GeneralMoonRest\",\n \"MoonRabbit\", \"Mooncake\", \"JubilantRabbit\", \"TV\", \"Movie\", \"Pumpkin\",\n \"BeamingFace\", \"Delighted\", \"ColdSweat\", \"FullMoonFace\", \"Partying\",\n \"GoGoGo\", \"ThanksFace\", \"SaluteFace\", \"Shrug\", \"ClownFace\", \"HappyDragon\",\n]);\n\n/** Returns true iff `s` is a valid Feishu emoji type string (case-sensitive). */\nexport function isValidFeishuEmojiType(s: string): boolean {\n return VALID_FEISHU_EMOJI_TYPES.has(s);\n}\n"],"mappings":"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","names":["text","createHash","createHash"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve-lark",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Lark/Feishu channel for the eve agent framework",
5
5
  "type": "module",
6
6
  "license": "MIT",