@openclaw/feishu 2026.5.24-beta.2 → 2026.5.26-beta.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.
@@ -1,17 +1,19 @@
1
1
  import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-DuL575sn.js";
2
2
  import { n as looksLikeFeishuId, r as normalizeFeishuTarget, t as detectIdType } from "./targets-BUjQ1TcA.js";
3
- import { a as resolveDefaultFeishuAccountId, f as isRecord$1, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-CRcvqpsl.js";
3
+ import { a as resolveDefaultFeishuAccountId, f as isRecord$2, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-CXnY5H8g.js";
4
+ import { n as createFeishuSendReceipt, s as createFeishuCardInteractionEnvelope } from "./send-result-CHvu8Rr7.js";
4
5
  import { t as messageActionTargetAliases } from "./security-audit-BIeA3W3Q.js";
5
6
  import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-ChjJKAJ9.js";
6
7
  import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-BIHeF-S_.js";
7
8
  import { t as resolveFeishuSessionConversation } from "./session-conversation-CZSMgac-.js";
8
- import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
9
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
9
10
  import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
11
+ import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
10
12
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
11
13
  import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
12
14
  import { adaptScopedAccountAccessor, createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
13
15
  import { buildChannelOutboundSessionRoute, createChatChannelPlugin, stripChannelTargetPrefix } from "openclaw/plugin-sdk/channel-core";
14
- import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
16
+ import { defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
15
17
  import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
16
18
  import { createAllowlistProviderGroupPolicyWarningCollector, projectConfigAccountIdWarningCollector } from "openclaw/plugin-sdk/channel-policy";
17
19
  import { createChannelDirectoryAdapter, createRuntimeDirectoryLiveAdapter, listDirectoryGroupEntriesFromMapKeysAndAllowFrom, listDirectoryUserEntriesFromAllowFromAndMapKeys } from "openclaw/plugin-sdk/directory-runtime";
@@ -19,15 +21,20 @@ import { normalizeMessagePresentation, renderMessagePresentationFallbackText } f
19
21
  import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
20
22
  import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
21
23
  import { buildProbeChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
22
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
24
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, normalizeAccountId as normalizeAccountId$1, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
23
25
  import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
24
26
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
25
27
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
26
28
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
27
29
  import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
28
- import { normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/account-id";
30
+ import { normalizeAccountId as normalizeAccountId$2 } from "openclaw/plugin-sdk/account-id";
29
31
  import { z } from "zod";
30
32
  import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$2 } from "openclaw/plugin-sdk/secret-input";
33
+ import fs from "node:fs";
34
+ import os from "node:os";
35
+ import path from "node:path";
36
+ import { loadSessionStore, resolveSessionFilePath, resolveStorePath, updateSessionStore } from "openclaw/plugin-sdk/session-store-runtime";
37
+ import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
31
38
  import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
32
39
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput as hasConfiguredSecretInput$1, mergeAllowFromEntries, patchTopLevelChannelConfigSection, promptSingleChannelSecretInput, splitSetupEntries } from "openclaw/plugin-sdk/setup";
33
40
  //#region extensions/feishu/src/approval-auth.ts
@@ -50,100 +57,6 @@ const feishuApprovalAuth = createResolvedApproverActionAuthAdapter({
50
57
  normalizeSenderId: (value) => normalizeFeishuApproverId(value)
51
58
  });
52
59
  //#endregion
53
- //#region extensions/feishu/src/card-interaction.ts
54
- const FEISHU_CARD_INTERACTION_VERSION = "ocf1";
55
- function isInteractionKind(value) {
56
- return value === "button" || value === "quick" || value === "meta";
57
- }
58
- function isMetadataValue(value) {
59
- return value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
60
- }
61
- function createFeishuCardInteractionEnvelope(envelope) {
62
- return {
63
- oc: FEISHU_CARD_INTERACTION_VERSION,
64
- ...envelope
65
- };
66
- }
67
- function buildFeishuCardActionTextFallback(event) {
68
- const actionValue = event.action.value;
69
- if (isRecord$1(actionValue)) {
70
- if (typeof actionValue.text === "string") return actionValue.text;
71
- if (typeof actionValue.command === "string") return actionValue.command;
72
- return JSON.stringify(actionValue);
73
- }
74
- return String(actionValue);
75
- }
76
- function decodeFeishuCardAction(params) {
77
- const { event, now = Date.now() } = params;
78
- const actionValue = event.action.value;
79
- if (!isRecord$1(actionValue) || actionValue.oc !== "ocf1") return {
80
- kind: "legacy",
81
- text: buildFeishuCardActionTextFallback(event)
82
- };
83
- if (!isInteractionKind(actionValue.k) || typeof actionValue.a !== "string" || !actionValue.a) return {
84
- kind: "invalid",
85
- reason: "malformed"
86
- };
87
- if (actionValue.q !== void 0 && typeof actionValue.q !== "string") return {
88
- kind: "invalid",
89
- reason: "malformed"
90
- };
91
- if (actionValue.m !== void 0) {
92
- if (!isRecord$1(actionValue.m)) return {
93
- kind: "invalid",
94
- reason: "malformed"
95
- };
96
- for (const value of Object.values(actionValue.m)) if (!isMetadataValue(value)) return {
97
- kind: "invalid",
98
- reason: "malformed"
99
- };
100
- }
101
- if (actionValue.c !== void 0) {
102
- if (!isRecord$1(actionValue.c)) return {
103
- kind: "invalid",
104
- reason: "malformed"
105
- };
106
- if (actionValue.c.u !== void 0 && typeof actionValue.c.u !== "string") return {
107
- kind: "invalid",
108
- reason: "malformed"
109
- };
110
- if (actionValue.c.h !== void 0 && typeof actionValue.c.h !== "string") return {
111
- kind: "invalid",
112
- reason: "malformed"
113
- };
114
- if (actionValue.c.s !== void 0 && typeof actionValue.c.s !== "string") return {
115
- kind: "invalid",
116
- reason: "malformed"
117
- };
118
- if (actionValue.c.e !== void 0 && !Number.isFinite(actionValue.c.e)) return {
119
- kind: "invalid",
120
- reason: "malformed"
121
- };
122
- if (actionValue.c.t !== void 0 && actionValue.c.t !== "p2p" && actionValue.c.t !== "group") return {
123
- kind: "invalid",
124
- reason: "malformed"
125
- };
126
- if (typeof actionValue.c.e === "number" && actionValue.c.e < now) return {
127
- kind: "invalid",
128
- reason: "stale"
129
- };
130
- const expectedUser = actionValue.c.u?.trim();
131
- if (expectedUser && expectedUser !== (event.operator.open_id ?? "").trim()) return {
132
- kind: "invalid",
133
- reason: "wrong_user"
134
- };
135
- const expectedChat = actionValue.c.h?.trim();
136
- if (expectedChat && expectedChat !== (event.context.chat_id ?? "").trim()) return {
137
- kind: "invalid",
138
- reason: "wrong_conversation"
139
- };
140
- }
141
- return {
142
- kind: "structured",
143
- envelope: actionValue
144
- };
145
- }
146
- //#endregion
147
60
  //#region extensions/feishu/src/config-schema.ts
148
61
  const ChannelActionsSchema = z.object({ reactions: z.boolean().optional() }).strict().optional();
149
62
  const DmPolicySchema = z.enum([
@@ -365,7 +278,7 @@ const FeishuConfigSchema = z.object({
365
278
  }).strict().superRefine((value, ctx) => {
366
279
  const defaultAccount = value.defaultAccount?.trim();
367
280
  if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
368
- const normalizedDefaultAccount = normalizeAccountId$1(defaultAccount);
281
+ const normalizedDefaultAccount = normalizeAccountId$2(defaultAccount);
369
282
  if (!Object.prototype.hasOwnProperty.call(value.accounts, normalizedDefaultAccount)) ctx.addIssue({
370
283
  code: z.ZodIssueCode.custom,
371
284
  path: ["defaultAccount"],
@@ -460,6 +373,556 @@ async function listFeishuDirectoryGroups(params) {
460
373
  }).map((entry) => entry.id));
461
374
  }
462
375
  //#endregion
376
+ //#region extensions/feishu/src/doctor.ts
377
+ const FEISHU_STATE_DIR = "feishu";
378
+ const BACKUP_PREFIX = "feishu-state-repair";
379
+ const BLANK_USER_MESSAGE_REPAIR_THRESHOLD = 3;
380
+ const SESSION_FILE_INSPECTION_MAX_BYTES = 16 * 1024 * 1024;
381
+ function timestampForPath(now = /* @__PURE__ */ new Date()) {
382
+ return now.toISOString().replaceAll(":", "-");
383
+ }
384
+ function isRecord$1(value) {
385
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
386
+ }
387
+ function toFeishuSessionEntry(value) {
388
+ if (!isRecord$1(value)) return {};
389
+ return {
390
+ sessionId: value.sessionId,
391
+ sessionFile: value.sessionFile
392
+ };
393
+ }
394
+ function countLabel(count, singular, plural = `${singular}s`) {
395
+ return `${count} ${count === 1 ? singular : plural}`;
396
+ }
397
+ function existsDir(dir) {
398
+ try {
399
+ return fs.statSync(dir).isDirectory();
400
+ } catch {
401
+ return false;
402
+ }
403
+ }
404
+ function existsFile(filePath) {
405
+ try {
406
+ return fs.statSync(filePath).isFile();
407
+ } catch {
408
+ return false;
409
+ }
410
+ }
411
+ function safeReadDir(dir) {
412
+ try {
413
+ return fs.readdirSync(dir, { withFileTypes: true });
414
+ } catch {
415
+ return [];
416
+ }
417
+ }
418
+ function isPathWithinRoot(targetPath, rootPath) {
419
+ const resolvedTarget = path.resolve(targetPath);
420
+ const resolvedRoot = path.resolve(rootPath);
421
+ const relative = path.relative(resolvedRoot, resolvedTarget);
422
+ return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
423
+ }
424
+ function formatDisplayPath(filePath) {
425
+ const home = os.homedir();
426
+ const resolved = path.resolve(filePath);
427
+ return resolved === home || resolved.startsWith(`${home}${path.sep}`) ? `~${resolved.slice(home.length)}` : resolved;
428
+ }
429
+ function formatFinding(finding) {
430
+ switch (finding.kind) {
431
+ case "corrupt-state-json": return `- Feishu local JSON state is corrupt: ${formatDisplayPath(finding.path)}`;
432
+ case "missing-session-transcript": return `- Feishu session ${finding.sessionKey} points to a missing transcript in ${formatDisplayPath(finding.storePath)}`;
433
+ case "invalid-session-transcript": return `- Feishu session ${finding.sessionKey} has an invalid transcript (${finding.reason}): ${formatDisplayPath(finding.path)}`;
434
+ case "blank-user-message-run": return `- Feishu session ${finding.sessionKey} contains ${finding.count} blank user messages: ${formatDisplayPath(finding.path)}`;
435
+ }
436
+ return finding;
437
+ }
438
+ function isFeishuSessionStoreKey(key) {
439
+ const normalized = key.trim().toLowerCase();
440
+ return /^agent:[^:]+:feishu(?::|$)/.test(normalized) || /^feishu(?::|$)/.test(normalized);
441
+ }
442
+ function isFeishuAcpBindingSessionKey(key) {
443
+ return /^agent:[^:]+:acp:binding:feishu(?::|$)/.test(key.trim().toLowerCase());
444
+ }
445
+ function normalizeMetadataString(value) {
446
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
447
+ }
448
+ function isFeishuSessionEntry(key, value) {
449
+ if (isFeishuAcpBindingSessionKey(key)) return false;
450
+ if (isFeishuSessionStoreKey(key)) return true;
451
+ if (!isRecord$1(value)) return false;
452
+ if (normalizeMetadataString(value.channel) === "feishu" || normalizeMetadataString(value.lastChannel) === "feishu") return true;
453
+ if (normalizeMetadataString((isRecord$1(value.route) ? value.route : null)?.channel) === "feishu") return true;
454
+ if (normalizeMetadataString((isRecord$1(value.deliveryContext) ? value.deliveryContext : null)?.channel) === "feishu") return true;
455
+ if (normalizeMetadataString((isRecord$1(value.pendingFinalDeliveryContext) ? value.pendingFinalDeliveryContext : null)?.channel) === "feishu") return true;
456
+ const origin = isRecord$1(value.origin) ? value.origin : null;
457
+ const originProvider = normalizeMetadataString(origin?.provider);
458
+ const originSurface = normalizeMetadataString(origin?.surface);
459
+ const originFrom = normalizeMetadataString(origin?.from);
460
+ return originProvider === "feishu" || originSurface.startsWith("feishu") || originFrom.startsWith("feishu:");
461
+ }
462
+ function collectConfiguredAgentIds(cfg) {
463
+ const ids = /* @__PURE__ */ new Set();
464
+ ids.add(resolveConfiguredDefaultAgentId(cfg));
465
+ for (const agent of cfg.agents?.list ?? []) if (typeof agent.id === "string" && agent.id.trim()) ids.add(normalizeAgentId(agent.id));
466
+ return [...ids].toSorted();
467
+ }
468
+ function resolveConfiguredDefaultAgentId(cfg) {
469
+ const agents = cfg.agents?.list ?? [];
470
+ const chosen = agents.find((agent) => agent?.default) ?? agents[0];
471
+ return normalizeAgentId(typeof chosen?.id === "string" && chosen.id.trim() ? chosen.id : "main");
472
+ }
473
+ function collectFeishuSessionTargets(params) {
474
+ const byStorePath = /* @__PURE__ */ new Map();
475
+ const addTarget = (target) => {
476
+ byStorePath.set(path.resolve(target.storePath), {
477
+ ...target,
478
+ storePath: path.resolve(target.storePath)
479
+ });
480
+ };
481
+ for (const agentId of collectConfiguredAgentIds(params.cfg)) addTarget({
482
+ agentId,
483
+ storePath: resolveStorePath(params.cfg.session?.store, {
484
+ agentId,
485
+ env: params.env
486
+ })
487
+ });
488
+ const agentsDir = path.join(params.stateDir, "agents");
489
+ for (const agentDir of safeReadDir(agentsDir)) {
490
+ if (!agentDir.isDirectory()) continue;
491
+ const agentId = normalizeAgentId(agentDir.name);
492
+ const storePath = path.join(agentsDir, agentDir.name, "sessions", "sessions.json");
493
+ if (existsFile(storePath)) addTarget({
494
+ agentId,
495
+ storePath
496
+ });
497
+ }
498
+ return [...byStorePath.values()].toSorted((left, right) => left.storePath.localeCompare(right.storePath));
499
+ }
500
+ function collectJsonFiles(rootDir, limit = 200) {
501
+ const files = [];
502
+ const visit = (dir) => {
503
+ if (files.length >= limit) return;
504
+ for (const entry of safeReadDir(dir).toSorted((left, right) => left.name.localeCompare(right.name))) {
505
+ const fullPath = path.join(dir, entry.name);
506
+ if (entry.isDirectory()) {
507
+ visit(fullPath);
508
+ continue;
509
+ }
510
+ if (entry.isFile() && entry.name.endsWith(".json")) files.push(fullPath);
511
+ if (files.length >= limit) return;
512
+ }
513
+ };
514
+ if (existsDir(rootDir)) visit(rootDir);
515
+ return files;
516
+ }
517
+ function collectCorruptFeishuStateJsonFindings(feishuStateDir) {
518
+ const findings = [];
519
+ for (const filePath of collectJsonFiles(feishuStateDir)) try {
520
+ JSON.parse(fs.readFileSync(filePath, "utf-8"));
521
+ } catch {
522
+ findings.push({
523
+ kind: "corrupt-state-json",
524
+ path: filePath
525
+ });
526
+ }
527
+ return findings;
528
+ }
529
+ function resolveSessionTranscriptCandidates(params) {
530
+ const candidates = /* @__PURE__ */ new Set();
531
+ const sessionsDir = path.dirname(params.storePath);
532
+ const addSafeCandidate = (candidate) => {
533
+ const resolved = path.isAbsolute(candidate) ? path.resolve(candidate) : path.resolve(sessionsDir, candidate);
534
+ if (resolved === sessionsDir || !isPathWithinRoot(resolved, sessionsDir)) return;
535
+ candidates.add(resolved);
536
+ };
537
+ if (typeof params.entry.sessionId === "string" && /^[a-z0-9][a-z0-9._-]{0,127}$/i.test(params.entry.sessionId)) {
538
+ candidates.add(resolveSessionFilePath(params.entry.sessionId, typeof params.entry.sessionFile === "string" ? { sessionFile: params.entry.sessionFile } : void 0, {
539
+ agentId: params.agentId,
540
+ sessionsDir
541
+ }));
542
+ return [...candidates].toSorted();
543
+ }
544
+ if (typeof params.entry.sessionFile === "string" && params.entry.sessionFile.trim()) addSafeCandidate(params.entry.sessionFile.trim());
545
+ return [...candidates].toSorted();
546
+ }
547
+ function isSessionHeader(value) {
548
+ return isRecord$1(value) && value.type === "session" && typeof value.id === "string";
549
+ }
550
+ function isBlankUserMessage(value) {
551
+ if (!isRecord$1(value) || value.type !== "message" || !isRecord$1(value.message)) return false;
552
+ if (value.message.role !== "user") return false;
553
+ const content = value.message.content;
554
+ if (typeof content === "string") return content.trim().length === 0;
555
+ return Array.isArray(content) && content.length === 0;
556
+ }
557
+ function isUserMessage(value) {
558
+ return isRecord$1(value) && value.type === "message" && isRecord$1(value.message) && value.message.role === "user";
559
+ }
560
+ function inspectSessionTranscript(params) {
561
+ let stat;
562
+ try {
563
+ stat = fs.statSync(params.transcriptPath);
564
+ } catch {
565
+ return null;
566
+ }
567
+ if (!stat.isFile()) return {
568
+ kind: "invalid-session-transcript",
569
+ sessionKey: params.sessionKey,
570
+ storePath: params.storePath,
571
+ path: params.transcriptPath,
572
+ reason: "not a file"
573
+ };
574
+ if (stat.size > SESSION_FILE_INSPECTION_MAX_BYTES) return null;
575
+ let raw = "";
576
+ try {
577
+ raw = fs.readFileSync(params.transcriptPath, "utf-8");
578
+ } catch {
579
+ return {
580
+ kind: "invalid-session-transcript",
581
+ sessionKey: params.sessionKey,
582
+ storePath: params.storePath,
583
+ path: params.transcriptPath,
584
+ reason: "unreadable"
585
+ };
586
+ }
587
+ const entries = [];
588
+ let malformedLines = 0;
589
+ let blankUserMessageRun = 0;
590
+ let maxBlankUserMessageRun = 0;
591
+ for (const line of raw.split(/\r?\n/)) {
592
+ if (!line.trim()) continue;
593
+ try {
594
+ const entry = JSON.parse(line);
595
+ entries.push(entry);
596
+ if (isBlankUserMessage(entry)) {
597
+ blankUserMessageRun += 1;
598
+ maxBlankUserMessageRun = Math.max(maxBlankUserMessageRun, blankUserMessageRun);
599
+ } else if (isUserMessage(entry)) blankUserMessageRun = 0;
600
+ } catch {
601
+ malformedLines += 1;
602
+ }
603
+ }
604
+ if (entries.length === 0) return {
605
+ kind: "invalid-session-transcript",
606
+ sessionKey: params.sessionKey,
607
+ storePath: params.storePath,
608
+ path: params.transcriptPath,
609
+ reason: "empty transcript"
610
+ };
611
+ if (!isSessionHeader(entries[0])) return {
612
+ kind: "invalid-session-transcript",
613
+ sessionKey: params.sessionKey,
614
+ storePath: params.storePath,
615
+ path: params.transcriptPath,
616
+ reason: "invalid session header"
617
+ };
618
+ if (malformedLines > 0) return {
619
+ kind: "invalid-session-transcript",
620
+ sessionKey: params.sessionKey,
621
+ storePath: params.storePath,
622
+ path: params.transcriptPath,
623
+ reason: `${malformedLines} malformed JSONL line(s)`
624
+ };
625
+ if (maxBlankUserMessageRun >= BLANK_USER_MESSAGE_REPAIR_THRESHOLD) return {
626
+ kind: "blank-user-message-run",
627
+ sessionKey: params.sessionKey,
628
+ storePath: params.storePath,
629
+ path: params.transcriptPath,
630
+ count: maxBlankUserMessageRun
631
+ };
632
+ return null;
633
+ }
634
+ function collectFeishuSessionFindings(params) {
635
+ const transcriptCandidates = resolveSessionTranscriptCandidates(params);
636
+ const existing = transcriptCandidates.filter(existsFile);
637
+ if (transcriptCandidates.length > 0 && existing.length === 0) return [{
638
+ kind: "missing-session-transcript",
639
+ sessionKey: params.sessionKey,
640
+ storePath: params.storePath
641
+ }];
642
+ const findings = [];
643
+ for (const transcriptPath of existing) {
644
+ const finding = inspectSessionTranscript({
645
+ sessionKey: params.sessionKey,
646
+ storePath: params.storePath,
647
+ transcriptPath
648
+ });
649
+ if (finding) findings.push(finding);
650
+ }
651
+ return findings;
652
+ }
653
+ function hasCorruptFeishuStateJsonFinding(inspection) {
654
+ return inspection.findings.some((finding) => finding.kind === "corrupt-state-json");
655
+ }
656
+ function sessionEntryId(storePath, key) {
657
+ return `${path.resolve(storePath)}\0${key}`;
658
+ }
659
+ function collectRepairSessionEntries(inspection) {
660
+ const entriesById = /* @__PURE__ */ new Map();
661
+ for (const entry of inspection.sessionEntries) entriesById.set(sessionEntryId(entry.storePath, entry.key), entry);
662
+ const repairEntries = [];
663
+ const seen = /* @__PURE__ */ new Set();
664
+ for (const finding of inspection.findings) {
665
+ if (finding.kind === "corrupt-state-json") continue;
666
+ const id = sessionEntryId(finding.storePath, finding.sessionKey);
667
+ if (seen.has(id)) continue;
668
+ const entry = entriesById.get(id);
669
+ if (entry) {
670
+ repairEntries.push(entry);
671
+ seen.add(id);
672
+ }
673
+ }
674
+ return repairEntries.toSorted((left, right) => left.storePath.localeCompare(right.storePath) || left.key.localeCompare(right.key));
675
+ }
676
+ function inspectFeishuDoctorState(params) {
677
+ const env = params.env ?? process.env;
678
+ const stateDir = resolveStateDir(env, os.homedir);
679
+ const feishuStateDir = path.join(stateDir, FEISHU_STATE_DIR);
680
+ const findings = collectCorruptFeishuStateJsonFindings(feishuStateDir);
681
+ const sessionEntries = [];
682
+ for (const target of collectFeishuSessionTargets({
683
+ cfg: params.cfg,
684
+ env,
685
+ stateDir
686
+ })) {
687
+ const store = loadSessionStore(target.storePath, { skipCache: true });
688
+ for (const [key, entry] of Object.entries(store).toSorted(([left], [right]) => left.localeCompare(right))) {
689
+ if (!isFeishuSessionEntry(key, entry)) continue;
690
+ const sessionEntry = toFeishuSessionEntry(entry);
691
+ sessionEntries.push({
692
+ key,
693
+ storePath: target.storePath,
694
+ agentId: target.agentId,
695
+ entry: sessionEntry
696
+ });
697
+ findings.push(...collectFeishuSessionFindings({
698
+ sessionKey: key,
699
+ storePath: target.storePath,
700
+ agentId: target.agentId,
701
+ entry: sessionEntry
702
+ }));
703
+ }
704
+ }
705
+ return {
706
+ stateDir,
707
+ feishuStateDir,
708
+ findings,
709
+ sessionEntries
710
+ };
711
+ }
712
+ function ensureBackupDir(stateDir, now) {
713
+ const backupDir = path.join(stateDir, "backups", `${BACKUP_PREFIX}-${timestampForPath(now)}`);
714
+ fs.mkdirSync(backupDir, {
715
+ recursive: true,
716
+ mode: 448
717
+ });
718
+ return backupDir;
719
+ }
720
+ function resolveUniquePath(candidate) {
721
+ if (!fs.existsSync(candidate)) return candidate;
722
+ for (let index = 1; index < 1e3; index += 1) {
723
+ const next = `${candidate}.${index}`;
724
+ if (!fs.existsSync(next)) return next;
725
+ }
726
+ throw new Error(`Unable to resolve unique path for ${candidate}`);
727
+ }
728
+ function movePathToBackup(params) {
729
+ if (!fs.existsSync(params.sourcePath)) return false;
730
+ const targetPath = resolveUniquePath(path.join(params.backupDir, params.relativeTarget));
731
+ fs.mkdirSync(path.dirname(targetPath), {
732
+ recursive: true,
733
+ mode: 448
734
+ });
735
+ fs.renameSync(params.sourcePath, targetPath);
736
+ return true;
737
+ }
738
+ function copyStoreBackup(params) {
739
+ if (!existsFile(params.storePath)) return;
740
+ const targetPath = path.join(params.backupDir, "session-stores", params.agentId, path.basename(params.storePath));
741
+ fs.mkdirSync(path.dirname(targetPath), {
742
+ recursive: true,
743
+ mode: 448
744
+ });
745
+ fs.copyFileSync(params.storePath, resolveUniquePath(targetPath));
746
+ }
747
+ function collectSessionArtifactPaths(params) {
748
+ const artifacts = /* @__PURE__ */ new Set();
749
+ for (const transcriptPath of resolveSessionTranscriptCandidates(params)) {
750
+ artifacts.add(transcriptPath);
751
+ if (transcriptPath.endsWith(".jsonl")) {
752
+ const base = transcriptPath.slice(0, -6);
753
+ artifacts.add(`${base}.trajectory.jsonl`);
754
+ artifacts.add(`${base}.trajectory-path.json`);
755
+ }
756
+ }
757
+ return [...artifacts].toSorted();
758
+ }
759
+ function archiveSessionArtifacts(params) {
760
+ const seen = /* @__PURE__ */ new Set();
761
+ let archived = 0;
762
+ for (const entry of params.entries) for (const artifactPath of collectSessionArtifactPaths({
763
+ storePath: params.storePath,
764
+ agentId: entry.agentId,
765
+ entry: entry.entry
766
+ })) {
767
+ if (seen.has(artifactPath) || !existsFile(artifactPath)) continue;
768
+ seen.add(artifactPath);
769
+ const archivedPath = resolveUniquePath(`${artifactPath}.deleted.${params.archiveTimestamp}`);
770
+ fs.renameSync(artifactPath, archivedPath);
771
+ archived += 1;
772
+ }
773
+ return archived;
774
+ }
775
+ async function repairFeishuDoctorState(params) {
776
+ const env = params.env ?? process.env;
777
+ const now = params.now ?? /* @__PURE__ */ new Date();
778
+ const inspection = params.inspection ?? inspectFeishuDoctorState({
779
+ cfg: params.cfg,
780
+ env
781
+ });
782
+ const backupDir = ensureBackupDir(inspection.stateDir, now);
783
+ const archiveTimestamp = timestampForPath(now);
784
+ const warnings = [];
785
+ const stateDirRepairAttempted = hasCorruptFeishuStateJsonFinding(inspection);
786
+ let rebuiltStateDir = false;
787
+ if (stateDirRepairAttempted) try {
788
+ rebuiltStateDir = movePathToBackup({
789
+ sourcePath: inspection.feishuStateDir,
790
+ backupDir,
791
+ relativeTarget: FEISHU_STATE_DIR
792
+ });
793
+ fs.mkdirSync(inspection.feishuStateDir, {
794
+ recursive: true,
795
+ mode: 448
796
+ });
797
+ } catch (error) {
798
+ warnings.push(`- Failed to rebuild Feishu local state: ${String(error)}`);
799
+ }
800
+ const entriesByStore = /* @__PURE__ */ new Map();
801
+ for (const entry of collectRepairSessionEntries(inspection)) {
802
+ const existing = entriesByStore.get(entry.storePath);
803
+ if (existing) existing.entries.push({
804
+ key: entry.key,
805
+ entry: entry.entry
806
+ });
807
+ else entriesByStore.set(entry.storePath, {
808
+ agentId: entry.agentId,
809
+ entries: [{
810
+ key: entry.key,
811
+ entry: entry.entry
812
+ }]
813
+ });
814
+ }
815
+ let removedSessionEntries = 0;
816
+ let touchedSessionStores = 0;
817
+ let archivedSessionArtifacts = 0;
818
+ for (const [storePath, group] of [...entriesByStore.entries()].toSorted(([left], [right]) => left.localeCompare(right))) try {
819
+ copyStoreBackup({
820
+ storePath,
821
+ backupDir,
822
+ agentId: group.agentId
823
+ });
824
+ const keys = new Set(group.entries.map((entry) => entry.key));
825
+ const removedEntries = await updateSessionStore(storePath, (store) => {
826
+ const removed = [];
827
+ for (const key of keys) if (Object.prototype.hasOwnProperty.call(store, key)) {
828
+ delete store[key];
829
+ const entry = group.entries.find((candidate) => candidate.key === key);
830
+ if (entry) removed.push(entry);
831
+ }
832
+ return removed;
833
+ }, {
834
+ skipMaintenance: true,
835
+ allowDropAcpMetaSessionKeys: [...keys]
836
+ });
837
+ const removed = removedEntries.length;
838
+ removedSessionEntries += removed;
839
+ if (removed > 0) {
840
+ touchedSessionStores += 1;
841
+ archivedSessionArtifacts += archiveSessionArtifacts({
842
+ storePath,
843
+ entries: removedEntries.map((entry) => ({
844
+ agentId: group.agentId,
845
+ entry: entry.entry
846
+ })),
847
+ archiveTimestamp
848
+ });
849
+ }
850
+ } catch (error) {
851
+ warnings.push(`- Failed to archive Feishu sessions in ${formatDisplayPath(storePath)}: ${String(error)}`);
852
+ }
853
+ return {
854
+ backupDir,
855
+ stateDirRepairAttempted,
856
+ rebuiltStateDir,
857
+ removedSessionEntries,
858
+ touchedSessionStores,
859
+ archivedSessionArtifacts,
860
+ warnings
861
+ };
862
+ }
863
+ function formatPreviewWarning(inspection) {
864
+ const previewFindings = inspection.findings.slice(0, 5).map(formatFinding);
865
+ const remaining = inspection.findings.length - previewFindings.length;
866
+ const repairActions = [];
867
+ if (hasCorruptFeishuStateJsonFinding(inspection)) repairActions.push(`archive ${formatDisplayPath(inspection.feishuStateDir)}`);
868
+ const repairSessionEntries = collectRepairSessionEntries(inspection);
869
+ if (repairSessionEntries.length > 0) repairActions.push(`archive artifacts and remove ${countLabel(repairSessionEntries.length, "flagged Feishu-scoped session entry", "flagged Feishu-scoped session entries")}`);
870
+ const repairSummary = repairActions.length > 0 ? repairActions.join(" and ") : "apply targeted Feishu state cleanup";
871
+ return [
872
+ "- Feishu local channel state may need repair.",
873
+ ...previewFindings,
874
+ ...remaining > 0 ? [`- ...and ${remaining} more Feishu state finding(s).`] : [],
875
+ `- Repair will ${repairSummary}, while preserving Feishu App ID/secret config and healthy session entries.`,
876
+ "- Run \"openclaw doctor --fix\" to rebuild Feishu local state."
877
+ ].join("\n");
878
+ }
879
+ function formatRepairChange(report) {
880
+ const stateRepairStatus = report.stateDirRepairAttempted ? report.rebuiltStateDir ? "yes" : "no existing state" : "not needed";
881
+ return [
882
+ "Feishu local state repaired.",
883
+ `- Backup dir: ${formatDisplayPath(report.backupDir)}`,
884
+ `- Rebuilt Feishu runtime state: ${stateRepairStatus}`,
885
+ `- Removed ${countLabel(report.removedSessionEntries, "Feishu-scoped session entry", "Feishu-scoped session entries")} from ${countLabel(report.touchedSessionStores, "session store")}.`,
886
+ `- Archived ${countLabel(report.archivedSessionArtifacts, "session artifact file")}.`,
887
+ "- Preserved Feishu App ID/secret config."
888
+ ].join("\n");
889
+ }
890
+ function hasConfiguredFeishuChannel(cfg) {
891
+ return Boolean(cfg.channels?.feishu);
892
+ }
893
+ async function runFeishuDoctorSequence(params) {
894
+ if (!hasConfiguredFeishuChannel(params.cfg)) return {
895
+ changeNotes: [],
896
+ warningNotes: []
897
+ };
898
+ const inspection = inspectFeishuDoctorState({
899
+ cfg: params.cfg,
900
+ env: params.env
901
+ });
902
+ if (inspection.findings.length === 0) return {
903
+ changeNotes: [],
904
+ warningNotes: []
905
+ };
906
+ if (!params.shouldRepair) return {
907
+ changeNotes: [],
908
+ warningNotes: [formatPreviewWarning(inspection)]
909
+ };
910
+ const report = await repairFeishuDoctorState({
911
+ cfg: params.cfg,
912
+ env: params.env,
913
+ inspection
914
+ });
915
+ return {
916
+ changeNotes: [formatRepairChange(report)],
917
+ warningNotes: report.warnings
918
+ };
919
+ }
920
+ const feishuDoctor = { runConfigSequence: async ({ cfg, env, shouldRepair }) => await runFeishuDoctorSequence({
921
+ cfg,
922
+ env,
923
+ shouldRepair
924
+ }) };
925
+ //#endregion
463
926
  //#region extensions/feishu/src/policy.ts
464
927
  const FEISHU_PROVIDER_PREFIX_RE = /^(feishu|lark):/i;
465
928
  const FEISHU_TYPED_PREFIX_RE = /^(chat|group|channel|user|dm|open_id):/i;
@@ -519,7 +982,7 @@ function createFeishuIngressSubject(params) {
519
982
  function createFeishuIngressResolver(params) {
520
983
  return createChannelIngressResolver({
521
984
  channelId: "feishu",
522
- accountId: normalizeAccountId(params.accountId) ?? "default",
985
+ accountId: normalizeAccountId$1(params.accountId) ?? "default",
523
986
  identity: feishuIngressIdentity,
524
987
  cfg: params.cfg,
525
988
  ...params.readAllowFromStore ? { readStoreAllowFrom: params.readAllowFromStore } : {}
@@ -625,8 +1088,8 @@ function resolveFeishuReplyPolicy(params) {
625
1088
  const resolvedCfg = resolveMergedAccountConfig({
626
1089
  channelConfig: feishuCfg,
627
1090
  accounts: feishuCfg?.accounts,
628
- accountId: normalizeAccountId(params.accountId),
629
- normalizeAccountId,
1091
+ accountId: normalizeAccountId$1(params.accountId),
1092
+ normalizeAccountId: normalizeAccountId$1,
630
1093
  omitKeys: ["defaultAccount"]
631
1094
  });
632
1095
  const groupRequireMention = resolveFeishuGroupConfig({
@@ -636,46 +1099,120 @@ function resolveFeishuReplyPolicy(params) {
636
1099
  return { requireMention: typeof groupRequireMention === "boolean" ? groupRequireMention : typeof resolvedCfg.requireMention === "boolean" ? resolvedCfg.requireMention : params.groupPolicy !== "open" };
637
1100
  }
638
1101
  //#endregion
639
- //#region extensions/feishu/src/send-result.ts
640
- function resolveFeishuReceiptKind(msgType) {
641
- switch (msgType) {
642
- case "audio": return "voice";
643
- case "image":
644
- case "media":
645
- case "file": return "media";
646
- case "interactive": return "card";
647
- case "post":
648
- case "text": return "text";
649
- default: return "unknown";
1102
+ //#region extensions/feishu/src/presentation-card.ts
1103
+ function escapeFeishuCardMarkdownText(text) {
1104
+ return text.replace(/[&<>]/g, (char) => {
1105
+ switch (char) {
1106
+ case "&": return "&amp;";
1107
+ case "<": return "&lt;";
1108
+ case ">": return "&gt;";
1109
+ default: return char;
1110
+ }
1111
+ });
1112
+ }
1113
+ function resolveSafeFeishuButtonUrl(url) {
1114
+ const trimmed = url?.trim();
1115
+ if (!trimmed) return;
1116
+ try {
1117
+ const parsed = new URL(trimmed);
1118
+ return parsed.protocol === "https:" || parsed.protocol === "http:" ? trimmed : void 0;
1119
+ } catch {
1120
+ return;
650
1121
  }
651
1122
  }
652
- function createFeishuSendReceipt(params) {
653
- const messageId = params.messageId?.trim();
654
- const chatId = params.chatId.trim();
655
- return createMessageReceiptFromOutboundResults({
656
- results: messageId ? [{
657
- channel: "feishu",
658
- messageId,
659
- chatId,
660
- conversationId: chatId
661
- }] : [],
662
- ...chatId ? { threadId: chatId } : {},
663
- kind: params.kind ?? "unknown"
1123
+ function resolveFeishuButtonUrl(button) {
1124
+ return button.url ?? button.webApp?.url ?? button.web_app?.url;
1125
+ }
1126
+ function mapFeishuButtonType(style) {
1127
+ if (style === "primary" || style === "success") return "primary";
1128
+ if (style === "danger") return "danger";
1129
+ return "default";
1130
+ }
1131
+ function buildFeishuPayloadButton(button) {
1132
+ const behaviors = [];
1133
+ const rendered = {
1134
+ tag: "button",
1135
+ text: {
1136
+ tag: "plain_text",
1137
+ content: button.label
1138
+ },
1139
+ type: mapFeishuButtonType(button.style)
1140
+ };
1141
+ const url = resolveFeishuButtonUrl(button);
1142
+ if (url) {
1143
+ const safeUrl = resolveSafeFeishuButtonUrl(url);
1144
+ if (safeUrl) behaviors.push({
1145
+ type: "open_url",
1146
+ default_url: safeUrl
1147
+ });
1148
+ }
1149
+ if (button.value) behaviors.push({
1150
+ type: "callback",
1151
+ value: createFeishuCardInteractionEnvelope({
1152
+ k: "quick",
1153
+ a: "feishu.payload.button",
1154
+ q: button.value
1155
+ })
664
1156
  });
1157
+ if (behaviors.length === 0) return;
1158
+ rendered.behaviors = behaviors;
1159
+ return rendered;
1160
+ }
1161
+ function buildFeishuCardElementsForBlock(block) {
1162
+ if (block.type === "text") return [{
1163
+ tag: "markdown",
1164
+ content: escapeFeishuCardMarkdownText(block.text)
1165
+ }];
1166
+ if (block.type === "context") return [{
1167
+ tag: "markdown",
1168
+ content: `<font color='grey'>${escapeFeishuCardMarkdownText(block.text)}</font>`
1169
+ }];
1170
+ if (block.type === "divider") return [{ tag: "hr" }];
1171
+ if (block.type === "buttons") return block.buttons.map((button) => buildFeishuPayloadButton(button)).filter((button) => Boolean(button));
1172
+ const labels = block.options.map((option) => `- ${option.label}`).join("\n");
1173
+ return [{
1174
+ tag: "markdown",
1175
+ content: `${escapeFeishuCardMarkdownText(block.placeholder?.trim() || "Options")}:\n${escapeFeishuCardMarkdownText(labels)}`
1176
+ }];
1177
+ }
1178
+ function resolvePresentationHeaderTemplate(tone) {
1179
+ if (tone === "danger") return "red";
1180
+ if (tone === "warning") return "orange";
1181
+ if (tone === "success") return "green";
1182
+ return "blue";
1183
+ }
1184
+ function buildFeishuPresentationCardElements(params) {
1185
+ const elements = [];
1186
+ const fallbackText = params.fallbackText?.trim();
1187
+ if (fallbackText) elements.push({
1188
+ tag: "markdown",
1189
+ content: escapeFeishuCardMarkdownText(fallbackText)
1190
+ });
1191
+ for (const block of params.presentation.blocks) for (const element of buildFeishuCardElementsForBlock(block)) elements.push(element);
1192
+ if (elements.length > 0) return elements;
1193
+ return [{
1194
+ tag: "markdown",
1195
+ content: renderMessagePresentationFallbackText({
1196
+ text: params.fallbackText,
1197
+ presentation: params.presentation.title ? {
1198
+ ...params.presentation.tone ? { tone: params.presentation.tone } : {},
1199
+ blocks: params.presentation.blocks
1200
+ } : params.presentation
1201
+ })
1202
+ }];
665
1203
  }
666
- function assertFeishuMessageApiSuccess(response, errorPrefix) {
667
- if (response.code !== 0) throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
668
- }
669
- function toFeishuSendResult(response, chatId, kind) {
670
- const messageId = response.data?.message_id ?? "unknown";
1204
+ function buildFeishuPresentationCard(params) {
671
1205
  return {
672
- messageId,
673
- chatId,
674
- receipt: createFeishuSendReceipt({
675
- messageId,
676
- chatId,
677
- kind
678
- })
1206
+ schema: "2.0",
1207
+ config: { width_mode: "fill" },
1208
+ ...params.presentation.title ? { header: {
1209
+ title: {
1210
+ tag: "plain_text",
1211
+ content: params.presentation.title
1212
+ },
1213
+ template: resolvePresentationHeaderTemplate(params.presentation.tone)
1214
+ } } : {},
1215
+ body: { elements: buildFeishuPresentationCardElements(params) }
679
1216
  };
680
1217
  }
681
1218
  //#endregion
@@ -756,19 +1293,15 @@ const t = createSetupTranslator();
756
1293
  const channel = "feishu";
757
1294
  const SCAN_TO_CREATE_TP = "ob_cli_app";
758
1295
  const FEISHU_SETUP_FLOW_KEY = "_flow";
759
- function normalizeString(value) {
760
- if (typeof value !== "string") return;
761
- return value.trim() || void 0;
762
- }
763
1296
  function isFeishuConfigured(cfg) {
764
1297
  const feishuCfg = cfg.channels?.feishu;
765
1298
  const isAppIdConfigured = (value) => {
766
- if (normalizeString(value)) return true;
1299
+ if (normalizeOptionalString(value)) return true;
767
1300
  if (!value || typeof value !== "object") return false;
768
1301
  const rec = value;
769
- const source = normalizeString(rec.source)?.toLowerCase();
770
- const id = normalizeString(rec.id);
771
- if (source === "env" && id) return Boolean(normalizeString(process.env[id]));
1302
+ const source = normalizeOptionalString(rec.source)?.toLowerCase();
1303
+ const id = normalizeOptionalString(rec.id);
1304
+ if (source === "env" && id) return Boolean(normalizeOptionalString(process.env[id]));
772
1305
  return hasConfiguredSecretInput$1(value);
773
1306
  };
774
1307
  const topLevelConfigured = isAppIdConfigured(feishuCfg?.appId) && hasConfiguredSecretInput$1(feishuCfg?.appSecret);
@@ -984,7 +1517,7 @@ async function runNewAppFlow(params) {
984
1517
  await noteFeishuCredentialHelp(prompter);
985
1518
  appId = await promptFeishuAppId({
986
1519
  prompter,
987
- initialValue: normalizeString(process.env.FEISHU_APP_ID)
1520
+ initialValue: normalizeOptionalString(process.env.FEISHU_APP_ID)
988
1521
  });
989
1522
  const appSecretResult = await promptSingleChannelSecretInput({
990
1523
  cfg: next,
@@ -1049,11 +1582,11 @@ async function runEditFlow(params) {
1049
1582
  const next = params.cfg;
1050
1583
  const feishuCfg = next.channels?.feishu;
1051
1584
  const resolveAppIdLabel = (value) => {
1052
- const asString = normalizeString(value);
1585
+ const asString = normalizeOptionalString(value);
1053
1586
  if (asString) return asString;
1054
1587
  if (value && typeof value === "object") {
1055
1588
  const rec = value;
1056
- if (normalizeString(rec.source) && normalizeString(rec.id)) return normalizeString(process.env[rec.id]) ?? `env:${String(rec.id)}`;
1589
+ if (normalizeOptionalString(rec.source) && normalizeOptionalString(rec.id)) return normalizeOptionalString(process.env[rec.id]) ?? `env:${String(rec.id)}`;
1057
1590
  if (hasConfiguredSecretInput$1(value)) return "(configured)";
1058
1591
  }
1059
1592
  };
@@ -1171,12 +1704,13 @@ function readBooleanParam(params, keys) {
1171
1704
  }
1172
1705
  }
1173
1706
  function hasLegacyFeishuCardCommandValue(actionValue) {
1174
- return isRecord$1(actionValue) && actionValue.oc !== "ocf1" && (Boolean(typeof actionValue.command === "string" && actionValue.command.trim()) || Boolean(typeof actionValue.text === "string" && actionValue.text.trim()));
1707
+ return isRecord$2(actionValue) && actionValue.oc !== "ocf1" && (Boolean(typeof actionValue.command === "string" && actionValue.command.trim()) || Boolean(typeof actionValue.text === "string" && actionValue.text.trim()));
1175
1708
  }
1176
1709
  function containsLegacyFeishuCardCommandValue(node) {
1177
1710
  if (Array.isArray(node)) return node.some((item) => containsLegacyFeishuCardCommandValue(item));
1178
- if (!isRecord$1(node)) return false;
1711
+ if (!isRecord$2(node)) return false;
1179
1712
  if (node.tag === "button" && hasLegacyFeishuCardCommandValue(node.value)) return true;
1713
+ if (node.tag === "button" && Array.isArray(node.behaviors) && node.behaviors.some((behavior) => isRecord$2(behavior) && hasLegacyFeishuCardCommandValue(behavior.value))) return true;
1180
1714
  return Object.values(node).some((value) => containsLegacyFeishuCardCommandValue(value));
1181
1715
  }
1182
1716
  const meta = {
@@ -1189,7 +1723,7 @@ const meta = {
1189
1723
  aliases: ["lark"],
1190
1724
  order: 70
1191
1725
  };
1192
- const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-C8KEo0lg.js"), "feishuChannelRuntime");
1726
+ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-ItBg9SfS.js"), "feishuChannelRuntime");
1193
1727
  function toFeishuMessageSendResult(result, kind) {
1194
1728
  const receipt = result.receipt ?? createFeishuSendReceipt({
1195
1729
  messageId: result.messageId,
@@ -1220,30 +1754,6 @@ const feishuMessageAdapter = defineChannelMessageAdapter({
1220
1754
  }
1221
1755
  }
1222
1756
  });
1223
- function buildFeishuPresentationCard(params) {
1224
- const fallbackPresentation = {
1225
- ...params.presentation.tone ? { tone: params.presentation.tone } : {},
1226
- blocks: params.presentation.blocks
1227
- };
1228
- return {
1229
- schema: "2.0",
1230
- config: { width_mode: "fill" },
1231
- ...params.presentation.title ? { header: {
1232
- title: {
1233
- tag: "plain_text",
1234
- content: params.presentation.title
1235
- },
1236
- template: "blue"
1237
- } } : {},
1238
- body: { elements: [{
1239
- tag: "markdown",
1240
- content: renderMessagePresentationFallbackText({
1241
- text: params.fallbackText,
1242
- presentation: fallbackPresentation
1243
- })
1244
- }] }
1245
- };
1246
- }
1247
1757
  async function createFeishuActionClient(account) {
1248
1758
  const { createFeishuClient } = await import("./client-BnH2fRL2.js").then((n) => n.t);
1249
1759
  return createFeishuClient(account);
@@ -1544,6 +2054,7 @@ const feishuPlugin = createChatChannelPlugin({
1544
2054
  },
1545
2055
  mentions: { stripPatterns: () => ["<at user_id=\"[^\"]*\">[^<]*</at>"] },
1546
2056
  reload: { configPrefixes: ["channels.feishu"] },
2057
+ doctor: feishuDoctor,
1547
2058
  configSchema: buildChannelConfigSchema(FeishuConfigSchema),
1548
2059
  config: {
1549
2060
  ...feishuConfigAdapter,
@@ -2032,7 +2543,7 @@ const feishuPlugin = createChatChannelPlugin({
2032
2543
  })
2033
2544
  }),
2034
2545
  gateway: { startAccount: async (ctx) => {
2035
- const { monitorFeishuProvider } = await import("./monitor-CPSt9A7K.js");
2546
+ const { monitorFeishuProvider } = await import("./monitor-gFxvkDyO.js");
2036
2547
  const account = resolveFeishuRuntimeAccount({
2037
2548
  cfg: ctx.cfg,
2038
2549
  accountId: ctx.accountId
@@ -2112,4 +2623,4 @@ const feishuPlugin = createChatChannelPlugin({
2112
2623
  }
2113
2624
  });
2114
2625
  //#endregion
2115
- export { listFeishuDirectoryPeers as _, setFeishuNamedAccountEnabled$1 as a, decodeFeishuCardAction as b, toFeishuSendResult as c, resolveFeishuDmIngressAccess as d, resolveFeishuGroupConfig as f, listFeishuDirectoryGroups as g, resolveFeishuReplyPolicy as h, feishuSetupAdapter as i, hasExplicitFeishuGroupConfig as l, resolveFeishuGroupSenderActivationIngressAccess as m, feishuSetupWizard as n, assertFeishuMessageApiSuccess as o, resolveFeishuGroupConversationIngressAccess as p, runFeishuLogin as r, resolveFeishuReceiptKind as s, feishuPlugin as t, normalizeFeishuAllowEntry as u, buildFeishuCardActionTextFallback as v, createFeishuCardInteractionEnvelope as y };
2626
+ export { setFeishuNamedAccountEnabled$1 as a, normalizeFeishuAllowEntry as c, resolveFeishuGroupConversationIngressAccess as d, resolveFeishuGroupSenderActivationIngressAccess as f, listFeishuDirectoryPeers as h, feishuSetupAdapter as i, resolveFeishuDmIngressAccess as l, listFeishuDirectoryGroups as m, feishuSetupWizard as n, buildFeishuPresentationCardElements as o, resolveFeishuReplyPolicy as p, runFeishuLogin as r, hasExplicitFeishuGroupConfig as s, feishuPlugin as t, resolveFeishuGroupConfig as u };