@senguoyun/dsh-arkme 0.1.82 → 0.1.83

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 (74) hide show
  1. package/docs/ai-points-implementation.md +43 -0
  2. package/docs/interwoven-compact-summary-regression-2026-09-28.md +35 -0
  3. package/docs/interwoven-original-read-receipt-regression-2026-09-28.md +50 -0
  4. package/docs/personal-detail-consistency-regression-2026-09-28.md +40 -0
  5. package/docs/send-to-self-roles-cross-client-api-handoff-2026-09-27.md +9 -0
  6. package/lib/client.js +2181 -1710
  7. package/lib/client.js.map +1 -1
  8. package/lib/harness-model-client.js +123 -37
  9. package/lib/harness-native-selection-client.js +57 -1
  10. package/lib/harness-sidebar-client.js +11 -0
  11. package/lib/index.js +194 -56
  12. package/lib/persistent-extension.js +1 -1
  13. package/lib/{persistent-runtime-DV9Hme-U.js → persistent-runtime-B6vPAaiG.js} +507 -42
  14. package/lib/sdk.js +11 -0
  15. package/lib/types/ai-points.d.ts +61 -0
  16. package/lib/types/ai-points.d.ts.map +1 -0
  17. package/lib/types/arkme-service.d.ts +10 -0
  18. package/lib/types/arkme-service.d.ts.map +1 -1
  19. package/lib/types/billing-gateway.d.ts.map +1 -1
  20. package/lib/types/client/ArkmeAccountUsage.d.ts +1 -3
  21. package/lib/types/client/ArkmeAccountUsage.d.ts.map +1 -1
  22. package/lib/types/client/ArkmeArkoSurface.d.ts.map +1 -1
  23. package/lib/types/client/ArkmeBillingSettings.d.ts +3 -2
  24. package/lib/types/client/ArkmeBillingSettings.d.ts.map +1 -1
  25. package/lib/types/client/ArkmeDataManagementSettings.d.ts.map +1 -1
  26. package/lib/types/client/ArkmeDocumentComposerInput.d.ts.map +1 -1
  27. package/lib/types/client/ArkmeEmojiPicker.d.ts.map +1 -1
  28. package/lib/types/client/ArkmeInterwovenReadReceipt.d.ts +12 -0
  29. package/lib/types/client/ArkmeInterwovenReadReceipt.d.ts.map +1 -0
  30. package/lib/types/client/ArkmeModelSelect.d.ts.map +1 -1
  31. package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
  32. package/lib/types/client/ArkmePointsConsumption.d.ts +5 -0
  33. package/lib/types/client/ArkmePointsConsumption.d.ts.map +1 -0
  34. package/lib/types/client/ArkmeRelatedQuickNotes.d.ts.map +1 -1
  35. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  36. package/lib/types/client/ArkoModelMenu.d.ts.map +1 -1
  37. package/lib/types/client/api.d.ts +1 -1
  38. package/lib/types/client/api.d.ts.map +1 -1
  39. package/lib/types/client/interwoven-moments.d.ts.map +1 -1
  40. package/lib/types/client/interwoven-read-receipt-store.d.ts +32 -0
  41. package/lib/types/client/interwoven-read-receipt-store.d.ts.map +1 -0
  42. package/lib/types/client/locales/actions-en.d.ts.map +1 -1
  43. package/lib/types/client/locales/dynamic-en.d.ts.map +1 -1
  44. package/lib/types/client/self-role-presentation.d.ts +6 -1
  45. package/lib/types/client/self-role-presentation.d.ts.map +1 -1
  46. package/lib/types/client/use-personal-extension-parents.d.ts +4 -0
  47. package/lib/types/client/use-personal-extension-parents.d.ts.map +1 -0
  48. package/lib/types/host-api.d.ts.map +1 -1
  49. package/lib/types/index.d.ts.map +1 -1
  50. package/lib/types/managed-ai/adapter.d.ts +2 -1
  51. package/lib/types/managed-ai/adapter.d.ts.map +1 -1
  52. package/lib/types/managed-ai/operation.d.ts +17 -0
  53. package/lib/types/managed-ai/operation.d.ts.map +1 -0
  54. package/lib/types/managed-ai/transport.d.ts +1 -0
  55. package/lib/types/managed-ai/transport.d.ts.map +1 -1
  56. package/lib/types/record-extension-tree.d.ts +2 -0
  57. package/lib/types/record-extension-tree.d.ts.map +1 -1
  58. package/lib/types/sdk/index.d.ts +4 -0
  59. package/lib/types/sdk/index.d.ts.map +1 -1
  60. package/lib/types/services/ai-points-service.d.ts +13 -0
  61. package/lib/types/services/ai-points-service.d.ts.map +1 -0
  62. package/lib/types/services/arko-service.d.ts.map +1 -1
  63. package/lib/types/services/chat-service.d.ts +9 -1
  64. package/lib/types/services/chat-service.d.ts.map +1 -1
  65. package/lib/types/services/interwoven-service.d.ts +3 -1
  66. package/lib/types/services/interwoven-service.d.ts.map +1 -1
  67. package/lib/types/tools/business/account/ai-points.d.ts +4 -0
  68. package/lib/types/tools/business/account/ai-points.d.ts.map +1 -0
  69. package/lib/types/tools/business/account/index.d.ts.map +1 -1
  70. package/lib/types/tools/ports/profile.d.ts +3 -0
  71. package/lib/types/tools/ports/profile.d.ts.map +1 -1
  72. package/lib/types/types.d.ts +17 -3
  73. package/lib/types/types.d.ts.map +1 -1
  74. package/package.json +5 -5
@@ -389,7 +389,7 @@ function object$8(value) {
389
389
  function keys(value, allowed) {
390
390
  if (Object.keys(value).some((key) => !allowed.includes(key))) fail$1();
391
391
  }
392
- function text$11(value, max) {
392
+ function text$12(value, max) {
393
393
  if (typeof value !== "string" || value.length > max) fail$1();
394
394
  }
395
395
  function integer$6(value) {
@@ -415,7 +415,7 @@ function parseArrangementBoardCachePages(value) {
415
415
  const board = object$8(page.board);
416
416
  keys(board, ["supported", "version"]);
417
417
  if (typeof board.supported !== "boolean") fail$1();
418
- text$11(board.version, 1024);
418
+ text$12(board.version, 1024);
419
419
  }
420
420
  const refs = /* @__PURE__ */ new Set();
421
421
  for (const rawItem of page.items) {
@@ -436,13 +436,13 @@ function parseArrangementBoardCachePages(value) {
436
436
  ]);
437
437
  if (item.status !== column || typeof item.reminderEnabled !== "boolean" || typeof item.arrangementRef !== "string" || !/^arkme-arrangement-v1\.[A-Za-z0-9_-]{43}$/.test(item.arrangementRef) || refs.has(item.arrangementRef)) fail$1();
438
438
  refs.add(item.arrangementRef);
439
- text$11(item.title, 2e4);
440
- text$11(item.description, 2e5);
441
- text$11(item.reminderState, 256);
439
+ text$12(item.title, 2e4);
440
+ text$12(item.description, 2e5);
441
+ text$12(item.reminderState, 256);
442
442
  integer$6(item.createdAtMillis);
443
443
  integer$6(item.updatedAtMillis);
444
444
  for (const key of ["dueAtMillis", "remindAtMillis"]) if (item[key] !== void 0) integer$6(item[key]);
445
- if (item.recognitionState !== void 0) text$11(item.recognitionState, 256);
445
+ if (item.recognitionState !== void 0) text$12(item.recognitionState, 256);
446
446
  if (item.creationSource !== void 0) {
447
447
  const source = object$8(item.creationSource);
448
448
  keys(source, [
@@ -459,7 +459,7 @@ function parseArrangementBoardCachePages(value) {
459
459
  for (const rawSource of source.items) {
460
460
  const entry = object$8(rawSource);
461
461
  keys(entry, ["text", "createdAtMillis"]);
462
- text$11(entry.text, 2e5);
462
+ text$12(entry.text, 2e5);
463
463
  if (entry.createdAtMillis !== void 0) integer$6(entry.createdAtMillis);
464
464
  }
465
465
  }
@@ -2024,7 +2024,11 @@ var ServiceRuntime = class {
2024
2024
  try {
2025
2025
  return await this.post(this.config.intelligentBaseUrl, path, body, session.accessToken, [200], signal, true, requestOptions());
2026
2026
  } catch (error) {
2027
- if (!(error instanceof ArkmePluginError) || !["auth-http-401", "auth-http-403"].includes(error.code)) throw error;
2027
+ if (!(error instanceof ArkmePluginError) || ![
2028
+ "auth-http-401",
2029
+ "auth-http-403",
2030
+ "invalid_access_token"
2031
+ ].includes(error.code)) throw error;
2028
2032
  session = await this.refreshAccessToken(session);
2029
2033
  return await this.post(this.config.intelligentBaseUrl, path, body, session.accessToken, [200], signal, true, requestOptions());
2030
2034
  }
@@ -2049,6 +2053,180 @@ var ServiceRuntime = class {
2049
2053
  }
2050
2054
  };
2051
2055
  //#endregion
2056
+ //#region src/ai-points.ts
2057
+ function pointsUnits(value) {
2058
+ if (!/^(?:0|[1-9]\d*)(?:\.\d{1,7})?$/.test(value)) throw new TypeError("Invalid exact AI points");
2059
+ const [whole = "0", fraction = ""] = value.split(".");
2060
+ const units = BigInt(whole) * 10000000n + BigInt(fraction.padEnd(7, "0"));
2061
+ if (units > 9223372036854775807n) throw new TypeError("AI points exceed accounting range");
2062
+ return units;
2063
+ }
2064
+ /** Existing recharge values retain their nano-CNY meaning: ¥1 = 100 points. */
2065
+ function nanoCnyToPoints(value) {
2066
+ if (!/^(?:0|[1-9]\d*)$/.test(value)) throw new TypeError("Invalid nano-CNY");
2067
+ const units = BigInt(value);
2068
+ const whole = units / 10000000n;
2069
+ const fraction = (units % 10000000n).toString().padStart(7, "0").replace(/0+$/, "");
2070
+ return fraction ? `${whole}.${fraction}` : whole.toString();
2071
+ }
2072
+ /** The server pages whole operations. Call-level charges remain the only facts. */
2073
+ function groupPointsConsumption(items) {
2074
+ const groups = /* @__PURE__ */ new Map();
2075
+ for (const item of items) {
2076
+ const key = item.businessCode === "agent" && item.operationUid ? `agent:${item.operationUid}` : `request:${item.requestUid}`;
2077
+ const calls = groups.get(key) ?? [];
2078
+ calls.push(item);
2079
+ groups.set(key, calls);
2080
+ }
2081
+ return [...groups].map(([key, calls]) => ({
2082
+ key,
2083
+ calls,
2084
+ chargedPoints: nanoCnyToPoints(calls.reduce((sum, call) => sum + pointsUnits(call.chargedPoints), 0n).toString())
2085
+ }));
2086
+ }
2087
+ //#endregion
2088
+ //#region src/services/ai-points-service.ts
2089
+ function invalid$12() {
2090
+ throw new ArkmePluginError("ai-points-contract-invalid", "积分数据暂时无法确认,请重试", true, 502);
2091
+ }
2092
+ function points(value) {
2093
+ if (typeof value !== "string") return invalid$12();
2094
+ try {
2095
+ pointsUnits(value);
2096
+ } catch {
2097
+ return invalid$12();
2098
+ }
2099
+ return value;
2100
+ }
2101
+ function timestamp$1(value) {
2102
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) return invalid$12();
2103
+ return value;
2104
+ }
2105
+ function text$11(value) {
2106
+ if (typeof value !== "string" || !value.trim()) return invalid$12();
2107
+ return value;
2108
+ }
2109
+ function integerString(value) {
2110
+ if (typeof value !== "string" || !/^(?:0|[1-9]\d*)$/.test(value)) return invalid$12();
2111
+ return value;
2112
+ }
2113
+ function unit(raw) {
2114
+ if (raw.unit !== "ai_points") invalid$12();
2115
+ }
2116
+ function parseAiPointsAccount(raw, accountScope) {
2117
+ unit(raw);
2118
+ if (raw.points_per_cny !== 100 || !Array.isArray(raw.grants)) return invalid$12();
2119
+ const result = {
2120
+ accountScope,
2121
+ unit: "ai_points",
2122
+ availablePoints: points(raw.available_points),
2123
+ reservedPoints: points(raw.reserved_points),
2124
+ grantedPoints: points(raw.granted_points),
2125
+ purchasedPoints: points(raw.purchased_points),
2126
+ observedAt: timestamp$1(raw.observed_at),
2127
+ grants: raw.grants.map((value) => {
2128
+ const row = objectValue$2(value);
2129
+ return {
2130
+ source: text$11(row.source),
2131
+ availablePoints: points(row.available_points),
2132
+ expiresAt: timestamp$1(row.expires_at)
2133
+ };
2134
+ })
2135
+ };
2136
+ if (pointsUnits(result.availablePoints) !== pointsUnits(result.grantedPoints) + pointsUnits(result.purchasedPoints) || result.grants.reduce((total, grant) => total + pointsUnits(grant.availablePoints), 0n) !== pointsUnits(result.grantedPoints)) return invalid$12();
2137
+ return result;
2138
+ }
2139
+ function parseAiPointsPage(raw, accountScope, month) {
2140
+ unit(raw);
2141
+ if (raw.month !== month || !Array.isArray(raw.items)) return invalid$12();
2142
+ const nextBeforeId = raw.next_before_id === "" ? "" : integerString(raw.next_before_id);
2143
+ const result = {
2144
+ accountScope,
2145
+ unit: "ai_points",
2146
+ month,
2147
+ chargedPoints: points(raw.charged_points),
2148
+ nextBeforeId,
2149
+ items: raw.items.map((value) => {
2150
+ const row = objectValue$2(value), tokens = objectValue$2(row.tokens);
2151
+ const chargedPoints = points(row.charged_points), grantedPoints = points(row.granted_points), purchasedPoints = points(row.purchased_points);
2152
+ if (pointsUnits(chargedPoints) <= 0n || pointsUnits(chargedPoints) !== pointsUnits(grantedPoints) + pointsUnits(purchasedPoints)) return invalid$12();
2153
+ const modelPoints = points(row.model_points);
2154
+ if (!Array.isArray(row.services) || row.services.length > 16) return invalid$12();
2155
+ const services = row.services.map((value) => {
2156
+ const service = objectValue$2(value);
2157
+ const quantity = integerString(service.quantity), chargedPoints = points(service.charged_points);
2158
+ if (quantity === "0" || pointsUnits(chargedPoints) === 0n) return invalid$12();
2159
+ return {
2160
+ code: text$11(service.code),
2161
+ quantity,
2162
+ chargedPoints
2163
+ };
2164
+ });
2165
+ if (new Set(services.map((service) => service.code)).size !== services.length || pointsUnits(modelPoints) + services.reduce((sum, service) => sum + pointsUnits(service.chargedPoints), 0n) !== pointsUnits(chargedPoints)) return invalid$12();
2166
+ if (typeof row.business_code !== "string") return invalid$12();
2167
+ return {
2168
+ operationUid: typeof row.operation_uid === "string" ? row.operation_uid : "",
2169
+ requestUid: text$11(row.request_uid),
2170
+ businessCode: row.business_code,
2171
+ model: text$11(row.model),
2172
+ chargedPoints,
2173
+ modelPoints,
2174
+ services,
2175
+ grantedPoints,
2176
+ purchasedPoints,
2177
+ createdAt: timestamp$1(row.created_at),
2178
+ completedAt: timestamp$1(row.completed_at),
2179
+ tokens: {
2180
+ cacheHitInput: integerString(tokens.cache_hit_input),
2181
+ cacheMissInput: integerString(tokens.cache_miss_input),
2182
+ output: integerString(tokens.output)
2183
+ }
2184
+ };
2185
+ })
2186
+ };
2187
+ if (new Set(result.items.map((item) => item.requestUid)).size !== result.items.length || groupPointsConsumption(result.items).length > 20) return invalid$12();
2188
+ return result;
2189
+ }
2190
+ /** One owner for UI, SDK and Tools. It never reads raw usage telemetry. */
2191
+ var AiPointsService = class {
2192
+ runtime;
2193
+ constructor(runtime) {
2194
+ this.runtime = runtime;
2195
+ }
2196
+ async session(expectedScope) {
2197
+ const session = await this.runtime.requireSession();
2198
+ const scope = `${this.runtime.config.environment}:${session.userId}`;
2199
+ if (expectedScope !== void 0 && expectedScope !== scope) throw new ArkmePluginError("ai-points-account-changed", "账号已切换,请重新读取积分", false, 409);
2200
+ return {
2201
+ session,
2202
+ scope
2203
+ };
2204
+ }
2205
+ async account(expectedScope, signal) {
2206
+ const { session, scope } = await this.session(expectedScope);
2207
+ const raw = await this.runtime.authenticatedIntelligentPost("/api/v1/managed-ai/points/query", {}, session, signal, {
2208
+ lane: "interactive-read",
2209
+ bypassCache: true
2210
+ });
2211
+ await this.session(scope);
2212
+ return parseAiPointsAccount(raw, scope);
2213
+ }
2214
+ async consumption(query, expectedScope, signal) {
2215
+ if (!/^\d{4}-(?:0[1-9]|1[0-2])$/.test(query.month) || query.beforeId !== void 0 && !/^[1-9]\d*$/.test(query.beforeId)) throw new ArkmePluginError("ai-points-query-invalid", "请选择有效月份", false, 400);
2216
+ const { session, scope } = await this.session(expectedScope);
2217
+ const raw = await this.runtime.authenticatedIntelligentPost("/api/v1/managed-ai/points/consumption/query", {
2218
+ month: query.month,
2219
+ before_id: query.beforeId ?? "",
2220
+ limit: 20
2221
+ }, session, signal, {
2222
+ lane: "interactive-read",
2223
+ bypassCache: true
2224
+ });
2225
+ await this.session(scope);
2226
+ return parseAiPointsPage(raw, scope, query.month);
2227
+ }
2228
+ };
2229
+ //#endregion
2052
2230
  //#region src/self-role-avatar-store.ts
2053
2231
  const MAX_SELF_ROLE_AVATAR_BYTES = 8 * 1024 * 1024;
2054
2232
  const PREFIX = "arkme-self-role-image-v1.";
@@ -3147,6 +3325,13 @@ function errorCode$1(status, body) {
3147
3325
  const providerCode = typeof providerError?.code === "string" ? providerError.code.toLowerCase() : "";
3148
3326
  if (status === 401 || status === 403) return "AUTH";
3149
3327
  if (status === 402 || providerCode.includes("balance") || providerCode.includes("quota")) return "QUOTA";
3328
+ if (status === 409 && [
3329
+ "operation_closed",
3330
+ "operation_limit",
3331
+ "operation_conflict"
3332
+ ].includes(providerCode)) return "OPERATION_ENDED";
3333
+ if (providerCode === "unsupported_model") return "UNKNOWN_MODEL";
3334
+ if (status === 409 && providerCode === "request_in_progress") return "REQUEST_IN_PROGRESS";
3150
3335
  if (status === 408 || status === 504) return "TIMEOUT";
3151
3336
  if (status === 429) return "RATE_LIMIT";
3152
3337
  if (status === 400 || status === 413 || status === 422) return "INVALID_REQUEST";
@@ -3618,6 +3803,7 @@ var ManagedAiTransport = class {
3618
3803
  const imageAttachments = requestImageAttachments(request);
3619
3804
  assertImageRequestWithinCapability(imageAttachments, capability);
3620
3805
  const bearer = await this.options.resolveBearer();
3806
+ const operationUid = this.options.prepareOperation?.(request, bearer);
3621
3807
  const imageCapability = capability.image;
3622
3808
  for (let dispatchAttempt = 0;; dispatchAttempt++) {
3623
3809
  let resolvedImageBytes = 0;
@@ -3634,6 +3820,7 @@ var ManagedAiTransport = class {
3634
3820
  method: "POST",
3635
3821
  headers: {
3636
3822
  Authorization: `Bearer ${bearer}`,
3823
+ ...operationUid === void 0 ? {} : { "X-Arkme-Turn-ID": operationUid },
3637
3824
  Accept: "text/event-stream",
3638
3825
  "Content-Type": "application/json",
3639
3826
  ...attributionHeaders(),
@@ -3893,7 +4080,7 @@ const ARKME_MANAGED_CATALOG_MAX_STALE_MS = 5 * ARKME_MANAGED_CATALOG_TTL_MS;
3893
4080
  const ARKME_MANAGED_CATALOG_TIMEOUT_MS = 1e4;
3894
4081
  const ARKME_MANAGED_MAXIMUM_IMAGE_PIXELS = 4e7;
3895
4082
  const ARKME_MANAGED_MAXIMUM_REQUEST_IMAGE_BYTES = 1 << 30;
3896
- const ARKME_INSUFFICIENT_BALANCE_MESSAGE = "Arkme AI 余额不足,请前往 Arkme 设置中的余额充值后重试";
4083
+ const ARKME_INSUFFICIENT_BALANCE_MESSAGE = "AI 额度不足,请充值后继续";
3897
4084
  const ARKME_LOGIN_MESSAGE = "请先登录或重新登录 Arkme 后再使用托管模型";
3898
4085
  const ARKME_AUTH_FAILURE_CODES = /* @__PURE__ */ new Set([
3899
4086
  "login-required",
@@ -3928,6 +4115,14 @@ function managedAiFailureFacts(error) {
3928
4115
  };
3929
4116
  }
3930
4117
  function managedAiLocalizedFailure(facts) {
4118
+ if (facts.code === "OPERATION_ENDED") return {
4119
+ code: facts.code,
4120
+ message: "本轮任务已结束,请发送新消息继续"
4121
+ };
4122
+ if (facts.code === "REQUEST_IN_PROGRESS") return {
4123
+ code: facts.code,
4124
+ message: "已有 AI 请求正在完成,请等待结果后重试"
4125
+ };
3931
4126
  if (facts.status === 402 || ["QUOTA", "INSUFFICIENT_BALANCE"].includes(facts.code)) return {
3932
4127
  code: "INSUFFICIENT_BALANCE",
3933
4128
  message: ARKME_INSUFFICIENT_BALANCE_MESSAGE
@@ -4271,6 +4466,21 @@ function parseReasoning(value) {
4271
4466
  defaultEffort: ReasoningEffortId(defaultEffort)
4272
4467
  };
4273
4468
  }
4469
+ function modelPointPriceDescription(value) {
4470
+ const price = asRecord(value);
4471
+ if (!price) return "计费信息暂时无法读取,实际按用量扣积分";
4472
+ const read = (key) => {
4473
+ const v = price[key];
4474
+ if (typeof v !== "string") throw new LlmError("模型积分报价无效", "MALFORMED_RESPONSE");
4475
+ try {
4476
+ pointsUnits(v);
4477
+ } catch {
4478
+ throw new LlmError("模型积分报价无效", "MALFORMED_RESPONSE");
4479
+ }
4480
+ return v;
4481
+ };
4482
+ return `每 1,000 Token:输入 ${read("cache_miss_input_per_thousand")} 积分,缓存输入 ${read("cache_hit_input_per_thousand")} 积分,输出 ${read("output_per_thousand")} 积分。已含服务费,实际按用量扣分。`;
4483
+ }
4274
4484
  function parseManagedCatalog(payload) {
4275
4485
  const envelope = asRecord(payload);
4276
4486
  if (envelope?.code !== 200) {
@@ -4298,6 +4508,7 @@ function parseManagedCatalog(payload) {
4298
4508
  return {
4299
4509
  id,
4300
4510
  name: requiredCatalogText(item, "display_name", "显示名称"),
4511
+ description: modelPointPriceDescription(item.point_pricing),
4301
4512
  contextWindow,
4302
4513
  maxTokens: defaultMaxTokens
4303
4514
  };
@@ -4358,7 +4569,7 @@ var ManagedModelCatalog = class {
4358
4569
  canUseLastGood() {
4359
4570
  return this.hasRemoteSnapshot && Date.now() - this.refreshedAt < ARKME_MANAGED_CATALOG_MAX_STALE_MS;
4360
4571
  }
4361
- async fetchCatalog(signal) {
4572
+ async fetchCatalog(signal, resolveModel) {
4362
4573
  const controller = new AbortController();
4363
4574
  let timedOut = false;
4364
4575
  const abortFromCaller = () => {
@@ -4380,7 +4591,7 @@ var ManagedModelCatalog = class {
4380
4591
  Authorization: `Bearer ${bearer}`,
4381
4592
  "Content-Type": "application/json"
4382
4593
  },
4383
- body: "{}",
4594
+ body: JSON.stringify(resolveModel === void 0 ? {} : { resolve_model: resolveModel }),
4384
4595
  signal: controller.signal
4385
4596
  });
4386
4597
  if (!response.ok) {
@@ -4398,12 +4609,19 @@ var ManagedModelCatalog = class {
4398
4609
  throw new LlmError("Arkme 模型目录返回异常", "MALFORMED_RESPONSE", { cause: error });
4399
4610
  }
4400
4611
  const snapshot = parseManagedCatalog(payload);
4401
- this.connectionSnapshot = managedConnection(this.baseUrl, snapshot.models);
4402
- this.modelIds = new Set(snapshot.models.map((model) => model.id));
4403
- this.capabilitySnapshot = snapshot.capabilities;
4404
- this.reasoningSnapshot = snapshot.reasoning;
4405
- this.hasRemoteSnapshot = true;
4406
- this.refreshedAt = Date.now();
4612
+ if (resolveModel !== void 0) {
4613
+ if (snapshot.models.length !== 1 || snapshot.models[0]?.id !== resolveModel) throw new LlmError("原回合模型信息无效", "UNKNOWN_MODEL");
4614
+ this.connectionSnapshot = managedConnection(this.baseUrl, [...this.connectionSnapshot.models.filter((item) => item.id !== resolveModel), ...snapshot.models]);
4615
+ for (const [key, value] of snapshot.capabilities) this.capabilitySnapshot.set(key, value);
4616
+ for (const [key, value] of snapshot.reasoning) this.reasoningSnapshot.set(key, value);
4617
+ } else {
4618
+ this.connectionSnapshot = managedConnection(this.baseUrl, snapshot.models);
4619
+ this.modelIds = new Set(snapshot.models.map((model) => model.id));
4620
+ this.capabilitySnapshot = snapshot.capabilities;
4621
+ this.reasoningSnapshot = snapshot.reasoning;
4622
+ this.hasRemoteSnapshot = true;
4623
+ this.refreshedAt = Date.now();
4624
+ }
4407
4625
  } catch (error) {
4408
4626
  if (signal?.aborted === true) throw localizeManagedAiError(new LlmError("Arkme 模型目录请求已取消", "ABORTED", { cause: error }));
4409
4627
  if (timedOut) throw localizeManagedAiError(new LlmError("Arkme 模型目录请求超时", "TIMEOUT", { cause: error }));
@@ -4433,15 +4651,29 @@ var ManagedModelCatalog = class {
4433
4651
  }
4434
4652
  async ensureModel(model, signal) {
4435
4653
  const known = this.modelIds.has(model);
4436
- if (this.isFresh()) return;
4437
- try {
4654
+ if (!this.isFresh()) try {
4438
4655
  await this.refresh(signal);
4439
4656
  } catch (error) {
4440
4657
  if (signal?.aborted === true || !this.canUseLastGood() || !known) throw error;
4441
4658
  }
4659
+ if (!this.modelIds.has(model)) try {
4660
+ await this.fetchCatalog(signal, model);
4661
+ } catch (error) {
4662
+ if (signal?.aborted === true) throw error;
4663
+ const facts = managedAiFailureFacts(error);
4664
+ if ((facts.status ?? 0) >= 500 || [
4665
+ "TRANSPORT",
4666
+ "TIMEOUT",
4667
+ "AUTH"
4668
+ ].includes(facts.code)) throw error;
4669
+ throw new LlmError("原回合模型已不可用,请重新选择模型", "UNKNOWN_MODEL", { cause: error });
4670
+ }
4671
+ }
4672
+ isListed(model) {
4673
+ return this.modelIds.has(model);
4442
4674
  }
4443
4675
  assertModel(model) {
4444
- if (!this.modelIds.has(model)) throw new LlmError(`当前 Arkme 托管服务不支持模型“${model}”,请重新选择模型`, "UNKNOWN_MODEL");
4676
+ if (!this.capabilitySnapshot.has(model)) throw new LlmError(`当前 Arkme 托管服务不支持模型“${model}”,请重新选择模型`, "UNKNOWN_MODEL");
4445
4677
  }
4446
4678
  };
4447
4679
  var ManagedAiLlmAdapter = class extends LlmAdapter {
@@ -4466,7 +4698,7 @@ var ManagedAiLlmAdapter = class extends LlmAdapter {
4466
4698
  async listModels(provider) {
4467
4699
  assertManagedProvider(provider);
4468
4700
  await this.catalog.refreshForListing();
4469
- return (await this.delegate.listModels(provider)).map((model) => ({
4701
+ return (await this.delegate.listModels(provider)).filter((model) => this.catalog.isListed(model.id)).map((model) => ({
4470
4702
  ...model,
4471
4703
  inputModalities: this.catalog.capability(model.id).inputModalities
4472
4704
  }));
@@ -4511,7 +4743,8 @@ function createManagedAiLlmAdapter(options) {
4511
4743
  resolveAttachmentReader: options.resolveAttachmentReader ?? (() => void 0),
4512
4744
  fetchImpl,
4513
4745
  resolveBearer: async () => await resolveBearer(options.credentialOwner),
4514
- resolveAnonymousUserId
4746
+ resolveAnonymousUserId,
4747
+ ...options.prepareOperation === void 0 ? {} : { prepareOperation: options.prepareOperation }
4515
4748
  }));
4516
4749
  }
4517
4750
  function registerManagedAiProvider(ctx, options) {
@@ -36218,18 +36451,14 @@ var HttpArkmeBillingGateway = class {
36218
36451
  return new ArkmePluginError(error.code, error.message, retryable, error.httpStatus, { cause: error });
36219
36452
  }
36220
36453
  async quota(signal) {
36221
- const data = objectValue$2(await this.post("intelligent", "/api/v1/managed-ai/balance/query", {}, "interactive-read", signal));
36222
- const currency = stringValue$2(data.currency).trim();
36223
- if (currency !== "CNY") throw new ArkmePluginError("billing-contract-invalid", "余额接口币种无效", true, 502);
36224
- const availableNanoCny = nanoCny(data.available_nano_cny, "available_nano_cny");
36225
- const totalNanoCny = nanoCny(data.total_nano_cny, "total_nano_cny");
36226
- const reservedNanoCny = nanoCny(data.reserved_nano_cny, "reserved_nano_cny");
36227
- if (BigInt(availableNanoCny) + BigInt(reservedNanoCny) !== BigInt(totalNanoCny)) throw new ArkmePluginError("billing-contract-invalid", "余额接口金额不一致", true, 502);
36454
+ const account = parseAiPointsAccount(objectValue$2(await this.post("intelligent", "/api/v1/managed-ai/points/query", {}, "interactive-read", signal)), "");
36455
+ const availableNanoCny = pointsUnits(account.availablePoints);
36456
+ const reservedNanoCny = pointsUnits(account.reservedPoints);
36228
36457
  return {
36229
- availableNanoCny,
36230
- totalNanoCny,
36231
- reservedNanoCny,
36232
- currency
36458
+ availableNanoCny: String(availableNanoCny),
36459
+ totalNanoCny: String(availableNanoCny + reservedNanoCny),
36460
+ reservedNanoCny: String(reservedNanoCny),
36461
+ currency: "CNY"
36233
36462
  };
36234
36463
  }
36235
36464
  async products(signal) {
@@ -36855,6 +37084,7 @@ function arkoModelOptionFromData(value) {
36855
37084
  displayName,
36856
37085
  provider,
36857
37086
  description,
37087
+ costDescription: stringValue$2(data.cost_description),
36858
37088
  recommended: booleanValue$14(data.recommended),
36859
37089
  selected: booleanValue$14(data.selected)
36860
37090
  };
@@ -36866,7 +37096,7 @@ function arkoModelCatalogFromData(data) {
36866
37096
  const options = listValue$26(data.items).map(arkoModelOptionFromData);
36867
37097
  const routeKeys = new Set(options.map((option) => option.routeKey));
36868
37098
  const selected = options.filter((option) => option.selected);
36869
- if (selectionSource !== "default" && selectionSource !== "personal" || options.length === 0 || options.length > 16 || routeKeys.size !== options.length || !routeKeys.has(defaultRouteKey) || !routeKeys.has(effectiveRouteKey) || selected.length !== 1 || selected[0]?.routeKey !== effectiveRouteKey) throw new ArkmePluginError("arko-model-contract-invalid", "Arko 模型目录响应无效", true, 502);
37099
+ if (selectionSource !== "default" && selectionSource !== "personal" && selectionSource !== "unavailable" || options.length === 0 || options.length > 256 || routeKeys.size !== options.length || !routeKeys.has(defaultRouteKey) || (selectionSource === "unavailable" ? routeKeys.has(effectiveRouteKey) || selected.length !== 0 : !routeKeys.has(effectiveRouteKey) || selected.length !== 1 || selected[0]?.routeKey !== effectiveRouteKey)) throw new ArkmePluginError("arko-model-contract-invalid", "Arko 模型目录响应无效", true, 502);
36870
37100
  return {
36871
37101
  defaultRouteKey,
36872
37102
  effectiveRouteKey,
@@ -37053,8 +37283,7 @@ var ArkoService = class {
37053
37283
  let modelRouteKey = options.modelRouteKey?.trim() ?? "";
37054
37284
  if (hasContinuationRun && modelRouteKey !== "") throw new ArkmePluginError("arko-continuation-model-invalid", "继续 Arko 任务时不能切换模型", false);
37055
37285
  if (!hasContinuationRun && modelRouteKey === "") try {
37056
- const catalog = await this.arkoModelCatalog(options.signal);
37057
- if (catalog.options.length > 1) modelRouteKey = catalog.effectiveRouteKey;
37286
+ modelRouteKey = (await this.arkoModelCatalog(options.signal)).effectiveRouteKey;
37058
37287
  } catch {}
37059
37288
  if (modelRouteKey !== "" && (modelRouteKey.length > 128 || !ARKO_MODEL_ROUTE_PATTERN.test(modelRouteKey))) throw new ArkmePluginError("arko-model-route-invalid", "请选择有效的 Arko 模型", false);
37060
37289
  const body = {
@@ -47108,6 +47337,23 @@ function integer$1(value) {
47108
47337
  const parsed = typeof value === "number" ? value : Number(value);
47109
47338
  return Number.isSafeInteger(parsed) ? parsed : 0;
47110
47339
  }
47340
+ /** The tree can be centered on the child while its parent lives outside the topic. */
47341
+ function projectOwnedRecordParent(response, openedRecordUid) {
47342
+ const data = object$3(response);
47343
+ if (openedRecordUid === "" || text$2(data.record_uid) !== openedRecordUid || text$2(data.root_record_uid) === "") throw new Error("记录延展树与当前快记不匹配");
47344
+ let parentUid = text$2(data.parent_record_uid);
47345
+ if (data.parent_record_uid === void 0) parentUid = text$2((Array.isArray(data.edges) ? data.edges : []).map(object$3).find((edge) => text$2(edge.child_record_uid) === openedRecordUid && (edge.status === void 0 || integer$1(edge.status) === 1))?.parent_record_uid);
47346
+ if (parentUid === "" || parentUid === openedRecordUid) return void 0;
47347
+ const record = (Array.isArray(data.records) ? data.records : []).map(object$3).find((record) => text$2(record.record_uid) === parentUid);
47348
+ return {
47349
+ recordUid: parentUid,
47350
+ parentRecordUid: "",
47351
+ level: 0,
47352
+ record: record ?? {},
47353
+ createdAtMillis: 0,
47354
+ protectedContent: record === void 0 || integer$1(record.content_access_state) !== 1 || record.status !== void 0 && integer$1(record.status) !== 1
47355
+ };
47356
+ }
47111
47357
  function parseNode(value, visiting, budget) {
47112
47358
  const raw = object$3(value);
47113
47359
  const recordUid = text$2(raw.record_uid);
@@ -51779,12 +52025,66 @@ var ChatService = class {
51779
52025
  };
51780
52026
  }
51781
52027
  /** Personal and topic notes share the record-owned tree, including replies outside the original topic. */
52028
+ async ownedRecordParentFromTree(data, recordUid, session, signal) {
52029
+ const parent = projectOwnedRecordParent(data, recordUid);
52030
+ if (parent === void 0) return void 0;
52031
+ const owner = recordOwnerId(parent.record.owner_user_id);
52032
+ if (parent.protectedContent || owner !== 0 && owner !== session.userId || arkmePrivacyLockedRecord(parent.record) || this.privacy !== void 0 && (await this.privacy.lockedRecordUids(session, signal)).has(parent.recordUid)) return {
52033
+ itemUid: parent.recordUid,
52034
+ senderName: "",
52035
+ title: "",
52036
+ textContent: "该延展源暂不可用"
52037
+ };
52038
+ const hydration = await this.media.hydrateRecordMediaPage([parent.record], session, signal);
52039
+ const item = this.record.recordTimelineItemFromRaw(parent.record, session.userId, {
52040
+ displayItems: hydration.displayItemsByRecordUid.get(parent.recordUid) ?? [],
52041
+ mediaUnavailable: hydration.unavailableRecordUids.has(parent.recordUid)
52042
+ });
52043
+ return {
52044
+ itemUid: parent.recordUid,
52045
+ senderName: item.senderName,
52046
+ title: item.title,
52047
+ textContent: item.textContent,
52048
+ textFormat: item.textFormat ?? "plain",
52049
+ sendAtMillis: item.sendAtMillis,
52050
+ ...owner === 0 ? {} : { recordOwnerUserId: owner },
52051
+ ...item.contentBlocks === void 0 ? {} : { contentBlocks: item.contentBlocks }
52052
+ };
52053
+ }
52054
+ /** Lightweight visible-row enrichment; never reads an entire topic before showing it. */
52055
+ async sourceMessageExtensionParent(sourceRef, actionRef, options = {}) {
52056
+ const session = await this.runtime.requireSession();
52057
+ const source = await this.source.openSourceRef(sourceRef.trim(), session.userId);
52058
+ const reference = await this.openMessageActionRef(actionRef, session.userId, source);
52059
+ if (reference.sourceKind !== "record" || ![
52060
+ "send_to_self",
52061
+ "default_category",
52062
+ "topic"
52063
+ ].includes(source.kind)) throw new ArkmePluginError("record-extension-source-invalid", "当前来源不支持读取个人延展源", false, 403);
52064
+ const data = await this.runtime.authenticatedPost("/api/v1/records/extensions/tree", { record_uid: reference.recordUid }, session, options.signal, {
52065
+ lane: "background-read",
52066
+ key: `record-extension-parent:${reference.recordUid}`,
52067
+ cacheMs: 5e3,
52068
+ failureCooldownMs: 2e3
52069
+ });
52070
+ const extensionParent = await this.ownedRecordParentFromTree(data, reference.recordUid, session, options.signal);
52071
+ return {
52072
+ recordUid: reference.recordUid,
52073
+ ...extensionParent === void 0 ? {} : { extensionParent }
52074
+ };
52075
+ }
51782
52076
  async loadOwnedRecordExtensions(recordUid, session, signal) {
51783
- const nodes = projectOwnedRecordExtensions(await this.runtime.authenticatedPost("/api/v1/records/extensions/tree", { record_uid: recordUid }, session, signal, {
52077
+ const data = await this.runtime.authenticatedPost("/api/v1/records/extensions/tree", { record_uid: recordUid }, session, signal, {
51784
52078
  lane: "interactive-read",
51785
52079
  bypassCache: true
51786
- }), recordUid);
51787
- if (nodes.length === 0) return void 0;
52080
+ });
52081
+ const nodes = projectOwnedRecordExtensions(data, recordUid);
52082
+ const extensionParent = await this.ownedRecordParentFromTree(data, recordUid, session, signal);
52083
+ if (nodes.length === 0) return extensionParent === void 0 ? void 0 : {
52084
+ extensionCount: 0,
52085
+ extensions: [],
52086
+ extensionParent
52087
+ };
51788
52088
  const readable = nodes.filter((node) => !node.protectedContent);
51789
52089
  const hydration = await this.media.hydrateRecordMediaPage(readable.map((node) => node.record), session, signal);
51790
52090
  const extensions = nodes.map((node) => {
@@ -51838,7 +52138,8 @@ var ChatService = class {
51838
52138
  extensions.sort((left, right) => right.sendAtMillis - left.sendAtMillis);
51839
52139
  return {
51840
52140
  extensionCount: extensions.length,
51841
- extensions
52141
+ extensions,
52142
+ ...extensionParent === void 0 ? {} : { extensionParent }
51842
52143
  };
51843
52144
  }
51844
52145
  async recordEditHistoryTarget(sourceRef, messageActionRef) {
@@ -51911,7 +52212,8 @@ var ChatService = class {
51911
52212
  return {
51912
52213
  parentRecordUid: reference.recordUid,
51913
52214
  extensionCount: recordContext?.extensionCount ?? 0,
51914
- extensions: recordContext?.extensions ?? []
52215
+ extensions: recordContext?.extensions ?? [],
52216
+ ...recordContext !== void 0 && "extensionParent" in recordContext ? { extensionParent: recordContext.extensionParent } : {}
51915
52217
  };
51916
52218
  }
51917
52219
  async extendSourceMessage(sourceRef, messageActionRef, textContent, recordUid, assets = [], options = {}) {
@@ -86468,6 +86770,7 @@ var InterwovenService = class {
86468
86770
  summary: stringValue$2(renderPayload.content ?? item.summary).trim().slice(0, 1e3),
86469
86771
  degraded: booleanValue$4(item.is_degraded),
86470
86772
  sourceChatSessionUid,
86773
+ ...stringValue$2(jumpTarget.subject_uid).trim() === "" ? {} : { legacyGroupUid: stringValue$2(jumpTarget.subject_uid).trim() },
86471
86774
  recordOwnerUserId,
86472
86775
  recordUid,
86473
86776
  relationUid,
@@ -86492,6 +86795,7 @@ var InterwovenService = class {
86492
86795
  userId: session.userId,
86493
86796
  sourceOwnerRef: source.ownerRef,
86494
86797
  sourceChatSessionUid: descriptor.sourceChatSessionUid,
86798
+ ...descriptor.legacyGroupUid === void 0 ? {} : { legacyGroupUid: descriptor.legacyGroupUid },
86495
86799
  recordOwnerUserId: descriptor.recordOwnerUserId,
86496
86800
  recordUid: descriptor.recordUid,
86497
86801
  relationUid: descriptor.relationUid,
@@ -86536,6 +86840,154 @@ var InterwovenService = class {
86536
86840
  ...partial ? { message: "部分交织瞬间暂时不可用,可稍后重试" } : {}
86537
86841
  };
86538
86842
  }
86843
+ async interwovenReadReceipts(sourceRef, momentRefs, signal) {
86844
+ const epoch = this.interactionEpoch;
86845
+ const session = await this.runtime.requireSession();
86846
+ const source = await this.source.openSourceRef(sourceRef, session.userId);
86847
+ if (source.kind !== "private_chat" || momentRefs.length < 1 || momentRefs.length > 20 || momentRefs.some((ref) => typeof ref !== "string" || !ref.trim() || ref.length > 4096) || new Set(momentRefs).size !== momentRefs.length) throw new ArkmePluginError("interwoven-param-invalid", "群互动已读查询参数无效", false, 400);
86848
+ const references = await Promise.all(momentRefs.map((ref) => this.openInterwovenMomentRef(ref, session.userId, source.ownerRef)));
86849
+ const peerId = await this.assertInterwovenOperationAllowed(source, session, signal);
86850
+ await this.resolveReceiptBridgeReferences(sourceRef, source.ownerRef, references, signal);
86851
+ const groupReads = /* @__PURE__ */ new Map();
86852
+ const read = (path, body) => this.runtime.authenticatedChatPost(path, body, session, signal, {
86853
+ lane: "interactive-read",
86854
+ bypassCache: true
86855
+ });
86856
+ const groupDetail = (uid) => {
86857
+ let pending = groupReads.get(uid);
86858
+ if (!pending) {
86859
+ pending = read("/api/v1/chats/detail", { chat_session_uid: uid });
86860
+ groupReads.set(uid, pending);
86861
+ }
86862
+ return pending;
86863
+ };
86864
+ const items = new Array(references.length);
86865
+ let next = 0;
86866
+ const worker = async () => {
86867
+ while (next < references.length) {
86868
+ signal?.throwIfAborted();
86869
+ const index = next++;
86870
+ const ref = references[index];
86871
+ const reader = ref.senderUserId === session.userId ? "peer" : "self";
86872
+ const unknown = {
86873
+ momentId: ref.momentId,
86874
+ reader,
86875
+ status: "unknown"
86876
+ };
86877
+ items[index] = unknown;
86878
+ const groupUid = ref.sourceChatSessionUid || ref.legacyGroupUid || "";
86879
+ if (!groupUid || reader === "self" && ref.senderUserId !== peerId) continue;
86880
+ try {
86881
+ const group = await groupDetail(groupUid);
86882
+ const groupSession = objectValue$2(group.session);
86883
+ if (groupSession.chat_session_uid !== groupUid || groupSession.session_kind !== 2 || groupSession.status !== 1) continue;
86884
+ let sequence = ref.sequence || ref.receiptSequence || 0;
86885
+ const receiptRecordUid = ref.receiptRecordUid || ref.recordUid;
86886
+ if (sequence <= 0) {
86887
+ if (ref.recordUid.startsWith("chat_legacy_receive_")) continue;
86888
+ const around = await read("/api/v1/chat/timeline/around", {
86889
+ chat_session_uid: groupUid,
86890
+ record_uid: ref.recordUid,
86891
+ record_owner_user_id: ref.recordOwnerUserId,
86892
+ before_limit: 1,
86893
+ after_limit: 1
86894
+ });
86895
+ if (around.chat_session_uid !== groupUid) continue;
86896
+ const candidates = [...listValue$11(around.items), around.anchor].map(objectValue$2).filter((item) => {
86897
+ const relation = objectValue$2(item.relation ?? item);
86898
+ return relation.chat_session_uid === groupUid && relation.record_uid === ref.recordUid && relation.record_owner_user_id === ref.recordOwnerUserId && relation.sender_user_id === ref.senderUserId && Number.isSafeInteger(relation.seq) && numberValue$13(relation.seq) > 0 && objectValue$2(item.record).status === 1;
86899
+ });
86900
+ const sequences = new Set(candidates.map((item) => numberValue$13(objectValue$2(item.relation ?? item).seq)));
86901
+ if (sequences.size !== 1) continue;
86902
+ sequence = [...sequences][0];
86903
+ ref.receiptSequence = sequence;
86904
+ }
86905
+ if (reader === "self") {
86906
+ const cursor = objectValue$2(group.current_cursor);
86907
+ const snapshot = objectValue$2(group.unread_snapshot);
86908
+ if (cursor.user_id !== session.userId || cursor.chat_session_uid !== groupUid || cursor.status !== 1) continue;
86909
+ const readSeq = snapshot.read_seq ?? cursor.read_seq;
86910
+ const lastSeq = snapshot.session_last_seq ?? groupSession.last_seq;
86911
+ if (typeof readSeq !== "number" || !Number.isSafeInteger(readSeq) || readSeq < 0 || typeof lastSeq !== "number" || !Number.isSafeInteger(lastSeq) || lastSeq < sequence) continue;
86912
+ items[index] = {
86913
+ ...unknown,
86914
+ status: readSeq >= sequence ? "read" : "unread"
86915
+ };
86916
+ } else {
86917
+ const detail = await read("/api/v1/chats/read-receipts/detail", {
86918
+ chat_session_uid: groupUid,
86919
+ record_uid: receiptRecordUid,
86920
+ seq: sequence
86921
+ });
86922
+ if (detail.chat_session_uid !== groupUid || detail.record_uid !== receiptRecordUid || detail.seq !== sequence || !Array.isArray(detail.items)) continue;
86923
+ const peers = detail.items.map(objectValue$2).filter((member) => member.user_id === peerId);
86924
+ if (peers.length !== 1) continue;
86925
+ const peer = peers[0];
86926
+ if (peer.read_status !== "read" && peer.read_status !== "unread" || typeof peer.read_at !== "number" || !Number.isSafeInteger(peer.read_at) || peer.read_at < 0 || peer.read_status === "unread" && peer.read_at !== 0) continue;
86927
+ items[index] = {
86928
+ ...unknown,
86929
+ status: peer.read_status,
86930
+ ...peer.read_status === "read" && peer.read_at > 0 ? { readAtMillis: peer.read_at } : {}
86931
+ };
86932
+ }
86933
+ } catch {
86934
+ signal?.throwIfAborted();
86935
+ }
86936
+ }
86937
+ };
86938
+ await Promise.all(Array.from({ length: Math.min(3, references.length) }, worker));
86939
+ await this.assertInteractionAccount(session.userId, epoch, signal);
86940
+ return { items };
86941
+ }
86942
+ async resolveReceiptBridgeReferences(sourceRef, privateUid, references, signal) {
86943
+ const pending = references.filter((ref) => !ref.sequence && !ref.receiptSequence && /^chat_legacy_receive_[a-f0-9]{32}$/.test(ref.recordUid));
86944
+ if (!pending.length) return;
86945
+ const matches = /* @__PURE__ */ new Map();
86946
+ let cursor;
86947
+ let expectedVersion;
86948
+ try {
86949
+ for (let page = 0; page < 3; page++) {
86950
+ const { data, userId } = await this.readPrivateInteractions("occurrences/query", {
86951
+ sourceRef,
86952
+ limit: 50,
86953
+ ...cursor === void 0 ? {} : { cursor },
86954
+ ...expectedVersion === void 0 ? {} : { expectedVersion },
86955
+ ...signal === void 0 ? {} : { signal }
86956
+ });
86957
+ const coverage = this.interactionCoverage(data);
86958
+ if (expectedVersion && coverage.version !== expectedVersion) return;
86959
+ expectedVersion = coverage.version;
86960
+ if (!Array.isArray(data.items) || data.items.length > 50 || typeof data.has_more !== "boolean") return;
86961
+ for (const raw of data.items) {
86962
+ const item = objectValue$2(raw);
86963
+ if (this.interactionCount(item.seq) === 0) return;
86964
+ const groupUid = stringValue$2(item.source_chat_session_uid).trim();
86965
+ const relationUid = stringValue$2(item.rel_uid).trim();
86966
+ const recordUid = stringValue$2(item.record_uid).trim();
86967
+ if (!relationUid || !recordUid || item.private_chat_session_uid !== privateUid) continue;
86968
+ const alias = `chat_legacy_receive_${createHash("sha256").update(`${groupUid}:${relationUid}`).digest("hex").slice(0, 32)}`;
86969
+ for (const ref of pending) {
86970
+ if (ref.recordUid !== alias || (ref.sourceChatSessionUid || ref.legacyGroupUid) !== groupUid || ref.recordOwnerUserId !== item.record_owner_user_id || ref.senderUserId !== item.sender_user_id || item.sender_is_me !== (ref.senderUserId === userId)) continue;
86971
+ const next = {
86972
+ recordUid,
86973
+ sequence: item.seq
86974
+ };
86975
+ const previous = matches.get(ref);
86976
+ matches.set(ref, previous === null || previous && (previous.recordUid !== recordUid || previous.sequence !== next.sequence) ? null : next);
86977
+ }
86978
+ }
86979
+ if (!data.has_more || pending.every((ref) => matches.has(ref))) break;
86980
+ if (typeof data.next_cursor !== "string" || !data.next_cursor || data.next_cursor.length > 2048) return;
86981
+ cursor = data.next_cursor;
86982
+ }
86983
+ for (const [ref, match] of matches) if (match) {
86984
+ ref.receiptRecordUid = match.recordUid;
86985
+ ref.receiptSequence = match.sequence;
86986
+ }
86987
+ } catch {
86988
+ signal?.throwIfAborted();
86989
+ }
86990
+ }
86539
86991
  async interwovenMomentDetail(sourceRef, momentRef, signal) {
86540
86992
  const session = await this.runtime.requireSession();
86541
86993
  const source = await this.source.openSourceRef(sourceRef, session.userId);
@@ -86603,7 +87055,7 @@ var InterwovenService = class {
86603
87055
  if (source.kind !== "private_chat") throw new ArkmePluginError("interwoven-source-invalid", "交织瞬间仅支持普通私聊", false, 400);
86604
87056
  if (!this.runtime.config.interwovenMomentsEnabled) throw new ArkmePluginError("interwoven-disabled", "交织瞬间能力当前未开启", false, 403);
86605
87057
  if (!booleanValue$4((await this.runtime.authenticatedAuthPost("/api/v1/auth/able-func", { func_type: 12 }, session, signal)).able)) throw new ArkmePluginError("interwoven-disabled", "交织瞬间能力当前未开放", false, 403);
86606
- await this.assertHumanPrivateSource(source, session, signal);
87058
+ return await this.assertHumanPrivateSource(source, session, signal);
86607
87059
  }
86608
87060
  async assertHumanPrivateSource(source, session, signal) {
86609
87061
  const detail = await this.runtime.authenticatedChatPost("/api/v1/chats/detail", { chat_session_uid: source.ownerRef }, session, signal);
@@ -98026,6 +98478,12 @@ var ArkmeService = class {
98026
98478
  async membershipCatalog(expectedUserId) {
98027
98479
  return await this.membershipOwner.catalog(expectedUserId);
98028
98480
  }
98481
+ async aiPointsAccount(expectedScope, signal) {
98482
+ return await new AiPointsService(this.runtime).account(expectedScope, signal);
98483
+ }
98484
+ async aiPointsConsumption(query, expectedScope, signal) {
98485
+ return await new AiPointsService(this.runtime).consumption(query, expectedScope, signal);
98486
+ }
98029
98487
  async accountTokenUsage(expectedScope) {
98030
98488
  return await new AccountUsageService(this.runtime).tokens(expectedScope);
98031
98489
  }
@@ -98260,6 +98718,7 @@ var ArkmeService = class {
98260
98718
  revisionPolling: true,
98261
98719
  userProfile: true,
98262
98720
  accountSettings: true,
98721
+ aiPoints: true,
98263
98722
  imageRead: true,
98264
98723
  recordCalendar: true,
98265
98724
  imageLibrary: true,
@@ -98775,6 +99234,9 @@ var ArkmeService = class {
98775
99234
  async interwovenMomentDetail(sourceRef, momentRef, signal) {
98776
99235
  return await this.interwoven.interwovenMomentDetail(sourceRef, momentRef, signal);
98777
99236
  }
99237
+ async interwovenReadReceipts(sourceRef, momentRefs, signal) {
99238
+ return await this.interwoven.interwovenReadReceipts(sourceRef, momentRefs, signal);
99239
+ }
98778
99240
  async recordEditHistoryPage(sourceRef, messageActionRef, cursorEditAt = 0, signal) {
98779
99241
  return await this.recordEditHistory.page(await this.chat.recordEditHistoryTarget(sourceRef, messageActionRef), cursorEditAt, signal);
98780
99242
  }
@@ -99212,6 +99674,9 @@ var ArkmeService = class {
99212
99674
  })
99213
99675
  };
99214
99676
  }
99677
+ async sourceMessageExtensionParent(sourceRef, messageActionRef, options = {}) {
99678
+ return this.chat.sourceMessageExtensionParent(sourceRef, messageActionRef, options);
99679
+ }
99215
99680
  async extendSourceMessage(sourceRef, messageActionRef, textContent, recordUid, fileRefs = [], options = {}) {
99216
99681
  if (options.textFormat === "markdown" && this.config.markdownQuickNotesEnabled !== true) throw new ArkmePluginError("markdown-send-disabled", "Markdown 发送尚未开放,请稍后重试", false, 403);
99217
99682
  const context = await this.chat.sourceMessageExtensionContext(sourceRef, messageActionRef, options);