@grinev/opencode-telegram-bot 0.21.2 → 0.22.1
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/.env.example +0 -15
- package/README.md +15 -8
- package/dist/app/formatters/summary-formatter.js +16 -0
- package/dist/app/managers/summary-aggregation-manager.js +174 -10
- package/dist/app/services/file-download-service.js +51 -2
- package/dist/app/services/scheduled-task-executor-service.js +22 -7
- package/dist/app/stores/settings-store.js +46 -4
- package/dist/bot/callbacks/callback-router.js +4 -1
- package/dist/bot/callbacks/scheduled-task-callback-handler.js +20 -1
- package/dist/bot/callbacks/settings-callback-handler.js +105 -0
- package/dist/bot/commands/definitions.js +1 -1
- package/dist/bot/commands/settings-command.js +10 -0
- package/dist/bot/commands/status-command.js +7 -2
- package/dist/bot/handlers/document-handler.js +1 -1
- package/dist/bot/handlers/media-group-handler.js +1 -1
- package/dist/bot/handlers/prompt.js +3 -3
- package/dist/bot/handlers/voice-handler.js +5 -2
- package/dist/bot/menus/inline-menu.js +3 -1
- package/dist/bot/menus/settings-menu.js +52 -0
- package/dist/bot/messages/thinking-message.js +1 -4
- package/dist/bot/messages/thinking-rendering.js +66 -0
- package/dist/bot/routers/command-router.js +2 -2
- package/dist/bot/services/event-subscription-service.js +304 -30
- package/dist/bot/streaming/compact-progress-streamer.js +168 -0
- package/dist/bot/streaming/finalize-assistant-response.js +9 -3
- package/dist/bot/streaming/response-streamer.js +16 -1
- package/dist/config.js +0 -15
- package/dist/i18n/ar.js +31 -6
- package/dist/i18n/de.js +32 -7
- package/dist/i18n/en.js +32 -7
- package/dist/i18n/es.js +32 -7
- package/dist/i18n/fr.js +32 -7
- package/dist/i18n/ru.js +32 -7
- package/dist/i18n/zh.js +32 -7
- package/dist/opencode/process.js +40 -3
- package/package.json +1 -1
- package/dist/bot/commands/tts-command.js +0 -13
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { isTtsConfigured } from "../../app/services/tts-service.js";
|
|
2
|
+
import { getCompactOutputMode, getResponseStreamingMode, getSendDiffFileAttachments, getShowAssistantRunFooter, getShowThinkingContent, getTtsMode, setCompactOutputMode, setResponseStreamingMode, setSendDiffFileAttachments, setShowAssistantRunFooter, setShowThinkingContent, setTtsMode, } from "../../app/stores/settings-store.js";
|
|
3
|
+
import { t } from "../../i18n/index.js";
|
|
4
|
+
import { logger } from "../../utils/logger.js";
|
|
5
|
+
import { appendInlineMenuCancelButton, ensureActiveInlineMenu } from "../menus/inline-menu.js";
|
|
6
|
+
import { buildSettingsMenuView, SETTINGS_ASSISTANT_FOOTER_CALLBACK, SETTINGS_CALLBACK_PREFIX, SETTINGS_COMPACT_OUTPUT_CALLBACK, SETTINGS_DIFF_FILES_CALLBACK, SETTINGS_RESPONSE_STREAMING_CALLBACK, SETTINGS_THINKING_CONTENT_CALLBACK, SETTINGS_TTS_CALLBACK, } from "../menus/settings-menu.js";
|
|
7
|
+
function getTtsSavedMessageKey(mode) {
|
|
8
|
+
if (mode === "all") {
|
|
9
|
+
return "tts.all";
|
|
10
|
+
}
|
|
11
|
+
if (mode === "auto") {
|
|
12
|
+
return "tts.auto";
|
|
13
|
+
}
|
|
14
|
+
return "tts.off";
|
|
15
|
+
}
|
|
16
|
+
function getNextTtsMode(mode) {
|
|
17
|
+
if (mode === "off") {
|
|
18
|
+
return "all";
|
|
19
|
+
}
|
|
20
|
+
if (mode === "all") {
|
|
21
|
+
return "auto";
|
|
22
|
+
}
|
|
23
|
+
return "off";
|
|
24
|
+
}
|
|
25
|
+
function getNextResponseStreamingMode(mode) {
|
|
26
|
+
return mode === "edit" ? "draft" : "edit";
|
|
27
|
+
}
|
|
28
|
+
export async function handleSettingsCallback(ctx) {
|
|
29
|
+
const callbackData = ctx.callbackQuery?.data;
|
|
30
|
+
if (!callbackData?.startsWith(SETTINGS_CALLBACK_PREFIX)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const isActiveMenu = await ensureActiveInlineMenu(ctx, "settings");
|
|
34
|
+
if (!isActiveMenu) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
if (callbackData === SETTINGS_COMPACT_OUTPUT_CALLBACK) {
|
|
39
|
+
setCompactOutputMode(!getCompactOutputMode());
|
|
40
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
41
|
+
await ctx.answerCallbackQuery({ text: t("settings.saved") });
|
|
42
|
+
await ctx.editMessageText(text, {
|
|
43
|
+
reply_markup: appendInlineMenuCancelButton(keyboard, "settings"),
|
|
44
|
+
});
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (callbackData === SETTINGS_THINKING_CONTENT_CALLBACK) {
|
|
48
|
+
setShowThinkingContent(!getShowThinkingContent());
|
|
49
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
50
|
+
await ctx.answerCallbackQuery({ text: t("settings.saved") });
|
|
51
|
+
await ctx.editMessageText(text, {
|
|
52
|
+
reply_markup: appendInlineMenuCancelButton(keyboard, "settings"),
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (callbackData === SETTINGS_RESPONSE_STREAMING_CALLBACK) {
|
|
57
|
+
setResponseStreamingMode(getNextResponseStreamingMode(getResponseStreamingMode()));
|
|
58
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
59
|
+
await ctx.answerCallbackQuery({ text: t("settings.saved") });
|
|
60
|
+
await ctx.editMessageText(text, {
|
|
61
|
+
reply_markup: appendInlineMenuCancelButton(keyboard, "settings"),
|
|
62
|
+
});
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (callbackData === SETTINGS_DIFF_FILES_CALLBACK) {
|
|
66
|
+
setSendDiffFileAttachments(!getSendDiffFileAttachments());
|
|
67
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
68
|
+
await ctx.answerCallbackQuery({ text: t("settings.saved") });
|
|
69
|
+
await ctx.editMessageText(text, {
|
|
70
|
+
reply_markup: appendInlineMenuCancelButton(keyboard, "settings"),
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (callbackData === SETTINGS_ASSISTANT_FOOTER_CALLBACK) {
|
|
75
|
+
setShowAssistantRunFooter(!getShowAssistantRunFooter());
|
|
76
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
77
|
+
await ctx.answerCallbackQuery({ text: t("settings.saved") });
|
|
78
|
+
await ctx.editMessageText(text, {
|
|
79
|
+
reply_markup: appendInlineMenuCancelButton(keyboard, "settings"),
|
|
80
|
+
});
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (callbackData === SETTINGS_TTS_CALLBACK) {
|
|
84
|
+
const nextMode = getNextTtsMode(getTtsMode());
|
|
85
|
+
if (nextMode !== "off" && !isTtsConfigured()) {
|
|
86
|
+
await ctx.answerCallbackQuery({ text: t("tts.not_configured"), show_alert: true });
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
setTtsMode(nextMode);
|
|
90
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
91
|
+
await ctx.answerCallbackQuery({ text: t(getTtsSavedMessageKey(nextMode)) });
|
|
92
|
+
await ctx.editMessageText(text, {
|
|
93
|
+
reply_markup: appendInlineMenuCancelButton(keyboard, "settings"),
|
|
94
|
+
});
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
await ctx.answerCallbackQuery({ text: t("callback.processing_error") });
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
logger.error("[Settings] Error handling settings callback:", error);
|
|
102
|
+
await ctx.answerCallbackQuery({ text: t("callback.processing_error") }).catch(() => { });
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -10,7 +10,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
10
10
|
{ command: "detach", descriptionKey: "cmd.description.detach" },
|
|
11
11
|
{ command: "sessions", descriptionKey: "cmd.description.sessions" },
|
|
12
12
|
{ command: "messages", descriptionKey: "cmd.description.messages" },
|
|
13
|
-
{ command: "
|
|
13
|
+
{ command: "settings", descriptionKey: "cmd.description.settings" },
|
|
14
14
|
{ command: "projects", descriptionKey: "cmd.description.projects" },
|
|
15
15
|
{ command: "worktree", descriptionKey: "cmd.description.worktree" },
|
|
16
16
|
{ command: "task", descriptionKey: "cmd.description.task" },
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { buildSettingsMenuView } from "../menus/settings-menu.js";
|
|
2
|
+
import { replyWithInlineMenu } from "../menus/inline-menu.js";
|
|
3
|
+
export async function settingsCommand(ctx) {
|
|
4
|
+
const { text, keyboard } = buildSettingsMenuView();
|
|
5
|
+
await replyWithInlineMenu(ctx, {
|
|
6
|
+
menuKind: "settings",
|
|
7
|
+
text,
|
|
8
|
+
keyboard,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { opencodeClient } from "../../opencode/client.js";
|
|
2
2
|
import { getGitWorktreeContext } from "../../app/services/worktree-service.js";
|
|
3
3
|
import { getCurrentSession } from "../../app/services/session-service.js";
|
|
4
|
-
import { getCurrentProject,
|
|
4
|
+
import { getCurrentProject, getTtsMode } from "../../app/stores/settings-store.js";
|
|
5
5
|
import { fetchCurrentAgent } from "../../app/services/agent-selection-service.js";
|
|
6
6
|
import { fetchCurrentModel } from "../../app/services/model-selection-service.js";
|
|
7
7
|
import { getAgentDisplayName } from "../../app/types/agent.js";
|
|
@@ -22,8 +22,13 @@ export async function statusCommand(ctx) {
|
|
|
22
22
|
if (data.version) {
|
|
23
23
|
message += `${t("status.line.version", { version: data.version })}\n`;
|
|
24
24
|
}
|
|
25
|
+
const ttsMode = getTtsMode();
|
|
25
26
|
message += `${t("status.line.tts", {
|
|
26
|
-
tts:
|
|
27
|
+
tts: ttsMode === "off"
|
|
28
|
+
? t("status.tts.off")
|
|
29
|
+
: ttsMode === "all"
|
|
30
|
+
? t("status.tts.all")
|
|
31
|
+
: t("status.tts.auto"),
|
|
27
32
|
})}\n`;
|
|
28
33
|
// Add agent information
|
|
29
34
|
const currentAgent = await fetchCurrentAgent();
|
|
@@ -18,7 +18,7 @@ export async function handleDocumentMessage(ctx, deps) {
|
|
|
18
18
|
const mimeType = doc.mime_type || "";
|
|
19
19
|
const filename = doc.file_name || "document";
|
|
20
20
|
try {
|
|
21
|
-
if (isTextMimeType(mimeType)) {
|
|
21
|
+
if (isTextMimeType(mimeType, filename)) {
|
|
22
22
|
if (!isFileSizeAllowed(doc.file_size, config.files.maxFileSizeKb)) {
|
|
23
23
|
logger.warn(`[Document] Text file too large: ${filename} (${doc.file_size} bytes > ${config.files.maxFileSizeKb}KB)`);
|
|
24
24
|
await ctx.reply(t("bot.text_file_too_large", { maxSizeKb: String(config.files.maxFileSizeKb) }));
|
|
@@ -137,7 +137,7 @@ export class MediaGroupAttachmentHandler {
|
|
|
137
137
|
const document = item.document;
|
|
138
138
|
const mimeType = document.mime_type || "";
|
|
139
139
|
const filename = document.file_name || "document";
|
|
140
|
-
if (isTextMimeType(mimeType)) {
|
|
140
|
+
if (isTextMimeType(mimeType, filename)) {
|
|
141
141
|
if (!isFileSizeAllowed(document.file_size, config.files.maxFileSizeKb)) {
|
|
142
142
|
return { reason: "text_file_too_large" };
|
|
143
143
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { opencodeClient } from "../../opencode/client.js";
|
|
2
|
-
import { clearSession, getCurrentSession, setCurrentSession } from "../../app/services/session-service.js";
|
|
2
|
+
import { clearSession, getCurrentSession, setCurrentSession, } from "../../app/services/session-service.js";
|
|
3
3
|
import { ingestSessionInfoForCache } from "../../app/services/session-cache-service.js";
|
|
4
|
-
import { getCurrentProject,
|
|
4
|
+
import { getCurrentProject, getTtsMode } from "../../app/stores/settings-store.js";
|
|
5
5
|
import { getStoredAgent, resolveProjectAgent } from "../../app/services/agent-selection-service.js";
|
|
6
6
|
import { getStoredModel } from "../../app/services/model-selection-service.js";
|
|
7
7
|
import { formatVariantForButton } from "../../app/services/variant-selection-service.js";
|
|
@@ -91,7 +91,7 @@ async function resetMismatchedSessionContext() {
|
|
|
91
91
|
*/
|
|
92
92
|
export async function processUserPrompt(ctx, text, deps, fileParts = [], options = {}) {
|
|
93
93
|
const { bot, ensureEventSubscription } = deps;
|
|
94
|
-
const responseMode = options.responseMode ?? (
|
|
94
|
+
const responseMode = options.responseMode ?? (getTtsMode() === "all" ? "text_and_tts" : "text_only");
|
|
95
95
|
const currentProject = getCurrentProject();
|
|
96
96
|
if (!currentProject) {
|
|
97
97
|
await ctx.reply(t("bot.project_not_selected"));
|
|
@@ -4,7 +4,8 @@ import { URL } from "node:url";
|
|
|
4
4
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
5
5
|
import { SocksProxyAgent } from "socks-proxy-agent";
|
|
6
6
|
import { config } from "../../config.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getTtsMode } from "../../app/stores/settings-store.js";
|
|
8
|
+
import { isSttConfigured, transcribeAudio, } from "../../app/services/stt-service.js";
|
|
8
9
|
import { processUserPrompt } from "./prompt.js";
|
|
9
10
|
import { logger } from "../../utils/logger.js";
|
|
10
11
|
import { t } from "../../i18n/index.js";
|
|
@@ -169,7 +170,9 @@ export async function handleVoiceMessage(ctx, deps) {
|
|
|
169
170
|
textForLLM = `${llmNote}\n${recognizedText}`;
|
|
170
171
|
}
|
|
171
172
|
// Process the recognized text as a prompt
|
|
172
|
-
|
|
173
|
+
const currentTtsMode = getTtsMode();
|
|
174
|
+
const responseMode = currentTtsMode === "all" || currentTtsMode === "auto" ? "text_and_tts" : "text_only";
|
|
175
|
+
await processPrompt(ctx, textForLLM, deps, [], { responseMode });
|
|
173
176
|
}
|
|
174
177
|
catch (err) {
|
|
175
178
|
const errorMessage = err instanceof Error ? err.message : "unknown error";
|
|
@@ -13,6 +13,7 @@ const INLINE_MENU_KINDS = [
|
|
|
13
13
|
"open",
|
|
14
14
|
"ls",
|
|
15
15
|
"worktree",
|
|
16
|
+
"settings",
|
|
16
17
|
];
|
|
17
18
|
export function isInlineMenuKind(value) {
|
|
18
19
|
return INLINE_MENU_KINDS.includes(value);
|
|
@@ -53,7 +54,8 @@ export function appendInlineMenuCancelButton(keyboard, menuKind) {
|
|
|
53
54
|
if (keyboard.inline_keyboard.length > 0) {
|
|
54
55
|
keyboard.row();
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
+
const buttonText = menuKind === "settings" ? t("inline.button.close") : t("inline.button.cancel");
|
|
58
|
+
keyboard.text(buttonText, getInlineCancelCallbackData(menuKind));
|
|
57
59
|
return keyboard;
|
|
58
60
|
}
|
|
59
61
|
export async function replyWithInlineMenu(ctx, options) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { InlineKeyboard } from "grammy";
|
|
2
|
+
import { getCompactOutputMode, getResponseStreamingMode, getSendDiffFileAttachments, getShowAssistantRunFooter, getShowThinkingContent, getTtsMode, } from "../../app/stores/settings-store.js";
|
|
3
|
+
import { t } from "../../i18n/index.js";
|
|
4
|
+
export const SETTINGS_CALLBACK_PREFIX = "settings:";
|
|
5
|
+
export const SETTINGS_COMPACT_OUTPUT_CALLBACK = `${SETTINGS_CALLBACK_PREFIX}compact_output`;
|
|
6
|
+
export const SETTINGS_THINKING_CONTENT_CALLBACK = `${SETTINGS_CALLBACK_PREFIX}thinking_content`;
|
|
7
|
+
export const SETTINGS_RESPONSE_STREAMING_CALLBACK = `${SETTINGS_CALLBACK_PREFIX}response_streaming`;
|
|
8
|
+
export const SETTINGS_DIFF_FILES_CALLBACK = `${SETTINGS_CALLBACK_PREFIX}diff_files`;
|
|
9
|
+
export const SETTINGS_ASSISTANT_FOOTER_CALLBACK = `${SETTINGS_CALLBACK_PREFIX}assistant_footer`;
|
|
10
|
+
export const SETTINGS_TTS_CALLBACK = `${SETTINGS_CALLBACK_PREFIX}tts`;
|
|
11
|
+
export function formatBooleanSettingValue(enabled) {
|
|
12
|
+
return enabled ? t("settings.value.on") : t("settings.value.off");
|
|
13
|
+
}
|
|
14
|
+
export function formatTtsModeValue(mode) {
|
|
15
|
+
if (mode === "all") {
|
|
16
|
+
return t("status.tts.all");
|
|
17
|
+
}
|
|
18
|
+
if (mode === "auto") {
|
|
19
|
+
return t("status.tts.auto");
|
|
20
|
+
}
|
|
21
|
+
return t("status.tts.off");
|
|
22
|
+
}
|
|
23
|
+
export function formatResponseStreamingModeValue(mode) {
|
|
24
|
+
return mode === "draft"
|
|
25
|
+
? t("settings.response_streaming.draft")
|
|
26
|
+
: t("settings.response_streaming.edit");
|
|
27
|
+
}
|
|
28
|
+
export function buildSettingsMenuView() {
|
|
29
|
+
const compactOutputMode = getCompactOutputMode();
|
|
30
|
+
const showThinkingContent = getShowThinkingContent();
|
|
31
|
+
const responseStreamingMode = getResponseStreamingMode();
|
|
32
|
+
const sendDiffFileAttachments = getSendDiffFileAttachments();
|
|
33
|
+
const showAssistantRunFooter = getShowAssistantRunFooter();
|
|
34
|
+
const ttsMode = getTtsMode();
|
|
35
|
+
const keyboard = new InlineKeyboard()
|
|
36
|
+
.text(`${t("settings.compact_output.label")}: ${formatBooleanSettingValue(compactOutputMode)}`, SETTINGS_COMPACT_OUTPUT_CALLBACK);
|
|
37
|
+
if (!compactOutputMode) {
|
|
38
|
+
keyboard.row().text(`${t("settings.thinking_content.label")}: ${formatBooleanSettingValue(showThinkingContent)}`, SETTINGS_THINKING_CONTENT_CALLBACK);
|
|
39
|
+
keyboard.row().text(`${t("settings.diff_files.label")}: ${formatBooleanSettingValue(sendDiffFileAttachments)}`, SETTINGS_DIFF_FILES_CALLBACK);
|
|
40
|
+
}
|
|
41
|
+
keyboard
|
|
42
|
+
.row()
|
|
43
|
+
.text(`${t("settings.response_streaming.label")}: ${formatResponseStreamingModeValue(responseStreamingMode)}`, SETTINGS_RESPONSE_STREAMING_CALLBACK)
|
|
44
|
+
.row()
|
|
45
|
+
.text(`${t("settings.assistant_footer.label")}: ${formatBooleanSettingValue(showAssistantRunFooter)}`, SETTINGS_ASSISTANT_FOOTER_CALLBACK)
|
|
46
|
+
.row()
|
|
47
|
+
.text(`${t("settings.tts.label")}: ${formatTtsModeValue(ttsMode)}`, SETTINGS_TTS_CALLBACK);
|
|
48
|
+
return {
|
|
49
|
+
text: t("settings.menu.title"),
|
|
50
|
+
keyboard,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { t } from "../../i18n/index.js";
|
|
2
|
-
export function deliverThinkingMessage(sessionId, batcher
|
|
3
|
-
if (options.hideThinkingMessages) {
|
|
4
|
-
return;
|
|
5
|
-
}
|
|
2
|
+
export function deliverThinkingMessage(sessionId, batcher) {
|
|
6
3
|
const message = t("bot.thinking");
|
|
7
4
|
batcher.sendTextNow(sessionId, message, "thinking_started");
|
|
8
5
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { t } from "../../i18n/index.js";
|
|
2
|
+
function formatHeader(title) {
|
|
3
|
+
const fallback = t("bot.thinking");
|
|
4
|
+
const normalizedTitle = title?.trim();
|
|
5
|
+
return normalizedTitle ? `${fallback} — ${normalizedTitle}` : fallback;
|
|
6
|
+
}
|
|
7
|
+
function quoteFallbackText(text) {
|
|
8
|
+
return text
|
|
9
|
+
.split("\n")
|
|
10
|
+
.map((line) => `> ${line}`)
|
|
11
|
+
.join("\n");
|
|
12
|
+
}
|
|
13
|
+
function splitText(text, maxLength) {
|
|
14
|
+
if (text.length <= maxLength) {
|
|
15
|
+
return [text];
|
|
16
|
+
}
|
|
17
|
+
const chunks = [];
|
|
18
|
+
for (let offset = 0; offset < text.length; offset += maxLength) {
|
|
19
|
+
chunks.push(text.slice(offset, offset + maxLength));
|
|
20
|
+
}
|
|
21
|
+
return chunks;
|
|
22
|
+
}
|
|
23
|
+
function createThinkingPart(header, text, expandable) {
|
|
24
|
+
if (!text) {
|
|
25
|
+
return {
|
|
26
|
+
text: header,
|
|
27
|
+
fallbackText: header,
|
|
28
|
+
source: "entities",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const renderedText = `${header}\n${text}`;
|
|
32
|
+
const entity = {
|
|
33
|
+
type: expandable ? "expandable_blockquote" : "blockquote",
|
|
34
|
+
offset: header.length + 1,
|
|
35
|
+
length: text.length,
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
text: renderedText,
|
|
39
|
+
entities: [entity],
|
|
40
|
+
fallbackText: `${header}\n${quoteFallbackText(text)}`,
|
|
41
|
+
source: "entities",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function prepareThinkingStreamingPayload(sections, maxPartLength, options = {}) {
|
|
45
|
+
const parts = [];
|
|
46
|
+
const expandable = options.expandable ?? true;
|
|
47
|
+
for (const section of sections) {
|
|
48
|
+
const header = formatHeader(section.title);
|
|
49
|
+
const text = section.text.replace(/\r\n/g, "\n").trimEnd();
|
|
50
|
+
const textLimit = Math.max(1, maxPartLength - header.length - 1);
|
|
51
|
+
const chunks = text ? splitText(text, textLimit) : [""];
|
|
52
|
+
for (const chunk of chunks) {
|
|
53
|
+
parts.push(createThinkingPart(header, chunk, expandable));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return parts.length > 0 ? { parts } : null;
|
|
57
|
+
}
|
|
58
|
+
export function makeThinkingPayloadExpandable(payload) {
|
|
59
|
+
return {
|
|
60
|
+
...payload,
|
|
61
|
+
parts: payload.parts.map((part) => ({
|
|
62
|
+
...part,
|
|
63
|
+
entities: part.entities?.map((entity) => entity.type === "blockquote" ? { ...entity, type: "expandable_blockquote" } : entity),
|
|
64
|
+
})),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { config } from "../../config.js";
|
|
2
|
-
import {
|
|
2
|
+
import { settingsCommand } from "../commands/settings-command.js";
|
|
3
3
|
import { opencodeStartCommand } from "../commands/opencode-start-command.js";
|
|
4
4
|
import { opencodeStopCommand } from "../commands/opencode-stop-command.js";
|
|
5
5
|
import { projectsCommand } from "../commands/projects-command.js";
|
|
@@ -52,7 +52,7 @@ export function registerCommandRouter(bot, deps) {
|
|
|
52
52
|
bot.command("start", startCommand);
|
|
53
53
|
bot.command("help", helpCommand);
|
|
54
54
|
bot.command("status", statusCommand);
|
|
55
|
-
bot.command("
|
|
55
|
+
bot.command("settings", settingsCommand);
|
|
56
56
|
bot.command("opencode_start", opencodeStartCommand);
|
|
57
57
|
bot.command("opencode_stop", opencodeStopCommand);
|
|
58
58
|
bot.command("projects", projectsCommand);
|