@senguoyun/dsh-arkme 0.1.29 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/lib/client.js +3525 -477
  2. package/lib/client.js.map +1 -1
  3. package/lib/index.js +235 -7
  4. package/lib/persistent-extension.js +1 -1
  5. package/lib/{persistent-runtime-DLKV_LTp.js → persistent-runtime-D-NwFt9z.js} +867 -21
  6. package/lib/sdk.js +44 -0
  7. package/lib/types/arkme-service.d.ts +20 -1
  8. package/lib/types/arkme-service.d.ts.map +1 -1
  9. package/lib/types/client/ArkmeNoteDetails.d.ts +3 -1
  10. package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
  11. package/lib/types/client/ArkmePersistentShell.d.ts +3 -0
  12. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  13. package/lib/types/client/ArkmeProductNavigation.d.ts +2 -1
  14. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  15. package/lib/types/client/ArkmeRichContent.d.ts +15 -1
  16. package/lib/types/client/ArkmeRichContent.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeSidebar.d.ts +14 -0
  18. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeVirtualWorkspace.d.ts +9 -2
  20. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  21. package/lib/types/client/ArkmeWebLoginOverlay.d.ts +6 -0
  22. package/lib/types/client/ArkmeWebLoginOverlay.d.ts.map +1 -0
  23. package/lib/types/client/DeepSeekHarnessSurface.d.ts +7 -4
  24. package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
  25. package/lib/types/client/api.d.ts +1 -1
  26. package/lib/types/client/api.d.ts.map +1 -1
  27. package/lib/types/client/arkme-auth-flow.d.ts +4 -0
  28. package/lib/types/client/arkme-auth-flow.d.ts.map +1 -1
  29. package/lib/types/client/index.d.ts +1 -0
  30. package/lib/types/client/index.d.ts.map +1 -1
  31. package/lib/types/client/ui-controller.d.ts +4 -0
  32. package/lib/types/client/ui-controller.d.ts.map +1 -1
  33. package/lib/types/extensions/manager.d.ts +4 -0
  34. package/lib/types/extensions/manager.d.ts.map +1 -1
  35. package/lib/types/extensions/persistent-bundle.d.ts +14 -0
  36. package/lib/types/extensions/persistent-bundle.d.ts.map +1 -1
  37. package/lib/types/extensions/persistent-client-bundle.d.ts +1 -1
  38. package/lib/types/extensions/persistent-client-bundle.d.ts.map +1 -1
  39. package/lib/types/extensions/persistent-runtime.d.ts.map +1 -1
  40. package/lib/types/extensions/profile-installer.d.ts.map +1 -1
  41. package/lib/types/harness-embed-contract.d.ts +2 -0
  42. package/lib/types/harness-embed-contract.d.ts.map +1 -0
  43. package/lib/types/harness-embed-route.d.ts +32 -0
  44. package/lib/types/harness-embed-route.d.ts.map +1 -0
  45. package/lib/types/host-api.d.ts.map +1 -1
  46. package/lib/types/index.d.ts.map +1 -1
  47. package/lib/types/sdk/index.d.ts +17 -2
  48. package/lib/types/sdk/index.d.ts.map +1 -1
  49. package/lib/types/services/chat-service.d.ts +37 -4
  50. package/lib/types/services/chat-service.d.ts.map +1 -1
  51. package/lib/types/tools/business/conversation/copy-link-extend.d.ts +2 -0
  52. package/lib/types/tools/business/conversation/copy-link-extend.d.ts.map +1 -0
  53. package/lib/types/tools/business/index.d.ts.map +1 -1
  54. package/lib/types/tools/ports/conversations.d.ts +4 -1
  55. package/lib/types/tools/ports/conversations.d.ts.map +1 -1
  56. package/lib/types/tools/prompts/business.d.ts.map +1 -1
  57. package/lib/types/types.d.ts +82 -1
  58. package/lib/types/types.d.ts.map +1 -1
  59. package/package.json +5 -5
@@ -1,6 +1,6 @@
1
1
  import { chmodSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
2
  import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicKey, randomBytes, randomUUID, timingSafeEqual, verify } from "node:crypto";
3
- import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
3
+ import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { open } from "node:fs/promises";
6
6
  import { pinyin } from "pinyin-pro";
@@ -7077,6 +7077,18 @@ const ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES = 20 * 1024 * 1024;
7077
7077
  //#region src/services/chat-service.ts
7078
7078
  const OFFICIAL_AUTHOR_USER_ID = 11;
7079
7079
  const OFFICIAL_AUTHOR_FALLBACK_DISPLAY_NAME = "即我作者";
7080
+ const MAX_MESSAGE_COPY_LINK_ITEMS = 100;
7081
+ const LINK_METADATA_MAX_BYTES = 128 * 1024;
7082
+ const LINK_METADATA_MAX_REDIRECTS = 3;
7083
+ const RECORD_UID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
7084
+ const HTML_ENTITY_MAP = {
7085
+ amp: "&",
7086
+ lt: "<",
7087
+ gt: ">",
7088
+ quot: "\"",
7089
+ apos: "'",
7090
+ nbsp: " "
7091
+ };
7080
7092
  function numberValue$16(value) {
7081
7093
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
7082
7094
  }
@@ -7101,6 +7113,88 @@ function favoriteStickerPersistenceItem(item) {
7101
7113
  function listValue$16(value) {
7102
7114
  return Array.isArray(value) ? value : [];
7103
7115
  }
7116
+ function htmlTextValue(value) {
7117
+ return value.replace(/<script[\s\S]*?<\/script>/giu, " ").replace(/<style[\s\S]*?<\/style>/giu, " ").replace(/<[^>]+>/gu, " ").replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z]+);/giu, (_match, entity) => {
7118
+ const normalized = entity.toLowerCase();
7119
+ if (normalized.startsWith("#x")) {
7120
+ const code = Number.parseInt(normalized.slice(2), 16);
7121
+ return Number.isFinite(code) ? String.fromCodePoint(code) : " ";
7122
+ }
7123
+ if (normalized.startsWith("#")) {
7124
+ const code = Number.parseInt(normalized.slice(1), 10);
7125
+ return Number.isFinite(code) ? String.fromCodePoint(code) : " ";
7126
+ }
7127
+ return HTML_ENTITY_MAP[normalized] ?? " ";
7128
+ }).replace(/\s+/gu, " ").trim();
7129
+ }
7130
+ function firstHtmlMetaContent(html, key) {
7131
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
7132
+ const patterns = [new RegExp(`<meta\\b(?=[^>]*(?:property|name)=["']${escaped}["'])(?=[^>]*content=["']([^"']*)["'])[^>]*>`, "iu"), new RegExp(`<meta\\b(?=[^>]*content=["']([^"']*)["'])(?=[^>]*(?:property|name)=["']${escaped}["'])[^>]*>`, "iu")];
7133
+ for (const pattern of patterns) {
7134
+ const value = pattern.exec(html)?.[1];
7135
+ if (value !== void 0) return htmlTextValue(value);
7136
+ }
7137
+ return "";
7138
+ }
7139
+ function fallbackLinkTitle(url) {
7140
+ const host = url.hostname.replace(/^www\./iu, "");
7141
+ if (host === "github.com") {
7142
+ const parts = url.pathname.split("/").filter(Boolean);
7143
+ if (parts.length >= 4 && parts[2] === "pull") return `Pull Request #${parts[3]} · ${parts[0]}/${parts[1]}`;
7144
+ if (parts.length >= 2) return `${parts[0]}/${parts[1]}`;
7145
+ }
7146
+ if (host === "codeup.aliyun.com" || host.endsWith(".codeup.aliyun.com")) {
7147
+ const parts = url.pathname.split("/").filter(Boolean);
7148
+ const changeIndex = parts.indexOf("change");
7149
+ const repository = changeIndex >= 2 ? parts[changeIndex - 1] : parts.at(-1);
7150
+ const changeNo = changeIndex >= 0 ? parts[changeIndex + 1] : void 0;
7151
+ if (repository !== void 0 && changeNo !== void 0) return `${repository} · Change #${changeNo}`;
7152
+ }
7153
+ return "分享链接";
7154
+ }
7155
+ function ensureMetadataUrl(rawUrl) {
7156
+ let url;
7157
+ try {
7158
+ url = new URL(rawUrl.trim());
7159
+ } catch {
7160
+ throw new ArkmePluginError("link-metadata-url-invalid", "链接暂不可预览", false, 400);
7161
+ }
7162
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new ArkmePluginError("link-metadata-url-invalid", "链接暂不可预览", false, 400);
7163
+ if (url.username !== "" || url.password !== "") throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
7164
+ const hostname = url.hostname.toLowerCase();
7165
+ if (hostname === "localhost" || hostname.endsWith(".local")) throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
7166
+ if (/^(?:127|10|0|169\.254|192\.168)\./u.test(hostname) || /^172\.(?:1[6-9]|2\d|3[0-1])\./u.test(hostname)) throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
7167
+ if (hostname === "::1" || hostname.startsWith("fc") || hostname.startsWith("fd") || hostname.startsWith("fe80")) throw new ArkmePluginError("link-metadata-url-forbidden", "链接暂不可预览", false, 403);
7168
+ return url;
7169
+ }
7170
+ async function readResponseTextPrefix(response) {
7171
+ if (response.body === null) return await response.text();
7172
+ const reader = response.body.getReader();
7173
+ const chunks = [];
7174
+ let bytes = 0;
7175
+ try {
7176
+ while (bytes < LINK_METADATA_MAX_BYTES) {
7177
+ const next = await reader.read();
7178
+ if (next.done) break;
7179
+ chunks.push(next.value);
7180
+ bytes += next.value.byteLength;
7181
+ }
7182
+ } finally {
7183
+ reader.releaseLock();
7184
+ }
7185
+ return new TextDecoder("utf-8", { fatal: false }).decode(Uint8Array.from(chunks.flatMap((chunk) => [...chunk])));
7186
+ }
7187
+ function linkMetadataFromHtml(url, html) {
7188
+ const title = firstHtmlMetaContent(html, "og:title") || firstHtmlMetaContent(html, "twitter:title") || htmlTextValue(/<title\b[^>]*>([\s\S]*?)<\/title>/iu.exec(html)?.[1] ?? "") || fallbackLinkTitle(url);
7189
+ const description = firstHtmlMetaContent(html, "og:description") || firstHtmlMetaContent(html, "description");
7190
+ const siteName = firstHtmlMetaContent(html, "og:site_name") || url.hostname.replace(/^www\./iu, "");
7191
+ return {
7192
+ url: url.toString(),
7193
+ title,
7194
+ ...description === "" ? {} : { description },
7195
+ ...siteName === "" ? {} : { siteName }
7196
+ };
7197
+ }
7104
7198
  function chatMemberRole$1(value) {
7105
7199
  if (value === "owner" || value === 1) return "owner";
7106
7200
  if (value === "admin" || value === 2) return "admin";
@@ -7121,6 +7215,185 @@ function integerLikeValue$1(value) {
7121
7215
  }
7122
7216
  return 0;
7123
7217
  }
7218
+ function epochMillisValue(value) {
7219
+ const timestamp = integerLikeValue$1(value);
7220
+ return timestamp > 0 && timestamp < 1e11 ? timestamp * 1e3 : timestamp;
7221
+ }
7222
+ function messageCopyLinkMediaItemFromData(value) {
7223
+ const data = objectValue$1(value);
7224
+ return {
7225
+ fileKind: integerLikeValue$1(data.file_kind),
7226
+ fileName: stringValue$1(data.file_name),
7227
+ size: Math.max(0, integerLikeValue$1(data.size))
7228
+ };
7229
+ }
7230
+ function messageCopyLinkStructuredContentFromData(value) {
7231
+ const data = objectValue$1(value);
7232
+ if (Object.keys(data).length === 0) return void 0;
7233
+ return {
7234
+ structuredKind: integerLikeValue$1(data.structured_kind),
7235
+ durationMillis: Math.max(0, integerLikeValue$1(data.duration_millis))
7236
+ };
7237
+ }
7238
+ function messageCopyLinkSnapshotItemFromData(value) {
7239
+ const data = objectValue$1(value);
7240
+ const structuredContent = messageCopyLinkStructuredContentFromData(data.structured_content);
7241
+ const recordUid = stringValue$1(data.record_uid ?? data.recordUid ?? data.uid).trim();
7242
+ const item = {
7243
+ ...recordUid === "" ? {} : { recordUid },
7244
+ sourceKind: stringValue$1(data.source_kind),
7245
+ senderDisplayName: stringValue$1(data.sender_display_name),
7246
+ senderAvatarUrl: stringValue$1(data.sender_avatar_url),
7247
+ title: stringValue$1(data.title),
7248
+ textContent: stringValue$1(data.text_content),
7249
+ sendAtMillis: epochMillisValue(data.send_at),
7250
+ templateKind: integerLikeValue$1(data.template_kind),
7251
+ displayKind: integerLikeValue$1(data.display_kind),
7252
+ officialMark: integerLikeValue$1(data.official_mark),
7253
+ mediaItems: listValue$16(data.media_items).map(messageCopyLinkMediaItemFromData),
7254
+ ...structuredContent === void 0 ? {} : { structuredContent }
7255
+ };
7256
+ if (item.sendAtMillis <= 0 || item.senderDisplayName.trim() === "" && item.sourceKind !== "agent_message") throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
7257
+ if (item.title.trim() === "" && item.textContent.trim() === "" && item.mediaItems.length === 0 && item.structuredContent === void 0) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
7258
+ return item;
7259
+ }
7260
+ function messageCopyLinkPresentationNodesFromData(value, itemCount, depth = 1, seenItemIndexes = /* @__PURE__ */ new Set()) {
7261
+ const rawNodes = listValue$16(value);
7262
+ if (rawNodes.length === 0) return [];
7263
+ if (depth > 8) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
7264
+ return rawNodes.map((rawNode) => {
7265
+ const data = objectValue$1(rawNode);
7266
+ const kind = stringValue$1(data.kind);
7267
+ if (kind === "item") {
7268
+ const itemIndex = integerLikeValue$1(data.item_index);
7269
+ if (itemIndex < 0 || itemIndex >= itemCount || seenItemIndexes.has(itemIndex)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
7270
+ seenItemIndexes.add(itemIndex);
7271
+ return {
7272
+ kind: "item",
7273
+ itemIndex
7274
+ };
7275
+ }
7276
+ if (kind === "forward_bundle") return {
7277
+ kind: "forward_bundle",
7278
+ title: stringValue$1(data.title),
7279
+ commentText: stringValue$1(data.comment_text),
7280
+ createdAtMillis: integerLikeValue$1(data.created_at),
7281
+ senderDisplayName: stringValue$1(data.sender_display_name),
7282
+ children: messageCopyLinkPresentationNodesFromData(data.children, itemCount, depth + 1, seenItemIndexes)
7283
+ };
7284
+ throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
7285
+ });
7286
+ }
7287
+ function messageCopyLinkSourceAnchorFromData(value) {
7288
+ const data = objectValue$1(value);
7289
+ return {
7290
+ relationUid: stringValue$1(data.rel_uid),
7291
+ recordUid: stringValue$1(data.record_uid),
7292
+ recordOwnerUserId: integerLikeValue$1(data.record_owner_user_id),
7293
+ sequence: integerLikeValue$1(data.seq)
7294
+ };
7295
+ }
7296
+ function firstTextValue(source, keys) {
7297
+ for (const key of keys) {
7298
+ const value = stringValue$1(source[key]).trim();
7299
+ if (value !== "") return value;
7300
+ }
7301
+ return "";
7302
+ }
7303
+ function messageCopyLinkExtensionItemFromData(value) {
7304
+ const data = objectValue$1(value);
7305
+ const core = objectValue$1(data.record_core ?? data.recordCore);
7306
+ const recordUid = firstTextValue(data, [
7307
+ "record_uid",
7308
+ "recordUid",
7309
+ "uid"
7310
+ ]) || firstTextValue(core, [
7311
+ "record_uid",
7312
+ "recordUid",
7313
+ "uid"
7314
+ ]);
7315
+ const senderDisplayName = firstTextValue(data, [
7316
+ "sender_display_name",
7317
+ "senderDisplayName",
7318
+ "nickname",
7319
+ "nick_name",
7320
+ "nickName",
7321
+ "owner_name",
7322
+ "ownerName"
7323
+ ]) || firstTextValue(core, [
7324
+ "sender_display_name",
7325
+ "senderDisplayName",
7326
+ "nickname",
7327
+ "nick_name",
7328
+ "nickName",
7329
+ "owner_name",
7330
+ "ownerName"
7331
+ ]) || "Arkme用户";
7332
+ const sendAtMillis = epochMillisValue(data.send_at ?? data.sendAt ?? data.created_at ?? data.createdAt ?? data.published_at ?? data.publishedAt ?? core.send_at ?? core.sendAt ?? core.created_at ?? core.createdAt ?? core.published_at ?? core.publishedAt);
7333
+ const title = stringValue$1(data.title ?? core.title);
7334
+ const textContent = stringValue$1(data.text_content ?? data.textContent ?? data.content ?? core.text_content ?? core.textContent ?? core.content);
7335
+ const mediaItems = listValue$16(data.media_items ?? data.mediaItems ?? core.media_items ?? core.mediaItems).map(messageCopyLinkMediaItemFromData);
7336
+ const structuredContent = messageCopyLinkStructuredContentFromData(data.structured_content ?? data.structuredContent ?? core.structured_content ?? core.structuredContent);
7337
+ if (recordUid === "" || sendAtMillis <= 0 || title.trim() === "" && textContent.trim() === "" && mediaItems.length === 0 && structuredContent === void 0) return;
7338
+ return {
7339
+ recordUid,
7340
+ level: Math.max(2, integerLikeValue$1(data.level) || 2),
7341
+ sourceKind: stringValue$1(data.source_kind ?? data.sourceKind) || "record_extension",
7342
+ senderDisplayName,
7343
+ senderAvatarUrl: firstTextValue(data, [
7344
+ "sender_avatar_url",
7345
+ "senderAvatarUrl",
7346
+ "avatar_ref",
7347
+ "avatarRef",
7348
+ "avatar_url",
7349
+ "avatarUrl",
7350
+ "avatar"
7351
+ ]),
7352
+ title,
7353
+ textContent,
7354
+ sendAtMillis,
7355
+ templateKind: integerLikeValue$1(data.template_kind ?? data.templateKind ?? core.template_kind ?? core.templateKind) || 1,
7356
+ displayKind: integerLikeValue$1(data.display_kind ?? data.displayKind ?? core.display_kind ?? core.displayKind),
7357
+ officialMark: integerLikeValue$1(data.official_mark ?? data.officialMark ?? core.official_mark ?? core.officialMark),
7358
+ mediaItems,
7359
+ ...structuredContent === void 0 ? {} : { structuredContent }
7360
+ };
7361
+ }
7362
+ function messageCopyLinkRecordContextFromData(data) {
7363
+ const context = objectValue$1(data.record_context ?? data.recordContext);
7364
+ const extensions = listValue$16(context.extensions ?? context.extend_records ?? context.extendRecords ?? context.list ?? data.extensions ?? data.extend_records ?? data.extendRecords ?? data.list).map(messageCopyLinkExtensionItemFromData).filter((item) => item !== void 0);
7365
+ const extensionCount = Math.max(integerLikeValue$1(context.extension_count ?? context.extensionCount ?? context.total ?? data.extension_count ?? data.extensionCount ?? data.total), extensions.length);
7366
+ if (extensionCount <= 0 && extensions.length === 0) return void 0;
7367
+ extensions.sort((left, right) => right.sendAtMillis - left.sendAtMillis);
7368
+ return {
7369
+ extensionCount,
7370
+ extensions
7371
+ };
7372
+ }
7373
+ function mergeMessageCopyLinkRecordContexts(left, right) {
7374
+ if (left === void 0) return right;
7375
+ if (right === void 0) return left;
7376
+ const byUid = /* @__PURE__ */ new Map();
7377
+ for (const item of [...left.extensions, ...right.extensions]) byUid.set(item.recordUid, item);
7378
+ const extensions = [...byUid.values()].sort((a, b) => b.sendAtMillis - a.sendAtMillis);
7379
+ return {
7380
+ extensionCount: Math.max(left.extensionCount, right.extensionCount, extensions.length),
7381
+ extensions
7382
+ };
7383
+ }
7384
+ function messageCopyLinkWorldTags(text) {
7385
+ return [...text.matchAll(/#(\S+)/gu)].map((match) => match[1]?.trim() ?? "").filter((tag) => tag !== "");
7386
+ }
7387
+ function messageCopyLinkRecordUidCandidates(items, anchors) {
7388
+ const candidates = [];
7389
+ const push = (value) => {
7390
+ const normalized = value?.trim() ?? "";
7391
+ if (normalized !== "" && !candidates.includes(normalized)) candidates.push(normalized);
7392
+ };
7393
+ for (const item of items) push(item.recordUid);
7394
+ for (const anchor of anchors) push(anchor.recordUid);
7395
+ return candidates;
7396
+ }
7124
7397
  function optionalString$2(value) {
7125
7398
  const text = stringValue$1(value).trim();
7126
7399
  return text === "" ? void 0 : text;
@@ -7479,6 +7752,8 @@ var ChatService = class {
7479
7752
  }
7480
7753
  async openOfficialAuthorPrivateChat(options = {}) {
7481
7754
  const session = await this.runtime.requireSession();
7755
+ const existing = await this.findPrivateChatByPeerUserId(OFFICIAL_AUTHOR_USER_ID, session, options.signal);
7756
+ if (existing !== void 0) return { source: existing };
7482
7757
  const created = await this.runtime.authenticatedSubjectPost("/api/v1/private/create-chat-ref-asen", {
7483
7758
  subject_uid: `dsh_official_author_${randomUUID().replace(/-/g, "")}`,
7484
7759
  network: "dsh",
@@ -7500,6 +7775,25 @@ var ChatService = class {
7500
7775
  ...options.signal === void 0 ? {} : { signal: options.signal }
7501
7776
  });
7502
7777
  }
7778
+ /**
7779
+ * Contact-author is an entry point, not a second conversation type. Scan the
7780
+ * ordinary directory first so repeated clicks always reuse the existing chat.
7781
+ */
7782
+ async findPrivateChatByPeerUserId(peerUserId, session, signal) {
7783
+ let cursor;
7784
+ for (let pageIndex = 0; pageIndex < 10; pageIndex += 1) {
7785
+ const page = await this.source.listSources("root", {
7786
+ limit: 50,
7787
+ refresh: true,
7788
+ ...cursor === void 0 ? {} : { cursor },
7789
+ ...signal === void 0 ? {} : { signal }
7790
+ });
7791
+ const existing = page.items.find((source) => source.kind === "private_chat" && source.peerUserId === peerUserId);
7792
+ if (existing !== void 0) return existing;
7793
+ if (!page.hasMore || page.nextCursor === void 0) return void 0;
7794
+ cursor = page.nextCursor;
7795
+ }
7796
+ }
7503
7797
  async officialAuthorProfile(options = {}) {
7504
7798
  const session = await this.runtime.requireSession();
7505
7799
  const profile = (await this.profile.publicProfileSummariesByUserIds([OFFICIAL_AUTHOR_USER_ID], session, options.signal)).get(OFFICIAL_AUTHOR_USER_ID);
@@ -7562,9 +7856,10 @@ var ChatService = class {
7562
7856
  sendAtMillis: options.cursor.sendAtMillis,
7563
7857
  recordUid: options.cursor.itemUid
7564
7858
  } : void 0);
7859
+ const signingKey = await this.runtime.stateStore.uniqueCode();
7565
7860
  return {
7566
7861
  source: await this.source.sourceItem(source),
7567
- items: page.items.map((item) => this.record.recordTimelineItem(item)),
7862
+ items: page.items.map((item) => this.withRecordMessageActionRef(source, this.record.recordTimelineItem(item), session.userId, signingKey)),
7568
7863
  hasMore: page.hasMore,
7569
7864
  ...page.nextCursor === void 0 ? {} : { nextCursor: {
7570
7865
  sendAtMillis: page.nextCursor.sendAtMillis,
@@ -7800,6 +8095,329 @@ var ChatService = class {
7800
8095
  readReceiptItemKey(itemUid, sequence) {
7801
8096
  return `${itemUid}\u0000${String(sequence)}`;
7802
8097
  }
8098
+ async copySourceMessageLink(sourceRef, actionRefs, options = {}) {
8099
+ const session = await this.runtime.requireSession();
8100
+ const source = await this.source.openSourceRef(sourceRef, session.userId);
8101
+ const references = this.forwardReferencesOrderedForCommand(await this.openMessageActionRefs(actionRefs, session.userId, source));
8102
+ if (references.length === 0 || references.length > 100) throw new ArkmePluginError("message-actions-selection-invalid", "请选择 1 至 100 条消息", false);
8103
+ this.validateForwardReferences(references);
8104
+ const sources = references.map((reference) => reference.sourceKind === "chat_relation" ? {
8105
+ kind: "chat_relation",
8106
+ chat_session_uid: reference.chatSessionUid,
8107
+ relation_uid: reference.relationUid
8108
+ } : {
8109
+ kind: "record",
8110
+ record_owner_user_id: reference.recordOwnerUserId,
8111
+ record_uid: reference.recordUid
8112
+ });
8113
+ const data = await this.runtime.authenticatedChatPost("/api/v1/chats/messages/copy-link/get-or-create", { sources }, session, options.signal);
8114
+ const sid = stringValue$1(data.sid).trim();
8115
+ const url = stringValue$1(data.url).trim();
8116
+ if (sid === "" || !/^https?:\/\//i.test(url)) throw new ArkmePluginError("message-copy-link-response-invalid", "复制链接失败,请稍后重试", true, 502);
8117
+ return {
8118
+ sid,
8119
+ url
8120
+ };
8121
+ }
8122
+ async resolveMessageCopyLink(sid, options = {}) {
8123
+ const normalizedSid = sid.trim();
8124
+ if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
8125
+ const session = await this.runtime.requireSession();
8126
+ const data = await this.runtime.authenticatedChatPost("/api/v1/chats/messages/copy-link/resolve", { sid: normalizedSid }, session, options.signal);
8127
+ const rawItems = listValue$16(data.items);
8128
+ if (rawItems.length === 0 && stringValue$1(data.audit_status) === "auditing") throw new ArkmePluginError("message-copy-link-auditing", "审核中", true, 409);
8129
+ const items = rawItems.map(messageCopyLinkSnapshotItemFromData);
8130
+ if (items.length === 0 || items.length > MAX_MESSAGE_COPY_LINK_ITEMS) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
8131
+ const presentation = messageCopyLinkPresentationNodesFromData(data.presentation, items.length);
8132
+ const sourceContext = objectValue$1(data.source_context);
8133
+ const accessMode = stringValue$1(data.access_mode);
8134
+ if (accessMode !== "normal" && accessMode !== "link_read_only") throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
8135
+ const anchors = accessMode === "normal" ? listValue$16(sourceContext.anchors).map(messageCopyLinkSourceAnchorFromData).filter((anchor) => anchor.relationUid.trim() !== "" && anchor.recordUid.trim() !== "" && anchor.recordOwnerUserId > 0) : [];
8136
+ const recordContext = mergeMessageCopyLinkRecordContexts(messageCopyLinkRecordContextFromData(data), accessMode === "normal" ? await this.loadMessageCopyLinkPublicExtensions(messageCopyLinkRecordUidCandidates(items, anchors), session, options.signal) : void 0);
8137
+ const result = {
8138
+ sid: stringValue$1(data.sid),
8139
+ displayTitle: stringValue$1(data.display_title),
8140
+ generatedAtMillis: epochMillisValue(data.generated_at),
8141
+ accessMode,
8142
+ items,
8143
+ presentation,
8144
+ ...accessMode === "normal" ? {
8145
+ sourceSessionUid: stringValue$1(sourceContext.chat_session_uid),
8146
+ sourceAnchors: anchors
8147
+ } : {},
8148
+ ...recordContext === void 0 ? {} : { recordContext }
8149
+ };
8150
+ if (result.sid !== normalizedSid || accessMode === "normal" && (result.sourceSessionUid === void 0 || result.sourceSessionUid.trim() === "" || anchors.length === 0)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
8151
+ return result;
8152
+ }
8153
+ async loadMessageCopyLinkPublicExtensions(recordUids, session, signal) {
8154
+ const worldPost = this.runtime.authenticatedWorldPost;
8155
+ const candidates = recordUids.map((recordUid) => recordUid.trim()).filter((recordUid, index, array) => recordUid !== "" && array.indexOf(recordUid) === index).slice(0, 3);
8156
+ if (candidates.length === 0 || worldPost === void 0) return void 0;
8157
+ for (const normalizedRecordUid of candidates) {
8158
+ const extensions = [];
8159
+ let offset = 0;
8160
+ let total = 0;
8161
+ let hasMore;
8162
+ try {
8163
+ while (true) {
8164
+ const data = await worldPost.call(this.runtime, "/api/v1/public-record/extend-list", {
8165
+ record_uid: normalizedRecordUid,
8166
+ limit: 50,
8167
+ offset
8168
+ }, session, signal, {
8169
+ lane: "interactive-read",
8170
+ bypassCache: true
8171
+ });
8172
+ const rawItems = listValue$16(data.list ?? data.items);
8173
+ if (offset === 0) total = Math.max(0, integerLikeValue$1(data.total));
8174
+ const pageItems = rawItems.map((raw) => messageCopyLinkExtensionItemFromData(raw)).filter((item) => item !== void 0);
8175
+ extensions.push(...pageItems);
8176
+ if (rawItems.length === 0) break;
8177
+ offset += rawItems.length;
8178
+ hasMore = data.has_more === true || data.hasMore === true ? true : data.has_more === false || data.hasMore === false ? false : void 0;
8179
+ if (total > 0 && offset >= total) break;
8180
+ if (hasMore === false) break;
8181
+ if (hasMore === void 0 && total <= 0) break;
8182
+ }
8183
+ } catch {
8184
+ continue;
8185
+ }
8186
+ const extensionCount = Math.max(total, extensions.length);
8187
+ if (extensionCount <= 0 && extensions.length === 0) continue;
8188
+ extensions.sort((left, right) => right.sendAtMillis - left.sendAtMillis);
8189
+ return {
8190
+ extensionCount,
8191
+ extensions
8192
+ };
8193
+ }
8194
+ }
8195
+ async extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options = {}) {
8196
+ const normalizedSid = sid.trim();
8197
+ const normalizedIndex = Math.trunc(itemIndex);
8198
+ const normalizedUid = recordUid.trim();
8199
+ const normalizedText = textContent.trim();
8200
+ if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid) || normalizedIndex < 0 || normalizedIndex >= MAX_MESSAGE_COPY_LINK_ITEMS) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
8201
+ if (!RECORD_UID_PATTERN.test(normalizedUid)) throw new ArkmePluginError("record-uid-invalid", "写入标识无效,请重试", false);
8202
+ if (normalizedText === "") throw new ArkmePluginError("record-text-empty", "请输入延展内容", false);
8203
+ if (normalizedText.length > this.runtime.config.maxTextLength) throw new ArkmePluginError("record-text-too-long", `内容不能超过 ${this.runtime.config.maxTextLength} 个字符`, false);
8204
+ const session = await this.runtime.requireSession();
8205
+ const detail = await this.resolveMessageCopyLink(normalizedSid, options);
8206
+ const item = detail.items[normalizedIndex];
8207
+ const anchor = detail.accessMode === "normal" ? detail.sourceAnchors?.[normalizedIndex] : void 0;
8208
+ const parentRecordUid = item === void 0 ? "" : messageCopyLinkRecordUidCandidates([item], anchor === void 0 ? [] : [anchor])[0] ?? "";
8209
+ if (item === void 0 || parentRecordUid === "") throw new ArkmePluginError("message-copy-link-extension-unavailable", "链接暂不可延展", false, 409);
8210
+ const profile = (await this.profile.refreshProfile()).profile;
8211
+ if (profile === null) throw new ArkmePluginError("profile-unavailable", "无法读取当前 Arkme 账号资料", true);
8212
+ if (profile.contact.phoneMasked === void 0) throw new ArkmePluginError("world-phone-binding-required", "请先在 Arkme 客户端绑定手机号,再延展快记", false);
8213
+ const sendAtMillis = Date.now();
8214
+ const recordResult = await this.record.createTextForConversation(normalizedUid, normalizedText);
8215
+ if (recordResult.localState !== "synced") throw new ArkmePluginError("message-copy-link-extension-record-pending", recordResult.error ?? "延展已保存到待重试队列,请稍后重试", true);
8216
+ const createdRecordUid = recordResult.recordUid.trim() || normalizedUid;
8217
+ const data = await this.runtime.authenticatedWorldPost("/api/v1/public-record/publish", {
8218
+ record_uid: createdRecordUid,
8219
+ content: normalizedText,
8220
+ text_content: normalizedText,
8221
+ tags: messageCopyLinkWorldTags(normalizedText),
8222
+ original_topic_id: 0,
8223
+ created_at: sendAtMillis,
8224
+ nick_name: profile.nickname || profile.displayName,
8225
+ avatar: profile.avatarRef,
8226
+ template_kind: 1,
8227
+ parent_record_uid: parentRecordUid
8228
+ }, session, options.signal);
8229
+ const publishedRecordUid = stringValue$1(data.record_uid).trim() || createdRecordUid;
8230
+ const senderDisplayName = profile.nickname.trim() || profile.displayName.trim() || "我";
8231
+ const senderAvatarUrl = profile.avatarRef.trim();
8232
+ this.source.invalidateSourceListCache(session.userId, "send_to_self");
8233
+ this.realtime.emitChatClientEvent({
8234
+ type: "projection-invalidated",
8235
+ revision: this.realtime.nextChatClientRevision(),
8236
+ projection: "record"
8237
+ });
8238
+ return {
8239
+ sid: detail.sid,
8240
+ recordUid: publishedRecordUid,
8241
+ parentRecordUid,
8242
+ status: numberValue$16(data.status ?? data.check_status ?? recordResult.status),
8243
+ localState: "synced",
8244
+ extension: {
8245
+ recordUid: publishedRecordUid,
8246
+ level: 2,
8247
+ sourceKind: "record_extension",
8248
+ senderDisplayName,
8249
+ ...senderAvatarUrl === "" ? {} : { senderAvatarUrl },
8250
+ title: "",
8251
+ textContent: normalizedText,
8252
+ sendAtMillis,
8253
+ templateKind: 1,
8254
+ displayKind: 0,
8255
+ officialMark: 0,
8256
+ mediaItems: []
8257
+ }
8258
+ };
8259
+ }
8260
+ async resolveLinkMetadata(rawUrl, options = {}) {
8261
+ const url = ensureMetadataUrl(rawUrl);
8262
+ try {
8263
+ return await this.fetchLinkMetadata(url, 0, options.signal);
8264
+ } catch (error) {
8265
+ if (error instanceof ArkmePluginError && (error.code === "link-metadata-url-invalid" || error.code === "link-metadata-url-forbidden")) throw error;
8266
+ return {
8267
+ url: url.toString(),
8268
+ title: fallbackLinkTitle(url),
8269
+ siteName: url.hostname.replace(/^www\./iu, "")
8270
+ };
8271
+ }
8272
+ }
8273
+ async fetchLinkMetadata(url, redirectCount, signal) {
8274
+ const controller = new AbortController();
8275
+ const abort = () => {
8276
+ controller.abort(signal?.reason);
8277
+ };
8278
+ if (signal?.aborted === true) abort();
8279
+ else signal?.addEventListener("abort", abort, { once: true });
8280
+ const timeout = setTimeout(() => {
8281
+ controller.abort();
8282
+ }, Math.min(8e3, this.runtime.config.requestTimeoutMs));
8283
+ try {
8284
+ const response = await this.runtime.fetchImpl(url, {
8285
+ method: "GET",
8286
+ redirect: "manual",
8287
+ headers: {
8288
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
8289
+ "Accept-Language": "zh-CN",
8290
+ "User-Agent": "Mozilla/5.0 ArkmeDSHPlugin/0.1 LinkPreview"
8291
+ },
8292
+ signal: controller.signal
8293
+ });
8294
+ if (response.status >= 300 && response.status < 400) {
8295
+ if (redirectCount >= LINK_METADATA_MAX_REDIRECTS) return {
8296
+ url: url.toString(),
8297
+ title: fallbackLinkTitle(url),
8298
+ siteName: url.hostname.replace(/^www\./iu, "")
8299
+ };
8300
+ const location = response.headers.get("location");
8301
+ if (location === null || location.trim() === "") return {
8302
+ url: url.toString(),
8303
+ title: fallbackLinkTitle(url),
8304
+ siteName: url.hostname.replace(/^www\./iu, "")
8305
+ };
8306
+ return await this.fetchLinkMetadata(ensureMetadataUrl(new URL(location, url).toString()), redirectCount + 1, signal);
8307
+ }
8308
+ if (!response.ok) return {
8309
+ url: url.toString(),
8310
+ title: fallbackLinkTitle(url),
8311
+ siteName: url.hostname.replace(/^www\./iu, "")
8312
+ };
8313
+ const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
8314
+ if (contentType !== "" && !contentType.includes("html") && !contentType.includes("xml") && !contentType.includes("text/")) return {
8315
+ url: url.toString(),
8316
+ title: fallbackLinkTitle(url),
8317
+ siteName: url.hostname.replace(/^www\./iu, "")
8318
+ };
8319
+ return linkMetadataFromHtml(url, await readResponseTextPrefix(response));
8320
+ } finally {
8321
+ clearTimeout(timeout);
8322
+ signal?.removeEventListener("abort", abort);
8323
+ }
8324
+ }
8325
+ async forwardSourceMessages(sourceRef, actionRefs, options = {}) {
8326
+ const session = await this.runtime.requireSession();
8327
+ const source = await this.source.openSourceRef(sourceRef, session.userId);
8328
+ const targetSourceRef = options.targetSourceRef?.trim() || sourceRef;
8329
+ const targetSource = targetSourceRef === sourceRef ? source : await this.source.openSourceRef(targetSourceRef, session.userId);
8330
+ const commentText = options.commentText?.trim() ?? "";
8331
+ if (commentText.length > this.runtime.config.maxTextLength) throw new ArkmePluginError("source-text-invalid", "发送内容超过长度限制", false);
8332
+ if (targetSource.kind !== "private_chat" && targetSource.kind !== "group_chat" && targetSource.kind !== "send_to_self" && targetSource.kind !== "default_category" && targetSource.kind !== "topic") throw new ArkmePluginError("message-actions-source-invalid", "当前数据源暂不支持转发", false, 409);
8333
+ const references = this.forwardReferencesOrderedForCommand(await this.openMessageActionRefs(actionRefs, session.userId, source));
8334
+ if (references.length === 0 || references.length > 100) throw new ArkmePluginError("message-actions-selection-invalid", "请选择 1 至 100 条消息", false);
8335
+ this.validateForwardReferences(references);
8336
+ const recordUid = options.recordUid?.trim() || randomUUID();
8337
+ const title = references.length === 1 ? "转发快记" : `转发 ${String(references.length)} 条快记`;
8338
+ const contentPayload = {
8339
+ payload_kind: 1,
8340
+ schema_version: 1,
8341
+ text_state: 1,
8342
+ forward_records: {
8343
+ render_kind: "forward_records",
8344
+ schema_version: 1,
8345
+ source_type: "quick_records",
8346
+ title: "转发快记",
8347
+ source_record_uids: references.map((reference) => reference.recordUid).filter((recordUid) => recordUid.trim() !== ""),
8348
+ created_at: Date.now(),
8349
+ summary_lines: references.slice(0, 3).map((reference) => `${reference.senderName}: ${reference.textContent.trim() || reference.title.trim() || "非文本内容"}`),
8350
+ items: references.map((reference, index) => this.forwardPayloadItem(reference, index))
8351
+ }
8352
+ };
8353
+ const appendCommentWarning = async (sent) => {
8354
+ if (commentText === "") return sent;
8355
+ try {
8356
+ await this.sendSourceText(targetSourceRef, commentText, { ...options.signal === void 0 ? {} : { signal: options.signal } });
8357
+ return sent;
8358
+ } catch {
8359
+ return {
8360
+ ...sent,
8361
+ warningText: "转发已完成,附言发送失败"
8362
+ };
8363
+ }
8364
+ };
8365
+ if (targetSource.kind === "private_chat" || targetSource.kind === "group_chat") {
8366
+ const sendAtMillis = Date.now();
8367
+ const clientRequestId = this.forwardClientRequestId(targetSource.ownerRef, references, sendAtMillis);
8368
+ const sourceRecordUids = references.some((reference) => reference.sourceKind === "chat_relation") ? [] : references.map((reference) => reference.recordUid).filter((recordUid) => recordUid.trim() !== "");
8369
+ const data = await this.runtime.authenticatedChatPost("/api/v1/chats/records/forward", {
8370
+ chat_session_uid: targetSource.ownerRef,
8371
+ client_request_id: clientRequestId,
8372
+ ...sourceRecordUids.length === 0 ? {} : { source_record_uids: sourceRecordUids },
8373
+ source_items: references.map((reference) => this.forwardSourceItemBody(reference)),
8374
+ ...commentText === "" ? {} : { comment_text: commentText },
8375
+ send_at: sendAtMillis
8376
+ }, session, options.signal);
8377
+ const sequence = numberValue$16(data.seq ?? data.sequence);
8378
+ this.realtime.scheduleChatSessionProjection(targetSource.ownerRef, sequence);
8379
+ return {
8380
+ sourceRef: targetSourceRef,
8381
+ itemUid: stringValue$1(data.record_uid).trim() || recordUid,
8382
+ status: numberValue$16(data.audit_status ?? data.status) || 1,
8383
+ ...sequence > 0 ? { sequence } : {},
8384
+ localState: "synced"
8385
+ };
8386
+ }
8387
+ if (targetSource.kind === "topic") {
8388
+ const data = await this.runtime.authenticatedPost("/api/v1/topics/records/create", {
8389
+ topic_uid: targetSource.ownerRef,
8390
+ record_uid: recordUid,
8391
+ template_kind: 1,
8392
+ title: "",
8393
+ text_content: title,
8394
+ content_payload: contentPayload,
8395
+ send_at: Date.now()
8396
+ }, session, options.signal);
8397
+ this.source.invalidateSourceListCache(session.userId, "send_to_self");
8398
+ return await appendCommentWarning({
8399
+ sourceRef: targetSourceRef,
8400
+ itemUid: stringValue$1(data.record_uid).trim() || recordUid,
8401
+ status: numberValue$16(data.status),
8402
+ localState: "synced"
8403
+ });
8404
+ }
8405
+ const data = await this.runtime.authenticatedPost("/api/v1/records/create", {
8406
+ record_uid: recordUid,
8407
+ template_kind: 1,
8408
+ title: "",
8409
+ text_content: title,
8410
+ content_payload: contentPayload,
8411
+ send_at: Date.now()
8412
+ }, session, options.signal);
8413
+ this.source.invalidateSourceListCache(session.userId, "send_to_self");
8414
+ return await appendCommentWarning({
8415
+ sourceRef: targetSourceRef,
8416
+ itemUid: stringValue$1(data.record_uid).trim() || recordUid,
8417
+ status: numberValue$16(data.status),
8418
+ localState: "synced"
8419
+ });
8420
+ }
7803
8421
  async sendSourceText(sourceRef, textContent, options = {}) {
7804
8422
  const session = await this.runtime.requireSession();
7805
8423
  const source = await this.source.openSourceRef(sourceRef, session.userId);
@@ -8053,9 +8671,6 @@ var ChatService = class {
8053
8671
  localState: "synced"
8054
8672
  };
8055
8673
  }
8056
- async sendGroupSourceTextWithAiPolish(sourceRef, chatSessionUid, originalText, recordUid, relationUid, session, options = {}) {
8057
- return await this.aiPolish.sendGroupSourceTextWithAiPolish(sourceRef, chatSessionUid, originalText, recordUid, relationUid, session, options);
8058
- }
8059
8674
  async sendSourceRich(sourceRef, input, options = {}) {
8060
8675
  if (this.runtime.config.richMediaSendEnabled === false) throw new ArkmePluginError("rich-content-disabled", "富内容发送已被插件配置关闭", false, 403);
8061
8676
  const session = await this.runtime.requireSession();
@@ -8285,12 +8900,6 @@ var ChatService = class {
8285
8900
  async fetchMedia(mediaRef, range, signal) {
8286
8901
  return await this.media.fetchMedia(mediaRef, range, signal);
8287
8902
  }
8288
- currentUserAgentSourceFallback(userId, source) {
8289
- return this.arko.currentUserAgentSourceFallback(userId, source);
8290
- }
8291
- async agentSourceDisplayName(session) {
8292
- return await this.arko.agentSourceDisplayName(session);
8293
- }
8294
8903
  async sendDirectText(recipientArkmeId, textContent, options = {}) {
8295
8904
  const session = await this.runtime.requireSession();
8296
8905
  const recipient = recipientArkmeId.trim();
@@ -8383,6 +8992,7 @@ var ChatService = class {
8383
8992
  const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
8384
8993
  const rawAgentSource = timelineAgentSource(relation, record, payload);
8385
8994
  const agentSource = senderUserId === session.userId ? this.arko.currentUserAgentSourceFallback(session.userId, rawAgentSource) : rawAgentSource;
8995
+ const contentBlocks = this.media.richContentBlocks(item, session.userId);
8386
8996
  const mentionsViewer = senderUserId !== session.userId && arkmeMentionMetadataMentionsViewer(record, payload, session.userId);
8387
8997
  items.push({
8388
8998
  itemUid: uid,
@@ -8401,7 +9011,7 @@ var ChatService = class {
8401
9011
  ...aiPolish === void 0 ? {} : { aiPolish },
8402
9012
  ...forwardRecords === void 0 ? {} : { forwardRecords },
8403
9013
  displayKind: numberValue$16(payload.display_kind),
8404
- contentBlocks: this.media.richContentBlocks(item, session.userId)
9014
+ contentBlocks
8405
9015
  });
8406
9016
  }
8407
9017
  return items;
@@ -8571,11 +9181,31 @@ var ChatService = class {
8571
9181
  const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
8572
9182
  const rawAgentSource = timelineAgentSource(relation, record, payload);
8573
9183
  const agentSource = senderUserId === session.userId ? this.arko.currentUserAgentSourceFallback(session.userId, rawAgentSource) : rawAgentSource;
9184
+ const contentBlocks = this.media.richContentBlocks(item, session.userId);
9185
+ const senderName = stringValue$1(relation.display_name_snapshot).trim() || "Arkme用户";
8574
9186
  const itemIndex = items.push({
8575
9187
  itemUid: uid,
8576
9188
  ...source.kind !== "group_chat" || relationUid === "" || senderUserId === session.userId ? {} : { messageRef: this.sealMessageRef(session.userId, source.ownerRef, relationUid, signingKey) },
9189
+ ...relationUid === "" ? {} : { messageActionRef: this.sealMessageActionRef(this.messageActionRefPayload({
9190
+ sourceKind: "chat_relation",
9191
+ userId: session.userId,
9192
+ sourceOwnerRef: source.ownerRef,
9193
+ chatSessionUid: source.ownerRef,
9194
+ relationUid,
9195
+ recordOwnerUserId: 0,
9196
+ recordUid: uid,
9197
+ senderUserId,
9198
+ senderName,
9199
+ title: stringValue$1(payload.title),
9200
+ textContent: stringValue$1(payload.text_content),
9201
+ sendAtMillis,
9202
+ sourceSequence: numberValue$16(relation.seq),
9203
+ templateKind: numberValue$16(payload.template_kind),
9204
+ displayKind: numberValue$16(payload.display_kind),
9205
+ contentBlocks
9206
+ }), signingKey) },
8577
9207
  ...senderUserId > 0 ? { memberRef: await this.sealChatMemberRef(session.userId, source.ownerRef, senderUserId) } : {},
8578
- senderName: stringValue$1(relation.display_name_snapshot).trim() || "Arkme用户",
9208
+ senderName,
8579
9209
  ...agentSource === void 0 ? {} : { agentSource },
8580
9210
  isMe: senderUserId === session.userId,
8581
9211
  sendAtMillis,
@@ -8591,7 +9221,7 @@ var ChatService = class {
8591
9221
  updateAtMillis: numberValue$16(payload.update_at ?? record.update_at),
8592
9222
  recordDurationMillis: numberValue$16(payload.record_duration_millis),
8593
9223
  editDurationMillis: numberValue$16(payload.edit_duration_millis),
8594
- contentBlocks: this.media.richContentBlocks(item, session.userId),
9224
+ contentBlocks,
8595
9225
  ...forwardRecords === void 0 ? {} : { forwardRecords }
8596
9226
  }) - 1;
8597
9227
  if (senderUserId > 0) senderUserIdByIndex.set(itemIndex, senderUserId);
@@ -8636,6 +9266,176 @@ var ChatService = class {
8636
9266
  if (raw.version !== 1 || result.viewerUserId !== expectedViewerUserId || result.chatSessionUid !== expectedChatSessionUid || !Number.isSafeInteger(result.targetUserId) || result.targetUserId <= 0) throw new ArkmePluginError("chat-member-ref-invalid", "聊天成员引用与当前账号或会话不匹配", false, 403);
8637
9267
  return result;
8638
9268
  }
9269
+ messageActionRefPayload(input) {
9270
+ const contentBlocks = input.contentBlocks ?? [];
9271
+ const fileNames = contentBlocks.filter((block) => block.kind === "file" && block.fileName.trim() !== "").slice(0, 5).map((block) => block.fileName.trim().slice(0, 256));
9272
+ return {
9273
+ version: 1,
9274
+ sourceKind: input.sourceKind,
9275
+ userId: input.userId,
9276
+ sourceOwnerRef: input.sourceOwnerRef,
9277
+ chatSessionUid: input.chatSessionUid,
9278
+ relationUid: input.relationUid,
9279
+ recordOwnerUserId: Math.trunc(input.recordOwnerUserId),
9280
+ recordUid: input.recordUid,
9281
+ senderUserId: Math.trunc(input.senderUserId),
9282
+ senderName: input.senderName.trim() || "Arkme用户",
9283
+ title: input.title.slice(0, 500),
9284
+ textContent: input.textContent.slice(0, this.runtime.config.maxTextLength),
9285
+ sendAtMillis: Math.trunc(input.sendAtMillis),
9286
+ sourceSequence: Math.max(0, Math.trunc(numberValue$16(input.sourceSequence))),
9287
+ templateKind: Math.trunc(input.templateKind),
9288
+ displayKind: Math.trunc(input.displayKind),
9289
+ imageCount: contentBlocks.filter((block) => block.kind === "image").length,
9290
+ voiceCount: contentBlocks.filter((block) => block.kind === "audio").length,
9291
+ fileCount: contentBlocks.filter((block) => block.kind === "file").length,
9292
+ fileNames
9293
+ };
9294
+ }
9295
+ withRecordMessageActionRef(source, item, userId, signingKey) {
9296
+ if (source.kind !== "send_to_self" && source.kind !== "default_category" && source.kind !== "topic" || item.itemUid.trim() === "" || !item.isMe) return item;
9297
+ return {
9298
+ ...item,
9299
+ messageActionRef: this.sealMessageActionRef(this.messageActionRefPayload({
9300
+ sourceKind: "record",
9301
+ userId,
9302
+ sourceOwnerRef: source.ownerRef,
9303
+ chatSessionUid: "",
9304
+ relationUid: "",
9305
+ recordOwnerUserId: userId,
9306
+ recordUid: item.itemUid,
9307
+ senderUserId: userId,
9308
+ senderName: item.senderName,
9309
+ title: item.title,
9310
+ textContent: item.textContent,
9311
+ sendAtMillis: item.sendAtMillis,
9312
+ templateKind: item.templateKind ?? 1,
9313
+ displayKind: item.displayKind ?? 0,
9314
+ contentBlocks: item.contentBlocks
9315
+ }), signingKey)
9316
+ };
9317
+ }
9318
+ sealMessageActionRef(payload, signingKey) {
9319
+ const encoded = encodeOpaqueJson$5(payload);
9320
+ return `arkme-message-action-v1.${encoded}.${createHmac("sha256", signingKey).update(encoded).digest("base64url")}`;
9321
+ }
9322
+ async openMessageActionRefs(actionRefs, expectedUserId, source) {
9323
+ const normalizedRefs = actionRefs.map((value) => value.trim()).filter((value) => value !== "");
9324
+ const uniqueRefs = new Set(normalizedRefs);
9325
+ if (normalizedRefs.length !== uniqueRefs.size) throw new ArkmePluginError("message-actions-selection-invalid", "不能选择重复消息", false);
9326
+ const references = [];
9327
+ const relationIdentities = /* @__PURE__ */ new Set();
9328
+ for (const actionRef of normalizedRefs) {
9329
+ const reference = await this.openMessageActionRef(actionRef, expectedUserId, source);
9330
+ const relationIdentity = reference.sourceKind === "chat_relation" ? `chat_relation\u0000${reference.chatSessionUid}\u0000${reference.relationUid}` : `record\u0000${String(reference.recordOwnerUserId)}\u0000${reference.recordUid}`;
9331
+ if (!relationIdentities.add(relationIdentity)) throw new ArkmePluginError("message-actions-selection-invalid", "不能选择重复消息", false);
9332
+ references.push(reference);
9333
+ }
9334
+ return references;
9335
+ }
9336
+ async openMessageActionRef(actionRef, expectedUserId, source) {
9337
+ const parts = actionRef.trim().split(".");
9338
+ if (parts.length !== 3 || parts[0] !== "arkme-message-action-v1") throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false);
9339
+ const payload = parts[1] ?? "";
9340
+ const supplied = Buffer.from(parts[2] ?? "", "base64url");
9341
+ const expected = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(payload).digest();
9342
+ if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false);
9343
+ let parsed;
9344
+ try {
9345
+ parsed = objectValue$1(decodeOpaqueJson$5(payload));
9346
+ } catch (error) {
9347
+ throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false, 400, { cause: error });
9348
+ }
9349
+ const fileNames = listValue$16(parsed.fileNames).map((value) => stringValue$1(value).trim()).filter((value) => value !== "").slice(0, 5);
9350
+ const result = {
9351
+ version: 1,
9352
+ sourceKind: stringValue$1(parsed.sourceKind).trim() === "record" ? "record" : "chat_relation",
9353
+ userId: Math.trunc(numberValue$16(parsed.userId)),
9354
+ sourceOwnerRef: stringValue$1(parsed.sourceOwnerRef).trim(),
9355
+ chatSessionUid: stringValue$1(parsed.chatSessionUid).trim(),
9356
+ relationUid: stringValue$1(parsed.relationUid).trim(),
9357
+ recordOwnerUserId: Math.trunc(numberValue$16(parsed.recordOwnerUserId)),
9358
+ recordUid: stringValue$1(parsed.recordUid).trim(),
9359
+ senderUserId: Math.trunc(numberValue$16(parsed.senderUserId)),
9360
+ senderName: stringValue$1(parsed.senderName).trim() || "Arkme用户",
9361
+ title: stringValue$1(parsed.title),
9362
+ textContent: stringValue$1(parsed.textContent),
9363
+ sendAtMillis: Math.trunc(numberValue$16(parsed.sendAtMillis)),
9364
+ sourceSequence: Math.max(0, Math.trunc(numberValue$16(parsed.sourceSequence))),
9365
+ templateKind: Math.trunc(numberValue$16(parsed.templateKind)),
9366
+ displayKind: Math.trunc(numberValue$16(parsed.displayKind)),
9367
+ imageCount: Math.max(0, Math.trunc(numberValue$16(parsed.imageCount))),
9368
+ voiceCount: Math.max(0, Math.trunc(numberValue$16(parsed.voiceCount))),
9369
+ fileCount: Math.max(0, Math.trunc(numberValue$16(parsed.fileCount))),
9370
+ fileNames
9371
+ };
9372
+ const matchesChatSource = result.sourceKind === "chat_relation" && (source.kind === "private_chat" || source.kind === "group_chat") && result.sourceOwnerRef === source.ownerRef && result.chatSessionUid === source.ownerRef && result.relationUid !== "" && result.recordUid !== "";
9373
+ const matchesRecordSource = result.sourceKind === "record" && (source.kind === "send_to_self" || source.kind === "default_category" || source.kind === "topic") && result.sourceOwnerRef === source.ownerRef && result.chatSessionUid === "" && result.relationUid === "" && result.recordOwnerUserId === expectedUserId && result.recordUid !== "";
9374
+ if (parsed.version !== 1 || result.userId !== expectedUserId || !matchesChatSource && !matchesRecordSource) throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用与当前账号或会话不匹配,请刷新后重试", false, 403);
9375
+ return result;
9376
+ }
9377
+ forwardPayloadItem(reference, itemOrder) {
9378
+ return {
9379
+ item_order: itemOrder,
9380
+ source_kind: reference.sourceKind,
9381
+ record_uid: reference.recordUid,
9382
+ source_type: reference.sourceKind === "chat_relation" ? "chat_record" : "record",
9383
+ ...reference.recordOwnerUserId > 0 ? { owner_id: reference.recordOwnerUserId } : {},
9384
+ ...reference.relationUid === "" ? {} : { source_rel_uid: reference.relationUid },
9385
+ ...reference.chatSessionUid === "" ? {} : { source_chat_session_uid: reference.chatSessionUid },
9386
+ ...reference.sourceSequence > 0 ? { source_seq: reference.sourceSequence } : {},
9387
+ source_sender_user_id: reference.senderUserId,
9388
+ source_display_name: reference.senderName,
9389
+ owner_name: reference.senderName,
9390
+ send_at: reference.sendAtMillis,
9391
+ title: reference.title,
9392
+ text: reference.textContent,
9393
+ text_preview: reference.textContent.trim().slice(0, 500),
9394
+ template_kind: reference.templateKind,
9395
+ display_kind: reference.displayKind,
9396
+ image_count: reference.imageCount,
9397
+ voice_count: reference.voiceCount,
9398
+ file_count: reference.fileCount,
9399
+ file_names: reference.fileNames
9400
+ };
9401
+ }
9402
+ forwardClientRequestId(targetChatSessionUid, references, sendAtMillis) {
9403
+ const sourceIdentity = references.map((reference) => reference.sourceKind === "chat_relation" ? `chat_record:${reference.chatSessionUid}:${reference.relationUid}` : reference.recordUid).join("|");
9404
+ const sourceHash = createHash("sha256").update(sourceIdentity).digest("hex").slice(0, 16);
9405
+ return `forward_${targetChatSessionUid}_${String(Math.trunc(sendAtMillis))}_${sourceHash}`;
9406
+ }
9407
+ forwardReferencesOrderedForCommand(references) {
9408
+ if (references.length < 2 || !references.every((reference) => reference.sourceKind === "chat_relation" && reference.sourceSequence > 0 && reference.chatSessionUid.trim() !== "")) return references;
9409
+ const sourceSessionUid = references[0]?.chatSessionUid.trim() ?? "";
9410
+ if (sourceSessionUid === "" || !references.every((reference) => reference.chatSessionUid.trim() === sourceSessionUid)) return references;
9411
+ const ordered = references.map((reference, index) => ({
9412
+ reference,
9413
+ index
9414
+ })).sort((left, right) => {
9415
+ const sequenceCompare = left.reference.sourceSequence - right.reference.sourceSequence;
9416
+ return sequenceCompare === 0 ? left.index - right.index : sequenceCompare;
9417
+ }).map((item) => item.reference);
9418
+ return ordered.every((reference, index) => reference === references[index]) ? references : ordered;
9419
+ }
9420
+ validateForwardReferences(references) {
9421
+ const sourceTypes = new Set(references.map((reference) => reference.sourceKind));
9422
+ if (sourceTypes.size > 1) throw new ArkmePluginError("message-actions-selection-invalid", "暂不支持混合转发不同类型的消息", false);
9423
+ if (!sourceTypes.has("chat_relation")) return;
9424
+ if (new Set(references.map((reference) => reference.chatSessionUid.trim()).filter((value) => value !== "")).size > 1) throw new ArkmePluginError("message-actions-selection-invalid", "暂不支持跨会话转发聊天消息", false);
9425
+ }
9426
+ forwardSourceItemBody(reference) {
9427
+ if (reference.sourceKind === "chat_relation") return {
9428
+ source_type: "chat_record",
9429
+ record_uid: reference.recordUid,
9430
+ source_chat_session_uid: reference.chatSessionUid,
9431
+ source_rel_uid: reference.relationUid,
9432
+ ...reference.sourceSequence > 0 ? { source_seq: reference.sourceSequence } : {}
9433
+ };
9434
+ return {
9435
+ source_type: "record",
9436
+ record_uid: reference.recordUid
9437
+ };
9438
+ }
8639
9439
  sealMessageRef(userId, chatSessionUid, relationUid, signingKey) {
8640
9440
  const payload = encodeOpaqueJson$5({
8641
9441
  version: 1,
@@ -17059,6 +17859,21 @@ var ArkmeService = class {
17059
17859
  async reportMessage(messageRef, reportType, options = {}) {
17060
17860
  return await this.chat.reportMessage(messageRef, reportType, options);
17061
17861
  }
17862
+ async copySourceMessageLink(sourceRef, actionRefs, options = {}) {
17863
+ return await this.chat.copySourceMessageLink(sourceRef, actionRefs, options);
17864
+ }
17865
+ async resolveMessageCopyLink(sid, options = {}) {
17866
+ return await this.chat.resolveMessageCopyLink(sid, options);
17867
+ }
17868
+ async extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options = {}) {
17869
+ return await this.chat.extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options);
17870
+ }
17871
+ async resolveLinkMetadata(rawUrl, options = {}) {
17872
+ return await this.chat.resolveLinkMetadata(rawUrl, options);
17873
+ }
17874
+ async forwardSourceMessages(sourceRef, actionRefs, options = {}) {
17875
+ return await this.chat.forwardSourceMessages(sourceRef, actionRefs, options);
17876
+ }
17062
17877
  async sendSourceText(sourceRef, textContent, options = {}) {
17063
17878
  return await this.chat.sendSourceText(sourceRef, textContent, options);
17064
17879
  }
@@ -18306,6 +19121,7 @@ function persistentClientFactory(requireModule, spec) {
18306
19121
  return {
18307
19122
  name: `arkme-extension-client:${spec.extensionId}`,
18308
19123
  async apply(ctx) {
19124
+ if ((typeof document.location?.search === "string" ? document.location.search : "").split(/[?&]/).includes("arkme-harness-embed=1")) return;
18309
19125
  const styles = new Styles();
18310
19126
  const taggedConsole = {
18311
19127
  ...console,
@@ -18420,7 +19236,7 @@ function renderPersistentClientBundle(packageName, spec) {
18420
19236
  const renderedSpec = {
18421
19237
  ...spec,
18422
19238
  clientContentDigest: spec.clientContentDigest ?? arkmeClientContentDigest(spec.code),
18423
- wrapperVersion: 3
19239
+ wrapperVersion: 4
18424
19240
  };
18425
19241
  return [
18426
19242
  `window.__ModuleLoader__.load({ id: ${JSON.stringify(packageName)}, factory: (require) => {`,
@@ -18438,13 +19254,16 @@ function renderArkmeBundleClientBundle(packageName, spec) {
18438
19254
  identityKey: "packageName"
18439
19255
  });
18440
19256
  }
18441
- function packageIdentity(extensionId) {
19257
+ function persistentExtensionPackageIdentity(extensionId) {
18442
19258
  const suffix = createHash("sha256").update(extensionId).digest("hex").slice(0, 16);
18443
19259
  return {
18444
19260
  packageName: `@arkme-local/ext-${suffix}`,
18445
19261
  entryId: `arkme-extension-${suffix}`
18446
19262
  };
18447
19263
  }
19264
+ function persistentExtensionBundleDirectory(profileDirectory, extensionId, version) {
19265
+ return join(profileDirectory, "arkme-extensions", createHash("sha256").update(extensionId).digest("hex").slice(0, 16), version);
19266
+ }
18448
19267
  function writeSecure(path, content) {
18449
19268
  writeFileSync(path, content, {
18450
19269
  encoding: "utf8",
@@ -18541,9 +19360,22 @@ function readPersistentExtensionActivation(installationUrl) {
18541
19360
  }
18542
19361
  return state;
18543
19362
  }
19363
+ function repairPersistentExtensionInstallation(input) {
19364
+ const installationPath = join(input.bundleDirectory, "installation.json");
19365
+ const installation = JSON.parse(readFileSync(installationPath, "utf8"));
19366
+ if (installation.format_version !== 1 || installation.extension_id !== input.extensionId || installation.version !== input.version || typeof installation.artifact_path !== "string" || installation.artifact_path.trim() === "" || installation.artifact_sha256 !== input.artifactSha256 || installation.manifest_sha256 !== input.manifestSha256 || typeof installation.signature !== "string" || installation.signature.trim() === "" || typeof installation.signing_key_id !== "string" || installation.signing_key_id.trim() === "" || typeof installation.trusted_public_key !== "string" || installation.trusted_public_key.trim() === "" || !Number.isSafeInteger(installation.published_at) || (installation.published_at ?? -1) < 0) throw new Error("Arkme persistent extension installation identity mismatch");
19367
+ const manifest = JSON.parse(readFileSync(join(input.bundleDirectory, "package.json"), "utf8"));
19368
+ if (manifest.name !== input.packageName || manifest.version !== input.version) throw new Error("Arkme persistent extension package identity mismatch");
19369
+ if (installation.artifact_path === input.artifactPath) return false;
19370
+ replaceSecure(installationPath, `${JSON.stringify({
19371
+ ...installation,
19372
+ artifact_path: input.artifactPath
19373
+ }, void 0, 2)}\n`);
19374
+ return true;
19375
+ }
18544
19376
  function materializePersistentExtensionBundle(input) {
18545
- const { packageName, entryId } = packageIdentity(input.resolution.extension_id);
18546
- const bundleDirectory = join(input.profileDirectory, "arkme-extensions", createHash("sha256").update(input.resolution.extension_id).digest("hex").slice(0, 16), input.resolution.version);
19377
+ const { packageName, entryId } = persistentExtensionPackageIdentity(input.resolution.extension_id);
19378
+ const bundleDirectory = persistentExtensionBundleDirectory(input.profileDirectory, input.resolution.extension_id, input.resolution.version);
18547
19379
  const installation = {
18548
19380
  format_version: 1,
18549
19381
  extension_id: input.resolution.extension_id,
@@ -18568,7 +19400,7 @@ function materializePersistentExtensionBundle(input) {
18568
19400
  try {
18569
19401
  const manifest = JSON.parse(readFileSync(join(bundleDirectory, "package.json"), "utf8"));
18570
19402
  const clientBundle = input.clientCode === void 0 ? void 0 : readFileSync(join(bundleDirectory, "lib", "client.js"), "utf8");
18571
- const clientReady = input.clientCode === void 0 || manifest.exports?.["./client"] === "./lib/client.js" && manifest.dsh?.client?.inject?.length === 0 && clientBundle?.includes("extensions.persistent.invoke") === true && clientBundle.includes("extensions.persistent.client-state") && clientBundle.includes(`\"wrapperVersion\":${String(3)}`);
19403
+ const clientReady = input.clientCode === void 0 || manifest.exports?.["./client"] === "./lib/client.js" && manifest.dsh?.client?.inject?.length === 0 && clientBundle?.includes("extensions.persistent.invoke") === true && clientBundle.includes("extensions.persistent.client-state") && clientBundle.includes(`\"wrapperVersion\":${String(4)}`);
18572
19404
  if (manifest.exports?.["."] === "./lib/index.js" && manifest.exports?.["./package.json"] === "./package.json" && clientReady) {
18573
19405
  if (!existsSync(activationPath)) writeSecure(activationPath, activationText(input.resolution.extension_id, true));
18574
19406
  return {
@@ -18654,7 +19486,21 @@ const VM_TIMEOUT_MS = 5e3;
18654
19486
  function readInstallation(url) {
18655
19487
  const value = JSON.parse(readFileSync(url, "utf8"));
18656
19488
  if (value.format_version !== 1 || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.extension_id) || value.version.trim() === "" || value.artifact_path.trim() === "" || value.trusted_public_key.trim() === "") throw new Error("Arkme persistent extension installation metadata is invalid");
18657
- return value;
19489
+ return {
19490
+ ...value,
19491
+ artifact_path: resolveRuntimeArtifactPath(value, url)
19492
+ };
19493
+ }
19494
+ function resolveRuntimeArtifactPath(installation, installationUrl) {
19495
+ const dshHome = process.env.DSH_HOME?.trim();
19496
+ const filename = basename(installation.artifact_path);
19497
+ if (dshHome !== void 0 && dshHome !== "" && filename !== "" && filename !== "." && filename !== "..") try {
19498
+ const root = realpathSync(join(dshHome, "arkme-self", "extensions"));
19499
+ const candidate = realpathSync(join(root, installation.extension_id, installation.version, filename));
19500
+ const fromRoot = relative(root, candidate);
19501
+ if (fromRoot !== "" && fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot) && sha256Hex(readFileSync(candidate)) === installation.artifact_sha256) return candidate;
19502
+ } catch {}
19503
+ return isAbsolute(installation.artifact_path) ? installation.artifact_path : resolve(dirname(fileURLToPath(installationUrl)), installation.artifact_path);
18658
19504
  }
18659
19505
  function jsonValue(value) {
18660
19506
  if (value === void 0) return null;
@@ -18922,4 +19768,4 @@ async function invokePersistentArkmeExtension(extensionId, method, args) {
18922
19768
  return jsonValue(await handler(jsonValue(args)));
18923
19769
  }
18924
19770
  //#endregion
18925
- export { readNativeBundleTarballV3 as A, ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE as B, arkmeSandboxEntryId as C, inspectNativeBundleArtifactV3 as D, inspectBundleArtifact as E, unpackArkmeExtension as F, ARKME_VOICEPRINT_ENROLLMENT_MAX_AUDIO_BYTES as G, validateVoiceprintPcm16Wav as H, ARKME_EXTENSION_FORMAT as I, ARKME_WORLD_PUBLISH_MAX_IMAGES as J, ARKME_VOICEPRINT_ENROLLMENT_MAX_DURATION_MS as K, ARKME_EXTENSION_ICON_MAX_BYTES as L, assertExtensionArtifactSize as M, canonicalExtensionJson as N, packBundleFiles as O, sha256Hex as P, ArkmeOutgoingCallError as Q, ARKME_EXTENSION_MAX_BYTES as R, ArkmeBundleArtifactError as S, canonicalBundleJson as T, ARKME_DEFAULT_SHARE_WEBSITE as U, ArkmeService as V, ARKME_PROVIDER_CONTRACT_VERSION as W, isArkmeBotAvatarRef as X, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES as Y, ArkmePluginError as Z, verifyExtensionResolutionSignature as _, persistentArkmeExtensionActive as a, ARKME_BUNDLE_MAX_BYTES as b, quarantinePersistentExtension as c, writePersistentExtensionActivation as d, renderArkmeBundleClientBundle as f, verifyBundleResolutionSignature as g, arkmeClientContentDigestFromSource as h, invokePersistentArkmeExtension as i, renderArkmeSandboxHostEntry as j, packLocalNativeBundleDirectoryV3 as k, readPersistentExtensionActivation as l, arkmeClientContentDigest as m, applyPersistentArkmeHostExtension as n, persistentArkmeExtensionRuntimeState as o, activeProfileExtensionOwnerConflicts as p, ARKME_VOICEPRINT_ENROLLMENT_MIN_DURATION_MS as q, deactivatePersistentArkmeExtension as r, materializePersistentExtensionBundle as s, activatePersistentArkmeExtension as t, refreshPersistentClientWrapper as u, verifyNativeBundleResolutionSignature as v, bundleSha256 as w, ARKME_NATIVE_BUNDLE_ARTIFACT_KIND as x, ARKME_BUNDLE_ARTIFACT_KIND as y, ARKME_EXTENSION_PREVIEW_MAX_BYTES as z };
19771
+ export { isArkmeBotAvatarRef as $, inspectNativeBundleArtifactV3 as A, ARKME_EXTENSION_ICON_MAX_BYTES as B, ARKME_BUNDLE_MAX_BYTES as C, bundleSha256 as D, arkmeSandboxEntryId as E, assertExtensionArtifactSize as F, validateVoiceprintPcm16Wav as G, ARKME_EXTENSION_PREVIEW_MAX_BYTES as H, canonicalExtensionJson as I, ARKME_VOICEPRINT_ENROLLMENT_MAX_AUDIO_BYTES as J, ARKME_DEFAULT_SHARE_WEBSITE as K, sha256Hex as L, packLocalNativeBundleDirectoryV3 as M, readNativeBundleTarballV3 as N, canonicalBundleJson as O, renderArkmeSandboxHostEntry as P, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES as Q, unpackArkmeExtension as R, ARKME_BUNDLE_ARTIFACT_KIND as S, ArkmeBundleArtifactError as T, ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE as U, ARKME_EXTENSION_MAX_BYTES as V, ArkmeService as W, ARKME_VOICEPRINT_ENROLLMENT_MIN_DURATION_MS as X, ARKME_VOICEPRINT_ENROLLMENT_MAX_DURATION_MS as Y, ARKME_WORLD_PUBLISH_MAX_IMAGES as Z, arkmeClientContentDigest as _, persistentArkmeExtensionActive as a, verifyExtensionResolutionSignature as b, persistentExtensionBundleDirectory as c, readPersistentExtensionActivation as d, ArkmePluginError as et, refreshPersistentClientWrapper as f, activeProfileExtensionOwnerConflicts as g, renderArkmeBundleClientBundle as h, invokePersistentArkmeExtension as i, packBundleFiles as j, inspectBundleArtifact as k, persistentExtensionPackageIdentity as l, writePersistentExtensionActivation as m, applyPersistentArkmeHostExtension as n, persistentArkmeExtensionRuntimeState as o, repairPersistentExtensionInstallation as p, ARKME_PROVIDER_CONTRACT_VERSION as q, deactivatePersistentArkmeExtension as r, materializePersistentExtensionBundle as s, activatePersistentArkmeExtension as t, ArkmeOutgoingCallError as tt, quarantinePersistentExtension as u, arkmeClientContentDigestFromSource as v, ARKME_NATIVE_BUNDLE_ARTIFACT_KIND as w, verifyNativeBundleResolutionSignature as x, verifyBundleResolutionSignature as y, ARKME_EXTENSION_FORMAT as z };