opencode-tbot 0.1.10 → 0.1.12

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/plugin.js CHANGED
@@ -211,18 +211,28 @@ function buildOpenCodeSdkConfig(options) {
211
211
  ...apiKey ? { auth: apiKey } : {}
212
212
  };
213
213
  }
214
+ var OpenCodePromptTimeoutError = class extends Error {
215
+ data;
216
+ constructor(data) {
217
+ super(data.message ?? "OpenCode prompt timed out.");
218
+ this.name = "OpenCodePromptTimeoutError";
219
+ this.data = data;
220
+ }
221
+ };
214
222
  var EMPTY_RESPONSE_TEXT = "OpenCode returned empty response.";
215
- var PROMPT_MESSAGE_POLL_DELAYS_MS = [
223
+ var PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS = [
216
224
  0,
217
225
  100,
218
226
  250,
219
227
  500,
220
- 1e3,
221
- 2e3,
222
- 4e3,
223
- 8e3
228
+ 1e3
224
229
  ];
230
+ var PROMPT_MESSAGE_POLL_INTERVAL_MS = 2e3;
231
+ var PROMPT_POLL_REQUEST_TIMEOUT_MS = 15e3;
232
+ var PROMPT_SEND_TIMEOUT_MS = 3e4;
233
+ var PROMPT_MESSAGE_POLL_TIMEOUT_MS = 6e4;
225
234
  var PROMPT_MESSAGE_POLL_LIMIT = 20;
235
+ var PROMPT_LOG_SERVICE = "opencode-tbot";
226
236
  var STRUCTURED_REPLY_SCHEMA = {
227
237
  type: "json_schema",
228
238
  retryCount: 2,
@@ -244,6 +254,11 @@ var StructuredReplySchema = z.object({ body_md: z.string() });
244
254
  var OpenCodeClient = class {
245
255
  client;
246
256
  fetchFn;
257
+ promptRequestTimeouts = {
258
+ pollRequestMs: PROMPT_POLL_REQUEST_TIMEOUT_MS,
259
+ sendMs: PROMPT_SEND_TIMEOUT_MS,
260
+ totalPollMs: PROMPT_MESSAGE_POLL_TIMEOUT_MS
261
+ };
247
262
  modelCache = {
248
263
  expiresAt: 0,
249
264
  promise: null,
@@ -288,8 +303,7 @@ var OpenCodeClient = class {
288
303
  return unwrapSdkData(await this.client.mcp.status(directory ? { directory } : void 0, SDK_OPTIONS));
289
304
  }
290
305
  async getSessionStatuses() {
291
- if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/session/status" });
292
- return unwrapSdkData(await this.client.session.status(void 0, SDK_OPTIONS));
306
+ return this.loadSessionStatuses();
293
307
  }
294
308
  async listProjects() {
295
309
  if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", { url: "/project" });
@@ -376,117 +390,162 @@ var OpenCodeClient = class {
376
390
  if (parts.length === 0) throw new Error("Prompt requires text or file attachments.");
377
391
  const knownMessageIds = await this.captureKnownMessageIds(input.sessionId);
378
392
  const initialData = await this.sendPromptRequest(input, parts);
379
- return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData, knownMessageIds), {
393
+ return buildPromptSessionResult(await this.resolvePromptResponse(input, initialData, knownMessageIds, startedAt), {
380
394
  emptyResponseText: EMPTY_RESPONSE_TEXT,
381
395
  finishedAt: Date.now(),
382
396
  startedAt,
383
397
  structured: input.structured ?? false
384
398
  });
385
399
  }
386
- async resolvePromptResponse(input, data, knownMessageIds) {
400
+ async resolvePromptResponse(input, data, knownMessageIds, startedAt) {
387
401
  const structured = input.structured ?? false;
388
402
  if (!shouldPollPromptMessage(data, structured)) return data;
389
403
  const messageId = extractMessageId(data.info);
390
- let expectedParentId = toAssistantMessage(data.info)?.parentID ?? null;
391
- let bestCandidate = data;
392
- let hasWaited = false;
393
- for (const delayMs of PROMPT_MESSAGE_POLL_DELAYS_MS) {
404
+ const candidateOptions = {
405
+ initialMessageId: messageId,
406
+ initialParentId: toAssistantMessage(data.info)?.parentID ?? null,
407
+ knownMessageIds,
408
+ requestStartedAt: resolvePromptCandidateStartTime(startedAt, data),
409
+ structured
410
+ };
411
+ let bestCandidate = selectPromptResponseCandidate([data], candidateOptions) ?? data;
412
+ const deadlineAt = Date.now() + this.promptRequestTimeouts.totalPollMs;
413
+ let idleStatusSeen = false;
414
+ let attempt = 0;
415
+ while (true) {
416
+ const delayMs = getPromptMessagePollDelayMs(attempt);
417
+ attempt += 1;
394
418
  if (delayMs > 0) {
395
- await delay(delayMs);
396
- hasWaited = true;
419
+ const remainingMs = deadlineAt - Date.now();
420
+ if (remainingMs <= 0) break;
421
+ await delay(Math.min(delayMs, remainingMs));
397
422
  }
398
423
  if (messageId) {
399
424
  const next = await this.fetchPromptMessage(input.sessionId, messageId);
400
425
  if (next) {
401
- bestCandidate = selectPromptResponseCandidate([bestCandidate, next], {
402
- initialMessageId: messageId,
403
- initialParentId: expectedParentId,
404
- knownMessageIds,
405
- structured
406
- }) ?? bestCandidate;
407
- expectedParentId = toAssistantMessage(next.info)?.parentID ?? expectedParentId;
408
- if (!shouldPollPromptMessage(next, structured)) return next;
426
+ bestCandidate = selectPromptResponseCandidate([bestCandidate, next], candidateOptions) ?? bestCandidate;
427
+ if (!shouldPollPromptMessage(next, structured)) return bestCandidate;
409
428
  }
410
429
  }
411
- const latest = await this.findLatestPromptResponse(input.sessionId, {
412
- initialMessageId: messageId,
413
- initialParentId: expectedParentId,
414
- knownMessageIds,
415
- structured
416
- });
430
+ const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "poll-messages");
417
431
  if (latest) {
418
- bestCandidate = selectPromptResponseCandidate([bestCandidate, latest], {
419
- initialMessageId: messageId,
420
- initialParentId: expectedParentId,
421
- knownMessageIds,
422
- structured
423
- }) ?? bestCandidate;
424
- expectedParentId = toAssistantMessage(latest.info)?.parentID ?? expectedParentId;
425
- if (!shouldPollPromptMessage(latest, structured)) return latest;
432
+ bestCandidate = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
433
+ if (!shouldPollPromptMessage(bestCandidate, structured)) return bestCandidate;
426
434
  }
427
- if ((await this.fetchPromptSessionStatus(input.sessionId))?.type === "idle" && hasWaited) break;
435
+ if ((await this.fetchPromptSessionStatus(input.sessionId))?.type === "idle") {
436
+ if (idleStatusSeen) break;
437
+ idleStatusSeen = true;
438
+ }
439
+ if (Date.now() >= deadlineAt) break;
428
440
  }
429
- const latest = await this.findLatestPromptResponse(input.sessionId, {
430
- initialMessageId: messageId,
431
- initialParentId: expectedParentId,
432
- knownMessageIds,
433
- structured
434
- });
435
- return selectPromptResponseCandidate([bestCandidate, latest], {
436
- initialMessageId: messageId,
437
- initialParentId: expectedParentId,
438
- knownMessageIds,
439
- structured
440
- }) ?? bestCandidate;
441
+ const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "final-scan");
442
+ const resolved = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions) ?? bestCandidate;
443
+ if (shouldPollPromptMessage(resolved, structured)) {
444
+ const error = createOpenCodePromptTimeoutError({
445
+ sessionId: input.sessionId,
446
+ stage: "final-scan",
447
+ timeoutMs: this.promptRequestTimeouts.totalPollMs,
448
+ messageId: messageId ?? void 0
449
+ });
450
+ this.logPromptRequestFailure(error, {
451
+ sessionId: input.sessionId,
452
+ stage: "final-scan",
453
+ timeoutMs: this.promptRequestTimeouts.totalPollMs,
454
+ messageId
455
+ });
456
+ throw error;
457
+ }
458
+ return resolved;
441
459
  }
442
460
  async fetchPromptMessage(sessionId, messageId) {
443
461
  try {
444
- if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponse(await this.requestRaw("get", {
445
- url: "/session/{sessionID}/message/{messageID}",
446
- path: {
462
+ return await this.runPromptRequestWithTimeout({
463
+ sessionId,
464
+ stage: "poll-message",
465
+ timeoutMs: this.promptRequestTimeouts.pollRequestMs,
466
+ messageId
467
+ }, async (signal) => {
468
+ if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponse(await this.requestRaw("get", {
469
+ url: "/session/{sessionID}/message/{messageID}",
470
+ path: {
471
+ sessionID: sessionId,
472
+ messageID: messageId
473
+ },
474
+ signal
475
+ }));
476
+ if (typeof this.client.session.message !== "function") return null;
477
+ return normalizePromptResponse(unwrapSdkData(await this.client.session.message({
447
478
  sessionID: sessionId,
448
479
  messageID: messageId
449
- }
450
- }));
451
- if (typeof this.client.session.message !== "function") return null;
452
- return normalizePromptResponse(unwrapSdkData(await this.client.session.message({
453
- sessionID: sessionId,
454
- messageID: messageId
455
- }, SDK_OPTIONS)));
456
- } catch {
480
+ }, {
481
+ ...SDK_OPTIONS,
482
+ signal
483
+ })));
484
+ });
485
+ } catch (error) {
486
+ this.logPromptRequestFailure(error, {
487
+ sessionId,
488
+ stage: "poll-message",
489
+ timeoutMs: this.promptRequestTimeouts.pollRequestMs,
490
+ messageId
491
+ });
457
492
  return null;
458
493
  }
459
494
  }
460
495
  async captureKnownMessageIds(sessionId) {
461
- const messages = await this.fetchRecentPromptMessages(sessionId);
496
+ const messages = await this.fetchRecentPromptMessages(sessionId, "capture-known-messages");
462
497
  if (!messages) return /* @__PURE__ */ new Set();
463
498
  return new Set(messages.map((message) => extractMessageId(message.info)).filter((id) => typeof id === "string" && id.length > 0));
464
499
  }
465
- async fetchRecentPromptMessages(sessionId) {
500
+ async fetchRecentPromptMessages(sessionId, stage) {
466
501
  try {
467
- if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponses(await this.requestRaw("get", {
468
- url: "/session/{sessionID}/message",
469
- path: { sessionID: sessionId },
470
- query: { limit: PROMPT_MESSAGE_POLL_LIMIT }
471
- }));
472
- if (typeof this.client.session.messages !== "function") return null;
473
- return normalizePromptResponses(unwrapSdkData(await this.client.session.messages({
474
- sessionID: sessionId,
475
- limit: PROMPT_MESSAGE_POLL_LIMIT
476
- }, SDK_OPTIONS)));
477
- } catch {
502
+ return await this.runPromptRequestWithTimeout({
503
+ sessionId,
504
+ stage,
505
+ timeoutMs: this.promptRequestTimeouts.pollRequestMs
506
+ }, async (signal) => {
507
+ if (hasRawSdkMethod(this.client, "get")) return normalizePromptResponses(await this.requestRaw("get", {
508
+ url: "/session/{sessionID}/message",
509
+ path: { sessionID: sessionId },
510
+ query: { limit: PROMPT_MESSAGE_POLL_LIMIT },
511
+ signal
512
+ }));
513
+ if (typeof this.client.session.messages !== "function") return null;
514
+ return normalizePromptResponses(unwrapSdkData(await this.client.session.messages({
515
+ sessionID: sessionId,
516
+ limit: PROMPT_MESSAGE_POLL_LIMIT
517
+ }, {
518
+ ...SDK_OPTIONS,
519
+ signal
520
+ })));
521
+ });
522
+ } catch (error) {
523
+ this.logPromptRequestFailure(error, {
524
+ sessionId,
525
+ stage,
526
+ timeoutMs: this.promptRequestTimeouts.pollRequestMs
527
+ });
478
528
  return null;
479
529
  }
480
530
  }
481
531
  async fetchPromptSessionStatus(sessionId) {
482
532
  try {
483
- return (await this.getSessionStatuses())[sessionId] ?? null;
484
- } catch {
533
+ return (await this.runPromptRequestWithTimeout({
534
+ sessionId,
535
+ stage: "poll-status",
536
+ timeoutMs: this.promptRequestTimeouts.pollRequestMs
537
+ }, async (signal) => this.loadSessionStatuses(signal)))[sessionId] ?? null;
538
+ } catch (error) {
539
+ this.logPromptRequestFailure(error, {
540
+ sessionId,
541
+ stage: "poll-status",
542
+ timeoutMs: this.promptRequestTimeouts.pollRequestMs
543
+ });
485
544
  return null;
486
545
  }
487
546
  }
488
- async findLatestPromptResponse(sessionId, options) {
489
- const messages = await this.fetchRecentPromptMessages(sessionId);
547
+ async findLatestPromptResponse(sessionId, options, stage) {
548
+ const messages = await this.fetchRecentPromptMessages(sessionId, stage);
490
549
  if (!messages || messages.length === 0) return null;
491
550
  return selectPromptResponseCandidate(messages, options);
492
551
  }
@@ -510,25 +569,44 @@ var OpenCodeClient = class {
510
569
  return unwrapSdkData(await this.client.config.providers(void 0, SDK_OPTIONS));
511
570
  }
512
571
  async sendPromptRequest(input, parts) {
513
- if (hasRawSdkMethod(this.client, "post")) return normalizePromptResponse(await this.requestRaw("post", {
514
- url: "/session/{sessionID}/message",
515
- path: { sessionID: input.sessionId },
516
- body: {
517
- ...input.agent ? { agent: input.agent } : {},
518
- ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
519
- ...input.model ? { model: input.model } : {},
520
- ...input.variant ? { variant: input.variant } : {},
521
- parts
522
- }
523
- }));
524
- return normalizePromptResponse(unwrapSdkData(await this.client.session.prompt({
525
- sessionID: input.sessionId,
526
- ...input.agent ? { agent: input.agent } : {},
527
- ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
528
- ...input.model ? { model: input.model } : {},
529
- ...input.variant ? { variant: input.variant } : {},
530
- parts
531
- }, SDK_OPTIONS)));
572
+ try {
573
+ return await this.runPromptRequestWithTimeout({
574
+ sessionId: input.sessionId,
575
+ stage: "send-prompt",
576
+ timeoutMs: this.promptRequestTimeouts.sendMs
577
+ }, async (signal) => {
578
+ if (hasRawSdkMethod(this.client, "post")) return normalizePromptResponse(await this.requestRaw("post", {
579
+ url: "/session/{sessionID}/message",
580
+ path: { sessionID: input.sessionId },
581
+ body: {
582
+ ...input.agent ? { agent: input.agent } : {},
583
+ ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
584
+ ...input.model ? { model: input.model } : {},
585
+ ...input.variant ? { variant: input.variant } : {},
586
+ parts
587
+ },
588
+ signal
589
+ }));
590
+ return normalizePromptResponse(unwrapSdkData(await this.client.session.prompt({
591
+ sessionID: input.sessionId,
592
+ ...input.agent ? { agent: input.agent } : {},
593
+ ...input.structured ? { format: STRUCTURED_REPLY_SCHEMA } : {},
594
+ ...input.model ? { model: input.model } : {},
595
+ ...input.variant ? { variant: input.variant } : {},
596
+ parts
597
+ }, {
598
+ ...SDK_OPTIONS,
599
+ signal
600
+ })));
601
+ });
602
+ } catch (error) {
603
+ this.logPromptRequestFailure(error, {
604
+ sessionId: input.sessionId,
605
+ stage: "send-prompt",
606
+ timeoutMs: this.promptRequestTimeouts.sendMs
607
+ });
608
+ throw error;
609
+ }
532
610
  }
533
611
  async requestRaw(method, options) {
534
612
  const handler = getRawSdkClient(this.client)?.[method];
@@ -538,6 +616,67 @@ var OpenCodeClient = class {
538
616
  ...options
539
617
  }));
540
618
  }
619
+ async loadSessionStatuses(signal) {
620
+ if (hasRawSdkMethod(this.client, "get")) return this.requestRaw("get", {
621
+ url: "/session/status",
622
+ ...signal ? { signal } : {}
623
+ });
624
+ return unwrapSdkData(await this.client.session.status(void 0, {
625
+ ...SDK_OPTIONS,
626
+ ...signal ? { signal } : {}
627
+ }));
628
+ }
629
+ async runPromptRequestWithTimeout(input, operation) {
630
+ const startedAt = Date.now();
631
+ const controller = new AbortController();
632
+ let timeoutHandle = null;
633
+ const timeoutPromise = new Promise((_, reject) => {
634
+ timeoutHandle = setTimeout(() => {
635
+ reject(createOpenCodePromptTimeoutError({
636
+ sessionId: input.sessionId,
637
+ stage: input.stage,
638
+ timeoutMs: input.timeoutMs,
639
+ messageId: input.messageId ?? void 0,
640
+ elapsedMs: Date.now() - startedAt
641
+ }));
642
+ controller.abort();
643
+ }, input.timeoutMs);
644
+ });
645
+ try {
646
+ return await Promise.race([operation(controller.signal), timeoutPromise]);
647
+ } finally {
648
+ if (timeoutHandle !== null) clearTimeout(timeoutHandle);
649
+ }
650
+ }
651
+ logPromptRequestFailure(error, input) {
652
+ if (error instanceof OpenCodePromptTimeoutError) {
653
+ this.logPromptRequest("warn", {
654
+ elapsedMs: error.data.elapsedMs,
655
+ messageId: error.data.messageId,
656
+ sessionId: error.data.sessionId,
657
+ stage: error.data.stage,
658
+ timeoutMs: error.data.timeoutMs
659
+ }, "OpenCode prompt request timed out");
660
+ return;
661
+ }
662
+ this.logPromptRequest("warn", {
663
+ error,
664
+ messageId: input.messageId ?? void 0,
665
+ sessionId: input.sessionId,
666
+ stage: input.stage,
667
+ timeoutMs: input.timeoutMs
668
+ }, "OpenCode prompt request failed");
669
+ }
670
+ logPromptRequest(level, extra, message) {
671
+ const log = this.client.app?.log;
672
+ if (typeof log !== "function") return;
673
+ log.call(this.client.app, {
674
+ service: PROMPT_LOG_SERVICE,
675
+ level,
676
+ message,
677
+ extra
678
+ }).catch(() => void 0);
679
+ }
541
680
  };
542
681
  function createOpenCodeClientFromSdkClient(client, fetchFn = fetch) {
543
682
  return new OpenCodeClient(void 0, client, fetchFn);
@@ -709,6 +848,16 @@ function extractMessageId(message) {
709
848
  function delay(ms) {
710
849
  return new Promise((resolve) => setTimeout(resolve, ms));
711
850
  }
851
+ function createOpenCodePromptTimeoutError(input) {
852
+ return new OpenCodePromptTimeoutError({
853
+ ...input,
854
+ elapsedMs: input.elapsedMs ?? input.timeoutMs,
855
+ message: input.message ?? "The OpenCode host did not finish this request in time."
856
+ });
857
+ }
858
+ function getPromptMessagePollDelayMs(attempt) {
859
+ return PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS[attempt] ?? PROMPT_MESSAGE_POLL_INTERVAL_MS;
860
+ }
712
861
  function extractStructuredMarkdown(structured) {
713
862
  const parsed = StructuredReplySchema.safeParse(structured);
714
863
  if (!parsed.success) return null;
@@ -789,19 +938,34 @@ function selectPromptResponseCandidate(candidates, options) {
789
938
  function comparePromptResponseCandidates(left, right, options) {
790
939
  const leftRank = getPromptResponseCandidateRank(left, options);
791
940
  const rightRank = getPromptResponseCandidateRank(right, options);
792
- return Number(rightRank.isUsable) - Number(leftRank.isUsable) || Number(rightRank.isInitial) - Number(leftRank.isInitial) || Number(rightRank.sharesParent) - Number(leftRank.sharesParent) || Number(rightRank.isNew) - Number(leftRank.isNew) || rightRank.createdAt - leftRank.createdAt;
941
+ return Number(rightRank.isUsable) - Number(leftRank.isUsable) || Number(rightRank.isInitial) - Number(leftRank.isInitial) || Number(rightRank.sharesParent) - Number(leftRank.sharesParent) || Number(rightRank.isNewSinceRequestStart) - Number(leftRank.isNewSinceRequestStart) || rightRank.createdAt - leftRank.createdAt;
793
942
  }
794
943
  function getPromptResponseCandidateRank(message, options) {
795
944
  const assistant = toAssistantMessage(message.info);
796
945
  const id = assistant?.id ?? null;
946
+ const createdAt = typeof assistant?.time?.created === "number" && Number.isFinite(assistant.time.created) ? assistant.time.created : 0;
797
947
  return {
798
- createdAt: typeof assistant?.time?.created === "number" && Number.isFinite(assistant.time.created) ? assistant.time.created : 0,
948
+ createdAt,
799
949
  isInitial: !!id && id === options.initialMessageId,
800
- isNew: !!id && !options.knownMessageIds.has(id),
950
+ isNewSinceRequestStart: isPromptResponseNewSinceRequestStart(id, createdAt, options.knownMessageIds, options.requestStartedAt),
801
951
  isUsable: !shouldPollPromptMessage(message, options.structured),
802
952
  sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId
803
953
  };
804
954
  }
955
+ function resolvePromptCandidateStartTime(startedAt, initialMessage) {
956
+ const initialCreatedAt = coerceFiniteNumber(toAssistantMessage(initialMessage.info)?.time?.created);
957
+ if (initialCreatedAt === null) return startedAt;
958
+ return areComparablePromptTimestamps(startedAt, initialCreatedAt) ? startedAt : initialCreatedAt;
959
+ }
960
+ function isPromptResponseNewSinceRequestStart(messageId, createdAt, knownMessageIds, requestStartedAt) {
961
+ if (!messageId || knownMessageIds.has(messageId)) return false;
962
+ if (requestStartedAt === null) return true;
963
+ return createdAt >= requestStartedAt;
964
+ }
965
+ function areComparablePromptTimestamps(left, right) {
966
+ const epochThresholdMs = 0xe8d4a51000;
967
+ return left >= epochThresholdMs && right >= epochThresholdMs;
968
+ }
805
969
  function isPlainRecord(value) {
806
970
  return value !== null && typeof value === "object" && !Array.isArray(value);
807
971
  }
@@ -2331,6 +2495,7 @@ var EN_BOT_COPY = {
2331
2495
  unexpected: "Unexpected error.",
2332
2496
  providerAuth: "Provider authentication failed.",
2333
2497
  requestAborted: "Request was aborted.",
2498
+ promptTimeout: "OpenCode request timed out.",
2334
2499
  structuredOutput: "Structured output validation failed.",
2335
2500
  voiceNotConfigured: "Voice transcription is not configured.",
2336
2501
  voiceDownload: "Failed to download the Telegram voice file.",
@@ -2566,6 +2731,7 @@ var ZH_CN_BOT_COPY = {
2566
2731
  unexpected: "发生未知错误。",
2567
2732
  providerAuth: "Provider 认证失败。",
2568
2733
  requestAborted: "请求已中止。",
2734
+ promptTimeout: "OpenCode 响应超时。",
2569
2735
  structuredOutput: "结构化输出校验失败。",
2570
2736
  voiceNotConfigured: "未配置语音转写服务。",
2571
2737
  voiceDownload: "下载 Telegram 语音文件失败。",
@@ -2913,6 +3079,10 @@ function normalizeError(error, copy) {
2913
3079
  message: copy.errors.requestAborted,
2914
3080
  cause: extractMessage(error.data) ?? null
2915
3081
  };
3082
+ if (isNamedError(error, "OpenCodePromptTimeoutError")) return {
3083
+ message: copy.errors.promptTimeout,
3084
+ cause: null
3085
+ };
2916
3086
  if (isNamedError(error, "StructuredOutputError")) return {
2917
3087
  message: copy.errors.structuredOutput,
2918
3088
  cause: joinNonEmptyParts([extractMessage(error.data), extractRetries(error.data)])