@grinev/opencode-telegram-bot 0.21.1 → 0.22.0
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 +10 -15
- package/README.md +33 -13
- package/dist/app/{start-bot-app.js → bootstrap/start-bot-app.js} +16 -15
- package/dist/{bot/utils/assistant-run-footer.js → app/formatters/assistant-run-footer-formatter.js} +1 -1
- package/dist/{summary → app/formatters}/subagent-formatter.js +8 -3
- package/dist/{summary/formatter.js → app/formatters/summary-formatter.js} +20 -132
- package/dist/{summary → app/formatters}/tool-message-batcher.js +1 -1
- package/dist/{bot/assistant-run-state.js → app/managers/assistant-run-state-manager.js} +1 -1
- package/dist/{attach/manager.js → app/managers/attach-manager.js} +1 -1
- package/dist/{background-session/tracker.js → app/managers/background-session-manager.js} +2 -2
- package/dist/{scheduled-task/foreground-state.js → app/managers/foreground-session-state-manager.js} +1 -1
- package/dist/{interaction/manager.js → app/managers/interaction-manager.js} +31 -1
- package/dist/{permission/manager.js → app/managers/permission-manager.js} +1 -1
- package/dist/{question/manager.js → app/managers/question-manager.js} +1 -1
- package/dist/{rename/manager.js → app/managers/rename-manager.js} +1 -1
- package/dist/{scheduled-task/creation-manager.js → app/managers/scheduled-task-creation-manager.js} +2 -2
- package/dist/{summary/aggregator.js → app/managers/summary-aggregation-manager.js} +177 -13
- package/dist/{agent/manager.js → app/services/agent-selection-service.js} +4 -5
- package/dist/{attach/service.js → app/services/attach-service.js} +23 -44
- package/dist/{bot/utils/busy-reconciliation.js → app/services/busy-reconciliation-service.js} +12 -7
- package/dist/app/services/command-catalog-service.js +21 -0
- package/dist/{bot/utils/external-user-input.js → app/services/external-user-input-service.js} +4 -19
- package/dist/app/services/file-browser-service.js +265 -0
- package/dist/{bot/utils/file-download.js → app/services/file-download-service.js} +14 -28
- package/dist/app/services/mcp-catalog-service.js +79 -0
- package/dist/app/services/message-history-service.js +81 -0
- package/dist/{model/capabilities.js → app/services/model-capabilities-service.js} +2 -2
- package/dist/{model/context-limit.js → app/services/model-context-limit-service.js} +4 -4
- package/dist/{model/manager.js → app/services/model-selection-service.js} +4 -4
- package/dist/{project/manager.js → app/services/project-service.js} +3 -3
- package/dist/app/services/project-switch-service.js +39 -0
- package/dist/{bot/utils/busy-guard.js → app/services/run-control-service.js} +3 -14
- package/dist/{scheduled-task/executor.js → app/services/scheduled-task-executor-service.js} +27 -12
- package/dist/{scheduled-task/runtime.js → app/services/scheduled-task-runtime-service.js} +17 -56
- package/dist/{scheduled-task/schedule-parser.js → app/services/scheduled-task-schedule-parser-service.js} +3 -3
- package/dist/{scheduled-task/session-ignore.js → app/services/scheduled-task-session-ignore-service.js} +1 -1
- package/dist/{session/cache-manager.js → app/services/session-cache-service.js} +3 -3
- package/dist/{session/manager.js → app/services/session-service.js} +1 -1
- package/dist/app/services/skills-catalog-service.js +21 -0
- package/dist/{stt/client.js → app/services/stt-service.js} +2 -2
- package/dist/{tts/client.js → app/services/tts-service.js} +68 -5
- package/dist/{variant/manager.js → app/services/variant-selection-service.js} +3 -3
- package/dist/{scheduled-task/store.js → app/stores/scheduled-task-store.js} +3 -3
- package/dist/{settings/manager.js → app/stores/settings-store.js} +49 -7
- package/dist/app/types/worktree.js +1 -0
- package/dist/bot/{handlers/agent.js → callbacks/agent-selection-callback-handler.js} +8 -57
- package/dist/bot/callbacks/callback-router.js +113 -0
- package/dist/bot/{commands/commands.js → callbacks/command-catalog-callback-handler.js} +17 -197
- package/dist/bot/{handlers/context.js → callbacks/context-control-callback-handler.js} +3 -32
- package/dist/bot/callbacks/file-browser-callback-handler.js +219 -0
- package/dist/bot/callbacks/inline-menu-cancel-callback-handler.js +32 -0
- package/dist/bot/{commands/mcps.js → callbacks/mcp-catalog-callback-handler.js} +7 -172
- package/dist/bot/{commands/messages.js → callbacks/message-history-callback-handler.js} +17 -248
- package/dist/bot/{handlers/model.js → callbacks/model-selection-callback-handler.js} +10 -71
- package/dist/bot/callbacks/permission-callback-handler.js +114 -0
- package/dist/bot/callbacks/project-callback-handler.js +79 -0
- package/dist/bot/callbacks/question-callback-handler.js +159 -0
- package/dist/bot/{commands/rename.js → callbacks/rename-callback-handler.js} +6 -34
- package/dist/bot/{commands/tasklist.js → callbacks/scheduled-task-callback-handler.js} +83 -68
- package/dist/bot/{commands/sessions.js → callbacks/session-callback-handler.js} +19 -158
- package/dist/bot/callbacks/settings-callback-handler.js +105 -0
- package/dist/bot/{commands/skills.js → callbacks/skills-catalog-callback-handler.js} +8 -188
- package/dist/bot/{handlers/variant.js → callbacks/variant-selection-callback-handler.js} +7 -69
- package/dist/bot/callbacks/worktree-callback-handler.js +97 -0
- package/dist/bot/commands/{abort.js → abort-command.js} +6 -6
- package/dist/bot/commands/command-catalog-command.js +42 -0
- package/dist/bot/commands/definitions.js +1 -1
- package/dist/bot/commands/{detach.js → detach-command.js} +8 -8
- package/dist/bot/commands/ls-command.js +48 -0
- package/dist/bot/commands/mcp-catalog-command.js +39 -0
- package/dist/bot/commands/messages-command.js +59 -0
- package/dist/bot/commands/{new.js → new-command.js} +12 -11
- package/dist/bot/commands/open-command.js +45 -0
- package/dist/bot/commands/{opencode-start.js → opencode-start-command.js} +1 -1
- package/dist/bot/commands/{opencode-stop.js → opencode-stop-command.js} +1 -1
- package/dist/bot/commands/projects-command.js +32 -0
- package/dist/bot/commands/rename-command.js +33 -0
- package/dist/bot/commands/sessions-command.js +42 -0
- package/dist/bot/commands/settings-command.js +10 -0
- package/dist/bot/commands/skills-catalog-command.js +42 -0
- package/dist/bot/commands/{start.js → start-command.js} +12 -12
- package/dist/bot/commands/{status.js → status-command.js} +15 -10
- package/dist/bot/commands/{task.js → task-command.js} +9 -81
- package/dist/bot/commands/tasklist-command.js +40 -0
- package/dist/bot/commands/worktree-command.js +47 -0
- package/dist/bot/handlers/{document.js → document-handler.js} +3 -3
- package/dist/bot/handlers/{media-group.js → media-group-handler.js} +3 -3
- package/dist/bot/handlers/photo-handler.js +44 -0
- package/dist/bot/handlers/prompt.js +17 -17
- package/dist/bot/handlers/text-message-handler.js +61 -0
- package/dist/bot/handlers/tts-response-handler.js +29 -0
- package/dist/bot/handlers/{voice.js → voice-handler.js} +6 -3
- package/dist/bot/index.js +27 -1179
- package/dist/{keyboard/manager.js → bot/keyboards/keyboard-manager.js} +6 -6
- package/dist/bot/keyboards/keyboard-types.js +1 -0
- package/dist/bot/{utils/keyboard.js → keyboards/main-reply-keyboard.js} +2 -2
- package/dist/bot/menus/agent-selection-menu.js +54 -0
- package/dist/bot/menus/command-catalog-menu.js +101 -0
- package/dist/bot/menus/context-control-menu.js +33 -0
- package/dist/bot/menus/file-browser-menu.js +265 -0
- package/dist/bot/{handlers → menus}/inline-menu.js +7 -34
- package/dist/bot/menus/mcp-catalog-menu.js +97 -0
- package/dist/bot/menus/message-history-menu.js +107 -0
- package/dist/bot/menus/model-selection-menu.js +66 -0
- package/dist/bot/menus/permission-menu.js +116 -0
- package/dist/bot/{commands/projects.js → menus/project-selection-menu.js} +5 -105
- package/dist/bot/{handlers/question.js → menus/question-menu.js} +10 -166
- package/dist/bot/menus/rename-menu.js +6 -0
- package/dist/bot/menus/scheduled-task-menu.js +42 -0
- package/dist/bot/menus/session-selection-menu.js +115 -0
- package/dist/bot/menus/settings-menu.js +52 -0
- package/dist/bot/menus/skills-catalog-menu.js +99 -0
- package/dist/bot/menus/variant-selection-menu.js +67 -0
- package/dist/bot/menus/worktree-selection-menu.js +76 -0
- package/dist/bot/{utils → messages}/assistant-rendering.js +2 -2
- package/dist/bot/messages/busy-blocked-renderer.js +11 -0
- package/dist/bot/messages/external-user-input-notification.js +22 -0
- package/dist/bot/messages/scheduled-task-delivery.js +63 -0
- package/dist/bot/{utils → messages}/send-downloaded-file.js +1 -1
- package/dist/bot/messages/summary-message-formatter.js +130 -0
- package/dist/bot/{utils → messages}/thinking-message.js +1 -4
- package/dist/bot/messages/thinking-rendering.js +66 -0
- package/dist/{interaction/guard.js → bot/middleware/interaction-guard-decision.js} +11 -4
- package/dist/bot/middleware/interaction-guard.js +2 -2
- package/dist/bot/middleware/unknown-command.js +1 -1
- package/dist/{pinned/format.js → bot/pinned/pinned-message-format.js} +1 -2
- package/dist/{pinned/manager.js → bot/pinned/pinned-message-manager.js} +10 -10
- package/dist/bot/pinned/pinned-message-types.js +1 -0
- package/dist/{summary → bot/render}/markdown-to-telegram-v2.js +2 -2
- package/dist/bot/render/types.js +1 -0
- package/dist/bot/routers/command-router.js +73 -0
- package/dist/bot/routers/message-router.js +150 -0
- package/dist/bot/services/attach-presentation.js +40 -0
- package/dist/bot/services/event-subscription-service.js +1116 -0
- package/dist/bot/services/project-switch-presentation.js +26 -0
- package/dist/bot/streaming/compact-progress-streamer.js +162 -0
- package/dist/bot/{utils → streaming}/finalize-assistant-response.js +9 -3
- package/dist/bot/streaming/response-streamer.js +16 -1
- package/dist/cli.js +4 -4
- package/dist/config.js +9 -18
- 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/index.js +1 -1
- package/dist/opencode/ready-refresh.js +2 -2
- package/dist/{service → runtime/service}/manager.js +2 -2
- package/dist/runtime/service/types.js +1 -0
- package/package.json +1 -1
- package/dist/bot/commands/ls.js +0 -445
- package/dist/bot/commands/open.js +0 -319
- package/dist/bot/commands/tts.js +0 -13
- package/dist/bot/commands/worktree.js +0 -201
- package/dist/bot/handlers/permission.js +0 -235
- package/dist/bot/utils/browser-roots.js +0 -140
- package/dist/bot/utils/file-tree.js +0 -92
- package/dist/bot/utils/send-tts-response.js +0 -37
- package/dist/bot/utils/switch-project.js +0 -45
- package/dist/bot/utils/telegram-file-url.js +0 -11
- package/dist/interaction/busy.js +0 -8
- package/dist/interaction/cleanup.js +0 -32
- /package/dist/{scheduled-task/display.js → app/formatters/scheduled-task-display-formatter.js} +0 -0
- /package/dist/{bot/utils/abort-error-suppression.js → app/managers/abort-suppression-manager.js} +0 -0
- /package/dist/{external-input/suppression.js → app/managers/external-input-suppression-manager.js} +0 -0
- /package/dist/{scheduled-task/next-run.js → app/services/scheduled-task-next-run-service.js} +0 -0
- /package/dist/{git/worktree.js → app/services/worktree-service.js} +0 -0
- /package/dist/{agent/types.js → app/types/agent.js} +0 -0
- /package/dist/{interaction/types.js → app/types/interaction.js} +0 -0
- /package/dist/{model/types.js → app/types/model.js} +0 -0
- /package/dist/{keyboard/types.js → app/types/permission.js} +0 -0
- /package/dist/{permission/types.js → app/types/project.js} +0 -0
- /package/dist/{pinned/types.js → app/types/question.js} +0 -0
- /package/dist/{scheduled-task/types.js → app/types/scheduled-task.js} +0 -0
- /package/dist/{question/types.js → app/types/session.js} +0 -0
- /package/dist/{service/types.js → app/types/settings.js} +0 -0
- /package/dist/{telegram/render/types.js → app/types/summary.js} +0 -0
- /package/dist/{variant/types.js → app/types/variant.js} +0 -0
- /package/dist/bot/commands/{help.js → help-command.js} +0 -0
- /package/dist/bot/{utils → messages}/send-with-markdown-fallback.js +0 -0
- /package/dist/bot/{utils → messages}/telegram-text.js +0 -0
- /package/dist/{telegram → bot}/render/block-fallback.js +0 -0
- /package/dist/{telegram → bot}/render/block-parser.js +0 -0
- /package/dist/{telegram → bot}/render/block-renderer.js +0 -0
- /package/dist/{telegram → bot}/render/chunker.js +0 -0
- /package/dist/{telegram → bot}/render/inline-renderer.js +0 -0
- /package/dist/{telegram → bot}/render/markdown-normalizer.js +0 -0
- /package/dist/{telegram → bot}/render/pipeline.js +0 -0
- /package/dist/{telegram → bot}/render/validator.js +0 -0
- /package/dist/bot/{utils/commands.js → routers/command-utils.js} +0 -0
- /package/dist/{service/runtime.js → runtime/service/env.js} +0 -0
|
@@ -1,75 +1,24 @@
|
|
|
1
|
-
import { InlineKeyboard } from "grammy";
|
|
2
1
|
import { opencodeClient } from "../../opencode/client.js";
|
|
3
|
-
import { resolveProjectAgent } from "../../agent
|
|
4
|
-
import { setCurrentSession } from "../../session
|
|
5
|
-
import { getCurrentProject } from "../../settings
|
|
6
|
-
import { clearAllInteractionState } from "../../interaction
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
2
|
+
import { resolveProjectAgent } from "../../app/services/agent-selection-service.js";
|
|
3
|
+
import { setCurrentSession } from "../../app/services/session-service.js";
|
|
4
|
+
import { getCurrentProject } from "../../app/stores/settings-store.js";
|
|
5
|
+
import { clearAllInteractionState, interactionManager } from "../../app/managers/interaction-manager.js";
|
|
6
|
+
import { keyboardManager } from "../keyboards/keyboard-manager.js";
|
|
7
|
+
import { appendInlineMenuCancelButton, ensureActiveInlineMenu } from "../menus/inline-menu.js";
|
|
8
|
+
import { isForegroundBusy } from "../../app/services/run-control-service.js";
|
|
9
|
+
import { replyBusyBlocked } from "../messages/busy-blocked-renderer.js";
|
|
11
10
|
import { logger } from "../../utils/logger.js";
|
|
12
11
|
import { safeBackgroundTask } from "../../utils/safe-background-task.js";
|
|
13
12
|
import { config } from "../../config.js";
|
|
14
|
-
import {
|
|
15
|
-
import { attachToSession } from "../../attach
|
|
16
|
-
import { renderAssistantFinalPartsSafe } from "../
|
|
17
|
-
import { sendRenderedBotPart } from "../
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
assistant_response: "a",
|
|
24
|
-
question_asked: "q",
|
|
25
|
-
permission_asked: "p",
|
|
26
|
-
};
|
|
27
|
-
const BACKGROUND_SESSION_KIND_BY_CALLBACK_MARKER = {
|
|
28
|
-
a: "assistant_response",
|
|
29
|
-
q: "question_asked",
|
|
30
|
-
p: "permission_asked",
|
|
31
|
-
};
|
|
32
|
-
function buildSessionPageCallback(page) {
|
|
33
|
-
return `${SESSION_PAGE_CALLBACK_PREFIX}${page}`;
|
|
34
|
-
}
|
|
35
|
-
function parseSessionPageCallback(data) {
|
|
36
|
-
if (!data.startsWith(SESSION_PAGE_CALLBACK_PREFIX)) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
const rawPage = data.slice(SESSION_PAGE_CALLBACK_PREFIX.length);
|
|
40
|
-
const page = Number(rawPage);
|
|
41
|
-
if (!Number.isInteger(page) || page < 0) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
return page;
|
|
45
|
-
}
|
|
46
|
-
function parseSessionIdCallback(data) {
|
|
47
|
-
if (!data.startsWith(SESSION_CALLBACK_PREFIX)) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
if (data.startsWith(SESSION_PAGE_CALLBACK_PREFIX)) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
const sessionId = data.slice(SESSION_CALLBACK_PREFIX.length);
|
|
54
|
-
return sessionId.length > 0 ? sessionId : null;
|
|
55
|
-
}
|
|
56
|
-
function parseBackgroundSessionCallback(data) {
|
|
57
|
-
if (!data.startsWith(BACKGROUND_SESSION_CALLBACK_PREFIX)) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const payload = data.slice(BACKGROUND_SESSION_CALLBACK_PREFIX.length);
|
|
61
|
-
const markerSeparatorIndex = payload.indexOf(":");
|
|
62
|
-
if (markerSeparatorIndex < 0) {
|
|
63
|
-
return payload.length > 0 ? { sessionId: payload, kind: null } : null;
|
|
64
|
-
}
|
|
65
|
-
const marker = payload.slice(0, markerSeparatorIndex);
|
|
66
|
-
const sessionId = payload.slice(markerSeparatorIndex + 1);
|
|
67
|
-
const kind = BACKGROUND_SESSION_KIND_BY_CALLBACK_MARKER[marker];
|
|
68
|
-
if (!kind || sessionId.length === 0) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
return { sessionId, kind };
|
|
72
|
-
}
|
|
13
|
+
import { t } from "../../i18n/index.js";
|
|
14
|
+
import { attachToSession } from "../../app/services/attach-service.js";
|
|
15
|
+
import { renderAssistantFinalPartsSafe } from "../messages/assistant-rendering.js";
|
|
16
|
+
import { sendRenderedBotPart } from "../messages/telegram-text.js";
|
|
17
|
+
import { buildSessionSelectionMenuView, parseBackgroundSessionCallback, parseSessionIdCallback, parseSessionPageCallback, SESSION_CALLBACK_PREFIX, loadSessionPage, } from "../menus/session-selection-menu.js";
|
|
18
|
+
const PREVIEW_MESSAGES_LIMIT = 6;
|
|
19
|
+
const LATEST_ASSISTANT_RESPONSE_MESSAGES_LIMIT = 20;
|
|
20
|
+
const PREVIEW_ITEM_MAX_LENGTH = 420;
|
|
21
|
+
const TELEGRAM_MESSAGE_LIMIT = 4096;
|
|
73
22
|
async function removeCallbackReplyMarkup(ctx) {
|
|
74
23
|
try {
|
|
75
24
|
await ctx.editMessageReplyMarkup();
|
|
@@ -78,90 +27,6 @@ async function removeCallbackReplyMarkup(ctx) {
|
|
|
78
27
|
logger.debug("[Sessions] Failed to remove background session button:", err);
|
|
79
28
|
}
|
|
80
29
|
}
|
|
81
|
-
export function buildBackgroundSessionOpenKeyboard(sessionId, kind) {
|
|
82
|
-
const marker = BACKGROUND_SESSION_KIND_CALLBACK_MARKERS[kind];
|
|
83
|
-
return new InlineKeyboard().text(t("background.open_session_button"), `${BACKGROUND_SESSION_CALLBACK_PREFIX}${marker}:${sessionId}`);
|
|
84
|
-
}
|
|
85
|
-
function formatSessionsSelectText(page) {
|
|
86
|
-
if (page === 0) {
|
|
87
|
-
return t("sessions.select");
|
|
88
|
-
}
|
|
89
|
-
return t("sessions.select_page", { page: page + 1 });
|
|
90
|
-
}
|
|
91
|
-
async function loadSessionPage(directory, page, pageSize) {
|
|
92
|
-
const startIndex = page * pageSize;
|
|
93
|
-
const endExclusive = startIndex + pageSize;
|
|
94
|
-
const { data: sessions, error } = await opencodeClient.session.list({
|
|
95
|
-
directory,
|
|
96
|
-
limit: endExclusive + SESSION_FETCH_EXTRA_COUNT,
|
|
97
|
-
roots: true,
|
|
98
|
-
});
|
|
99
|
-
if (error || !sessions) {
|
|
100
|
-
throw error || new Error("No data received from server");
|
|
101
|
-
}
|
|
102
|
-
const hasNext = sessions.length > endExclusive;
|
|
103
|
-
const pagedSessions = sessions.slice(startIndex, endExclusive);
|
|
104
|
-
logger.debug(`[Sessions] Loaded page=${page + 1}, startIndex=${startIndex}, endExclusive=${endExclusive}, pageSize=${pageSize}, items=${pagedSessions.length}, hasNext=${hasNext}`);
|
|
105
|
-
return {
|
|
106
|
-
sessions: pagedSessions,
|
|
107
|
-
hasNext,
|
|
108
|
-
page,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function buildSessionsKeyboard(pageData, pageSize) {
|
|
112
|
-
const keyboard = new InlineKeyboard();
|
|
113
|
-
const localeForDate = getDateLocale();
|
|
114
|
-
const pageStartIndex = pageData.page * pageSize;
|
|
115
|
-
pageData.sessions.forEach((session, index) => {
|
|
116
|
-
const date = new Date(session.time.created).toLocaleDateString(localeForDate);
|
|
117
|
-
const label = `${pageStartIndex + index + 1}. ${session.title} (${date})`;
|
|
118
|
-
keyboard.text(label, `${SESSION_CALLBACK_PREFIX}${session.id}`).row();
|
|
119
|
-
});
|
|
120
|
-
if (pageData.page > 0) {
|
|
121
|
-
keyboard.text(t("sessions.button.prev_page"), buildSessionPageCallback(pageData.page - 1));
|
|
122
|
-
}
|
|
123
|
-
if (pageData.hasNext) {
|
|
124
|
-
keyboard.text(t("sessions.button.next_page"), buildSessionPageCallback(pageData.page + 1));
|
|
125
|
-
}
|
|
126
|
-
if (pageData.page > 0 || pageData.hasNext) {
|
|
127
|
-
keyboard.row();
|
|
128
|
-
}
|
|
129
|
-
return keyboard;
|
|
130
|
-
}
|
|
131
|
-
export async function sessionsCommand(ctx) {
|
|
132
|
-
try {
|
|
133
|
-
if (isForegroundBusy()) {
|
|
134
|
-
await replyBusyBlocked(ctx);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const pageSize = config.bot.sessionsListLimit;
|
|
138
|
-
const currentProject = getCurrentProject();
|
|
139
|
-
if (!currentProject) {
|
|
140
|
-
await ctx.reply(t("sessions.project_not_selected"));
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
logger.debug(`[Sessions] Fetching sessions for directory: ${currentProject.worktree}`);
|
|
144
|
-
const firstPage = await loadSessionPage(currentProject.worktree, 0, pageSize);
|
|
145
|
-
logger.debug(`[Sessions] Found ${firstPage.sessions.length} sessions on page 1`);
|
|
146
|
-
firstPage.sessions.forEach((session) => {
|
|
147
|
-
logger.debug(`[Sessions] Session: ${session.title} | ${session.directory}`);
|
|
148
|
-
});
|
|
149
|
-
if (firstPage.sessions.length === 0) {
|
|
150
|
-
await ctx.reply(t("sessions.empty"));
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const keyboard = buildSessionsKeyboard(firstPage, pageSize);
|
|
154
|
-
await replyWithInlineMenu(ctx, {
|
|
155
|
-
menuKind: "session",
|
|
156
|
-
text: formatSessionsSelectText(firstPage.page),
|
|
157
|
-
keyboard,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
logger.error("[Sessions] Error fetching sessions:", error);
|
|
162
|
-
await ctx.reply(t("sessions.fetch_error"));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
30
|
async function selectSessionById(ctx, deps, sessionId, options) {
|
|
166
31
|
const currentProject = getCurrentProject();
|
|
167
32
|
if (!currentProject) {
|
|
@@ -327,9 +192,9 @@ export async function handleSessionSelect(ctx, deps) {
|
|
|
327
192
|
await ctx.answerCallbackQuery({ text: t("sessions.page_empty_callback") });
|
|
328
193
|
return true;
|
|
329
194
|
}
|
|
330
|
-
const keyboard =
|
|
195
|
+
const { text, keyboard } = buildSessionSelectionMenuView(pageData, pageSize);
|
|
331
196
|
appendInlineMenuCancelButton(keyboard, "session");
|
|
332
|
-
await ctx.editMessageText(
|
|
197
|
+
await ctx.editMessageText(text, {
|
|
333
198
|
reply_markup: keyboard,
|
|
334
199
|
});
|
|
335
200
|
await ctx.answerCallbackQuery();
|
|
@@ -359,10 +224,6 @@ export async function handleSessionSelect(ctx, deps) {
|
|
|
359
224
|
}
|
|
360
225
|
return true;
|
|
361
226
|
}
|
|
362
|
-
const PREVIEW_MESSAGES_LIMIT = 6;
|
|
363
|
-
const LATEST_ASSISTANT_RESPONSE_MESSAGES_LIMIT = 20;
|
|
364
|
-
const PREVIEW_ITEM_MAX_LENGTH = 420;
|
|
365
|
-
const TELEGRAM_MESSAGE_LIMIT = 4096;
|
|
366
227
|
function extractTextParts(parts, options = {}) {
|
|
367
228
|
const textParts = parts
|
|
368
229
|
.filter((part) => part.type === "text" && typeof part.text === "string")
|
|
@@ -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
|
+
}
|
|
@@ -1,55 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { interactionManager } from "../../interaction/manager.js";
|
|
1
|
+
import { config } from "../../config.js";
|
|
2
|
+
import { getCurrentProject } from "../../app/stores/settings-store.js";
|
|
3
|
+
import { interactionManager } from "../../app/managers/interaction-manager.js";
|
|
5
4
|
import { logger } from "../../utils/logger.js";
|
|
6
5
|
import { t } from "../../i18n/index.js";
|
|
7
|
-
import { config } from "../../config.js";
|
|
8
6
|
import { processUserPrompt } from "../handlers/prompt.js";
|
|
9
|
-
|
|
10
|
-
const SKILLS_CALLBACK_SELECT_PREFIX = `${SKILLS_CALLBACK_PREFIX}select:`;
|
|
11
|
-
const SKILLS_CALLBACK_PAGE_PREFIX = `${SKILLS_CALLBACK_PREFIX}page:`;
|
|
12
|
-
const SKILLS_CALLBACK_CANCEL = `${SKILLS_CALLBACK_PREFIX}cancel`;
|
|
13
|
-
const SKILLS_CALLBACK_EXECUTE = `${SKILLS_CALLBACK_PREFIX}execute`;
|
|
14
|
-
const MAX_INLINE_BUTTON_LABEL_LENGTH = 64;
|
|
15
|
-
function formatExecutingSkillMessage(skillName, args) {
|
|
16
|
-
const prefix = t("skills.executing_prefix");
|
|
17
|
-
const skillText = `/${skillName}`;
|
|
18
|
-
const argsSuffix = args ? ` ${args}` : "";
|
|
19
|
-
return {
|
|
20
|
-
text: `${prefix}\n${skillText}${argsSuffix}`,
|
|
21
|
-
entities: [
|
|
22
|
-
{
|
|
23
|
-
type: "code",
|
|
24
|
-
offset: prefix.length + 1,
|
|
25
|
-
length: skillText.length,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
export function buildSkillPageCallback(page) {
|
|
31
|
-
return `${SKILLS_CALLBACK_PAGE_PREFIX}${page}`;
|
|
32
|
-
}
|
|
33
|
-
export function parseSkillPageCallback(data) {
|
|
34
|
-
if (!data.startsWith(SKILLS_CALLBACK_PAGE_PREFIX)) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const rawPage = data.slice(SKILLS_CALLBACK_PAGE_PREFIX.length);
|
|
38
|
-
const page = Number(rawPage);
|
|
39
|
-
if (!Number.isInteger(page) || page < 0) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return page;
|
|
43
|
-
}
|
|
44
|
-
export function formatSkillsSelectText(page) {
|
|
45
|
-
if (page === 0) {
|
|
46
|
-
return t("skills.select");
|
|
47
|
-
}
|
|
48
|
-
return t("skills.select_page", { page: page + 1 });
|
|
49
|
-
}
|
|
50
|
-
function normalizeDirectoryForCommandApi(directory) {
|
|
51
|
-
return directory.replace(/\\/g, "/");
|
|
52
|
-
}
|
|
7
|
+
import { buildSkillsConfirmKeyboard, buildSkillsListKeyboard, calculateSkillsPaginationRange, formatExecutingSkillMessage, formatSkillsSelectText, parseSkillPageCallback, parseSkillSelectCallback, SKILLS_CALLBACK_CANCEL, SKILLS_CALLBACK_EXECUTE, SKILLS_CALLBACK_PREFIX, } from "../menus/skills-catalog-menu.js";
|
|
53
8
|
function getCallbackMessageId(ctx) {
|
|
54
9
|
const message = ctx.callbackQuery?.message;
|
|
55
10
|
if (!message || !("message_id" in message)) {
|
|
@@ -58,53 +13,6 @@ function getCallbackMessageId(ctx) {
|
|
|
58
13
|
const messageId = message.message_id;
|
|
59
14
|
return typeof messageId === "number" ? messageId : null;
|
|
60
15
|
}
|
|
61
|
-
function formatSkillButtonLabel(skill) {
|
|
62
|
-
const description = skill.description?.trim() || t("skills.no_description");
|
|
63
|
-
const rawLabel = `/${skill.name} - ${description}`;
|
|
64
|
-
if (rawLabel.length <= MAX_INLINE_BUTTON_LABEL_LENGTH) {
|
|
65
|
-
return rawLabel;
|
|
66
|
-
}
|
|
67
|
-
return `${rawLabel.slice(0, MAX_INLINE_BUTTON_LABEL_LENGTH - 3)}...`;
|
|
68
|
-
}
|
|
69
|
-
export function calculateSkillsPaginationRange(totalSkills, page, pageSize) {
|
|
70
|
-
const safePageSize = Math.max(1, pageSize);
|
|
71
|
-
const totalPages = Math.max(1, Math.ceil(totalSkills / safePageSize));
|
|
72
|
-
const normalizedPage = Math.min(Math.max(0, page), totalPages - 1);
|
|
73
|
-
const startIndex = normalizedPage * safePageSize;
|
|
74
|
-
const endIndex = Math.min(startIndex + safePageSize, totalSkills);
|
|
75
|
-
return {
|
|
76
|
-
page: normalizedPage,
|
|
77
|
-
totalPages,
|
|
78
|
-
startIndex,
|
|
79
|
-
endIndex,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function buildSkillsListKeyboard(skills, page, pageSize) {
|
|
83
|
-
const keyboard = new InlineKeyboard();
|
|
84
|
-
const { page: normalizedPage, totalPages, startIndex, endIndex, } = calculateSkillsPaginationRange(skills.length, page, pageSize);
|
|
85
|
-
skills.slice(startIndex, endIndex).forEach((skill, index) => {
|
|
86
|
-
const globalIndex = startIndex + index;
|
|
87
|
-
keyboard
|
|
88
|
-
.text(formatSkillButtonLabel(skill), `${SKILLS_CALLBACK_SELECT_PREFIX}${globalIndex}`)
|
|
89
|
-
.row();
|
|
90
|
-
});
|
|
91
|
-
if (totalPages > 1) {
|
|
92
|
-
if (normalizedPage > 0) {
|
|
93
|
-
keyboard.text(t("skills.button.prev_page"), buildSkillPageCallback(normalizedPage - 1));
|
|
94
|
-
}
|
|
95
|
-
if (normalizedPage < totalPages - 1) {
|
|
96
|
-
keyboard.text(t("skills.button.next_page"), buildSkillPageCallback(normalizedPage + 1));
|
|
97
|
-
}
|
|
98
|
-
keyboard.row();
|
|
99
|
-
}
|
|
100
|
-
keyboard.text(t("skills.button.cancel"), SKILLS_CALLBACK_CANCEL);
|
|
101
|
-
return keyboard;
|
|
102
|
-
}
|
|
103
|
-
function buildSkillsConfirmKeyboard() {
|
|
104
|
-
return new InlineKeyboard()
|
|
105
|
-
.text(t("skills.button.execute"), SKILLS_CALLBACK_EXECUTE)
|
|
106
|
-
.text(t("skills.button.cancel"), SKILLS_CALLBACK_CANCEL);
|
|
107
|
-
}
|
|
108
16
|
function parseSkillItems(value) {
|
|
109
17
|
if (!Array.isArray(value)) {
|
|
110
18
|
return null;
|
|
@@ -126,7 +34,7 @@ function parseSkillItems(value) {
|
|
|
126
34
|
}
|
|
127
35
|
return skills;
|
|
128
36
|
}
|
|
129
|
-
function parseSkillsMetadata(state) {
|
|
37
|
+
export function parseSkillsMetadata(state) {
|
|
130
38
|
if (!state || state.kind !== "custom") {
|
|
131
39
|
return null;
|
|
132
40
|
}
|
|
@@ -169,41 +77,13 @@ function parseSkillsMetadata(state) {
|
|
|
169
77
|
}
|
|
170
78
|
return null;
|
|
171
79
|
}
|
|
172
|
-
function clearSkillsInteraction(reason) {
|
|
80
|
+
export function clearSkillsInteraction(reason) {
|
|
173
81
|
const metadata = parseSkillsMetadata(interactionManager.getSnapshot());
|
|
174
82
|
if (metadata) {
|
|
175
83
|
interactionManager.clear(reason);
|
|
176
84
|
}
|
|
177
85
|
}
|
|
178
|
-
async function
|
|
179
|
-
const { data, error } = await opencodeClient.command.list({
|
|
180
|
-
directory: normalizeDirectoryForCommandApi(projectDirectory),
|
|
181
|
-
});
|
|
182
|
-
if (error || !data) {
|
|
183
|
-
throw error || new Error("No skill data received");
|
|
184
|
-
}
|
|
185
|
-
return data
|
|
186
|
-
.filter((skill) => {
|
|
187
|
-
const source = skill.source;
|
|
188
|
-
return typeof skill.name === "string" && skill.name.trim().length > 0 && source === "skill";
|
|
189
|
-
})
|
|
190
|
-
.map((skill) => ({
|
|
191
|
-
name: skill.name,
|
|
192
|
-
description: skill.description,
|
|
193
|
-
}));
|
|
194
|
-
}
|
|
195
|
-
function parseSelectIndex(data) {
|
|
196
|
-
if (!data.startsWith(SKILLS_CALLBACK_SELECT_PREFIX)) {
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
const rawIndex = data.slice(SKILLS_CALLBACK_SELECT_PREFIX.length);
|
|
200
|
-
const index = Number(rawIndex);
|
|
201
|
-
if (!Number.isInteger(index) || index < 0) {
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
return index;
|
|
205
|
-
}
|
|
206
|
-
async function executeSkill(ctx, deps, params) {
|
|
86
|
+
export async function executeSkill(ctx, deps, params) {
|
|
207
87
|
const currentProject = getCurrentProject();
|
|
208
88
|
if (!currentProject) {
|
|
209
89
|
await ctx.reply(t("bot.project_not_selected"));
|
|
@@ -218,41 +98,6 @@ async function executeSkill(ctx, deps, params) {
|
|
|
218
98
|
const promptText = args ? `/${params.skillName} ${args}` : `/${params.skillName}`;
|
|
219
99
|
await processUserPrompt(ctx, promptText, deps);
|
|
220
100
|
}
|
|
221
|
-
export async function skillsCommand(ctx) {
|
|
222
|
-
try {
|
|
223
|
-
const currentProject = getCurrentProject();
|
|
224
|
-
if (!currentProject) {
|
|
225
|
-
await ctx.reply(t("bot.project_not_selected"));
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const skills = await getSkillList(currentProject.worktree);
|
|
229
|
-
if (skills.length === 0) {
|
|
230
|
-
await ctx.reply(t("skills.empty"));
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
const pageSize = config.bot.commandsListLimit;
|
|
234
|
-
const keyboard = buildSkillsListKeyboard(skills, 0, pageSize);
|
|
235
|
-
const message = await ctx.reply(formatSkillsSelectText(0), {
|
|
236
|
-
reply_markup: keyboard,
|
|
237
|
-
});
|
|
238
|
-
interactionManager.start({
|
|
239
|
-
kind: "custom",
|
|
240
|
-
expectedInput: "callback",
|
|
241
|
-
metadata: {
|
|
242
|
-
flow: "skills",
|
|
243
|
-
stage: "list",
|
|
244
|
-
messageId: message.message_id,
|
|
245
|
-
projectDirectory: currentProject.worktree,
|
|
246
|
-
skills,
|
|
247
|
-
page: 0,
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
logger.error("[Skills] Error fetching skills list:", error);
|
|
253
|
-
await ctx.reply(t("skills.fetch_error"));
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
101
|
export async function handleSkillsCallback(ctx, deps) {
|
|
257
102
|
const data = ctx.callbackQuery?.data;
|
|
258
103
|
if (!data || !data.startsWith(SKILLS_CALLBACK_PREFIX)) {
|
|
@@ -316,7 +161,7 @@ export async function handleSkillsCallback(ctx, deps) {
|
|
|
316
161
|
});
|
|
317
162
|
return true;
|
|
318
163
|
}
|
|
319
|
-
const skillIndex =
|
|
164
|
+
const skillIndex = parseSkillSelectCallback(data);
|
|
320
165
|
if (skillIndex === null || metadata.stage !== "list") {
|
|
321
166
|
await ctx.answerCallbackQuery({ text: t("callback.processing_error"), show_alert: true });
|
|
322
167
|
return true;
|
|
@@ -349,28 +194,3 @@ export async function handleSkillsCallback(ctx, deps) {
|
|
|
349
194
|
return true;
|
|
350
195
|
}
|
|
351
196
|
}
|
|
352
|
-
export async function handleSkillTextArguments(ctx, deps) {
|
|
353
|
-
const text = ctx.message?.text;
|
|
354
|
-
if (!text || text.startsWith("/")) {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
const metadata = parseSkillsMetadata(interactionManager.getSnapshot());
|
|
358
|
-
if (!metadata || metadata.stage !== "confirm") {
|
|
359
|
-
return false;
|
|
360
|
-
}
|
|
361
|
-
const argumentsText = text.trim();
|
|
362
|
-
if (!argumentsText) {
|
|
363
|
-
await ctx.reply(t("skills.arguments_empty"));
|
|
364
|
-
return true;
|
|
365
|
-
}
|
|
366
|
-
clearSkillsInteraction("skills_arguments_submitted");
|
|
367
|
-
if (ctx.chat) {
|
|
368
|
-
await ctx.api.deleteMessage(ctx.chat.id, metadata.messageId).catch(() => { });
|
|
369
|
-
}
|
|
370
|
-
await executeSkill(ctx, deps, {
|
|
371
|
-
projectDirectory: metadata.projectDirectory,
|
|
372
|
-
skillName: metadata.skillName,
|
|
373
|
-
argumentsText,
|
|
374
|
-
});
|
|
375
|
-
return true;
|
|
376
|
-
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
|
|
1
|
+
import { getStoredAgent, resolveProjectAgent } from "../../app/services/agent-selection-service.js";
|
|
2
|
+
import { getStoredModel } from "../../app/services/model-selection-service.js";
|
|
3
|
+
import { formatVariantForButton, formatVariantForDisplay, setCurrentVariant, } from "../../app/services/variant-selection-service.js";
|
|
5
4
|
import { logger } from "../../utils/logger.js";
|
|
6
|
-
import { keyboardManager } from "../../keyboard/manager.js";
|
|
7
|
-
import { pinnedMessageManager } from "../../pinned/manager.js";
|
|
8
|
-
import { createMainKeyboard } from "../utils/keyboard.js";
|
|
9
|
-
import { clearActiveInlineMenu, ensureActiveInlineMenu, replyWithInlineMenu, } from "./inline-menu.js";
|
|
10
5
|
import { t } from "../../i18n/index.js";
|
|
6
|
+
import { createMainKeyboard } from "../keyboards/main-reply-keyboard.js";
|
|
7
|
+
import { keyboardManager } from "../keyboards/keyboard-manager.js";
|
|
8
|
+
import { pinnedMessageManager } from "../pinned/pinned-message-manager.js";
|
|
9
|
+
import { clearActiveInlineMenu, ensureActiveInlineMenu } from "../menus/inline-menu.js";
|
|
11
10
|
/**
|
|
12
11
|
* Handle variant selection callback
|
|
13
12
|
* @param ctx grammY context
|
|
@@ -76,64 +75,3 @@ export async function handleVariantSelect(ctx) {
|
|
|
76
75
|
return false;
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Build inline keyboard with available variants
|
|
81
|
-
* @param currentVariant Current variant for highlighting
|
|
82
|
-
* @param providerID Provider ID
|
|
83
|
-
* @param modelID Model ID
|
|
84
|
-
* @returns InlineKeyboard with variant selection buttons
|
|
85
|
-
*/
|
|
86
|
-
export async function buildVariantSelectionMenu(currentVariant, providerID, modelID) {
|
|
87
|
-
const keyboard = new InlineKeyboard();
|
|
88
|
-
const variants = await getAvailableVariants(providerID, modelID);
|
|
89
|
-
if (variants.length === 0) {
|
|
90
|
-
logger.warn("[VariantHandler] No variants found");
|
|
91
|
-
return keyboard;
|
|
92
|
-
}
|
|
93
|
-
// Filter only active variants (not disabled)
|
|
94
|
-
const activeVariants = variants.filter((v) => !v.disabled);
|
|
95
|
-
if (activeVariants.length === 0) {
|
|
96
|
-
logger.warn("[VariantHandler] No active variants found");
|
|
97
|
-
// If no active variants, show default at least
|
|
98
|
-
keyboard.text(`✅ ${formatVariantForDisplay("default")}`, "variant:default").row();
|
|
99
|
-
return keyboard;
|
|
100
|
-
}
|
|
101
|
-
// Add button for each variant (one per row)
|
|
102
|
-
activeVariants.forEach((variant) => {
|
|
103
|
-
const isActive = variant.id === currentVariant;
|
|
104
|
-
const label = formatVariantForDisplay(variant.id);
|
|
105
|
-
const labelWithCheck = isActive ? `✅ ${label}` : label;
|
|
106
|
-
keyboard.text(labelWithCheck, `variant:${variant.id}`).row();
|
|
107
|
-
});
|
|
108
|
-
return keyboard;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Show variant selection menu
|
|
112
|
-
* @param ctx grammY context
|
|
113
|
-
*/
|
|
114
|
-
export async function showVariantSelectionMenu(ctx) {
|
|
115
|
-
try {
|
|
116
|
-
const currentModel = getStoredModel();
|
|
117
|
-
if (!currentModel.providerID || !currentModel.modelID) {
|
|
118
|
-
await ctx.reply(t("variant.select_model_first"));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const currentVariant = getCurrentVariant();
|
|
122
|
-
const keyboard = await buildVariantSelectionMenu(currentVariant, currentModel.providerID, currentModel.modelID);
|
|
123
|
-
if (keyboard.inline_keyboard.length === 0) {
|
|
124
|
-
await ctx.reply(t("variant.menu.empty"));
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const displayName = formatVariantForDisplay(currentVariant);
|
|
128
|
-
const text = t("variant.menu.current", { name: displayName });
|
|
129
|
-
await replyWithInlineMenu(ctx, {
|
|
130
|
-
menuKind: "variant",
|
|
131
|
-
text,
|
|
132
|
-
keyboard,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
catch (err) {
|
|
136
|
-
logger.error("[VariantHandler] Error showing variant menu:", err);
|
|
137
|
-
await ctx.reply(t("variant.menu.error"));
|
|
138
|
-
}
|
|
139
|
-
}
|