@grinev/opencode-telegram-bot 0.17.0 → 0.18.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.
@@ -15,6 +15,8 @@ class PinnedMessageManager {
15
15
  chatId: null,
16
16
  sessionId: null,
17
17
  sessionTitle: t("pinned.default_session_title"),
18
+ attachActive: false,
19
+ attachBusy: false,
18
20
  projectPath: "",
19
21
  projectBranch: null,
20
22
  projectWorktreePath: null,
@@ -55,6 +57,8 @@ class PinnedMessageManager {
55
57
  // Update state
56
58
  this.state.sessionId = sessionId;
57
59
  this.state.sessionTitle = sessionTitle || t("pinned.default_session_title");
60
+ this.state.attachActive = false;
61
+ this.state.attachBusy = false;
58
62
  await this.refreshProjectMetadata();
59
63
  // Fetch context limit for current model
60
64
  await this.fetchContextLimit();
@@ -83,6 +87,15 @@ class PinnedMessageManager {
83
87
  await this.updatePinnedMessage();
84
88
  }
85
89
  }
90
+ async setAttachState(active, busy) {
91
+ const nextBusy = active ? busy : false;
92
+ if (this.state.attachActive === active && this.state.attachBusy === nextBusy) {
93
+ return;
94
+ }
95
+ this.state.attachActive = active;
96
+ this.state.attachBusy = nextBusy;
97
+ await this.updatePinnedMessage();
98
+ }
86
99
  /**
87
100
  * Load context token usage from session history
88
101
  */
@@ -672,6 +685,9 @@ class PinnedMessageManager {
672
685
  // Just reset state if not initialized
673
686
  this.state.messageId = null;
674
687
  this.state.sessionId = null;
688
+ this.state.sessionTitle = t("pinned.default_session_title");
689
+ this.state.attachActive = false;
690
+ this.state.attachBusy = false;
675
691
  this.state.tokensUsed = 0;
676
692
  this.state.tokensLimit = 0;
677
693
  this.state.projectPath = "";
@@ -691,6 +707,8 @@ class PinnedMessageManager {
691
707
  this.state.messageId = null;
692
708
  this.state.sessionId = null;
693
709
  this.state.sessionTitle = t("pinned.default_session_title");
710
+ this.state.attachActive = false;
711
+ this.state.attachBusy = false;
694
712
  this.state.projectPath = "";
695
713
  this.state.projectBranch = null;
696
714
  this.state.projectWorktreePath = null;
@@ -32,6 +32,7 @@ class SummaryAggregator {
32
32
  lastUpdated = 0;
33
33
  onCompleteCallback = null;
34
34
  onPartialCallback = null;
35
+ onExternalUserInputCallback = null;
35
36
  onToolCallback = null;
36
37
  onToolFileCallback = null;
37
38
  onQuestionCallback = null;
@@ -50,6 +51,7 @@ class SummaryAggregator {
50
51
  onClearedCallback = null;
51
52
  processedToolStates = new Set();
52
53
  thinkingFiredForMessages = new Set();
54
+ deliveredExternalUserMessageIds = new Set();
53
55
  knownTextPartIds = new Map();
54
56
  bot = null;
55
57
  chatId = null;
@@ -73,6 +75,9 @@ class SummaryAggregator {
73
75
  setOnPartial(callback) {
74
76
  this.onPartialCallback = callback;
75
77
  }
78
+ setOnExternalUserInput(callback) {
79
+ this.onExternalUserInputCallback = callback;
80
+ }
76
81
  setOnTool(callback) {
77
82
  this.onToolCallback = callback;
78
83
  }
@@ -226,6 +231,7 @@ class SummaryAggregator {
226
231
  this.knownTextPartIds.clear();
227
232
  this.processedToolStates.clear();
228
233
  this.thinkingFiredForMessages.clear();
234
+ this.deliveredExternalUserMessageIds.clear();
229
235
  this.trackedSessionParents.clear();
230
236
  this.subagentStates.clear();
231
237
  this.subagentOrder = [];
@@ -605,6 +611,10 @@ class SummaryAggregator {
605
611
  }
606
612
  const messageID = info.id;
607
613
  this.messages.set(messageID, { role: info.role });
614
+ if (info.role === "user") {
615
+ this.emitExternalUserInputIfReady(info.sessionID, messageID);
616
+ return;
617
+ }
608
618
  if (info.role === "assistant") {
609
619
  if (!this.textMessageStates.has(messageID)) {
610
620
  this.textMessageStates.set(messageID, {
@@ -655,15 +665,8 @@ class SummaryAggregator {
655
665
  completedAt: time?.completed,
656
666
  });
657
667
  }
658
- this.textMessageStates.delete(messageID);
659
- this.messages.delete(messageID);
660
- this.partHashes.delete(messageID);
661
- this.knownTextPartIds.delete(messageID);
668
+ this.cleanupCompletedMessage(messageID);
662
669
  logger.debug(`[Aggregator] Message completed cleanup: remaining messages=${this.textMessageStates.size}`);
663
- if (this.textMessageStates.size === 0) {
664
- logger.debug("[Aggregator] No more active messages, stopping typing indicator");
665
- this.stopTypingIndicator();
666
- }
667
670
  }
668
671
  this.lastUpdated = Date.now();
669
672
  }
@@ -741,6 +744,9 @@ class SummaryAggregator {
741
744
  this.startTypingIndicator();
742
745
  this.emitPartialText(part.sessionID, messageID, fullText);
743
746
  }
747
+ else if (messageInfo && messageInfo.role === "user") {
748
+ this.emitExternalUserInputIfReady(part.sessionID, messageID);
749
+ }
744
750
  else {
745
751
  const state = this.getOrCreateTextMessageState(messageID);
746
752
  state.optimisticUpdateCount++;
@@ -857,9 +863,48 @@ class SummaryAggregator {
857
863
  if (!combined.trim()) {
858
864
  return;
859
865
  }
866
+ const messageInfo = this.messages.get(messageID);
867
+ if (messageInfo?.role === "user") {
868
+ this.emitExternalUserInputIfReady(sessionID, messageID);
869
+ return;
870
+ }
860
871
  this.startTypingIndicator();
861
872
  this.emitPartialText(sessionID, messageID, combined);
862
873
  }
874
+ emitExternalUserInputIfReady(sessionId, messageId) {
875
+ if (sessionId !== this.currentSessionId || this.deliveredExternalUserMessageIds.has(messageId)) {
876
+ return;
877
+ }
878
+ const messageInfo = this.messages.get(messageId);
879
+ if (!messageInfo || messageInfo.role !== "user") {
880
+ return;
881
+ }
882
+ const messageText = this.getCombinedMessageText(messageId).trim();
883
+ if (!messageText) {
884
+ return;
885
+ }
886
+ this.deliveredExternalUserMessageIds.add(messageId);
887
+ this.cleanupCompletedMessage(messageId);
888
+ if (!this.onExternalUserInputCallback) {
889
+ return;
890
+ }
891
+ const callback = this.onExternalUserInputCallback;
892
+ setImmediate(() => {
893
+ Promise.resolve(callback(sessionId, messageId, messageText)).catch((err) => {
894
+ logger.error("[Aggregator] Error in external user input callback:", err);
895
+ });
896
+ });
897
+ }
898
+ cleanupCompletedMessage(messageId) {
899
+ this.textMessageStates.delete(messageId);
900
+ this.messages.delete(messageId);
901
+ this.partHashes.delete(messageId);
902
+ this.knownTextPartIds.delete(messageId);
903
+ if (this.textMessageStates.size === 0) {
904
+ logger.debug("[Aggregator] No more active messages, stopping typing indicator");
905
+ this.stopTypingIndicator();
906
+ }
907
+ }
863
908
  emitPartialText(sessionId, messageId, messageText) {
864
909
  if (!this.onPartialCallback || !messageText.trim()) {
865
910
  return;
@@ -1103,7 +1148,7 @@ class SummaryAggregator {
1103
1148
  const callback = this.onQuestionCallback;
1104
1149
  setImmediate(async () => {
1105
1150
  try {
1106
- await callback(questions, id);
1151
+ await callback(questions, id, sessionID);
1107
1152
  }
1108
1153
  catch (err) {
1109
1154
  logger.error("[Aggregator] Error in question callback:", err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grinev/opencode-telegram-bot",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Telegram bot client for OpenCode to run and monitor coding tasks from chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",