@ycodium-ai/plugin-im 0.2.3220 → 0.2.3230

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -126460,9 +126460,21 @@ var makeConclusionLedger = (core) => {
126460
126460
  unpersisted.delete(key);
126461
126461
  if (added) await recover?.(debt);
126462
126462
  },
126463
+ /** selection 为 null 表示执行视图不可读且本会话仍有欠账,须等恢复器判定后投递。 */
126463
126464
  forTurn: async (threadId, turnId = null) => {
126464
126465
  await Promise.all(registering.get(threadId) ?? []);
126465
- const execution = await core.host.projections.getThreadExecution(threadId);
126466
+ const execution = await core.host.projections.getThreadExecution(threadId).catch((error) => {
126467
+ const owes = Object.values(conclusionDebtsOf(core.state)).some(
126468
+ (debt) => debt.threadId === threadId
126469
+ );
126470
+ if (!owes) return null;
126471
+ core.host.log.warn("chat deferred a conclusion until its execution can be read", {
126472
+ threadId,
126473
+ error: describeError(error)
126474
+ });
126475
+ return void 0;
126476
+ });
126477
+ if (execution === void 0) return { selection: null, handled: false, execution: null };
126466
126478
  return {
126467
126479
  execution,
126468
126480
  ...execution === null ? { selection: void 0, handled: false } : forExecutionTurn(
@@ -133354,6 +133366,87 @@ var makeCommands = (core, deps) => {
133354
133366
  return { handleCommand, executeCommand, targetOf };
133355
133367
  };
133356
133368
 
133369
+ // src/connectionError.ts
133370
+ var NETWORK_HINTS = {
133371
+ ENOTFOUND: "networkDns",
133372
+ EAI_AGAIN: "networkDns",
133373
+ ECONNREFUSED: "networkRefused",
133374
+ ECONNRESET: "networkReset",
133375
+ EPIPE: "networkReset",
133376
+ UND_ERR_SOCKET: "networkReset",
133377
+ ENETUNREACH: "networkUnreachable",
133378
+ EHOSTUNREACH: "networkUnreachable",
133379
+ ERR_NETWORK_ACCESS_DENIED: "networkUnreachable",
133380
+ CERT_HAS_EXPIRED: "networkTls",
133381
+ CERT_NOT_YET_VALID: "networkTls",
133382
+ DEPTH_ZERO_SELF_SIGNED_CERT: "networkTls",
133383
+ SELF_SIGNED_CERT_IN_CHAIN: "networkTls",
133384
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE: "networkTls",
133385
+ UNABLE_TO_GET_ISSUER_CERT_LOCALLY: "networkTls",
133386
+ ERR_TLS_CERT_ALTNAME_INVALID: "networkTls",
133387
+ ERR_SSL_WRONG_VERSION_NUMBER: "networkTls",
133388
+ ERR_TLS_HANDSHAKE_TIMEOUT: "networkTls",
133389
+ ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR: "networkTls",
133390
+ ETIMEDOUT: "networkTimeout",
133391
+ UND_ERR_CONNECT_TIMEOUT: "networkTimeout",
133392
+ UND_ERR_HEADERS_TIMEOUT: "networkTimeout",
133393
+ UND_ERR_BODY_TIMEOUT: "networkTimeout",
133394
+ timeout: "networkTimeout",
133395
+ ERR_PROXY_CONNECTION_FAILED: "networkProxy"
133396
+ };
133397
+ var NETWORK_REASONS = new Set(Object.keys(NETWORK_HINTS));
133398
+ var GENERIC_REASONS = /* @__PURE__ */ new Set(["timeout"]);
133399
+ var MAX_NODES = 16;
133400
+ var recordOf = (value2) => value2 !== null && typeof value2 === "object" ? value2 : void 0;
133401
+ var httpStatusOf = (value2) => typeof value2 === "number" && Number.isInteger(value2) && value2 >= 100 && value2 <= 599 ? value2 : void 0;
133402
+ var networkReason = (value2) => typeof value2 === "string" && NETWORK_REASONS.has(value2) ? value2 : void 0;
133403
+ var canonicalCode = (entry) => entry["name"] === "TimeoutError" ? "timeout" : entry["code"];
133404
+ var visitedErrors = (error) => {
133405
+ const queue = [error];
133406
+ const seen = /* @__PURE__ */ new Set();
133407
+ const chain = [];
133408
+ while (queue.length > 0 && chain.length < MAX_NODES) {
133409
+ const entry = recordOf(queue.shift());
133410
+ if (entry === void 0 || seen.has(entry)) continue;
133411
+ seen.add(entry);
133412
+ chain.push(entry);
133413
+ if (recordOf(entry["cause"]) !== void 0) queue.push(entry["cause"]);
133414
+ if (Array.isArray(entry["errors"])) queue.push(...entry["errors"].slice(0, MAX_NODES));
133415
+ }
133416
+ return chain;
133417
+ };
133418
+ var connectionFacts = (error) => {
133419
+ const nodes = visitedErrors(error).map((entry) => {
133420
+ const response = recordOf(entry["response"]);
133421
+ const fromCode = networkReason(canonicalCode(entry));
133422
+ return {
133423
+ reasons: fromCode === void 0 ? [] : [fromCode],
133424
+ httpStatus: httpStatusOf(entry["status"] ?? entry["statusCode"] ?? response?.["status"])
133425
+ };
133426
+ });
133427
+ const all2 = [...new Set(nodes.flatMap((node) => node.reasons))];
133428
+ const precise = all2.filter((reason) => !GENERIC_REASONS.has(reason));
133429
+ const httpStatus = nodes.find((node) => node.httpStatus !== void 0)?.httpStatus;
133430
+ return {
133431
+ reasons: precise.length > 0 ? precise : all2,
133432
+ ...httpStatus !== void 0 ? { httpStatus } : {}
133433
+ };
133434
+ };
133435
+ var connectionHint = (error) => {
133436
+ const { reasons, httpStatus } = connectionFacts(error);
133437
+ if (reasons.length > 1) return "networkMultipleCauses";
133438
+ const mapped = reasons[0] !== void 0 ? NETWORK_HINTS[reasons[0]] : void 0;
133439
+ if (mapped !== void 0) return mapped;
133440
+ if (httpStatus === 407) return "networkProxy";
133441
+ if (httpStatus === 429) return "platformRateLimited";
133442
+ if (httpStatus !== void 0 && httpStatus >= 500) return "platformUnavailable";
133443
+ return void 0;
133444
+ };
133445
+ var connectionHintFields = (error, platformHint) => {
133446
+ const hint = platformHint ?? connectionHint(error);
133447
+ return hint === void 0 ? {} : { hint };
133448
+ };
133449
+
133357
133450
  // src/inboundStore.ts
133358
133451
  import * as NodeCrypto from "node:crypto";
133359
133452
  import * as NodeFSP from "node:fs/promises";
@@ -134068,7 +134161,7 @@ var startConnections = (core, inboundHandlersFor, inboundState, recovery) => {
134068
134161
  state: "failed",
134069
134162
  ...status.reason === void 0 ? {} : { reason: status.reason },
134070
134163
  ...status.error === void 0 ? {} : { error: redactor.redact(describeError(status.error)) },
134071
- ...status.hint === void 0 ? {} : { hint: status.hint }
134164
+ ...connectionHintFields(status.error, status.hint)
134072
134165
  });
134073
134166
  }
134074
134167
  });
@@ -134081,7 +134174,7 @@ var startConnections = (core, inboundHandlersFor, inboundState, recovery) => {
134081
134174
  record4(bot.rowId, {
134082
134175
  state: "failed",
134083
134176
  error: detail,
134084
- ...error instanceof InboxUnreadableError ? { reason: "inbox-unreadable", hint: "inboxUnreadable" } : {}
134177
+ ...error instanceof InboxUnreadableError ? { reason: "inbox-unreadable", hint: "inboxUnreadable" } : connectionHintFields(error)
134085
134178
  });
134086
134179
  host.log.warn("chat could not start an inbound connection", {
134087
134180
  bot: bot.displayName,
@@ -135640,11 +135733,11 @@ var parseNumberedReply = (item, text4, t) => {
135640
135733
  // src/questionFlow.ts
135641
135734
  var makeQuestionFlow = (host) => {
135642
135735
  const { t } = host;
135643
- const recordOf = (requestId) => questionsOf(host.state())[requestId];
135644
- const itemOf = (requestId, questionId) => recordOf(requestId)?.items.find((item) => item.id === questionId);
135736
+ const recordOf2 = (requestId) => questionsOf(host.state())[requestId];
135737
+ const itemOf = (requestId, questionId) => recordOf2(requestId)?.items.find((item) => item.id === questionId);
135645
135738
  const hasButtons = (chatKey) => host.target(chatKey)?.adapter.choiceCards !== void 0;
135646
135739
  const redraw = async (requestId, questionId) => {
135647
- const record4 = recordOf(requestId);
135740
+ const record4 = recordOf2(requestId);
135648
135741
  const item = itemOf(requestId, questionId);
135649
135742
  const target2 = record4 === void 0 ? void 0 : host.target(record4.chatKey);
135650
135743
  if (record4 === void 0 || item?.cardId === void 0 || target2 === void 0) return;
@@ -135660,7 +135753,7 @@ var makeQuestionFlow = (host) => {
135660
135753
  }
135661
135754
  };
135662
135755
  const freeze = async (requestId, questionId) => {
135663
- const record4 = recordOf(requestId);
135756
+ const record4 = recordOf2(requestId);
135664
135757
  const item = itemOf(requestId, questionId);
135665
135758
  const target2 = record4 === void 0 ? void 0 : host.target(record4.chatKey);
135666
135759
  if (record4 === void 0 || item?.cardId === void 0 || target2 === void 0) return;
@@ -135681,7 +135774,7 @@ var makeQuestionFlow = (host) => {
135681
135774
  }
135682
135775
  };
135683
135776
  const sayNext = async (requestId) => {
135684
- const record4 = recordOf(requestId);
135777
+ const record4 = recordOf2(requestId);
135685
135778
  if (record4 === void 0 || record4.resolved) return;
135686
135779
  const item = record4.items.find(isUnanswered);
135687
135780
  if (item === void 0) return;
@@ -135691,7 +135784,7 @@ var makeQuestionFlow = (host) => {
135691
135784
  );
135692
135785
  };
135693
135786
  const post = async (requestId) => {
135694
- const first = recordOf(requestId);
135787
+ const first = recordOf2(requestId);
135695
135788
  const target2 = first === void 0 ? void 0 : host.target(first.chatKey);
135696
135789
  if (first === void 0 || target2 === void 0) return;
135697
135790
  const cards = target2.adapter.choiceCards;
@@ -135700,7 +135793,7 @@ var makeQuestionFlow = (host) => {
135700
135793
  return;
135701
135794
  }
135702
135795
  for (const questionId of first.items.map((item) => item.id)) {
135703
- const record4 = recordOf(requestId);
135796
+ const record4 = recordOf2(requestId);
135704
135797
  const item = itemOf(requestId, questionId);
135705
135798
  if (record4 === void 0 || record4.resolved || item === void 0) return;
135706
135799
  try {
@@ -135710,7 +135803,7 @@ var makeQuestionFlow = (host) => {
135710
135803
  );
135711
135804
  if (cardId === null) continue;
135712
135805
  host.commit("question-carded", { requestId, questionId, cardId });
135713
- const after = recordOf(requestId);
135806
+ const after = recordOf2(requestId);
135714
135807
  if (after?.resolved === true || itemOf(requestId, questionId)?.answer !== void 0) {
135715
135808
  await freeze(requestId, questionId);
135716
135809
  }
@@ -135727,7 +135820,7 @@ var makeQuestionFlow = (host) => {
135727
135820
  host.warn("chat cannot show questions on this platform");
135728
135821
  return;
135729
135822
  }
135730
- const fresh = pending.filter((request) => recordOf(request.requestId) === void 0);
135823
+ const fresh = pending.filter((request) => recordOf2(request.requestId) === void 0);
135731
135824
  for (const request of buttons ? fresh : fresh.slice(0, 1)) {
135732
135825
  host.commit("question-offered", {
135733
135826
  chatKey,
@@ -135750,18 +135843,18 @@ var makeQuestionFlow = (host) => {
135750
135843
  ...question.multiSelect ? { submitToken: host.newToken() } : {}
135751
135844
  }))
135752
135845
  });
135753
- if (recordOf(request.requestId) === void 0) continue;
135846
+ if (recordOf2(request.requestId) === void 0) continue;
135754
135847
  host.outbound(chatKey, () => post(request.requestId));
135755
135848
  }
135756
135849
  };
135757
135850
  const answer = async (requestId, questionId, value2, label) => {
135758
- const before = recordOf(requestId);
135851
+ const before = recordOf2(requestId);
135759
135852
  const item = itemOf(requestId, questionId);
135760
135853
  if (before === void 0 || before.resolved || item === void 0 || !isUnanswered(item)) {
135761
135854
  return false;
135762
135855
  }
135763
135856
  host.commit("question-answered", { requestId, questionId, answer: value2, label });
135764
- const after = recordOf(requestId);
135857
+ const after = recordOf2(requestId);
135765
135858
  if (after === void 0) return false;
135766
135859
  const complete = after.items.every((entry) => !isUnanswered(entry));
135767
135860
  if (complete) host.commit("question-resolved", { requestId, ending: "here" });
@@ -135840,7 +135933,7 @@ var makeQuestionFlow = (host) => {
135840
135933
  };
135841
135934
  const answerText = (input) => answer(input.requestId, input.questionId, input.value, input.label);
135842
135935
  const resolved = (requestId, outcome) => {
135843
- const record4 = recordOf(requestId);
135936
+ const record4 = recordOf2(requestId);
135844
135937
  if (record4 === void 0 || record4.resolved) return;
135845
135938
  const ending = outcome === "answered" ? "elsewhere" : "cancelled";
135846
135939
  host.commit("question-resolved", { requestId, ending });
@@ -135938,7 +136031,7 @@ var makeQueueFlow = (host, deps) => {
135938
136031
  const { t } = host;
135939
136032
  const shown = /* @__PURE__ */ new Map();
135940
136033
  const notes = /* @__PURE__ */ new Map();
135941
- const recordOf = (intentId) => queueOf(host.state())[intentId];
136034
+ const recordOf2 = (intentId) => queueOf(host.state())[intentId];
135942
136035
  const factsOf = (execution, intentId) => ({
135943
136036
  activeTurnId: execution.activeTurnId,
135944
136037
  queuePaused: execution.queuePaused,
@@ -135976,7 +136069,7 @@ var makeQueueFlow = (host, deps) => {
135976
136069
  resumeToken: host.newToken()
135977
136070
  });
135978
136071
  host.outbound(chatKey, async () => {
135979
- const record4 = recordOf(intentId);
136072
+ const record4 = recordOf2(intentId);
135980
136073
  if (record4 === void 0) return;
135981
136074
  try {
135982
136075
  const latest = await deps.readExecution(threadId) ?? execution;
@@ -136002,7 +136095,7 @@ var makeQueueFlow = (host, deps) => {
136002
136095
  });
136003
136096
  };
136004
136097
  const redraw = async (intentId, released) => {
136005
- const record4 = recordOf(intentId);
136098
+ const record4 = recordOf2(intentId);
136006
136099
  const target2 = record4 === void 0 ? void 0 : host.target(record4.chatKey);
136007
136100
  if (record4 === void 0 || record4.settled !== void 0 || target2 === void 0) return;
136008
136101
  if (record4.cardId === void 0) return;
@@ -138406,6 +138499,9 @@ var fetchUserName = async (input) => {
138406
138499
  };
138407
138500
  var sendChatText = async (input) => postMessage({ ...input, operation: "send", msgType: "text", content: { text: input.text } });
138408
138501
  var LARK_WORKING_REACTION = "OnIt";
138502
+ var LARK_DONE_REACTION = "DONE";
138503
+ var LARK_FAILED_REACTION = "ERROR";
138504
+ var LARK_REACTION_TIMEOUT_MS = 2e3;
138409
138505
  var addReaction = async (input) => {
138410
138506
  const response = await input.http(
138411
138507
  `${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.messageId)}/reactions`,
@@ -138415,7 +138511,8 @@ var addReaction = async (input) => {
138415
138511
  authorization: `Bearer ${input.token}`,
138416
138512
  "content-type": "application/json; charset=utf-8"
138417
138513
  },
138418
- body: JSON.stringify({ reaction_type: { emoji_type: input.emojiType } })
138514
+ body: JSON.stringify({ reaction_type: { emoji_type: input.emojiType } }),
138515
+ ...input.signal === void 0 ? {} : { signal: input.signal }
138419
138516
  }
138420
138517
  );
138421
138518
  const body = await readEnvelope("reactions.create", response);
@@ -138426,7 +138523,8 @@ var removeReaction = async (input) => {
138426
138523
  `${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.messageId)}/reactions/${encodeURIComponent(input.reactionId)}`,
138427
138524
  {
138428
138525
  method: "DELETE",
138429
- headers: { authorization: `Bearer ${input.token}` }
138526
+ headers: { authorization: `Bearer ${input.token}` },
138527
+ ...input.signal === void 0 ? {} : { signal: input.signal }
138430
138528
  }
138431
138529
  );
138432
138530
  await readEnvelope("reactions.delete", response);
@@ -144251,7 +144349,7 @@ var startLarkInbound = (input) => {
144251
144349
  state: "failed",
144252
144350
  reason: hint === "larkCredentials" ? "credentials-rejected" : "disconnected",
144253
144351
  error,
144254
- ...hint === void 0 ? {} : { hint }
144352
+ ...connectionHintFields(error, hint)
144255
144353
  });
144256
144354
  }
144257
144355
  };
@@ -144310,6 +144408,22 @@ var larkInboundPersistence = (contextFor) => ({
144310
144408
  }
144311
144409
  });
144312
144410
 
144411
+ // src/deadline.ts
144412
+ var withDeadline2 = async (timeoutMs, load) => {
144413
+ const signal = AbortSignal.timeout(timeoutMs);
144414
+ let onAbort = () => {
144415
+ };
144416
+ const aborted = new Promise((_, reject) => {
144417
+ onAbort = () => reject(signal.reason);
144418
+ signal.addEventListener("abort", onAbort, { once: true });
144419
+ });
144420
+ try {
144421
+ return await Promise.race([load(signal), aborted]);
144422
+ } finally {
144423
+ signal.removeEventListener("abort", onAbort);
144424
+ }
144425
+ };
144426
+
144313
144427
  // src/boundedSeen.ts
144314
144428
  var makeBoundedSeen = (limit) => {
144315
144429
  const seen = /* @__PURE__ */ new Set();
@@ -144328,22 +144442,6 @@ var makeBoundedSeen = (limit) => {
144328
144442
  };
144329
144443
  };
144330
144444
 
144331
- // src/deadline.ts
144332
- var withDeadline2 = async (timeoutMs, load) => {
144333
- const signal = AbortSignal.timeout(timeoutMs);
144334
- let onAbort = () => {
144335
- };
144336
- const aborted = new Promise((_, reject) => {
144337
- onAbort = () => reject(signal.reason);
144338
- signal.addEventListener("abort", onAbort, { once: true });
144339
- });
144340
- try {
144341
- return await Promise.race([load(signal), aborted]);
144342
- } finally {
144343
- signal.removeEventListener("abort", onAbort);
144344
- }
144345
- };
144346
-
144347
144445
  // src/replyReference.ts
144348
144446
  import * as NodeCrypto4 from "node:crypto";
144349
144447
 
@@ -144977,12 +145075,11 @@ var makeLarkAdapter = (deps) => {
144977
145075
  detail: `token ok; expiresIn=${String(expiresInSeconds)}s`
144978
145076
  };
144979
145077
  } catch (error) {
144980
- const hint = larkConnectionHint(error);
144981
145078
  return {
144982
145079
  stage: "tenant_access_token",
144983
145080
  ok: false,
144984
145081
  detail: describeError(error),
144985
- ...hint === void 0 ? {} : { hint }
145082
+ ...connectionHintFields(error, larkConnectionHint(error))
144986
145083
  };
144987
145084
  }
144988
145085
  },
@@ -145087,13 +145184,22 @@ var makeLarkAdapter = (deps) => {
145087
145184
  messageId,
145088
145185
  emojiType: LARK_WORKING_REACTION
145089
145186
  }),
145090
- clearAcknowledge: async (binding, messageId, ackId) => {
145091
- await removeReaction({
145092
- http: deps.http,
145093
- token: await tenantToken(binding),
145094
- messageId,
145095
- reactionId: ackId
145096
- });
145187
+ clearAcknowledge: async (binding, messageId, ackId, outcome) => {
145188
+ const token = await tenantToken(binding);
145189
+ await withDeadline2(
145190
+ LARK_REACTION_TIMEOUT_MS,
145191
+ (signal) => removeReaction({ http: deps.http, token, messageId, reactionId: ackId, signal })
145192
+ );
145193
+ const emojiType = outcome === "done" ? LARK_DONE_REACTION : outcome === "failed" ? LARK_FAILED_REACTION : null;
145194
+ if (emojiType === null) return;
145195
+ try {
145196
+ await withDeadline2(
145197
+ LARK_REACTION_TIMEOUT_MS,
145198
+ (signal) => addReaction({ http: deps.http, token, messageId, emojiType, signal })
145199
+ );
145200
+ } catch (error) {
145201
+ deps.log.warn("lark could not react to the message", { error: describeError(error) });
145202
+ }
145097
145203
  },
145098
145204
  fetchResource: async (binding, input) => {
145099
145205
  const resource = input.resource;
@@ -145330,6 +145436,13 @@ var en_default = {
145330
145436
  turnFailedNoDetail: "This turn didn't finish. Reference {{ref}}. Send the message again; if it keeps failing, send /new to start a new conversation.",
145331
145437
  turnFailedBrief: "This turn didn't finish: {{detail}}\nSend the message again; if it keeps failing, send /new to start a new conversation.",
145332
145438
  turnFailedBriefNoDetail: "This turn didn't finish. Send the message again; if it keeps failing, send /new to start a new conversation.",
145439
+ turnFailedWithReference: "{{message}}\nReference {{ref}}.",
145440
+ turnFailedQuota: "The model quota or balance is insufficient, so this turn didn't finish. Send /model to switch to another model, or ask an administrator to add quota, then try again.",
145441
+ turnFailedRateLimit: "The model service is rate-limiting requests, so this turn didn't finish. Please try again later.",
145442
+ turnFailedContextLimit: "This conversation exceeds the model context limit, so this turn didn't finish. Send /new to start a new conversation, then send your request again.",
145443
+ turnFailedAuth: "The model credentials are missing or invalid, so this turn didn't finish. Ask an administrator to check the model settings, then try again.",
145444
+ turnFailedModelUnavailable: "The current model does not exist or does not support the selected settings, so this turn didn't finish. Send /model to list models, then send /model followed by a number (for example /model 2) to pick one for this chat, and send your request again.",
145445
+ turnFailedTransport: "The model service is temporarily unreachable, so this turn didn't finish. Please try again later.",
145333
145446
  attachmentReceived: "Got {{names}} ({{count}} waiting). Tell me what to do with them.",
145334
145447
  attachmentFailed: "Could not take {{name}}: {{reason}}.",
145335
145448
  attachmentReason: {
@@ -145681,6 +145794,16 @@ var en_default = {
145681
145794
  failed: "Connection failed",
145682
145795
  notSaved: "Save this row first (credentials are read from saved config only).",
145683
145796
  hint: {
145797
+ networkMultipleCauses: "This failure has more than one network cause. Check this machine's network and this bot's Proxy setting, then check again.",
145798
+ networkDns: "The service's domain name could not be resolved. Check this machine's DNS and network settings, then check again.",
145799
+ networkRefused: "The connection was refused. Check whether a firewall or security software on this machine or its network is blocking it, then check again.",
145800
+ networkReset: "The connection was cut off by the other side. Check this machine's network and whether the target service is running normally, then check again.",
145801
+ networkUnreachable: "The target network is unreachable. Check this machine's outbound network access and access restrictions, then check again.",
145802
+ networkTls: "TLS or certificate validation failed. Check this machine's system clock, certificate chain and network settings, and whether a proxy or security software is intercepting HTTPS, then check again.",
145803
+ networkTimeout: "The request timed out. Check whether the target service is running normally and check this machine's network, then check again.",
145804
+ networkProxy: "Proxy connection or authentication failed. Check this row's Proxy setting and the selected proxy's configuration on this machine; if no proxy is set, check other proxies on this machine or its network, then check again.",
145805
+ platformRateLimited: "The platform is rate limiting requests. Try again later.",
145806
+ platformUnavailable: "The target service is temporarily having problems. Try again later.",
145684
145807
  connecting: "Still connecting. There is usually a result within 20 seconds; check again in a moment.",
145685
145808
  badToken: "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key.",
145686
145809
  unauthorized: "Telegram does not accept this token. It may have been revoked; get the current one from BotFather. Save the corrected token here to reconnect automatically; if nothing needs changing, use Reconnect below.",
@@ -146100,6 +146223,13 @@ var zh_CN_default = {
146100
146223
  turnFailedNoDetail: "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u53C2\u8003\u53F7 {{ref}}\u3002\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u4E00\u76F4\u4E0D\u884C\u5C31\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\u3002",
146101
146224
  turnFailedBrief: "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\uFF1A{{detail}}\n\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u4E00\u76F4\u4E0D\u884C\u5C31\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\u3002",
146102
146225
  turnFailedBriefNoDetail: "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u4E00\u76F4\u4E0D\u884C\u5C31\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\u3002",
146226
+ turnFailedWithReference: "{{message}}\n\u53C2\u8003\u53F7 {{ref}}\u3002",
146227
+ turnFailedQuota: "\u6A21\u578B\u989D\u5EA6\u6216\u4F59\u989D\u4E0D\u8DB3\uFF0C\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u53EF\u4EE5\u53D1 /model \u6362\u4E2A\u6A21\u578B\u518D\u8BD5\uFF0C\u6216\u8BF7\u7BA1\u7406\u5458\u8865\u5145\u989D\u5EA6\u540E\u91CD\u8BD5\u3002",
146228
+ turnFailedRateLimit: "\u6A21\u578B\u670D\u52A1\u6B63\u5728\u9650\u6D41\uFF0C\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
146229
+ turnFailedContextLimit: "\u5F53\u524D\u4F1A\u8BDD\u5185\u5BB9\u8D85\u8FC7\u6A21\u578B\u4E0A\u4E0B\u6587\u4E0A\u9650\uFF0C\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u8BF7\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\uFF0C\u518D\u628A\u8BF7\u6C42\u91CD\u65B0\u53D1\u4E00\u904D\u3002",
146230
+ turnFailedAuth: "\u6A21\u578B\u51ED\u636E\u7F3A\u5931\u6216\u65E0\u6548\uFF0C\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u8BF7\u7BA1\u7406\u5458\u68C0\u67E5\u6A21\u578B\u914D\u7F6E\uFF0C\u4E4B\u540E\u4F60\u518D\u53D1\u4E00\u6B21\u3002",
146231
+ turnFailedModelUnavailable: "\u5F53\u524D\u6A21\u578B\u4E0D\u5B58\u5728\u6216\u4E0D\u652F\u6301\u6240\u9009\u914D\u7F6E\uFF0C\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u8BF7\u53D1 /model \u67E5\u770B\u53EF\u9009\u6A21\u578B\uFF0C\u518D\u53D1 /model \u52A0\u7F16\u53F7\uFF08\u4F8B\u5982 /model 2\uFF09\u4E3A\u8FD9\u4E2A\u804A\u5929\u9009\u4E00\u4E2A\uFF0C\u7136\u540E\u91CD\u53D1\u8BF7\u6C42\u3002",
146232
+ turnFailedTransport: "\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u6A21\u578B\u670D\u52A1\uFF0C\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
146103
146233
  attachmentReceived: "\u6536\u5230 {{names}}\uFF08\u73B0\u6709 {{count}} \u4E2A\u5F85\u5904\u7406\uFF09\u3002\u8BF4\u4E00\u53E5\u60F3\u8BA9\u6211\u600E\u4E48\u5904\u7406\u3002",
146104
146234
  attachmentFailed: "\u6CA1\u80FD\u6536\u4E0B {{name}}\uFF1A{{reason}}\u3002",
146105
146235
  attachmentReason: {
@@ -146451,6 +146581,16 @@ var zh_CN_default = {
146451
146581
  failed: "\u8FDE\u63A5\u5931\u8D25",
146452
146582
  notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u68C0\u67E5\u8FDE\u63A5\uFF08\u51ED\u636E\u53EA\u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u8BFB\u53D6\uFF09\u3002",
146453
146583
  hint: {
146584
+ networkMultipleCauses: "\u8FD9\u6B21\u5931\u8D25\u80CC\u540E\u4E0D\u6B62\u4E00\u79CD\u7F51\u7EDC\u95EE\u9898\u3002\u8BF7\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u7684\u7F51\u7EDC\u548C\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u300C\u4EE3\u7406\u300D\u8BBE\u7F6E\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146585
+ networkDns: "\u670D\u52A1\u57DF\u540D\u89E3\u6790\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u7684 DNS \u548C\u7F51\u7EDC\u8BBE\u7F6E\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146586
+ networkRefused: "\u76EE\u6807\u62D2\u7EDD\u4E86\u8FDE\u63A5\u3002\u8BF7\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u548C\u6240\u5728\u7F51\u7EDC\u7684\u9632\u706B\u5899\u3001\u5B89\u5168\u8F6F\u4EF6\u662F\u5426\u62E6\u4E86\u8FD9\u4E2A\u8FDE\u63A5\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146587
+ networkReset: "\u8FDE\u63A5\u88AB\u5BF9\u65B9\u4E2D\u65AD\u3002\u8BF7\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u7684\u7F51\u7EDC\uFF0C\u4EE5\u53CA\u76EE\u6807\u670D\u52A1\u662F\u5426\u5728\u6B63\u5E38\u8FD0\u884C\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146588
+ networkUnreachable: "\u76EE\u6807\u7F51\u7EDC\u4E0D\u53EF\u8FBE\u3002\u8BF7\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u7684\u7F51\u7EDC\u51FA\u53E3\u548C\u8BBF\u95EE\u9650\u5236\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146589
+ networkTls: "TLS \u6216\u8BC1\u4E66\u6821\u9A8C\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u7684\u7CFB\u7EDF\u65F6\u95F4\u3001\u8BC1\u4E66\u94FE\u548C\u7F51\u7EDC\u8BBE\u7F6E\uFF0C\u4EE5\u53CA\u4EE3\u7406\u6216\u5B89\u5168\u8F6F\u4EF6\u662F\u5426\u62E6\u622A\u4E86 HTTPS \u8FDE\u63A5\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146590
+ networkTimeout: "\u8BF7\u6C42\u8D85\u65F6\u3002\u8BF7\u786E\u8BA4\u76EE\u6807\u670D\u52A1\u662F\u5426\u5728\u6B63\u5E38\u8FD0\u884C\uFF0C\u5E76\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u7684\u7F51\u7EDC\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146591
+ networkProxy: "\u4EE3\u7406\u8FDE\u63A5\u6216\u8BA4\u8BC1\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u8FD9\u4E00\u884C\u7684\u300C\u4EE3\u7406\u300D\u8BBE\u7F6E\u548C\u6240\u9009\u4EE3\u7406\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u7684\u914D\u7F6E\uFF1B\u6CA1\u8BBE\u4EE3\u7406\u7684\u8BDD\uFF0C\u68C0\u67E5\u8FD9\u53F0\u673A\u5668\u6216\u6240\u5728\u7F51\u7EDC\u4E0A\u7684\u5176\u4ED6\u4EE3\u7406\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146592
+ platformRateLimited: "\u5E73\u53F0\u6B63\u5728\u9650\u6D41\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
146593
+ platformUnavailable: "\u76EE\u6807\u670D\u52A1\u6682\u65F6\u5F02\u5E38\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
146454
146594
  connecting: "\u8FD8\u5728\u8FDE\u63A5\uFF0C\u4E00\u822C 20 \u79D2\u5185\u4F1A\u6709\u7ED3\u679C\uFF0C\u7A0D\u540E\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
146455
146595
  badToken: "\u8FD9\u4E0D\u50CF\u673A\u5668\u4EBA\u7684 token\u3002\u5230 BotFather \u91CD\u65B0\u590D\u5236\u4E00\u4EFD\uFF1A\u6570\u5B57\u3001\u5192\u53F7\uFF0C\u540E\u9762\u662F\u5BC6\u94A5\u3002",
146456
146596
  unauthorized: "Telegram \u4E0D\u8BA4\u8FD9\u4E2A token\uFF0C\u53EF\u80FD\u5DF2\u7ECF\u4F5C\u5E9F\u3002\u5230 BotFather \u53D6\u73B0\u5728\u7684\u90A3\u4E00\u4EFD\u3002\u6539\u597D\u540E\u5728\u8FD9\u91CC\u4FDD\u5B58\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u91CD\u65B0\u8FDE\u63A5\uFF1B\u6CA1\u6709\u8981\u6539\u7684\u4E5F\u53EF\u4EE5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u3002",
@@ -146856,11 +146996,11 @@ var makeConclusionRecovery = (core, ledger, delivery, failures) => {
146856
146996
  enqueueConclusion(core, ledger, binding, selection, async (owed) => {
146857
146997
  try {
146858
146998
  if (turn.state === "error") {
146859
- const detail = execution.latestTurn?.state === "error" && execution.latestTurn.turnId === turn.turnId ? execution.session?.lastError : void 0;
146999
+ const session = execution.latestTurn?.state === "error" && execution.latestTurn.turnId === turn.turnId ? execution.session : void 0;
146860
147000
  return delivery.sendText(
146861
147001
  binding,
146862
147002
  debt.threadId,
146863
- failures.forFailure(debt.threadId, detail),
147003
+ failures.forFailure(debt.threadId, session?.lastError, session?.failureCategory),
146864
147004
  true,
146865
147005
  turn.turnId,
146866
147006
  owed
@@ -146978,7 +147118,7 @@ var makeNotifications = (core, deps) => {
146978
147118
  (debt) => debt.threadId === threadId
146979
147119
  );
146980
147120
  const { selection, handled, execution } = said || owes || targets.length > 0 ? await conclusions.forTurn(threadId) : { selection: void 0, handled: false, execution: null };
146981
- if (selection !== void 0) {
147121
+ if (selection !== void 0 && selection !== null) {
146982
147122
  const binding = bindingForChatKey(selection.debt.chatKey);
146983
147123
  if (binding !== void 0 && !streamsTo(binding, threadId) && execution?.session?.activeTurnId == null && execution?.latestTurn?.state !== "running" && execution?.latestTurn?.state !== "error" && payload.session?.status !== "error") {
146984
147124
  const turnId = execution?.latestTurn?.turnId ?? null;
@@ -146987,7 +147127,7 @@ var makeNotifications = (core, deps) => {
146987
147127
  return send(binding, { recovered: false, streaming: false, owed });
146988
147128
  });
146989
147129
  }
146990
- } else if (said && !handled) {
147130
+ } else if (selection !== null && said && !handled) {
146991
147131
  const send = await delivery.prepareConclusion(threadId);
146992
147132
  for (const binding of targets) {
146993
147133
  const streaming = streamsTo(binding, threadId);
@@ -147018,16 +147158,17 @@ var makeNotifications = (core, deps) => {
147018
147158
  await ready;
147019
147159
  const targets = targetsForProject(threadId, await projectOfThread(threadId));
147020
147160
  const { selection, handled, execution } = await conclusions.forTurn(threadId);
147021
- const debtBinding = selection === void 0 ? void 0 : bindingForChatKey(selection.debt.chatKey);
147022
- const recipients = selection !== void 0 ? debtBinding === void 0 ? [] : [debtBinding] : targets;
147023
- const textTargets = handled ? [] : recipients.filter(
147161
+ const debtBinding = selection == null ? void 0 : bindingForChatKey(selection.debt.chatKey);
147162
+ const recipients = selection == null ? targets : debtBinding === void 0 ? [] : [debtBinding];
147163
+ const textTargets = handled || selection === null ? [] : recipients.filter(
147024
147164
  (binding) => !(streamsTo(binding, threadId) && turnCards.covers(threadId))
147025
147165
  );
147026
147166
  const drawingTurn = turnCards.drawingTurn(threadId);
147027
- const notice = textTargets.length > 0 || drawingTurn !== null ? failures.forFailure(threadId, readThreadFailed(event)?.detail) : null;
147167
+ const failure2 = readThreadFailed(event);
147168
+ const notice = textTargets.length > 0 || drawingTurn !== null ? failures.forFailure(threadId, failure2?.detail, failure2?.failureCategory) : null;
147028
147169
  if (notice !== null && drawingTurn !== null)
147029
147170
  failures.holdForCard(threadId, drawingTurn, notice);
147030
- if (notice !== null) {
147171
+ if (notice !== null && selection !== null) {
147031
147172
  for (const binding of textTargets) {
147032
147173
  const send = (owed) => delivery.sendText(binding, threadId, notice, false, null, owed);
147033
147174
  enqueueConclusion(core, conclusions, binding, selection, send);
@@ -147602,6 +147743,17 @@ var makeProjectRows = (core) => {
147602
147743
  };
147603
147744
 
147604
147745
  // src/failureNotices.ts
147746
+ var PROVIDER_FAILURES = {
147747
+ quota: { code: "MODEL_QUOTA", messageKey: "reply.turnFailedQuota" },
147748
+ "rate-limit": { code: "MODEL_RATE_LIMIT", messageKey: "reply.turnFailedRateLimit" },
147749
+ "context-limit": { code: "MODEL_CONTEXT_LIMIT", messageKey: "reply.turnFailedContextLimit" },
147750
+ auth: { code: "MODEL_AUTH", messageKey: "reply.turnFailedAuth" },
147751
+ "model-unavailable": {
147752
+ code: "MODEL_UNAVAILABLE",
147753
+ messageKey: "reply.turnFailedModelUnavailable"
147754
+ },
147755
+ transport: { code: "MODEL_TRANSPORT", messageKey: "reply.turnFailedTransport" }
147756
+ };
147605
147757
  var excerptSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
147606
147758
  var graphemeExcerpt = (value2) => {
147607
147759
  const characters = Array.from(
@@ -147639,9 +147791,20 @@ var makeFailureNotices = (core) => {
147639
147791
  return core.t("reply.turnRetrying", { ref: reference2 });
147640
147792
  },
147641
147793
  /** 一次失败通知的那句话(带新参考号,同号进日志):实时失败事件与重启补发各取一次。 */
147642
- forFailure: (threadId, detail) => {
147794
+ forFailure: (threadId, detail, category) => {
147643
147795
  const reference2 = diagnosticRef("msg");
147644
- core.host.log.warn("chat turn failed", { threadId, reference: reference2 });
147796
+ const failure2 = category === void 0 || category === "unknown" ? null : PROVIDER_FAILURES[category];
147797
+ core.host.log.warn("chat turn failed", {
147798
+ threadId,
147799
+ reference: reference2,
147800
+ code: failure2?.code ?? null,
147801
+ detail
147802
+ });
147803
+ if (failure2 !== null)
147804
+ return core.t("reply.turnFailedWithReference", {
147805
+ message: core.t(failure2.messageKey),
147806
+ ref: reference2
147807
+ });
147645
147808
  const said = reasonOf2(detail);
147646
147809
  const text4 = said === "" ? core.t("reply.turnFailedNoDetail", { ref: reference2 }) : core.t("reply.turnFailed", { detail: said, ref: reference2 });
147647
147810
  return text4;
@@ -147654,11 +147817,13 @@ var makeFailureNotices = (core) => {
147654
147817
  * 卡收尾(无论怎么收的都调):清掉这条会话的寄存。失败收尾时,寄存是这一轮的就用它(同一个号),
147655
147818
  * 否则只写原因与下一步,不另取号。不是失败收尾回 null。
147656
147819
  */
147657
- forCard: (threadId, turnId, detail, failed) => {
147820
+ forCard: (threadId, turnId, detail, failed, category) => {
147658
147821
  const held = heldForCard.get(threadId);
147659
147822
  heldForCard.delete(threadId);
147660
147823
  if (!failed) return null;
147661
147824
  if (held !== void 0 && held.turnId === turnId) return held.text;
147825
+ if (category !== void 0 && category !== "unknown")
147826
+ return core.t(PROVIDER_FAILURES[category].messageKey);
147662
147827
  const said = reasonOf2(detail);
147663
147828
  return said === "" ? core.t("reply.turnFailedBriefNoDetail") : core.t("reply.turnFailedBrief", { detail: said });
147664
147829
  }
@@ -148009,7 +148174,8 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
148009
148174
  threadId,
148010
148175
  turnId,
148011
148176
  session?.lastError,
148012
- outcome === "failed"
148177
+ outcome === "failed",
148178
+ session?.failureCategory
148013
148179
  );
148014
148180
  if (errorText2 !== null) return { status: "error", errorText: errorText2 };
148015
148181
  if (stopped || outcome === "stopped") {
@@ -148022,6 +148188,7 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
148022
148188
  finalize: async (threadId, turnId, chatKey, send) => {
148023
148189
  const { selection, handled } = await conclusions.forTurn(threadId, turnId);
148024
148190
  if (handled) return true;
148191
+ if (selection === null) return false;
148025
148192
  if (selection === void 0) return send();
148026
148193
  if (!await conclusions.deliver(selection, send)) return false;
148027
148194
  const binding = bindingForChatKey(chatKey);
@@ -148032,7 +148199,7 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
148032
148199
  },
148033
148200
  fallback: async (threadId, turnId, chatKey, view) => {
148034
148201
  const { selection, handled } = await conclusions.forTurn(threadId, turnId);
148035
- if (handled) return;
148202
+ if (handled || selection === null) return;
148036
148203
  const binding = bindingForChatKey(chatKey);
148037
148204
  if (binding === void 0) return;
148038
148205
  const text4 = [view.body, view.errorText ?? ""].filter((part) => part !== "").join("\n\n");
@@ -148499,12 +148666,14 @@ import * as NodeCrypto6 from "node:crypto";
148499
148666
  // src/telegramApiResponse.ts
148500
148667
  var TelegramApiError = class extends Error {
148501
148668
  code;
148669
+ status;
148502
148670
  /** 限流(429)时平台给的等待秒数;别的错误没有。 */
148503
148671
  retryAfterSeconds;
148504
- constructor(method, code2, description, retryAfterSeconds) {
148505
- super(`telegram ${method} failed (${String(code2 ?? "?")}): ${description}`);
148672
+ constructor(method, code2, description, retryAfterSeconds, options) {
148673
+ super(`telegram ${method} failed (${String(code2 ?? "?")}): ${description}`, options);
148506
148674
  this.name = "TelegramApiError";
148507
148675
  this.code = code2;
148676
+ this.status = options?.status;
148508
148677
  this.retryAfterSeconds = retryAfterSeconds;
148509
148678
  }
148510
148679
  };
@@ -148519,7 +148688,8 @@ var readTelegramResponse = async (method, response) => {
148519
148688
  method,
148520
148689
  response.status === 401 ? 401 : envelope?.error_code ?? (response.ok ? void 0 : response.status),
148521
148690
  envelope?.description ?? "no reason",
148522
- envelope?.parameters?.retry_after
148691
+ envelope?.parameters?.retry_after,
148692
+ { status: response.status }
148523
148693
  );
148524
148694
  }
148525
148695
  return envelope.result;
@@ -148653,7 +148823,13 @@ var makeTelegramApi = (input) => {
148653
148823
  return await input.http(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
148654
148824
  } catch (error) {
148655
148825
  if (error instanceof Error && error.name === "TimeoutError") {
148656
- throw new TelegramApiError(method, void 0, `timed out after ${String(timeoutMs)} ms`);
148826
+ throw new TelegramApiError(
148827
+ method,
148828
+ void 0,
148829
+ `timed out after ${String(timeoutMs)} ms`,
148830
+ void 0,
148831
+ { cause: error }
148832
+ );
148657
148833
  }
148658
148834
  throw error;
148659
148835
  }
@@ -148878,6 +149054,98 @@ var TELEGRAM_MENU = [
148878
149054
  ];
148879
149055
  var telegramCommandMenu = (t) => TELEGRAM_MENU.map((item) => ({ command: item.command, description: t(item.key) }));
148880
149056
 
149057
+ // src/telegramConnectionCheck.ts
149058
+ var webhookHost = (url) => {
149059
+ try {
149060
+ return new URL(url).host;
149061
+ } catch {
149062
+ return "(unreadable)";
149063
+ }
149064
+ };
149065
+ var NOT_BOT_DETAIL = "token is not a bot's";
149066
+ var webhookActiveDetail = (url) => `webhook is set (host ${webhookHost(url)})`;
149067
+ var transportFailure = (stage, error) => ({
149068
+ ok: false,
149069
+ stage,
149070
+ detail: describeError(error),
149071
+ error
149072
+ });
149073
+ var probeTelegramBot = async (api) => {
149074
+ let own;
149075
+ try {
149076
+ own = await api.getMe();
149077
+ } catch (error) {
149078
+ return transportFailure("getMe", error);
149079
+ }
149080
+ if (own.is_bot !== true) {
149081
+ return { ok: false, stage: "getMe", hint: "notBot", detail: NOT_BOT_DETAIL };
149082
+ }
149083
+ let webhook = "";
149084
+ try {
149085
+ webhook = (await api.getWebhookInfo()).url ?? "";
149086
+ } catch (error) {
149087
+ return transportFailure("getWebhookInfo", error);
149088
+ }
149089
+ if (webhook !== "") {
149090
+ return {
149091
+ ok: false,
149092
+ stage: "getWebhookInfo",
149093
+ hint: "webhookActive",
149094
+ detail: webhookActiveDetail(webhook)
149095
+ };
149096
+ }
149097
+ return { ok: true, username: own.username, id: own.id };
149098
+ };
149099
+ var TelegramConnectGateError = class extends Error {
149100
+ hint;
149101
+ constructor(hint, detail) {
149102
+ super(detail);
149103
+ this.name = "TelegramConnectGateError";
149104
+ this.hint = hint;
149105
+ }
149106
+ };
149107
+ var assertTelegramPollingReady = async (api) => {
149108
+ const probed = await probeTelegramBot(api);
149109
+ if (!probed.ok) {
149110
+ if ("error" in probed) throw probed.error;
149111
+ throw new TelegramConnectGateError(probed.hint, probed.detail);
149112
+ }
149113
+ return { username: probed.username ?? null, id: probed.id };
149114
+ };
149115
+ var telegramPollingFailureHint = (error) => error instanceof TelegramConnectGateError ? error.hint : error instanceof TelegramApiError && error.code === 409 ? "webhookActive" : void 0;
149116
+ var checkTelegramConnection = async (bot, token, apiFor) => {
149117
+ if (token === "") {
149118
+ return { stage: "credentials", ok: false, detail: "missing bot token in saved config" };
149119
+ }
149120
+ if (!isTelegramToken(token)) {
149121
+ return {
149122
+ stage: "token-format",
149123
+ ok: false,
149124
+ detail: "saved token is not in the BotFather format",
149125
+ hint: "badToken"
149126
+ };
149127
+ }
149128
+ const api = await apiFor(bot);
149129
+ const probed = await probeTelegramBot(api);
149130
+ if (probed.ok) {
149131
+ const name = probed.username;
149132
+ const username = name !== void 0 && name !== "" ? `@${name}` : "(no username)";
149133
+ return { stage: "getMe", ok: true, detail: `username=${username} webhook=none` };
149134
+ }
149135
+ if ("error" in probed) {
149136
+ return {
149137
+ stage: probed.stage,
149138
+ ok: false,
149139
+ detail: probed.detail,
149140
+ ...connectionHintFields(
149141
+ probed.error,
149142
+ probed.stage === "getMe" && isTelegramCredentialsRejected(probed.error) ? "unauthorized" : void 0
149143
+ )
149144
+ };
149145
+ }
149146
+ return { stage: probed.stage, ok: false, detail: probed.detail, hint: probed.hint };
149147
+ };
149148
+
148881
149149
  // src/telegramChoice.ts
148882
149150
  var renderChoiceCard = (card) => ({
148883
149151
  text: [card.title, card.heading ?? "", card.body, card.note ?? ""].filter((part) => part !== "").join("\n\n"),
@@ -149069,15 +149337,13 @@ var tokenFingerprint = (bot) => NodeCrypto6.createHash("sha256").update(tokenOf(
149069
149337
  var REACTION_WORKING = "\u{1F440}";
149070
149338
  var REACTION_DONE = "\u{1F44D}";
149071
149339
  var REACTION_FAILED = "\u{1F44E}";
149072
- var telegramConfigIssues = (binding) => tokenOf(binding) === "" ? [{ column: "telegramToken", code: "required" }] : [];
149073
- var defaultSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
149074
- var webhookHost = (url) => {
149075
- try {
149076
- return new URL(url).host;
149077
- } catch {
149078
- return "(unreadable)";
149079
- }
149340
+ var telegramConfigIssues = (binding) => {
149341
+ const token = tokenOf(binding);
149342
+ if (token === "") return [{ column: "telegramToken", code: "required" }];
149343
+ if (!isTelegramToken(token)) return [{ column: "telegramToken", code: "format" }];
149344
+ return [];
149080
149345
  };
149346
+ var defaultSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
149081
149347
  var makeTelegramAdapter = (deps) => {
149082
149348
  const apis = /* @__PURE__ */ new Map();
149083
149349
  const sleep4 = deps.sleep ?? defaultSleep;
@@ -149179,7 +149445,7 @@ var makeTelegramAdapter = (deps) => {
149179
149445
  quotesReplies: true,
149180
149446
  columns: COLUMNS2,
149181
149447
  validate: telegramConfigIssues,
149182
- isConfigured: (binding) => telegramConfigIssues(binding).length === 0,
149448
+ isConfigured: (binding) => telegramConfigIssues(binding).every((issue) => issue.code !== "required"),
149183
149449
  connect: async (binding, handlers) => {
149184
149450
  let stop = () => {
149185
149451
  };
@@ -149217,8 +149483,8 @@ var makeTelegramAdapter = (deps) => {
149217
149483
  liveApi = api;
149218
149484
  if (!running) return;
149219
149485
  if (botUsername == null) {
149220
- const own = await api.getMe();
149221
- botUsername = own.username ?? null;
149486
+ const own = await assertTelegramPollingReady(api);
149487
+ botUsername = own.username;
149222
149488
  botId = own.id;
149223
149489
  if (running) handlers.onConnectionState?.({ state: "connected" });
149224
149490
  }
@@ -149344,7 +149610,12 @@ var makeTelegramAdapter = (deps) => {
149344
149610
  });
149345
149611
  return;
149346
149612
  }
149347
- if (checkingConnection) handlers.onConnectionState?.({ state: "failed", error });
149613
+ if (checkingConnection)
149614
+ handlers.onConnectionState?.({
149615
+ state: "failed",
149616
+ error,
149617
+ ...connectionHintFields(error, telegramPollingFailureHint(error))
149618
+ });
149348
149619
  handlers.onError(error instanceof Error ? error.message : String(error));
149349
149620
  await Promise.race([(deps.sleep ?? defaultSleep)(backoff), stopped]);
149350
149621
  backoff = Math.min(backoff * 2, BACKOFF_MAX_MS);
@@ -149366,50 +149637,7 @@ var makeTelegramAdapter = (deps) => {
149366
149637
  const api = await apiFor(binding);
149367
149638
  await api.sendMessage(binding.chatId, text4, options?.retry, binding.reply?.messageId);
149368
149639
  },
149369
- checkConnection: async (bot) => {
149370
- const token = tokenOf(bot);
149371
- if (token === "") {
149372
- return { stage: "credentials", ok: false, detail: "missing bot token in saved config" };
149373
- }
149374
- if (!isTelegramToken(token)) {
149375
- return {
149376
- stage: "token-format",
149377
- ok: false,
149378
- detail: "saved token is not in the BotFather format",
149379
- hint: "badToken"
149380
- };
149381
- }
149382
- const api = await apiFor(bot);
149383
- let username;
149384
- try {
149385
- const me = await api.getMe();
149386
- if (me.is_bot !== true) {
149387
- return { stage: "getMe", ok: false, detail: "token is not a bot's", hint: "notBot" };
149388
- }
149389
- username = me.username !== void 0 && me.username !== "" ? `@${me.username}` : "(no username)";
149390
- } catch (error) {
149391
- return {
149392
- stage: "getMe",
149393
- ok: false,
149394
- detail: describeError(error),
149395
- ...isTelegramCredentialsRejected(error) ? { hint: "unauthorized" } : {}
149396
- };
149397
- }
149398
- try {
149399
- const webhook = (await api.getWebhookInfo()).url ?? "";
149400
- if (webhook !== "") {
149401
- return {
149402
- stage: "getWebhookInfo",
149403
- ok: false,
149404
- detail: `webhook is set (host ${webhookHost(webhook)})`,
149405
- hint: "webhookActive"
149406
- };
149407
- }
149408
- } catch (error) {
149409
- return { stage: "getWebhookInfo", ok: false, detail: describeError(error) };
149410
- }
149411
- return { stage: "getMe", ok: true, detail: `username=${username} webhook=none` };
149412
- },
149640
+ checkConnection: (bot) => checkTelegramConnection(bot, tokenOf(bot), apiFor),
149413
149641
  sendTyping: async (binding) => {
149414
149642
  await (await apiFor(binding)).sendTyping(binding.chatId);
149415
149643
  },
@@ -150150,7 +150378,13 @@ var probe = async (adapters, bot) => {
150150
150378
  result2.hint
150151
150379
  );
150152
150380
  } catch (error) {
150153
- return reportOf(bot, "check", false, redactor.redact(describeError(error)));
150381
+ return reportOf(
150382
+ bot,
150383
+ "check",
150384
+ false,
150385
+ redactor.redact(describeError(error)),
150386
+ connectionHint(error)
150387
+ );
150154
150388
  }
150155
150389
  };
150156
150390
  var emitReport = (emit, report, action) => {
@@ -150676,6 +150910,16 @@ var makeWechatAdapter = (deps) => {
150676
150910
  };
150677
150911
  };
150678
150912
 
150913
+ // src/wecomAckError.ts
150914
+ var WecomAckError = class extends Error {
150915
+ code;
150916
+ constructor(code2, msg) {
150917
+ super(`WeCom ack failed: ${String(code2)} ${msg}`);
150918
+ this.name = "WecomAckError";
150919
+ this.code = code2;
150920
+ }
150921
+ };
150922
+
150679
150923
  // src/wecomUpload.ts
150680
150924
  import * as NodeCrypto8 from "node:crypto";
150681
150925
  var WECOM_CHUNK_BYTES = 512 * 1024;
@@ -150888,15 +151132,12 @@ var parseWecomMessage = (frame, botId) => {
150888
151132
  };
150889
151133
  };
150890
151134
  var splitForWecom = (text4, limit = WECOM_TEXT_MAX_CHARS) => splitMessage(text4, limit);
150891
- var decoder = new TextDecoder();
150892
- var WecomAckError = class extends Error {
150893
- code;
150894
- constructor(code2, msg) {
150895
- super(`WeCom ack failed: ${String(code2)} ${msg}`);
150896
- this.name = "WecomAckError";
150897
- this.code = code2;
151135
+ var sendWecomText = async (text4, sendChunk) => {
151136
+ for (const [index, content] of splitForWecom(text4).entries()) {
151137
+ await sendChunk(content, index === 0);
150898
151138
  }
150899
151139
  };
151140
+ var decoder = new TextDecoder();
150900
151141
  var subscriptionHint = (error) => {
150901
151142
  if (!(error instanceof WecomAckError)) return void 0;
150902
151143
  switch (error.code) {
@@ -151029,41 +151270,39 @@ var runWecomClient = (input) => {
151029
151270
  return false;
151030
151271
  }
151031
151272
  );
151032
- const sendText = async (chatId, text4) => {
151033
- for (const [index, content] of splitForWecom(text4).entries()) {
151034
- const inboundRequestId = index === 0 ? pendingRequests.get(chatId) : void 0;
151035
- if (inboundRequestId !== void 0) {
151036
- pendingRequests.delete(chatId);
151037
- await send({
151038
- cmd: "aibot_respond_msg",
151039
- headers: { req_id: inboundRequestId },
151040
- body: {
151041
- msgtype: "stream",
151042
- stream: { id: nextRequestId("stream"), finish: true, content }
151043
- }
151044
- });
151045
- continue;
151046
- }
151047
- const requestId = nextRequestId("aibot_send_msg");
151048
- const ack = new Promise((resolve2, reject) => {
151049
- pendingAcks.set(requestId, { resolve: () => resolve2(), reject });
151050
- setTimeout(() => {
151051
- if (pendingAcks.delete(requestId)) resolve2();
151052
- }, SEND_ACK_TIMEOUT_MS);
151273
+ const sendTextChunk = async (chatId, content, firstChunk) => {
151274
+ const inboundRequestId = firstChunk ? pendingRequests.get(chatId) : void 0;
151275
+ if (inboundRequestId !== void 0) {
151276
+ pendingRequests.delete(chatId);
151277
+ await send({
151278
+ cmd: "aibot_respond_msg",
151279
+ headers: { req_id: inboundRequestId },
151280
+ body: {
151281
+ msgtype: "stream",
151282
+ stream: { id: nextRequestId("stream"), finish: true, content }
151283
+ }
151053
151284
  });
151054
- try {
151055
- await Promise.all([
151056
- send({
151057
- cmd: "aibot_send_msg",
151058
- headers: { req_id: requestId },
151059
- body: { chatid: chatId, msgtype: "markdown", markdown: { content } }
151060
- }),
151061
- ack
151062
- ]);
151063
- } catch (error) {
151064
- pendingAcks.delete(requestId);
151065
- throw error;
151066
- }
151285
+ return;
151286
+ }
151287
+ const requestId = nextRequestId("aibot_send_msg");
151288
+ const ack = new Promise((resolve2, reject) => {
151289
+ pendingAcks.set(requestId, { resolve: () => resolve2(), reject });
151290
+ setTimeout(() => {
151291
+ if (pendingAcks.delete(requestId)) resolve2();
151292
+ }, SEND_ACK_TIMEOUT_MS);
151293
+ });
151294
+ try {
151295
+ await Promise.all([
151296
+ send({
151297
+ cmd: "aibot_send_msg",
151298
+ headers: { req_id: requestId },
151299
+ body: { chatid: chatId, msgtype: "markdown", markdown: { content } }
151300
+ }),
151301
+ ack
151302
+ ]);
151303
+ } catch (error) {
151304
+ pendingAcks.delete(requestId);
151305
+ throw error;
151067
151306
  }
151068
151307
  };
151069
151308
  const request = async (cmd, body) => {
@@ -151140,7 +151379,8 @@ var runWecomClient = (input) => {
151140
151379
  };
151141
151380
  };
151142
151381
  return {
151143
- sendText,
151382
+ sendText: (chatId, text4) => sendWecomText(text4, (content, firstChunk) => sendTextChunk(chatId, content, firstChunk)),
151383
+ sendTextChunk,
151144
151384
  sendMedia,
151145
151385
  rememberConversation: (chatId, conversation) => {
151146
151386
  conversations.set(chatId, conversation);
@@ -151245,6 +151485,38 @@ var fetchWecomMedia = async (input) => {
151245
151485
  return { bytes, name };
151246
151486
  };
151247
151487
 
151488
+ // src/wecomSendError.ts
151489
+ var WecomSendError = class extends Error {
151490
+ operation;
151491
+ code;
151492
+ providerCode;
151493
+ constructor(cause, operation, code2, providerCode) {
151494
+ super(
151495
+ providerCode === void 0 && cause instanceof Error ? cause.message : `WeCom ${operation} failed (${code2}, provider=${providerCode ?? "-"})`,
151496
+ { cause }
151497
+ );
151498
+ this.name = "WecomSendError";
151499
+ this.operation = operation;
151500
+ this.code = code2;
151501
+ this.providerCode = providerCode;
151502
+ }
151503
+ };
151504
+ var wecomSendError = (error, operation) => {
151505
+ if (error instanceof ImNotConnectedError || error instanceof WecomSendError || error instanceof Error && error.name === "AbortError" || error instanceof TypeError || error instanceof ReferenceError || error instanceof SyntaxError)
151506
+ return error;
151507
+ const providerCode = error instanceof WecomAckError ? error.code : void 0;
151508
+ let code2 = providerCode === void 0 ? "channel-delivery-uncertain" : "channel-delivery-failed";
151509
+ if (providerCode === 48002) code2 = "channel-permission";
151510
+ if (providerCode === 45009) code2 = "channel-rate-limit";
151511
+ return new WecomSendError(error, operation, code2, providerCode);
151512
+ };
151513
+ var wecomSendDiagnostic = (error) => error instanceof WecomSendError ? {
151514
+ operation: error.operation,
151515
+ code: error.code,
151516
+ providerCode: error.providerCode
151517
+ } : void 0;
151518
+ var wecomSendVerdict = (error) => error instanceof ImNotConnectedError || error instanceof WecomSendError && error.code === "channel-rate-limit" ? { retry: true } : NO_RETRY;
151519
+
151248
151520
  // src/wecomAdapter.ts
151249
151521
  var WECOM_CARD_THROTTLE_MS = 400;
151250
151522
  var LIVE_STREAM_LIMIT = 200;
@@ -151271,6 +151543,16 @@ var makeWecomAdapter = (deps) => {
151271
151543
  const clients = /* @__PURE__ */ new Map();
151272
151544
  const managedConnections = /* @__PURE__ */ new Map();
151273
151545
  const t = deps.translate;
151546
+ const sending = async (operation, request) => {
151547
+ try {
151548
+ return await request();
151549
+ } catch (error) {
151550
+ const failure2 = wecomSendError(error, operation);
151551
+ const diagnostic = wecomSendDiagnostic(failure2);
151552
+ if (diagnostic !== void 0) deps.log.warn("wecom send failed", diagnostic);
151553
+ throw failure2;
151554
+ }
151555
+ };
151274
151556
  const liveStreams = /* @__PURE__ */ new Map();
151275
151557
  const rememberStream = (key, entry) => {
151276
151558
  liveStreams.set(key, entry);
@@ -151291,19 +151573,19 @@ var makeWecomAdapter = (deps) => {
151291
151573
  shown = `${t("card.truncated", { count: body.length - kept.length })}
151292
151574
  ${kept}`;
151293
151575
  }
151294
- await entry.stream.send(joinParts(head, shown, foot), false);
151576
+ await sending("replyStream", () => entry.stream.send(joinParts(head, shown, foot), false));
151295
151577
  return;
151296
151578
  }
151297
151579
  const chunks = splitForWecom(joinParts(head, body, foot));
151298
- const delivered = await entry.stream.send(chunks[0] ?? "", true);
151580
+ const delivered = await sending("replyStream", () => entry.stream.send(chunks[0] ?? "", true));
151299
151581
  for (const chunk of delivered ? chunks.slice(1) : chunks) {
151300
- await entry.client.sendText(entry.chatId, chunk);
151582
+ await sending("sendMessage", () => entry.client.sendText(entry.chatId, chunk));
151301
151583
  }
151302
151584
  };
151303
151585
  const sendMedia = async (binding, kind, media) => {
151304
151586
  const client = clients.get(binding.rowId);
151305
151587
  if (client === void 0) throw new Error("WeCom is not connected right now");
151306
- await client.sendMedia(binding.chatId, kind, media);
151588
+ await sending("sendMedia", () => client.sendMedia(binding.chatId, kind, media));
151307
151589
  deps.log.info("wecom delivered a file", { name: media.name, as: kind });
151308
151590
  };
151309
151591
  return {
@@ -151418,13 +151700,26 @@ ${kept}`;
151418
151700
  }
151419
151701
  };
151420
151702
  },
151421
- // 不看 `options.retry`:长连接的回执没有核实过的限流码,也没有幂等键,失败一律说不清发没发出去。
151422
- sendText: async (binding, text4) => {
151423
- const client = clients.get(binding.rowId);
151424
- if (client === void 0) {
151425
- throw new ImNotConnectedError(`wecom bot '${binding.displayName}' has no connection.`);
151426
- }
151427
- await client.sendText(binding.chatId, text4);
151703
+ sendText: async (binding, text4, options) => {
151704
+ await sendWecomText(
151705
+ text4,
151706
+ (chunk, firstChunk) => sendWithRetry(
151707
+ async () => {
151708
+ const client = clients.get(binding.rowId);
151709
+ if (client === void 0) {
151710
+ throw new ImNotConnectedError(
151711
+ `wecom bot '${binding.displayName}' has no connection.`
151712
+ );
151713
+ }
151714
+ await sending(
151715
+ "sendMessage",
151716
+ () => client.sendTextChunk(binding.chatId, chunk, firstChunk)
151717
+ );
151718
+ },
151719
+ wecomSendVerdict,
151720
+ options?.retry
151721
+ )
151722
+ );
151428
151723
  },
151429
151724
  checkConnection: async (bot) => {
151430
151725
  const managed = managedConnections.get(bot.rowId);
@@ -151498,7 +151793,10 @@ ${kept}`;
151498
151793
  }
151499
151794
  },
151500
151795
  askApproval: async (binding, request) => {
151501
- await (clients.get(binding.rowId)?.sendText(binding.chatId, numberedApprovalText(request)) ?? Promise.resolve());
151796
+ const client = clients.get(binding.rowId);
151797
+ const text4 = numberedApprovalText(request);
151798
+ if (client !== void 0)
151799
+ await sending("sendMessage", () => client.sendText(binding.chatId, text4));
151502
151800
  return null;
151503
151801
  },
151504
151802
  turnCards: {
@@ -151555,6 +151853,7 @@ var REQUEST_TIMEOUT_MS = 15e3;
151555
151853
  var MEDIA_TIMEOUT_MS = 6e4;
151556
151854
  var WecomAppApiError = class extends Error {
151557
151855
  code;
151856
+ status;
151558
151857
  providerCode;
151559
151858
  hint;
151560
151859
  invalidUsers;
@@ -151562,6 +151861,7 @@ var WecomAppApiError = class extends Error {
151562
151861
  super(message);
151563
151862
  this.name = "WecomAppApiError";
151564
151863
  this.code = code2;
151864
+ this.status = extras?.status;
151565
151865
  this.providerCode = extras?.providerCode;
151566
151866
  this.hint = extras?.hint;
151567
151867
  this.invalidUsers = extras?.invalidUsers ?? [];
@@ -151771,30 +152071,31 @@ var WecomAppApi = class {
151771
152071
  });
151772
152072
  }
151773
152073
  }
151774
- #apiError(errcode, errmsg, invalidUsers) {
152074
+ #apiError(errcode, errmsg, invalidUsers, status) {
151775
152075
  const code2 = String(errcode);
152076
+ const details = { providerCode: code2, status };
151776
152077
  const message = typeof errmsg === "string" && errmsg !== "" ? `WeCom API failed (${code2}): ${errmsg}` : `WeCom API failed (${code2}).`;
151777
152078
  if (TOKEN_INVALID_CODES.has(code2)) this.invalidateToken();
151778
152079
  if (errcode === 60020) {
151779
152080
  return new WecomAppApiError("trusted-ip", "WeCom rejected the outbound IP (errcode 60020).", {
151780
- providerCode: code2,
152081
+ ...details,
151781
152082
  hint: "wecomTrustedIp"
151782
152083
  });
151783
152084
  }
151784
152085
  if (errcode === 81013) {
151785
152086
  return new WecomAppApiError("invalid-user", message, {
151786
- providerCode: code2,
152087
+ ...details,
151787
152088
  hint: "wecomInvalidUser",
151788
152089
  invalidUsers: parseInvalidUsers(invalidUsers)
151789
152090
  });
151790
152091
  }
151791
152092
  if (errcode === 40056) {
151792
152093
  return new WecomAppApiError("invalid-agent", message, {
151793
- providerCode: code2,
152094
+ ...details,
151794
152095
  hint: "wecomInvalidAgent"
151795
152096
  });
151796
152097
  }
151797
- return new WecomAppApiError("api-failed", message, { providerCode: code2 });
152098
+ return new WecomAppApiError("api-failed", message, details);
151798
152099
  }
151799
152100
  async #withRequest(url, init, timeoutMs, read) {
151800
152101
  const deadline = new AbortController();
@@ -151821,15 +152122,29 @@ var WecomAppApi = class {
151821
152122
  } catch {
151822
152123
  throw new WecomAppApiError(
151823
152124
  "bad-response",
151824
- `WeCom returned non-JSON (HTTP ${String(response.status)}).`
152125
+ `WeCom returned non-JSON (HTTP ${String(response.status)}).`,
152126
+ { status: response.status }
151825
152127
  );
151826
152128
  }
151827
152129
  if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
151828
- throw new WecomAppApiError("bad-response", "WeCom returned a non-object JSON body.");
152130
+ throw new WecomAppApiError("bad-response", "WeCom returned a non-object JSON body.", {
152131
+ status: response.status
152132
+ });
151829
152133
  }
151830
152134
  const result2 = payload;
151831
152135
  const errcode = Number(result2["errcode"] ?? 0);
151832
- if (errcode !== 0) throw this.#apiError(errcode, result2["errmsg"], result2["invaliduser"]);
152136
+ if (errcode !== 0) {
152137
+ throw this.#apiError(errcode, result2["errmsg"], result2["invaliduser"], response.status);
152138
+ }
152139
+ if (!response.ok) {
152140
+ throw new WecomAppApiError(
152141
+ "bad-response",
152142
+ `WeCom returned HTTP ${String(response.status)}.`,
152143
+ {
152144
+ status: response.status
152145
+ }
152146
+ );
152147
+ }
151833
152148
  return result2;
151834
152149
  });
151835
152150
  }
@@ -152565,7 +152880,10 @@ var makeWecomAppAdapter = (deps) => {
152565
152880
  stage: "gettoken",
152566
152881
  ok: false,
152567
152882
  detail: describeError(error),
152568
- ...error instanceof WecomAppApiError && error.hint !== void 0 ? { hint: error.hint } : {}
152883
+ ...connectionHintFields(
152884
+ error,
152885
+ error instanceof WecomAppApiError ? error.hint : void 0
152886
+ )
152569
152887
  };
152570
152888
  }
152571
152889
  },
@@ -152584,7 +152902,7 @@ var makeWecomAppAdapter = (deps) => {
152584
152902
  const report = (state) => {
152585
152903
  if (closed) return;
152586
152904
  handlers.onConnectionState?.(
152587
- state.state === "failed" && state.error instanceof WecomAppApiError && state.error.hint !== void 0 ? { ...state, hint: state.error.hint } : state
152905
+ state.state === "failed" && state.error instanceof WecomAppApiError && state.error.hint !== void 0 ? { ...state, hint: state.error.hint } : state.state === "failed" ? { ...state, ...connectionHintFields(state.error, state.hint) } : state
152588
152906
  );
152589
152907
  };
152590
152908
  apis.set(bot.rowId, { api, report });