@senguoyun/dsh-arkme 0.1.39 → 0.1.41

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 (106) hide show
  1. package/cordis.patch.yml +4 -0
  2. package/docs/account-session-isolation.md +21 -0
  3. package/lib/client.js +630 -288
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +3710 -429
  6. package/lib/persistent-extension.js +1 -1
  7. package/lib/{persistent-runtime-DLrd2RMI.js → persistent-runtime-B1gJH2Nx.js} +706 -281
  8. package/lib/sdk.js +31 -0
  9. package/lib/types/account-session-owner.d.ts +49 -0
  10. package/lib/types/account-session-owner.d.ts.map +1 -0
  11. package/lib/types/arkme-service.d.ts +7 -1
  12. package/lib/types/arkme-service.d.ts.map +1 -1
  13. package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
  14. package/lib/types/client/ArkmeRichText.d.ts +2 -1
  15. package/lib/types/client/ArkmeRichText.d.ts.map +1 -1
  16. package/lib/types/client/ArkmeSettingsSurface.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  18. package/lib/types/client/ArkmeUpdateSurfaces.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeVirtualWorkspace.d.ts +3 -0
  20. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  21. package/lib/types/client/OpenApiMcpSettings.d.ts +9 -0
  22. package/lib/types/client/OpenApiMcpSettings.d.ts.map +1 -0
  23. package/lib/types/client/arkme-login-errors.d.ts.map +1 -1
  24. package/lib/types/client/arkme-login-locales.d.ts +1 -0
  25. package/lib/types/client/arkme-login-locales.d.ts.map +1 -1
  26. package/lib/types/client/user-ban.d.ts +8 -0
  27. package/lib/types/client/user-ban.d.ts.map +1 -0
  28. package/lib/types/dsh-remote/api-proxy-adapter.d.ts +117 -1
  29. package/lib/types/dsh-remote/api-proxy-adapter.d.ts.map +1 -1
  30. package/lib/types/dsh-remote/channel-manager.d.ts.map +1 -1
  31. package/lib/types/dsh-remote/command-ledger.d.ts +2 -0
  32. package/lib/types/dsh-remote/command-ledger.d.ts.map +1 -1
  33. package/lib/types/dsh-remote/control-plane.d.ts +5 -0
  34. package/lib/types/dsh-remote/control-plane.d.ts.map +1 -1
  35. package/lib/types/dsh-remote/crypto.d.ts +2 -0
  36. package/lib/types/dsh-remote/crypto.d.ts.map +1 -1
  37. package/lib/types/dsh-remote/dsh-event-contract.d.ts +3 -0
  38. package/lib/types/dsh-remote/dsh-event-contract.d.ts.map +1 -1
  39. package/lib/types/dsh-remote/host.d.ts +35 -0
  40. package/lib/types/dsh-remote/host.d.ts.map +1 -1
  41. package/lib/types/dsh-remote/presentation.d.ts +12 -0
  42. package/lib/types/dsh-remote/presentation.d.ts.map +1 -0
  43. package/lib/types/dsh-remote/protocol-v1.d.ts +8 -1
  44. package/lib/types/dsh-remote/protocol-v1.d.ts.map +1 -1
  45. package/lib/types/dsh-remote/realtime-transport.d.ts.map +1 -1
  46. package/lib/types/dsh-remote/session-ownership-store.d.ts +39 -0
  47. package/lib/types/dsh-remote/session-ownership-store.d.ts.map +1 -0
  48. package/lib/types/dsh-remote/turn-projector.d.ts +26 -0
  49. package/lib/types/dsh-remote/turn-projector.d.ts.map +1 -0
  50. package/lib/types/dsh-remote/types.d.ts +76 -2
  51. package/lib/types/dsh-remote/types.d.ts.map +1 -1
  52. package/lib/types/host-api.d.ts +3 -1
  53. package/lib/types/host-api.d.ts.map +1 -1
  54. package/lib/types/index.d.ts +4 -0
  55. package/lib/types/index.d.ts.map +1 -1
  56. package/lib/types/managed-access-credential.d.ts +14 -0
  57. package/lib/types/managed-access-credential.d.ts.map +1 -0
  58. package/lib/types/managed-ai/adapter.d.ts +2 -5
  59. package/lib/types/managed-ai/adapter.d.ts.map +1 -1
  60. package/lib/types/openapi-mcp/bounded-response.d.ts +2 -0
  61. package/lib/types/openapi-mcp/bounded-response.d.ts.map +1 -0
  62. package/lib/types/openapi-mcp/control-plane.d.ts +17 -0
  63. package/lib/types/openapi-mcp/control-plane.d.ts.map +1 -0
  64. package/lib/types/openapi-mcp/controller.d.ts +100 -0
  65. package/lib/types/openapi-mcp/controller.d.ts.map +1 -0
  66. package/lib/types/openapi-mcp/credential-store.d.ts +14 -0
  67. package/lib/types/openapi-mcp/credential-store.d.ts.map +1 -0
  68. package/lib/types/openapi-mcp/execution-fence.d.ts +5 -0
  69. package/lib/types/openapi-mcp/execution-fence.d.ts.map +1 -0
  70. package/lib/types/openapi-mcp/manifest-source.d.ts +14 -0
  71. package/lib/types/openapi-mcp/manifest-source.d.ts.map +1 -0
  72. package/lib/types/openapi-mcp/mcp-runtime.d.ts +15 -0
  73. package/lib/types/openapi-mcp/mcp-runtime.d.ts.map +1 -0
  74. package/lib/types/openapi-mcp/reconcile-lock.d.ts +8 -0
  75. package/lib/types/openapi-mcp/reconcile-lock.d.ts.map +1 -0
  76. package/lib/types/openapi-mcp/session-observer.d.ts +19 -0
  77. package/lib/types/openapi-mcp/session-observer.d.ts.map +1 -0
  78. package/lib/types/openapi-mcp/status-tool.d.ts +4 -0
  79. package/lib/types/openapi-mcp/status-tool.d.ts.map +1 -0
  80. package/lib/types/openapi-mcp/types.d.ts +73 -0
  81. package/lib/types/openapi-mcp/types.d.ts.map +1 -0
  82. package/lib/types/sdk/index.d.ts +14 -2
  83. package/lib/types/sdk/index.d.ts.map +1 -1
  84. package/lib/types/services/chat-service.d.ts +6 -0
  85. package/lib/types/services/chat-service.d.ts.map +1 -1
  86. package/lib/types/services/desktop-attention-bridge.d.ts +2 -1
  87. package/lib/types/services/desktop-attention-bridge.d.ts.map +1 -1
  88. package/lib/types/services/service.d.ts +10 -1
  89. package/lib/types/services/service.d.ts.map +1 -1
  90. package/lib/types/services/user-ban-service.d.ts +21 -0
  91. package/lib/types/services/user-ban-service.d.ts.map +1 -0
  92. package/lib/types/tools/business/conversation/index.d.ts.map +1 -1
  93. package/lib/types/tools/business/conversation/user-ban.d.ts +5 -0
  94. package/lib/types/tools/business/conversation/user-ban.d.ts.map +1 -0
  95. package/lib/types/tools/business/index.d.ts.map +1 -1
  96. package/lib/types/tools/ports/index.d.ts +3 -2
  97. package/lib/types/tools/ports/index.d.ts.map +1 -1
  98. package/lib/types/tools/ports/user-ban.d.ts +7 -0
  99. package/lib/types/tools/ports/user-ban.d.ts.map +1 -0
  100. package/lib/types/tools/prompts/business.d.ts.map +1 -1
  101. package/lib/types/tools/registry/registrar.d.ts.map +1 -1
  102. package/lib/types/types.d.ts +35 -1
  103. package/lib/types/types.d.ts.map +1 -1
  104. package/package.json +14 -5
  105. package/lib/types/managed-ai/credential.d.ts +0 -11
  106. package/lib/types/managed-ai/credential.d.ts.map +0 -1
@@ -27,6 +27,357 @@ var __exportAll = (all, no_symbols) => {
27
27
  return target;
28
28
  };
29
29
  //#endregion
30
+ //#region src/services/desktop-attention-bridge.ts
31
+ const DESKTOP_BRIDGE_TIMEOUT_MS = 2e3;
32
+ const DESKTOP_NOTIFICATION_RETRY_MS = 250;
33
+ const MAX_NOTIFICATION_TITLE_LENGTH = 128;
34
+ const MAX_NOTIFICATION_BODY_LENGTH = 512;
35
+ const MAX_NATIVE_BADGE_COUNT = 999999;
36
+ function nonEmptyBounded(value, maxLength) {
37
+ const normalized = value?.trim() ?? "";
38
+ return normalized !== "" && normalized.length <= maxLength ? normalized : void 0;
39
+ }
40
+ function isLoopbackHostname(hostname) {
41
+ return hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
42
+ }
43
+ /**
44
+ * Resolve the native bridge only when the complete, client-injected contract is
45
+ * present. Partial or non-loopback configuration is rejected as unavailable;
46
+ * no credential is ever sent to a caller-controlled origin.
47
+ */
48
+ function arkmeDesktopBridgeConfigFromEnv(env) {
49
+ const rawEndpoint = env.ARKME_DESKTOP_BRIDGE_URL?.trim() ?? "";
50
+ const token = nonEmptyBounded(env.ARKME_DESKTOP_BRIDGE_TOKEN, 512);
51
+ const sessionId = nonEmptyBounded(env.ARKME_DESKTOP_BRIDGE_SESSION_ID, 160);
52
+ if (rawEndpoint === "" && token === void 0 && sessionId === void 0) return void 0;
53
+ if (rawEndpoint === "" || token === void 0 || token.length < 16 || /\s/.test(token) || sessionId === void 0 || sessionId.length < 8 || !/^[A-Za-z0-9._:-]+$/.test(sessionId)) return void 0;
54
+ let endpoint;
55
+ try {
56
+ endpoint = new URL(rawEndpoint);
57
+ } catch {
58
+ return;
59
+ }
60
+ if (endpoint.protocol !== "http:" || !isLoopbackHostname(endpoint.hostname) || endpoint.port === "" || endpoint.username !== "" || endpoint.password !== "" || endpoint.pathname !== "/v1/actions" || endpoint.search !== "" || endpoint.hash !== "") return void 0;
61
+ return {
62
+ endpoint: endpoint.toString(),
63
+ token,
64
+ sessionId
65
+ };
66
+ }
67
+ function responseObject(value) {
68
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
69
+ }
70
+ async function requestArkmeDesktopBridge(config, fetchImpl, action, payload) {
71
+ const response = await fetchImpl(config.endpoint, {
72
+ method: "POST",
73
+ headers: {
74
+ authorization: `Bearer ${config.token}`,
75
+ "content-type": "application/json"
76
+ },
77
+ body: JSON.stringify({
78
+ schemaVersion: 1,
79
+ sessionId: config.sessionId,
80
+ action,
81
+ payload
82
+ }),
83
+ signal: AbortSignal.timeout(DESKTOP_BRIDGE_TIMEOUT_MS)
84
+ });
85
+ if (!response.ok) throw new Error(`desktop bridge returned HTTP ${String(response.status)}`);
86
+ const body = responseObject(await response.json());
87
+ const value = responseObject(body?.value);
88
+ if (body?.ok !== true || value === void 0) throw new Error("desktop bridge returned an invalid response");
89
+ return value;
90
+ }
91
+ function outcomeFromValue(value) {
92
+ if (typeof value.accepted !== "boolean") return void 0;
93
+ return value.outcome === "accepted" || value.outcome === "duplicate" || value.outcome === "unsupported" || value.outcome === "native-failed" || value.outcome === "expired" || value.outcome === "rate-limited" ? value.outcome : void 0;
94
+ }
95
+ var ArkmeDesktopAttentionBridge = class {
96
+ config;
97
+ fetchImpl;
98
+ capabilities;
99
+ badgeRevision = 0;
100
+ badgeSourceRevision = 0;
101
+ badgeGeneration;
102
+ lastBadgeCount;
103
+ badgeMutationTail = Promise.resolve();
104
+ constructor(config = arkmeDesktopBridgeConfigFromEnv(process.env), fetchImpl = fetch, badgeGeneration = Number(process.hrtime.bigint() / 1000n)) {
105
+ this.config = config;
106
+ this.fetchImpl = fetchImpl;
107
+ this.badgeGeneration = Number.isSafeInteger(badgeGeneration) && badgeGeneration > 0 ? badgeGeneration : Math.max(1, Date.now() * 1e3);
108
+ }
109
+ available() {
110
+ return this.config !== void 0;
111
+ }
112
+ async showNotification(payload) {
113
+ const capabilities = await this.readCapabilities(true);
114
+ if (capabilities?.notificationShow !== true) return {
115
+ fallbackToBrowser: true,
116
+ outcome: capabilities === void 0 ? "unavailable" : "unsupported"
117
+ };
118
+ const boundedPayload = {
119
+ ...payload,
120
+ presentation: {
121
+ title: payload.presentation.title.trim().slice(0, MAX_NOTIFICATION_TITLE_LENGTH),
122
+ body: payload.presentation.body.trim().slice(0, MAX_NOTIFICATION_BODY_LENGTH)
123
+ }
124
+ };
125
+ for (let attempt = 0; attempt < 2; attempt += 1) {
126
+ try {
127
+ const outcome = outcomeFromValue(await this.request("notification.show", boundedPayload)) ?? "native-failed";
128
+ if (outcome !== "native-failed" || attempt > 0) return {
129
+ fallbackToBrowser: false,
130
+ outcome
131
+ };
132
+ } catch {
133
+ if (attempt > 0) return {
134
+ fallbackToBrowser: false,
135
+ outcome: "native-failed"
136
+ };
137
+ }
138
+ await new Promise((resolve) => {
139
+ setTimeout(resolve, DESKTOP_NOTIFICATION_RETRY_MS);
140
+ });
141
+ }
142
+ return {
143
+ fallbackToBrowser: false,
144
+ outcome: "native-failed"
145
+ };
146
+ }
147
+ async applyBadgeSummary(summary) {
148
+ const count = Math.min(MAX_NATIVE_BADGE_COUNT, Math.max(0, Math.trunc(Number.isFinite(summary.count) ? summary.count : 0)));
149
+ const revision = Math.trunc(summary.revision);
150
+ if (!Number.isSafeInteger(revision) || revision <= 0) return false;
151
+ const pending = this.badgeMutationTail.then(async () => await this.applyBadgeSnapshotSerial(count, revision));
152
+ this.badgeMutationTail = pending.then(() => void 0, () => void 0);
153
+ return await pending;
154
+ }
155
+ async resetBadgeCount() {
156
+ const pending = this.badgeMutationTail.then(async () => {
157
+ this.badgeGeneration += 1;
158
+ this.badgeRevision = 0;
159
+ this.badgeSourceRevision = 0;
160
+ this.lastBadgeCount = void 0;
161
+ for (let attempt = 0; attempt < 3; attempt += 1) if (await this.applyBadgeSnapshotSerial(0, 1)) return true;
162
+ return false;
163
+ });
164
+ this.badgeMutationTail = pending.then(() => void 0, () => void 0);
165
+ return await pending;
166
+ }
167
+ async applyBadgeSnapshotSerial(count, sourceRevision) {
168
+ if (sourceRevision < this.badgeSourceRevision || sourceRevision === this.badgeSourceRevision && count === this.lastBadgeCount) return true;
169
+ const capabilities = await this.readCapabilities();
170
+ if (capabilities === void 0) return this.config === void 0;
171
+ if (capabilities.badgeApplySnapshot.mode === "unsupported") return true;
172
+ const revision = Math.max(sourceRevision, this.badgeRevision + 1);
173
+ try {
174
+ const outcome = outcomeFromValue(await this.request("badge.applySnapshot", {
175
+ generation: this.badgeGeneration,
176
+ revision,
177
+ count
178
+ }));
179
+ if (outcome !== "accepted" && outcome !== "duplicate") return false;
180
+ this.badgeRevision = revision;
181
+ this.badgeSourceRevision = sourceRevision;
182
+ this.lastBadgeCount = count;
183
+ return true;
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
188
+ async readCapabilities(refresh = false) {
189
+ if (this.config === void 0) return void 0;
190
+ if (!refresh && this.capabilities !== void 0) return this.capabilities;
191
+ try {
192
+ const value = await this.request("capabilities.get", {});
193
+ if (value.schemaVersion !== 1 || value.sessionId !== this.config.sessionId) return void 0;
194
+ const capabilities = responseObject(value.capabilities);
195
+ const mode = responseObject(capabilities?.badgeApplySnapshot)?.mode;
196
+ if (capabilities?.notificationShow !== true && capabilities?.notificationShow !== false) return void 0;
197
+ if (mode !== "count" && mode !== "dot" && mode !== "unsupported") return void 0;
198
+ this.capabilities = {
199
+ notificationShow: capabilities.notificationShow,
200
+ badgeApplySnapshot: { mode }
201
+ };
202
+ return this.capabilities;
203
+ } catch {
204
+ return;
205
+ }
206
+ }
207
+ async request(action, payload) {
208
+ const config = this.config;
209
+ if (config === void 0) throw new Error("desktop bridge unavailable");
210
+ return await requestArkmeDesktopBridge(config, this.fetchImpl, action, payload);
211
+ }
212
+ };
213
+ //#endregion
214
+ //#region src/account-session-owner.ts
215
+ var ArkmeAccountSessionOwner = class {
216
+ store;
217
+ bridge;
218
+ bridgeRequired;
219
+ readyIdentity;
220
+ startTask;
221
+ mutationTail = Promise.resolve();
222
+ listeners = /* @__PURE__ */ new Set();
223
+ guestConversationProbe = async () => true;
224
+ scopeCloseBarrier = async () => void 0;
225
+ constructor(store, bridge, bridgeRequired = false) {
226
+ this.store = store;
227
+ this.bridge = bridge;
228
+ this.bridgeRequired = bridgeRequired;
229
+ }
230
+ start() {
231
+ this.startTask ??= this.attestCurrent();
232
+ return this.startTask;
233
+ }
234
+ attachGuestConversationProbe(probe) {
235
+ this.guestConversationProbe = probe;
236
+ }
237
+ attachScopeCloseBarrier(barrier) {
238
+ this.scopeCloseBarrier = barrier;
239
+ }
240
+ async write(session) {
241
+ await this.start();
242
+ await this.serial(async () => {
243
+ const current = await this.store.read();
244
+ if (current?.userId === session.userId) {
245
+ await this.store.write(session);
246
+ return;
247
+ }
248
+ const claimCurrentGuest = current === void 0 ? await this.guestConversationProbe() : void 0;
249
+ await this.transition({
250
+ kind: "account",
251
+ userId: session.userId,
252
+ ...claimCurrentGuest === void 0 ? {} : { claimCurrentGuest }
253
+ }, async () => {
254
+ await this.store.write(session);
255
+ });
256
+ });
257
+ }
258
+ async delete() {
259
+ await this.start();
260
+ await this.serial(async () => {
261
+ if (await this.store.read() === void 0) return;
262
+ await this.transition({ kind: "guest" }, async () => {
263
+ await this.store.delete();
264
+ });
265
+ });
266
+ }
267
+ async scopedSession() {
268
+ if (this.startTask === void 0) return void 0;
269
+ await this.startTask;
270
+ const session = await this.store.read();
271
+ if (this.bridge === void 0 && !this.bridgeRequired) this.setReady(identityKey(session === void 0 ? { kind: "guest" } : {
272
+ kind: "account",
273
+ userId: session.userId
274
+ }));
275
+ return session !== void 0 && this.readyIdentity === identityKey({
276
+ kind: "account",
277
+ userId: session.userId
278
+ }) ? session : void 0;
279
+ }
280
+ ready() {
281
+ return this.readyIdentity !== void 0;
282
+ }
283
+ subscribe(listener) {
284
+ this.listeners.add(listener);
285
+ listener();
286
+ return () => {
287
+ this.listeners.delete(listener);
288
+ };
289
+ }
290
+ async attestCurrent() {
291
+ const session = await this.store.read();
292
+ const identity = session === void 0 ? { kind: "guest" } : {
293
+ kind: "account",
294
+ userId: session.userId
295
+ };
296
+ if (this.bridge === void 0) {
297
+ if (this.bridgeRequired) throw new Error("Arkme desktop account scope bridge is required");
298
+ this.setReady(identityKey(identity));
299
+ return;
300
+ }
301
+ const result = await this.bridge.attest(identity);
302
+ this.setReady(result.status === "ready" ? identityKey(identity) : void 0);
303
+ }
304
+ async transition(identity, mutate) {
305
+ if (this.bridge === void 0) {
306
+ if (this.bridgeRequired) throw new Error("Arkme desktop account scope bridge is required");
307
+ await mutate();
308
+ this.setReady(identityKey(identity));
309
+ return;
310
+ }
311
+ const previousIdentity = this.readyIdentity;
312
+ const prepared = await this.bridge.prepare(identity);
313
+ this.setReady(void 0);
314
+ try {
315
+ await this.scopeCloseBarrier();
316
+ await mutate();
317
+ } catch (error) {
318
+ try {
319
+ await this.bridge.abort(prepared.transitionRef);
320
+ } finally {
321
+ this.setReady(previousIdentity);
322
+ }
323
+ throw error;
324
+ }
325
+ const committed = await this.bridge.commit(prepared.transitionRef);
326
+ this.setReady(committed.status === "ready" ? identityKey(identity) : void 0);
327
+ }
328
+ async serial(action) {
329
+ const pending = this.mutationTail.then(action);
330
+ this.mutationTail = pending.catch(() => void 0);
331
+ await pending;
332
+ }
333
+ setReady(identity) {
334
+ if (this.readyIdentity === identity) return;
335
+ this.readyIdentity = identity;
336
+ for (const listener of this.listeners) listener();
337
+ }
338
+ };
339
+ function createArkmeAccountSessionOwner(store, fetchImpl, env = process.env) {
340
+ const required = env.ARKME_ACCOUNT_SCOPE_REQUIRED === "1";
341
+ const config = required ? arkmeDesktopBridgeConfigFromEnv(env) : void 0;
342
+ return new ArkmeAccountSessionOwner(store, config === void 0 ? void 0 : new HttpArkmeAccountScopeBridge(config, fetchImpl), required);
343
+ }
344
+ var HttpArkmeAccountScopeBridge = class {
345
+ config;
346
+ fetchImpl;
347
+ constructor(config, fetchImpl) {
348
+ this.config = config;
349
+ this.fetchImpl = fetchImpl;
350
+ }
351
+ async attest(identity) {
352
+ return status(await this.request("account.scope.attest", identity));
353
+ }
354
+ async prepare(identity) {
355
+ const value = await this.request("account.scope.prepare", identity);
356
+ const transitionRef = typeof value.transitionRef === "string" ? value.transitionRef : "";
357
+ if (!/^[A-Za-z0-9._:-]{8,160}$/.test(transitionRef)) throw new Error("Desktop account scope prepare response is invalid");
358
+ return { transitionRef };
359
+ }
360
+ async commit(transitionRef) {
361
+ return status(await this.request("account.scope.commit", { transitionRef }));
362
+ }
363
+ async abort(transitionRef) {
364
+ if (status(await this.request("account.scope.abort", { transitionRef })).status !== "ready") throw new Error("Desktop account scope abort response is invalid");
365
+ return { status: "ready" };
366
+ }
367
+ async request(action, payload) {
368
+ return await requestArkmeDesktopBridge(this.config, this.fetchImpl, action, payload);
369
+ }
370
+ };
371
+ function status(value) {
372
+ if (value.status !== "ready" && value.status !== "relaunch") throw new Error("Desktop account scope response is invalid");
373
+ return { status: value.status };
374
+ }
375
+ function identityKey(identity) {
376
+ if (identity.kind === "guest") return "guest";
377
+ if (!Number.isSafeInteger(identity.userId) || identity.userId <= 0) throw new Error("Arkme account scope identity is invalid");
378
+ return `account:${String(identity.userId)}`;
379
+ }
380
+ //#endregion
30
381
  //#region src/secret-value.ts
31
382
  var SecretValue = class {
32
383
  #value;
@@ -38,15 +389,15 @@ var SecretValue = class {
38
389
  }
39
390
  };
40
391
  //#endregion
41
- //#region src/managed-ai/credential.ts
392
+ //#region src/managed-access-credential.ts
42
393
  const MAX_JWT_PAYLOAD_CHARS = 16 * 1024;
43
- /** Resolve the Host-owned bearer, refreshing a near-expiry JWT before it reaches the relay. */
394
+ /** Resolve a current Host-owned Access credential without exposing refresh credentials to consumers. */
44
395
  async function resolveManagedAccessCredential(runtime, refreshWindowMillis = 6e4) {
45
396
  let session = await runtime.requireSession();
46
397
  if (managedAccessTokenExpiresWithin(session.accessToken, refreshWindowMillis)) session = await runtime.refreshAccessToken(session);
47
398
  return new SecretValue(session.accessToken);
48
399
  }
49
- /** Treat a parseable JWT expiry as a refresh hint; opaque access tokens remain usable. */
400
+ /** Treat a parseable JWT expiry as a refresh hint; opaque Access credentials remain usable. */
50
401
  function managedAccessTokenExpiresWithin(accessToken, windowMillis, nowMillis = Date.now()) {
51
402
  const payload = accessToken.split(".")[1];
52
403
  if (payload === void 0 || payload === "" || payload.length > MAX_JWT_PAYLOAD_CHARS) return false;
@@ -848,13 +1199,37 @@ var ServiceRuntime = class {
848
1199
  pendingSessionStore;
849
1200
  requestCoordinator = new ArkmeRequestCoordinator();
850
1201
  refreshInFlightByUserId = /* @__PURE__ */ new Map();
1202
+ accountSessions;
851
1203
  pendingBindingSession;
852
- constructor(config, sessionStore, stateStore, fetchImpl = fetch, pendingSessionStore) {
1204
+ constructor(config, sessionStore, stateStore, fetchImpl = fetch, pendingSessionStore, accountSessions) {
853
1205
  this.config = config;
854
1206
  this.sessionStore = sessionStore;
855
1207
  this.stateStore = stateStore;
856
1208
  this.fetchImpl = fetchImpl;
857
1209
  this.pendingSessionStore = pendingSessionStore;
1210
+ this.accountSessions = accountSessions ?? new ArkmeAccountSessionOwner(sessionStore);
1211
+ }
1212
+ async startAccountScope() {
1213
+ await this.accountSessions.start();
1214
+ }
1215
+ attachGuestConversationProbe(probe) {
1216
+ this.accountSessions.attachGuestConversationProbe(probe);
1217
+ }
1218
+ accountScopeReady() {
1219
+ return this.accountSessions.ready();
1220
+ }
1221
+ subscribeAccountScope(listener) {
1222
+ return this.accountSessions.subscribe(listener);
1223
+ }
1224
+ async accountScopedSession() {
1225
+ await this.accountSessions.start();
1226
+ return await this.accountSessions.scopedSession();
1227
+ }
1228
+ async writeSession(session) {
1229
+ await this.accountSessions.write(session);
1230
+ }
1231
+ async deleteSession() {
1232
+ await this.accountSessions.delete();
858
1233
  }
859
1234
  requestStats() {
860
1235
  return this.requestCoordinator.snapshotStats();
@@ -873,7 +1248,7 @@ var ServiceRuntime = class {
873
1248
  this.requestCoordinator.dispose();
874
1249
  }
875
1250
  async requireSession() {
876
- const session = await this.sessionStore.read();
1251
+ const session = await this.accountScopedSession();
877
1252
  if (session === void 0) throw new ArkmePluginError("login-required", "请先登录 Arkme", false, 401);
878
1253
  return session;
879
1254
  }
@@ -920,12 +1295,17 @@ var ServiceRuntime = class {
920
1295
  accessToken
921
1296
  };
922
1297
  if (this.isPendingBindingSession(session)) await this.writePendingBindingSession(updated);
923
- else await this.sessionStore.write(updated);
1298
+ else await this.writeSession(updated);
924
1299
  return updated;
925
1300
  } catch (error) {
926
- if (error instanceof ArkmePluginError && ["auth-http-401", "auth-http-403"].includes(error.code)) {
1301
+ if (error instanceof ArkmePluginError && error.code === "arkme-code-1004") {
927
1302
  if (this.isPendingBindingSession(session)) await this.clearPendingBindingSession();
928
1303
  else await this.sessionStore.delete();
1304
+ throw new ArkmePluginError("account-unavailable", "当前即我账号暂不可用", false, 403);
1305
+ }
1306
+ if (error instanceof ArkmePluginError && ["auth-http-401", "auth-http-403"].includes(error.code)) {
1307
+ if (this.isPendingBindingSession(session)) await this.clearPendingBindingSession();
1308
+ else await this.deleteSession();
929
1309
  throw new ArkmePluginError("login-expired", "Arkme 登录已过期,请重新扫码", false, 401);
930
1310
  }
931
1311
  throw error;
@@ -11630,7 +12010,7 @@ function optionalString$3(value) {
11630
12010
  function optionalBooleanValue$1(value) {
11631
12011
  return typeof value === "boolean" ? value : void 0;
11632
12012
  }
11633
- function optionalRecord(value) {
12013
+ function optionalRecord$1(value) {
11634
12014
  const record = objectValue$1(value);
11635
12015
  return Object.keys(record).length === 0 ? void 0 : record;
11636
12016
  }
@@ -11755,7 +12135,7 @@ function arkoRunProjectionFromData(data) {
11755
12135
  if (runUid === "" || status === "") return void 0;
11756
12136
  const errorCode = stringValue$1(data.agent_run_error_code ?? data.error_code).trim();
11757
12137
  const retryOfRunUid = stringValue$1(data.retry_of_run_uid).trim();
11758
- const clientAction = optionalRecord(data.agent_client_action ?? data.client_action);
12138
+ const clientAction = optionalRecord$1(data.agent_client_action ?? data.client_action);
11759
12139
  return {
11760
12140
  runUid,
11761
12141
  status,
@@ -11770,7 +12150,7 @@ function arkoCreatedRecordUids(value) {
11770
12150
  }
11771
12151
  function arkoStreamFrame(raw) {
11772
12152
  try {
11773
- return optionalRecord(JSON.parse(raw));
12153
+ return optionalRecord$1(JSON.parse(raw));
11774
12154
  } catch {
11775
12155
  return;
11776
12156
  }
@@ -11930,7 +12310,7 @@ var ArkoService = class {
11930
12310
  const normalizedRunUid = stringValue$1(data.run_uid).trim() || runUid.trim();
11931
12311
  const sequence = numberValue$28(data.sequence);
11932
12312
  const surfaceAssistantMsgId = numberValue$28(data.surface_assistant_msg_id);
11933
- const clientAction = optionalRecord(data.client_action);
12313
+ const clientAction = optionalRecord$1(data.client_action);
11934
12314
  if (normalizedRunUid !== runUid.trim() || !ARKO_RUN_STATUSES.has(status) || !Number.isSafeInteger(sequence) || sequence < 0 || !Number.isSafeInteger(surfaceAssistantMsgId) || surfaceAssistantMsgId <= 0) throw new ArkmePluginError("arko-run-status-contract-invalid", "Arko 运行状态响应不完整", true, 502);
11935
12315
  return {
11936
12316
  sessionId,
@@ -12116,12 +12496,12 @@ var ArkoService = class {
12116
12496
  } else if (booleanValue$14(tail.error) || stringValue$1(tail.status).trim() === "error") status = "failed";
12117
12497
  else status = ARKO_COMPLETED_STATUS;
12118
12498
  createdRecordUids = arkoCreatedRecordUids(tail.created_record_uids);
12119
- const nextProfile = optionalRecord(tail.agent_profile);
12499
+ const nextProfile = optionalRecord$1(tail.agent_profile);
12120
12500
  if (nextProfile !== void 0) profile = arkoProfileFromData(nextProfile);
12121
12501
  if (booleanValue$14(tail.error) || stringValue$1(tail.status).trim() === "error") errorMessage = stringValue$1(tail.error_message).trim() || "Arko 执行失败";
12122
12502
  }
12123
12503
  const type = stringValue$1(frame.type ?? frame.event).trim();
12124
- const payload = optionalRecord(frame.payload) ?? frame;
12504
+ const payload = optionalRecord$1(frame.payload) ?? frame;
12125
12505
  if (type === "message.delta") text += stringValue$1(payload.text ?? payload.content);
12126
12506
  else if (type === "message.final") {
12127
12507
  const finalText = stringValue$1(payload.text ?? payload.content);
@@ -12135,7 +12515,7 @@ var ArkoService = class {
12135
12515
  status = doneRun.status;
12136
12516
  } else status = ARKO_COMPLETED_STATUS;
12137
12517
  createdRecordUids = arkoCreatedRecordUids(payload.created_record_uids);
12138
- const nextProfile = optionalRecord(payload.agent_profile);
12518
+ const nextProfile = optionalRecord$1(payload.agent_profile);
12139
12519
  if (nextProfile !== void 0) profile = arkoProfileFromData(nextProfile);
12140
12520
  } else if (type === "error") {
12141
12521
  terminal = true;
@@ -12624,7 +13004,7 @@ var AuthService = class {
12624
13004
  this.runtime.invalidateScope(this.runtime.requestScope(userId));
12625
13005
  this.runtime.clearRefreshForUser(userId);
12626
13006
  }
12627
- await this.runtime.sessionStore.delete();
13007
+ await this.runtime.deleteSession();
12628
13008
  await this.runtime.clearPendingBindingSession();
12629
13009
  this.profile.invalidate();
12630
13010
  this.jiwoAttemptGeneration += 1;
@@ -12648,7 +13028,7 @@ var AuthService = class {
12648
13028
  };
12649
13029
  if (snapshot.status === "binding-required") {
12650
13030
  await this.runtime.writePendingBindingSession(activeSession);
12651
- await this.runtime.sessionStore.delete();
13031
+ await this.runtime.deleteSession();
12652
13032
  this.lifecycle.reconnectChatRealtime();
12653
13033
  }
12654
13034
  return snapshot;
@@ -12930,12 +13310,12 @@ var AuthService = class {
12930
13310
  const snapshot = await this.authSnapshotForSession(session, { forceProfile: true });
12931
13311
  if (snapshot.status === "authenticated") {
12932
13312
  await this.runtime.clearPendingBindingSession();
12933
- await this.runtime.sessionStore.write(session);
13313
+ await this.runtime.writeSession(session);
12934
13314
  this.lifecycle.reconnectChatRealtime();
12935
13315
  return snapshot;
12936
13316
  }
12937
13317
  await this.runtime.writePendingBindingSession(session);
12938
- await this.runtime.sessionStore.delete();
13318
+ await this.runtime.deleteSession();
12939
13319
  this.lifecycle.reconnectChatRealtime();
12940
13320
  return snapshot;
12941
13321
  }
@@ -17051,187 +17431,6 @@ var ArkmeChatRealtimeRuntime = class {
17051
17431
  }
17052
17432
  };
17053
17433
  //#endregion
17054
- //#region src/services/desktop-attention-bridge.ts
17055
- const DESKTOP_BRIDGE_TIMEOUT_MS = 2e3;
17056
- const DESKTOP_NOTIFICATION_RETRY_MS = 250;
17057
- const MAX_NOTIFICATION_TITLE_LENGTH = 128;
17058
- const MAX_NOTIFICATION_BODY_LENGTH = 512;
17059
- const MAX_NATIVE_BADGE_COUNT = 999999;
17060
- function nonEmptyBounded(value, maxLength) {
17061
- const normalized = value?.trim() ?? "";
17062
- return normalized !== "" && normalized.length <= maxLength ? normalized : void 0;
17063
- }
17064
- function isLoopbackHostname(hostname) {
17065
- return hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
17066
- }
17067
- /**
17068
- * Resolve the native bridge only when the complete, client-injected contract is
17069
- * present. Partial or non-loopback configuration is rejected as unavailable;
17070
- * no credential is ever sent to a caller-controlled origin.
17071
- */
17072
- function arkmeDesktopBridgeConfigFromEnv(env) {
17073
- const rawEndpoint = env.ARKME_DESKTOP_BRIDGE_URL?.trim() ?? "";
17074
- const token = nonEmptyBounded(env.ARKME_DESKTOP_BRIDGE_TOKEN, 512);
17075
- const sessionId = nonEmptyBounded(env.ARKME_DESKTOP_BRIDGE_SESSION_ID, 160);
17076
- if (rawEndpoint === "" && token === void 0 && sessionId === void 0) return void 0;
17077
- if (rawEndpoint === "" || token === void 0 || token.length < 16 || /\s/.test(token) || sessionId === void 0 || sessionId.length < 8 || !/^[A-Za-z0-9._:-]+$/.test(sessionId)) return void 0;
17078
- let endpoint;
17079
- try {
17080
- endpoint = new URL(rawEndpoint);
17081
- } catch {
17082
- return;
17083
- }
17084
- if (endpoint.protocol !== "http:" || !isLoopbackHostname(endpoint.hostname) || endpoint.port === "" || endpoint.username !== "" || endpoint.password !== "" || endpoint.pathname !== "/v1/actions" || endpoint.search !== "" || endpoint.hash !== "") return void 0;
17085
- return {
17086
- endpoint: endpoint.toString(),
17087
- token,
17088
- sessionId
17089
- };
17090
- }
17091
- function responseObject(value) {
17092
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
17093
- }
17094
- function outcomeFromValue(value) {
17095
- if (typeof value.accepted !== "boolean") return void 0;
17096
- return value.outcome === "accepted" || value.outcome === "duplicate" || value.outcome === "unsupported" || value.outcome === "native-failed" || value.outcome === "expired" || value.outcome === "rate-limited" ? value.outcome : void 0;
17097
- }
17098
- var ArkmeDesktopAttentionBridge = class {
17099
- config;
17100
- fetchImpl;
17101
- capabilities;
17102
- badgeRevision = 0;
17103
- badgeSourceRevision = 0;
17104
- badgeGeneration;
17105
- lastBadgeCount;
17106
- badgeMutationTail = Promise.resolve();
17107
- constructor(config = arkmeDesktopBridgeConfigFromEnv(process.env), fetchImpl = fetch, badgeGeneration = Number(process.hrtime.bigint() / 1000n)) {
17108
- this.config = config;
17109
- this.fetchImpl = fetchImpl;
17110
- this.badgeGeneration = Number.isSafeInteger(badgeGeneration) && badgeGeneration > 0 ? badgeGeneration : Math.max(1, Date.now() * 1e3);
17111
- }
17112
- available() {
17113
- return this.config !== void 0;
17114
- }
17115
- async showNotification(payload) {
17116
- const capabilities = await this.readCapabilities(true);
17117
- if (capabilities?.notificationShow !== true) return {
17118
- fallbackToBrowser: true,
17119
- outcome: capabilities === void 0 ? "unavailable" : "unsupported"
17120
- };
17121
- const boundedPayload = {
17122
- ...payload,
17123
- presentation: {
17124
- title: payload.presentation.title.trim().slice(0, MAX_NOTIFICATION_TITLE_LENGTH),
17125
- body: payload.presentation.body.trim().slice(0, MAX_NOTIFICATION_BODY_LENGTH)
17126
- }
17127
- };
17128
- for (let attempt = 0; attempt < 2; attempt += 1) {
17129
- try {
17130
- const outcome = outcomeFromValue(await this.request("notification.show", boundedPayload)) ?? "native-failed";
17131
- if (outcome !== "native-failed" || attempt > 0) return {
17132
- fallbackToBrowser: false,
17133
- outcome
17134
- };
17135
- } catch {
17136
- if (attempt > 0) return {
17137
- fallbackToBrowser: false,
17138
- outcome: "native-failed"
17139
- };
17140
- }
17141
- await new Promise((resolve) => {
17142
- setTimeout(resolve, DESKTOP_NOTIFICATION_RETRY_MS);
17143
- });
17144
- }
17145
- return {
17146
- fallbackToBrowser: false,
17147
- outcome: "native-failed"
17148
- };
17149
- }
17150
- async applyBadgeSummary(summary) {
17151
- const count = Math.min(MAX_NATIVE_BADGE_COUNT, Math.max(0, Math.trunc(Number.isFinite(summary.count) ? summary.count : 0)));
17152
- const revision = Math.trunc(summary.revision);
17153
- if (!Number.isSafeInteger(revision) || revision <= 0) return false;
17154
- const pending = this.badgeMutationTail.then(async () => await this.applyBadgeSnapshotSerial(count, revision));
17155
- this.badgeMutationTail = pending.then(() => void 0, () => void 0);
17156
- return await pending;
17157
- }
17158
- async resetBadgeCount() {
17159
- const pending = this.badgeMutationTail.then(async () => {
17160
- this.badgeGeneration += 1;
17161
- this.badgeRevision = 0;
17162
- this.badgeSourceRevision = 0;
17163
- this.lastBadgeCount = void 0;
17164
- for (let attempt = 0; attempt < 3; attempt += 1) if (await this.applyBadgeSnapshotSerial(0, 1)) return true;
17165
- return false;
17166
- });
17167
- this.badgeMutationTail = pending.then(() => void 0, () => void 0);
17168
- return await pending;
17169
- }
17170
- async applyBadgeSnapshotSerial(count, sourceRevision) {
17171
- if (sourceRevision < this.badgeSourceRevision || sourceRevision === this.badgeSourceRevision && count === this.lastBadgeCount) return true;
17172
- const capabilities = await this.readCapabilities();
17173
- if (capabilities === void 0) return this.config === void 0;
17174
- if (capabilities.badgeApplySnapshot.mode === "unsupported") return true;
17175
- const revision = Math.max(sourceRevision, this.badgeRevision + 1);
17176
- try {
17177
- const outcome = outcomeFromValue(await this.request("badge.applySnapshot", {
17178
- generation: this.badgeGeneration,
17179
- revision,
17180
- count
17181
- }));
17182
- if (outcome !== "accepted" && outcome !== "duplicate") return false;
17183
- this.badgeRevision = revision;
17184
- this.badgeSourceRevision = sourceRevision;
17185
- this.lastBadgeCount = count;
17186
- return true;
17187
- } catch {
17188
- return false;
17189
- }
17190
- }
17191
- async readCapabilities(refresh = false) {
17192
- if (this.config === void 0) return void 0;
17193
- if (!refresh && this.capabilities !== void 0) return this.capabilities;
17194
- try {
17195
- const value = await this.request("capabilities.get", {});
17196
- if (value.schemaVersion !== 1 || value.sessionId !== this.config.sessionId) return void 0;
17197
- const capabilities = responseObject(value.capabilities);
17198
- const mode = responseObject(capabilities?.badgeApplySnapshot)?.mode;
17199
- if (capabilities?.notificationShow !== true && capabilities?.notificationShow !== false) return void 0;
17200
- if (mode !== "count" && mode !== "dot" && mode !== "unsupported") return void 0;
17201
- this.capabilities = {
17202
- notificationShow: capabilities.notificationShow,
17203
- badgeApplySnapshot: { mode }
17204
- };
17205
- return this.capabilities;
17206
- } catch {
17207
- return;
17208
- }
17209
- }
17210
- async request(action, payload) {
17211
- const config = this.config;
17212
- if (config === void 0) throw new Error("desktop bridge unavailable");
17213
- const response = await this.fetchImpl(config.endpoint, {
17214
- method: "POST",
17215
- headers: {
17216
- authorization: `Bearer ${config.token}`,
17217
- "content-type": "application/json"
17218
- },
17219
- body: JSON.stringify({
17220
- schemaVersion: 1,
17221
- sessionId: config.sessionId,
17222
- action,
17223
- payload
17224
- }),
17225
- signal: AbortSignal.timeout(DESKTOP_BRIDGE_TIMEOUT_MS)
17226
- });
17227
- if (!response.ok) throw new Error(`desktop bridge returned HTTP ${String(response.status)}`);
17228
- const body = responseObject(await response.json());
17229
- const value = responseObject(body?.value);
17230
- if (body?.ok !== true || value === void 0) throw new Error("desktop bridge returned an invalid response");
17231
- return value;
17232
- }
17233
- };
17234
- //#endregion
17235
17434
  //#region src/services/chat-realtime-service.ts
17236
17435
  const MAX_PROJECTION_RETRIES = 5;
17237
17436
  const MAX_ATTENTION_SUMMARY_RETRIES = 5;
@@ -19177,6 +19376,57 @@ function chatExtensionTreeItemFromData(value, fallbackParentRecordUid) {
19177
19376
  ...structuredContent === void 0 ? {} : { structuredContent }
19178
19377
  };
19179
19378
  }
19379
+ function extensionMediaRecord(recordUid, contentPayload, displayItems = []) {
19380
+ return {
19381
+ record_uid: recordUid,
19382
+ content_payload: contentPayload,
19383
+ ...displayItems.length === 0 ? {} : { media_display_items: displayItems }
19384
+ };
19385
+ }
19386
+ function publicExtensionMediaRecord(value, recordUid) {
19387
+ const data = objectValue$1(value);
19388
+ const core = objectValue$1(data.record_core ?? data.recordCore);
19389
+ const contentPayload = objectValue$1(data.content_payload ?? data.contentPayload ?? core.content_payload ?? core.contentPayload);
19390
+ const mediaItems = listValue$16(data.media_items ?? data.mediaItems ?? core.media_items ?? core.mediaItems);
19391
+ const mediaRefs = listValue$16(contentPayload.media_refs ?? contentPayload.mediaRefs);
19392
+ const displayItems = [
19393
+ ...listValue$16(data.media_display_items ?? data.mediaDisplayItems),
19394
+ ...listValue$16(core.media_display_items ?? core.mediaDisplayItems),
19395
+ ...mediaItems
19396
+ ];
19397
+ return extensionMediaRecord(recordUid, {
19398
+ ...contentPayload,
19399
+ ...mediaRefs.length > 0 || mediaItems.length === 0 ? {} : { media_refs: mediaItems }
19400
+ }, displayItems);
19401
+ }
19402
+ function chatExtensionMediaRecord(value, recordUid) {
19403
+ const child = objectValue$1(value);
19404
+ const item = objectValue$1(child.item);
19405
+ const record = objectValue$1(item.record);
19406
+ const payload = objectValue$1(record.payload);
19407
+ return extensionMediaRecord(recordUid, objectValue$1(payload.content_payload ?? payload.contentPayload ?? record.content_payload ?? record.contentPayload), [
19408
+ ...listValue$16(child.media_display_items ?? child.mediaDisplayItems),
19409
+ ...listValue$16(item.media_display_items ?? item.mediaDisplayItems),
19410
+ ...listValue$16(record.media_display_items ?? record.mediaDisplayItems),
19411
+ ...listValue$16(payload.media_display_items ?? payload.mediaDisplayItems)
19412
+ ]);
19413
+ }
19414
+ function uploadedExtensionMediaRecord(recordUid, assets) {
19415
+ return extensionMediaRecord(recordUid, {
19416
+ payload_kind: 2,
19417
+ schema_version: 1,
19418
+ media_refs: assets.map((asset, index) => ({
19419
+ file_asset_uid: asset.fileAssetUid,
19420
+ content_file_role: 1,
19421
+ render_role: 1,
19422
+ sort_order: index,
19423
+ file_name: asset.fileName,
19424
+ file_kind: asset.fileKind,
19425
+ mime_type: asset.mimeType,
19426
+ size: asset.size
19427
+ }))
19428
+ });
19429
+ }
19180
19430
  function messageCopyLinkRecordContextFromData(data) {
19181
19431
  const context = objectValue$1(data.record_context ?? data.recordContext);
19182
19432
  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);
@@ -19472,6 +19722,66 @@ var ChatService = class {
19472
19722
  this.privacy = privacy;
19473
19723
  this.messageActions = messageActions;
19474
19724
  }
19725
+ async hydrateExtensionMedia(projections, session, signal) {
19726
+ const mediaProjections = projections.filter(({ extension }) => extension.mediaItems.length > 0);
19727
+ if (mediaProjections.length === 0) return projections.map(({ extension }) => extension);
19728
+ let directlyHydrated;
19729
+ try {
19730
+ directlyHydrated = new Map(mediaProjections.map(({ extension, mediaRecord }) => [extension.recordUid, this.media.richContentBlocks(mediaRecord, session.userId)]));
19731
+ } catch (error) {
19732
+ if (signal?.aborted === true) throw error;
19733
+ const mediaRecordUids = new Set(mediaProjections.map(({ extension }) => extension.recordUid));
19734
+ return projections.map(({ extension }) => mediaRecordUids.has(extension.recordUid) ? {
19735
+ ...extension,
19736
+ mediaUnavailable: true
19737
+ } : extension);
19738
+ }
19739
+ const unresolved = mediaProjections.filter(({ extension }) => (directlyHydrated.get(extension.recordUid)?.length ?? 0) === 0);
19740
+ if (unresolved.length === 0) return projections.map(({ extension }) => {
19741
+ const contentBlocks = directlyHydrated.get(extension.recordUid);
19742
+ return contentBlocks === void 0 ? extension : {
19743
+ ...extension,
19744
+ contentBlocks
19745
+ };
19746
+ });
19747
+ try {
19748
+ const media = await this.media.hydrateRecordMediaPage(unresolved.map(({ mediaRecord }) => mediaRecord), session, signal);
19749
+ const hydratedByRecordUid = new Map(mediaProjections.map(({ extension, mediaRecord }) => {
19750
+ const directContentBlocks = directlyHydrated.get(extension.recordUid) ?? [];
19751
+ const displayItems = directContentBlocks.length > 0 ? [] : media.displayItemsByRecordUid.get(extension.recordUid) ?? [];
19752
+ const contentBlocks = directContentBlocks.length > 0 ? directContentBlocks : this.media.richContentBlocks(mediaRecord, session.userId, displayItems);
19753
+ return [extension.recordUid, {
19754
+ ...extension,
19755
+ ...contentBlocks.length === 0 ? { mediaUnavailable: true } : { contentBlocks }
19756
+ }];
19757
+ }));
19758
+ return projections.map(({ extension }) => hydratedByRecordUid.get(extension.recordUid) ?? extension);
19759
+ } catch (error) {
19760
+ if (signal?.aborted === true) throw error;
19761
+ const mediaRecordUids = new Set(mediaProjections.map(({ extension }) => extension.recordUid));
19762
+ return projections.map(({ extension }) => {
19763
+ const contentBlocks = directlyHydrated.get(extension.recordUid) ?? [];
19764
+ if (contentBlocks.length > 0) return {
19765
+ ...extension,
19766
+ contentBlocks
19767
+ };
19768
+ return mediaRecordUids.has(extension.recordUid) ? {
19769
+ ...extension,
19770
+ mediaUnavailable: true
19771
+ } : extension;
19772
+ });
19773
+ }
19774
+ }
19775
+ async hydrateUploadedExtension(extension, assets, session, signal) {
19776
+ if (assets.length === 0) return extension;
19777
+ return (await this.hydrateExtensionMedia([{
19778
+ extension,
19779
+ mediaRecord: uploadedExtensionMediaRecord(extension.recordUid, assets)
19780
+ }], session, signal))[0] ?? {
19781
+ ...extension,
19782
+ mediaUnavailable: true
19783
+ };
19784
+ }
19475
19785
  async readDirectBotConversation(botId, chatSessionUid, options = {}) {
19476
19786
  const normalizedBotId = botId.trim();
19477
19787
  const normalizedSessionUid = chatSessionUid.trim();
@@ -20261,7 +20571,14 @@ var ChatService = class {
20261
20571
  });
20262
20572
  const rawItems = listValue$16(data.list ?? data.items);
20263
20573
  if (offset === 0) total = Math.max(0, integerLikeValue(data.total));
20264
- const pageItems = rawItems.map((raw) => messageCopyLinkExtensionItemFromData(raw)).filter((item) => item !== void 0);
20574
+ const pageProjections = rawItems.flatMap((raw) => {
20575
+ const extension = messageCopyLinkExtensionItemFromData(raw);
20576
+ return extension === void 0 ? [] : [{
20577
+ extension,
20578
+ mediaRecord: publicExtensionMediaRecord(raw, extension.recordUid)
20579
+ }];
20580
+ });
20581
+ const pageItems = await this.hydrateExtensionMedia(pageProjections, session, signal);
20265
20582
  extensions.push(...pageItems);
20266
20583
  if (rawItems.length === 0) break;
20267
20584
  offset += rawItems.length;
@@ -20294,7 +20611,14 @@ var ChatService = class {
20294
20611
  parent_record_uid: parentRecordUid,
20295
20612
  limit: 100
20296
20613
  }, session, signal);
20297
- const extensions = listValue$16(data.children).map((child) => chatExtensionTreeItemFromData(child, parentRecordUid)).filter((item) => item !== void 0).sort((left, right) => right.sendAtMillis - left.sendAtMillis);
20614
+ const projections = listValue$16(data.children).flatMap((child) => {
20615
+ const extension = chatExtensionTreeItemFromData(child, parentRecordUid);
20616
+ return extension === void 0 ? [] : [{
20617
+ extension,
20618
+ mediaRecord: chatExtensionMediaRecord(child, extension.recordUid)
20619
+ }];
20620
+ });
20621
+ const extensions = (await this.hydrateExtensionMedia(projections, session, signal)).sort((left, right) => right.sendAtMillis - left.sendAtMillis);
20298
20622
  const extensionCount = Math.max(integerLikeValue(data.total), extensions.length);
20299
20623
  return extensionCount <= 0 ? void 0 : {
20300
20624
  extensionCount,
@@ -20379,6 +20703,26 @@ var ChatService = class {
20379
20703
  const createdRecordUid = stringValue$1(data.child_record_uid).trim() || normalizedUid;
20380
20704
  const sequence = numberValue$19(data.seq);
20381
20705
  this.realtime.scheduleChatSessionProjection(reference.chatSessionUid, sequence);
20706
+ const extension = await this.hydrateUploadedExtension({
20707
+ recordUid: createdRecordUid,
20708
+ parentRecordUid: reference.recordUid,
20709
+ recordOwnerUserId: session.userId,
20710
+ level: extensionLevel,
20711
+ sourceKind: "record_extension",
20712
+ senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20713
+ ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20714
+ title: "",
20715
+ textContent: normalizedText,
20716
+ sendAtMillis,
20717
+ templateKind: assets.length === 0 ? 1 : 2,
20718
+ displayKind: 0,
20719
+ officialMark: 0,
20720
+ mediaItems: assets.map((asset) => ({
20721
+ fileKind: asset.fileKind,
20722
+ fileName: asset.fileName,
20723
+ size: asset.size
20724
+ }))
20725
+ }, assets, session, options.signal);
20382
20726
  return {
20383
20727
  recordUid: createdRecordUid,
20384
20728
  parentRecordUid: reference.recordUid,
@@ -20386,26 +20730,7 @@ var ChatService = class {
20386
20730
  ...sequence > 0 ? { sequence } : {},
20387
20731
  status: 1,
20388
20732
  localState: "synced",
20389
- extension: {
20390
- recordUid: createdRecordUid,
20391
- parentRecordUid: reference.recordUid,
20392
- recordOwnerUserId: session.userId,
20393
- level: extensionLevel,
20394
- sourceKind: "record_extension",
20395
- senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20396
- ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20397
- title: "",
20398
- textContent: normalizedText,
20399
- sendAtMillis,
20400
- templateKind: assets.length === 0 ? 1 : 2,
20401
- displayKind: 0,
20402
- officialMark: 0,
20403
- mediaItems: assets.map((asset) => ({
20404
- fileKind: asset.fileKind,
20405
- fileName: asset.fileName,
20406
- size: asset.size
20407
- }))
20408
- }
20733
+ extension
20409
20734
  };
20410
20735
  }
20411
20736
  if (source.kind === "topic") {
@@ -20449,32 +20774,33 @@ var ChatService = class {
20449
20774
  revision: this.realtime.nextChatClientRevision(),
20450
20775
  projection: "record"
20451
20776
  });
20777
+ const extension = await this.hydrateUploadedExtension({
20778
+ recordUid: createdRecordUid,
20779
+ parentRecordUid: reference.recordUid,
20780
+ recordOwnerUserId: session.userId,
20781
+ level: extensionLevel,
20782
+ sourceKind: "record_extension",
20783
+ senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20784
+ ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20785
+ title: "",
20786
+ textContent: normalizedText,
20787
+ sendAtMillis,
20788
+ templateKind: assets.length === 0 ? 1 : 2,
20789
+ displayKind: 0,
20790
+ officialMark: 0,
20791
+ mediaItems: assets.map((asset) => ({
20792
+ fileKind: asset.fileKind,
20793
+ fileName: asset.fileName,
20794
+ size: asset.size
20795
+ }))
20796
+ }, assets, session, options.signal);
20452
20797
  return {
20453
20798
  recordUid: createdRecordUid,
20454
20799
  parentRecordUid: createdParentUid,
20455
20800
  ...relationUid === "" ? {} : { relationUid },
20456
20801
  status: numberValue$19(data.record_status ?? data.status),
20457
20802
  localState: "synced",
20458
- extension: {
20459
- recordUid: createdRecordUid,
20460
- parentRecordUid: reference.recordUid,
20461
- recordOwnerUserId: session.userId,
20462
- level: extensionLevel,
20463
- sourceKind: "record_extension",
20464
- senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20465
- ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20466
- title: "",
20467
- textContent: normalizedText,
20468
- sendAtMillis,
20469
- templateKind: assets.length === 0 ? 1 : 2,
20470
- displayKind: 0,
20471
- officialMark: 0,
20472
- mediaItems: assets.map((asset) => ({
20473
- fileKind: asset.fileKind,
20474
- fileName: asset.fileName,
20475
- size: asset.size
20476
- }))
20477
- }
20803
+ extension
20478
20804
  };
20479
20805
  }
20480
20806
  if (source.kind === "send_to_self" || source.kind === "default_category") {
@@ -20488,32 +20814,33 @@ var ChatService = class {
20488
20814
  revision: this.realtime.nextChatClientRevision(),
20489
20815
  projection: "record"
20490
20816
  });
20817
+ const extension = await this.hydrateUploadedExtension({
20818
+ recordUid: recordResult.recordUid,
20819
+ parentRecordUid: reference.recordUid,
20820
+ recordOwnerUserId: session.userId,
20821
+ level: extensionLevel,
20822
+ sourceKind: "record_extension",
20823
+ senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20824
+ ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20825
+ title: "",
20826
+ textContent: normalizedText,
20827
+ sendAtMillis,
20828
+ templateKind: assets.length === 0 ? 1 : 2,
20829
+ displayKind: 0,
20830
+ officialMark: 0,
20831
+ mediaItems: assets.map((asset) => ({
20832
+ fileKind: asset.fileKind,
20833
+ fileName: asset.fileName,
20834
+ size: asset.size
20835
+ }))
20836
+ }, assets, session, options.signal);
20491
20837
  return {
20492
20838
  recordUid: recordResult.recordUid,
20493
20839
  parentRecordUid: reference.recordUid,
20494
20840
  status: recordResult.status,
20495
20841
  localState: recordResult.localState,
20496
20842
  ...recordResult.error === void 0 ? {} : { error: recordResult.error },
20497
- extension: {
20498
- recordUid: recordResult.recordUid,
20499
- parentRecordUid: reference.recordUid,
20500
- recordOwnerUserId: session.userId,
20501
- level: extensionLevel,
20502
- sourceKind: "record_extension",
20503
- senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20504
- ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20505
- title: "",
20506
- textContent: normalizedText,
20507
- sendAtMillis,
20508
- templateKind: assets.length === 0 ? 1 : 2,
20509
- displayKind: 0,
20510
- officialMark: 0,
20511
- mediaItems: assets.map((asset) => ({
20512
- fileKind: asset.fileKind,
20513
- fileName: asset.fileName,
20514
- size: asset.size
20515
- }))
20516
- }
20843
+ extension
20517
20844
  };
20518
20845
  }
20519
20846
  const existingExtension = (await this.loadMessageCopyLinkPublicExtensions([reference.recordUid], session, options.signal, { strict: true }))?.extensions.find((extension) => extension.recordUid === normalizedUid);
@@ -20593,29 +20920,30 @@ var ChatService = class {
20593
20920
  revision: this.realtime.nextChatClientRevision(),
20594
20921
  projection: "record"
20595
20922
  });
20923
+ const extension = await this.hydrateUploadedExtension({
20924
+ recordUid: publishedRecordUid,
20925
+ level: 2,
20926
+ sourceKind: "record_extension",
20927
+ senderDisplayName,
20928
+ ...senderAvatarUrl === "" ? {} : { senderAvatarUrl },
20929
+ title: "",
20930
+ textContent: normalizedText,
20931
+ sendAtMillis,
20932
+ templateKind: assets.length === 0 ? 1 : 2,
20933
+ displayKind: 0,
20934
+ officialMark: 0,
20935
+ mediaItems: assets.map((asset) => ({
20936
+ fileKind: asset.fileKind,
20937
+ fileName: asset.fileName,
20938
+ size: asset.size
20939
+ }))
20940
+ }, assets, session, options.signal);
20596
20941
  return {
20597
20942
  recordUid: publishedRecordUid,
20598
20943
  parentRecordUid: reference.recordUid,
20599
20944
  status: numberValue$19(data.status ?? data.check_status ?? createdRecordStatus),
20600
20945
  localState: "synced",
20601
- extension: {
20602
- recordUid: publishedRecordUid,
20603
- level: 2,
20604
- sourceKind: "record_extension",
20605
- senderDisplayName,
20606
- ...senderAvatarUrl === "" ? {} : { senderAvatarUrl },
20607
- title: "",
20608
- textContent: normalizedText,
20609
- sendAtMillis,
20610
- templateKind: assets.length === 0 ? 1 : 2,
20611
- displayKind: 0,
20612
- officialMark: 0,
20613
- mediaItems: assets.map((asset) => ({
20614
- fileKind: asset.fileKind,
20615
- fileName: asset.fileName,
20616
- size: asset.size
20617
- }))
20618
- }
20946
+ extension
20619
20947
  };
20620
20948
  }
20621
20949
  async extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options = {}) {
@@ -21997,6 +22325,17 @@ var ChatService = class {
21997
22325
  }
21998
22326
  }, session, options.signal);
21999
22327
  }
22328
+ async resolvePrivateChatPeer(sourceRef, signal) {
22329
+ const session = await this.runtime.requireSession();
22330
+ const source = await this.source.openSourceRef(sourceRef, session.userId);
22331
+ if (source.kind !== "private_chat") throw new ArkmePluginError("user-ban-private-chat-required", "封禁操作仅支持一对一私聊用户", false);
22332
+ const peerIds = [...new Set((await this.rawChatMembers(source.ownerRef, true, session, signal)).map((item) => Math.trunc(numberValue$19(item.user_id))).filter((userId) => Number.isSafeInteger(userId) && userId > 0 && userId !== session.userId))];
22333
+ if (peerIds.length !== 1) throw new ArkmePluginError("user-ban-peer-invalid", "无法确认当前私聊用户,请刷新会话后重试", true, 409);
22334
+ return {
22335
+ userId: peerIds[0],
22336
+ displayName: source.displayName
22337
+ };
22338
+ }
22000
22339
  async rawChatMembers(chatSessionUid, activeOnly, session, signal) {
22001
22340
  return listValue$16((await this.runtime.authenticatedChatPost("/api/v1/chats/members/list", {
22002
22341
  chat_session_uid: chatSessionUid,
@@ -60595,6 +60934,78 @@ var VoiceprintService = class {
60595
60934
  }
60596
60935
  };
60597
60936
  //#endregion
60937
+ //#region src/services/user-ban-service.ts
60938
+ function integer(value) {
60939
+ if (typeof value === "number" && Number.isSafeInteger(value)) return value;
60940
+ return 0;
60941
+ }
60942
+ function optionalRecord(raw, sourceRef, peer) {
60943
+ const item = objectValue$1(raw);
60944
+ if (Object.keys(item).length === 0) return void 0;
60945
+ const userId = integer(item.user_id);
60946
+ const status = integer(item.status);
60947
+ const operatorId = integer(item.operator_id);
60948
+ const bannedAtMillis = integer(item.banned_at);
60949
+ const unbannedAtMillis = integer(item.unbanned_at);
60950
+ const updatedAtMillis = integer(item.updated_at);
60951
+ if (userId !== peer.userId || status !== 1 && status !== 2 || operatorId <= 0 || bannedAtMillis < 0 || unbannedAtMillis < 0 || updatedAtMillis <= 0 || status === 1 && bannedAtMillis <= 0 || status === 2 && unbannedAtMillis <= 0) throw new ArkmePluginError("user-ban-contract-invalid", "封禁服务返回的数据不完整", true, 502);
60952
+ return {
60953
+ sourceRef,
60954
+ targetUserId: userId,
60955
+ displayName: peer.displayName,
60956
+ status: status === 1 ? "banned" : "unbanned",
60957
+ operatorUserId: operatorId,
60958
+ remark: stringValue$1(item.remark).trim(),
60959
+ bannedAtMillis,
60960
+ unbannedAtMillis,
60961
+ updatedAtMillis
60962
+ };
60963
+ }
60964
+ /** Owns employee-only user-ban behavior; Browser, SDK and Tools share this one path. */
60965
+ var UserBanService = class {
60966
+ runtime;
60967
+ peers;
60968
+ constructor(runtime, peers) {
60969
+ this.runtime = runtime;
60970
+ this.peers = peers;
60971
+ }
60972
+ async status(sourceRef, signal) {
60973
+ const peer = await this.peers.resolvePrivateChatPeer(sourceRef, signal);
60974
+ const data = await this.runtime.authenticatedAuthReadPost("/api/v1/user-ban/status", { user_id: peer.userId }, void 0, signal, { bypassCache: true });
60975
+ const exists = data.exists === true;
60976
+ const record = optionalRecord(data.item, sourceRef, peer);
60977
+ if (exists !== (record !== void 0) || data.banned !== (record?.status === "banned")) throw new ArkmePluginError("user-ban-contract-invalid", "封禁服务返回的数据不完整", true, 502);
60978
+ return {
60979
+ sourceRef,
60980
+ targetUserId: peer.userId,
60981
+ displayName: peer.displayName,
60982
+ exists,
60983
+ banned: record?.status === "banned",
60984
+ ...record === void 0 ? {} : { record }
60985
+ };
60986
+ }
60987
+ async ban(sourceRef, remark = "", signal) {
60988
+ return await this.setStatus(sourceRef, true, remark, signal);
60989
+ }
60990
+ async unban(sourceRef, remark = "", signal) {
60991
+ return await this.setStatus(sourceRef, false, remark, signal);
60992
+ }
60993
+ async setStatus(sourceRef, banned, remarkInput, signal) {
60994
+ const remark = remarkInput.trim();
60995
+ if (Array.from(remark).length > 255) throw new ArkmePluginError("user-ban-remark-invalid", "封禁备注最多 255 个字符", false);
60996
+ const peer = await this.peers.resolvePrivateChatPeer(sourceRef, signal);
60997
+ const record = optionalRecord(await this.runtime.authenticatedAuthPost(banned ? "/api/v1/user-ban/ban" : "/api/v1/user-ban/unban", {
60998
+ user_id: peer.userId,
60999
+ remark
61000
+ }, void 0, signal, {
61001
+ bypassCache: true,
61002
+ trackWriteOutcome: true
61003
+ }), sourceRef, peer);
61004
+ if (record === void 0 || record.status !== (banned ? "banned" : "unbanned")) throw new ArkmePluginError("user-ban-contract-invalid", "封禁服务返回的数据不完整", true, 502);
61005
+ return record;
61006
+ }
61007
+ };
61008
+ //#endregion
60598
61009
  //#region src/services/world-service.ts
60599
61010
  const ARKME_WORLD_IMAGE_REF_TTL_MILLIS = 900 * 1e3;
60600
61011
  const MAX_ARKME_WORLD_IMAGE_REFS = 2048;
@@ -61779,6 +62190,7 @@ var ArkmeService = class {
61779
62190
  fetchImpl;
61780
62191
  pendingSessionStore;
61781
62192
  runtime;
62193
+ accountScope;
61782
62194
  billingGateway;
61783
62195
  aiVideo;
61784
62196
  arrangement;
@@ -61813,6 +62225,7 @@ var ArkmeService = class {
61813
62225
  contactDirectory;
61814
62226
  unmarkedSpeaker;
61815
62227
  voiceprint;
62228
+ userBan;
61816
62229
  backgroundSoundPreferenceOwner;
61817
62230
  fileTransfers;
61818
62231
  localFileOpener;
@@ -61823,7 +62236,8 @@ var ArkmeService = class {
61823
62236
  this.stateStore = stateStore;
61824
62237
  this.fetchImpl = fetchImpl;
61825
62238
  this.pendingSessionStore = pendingSessionStore;
61826
- this.runtime = new ServiceRuntime(config, sessionStore, stateStore, fetchImpl, pendingSessionStore);
62239
+ this.accountScope = createArkmeAccountSessionOwner(sessionStore, fetchImpl);
62240
+ this.runtime = new ServiceRuntime(config, sessionStore, stateStore, fetchImpl, pendingSessionStore, this.accountScope);
61827
62241
  this.billingGateway = billingGateway ?? new HttpArkmeBillingGateway(this.runtime);
61828
62242
  this.privacy = new ArkmePrivacyVisibilityService(this.runtime);
61829
62243
  this.aiVideo = new AiVideoService(this.runtime);
@@ -61864,6 +62278,7 @@ var ArkmeService = class {
61864
62278
  this.aiPolish = new GroupAiPolishService(this.runtime, this.source, { sendChatSourceTextRaw: async (...args) => await this.chat.sendChatSourceTextRaw(...args) });
61865
62279
  this.realtime = new ChatRealtimeService(this.runtime, this.source, { chatTimelineItems: async (data, session, chatSessionUid) => await this.chat.chatTimelineItems(data, session, chatSessionUid) });
61866
62280
  this.chat = new ChatService(this.runtime, this.source, this.profile, this.media, this.record, this.bot, this.arko, this.aiPolish, this.realtime, this.privacy, this.messageActions);
62281
+ this.userBan = new UserBanService(this.runtime, this.chat);
61867
62282
  this.botConversation = new BotConversationService(this.runtime, this.bot, this.chat, async () => {
61868
62283
  await this.realtime.invalidateRecordProjection();
61869
62284
  }, this.messageActions);
@@ -62134,6 +62549,7 @@ var ArkmeService = class {
62134
62549
  sourceTextSend: true,
62135
62550
  messageReadReceipts: true,
62136
62551
  messageReport: true,
62552
+ userBanManagement: true,
62137
62553
  richContentRead: this.config.richMediaRenderEnabled !== false,
62138
62554
  richContentSend: this.config.richMediaSendEnabled !== false,
62139
62555
  ...this.config.richMediaSendEnabled === false ? {} : { backgroundSound: true },
@@ -62530,6 +62946,15 @@ var ArkmeService = class {
62530
62946
  async userCard(userId, signal) {
62531
62947
  return await this.profile.userCard(userId, signal);
62532
62948
  }
62949
+ async userBanStatus(sourceRef, signal) {
62950
+ return await this.userBan.status(sourceRef, signal);
62951
+ }
62952
+ async banPrivateChatUser(sourceRef, remark = "", signal) {
62953
+ return await this.userBan.ban(sourceRef, remark, signal);
62954
+ }
62955
+ async unbanPrivateChatUser(sourceRef, remark = "", signal) {
62956
+ return await this.userBan.unban(sourceRef, remark, signal);
62957
+ }
62533
62958
  async openPrivateChatFromUser(peerUserId, options = {}) {
62534
62959
  return await this.chat.openPrivateChatFromUser(peerUserId, options);
62535
62960
  }
@@ -64514,4 +64939,4 @@ async function invokePersistentArkmeExtension(extensionId, method, args) {
64514
64939
  return jsonValue(await handler(jsonValue(args)));
64515
64940
  }
64516
64941
  //#endregion
64517
- export { ARKME_VOICEPRINT_ENROLLMENT_MIN_DURATION_MS 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, arkmeRichBackgroundSound as J, isRecordingInstantOnOrAfterUnixEpoch as K, sha256Hex as L, packLocalNativeBundleDirectoryV3 as M, readNativeBundleTarballV3 as N, canonicalBundleJson as O, renderArkmeSandboxHostEntry as P, ARKME_VOICEPRINT_ENROLLMENT_MAX_DURATION_MS 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_PROVIDER_CONTRACT_VERSION as X, ARKME_DEFAULT_SHARE_WEBSITE as Y, ARKME_VOICEPRINT_ENROLLMENT_MAX_AUDIO_BYTES as Z, arkmeClientContentDigest as _, persistentArkmeExtensionActive as a, arkmeNormalizedFileMimeType as at, verifyExtensionResolutionSignature as b, persistentExtensionBundleDirectory as c, recordingImportFileKind as ct, readPersistentExtensionActivation as d, ArkmePluginError as dt, ARKME_WORLD_PUBLISH_MAX_IMAGES as et, refreshPersistentClientWrapper as f, ArkmeOutgoingCallError as ft, activeProfileExtensionOwnerConflicts as g, renderArkmeBundleClientBundle as h, invokePersistentArkmeExtension as i, arkmeCanInlineLocalFile as it, packBundleFiles as j, inspectBundleArtifact as k, persistentExtensionPackageIdentity as l, sameRecordingImportIdentity as lt, writePersistentExtensionActivation as m, applyPersistentArkmeHostExtension as n, ARKME_RUNTIME_INSTANCE_ID as nt, persistentArkmeExtensionRuntimeState as o, arkmePickedFileKind as ot, repairPersistentExtensionInstallation as p, arkmeFileBackgroundSound as q, deactivatePersistentArkmeExtension as r, ARKME_TOOL_FILE_MAX_BYTES as rt, materializePersistentExtensionBundle as s, isArkmeBotAvatarRef as st, activatePersistentArkmeExtension as t, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES as tt, quarantinePersistentExtension as u, MAX_RECORDING_IMPORT_BYTES as ut, arkmeClientContentDigestFromSource as v, ARKME_NATIVE_BUNDLE_ARTIFACT_KIND as w, verifyNativeBundleResolutionSignature as x, verifyBundleResolutionSignature as y, ARKME_EXTENSION_FORMAT as z };
64942
+ export { ARKME_VOICEPRINT_ENROLLMENT_MIN_DURATION_MS 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, arkmeRichBackgroundSound as J, isRecordingInstantOnOrAfterUnixEpoch as K, sha256Hex as L, packLocalNativeBundleDirectoryV3 as M, readNativeBundleTarballV3 as N, canonicalBundleJson as O, renderArkmeSandboxHostEntry as P, ARKME_VOICEPRINT_ENROLLMENT_MAX_DURATION_MS 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_PROVIDER_CONTRACT_VERSION as X, ARKME_DEFAULT_SHARE_WEBSITE as Y, ARKME_VOICEPRINT_ENROLLMENT_MAX_AUDIO_BYTES as Z, arkmeClientContentDigest as _, persistentArkmeExtensionActive as a, arkmeNormalizedFileMimeType as at, verifyExtensionResolutionSignature as b, persistentExtensionBundleDirectory as c, recordingImportFileKind as ct, readPersistentExtensionActivation as d, ArkmePluginError as dt, ARKME_WORLD_PUBLISH_MAX_IMAGES as et, refreshPersistentClientWrapper as f, ArkmeOutgoingCallError as ft, activeProfileExtensionOwnerConflicts as g, renderArkmeBundleClientBundle as h, invokePersistentArkmeExtension as i, arkmeCanInlineLocalFile as it, packBundleFiles as j, inspectBundleArtifact as k, persistentExtensionPackageIdentity as l, sameRecordingImportIdentity as lt, writePersistentExtensionActivation as m, applyPersistentArkmeHostExtension as n, ARKME_RUNTIME_INSTANCE_ID as nt, persistentArkmeExtensionRuntimeState as o, arkmePickedFileKind as ot, repairPersistentExtensionInstallation as p, SecretValue as pt, arkmeFileBackgroundSound as q, deactivatePersistentArkmeExtension as r, ARKME_TOOL_FILE_MAX_BYTES as rt, materializePersistentExtensionBundle as s, isArkmeBotAvatarRef as st, activatePersistentArkmeExtension as t, ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES as tt, quarantinePersistentExtension as u, MAX_RECORDING_IMPORT_BYTES as ut, arkmeClientContentDigestFromSource as v, ARKME_NATIVE_BUNDLE_ARTIFACT_KIND as w, verifyNativeBundleResolutionSignature as x, verifyBundleResolutionSignature as y, ARKME_EXTENSION_FORMAT as z };