@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,6 +1,6 @@
|
|
|
1
|
-
import { normalizePathForDisplay, prepareCodeFile } from "
|
|
2
|
-
import { logger } from "
|
|
3
|
-
import { getCurrentProject } from "../settings
|
|
1
|
+
import { normalizePathForDisplay, prepareCodeFile } from "../formatters/summary-formatter.js";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
import { getCurrentProject } from "../stores/settings-store.js";
|
|
4
4
|
function extractFirstUpdatedFileFromTitle(title) {
|
|
5
5
|
for (const rawLine of title.split("\n")) {
|
|
6
6
|
const line = rawLine.trim();
|
|
@@ -38,6 +38,7 @@ function normalizeSnapshotValue(value) {
|
|
|
38
38
|
class SummaryAggregator {
|
|
39
39
|
currentSessionId = null;
|
|
40
40
|
textMessageStates = new Map();
|
|
41
|
+
thinkingMessageStates = new Map();
|
|
41
42
|
messages = new Map();
|
|
42
43
|
messageCount = 0;
|
|
43
44
|
lastUpdated = 0;
|
|
@@ -45,10 +46,12 @@ class SummaryAggregator {
|
|
|
45
46
|
onPartialCallback = null;
|
|
46
47
|
onExternalUserInputCallback = null;
|
|
47
48
|
onToolCallback = null;
|
|
49
|
+
onRootToolUpdateCallback = null;
|
|
48
50
|
onToolFileCallback = null;
|
|
49
51
|
onQuestionCallback = null;
|
|
50
52
|
onQuestionErrorCallback = null;
|
|
51
53
|
onThinkingCallback = null;
|
|
54
|
+
onThinkingFinishedCallback = null;
|
|
52
55
|
onTokensCallback = null;
|
|
53
56
|
onCostCallback = null;
|
|
54
57
|
onSubagentCallback = null;
|
|
@@ -62,6 +65,7 @@ class SummaryAggregator {
|
|
|
62
65
|
onClearedCallback = null;
|
|
63
66
|
processedToolStates = new Set();
|
|
64
67
|
thinkingFiredForMessages = new Set();
|
|
68
|
+
thinkingFinishedForMessages = new Set();
|
|
65
69
|
deliveredExternalUserMessageIds = new Set();
|
|
66
70
|
knownTextPartIds = new Map();
|
|
67
71
|
bot = null;
|
|
@@ -93,6 +97,9 @@ class SummaryAggregator {
|
|
|
93
97
|
setOnTool(callback) {
|
|
94
98
|
this.onToolCallback = callback;
|
|
95
99
|
}
|
|
100
|
+
setOnRootToolUpdate(callback) {
|
|
101
|
+
this.onRootToolUpdateCallback = callback;
|
|
102
|
+
}
|
|
96
103
|
setOnToolFile(callback) {
|
|
97
104
|
this.onToolFileCallback = callback;
|
|
98
105
|
}
|
|
@@ -105,6 +112,9 @@ class SummaryAggregator {
|
|
|
105
112
|
setOnThinking(callback) {
|
|
106
113
|
this.onThinkingCallback = callback;
|
|
107
114
|
}
|
|
115
|
+
setOnThinkingFinished(callback) {
|
|
116
|
+
this.onThinkingFinishedCallback = callback;
|
|
117
|
+
}
|
|
108
118
|
setOnTokens(callback) {
|
|
109
119
|
this.onTokensCallback = callback;
|
|
110
120
|
}
|
|
@@ -242,11 +252,13 @@ class SummaryAggregator {
|
|
|
242
252
|
this.stopTypingIndicator();
|
|
243
253
|
this.currentSessionId = null;
|
|
244
254
|
this.textMessageStates.clear();
|
|
255
|
+
this.thinkingMessageStates.clear();
|
|
245
256
|
this.messages.clear();
|
|
246
257
|
this.partHashes.clear();
|
|
247
258
|
this.knownTextPartIds.clear();
|
|
248
259
|
this.processedToolStates.clear();
|
|
249
260
|
this.thinkingFiredForMessages.clear();
|
|
261
|
+
this.thinkingFinishedForMessages.clear();
|
|
250
262
|
this.deliveredExternalUserMessageIds.clear();
|
|
251
263
|
this.trackedSessionParents.clear();
|
|
252
264
|
this.subagentStates.clear();
|
|
@@ -755,26 +767,38 @@ class SummaryAggregator {
|
|
|
755
767
|
this.registerKnownTextPart(messageID, part.id);
|
|
756
768
|
this.registerTextPart(messageID, part.id);
|
|
757
769
|
}
|
|
770
|
+
if (part.type === "reasoning") {
|
|
771
|
+
this.registerThinkingPart(messageID, part.id, this.extractReasoningTitle(part));
|
|
772
|
+
}
|
|
758
773
|
const deltaFromUpdated = event.properties.delta;
|
|
759
774
|
if (part.type === "text" &&
|
|
760
775
|
typeof deltaFromUpdated === "string" &&
|
|
761
776
|
deltaFromUpdated.length > 0) {
|
|
777
|
+
this.emitThinkingFinishedOnce(part.sessionID, messageID);
|
|
762
778
|
this.applyTextDelta(part.sessionID, messageID, part.id, deltaFromUpdated, part.text);
|
|
763
779
|
this.lastUpdated = Date.now();
|
|
764
780
|
return;
|
|
765
781
|
}
|
|
782
|
+
if (part.type === "reasoning" &&
|
|
783
|
+
typeof deltaFromUpdated === "string" &&
|
|
784
|
+
deltaFromUpdated.length > 0) {
|
|
785
|
+
const partText = "text" in part && typeof part.text === "string" ? part.text : undefined;
|
|
786
|
+
this.applyThinkingDelta(part.sessionID, messageID, part.id, deltaFromUpdated, partText, this.extractReasoningTitle(part));
|
|
787
|
+
this.lastUpdated = Date.now();
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
766
790
|
if (part.type === "reasoning") {
|
|
767
|
-
// Fire the thinking callback
|
|
768
|
-
//
|
|
769
|
-
|
|
791
|
+
// Fire the thinking callback on every reasoning update. The first update
|
|
792
|
+
// preserves the old lightweight indicator behavior for callers that do
|
|
793
|
+
// not display full reasoning content.
|
|
794
|
+
const isFirstUpdate = !this.thinkingFiredForMessages.has(messageID);
|
|
795
|
+
if (isFirstUpdate) {
|
|
770
796
|
this.thinkingFiredForMessages.add(messageID);
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
});
|
|
797
|
+
}
|
|
798
|
+
const partText = "text" in part && typeof part.text === "string" ? part.text : "";
|
|
799
|
+
const wasUpdated = this.setThinkingPartSnapshot(messageID, part.id, partText, this.extractReasoningTitle(part));
|
|
800
|
+
if (isFirstUpdate || wasUpdated) {
|
|
801
|
+
this.emitThinkingUpdate(part.sessionID, messageID, isFirstUpdate);
|
|
778
802
|
}
|
|
779
803
|
}
|
|
780
804
|
else if (part.type === "text" && "text" in part && part.text) {
|
|
@@ -784,6 +808,7 @@ class SummaryAggregator {
|
|
|
784
808
|
if (!wasUpdated) {
|
|
785
809
|
return;
|
|
786
810
|
}
|
|
811
|
+
this.emitThinkingFinishedOnce(part.sessionID, messageID);
|
|
787
812
|
const fullText = this.getCombinedMessageText(messageID);
|
|
788
813
|
if (messageInfo && messageInfo.role === "assistant") {
|
|
789
814
|
this.startTypingIndicator();
|
|
@@ -808,6 +833,19 @@ class SummaryAggregator {
|
|
|
808
833
|
this.updateSubagentFromTaskTool(part.sessionID, input);
|
|
809
834
|
}
|
|
810
835
|
logger.debug(`[Aggregator] Tool event: callID=${part.callID}, tool=${part.tool}, status=${"status" in state ? state.status : "unknown"}`);
|
|
836
|
+
if (this.onRootToolUpdateCallback) {
|
|
837
|
+
this.onRootToolUpdateCallback({
|
|
838
|
+
sessionId: part.sessionID,
|
|
839
|
+
messageId: messageID,
|
|
840
|
+
callId: part.callID,
|
|
841
|
+
tool: part.tool,
|
|
842
|
+
state: part.state,
|
|
843
|
+
input,
|
|
844
|
+
title,
|
|
845
|
+
metadata: "metadata" in state ? state.metadata : undefined,
|
|
846
|
+
hasFileAttachment: false,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
811
849
|
if (part.tool === "question") {
|
|
812
850
|
logger.debug(`[Aggregator] Question tool part update:`, JSON.stringify(part, null, 2));
|
|
813
851
|
// If the question tool fails, clear the active poll
|
|
@@ -871,6 +909,13 @@ class SummaryAggregator {
|
|
|
871
909
|
if (!sessionID || !messageID || typeof delta !== "string" || delta.length === 0) {
|
|
872
910
|
return;
|
|
873
911
|
}
|
|
912
|
+
if (partType === "reasoning" || (!partType && this.isKnownThinkingPart(messageID, partID))) {
|
|
913
|
+
const title = part
|
|
914
|
+
? this.extractReasoningTitle(part)
|
|
915
|
+
: undefined;
|
|
916
|
+
this.applyThinkingDelta(sessionID, messageID, partID, delta, part?.text, title);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
874
919
|
if (partType && partType !== "text") {
|
|
875
920
|
return;
|
|
876
921
|
}
|
|
@@ -890,6 +935,7 @@ class SummaryAggregator {
|
|
|
890
935
|
this.registerTextPart(messageID, partID);
|
|
891
936
|
}
|
|
892
937
|
}
|
|
938
|
+
this.emitThinkingFinishedOnce(sessionID, messageID);
|
|
893
939
|
this.applyTextDelta(sessionID, messageID, partID, delta, part?.text);
|
|
894
940
|
}
|
|
895
941
|
applyTextDelta(sessionID, messageID, partID, delta, fullTextHint) {
|
|
@@ -916,6 +962,121 @@ class SummaryAggregator {
|
|
|
916
962
|
this.startTypingIndicator();
|
|
917
963
|
this.emitPartialText(sessionID, messageID, combined);
|
|
918
964
|
}
|
|
965
|
+
extractReasoningTitle(part) {
|
|
966
|
+
for (const key of ["title", "heading", "summary", "name"]) {
|
|
967
|
+
const value = part[key];
|
|
968
|
+
if (typeof value === "string" && value.trim()) {
|
|
969
|
+
return value;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const metadata = part.metadata;
|
|
973
|
+
if (metadata && typeof metadata === "object") {
|
|
974
|
+
for (const key of ["title", "heading", "summary", "name"]) {
|
|
975
|
+
const value = metadata[key];
|
|
976
|
+
if (typeof value === "string" && value.trim()) {
|
|
977
|
+
return value;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return undefined;
|
|
982
|
+
}
|
|
983
|
+
getOrCreateThinkingMessageState(messageID) {
|
|
984
|
+
let state = this.thinkingMessageStates.get(messageID);
|
|
985
|
+
if (!state) {
|
|
986
|
+
state = {
|
|
987
|
+
orderedPartIds: [],
|
|
988
|
+
sections: new Map(),
|
|
989
|
+
};
|
|
990
|
+
this.thinkingMessageStates.set(messageID, state);
|
|
991
|
+
}
|
|
992
|
+
return state;
|
|
993
|
+
}
|
|
994
|
+
isKnownThinkingPart(messageID, partID) {
|
|
995
|
+
return this.thinkingMessageStates.get(messageID)?.sections.has(partID) ?? false;
|
|
996
|
+
}
|
|
997
|
+
registerThinkingPart(messageID, partID, title) {
|
|
998
|
+
const state = this.getOrCreateThinkingMessageState(messageID);
|
|
999
|
+
if (!state.orderedPartIds.includes(partID)) {
|
|
1000
|
+
state.orderedPartIds.push(partID);
|
|
1001
|
+
}
|
|
1002
|
+
const existing = state.sections.get(partID);
|
|
1003
|
+
if (!existing) {
|
|
1004
|
+
const section = { id: partID, text: "" };
|
|
1005
|
+
if (title) {
|
|
1006
|
+
section.title = title;
|
|
1007
|
+
}
|
|
1008
|
+
state.sections.set(partID, section);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
if (title && existing.title !== title) {
|
|
1012
|
+
existing.title = title;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
setThinkingPartSnapshot(messageID, partID, text, title) {
|
|
1016
|
+
this.registerThinkingPart(messageID, partID, title);
|
|
1017
|
+
const state = this.getOrCreateThinkingMessageState(messageID);
|
|
1018
|
+
const existing = state.sections.get(partID);
|
|
1019
|
+
const nextTitle = title ?? existing?.title;
|
|
1020
|
+
if (existing && existing.text === text && existing.title === nextTitle) {
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
const next = { id: partID, text };
|
|
1024
|
+
if (nextTitle) {
|
|
1025
|
+
next.title = nextTitle;
|
|
1026
|
+
}
|
|
1027
|
+
state.sections.set(partID, next);
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
1030
|
+
applyThinkingDelta(sessionID, messageID, partID, delta, fullTextHint, title) {
|
|
1031
|
+
if (sessionID !== this.currentSessionId) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
this.registerThinkingPart(messageID, partID, title);
|
|
1035
|
+
const state = this.getOrCreateThinkingMessageState(messageID);
|
|
1036
|
+
const existing = state.sections.get(partID);
|
|
1037
|
+
const previous = existing?.text ?? "";
|
|
1038
|
+
let accumulated = `${previous}${delta}`;
|
|
1039
|
+
if (typeof fullTextHint === "string" && fullTextHint.length > accumulated.length) {
|
|
1040
|
+
accumulated = fullTextHint;
|
|
1041
|
+
}
|
|
1042
|
+
this.setThinkingPartSnapshot(messageID, partID, accumulated, title ?? existing?.title);
|
|
1043
|
+
this.emitThinkingUpdate(sessionID, messageID, false);
|
|
1044
|
+
}
|
|
1045
|
+
getThinkingSections(messageID) {
|
|
1046
|
+
const state = this.thinkingMessageStates.get(messageID);
|
|
1047
|
+
if (!state) {
|
|
1048
|
+
return [];
|
|
1049
|
+
}
|
|
1050
|
+
return state.orderedPartIds
|
|
1051
|
+
.map((partID) => state.sections.get(partID))
|
|
1052
|
+
.filter((section) => Boolean(section))
|
|
1053
|
+
.map((section) => ({ ...section }));
|
|
1054
|
+
}
|
|
1055
|
+
emitThinkingUpdate(sessionId, messageId, isFirstUpdate) {
|
|
1056
|
+
if (!this.onThinkingCallback) {
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const sections = this.getThinkingSections(messageId);
|
|
1060
|
+
if (sections.length === 0) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const callback = this.onThinkingCallback;
|
|
1064
|
+
setImmediate(() => {
|
|
1065
|
+
callback({ sessionId, messageId, sections, isFirstUpdate });
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
emitThinkingFinishedOnce(sessionId, messageId) {
|
|
1069
|
+
if (!this.onThinkingFinishedCallback ||
|
|
1070
|
+
!this.thinkingFiredForMessages.has(messageId) ||
|
|
1071
|
+
this.thinkingFinishedForMessages.has(messageId)) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
this.thinkingFinishedForMessages.add(messageId);
|
|
1075
|
+
const callback = this.onThinkingFinishedCallback;
|
|
1076
|
+
setImmediate(() => {
|
|
1077
|
+
callback(sessionId, messageId);
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
919
1080
|
emitExternalUserInputIfReady(sessionId, messageId) {
|
|
920
1081
|
if (sessionId !== this.currentSessionId || this.deliveredExternalUserMessageIds.has(messageId)) {
|
|
921
1082
|
return;
|
|
@@ -942,9 +1103,12 @@ class SummaryAggregator {
|
|
|
942
1103
|
}
|
|
943
1104
|
cleanupCompletedMessage(messageId) {
|
|
944
1105
|
this.textMessageStates.delete(messageId);
|
|
1106
|
+
this.thinkingMessageStates.delete(messageId);
|
|
945
1107
|
this.messages.delete(messageId);
|
|
946
1108
|
this.partHashes.delete(messageId);
|
|
947
1109
|
this.knownTextPartIds.delete(messageId);
|
|
1110
|
+
this.thinkingFiredForMessages.delete(messageId);
|
|
1111
|
+
this.thinkingFinishedForMessages.delete(messageId);
|
|
948
1112
|
if (this.textMessageStates.size === 0) {
|
|
949
1113
|
logger.debug("[Aggregator] No more active messages, stopping typing indicator");
|
|
950
1114
|
this.stopTypingIndicator();
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { opencodeClient } from "
|
|
2
|
-
import { getCurrentProject } from "../settings
|
|
3
|
-
import { getCurrentSession } from "
|
|
4
|
-
import {
|
|
5
|
-
import { logger } from "../utils/logger.js";
|
|
1
|
+
import { opencodeClient } from "../../opencode/client.js";
|
|
2
|
+
import { getCurrentAgent, getCurrentProject, setCurrentAgent } from "../stores/settings-store.js";
|
|
3
|
+
import { getCurrentSession } from "./session-service.js";
|
|
4
|
+
import { logger } from "../../utils/logger.js";
|
|
6
5
|
/**
|
|
7
6
|
* Get list of available agents from OpenCode API
|
|
8
7
|
* @returns Array of available agents (filtered by mode and hidden flag)
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { opencodeClient } from "
|
|
2
|
-
import { isOpencodeServerHealthy } from "
|
|
3
|
-
import { summaryAggregator } from "../summary
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { opencodeClient } from "../../opencode/client.js";
|
|
2
|
+
import { isOpencodeServerHealthy } from "../../opencode/ready-refresh.js";
|
|
3
|
+
import { summaryAggregator } from "../managers/summary-aggregation-manager.js";
|
|
4
|
+
import { questionManager } from "../managers/question-manager.js";
|
|
5
|
+
import { permissionManager } from "../managers/permission-manager.js";
|
|
6
|
+
import { getCurrentSession } from "./session-service.js";
|
|
7
|
+
import { getCurrentProject } from "../stores/settings-store.js";
|
|
8
|
+
import { attachManager } from "../managers/attach-manager.js";
|
|
9
|
+
import { logger } from "../../utils/logger.js";
|
|
10
|
+
import { isExpectedOpencodeUnavailableError } from "../../utils/opencode-error.js";
|
|
11
|
+
let attachPresentation = null;
|
|
12
|
+
export function configureAttachPresentation(deps) {
|
|
13
|
+
attachPresentation = deps;
|
|
14
|
+
}
|
|
15
15
|
function getAttachBusyStatus(sessionId, statuses) {
|
|
16
16
|
if (!statuses || typeof statuses !== "object") {
|
|
17
17
|
return false;
|
|
@@ -19,36 +19,12 @@ function getAttachBusyStatus(sessionId, statuses) {
|
|
|
19
19
|
const sessionStatus = statuses[sessionId];
|
|
20
20
|
return sessionStatus?.type === "busy";
|
|
21
21
|
}
|
|
22
|
-
async function ensureAttachPinnedSession({ api, chatId, session, forceFullRestore = false, }) {
|
|
23
|
-
if (!pinnedMessageManager.isInitialized()) {
|
|
24
|
-
pinnedMessageManager.initialize(api, chatId);
|
|
25
|
-
}
|
|
26
|
-
keyboardManager.initialize(api, chatId);
|
|
27
|
-
const pinnedState = pinnedMessageManager.getState();
|
|
28
|
-
if (pinnedState.sessionId === session.id && pinnedState.messageId) {
|
|
29
|
-
if (forceFullRestore) {
|
|
30
|
-
await pinnedMessageManager.loadContextFromHistory(session.id, session.directory);
|
|
31
|
-
}
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (pinnedState.messageId && pinnedState.sessionId === null) {
|
|
35
|
-
await pinnedMessageManager.restoreExistingSession(session.id, session.title);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
await pinnedMessageManager.onSessionChange(session.id, session.title);
|
|
39
|
-
}
|
|
40
|
-
await pinnedMessageManager.loadContextFromHistory(session.id, session.directory);
|
|
41
|
-
const contextInfo = pinnedMessageManager.getContextInfo();
|
|
42
|
-
if (contextInfo) {
|
|
43
|
-
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
22
|
async function syncPinnedAttachState() {
|
|
47
|
-
if (!
|
|
23
|
+
if (!attachPresentation) {
|
|
48
24
|
return;
|
|
49
25
|
}
|
|
50
26
|
const attached = attachManager.getSnapshot();
|
|
51
|
-
await
|
|
27
|
+
await attachPresentation.syncAttachState(attached !== null, attached?.busy ?? false);
|
|
52
28
|
}
|
|
53
29
|
async function restorePendingQuestion(bot, chatId, sessionId, directory) {
|
|
54
30
|
const { data, error } = await opencodeClient.question.list({
|
|
@@ -64,11 +40,11 @@ async function restorePendingQuestion(bot, chatId, sessionId, directory) {
|
|
|
64
40
|
return false;
|
|
65
41
|
}
|
|
66
42
|
const pendingQuestion = data.find((request) => request.sessionID === sessionId);
|
|
67
|
-
if (!pendingQuestion) {
|
|
43
|
+
if (!pendingQuestion || !attachPresentation) {
|
|
68
44
|
return false;
|
|
69
45
|
}
|
|
70
46
|
questionManager.startQuestions(pendingQuestion.questions, pendingQuestion.id);
|
|
71
|
-
await showCurrentQuestion(bot.api, chatId);
|
|
47
|
+
await attachPresentation.showCurrentQuestion(bot.api, chatId);
|
|
72
48
|
return true;
|
|
73
49
|
}
|
|
74
50
|
async function restorePendingPermissions(bot, chatId, sessionId, directory) {
|
|
@@ -85,15 +61,18 @@ async function restorePendingPermissions(bot, chatId, sessionId, directory) {
|
|
|
85
61
|
return 0;
|
|
86
62
|
}
|
|
87
63
|
const pendingPermissions = data.filter((request) => request.sessionID === sessionId);
|
|
64
|
+
if (!attachPresentation) {
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
88
67
|
for (const request of pendingPermissions) {
|
|
89
|
-
await showPermissionRequest(bot.api, chatId, request);
|
|
68
|
+
await attachPresentation.showPermissionRequest(bot.api, chatId, request);
|
|
90
69
|
}
|
|
91
70
|
return pendingPermissions.length;
|
|
92
71
|
}
|
|
93
72
|
export async function attachToSession(deps) {
|
|
94
73
|
const { bot, chatId, session, ensureEventSubscription, forceFullRestore = false } = deps;
|
|
95
74
|
const alreadyAttached = attachManager.isAttachedSession(session.id, session.directory);
|
|
96
|
-
await
|
|
75
|
+
await attachPresentation?.ensurePinnedSession({
|
|
97
76
|
api: bot.api,
|
|
98
77
|
chatId,
|
|
99
78
|
session,
|
package/dist/{bot/utils/busy-reconciliation.js → app/services/busy-reconciliation-service.js}
RENAMED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { opencodeClient } from "../../opencode/client.js";
|
|
2
|
-
import { foregroundSessionState, } from "
|
|
3
|
-
import { scheduledTaskRuntime } from "
|
|
4
|
-
import { attachManager } from "
|
|
5
|
-
import { markAttachedSessionBusy, markAttachedSessionIdle } from "
|
|
6
|
-
import { assistantRunState } from "../assistant-run-state.js";
|
|
7
|
-
import { clearPromptResponseMode } from "../handlers/prompt.js";
|
|
2
|
+
import { foregroundSessionState, } from "../managers/foreground-session-state-manager.js";
|
|
3
|
+
import { scheduledTaskRuntime } from "./scheduled-task-runtime-service.js";
|
|
4
|
+
import { attachManager } from "../managers/attach-manager.js";
|
|
5
|
+
import { markAttachedSessionBusy, markAttachedSessionIdle } from "./attach-service.js";
|
|
6
|
+
import { assistantRunState } from "../managers/assistant-run-state-manager.js";
|
|
8
7
|
import { logger } from "../../utils/logger.js";
|
|
9
8
|
const RECONCILE_MIN_INTERVAL_MS = 10_000;
|
|
10
9
|
const FOREGROUND_BUSY_RECONCILE_GRACE_MS = 2_000;
|
|
11
10
|
const inFlightDirectories = new Set();
|
|
12
11
|
const lastReconcileAtByDirectory = new Map();
|
|
13
12
|
let responseStreamerInstance = null;
|
|
13
|
+
let clearPromptResponseModeForReconciliation = null;
|
|
14
14
|
export function setResponseStreamerForReconciliation(streamer) {
|
|
15
15
|
responseStreamerInstance = streamer;
|
|
16
16
|
}
|
|
17
|
+
export function setPromptResponseModeClearerForReconciliation(clearer) {
|
|
18
|
+
clearPromptResponseModeForReconciliation = clearer;
|
|
19
|
+
}
|
|
17
20
|
function getReconciliationTargets(directory) {
|
|
18
21
|
const foregroundBusySessions = foregroundSessionState
|
|
19
22
|
.getBusySessions()
|
|
@@ -38,7 +41,7 @@ function isWithinForegroundBusyGracePeriod(session, now) {
|
|
|
38
41
|
async function clearForegroundBusySession(sessionId, reason) {
|
|
39
42
|
foregroundSessionState.markIdle(sessionId);
|
|
40
43
|
assistantRunState.clearRun(sessionId, reason);
|
|
41
|
-
|
|
44
|
+
clearPromptResponseModeForReconciliation?.(sessionId);
|
|
42
45
|
}
|
|
43
46
|
export async function reconcileBusyStateNow(directory, now = Date.now()) {
|
|
44
47
|
if (!directory) {
|
|
@@ -118,4 +121,6 @@ export async function reconcileBusyState(directory, now = Date.now()) {
|
|
|
118
121
|
export function __resetBusyReconciliationForTests() {
|
|
119
122
|
inFlightDirectories.clear();
|
|
120
123
|
lastReconcileAtByDirectory.clear();
|
|
124
|
+
responseStreamerInstance = null;
|
|
125
|
+
clearPromptResponseModeForReconciliation = null;
|
|
121
126
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { opencodeClient } from "../../opencode/client.js";
|
|
2
|
+
function normalizeDirectoryForCommandApi(directory) {
|
|
3
|
+
return directory.replace(/\\/g, "/");
|
|
4
|
+
}
|
|
5
|
+
export async function loadCommandCatalog(projectDirectory) {
|
|
6
|
+
const { data, error } = await opencodeClient.command.list({
|
|
7
|
+
directory: normalizeDirectoryForCommandApi(projectDirectory),
|
|
8
|
+
});
|
|
9
|
+
if (error || !data) {
|
|
10
|
+
throw error || new Error("No command data received");
|
|
11
|
+
}
|
|
12
|
+
return data
|
|
13
|
+
.filter((command) => {
|
|
14
|
+
const source = command.source;
|
|
15
|
+
return (typeof command.name === "string" && command.name.trim().length > 0 && source === "command");
|
|
16
|
+
})
|
|
17
|
+
.map((command) => ({
|
|
18
|
+
name: command.name,
|
|
19
|
+
description: command.description,
|
|
20
|
+
}));
|
|
21
|
+
}
|
package/dist/{bot/utils/external-user-input.js → app/services/external-user-input-service.js}
RENAMED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { t } from "../../i18n/index.js";
|
|
2
|
-
import { escapePlainTextForTelegramMarkdownV2 } from "../../summary/formatter.js";
|
|
3
|
-
import { sendBotText } from "./telegram-text.js";
|
|
4
2
|
const EXTERNAL_USER_INPUT_MAX_DISPLAY_LENGTH = 2000;
|
|
3
|
+
const MARKDOWN_V2_RESERVED_CHARS = /([_\*\[\]\(\)~`>#+\-=|{}.!\\])/g;
|
|
4
|
+
function escapePlainTextForTelegramMarkdownV2(text) {
|
|
5
|
+
return text.replace(MARKDOWN_V2_RESERVED_CHARS, "\\$1");
|
|
6
|
+
}
|
|
5
7
|
function normalizeExternalUserInputText(text) {
|
|
6
8
|
return text.replace(/\r\n/g, "\n").trim();
|
|
7
9
|
}
|
|
@@ -35,20 +37,3 @@ export function buildExternalUserInputNotification(text) {
|
|
|
35
37
|
rawFallbackText: `${title}\n\n${buildQuotedPlainText(displayText)}`,
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
|
-
export async function deliverExternalUserInputNotification({ api, chatId, currentSessionId, sessionId, text, consumeSuppressedInput, }) {
|
|
39
|
-
const notification = buildExternalUserInputNotification(text);
|
|
40
|
-
if (!notification || currentSessionId !== sessionId) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
if (consumeSuppressedInput(sessionId, text)) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
await sendBotText({
|
|
47
|
-
api,
|
|
48
|
-
chatId,
|
|
49
|
-
text: notification.text,
|
|
50
|
-
rawFallbackText: notification.rawFallbackText,
|
|
51
|
-
format: "markdown_v2",
|
|
52
|
-
});
|
|
53
|
-
return true;
|
|
54
|
-
}
|