opencode-tbot 0.1.31 → 0.1.32
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 +179 -98
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -2,7 +2,6 @@ import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadA
|
|
|
2
2
|
import { appendFile, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join } from "node:path";
|
|
4
4
|
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
5
|
-
import { z } from "zod";
|
|
6
5
|
import { randomUUID } from "node:crypto";
|
|
7
6
|
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
8
7
|
import { run } from "@grammyjs/runner";
|
|
@@ -571,6 +570,139 @@ var FileSessionRepository = class {
|
|
|
571
570
|
}
|
|
572
571
|
};
|
|
573
572
|
//#endregion
|
|
573
|
+
//#region src/infra/utils/markdown-text.ts
|
|
574
|
+
var HTML_TAG_PATTERN = /<\/?[A-Za-z][^>]*>/g;
|
|
575
|
+
function stripMarkdownToPlainText(markdown) {
|
|
576
|
+
const lines = preprocessMarkdownForPlainText(markdown).split("\n");
|
|
577
|
+
const rendered = [];
|
|
578
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
579
|
+
const tableBlock = consumeMarkdownTable$1(lines, index);
|
|
580
|
+
if (tableBlock) {
|
|
581
|
+
rendered.push(renderTableAsPlainText(tableBlock.rows));
|
|
582
|
+
index = tableBlock.nextIndex - 1;
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
rendered.push(lines[index] ?? "");
|
|
586
|
+
}
|
|
587
|
+
return rendered.join("\n").replace(/```[A-Za-z0-9_-]*\n?/g, "").replace(/```/g, "").replace(/^#{1,6}\s+/gm, "").replace(/^\s*>\s?/gm, "").replace(/^(\s*)[-+*]\s+/gm, "$1- ").replace(/^(\s*)(\d+)[.)]\s+/gm, "$1$2. ").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/(\*\*|__)(.*?)\1/g, "$2").replace(/(\*|_)(.*?)\1/g, "$2").replace(/`([^`]+)`/g, "$1").replace(HTML_TAG_PATTERN, "").trim();
|
|
588
|
+
}
|
|
589
|
+
function preprocessMarkdownForPlainText(markdown) {
|
|
590
|
+
const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
|
|
591
|
+
const processed = [];
|
|
592
|
+
let activeFence = null;
|
|
593
|
+
for (const line of lines) {
|
|
594
|
+
const fenceMatch = line.match(/^```([A-Za-z0-9_-]+)?\s*$/);
|
|
595
|
+
if (fenceMatch) {
|
|
596
|
+
const language = (fenceMatch[1] ?? "").toLowerCase();
|
|
597
|
+
if (activeFence === "markdown") {
|
|
598
|
+
activeFence = null;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (activeFence === "plain") {
|
|
602
|
+
processed.push(line);
|
|
603
|
+
activeFence = null;
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (language === "md" || language === "markdown") {
|
|
607
|
+
activeFence = "markdown";
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
activeFence = "plain";
|
|
611
|
+
processed.push(line);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
processed.push(line);
|
|
615
|
+
}
|
|
616
|
+
return processed.join("\n");
|
|
617
|
+
}
|
|
618
|
+
function consumeMarkdownTable$1(lines, startIndex) {
|
|
619
|
+
if (startIndex + 1 >= lines.length) return null;
|
|
620
|
+
const headerCells = parseMarkdownTableRow$1(lines[startIndex] ?? "");
|
|
621
|
+
const separatorCells = parseMarkdownTableSeparator$1(lines[startIndex + 1] ?? "");
|
|
622
|
+
if (!headerCells || !separatorCells || headerCells.length !== separatorCells.length) return null;
|
|
623
|
+
const rows = [headerCells];
|
|
624
|
+
let index = startIndex + 2;
|
|
625
|
+
while (index < lines.length) {
|
|
626
|
+
const rowCells = parseMarkdownTableRow$1(lines[index] ?? "");
|
|
627
|
+
if (!rowCells || rowCells.length !== headerCells.length) break;
|
|
628
|
+
rows.push(rowCells);
|
|
629
|
+
index += 1;
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
rows,
|
|
633
|
+
nextIndex: index
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function parseMarkdownTableRow$1(line) {
|
|
637
|
+
const trimmed = line.trim();
|
|
638
|
+
if (!trimmed.includes("|")) return null;
|
|
639
|
+
const cells = splitMarkdownTableCells$1(trimmed).map((cell) => normalizeTableCell$1(cell));
|
|
640
|
+
return cells.length >= 2 ? cells : null;
|
|
641
|
+
}
|
|
642
|
+
function parseMarkdownTableSeparator$1(line) {
|
|
643
|
+
const cells = splitMarkdownTableCells$1(line.trim());
|
|
644
|
+
if (cells.length < 2) return null;
|
|
645
|
+
return cells.every((cell) => /^:?-{3,}:?$/.test(cell.trim())) ? cells : null;
|
|
646
|
+
}
|
|
647
|
+
function splitMarkdownTableCells$1(line) {
|
|
648
|
+
const content = line.replace(/^\|/, "").replace(/\|$/, "");
|
|
649
|
+
const cells = [];
|
|
650
|
+
let current = "";
|
|
651
|
+
let escaped = false;
|
|
652
|
+
for (const char of content) {
|
|
653
|
+
if (escaped) {
|
|
654
|
+
current += char;
|
|
655
|
+
escaped = false;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (char === "\\") {
|
|
659
|
+
escaped = true;
|
|
660
|
+
current += char;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (char === "|") {
|
|
664
|
+
cells.push(current);
|
|
665
|
+
current = "";
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
current += char;
|
|
669
|
+
}
|
|
670
|
+
cells.push(current);
|
|
671
|
+
return cells;
|
|
672
|
+
}
|
|
673
|
+
function normalizeTableCell$1(cell) {
|
|
674
|
+
return cell.trim().replace(/\\\|/g, "|").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/(\*\*|__)(.*?)\1/g, "$2").replace(/(\*|_)(.*?)\1/g, "$2").replace(/`([^`]+)`/g, "$1").replace(HTML_TAG_PATTERN, "");
|
|
675
|
+
}
|
|
676
|
+
function renderTableAsPlainText(rows) {
|
|
677
|
+
return buildAlignedTableLines$1(rows).join("\n");
|
|
678
|
+
}
|
|
679
|
+
function buildAlignedTableLines$1(rows) {
|
|
680
|
+
const columnWidths = calculateTableColumnWidths$1(rows);
|
|
681
|
+
return [
|
|
682
|
+
formatTableRow$1(rows[0] ?? [], columnWidths),
|
|
683
|
+
columnWidths.map((width) => "-".repeat(Math.max(3, width))).join("-+-"),
|
|
684
|
+
...rows.slice(1).map((row) => formatTableRow$1(row, columnWidths))
|
|
685
|
+
];
|
|
686
|
+
}
|
|
687
|
+
function calculateTableColumnWidths$1(rows) {
|
|
688
|
+
return (rows[0] ?? []).map((_, columnIndex) => rows.reduce((maxWidth, row) => Math.max(maxWidth, getDisplayWidth$1(row[columnIndex] ?? "")), 0));
|
|
689
|
+
}
|
|
690
|
+
function formatTableRow$1(row, columnWidths) {
|
|
691
|
+
return row.map((cell, index) => padDisplayWidth$1(cell, columnWidths[index] ?? 0)).join(" | ");
|
|
692
|
+
}
|
|
693
|
+
function padDisplayWidth$1(value, targetWidth) {
|
|
694
|
+
const padding = Math.max(0, targetWidth - getDisplayWidth$1(value));
|
|
695
|
+
return `${value}${" ".repeat(padding)}`;
|
|
696
|
+
}
|
|
697
|
+
function getDisplayWidth$1(value) {
|
|
698
|
+
let width = 0;
|
|
699
|
+
for (const char of value) width += isWideCharacter$1(char.codePointAt(0) ?? 0) ? 2 : 1;
|
|
700
|
+
return width;
|
|
701
|
+
}
|
|
702
|
+
function isWideCharacter$1(codePoint) {
|
|
703
|
+
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510);
|
|
704
|
+
}
|
|
705
|
+
//#endregion
|
|
574
706
|
//#region src/services/opencode/opencode.client.ts
|
|
575
707
|
function buildOpenCodeSdkConfig(options) {
|
|
576
708
|
const apiKey = options.apiKey?.trim();
|
|
@@ -614,24 +746,11 @@ var DEFAULT_OPENCODE_PROMPT_TIMEOUT_POLICY = {
|
|
|
614
746
|
recoveryInactivityTimeoutMs: 12e4,
|
|
615
747
|
waitTimeoutMs: 18e5
|
|
616
748
|
};
|
|
617
|
-
var STRUCTURED_REPLY_SCHEMA = {
|
|
618
|
-
type: "json_schema",
|
|
619
|
-
retryCount: 2,
|
|
620
|
-
schema: {
|
|
621
|
-
type: "object",
|
|
622
|
-
additionalProperties: false,
|
|
623
|
-
required: ["body_md"],
|
|
624
|
-
properties: { body_md: {
|
|
625
|
-
type: "string",
|
|
626
|
-
description: "Markdown body only. Do not include duration, token usage, or any footer. Do not wrap the whole answer in ```md or ```markdown fences. Use Markdown formatting directly unless the user explicitly asks for raw Markdown source."
|
|
627
|
-
} }
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
749
|
var SDK_OPTIONS = {
|
|
631
750
|
responseStyle: "data",
|
|
632
751
|
throwOnError: true
|
|
633
752
|
};
|
|
634
|
-
var
|
|
753
|
+
var TEXT_OUTPUT_FORMAT = { type: "text" };
|
|
635
754
|
var OpenCodeClient = class {
|
|
636
755
|
client;
|
|
637
756
|
fetchFn;
|
|
@@ -819,20 +938,17 @@ var OpenCodeClient = class {
|
|
|
819
938
|
return buildPromptSessionResult(await this.resolvePromptResponse(input, null, knownMessageIds, startedAt), {
|
|
820
939
|
emptyResponseText: EMPTY_RESPONSE_TEXT,
|
|
821
940
|
finishedAt: Date.now(),
|
|
822
|
-
startedAt
|
|
823
|
-
structured: input.structured ?? false
|
|
941
|
+
startedAt
|
|
824
942
|
});
|
|
825
943
|
}
|
|
826
944
|
async resolvePromptResponse(input, data, knownMessageIds, startedAt) {
|
|
827
|
-
|
|
828
|
-
if (data && shouldReturnPromptResponseImmediately(data, structured)) return data;
|
|
945
|
+
if (data && shouldReturnPromptResponseImmediately(data)) return data;
|
|
829
946
|
const messageId = data ? extractMessageId(data.info) : null;
|
|
830
947
|
const candidateOptions = {
|
|
831
948
|
initialMessageId: messageId,
|
|
832
949
|
initialParentId: data ? toAssistantMessage(data.info)?.parentID ?? null : null,
|
|
833
950
|
knownMessageIds,
|
|
834
|
-
requestStartedAt: resolvePromptCandidateStartTime(startedAt, data)
|
|
835
|
-
structured
|
|
951
|
+
requestStartedAt: resolvePromptCandidateStartTime(startedAt, data)
|
|
836
952
|
};
|
|
837
953
|
let bestCandidate = selectPromptResponseCandidate(data ? [data] : [], candidateOptions);
|
|
838
954
|
let lastProgressAt = Date.now();
|
|
@@ -857,26 +973,26 @@ var OpenCodeClient = class {
|
|
|
857
973
|
if (next) {
|
|
858
974
|
const nextCandidate = selectPromptResponseCandidate([bestCandidate, next], candidateOptions);
|
|
859
975
|
if (nextCandidate) {
|
|
860
|
-
if (didPromptResponseAdvance(bestCandidate, nextCandidate
|
|
976
|
+
if (didPromptResponseAdvance(bestCandidate, nextCandidate)) {
|
|
861
977
|
lastProgressAt = Date.now();
|
|
862
978
|
idleStatusSeen = false;
|
|
863
979
|
}
|
|
864
980
|
bestCandidate = nextCandidate;
|
|
865
981
|
}
|
|
866
|
-
if (bestCandidate && isPromptResponseForCurrentRequest(bestCandidate, candidateOptions) && shouldReturnPromptResponseImmediately(bestCandidate
|
|
982
|
+
if (bestCandidate && isPromptResponseForCurrentRequest(bestCandidate, candidateOptions) && shouldReturnPromptResponseImmediately(bestCandidate)) return bestCandidate;
|
|
867
983
|
}
|
|
868
984
|
}
|
|
869
985
|
const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "poll-messages", input.signal);
|
|
870
986
|
if (latest) {
|
|
871
987
|
const nextCandidate = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions);
|
|
872
988
|
if (nextCandidate) {
|
|
873
|
-
if (didPromptResponseAdvance(bestCandidate, nextCandidate
|
|
989
|
+
if (didPromptResponseAdvance(bestCandidate, nextCandidate)) {
|
|
874
990
|
lastProgressAt = Date.now();
|
|
875
991
|
idleStatusSeen = false;
|
|
876
992
|
}
|
|
877
993
|
bestCandidate = nextCandidate;
|
|
878
994
|
}
|
|
879
|
-
if (bestCandidate && isPromptResponseForCurrentRequest(bestCandidate, candidateOptions) && shouldReturnPromptResponseImmediately(bestCandidate
|
|
995
|
+
if (bestCandidate && isPromptResponseForCurrentRequest(bestCandidate, candidateOptions) && shouldReturnPromptResponseImmediately(bestCandidate)) return bestCandidate;
|
|
880
996
|
}
|
|
881
997
|
const status = await this.fetchPromptSessionStatus(input.sessionId, input.signal);
|
|
882
998
|
lastStatus = status;
|
|
@@ -887,14 +1003,14 @@ var OpenCodeClient = class {
|
|
|
887
1003
|
if (idleStatusSeen) break;
|
|
888
1004
|
idleStatusSeen = true;
|
|
889
1005
|
}
|
|
890
|
-
if (bestCandidate && isPromptResponseForCurrentRequest(bestCandidate, candidateOptions) && isCompletedEmptyPromptResponse(bestCandidate
|
|
1006
|
+
if (bestCandidate && isPromptResponseForCurrentRequest(bestCandidate, candidateOptions) && isCompletedEmptyPromptResponse(bestCandidate) && status?.type !== "busy" && status?.type !== "retry") break;
|
|
891
1007
|
if (Date.now() >= deadlineAt) break;
|
|
892
1008
|
}
|
|
893
1009
|
const latest = await this.findLatestPromptResponse(input.sessionId, candidateOptions, "final-scan", input.signal);
|
|
894
1010
|
const resolved = selectPromptResponseCandidate([bestCandidate, latest], candidateOptions);
|
|
895
1011
|
const requestScopedResolved = resolved && isPromptResponseForCurrentRequest(resolved, candidateOptions) ? resolved : null;
|
|
896
|
-
if (lastStatus?.type === "idle" && (!requestScopedResolved || shouldPollPromptMessage(requestScopedResolved
|
|
897
|
-
if (!requestScopedResolved || shouldPollPromptMessage(requestScopedResolved
|
|
1012
|
+
if (lastStatus?.type === "idle" && (!requestScopedResolved || shouldPollPromptMessage(requestScopedResolved))) throw createMessageAbortedError();
|
|
1013
|
+
if (!requestScopedResolved || shouldPollPromptMessage(requestScopedResolved)) {
|
|
898
1014
|
const timeoutReason = Date.now() >= deadlineAt ? "max-wait" : "recovery-inactivity";
|
|
899
1015
|
const timeoutMs = timeoutReason === "max-wait" ? this.promptTimeoutPolicy.waitTimeoutMs : this.promptTimeoutPolicy.recoveryInactivityTimeoutMs;
|
|
900
1016
|
const error = createOpenCodePromptTimeoutError({
|
|
@@ -1020,9 +1136,10 @@ var OpenCodeClient = class {
|
|
|
1020
1136
|
return this.callScopedSdkMethod("config", "providers", {});
|
|
1021
1137
|
}
|
|
1022
1138
|
async sendPromptRequest(input, parts) {
|
|
1139
|
+
const format = input.format ?? TEXT_OUTPUT_FORMAT;
|
|
1023
1140
|
const requestBody = {
|
|
1024
1141
|
...input.agent ? { agent: input.agent } : {},
|
|
1025
|
-
|
|
1142
|
+
format,
|
|
1026
1143
|
...input.model ? { model: input.model } : {},
|
|
1027
1144
|
...input.variant ? { variant: input.variant } : {},
|
|
1028
1145
|
parts
|
|
@@ -1239,10 +1356,9 @@ function extractTextFromParts(parts) {
|
|
|
1239
1356
|
}
|
|
1240
1357
|
function buildPromptSessionResult(data, options) {
|
|
1241
1358
|
const assistantInfo = toAssistantMessage(data.info);
|
|
1242
|
-
const structuredPayload = extractStructuredPayload(assistantInfo);
|
|
1243
|
-
const bodyMd = options.structured ? extractStructuredMarkdown(structuredPayload) : null;
|
|
1244
1359
|
const responseParts = Array.isArray(data.parts) ? data.parts : [];
|
|
1245
|
-
const
|
|
1360
|
+
const bodyMd = extractTextFromParts(responseParts) || null;
|
|
1361
|
+
const fallbackText = bodyMd ? stripMarkdownToPlainText(bodyMd) || bodyMd : options.emptyResponseText;
|
|
1246
1362
|
return {
|
|
1247
1363
|
assistantError: assistantInfo?.error ?? null,
|
|
1248
1364
|
bodyMd,
|
|
@@ -1250,22 +1366,21 @@ function buildPromptSessionResult(data, options) {
|
|
|
1250
1366
|
info: assistantInfo,
|
|
1251
1367
|
metrics: extractPromptMetrics(assistantInfo, options.startedAt, options.finishedAt),
|
|
1252
1368
|
parts: responseParts,
|
|
1253
|
-
structured:
|
|
1369
|
+
structured: null
|
|
1254
1370
|
};
|
|
1255
1371
|
}
|
|
1256
|
-
function shouldPollPromptMessage(data
|
|
1372
|
+
function shouldPollPromptMessage(data) {
|
|
1257
1373
|
const assistantInfo = toAssistantMessage(data.info);
|
|
1258
|
-
const bodyMd = structured ? extractStructuredMarkdown(extractStructuredPayload(assistantInfo)) : null;
|
|
1259
1374
|
const hasText = extractTextFromParts(Array.isArray(data.parts) ? data.parts : []).length > 0;
|
|
1260
1375
|
const hasAssistantError = !!assistantInfo?.error;
|
|
1261
1376
|
const isCompleted = isAssistantMessageCompleted(assistantInfo);
|
|
1262
|
-
return !hasText && !
|
|
1377
|
+
return !hasText && !hasAssistantError && !isCompleted;
|
|
1263
1378
|
}
|
|
1264
|
-
function shouldReturnPromptResponseImmediately(data
|
|
1265
|
-
return !shouldPollPromptMessage(data
|
|
1379
|
+
function shouldReturnPromptResponseImmediately(data) {
|
|
1380
|
+
return !shouldPollPromptMessage(data) && !isCompletedEmptyPromptResponse(data);
|
|
1266
1381
|
}
|
|
1267
|
-
function isPromptResponseUsable(data
|
|
1268
|
-
return !shouldPollPromptMessage(data
|
|
1382
|
+
function isPromptResponseUsable(data) {
|
|
1383
|
+
return !shouldPollPromptMessage(data) && !isCompletedEmptyPromptResponse(data);
|
|
1269
1384
|
}
|
|
1270
1385
|
function normalizePromptResponse(response) {
|
|
1271
1386
|
return {
|
|
@@ -1300,8 +1415,6 @@ function toAssistantMessage(message) {
|
|
|
1300
1415
|
if ("providerID" in message && typeof message.providerID === "string" && message.providerID.trim().length > 0) normalized.providerID = message.providerID;
|
|
1301
1416
|
if ("role" in message && message.role === "assistant") normalized.role = "assistant";
|
|
1302
1417
|
if ("sessionID" in message && typeof message.sessionID === "string" && message.sessionID.trim().length > 0) normalized.sessionID = message.sessionID;
|
|
1303
|
-
const structuredPayload = extractStructuredPayload(message);
|
|
1304
|
-
if (structuredPayload !== null) normalized.structured = structuredPayload;
|
|
1305
1418
|
if ("summary" in message && typeof message.summary === "boolean") normalized.summary = message.summary;
|
|
1306
1419
|
if ("time" in message && isPlainRecord$1(message.time)) normalized.time = {
|
|
1307
1420
|
...typeof message.time.created === "number" && Number.isFinite(message.time.created) ? { created: message.time.created } : {},
|
|
@@ -1342,8 +1455,8 @@ function delay(ms, signal) {
|
|
|
1342
1455
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1343
1456
|
});
|
|
1344
1457
|
}
|
|
1345
|
-
function didPromptResponseAdvance(previous, next
|
|
1346
|
-
return getPromptResponseProgressSignature(previous
|
|
1458
|
+
function didPromptResponseAdvance(previous, next) {
|
|
1459
|
+
return getPromptResponseProgressSignature(previous) !== getPromptResponseProgressSignature(next);
|
|
1347
1460
|
}
|
|
1348
1461
|
function createOpenCodePromptTimeoutError(input) {
|
|
1349
1462
|
return new OpenCodePromptTimeoutError({
|
|
@@ -1365,12 +1478,6 @@ function resolvePromptEndpointKind(stage) {
|
|
|
1365
1478
|
function getPromptMessagePollDelayMs(attempt) {
|
|
1366
1479
|
return PROMPT_MESSAGE_POLL_INITIAL_DELAYS_MS[attempt] ?? PROMPT_MESSAGE_POLL_INTERVAL_MS;
|
|
1367
1480
|
}
|
|
1368
|
-
function extractStructuredMarkdown(structured) {
|
|
1369
|
-
const parsed = StructuredReplySchema.safeParse(structured);
|
|
1370
|
-
if (!parsed.success) return null;
|
|
1371
|
-
const bodyMd = parsed.data.body_md.replace(/\r\n?/g, "\n").trim();
|
|
1372
|
-
return bodyMd.length > 0 ? bodyMd : null;
|
|
1373
|
-
}
|
|
1374
1481
|
function extractPromptMetrics(info, startedAt, finishedAt) {
|
|
1375
1482
|
const createdAt = typeof info?.time?.created === "number" && Number.isFinite(info.time.created) ? info.time.created : null;
|
|
1376
1483
|
const completedAt = typeof info?.time?.completed === "number" && Number.isFinite(info.time.completed) ? info.time.completed : null;
|
|
@@ -1463,17 +1570,10 @@ function normalizeAssistantError(value) {
|
|
|
1463
1570
|
function isAssistantMessageCompleted(message) {
|
|
1464
1571
|
return !!message?.error || typeof message?.time?.completed === "number" || typeof message?.finish === "string" && message.finish.trim().length > 0;
|
|
1465
1572
|
}
|
|
1466
|
-
function isCompletedEmptyPromptResponse(data
|
|
1573
|
+
function isCompletedEmptyPromptResponse(data) {
|
|
1467
1574
|
const assistantInfo = toAssistantMessage(data.info);
|
|
1468
|
-
const bodyMd = structured ? extractStructuredMarkdown(extractStructuredPayload(assistantInfo)) : null;
|
|
1469
1575
|
const hasText = extractTextFromParts(Array.isArray(data.parts) ? data.parts : []).length > 0;
|
|
1470
|
-
return isAssistantMessageCompleted(assistantInfo) && !assistantInfo?.error && !hasText
|
|
1471
|
-
}
|
|
1472
|
-
function extractStructuredPayload(message) {
|
|
1473
|
-
if (!isPlainRecord$1(message)) return null;
|
|
1474
|
-
if ("structured" in message && message.structured !== void 0) return message.structured;
|
|
1475
|
-
if ("structured_output" in message && message.structured_output !== void 0) return message.structured_output;
|
|
1476
|
-
return null;
|
|
1576
|
+
return isAssistantMessageCompleted(assistantInfo) && !assistantInfo?.error && !hasText;
|
|
1477
1577
|
}
|
|
1478
1578
|
function selectPromptResponseCandidate(candidates, options) {
|
|
1479
1579
|
const availableCandidates = candidates.filter((candidate) => !!candidate).filter((candidate) => toAssistantMessage(candidate.info) !== null);
|
|
@@ -1493,7 +1593,7 @@ function getPromptResponseCandidateRank(message, options) {
|
|
|
1493
1593
|
createdAt,
|
|
1494
1594
|
isInitial: !!id && id === options.initialMessageId,
|
|
1495
1595
|
isNewSinceRequestStart: isPromptResponseNewSinceRequestStart(id, createdAt, options.knownMessageIds, options.requestStartedAt),
|
|
1496
|
-
isUsable: isPromptResponseUsable(message
|
|
1596
|
+
isUsable: isPromptResponseUsable(message),
|
|
1497
1597
|
sharesParent: !!assistant?.parentID && assistant.parentID === options.initialParentId
|
|
1498
1598
|
};
|
|
1499
1599
|
}
|
|
@@ -1503,13 +1603,13 @@ function resolvePromptCandidateStartTime(startedAt, initialMessage) {
|
|
|
1503
1603
|
if (initialCreatedAt === null) return startedAt;
|
|
1504
1604
|
return areComparablePromptTimestamps(startedAt, initialCreatedAt) ? startedAt : initialCreatedAt;
|
|
1505
1605
|
}
|
|
1506
|
-
function getPromptResponseProgressSignature(response
|
|
1606
|
+
function getPromptResponseProgressSignature(response) {
|
|
1507
1607
|
if (!response) return "null";
|
|
1508
1608
|
const assistant = toAssistantMessage(response.info);
|
|
1509
1609
|
const responseParts = Array.isArray(response.parts) ? response.parts : [];
|
|
1510
1610
|
return JSON.stringify({
|
|
1511
1611
|
assistantError: assistant?.error?.name ?? null,
|
|
1512
|
-
bodyMd:
|
|
1612
|
+
bodyMd: extractTextFromParts(responseParts) || null,
|
|
1513
1613
|
completedAt: assistant?.time?.completed ?? null,
|
|
1514
1614
|
finish: assistant?.finish ?? null,
|
|
1515
1615
|
id: assistant?.id ?? null,
|
|
@@ -2396,7 +2496,7 @@ var SendPromptUseCase = class {
|
|
|
2396
2496
|
prompt: promptText,
|
|
2397
2497
|
...files.length > 0 ? { files } : {},
|
|
2398
2498
|
...selectedAgent ? { agent: selectedAgent.name } : {},
|
|
2399
|
-
|
|
2499
|
+
format: { type: "text" },
|
|
2400
2500
|
...model ? { model } : {},
|
|
2401
2501
|
...input.signal ? { signal: input.signal } : {},
|
|
2402
2502
|
...activeBinding.modelVariant ? { variant: activeBinding.modelVariant } : {}
|
|
@@ -2900,7 +3000,7 @@ async function handleSessionError(runtime, event) {
|
|
|
2900
3000
|
component: "plugin-event",
|
|
2901
3001
|
sessionId: event.sessionId
|
|
2902
3002
|
});
|
|
2903
|
-
if (runtime.container.foregroundSessionTracker.fail(event.sessionId, event.error
|
|
3003
|
+
if (runtime.container.foregroundSessionTracker.fail(event.sessionId, normalizeForegroundSessionError(event.error))) {
|
|
2904
3004
|
logger.warn({
|
|
2905
3005
|
error: event.error,
|
|
2906
3006
|
event: "plugin-event.session.error.foreground_suppressed"
|
|
@@ -3018,6 +3118,16 @@ function extractSessionErrorMessage(error) {
|
|
|
3018
3118
|
if (isPlainRecord(error.data) && typeof error.data.message === "string" && error.data.message.trim().length > 0) return error.data.message.trim();
|
|
3019
3119
|
return asNonEmptyString(error.name);
|
|
3020
3120
|
}
|
|
3121
|
+
function normalizeForegroundSessionError(error) {
|
|
3122
|
+
if (error instanceof Error) return error;
|
|
3123
|
+
if (isPlainRecord(error) && typeof error.name === "string" && error.name.trim().length > 0) {
|
|
3124
|
+
const normalized = new Error(extractSessionErrorMessage(error) ?? "Unknown session error.");
|
|
3125
|
+
normalized.name = error.name.trim();
|
|
3126
|
+
if (isPlainRecord(error.data)) normalized.data = error.data;
|
|
3127
|
+
return normalized;
|
|
3128
|
+
}
|
|
3129
|
+
return /* @__PURE__ */ new Error("Unknown session error.");
|
|
3130
|
+
}
|
|
3021
3131
|
function asNonEmptyString(value) {
|
|
3022
3132
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
3023
3133
|
}
|
|
@@ -4713,7 +4823,7 @@ var MARKDOWN_SPECIAL_CHARACTERS = /([_*\[\]()~`>#+\-=|{}.!\\])/g;
|
|
|
4713
4823
|
function buildTelegramPromptReply(result, copy = BOT_COPY) {
|
|
4714
4824
|
const renderedMarkdown = result.bodyMd ? renderMarkdownToTelegramMarkdownV2(result.bodyMd) : null;
|
|
4715
4825
|
const footerPlain = formatPlainMetricsFooter(result.metrics, copy);
|
|
4716
|
-
const fallback = { text: joinBodyAndFooter(truncatePlainBody(normalizePlainBody(result,
|
|
4826
|
+
const fallback = { text: joinBodyAndFooter(truncatePlainBody(normalizePlainBody(result, copy), footerPlain), footerPlain) };
|
|
4717
4827
|
if (!renderedMarkdown) return {
|
|
4718
4828
|
preferred: fallback,
|
|
4719
4829
|
fallback
|
|
@@ -4817,24 +4927,10 @@ function renderMarkdownToTelegramMarkdownV2(markdown) {
|
|
|
4817
4927
|
if (inCodeBlock) return null;
|
|
4818
4928
|
return rendered.join("\n");
|
|
4819
4929
|
}
|
|
4820
|
-
function
|
|
4821
|
-
const lines = preprocessMarkdownForTelegram(markdown).split("\n");
|
|
4822
|
-
const rendered = [];
|
|
4823
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
4824
|
-
const tableBlock = consumeMarkdownTable(lines, index);
|
|
4825
|
-
if (tableBlock) {
|
|
4826
|
-
rendered.push(renderTableAsPlainText(tableBlock.rows));
|
|
4827
|
-
index = tableBlock.nextIndex - 1;
|
|
4828
|
-
continue;
|
|
4829
|
-
}
|
|
4830
|
-
rendered.push(lines[index]);
|
|
4831
|
-
}
|
|
4832
|
-
return rendered.join("\n").replace(/```[A-Za-z0-9_-]*\n?/g, "").replace(/```/g, "").replace(/^#{1,6}\s+/gm, "").replace(/^\s*>\s?/gm, "").replace(/^(\s*)[-+*]\s+/gm, "$1- ").replace(/^(\s*)(\d+)[.)]\s+/gm, "$1$2. ").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/(\*\*|__)(.*?)\1/g, "$2").replace(/(\*|_)(.*?)\1/g, "$2").replace(/`([^`]+)`/g, "$1").trim();
|
|
4833
|
-
}
|
|
4834
|
-
function normalizePlainBody(result, preferStructured, copy) {
|
|
4930
|
+
function normalizePlainBody(result, copy) {
|
|
4835
4931
|
const fromStructured = result.bodyMd ? stripMarkdownToPlainText(result.bodyMd) : "";
|
|
4836
4932
|
const fromFallback = result.fallbackText.trim();
|
|
4837
|
-
return (
|
|
4933
|
+
return (fromStructured || fromFallback).trim() || result.fallbackText || copy.prompt.emptyResponse;
|
|
4838
4934
|
}
|
|
4839
4935
|
function truncatePlainBody(body, footer) {
|
|
4840
4936
|
const reservedLength = footer.length + 2;
|
|
@@ -4958,9 +5054,6 @@ function renderTableAsTelegramCodeBlock(rows) {
|
|
|
4958
5054
|
"```"
|
|
4959
5055
|
].join("\n");
|
|
4960
5056
|
}
|
|
4961
|
-
function renderTableAsPlainText(rows) {
|
|
4962
|
-
return buildAlignedTableLines(rows).join("\n");
|
|
4963
|
-
}
|
|
4964
5057
|
function buildAlignedTableLines(rows) {
|
|
4965
5058
|
const columnWidths = calculateTableColumnWidths(rows);
|
|
4966
5059
|
return [
|
|
@@ -5417,7 +5510,7 @@ async function executePromptRequest(ctx, dependencies, resolvePrompt) {
|
|
|
5417
5510
|
},
|
|
5418
5511
|
signal: foregroundRequest.signal,
|
|
5419
5512
|
text: promptInput.text
|
|
5420
|
-
})).assistantReply, copy
|
|
5513
|
+
})).assistantReply, copy), copy);
|
|
5421
5514
|
try {
|
|
5422
5515
|
await ctx.reply(telegramReply.preferred.text, telegramReply.preferred.options);
|
|
5423
5516
|
} catch (replyError) {
|
|
@@ -5438,26 +5531,14 @@ async function executePromptRequest(ctx, dependencies, resolvePrompt) {
|
|
|
5438
5531
|
}
|
|
5439
5532
|
}
|
|
5440
5533
|
}
|
|
5441
|
-
function normalizePromptReplyForDisplay(promptReply, copy
|
|
5534
|
+
function normalizePromptReplyForDisplay(promptReply, copy) {
|
|
5442
5535
|
if (!promptReply.assistantError) return promptReply;
|
|
5443
|
-
if (isRecoverableStructuredOutputError(promptReply)) {
|
|
5444
|
-
dependencies.logger.warn?.({ error: promptReply.assistantError }, "structured output validation failed, falling back to assistant text reply");
|
|
5445
|
-
return {
|
|
5446
|
-
...promptReply,
|
|
5447
|
-
assistantError: null
|
|
5448
|
-
};
|
|
5449
|
-
}
|
|
5450
5536
|
return {
|
|
5451
5537
|
...promptReply,
|
|
5452
5538
|
bodyMd: null,
|
|
5453
5539
|
fallbackText: presentError(promptReply.assistantError, copy)
|
|
5454
5540
|
};
|
|
5455
5541
|
}
|
|
5456
|
-
function isRecoverableStructuredOutputError(promptReply) {
|
|
5457
|
-
if (promptReply.assistantError?.name !== "StructuredOutputError") return false;
|
|
5458
|
-
if (promptReply.bodyMd?.trim()) return true;
|
|
5459
|
-
return promptReply.parts.some((part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0);
|
|
5460
|
-
}
|
|
5461
5542
|
//#endregion
|
|
5462
5543
|
//#region src/bot/handlers/file.handler.ts
|
|
5463
5544
|
var TELEGRAM_MAX_DOWNLOAD_BYTES = 20 * 1024 * 1024;
|