@deadragdoll/tellymcp 0.0.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.
Files changed (124) hide show
  1. package/.env.example.client +93 -0
  2. package/.env.example.gateway +120 -0
  3. package/CHANGELOG.md +155 -0
  4. package/LICENSE +21 -0
  5. package/README-ru.md +338 -0
  6. package/README.md +1262 -0
  7. package/STANDALONE-ru.md +266 -0
  8. package/STANDALONE.md +266 -0
  9. package/TOOLS.md +1296 -0
  10. package/config/templates/env.both.template +83 -0
  11. package/config/templates/env.client.template +60 -0
  12. package/config/templates/env.gateway.template +82 -0
  13. package/dist/cli.js +636 -0
  14. package/dist/index.js +17 -0
  15. package/dist/lib/logfeed/store.js +52 -0
  16. package/dist/lib/middlewares/tracer.js +172 -0
  17. package/dist/lib/mixins/db.js +267 -0
  18. package/dist/lib/mixins/logfeed.js +34 -0
  19. package/dist/lib/mixins/session.errors.js +142 -0
  20. package/dist/lib/moleculer.js +2 -0
  21. package/dist/lib/trace.js +147 -0
  22. package/dist/lib/traceContext.js +116 -0
  23. package/dist/moleculer.config.js +274 -0
  24. package/dist/services/features/telegram-mcp/approval.service.js +33 -0
  25. package/dist/services/features/telegram-mcp/browser.service.js +42 -0
  26. package/dist/services/features/telegram-mcp/collaboration.service.js +53 -0
  27. package/dist/services/features/telegram-mcp/ensuredb.service.js +337 -0
  28. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +378 -0
  29. package/dist/services/features/telegram-mcp/gateway-loopback.js +10 -0
  30. package/dist/services/features/telegram-mcp/gateway-rmq.service.js +294 -0
  31. package/dist/services/features/telegram-mcp/gateway-socket.service.js +1463 -0
  32. package/dist/services/features/telegram-mcp/gateway.service.js +1141 -0
  33. package/dist/services/features/telegram-mcp/inbox.service.js +33 -0
  34. package/dist/services/features/telegram-mcp/mcp-http.service.js +76 -0
  35. package/dist/services/features/telegram-mcp/mcp-server.service.js +127 -0
  36. package/dist/services/features/telegram-mcp/notify.service.js +33 -0
  37. package/dist/services/features/telegram-mcp/pair.service.js +33 -0
  38. package/dist/services/features/telegram-mcp/runtime.service.js +36 -0
  39. package/dist/services/features/telegram-mcp/session-context.service.js +33 -0
  40. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +103 -0
  41. package/dist/services/features/telegram-mcp/src/app/config/env.js +317 -0
  42. package/dist/services/features/telegram-mcp/src/app/http.js +774 -0
  43. package/dist/services/features/telegram-mcp/src/app/index.js +2 -0
  44. package/dist/services/features/telegram-mcp/src/app/providers/mcp/server.js +13 -0
  45. package/dist/services/features/telegram-mcp/src/app/providers/redis/client.js +18 -0
  46. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +740 -0
  47. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +267 -0
  48. package/dist/services/features/telegram-mcp/src/app/webapp/relay.js +69 -0
  49. package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +9 -0
  50. package/dist/services/features/telegram-mcp/src/entities/auth/model/types.js +2 -0
  51. package/dist/services/features/telegram-mcp/src/entities/browser/model/types.js +2 -0
  52. package/dist/services/features/telegram-mcp/src/entities/collaboration/model/types.js +2 -0
  53. package/dist/services/features/telegram-mcp/src/entities/inbox/model/types.js +2 -0
  54. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +545 -0
  55. package/dist/services/features/telegram-mcp/src/entities/request/model/types.js +2 -0
  56. package/dist/services/features/telegram-mcp/src/entities/session/model/types.js +2 -0
  57. package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +33 -0
  58. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClearLogsTool.js +28 -0
  59. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +28 -0
  60. package/dist/services/features/telegram-mcp/src/features/browser/model/browserCloseTool.js +28 -0
  61. package/dist/services/features/telegram-mcp/src/features/browser/model/browserComputedStyleTool.js +28 -0
  62. package/dist/services/features/telegram-mcp/src/features/browser/model/browserConsoleTool.js +28 -0
  63. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +28 -0
  64. package/dist/services/features/telegram-mcp/src/features/browser/model/browserErrorsTool.js +28 -0
  65. package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +28 -0
  66. package/dist/services/features/telegram-mcp/src/features/browser/model/browserNetworkFailuresTool.js +28 -0
  67. package/dist/services/features/telegram-mcp/src/features/browser/model/browserOpenTool.js +33 -0
  68. package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +28 -0
  69. package/dist/services/features/telegram-mcp/src/features/browser/model/browserReloadTool.js +28 -0
  70. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +28 -0
  71. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +689 -0
  72. package/dist/services/features/telegram-mcp/src/features/browser/model/browserWaitForTool.js +28 -0
  73. package/dist/services/features/telegram-mcp/src/features/browser/model/browserWaitForUrlTool.js +28 -0
  74. package/dist/services/features/telegram-mcp/src/features/collaboration/model/backend.js +2 -0
  75. package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +26 -0
  76. package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +390 -0
  77. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +102 -0
  78. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +33 -0
  79. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +33 -0
  80. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +69 -0
  81. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +657 -0
  82. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayReplyResolution.js +17 -0
  83. package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +33 -0
  84. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +33 -0
  85. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +33 -0
  86. package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +77 -0
  87. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +93 -0
  88. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +33 -0
  89. package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +33 -0
  90. package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +33 -0
  91. package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +202 -0
  92. package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +33 -0
  93. package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +33 -0
  94. package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +33 -0
  95. package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +33 -0
  96. package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +409 -0
  97. package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +33 -0
  98. package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +33 -0
  99. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +123 -0
  100. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +33 -0
  101. package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +243 -0
  102. package/dist/services/features/telegram-mcp/src/shared/api/storage/contract.js +2 -0
  103. package/dist/services/features/telegram-mcp/src/shared/api/tool-registry/registry.js +8 -0
  104. package/dist/services/features/telegram-mcp/src/shared/api/tool-registry/types.js +2 -0
  105. package/dist/services/features/telegram-mcp/src/shared/api/transport/contract.js +2 -0
  106. package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +86 -0
  107. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +436 -0
  108. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabSemantics.js +21 -0
  109. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabUi.js +87 -0
  110. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/messageFormat.js +60 -0
  111. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +46 -0
  112. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +6534 -0
  113. package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +280 -0
  114. package/dist/services/features/telegram-mcp/src/shared/lib/ids/ids.js +34 -0
  115. package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +68 -0
  116. package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +223 -0
  117. package/dist/services/features/telegram-mcp/src/shared/lib/redact-secrets/redactSecrets.js +22 -0
  118. package/dist/services/features/telegram-mcp/src/shared/lib/truncate/truncate.js +12 -0
  119. package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +124 -0
  120. package/dist/services/features/telegram-mcp/src/shared/types/common.js +2 -0
  121. package/dist/services/features/telegram-mcp/standalone-http.service.js +113 -0
  122. package/dist/services/features/telegram-mcp/tools-sync.service.js +33 -0
  123. package/package.json +110 -0
  124. package/scripts/postinstall.js +60 -0
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetTmuxTargetTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify(output, null, 2),
10
+ },
11
+ ];
12
+ }
13
+ class GetTmuxTargetTool {
14
+ sessionContextService;
15
+ constructor(sessionContextService) {
16
+ this.sessionContextService = sessionContextService;
17
+ }
18
+ register(server) {
19
+ server.registerTool("get_tmux_target", {
20
+ title: "Get tmux Target",
21
+ description: "Debug/setup tool. Return the stored tmux pane target for a session, including the last time the service nudged that tmux pane. Do not call this in the normal inbox-processing path after a tmux nudge.",
22
+ inputSchema: schema_1.getTmuxTargetInputSchema,
23
+ outputSchema: schema_1.getTmuxTargetOutputSchema,
24
+ }, async (args) => {
25
+ const output = await this.sessionContextService.getTmuxTarget(args);
26
+ return {
27
+ content: createContent(output),
28
+ structuredContent: output,
29
+ };
30
+ });
31
+ }
32
+ }
33
+ exports.GetTmuxTargetTool = GetTmuxTargetTool;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RenameSessionTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify(output, null, 2),
10
+ },
11
+ ];
12
+ }
13
+ class RenameSessionTool {
14
+ sessionContextService;
15
+ constructor(sessionContextService) {
16
+ this.sessionContextService = sessionContextService;
17
+ }
18
+ register(server) {
19
+ server.registerTool("rename_session", {
20
+ title: "Rename Session",
21
+ description: "Rename only the human-readable session label. Use this when the user wants a clearer session name; it does not change session_id, pairing, tmux target, or saved context.",
22
+ inputSchema: schema_1.renameSessionInputSchema,
23
+ outputSchema: schema_1.renameSessionOutputSchema,
24
+ }, async (args) => {
25
+ const output = await this.sessionContextService.renameSession(args);
26
+ return {
27
+ content: createContent(output),
28
+ structuredContent: output,
29
+ };
30
+ });
31
+ }
32
+ }
33
+ exports.RenameSessionTool = RenameSessionTool;
@@ -0,0 +1,409 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionContextService = void 0;
4
+ const redactSecrets_1 = require("../../../shared/lib/redact-secrets/redactSecrets");
5
+ class SessionContextService {
6
+ sessionStore;
7
+ bindingStore;
8
+ logger;
9
+ projectIdentityResolver;
10
+ constructor(sessionStore, bindingStore, logger, projectIdentityResolver) {
11
+ this.sessionStore = sessionStore;
12
+ this.bindingStore = bindingStore;
13
+ this.logger = logger;
14
+ this.projectIdentityResolver = projectIdentityResolver;
15
+ }
16
+ async setContext(input) {
17
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
18
+ const updatedAt = new Date().toISOString();
19
+ const existing = await this.sessionStore.getSession(resolved.sessionId);
20
+ const binding = await this.bindingStore.getBinding(resolved.sessionId);
21
+ await this.sessionStore.setSession({
22
+ sessionId: resolved.sessionId,
23
+ ...(input.session_label
24
+ ? { label: (0, redactSecrets_1.redactSecrets)(input.session_label) }
25
+ : resolved.sessionLabel
26
+ ? { label: (0, redactSecrets_1.redactSecrets)(resolved.sessionLabel) }
27
+ : existing?.label
28
+ ? { label: existing.label }
29
+ : {}),
30
+ ...(existing?.cwd ? { cwd: existing.cwd } : { cwd: resolved.cwd }),
31
+ ...(existing?.linkedSessionId
32
+ ? { linkedSessionId: existing.linkedSessionId }
33
+ : {}),
34
+ ...(existing?.activeProjectUuid
35
+ ? { activeProjectUuid: existing.activeProjectUuid }
36
+ : {}),
37
+ ...(existing?.activeProjectName
38
+ ? { activeProjectName: existing.activeProjectName }
39
+ : {}),
40
+ ...(input.task
41
+ ? { task: (0, redactSecrets_1.redactSecrets)(input.task) }
42
+ : existing?.task
43
+ ? { task: existing.task }
44
+ : {}),
45
+ summary: (0, redactSecrets_1.redactSecrets)(input.summary),
46
+ ...(input.files?.length
47
+ ? { files: input.files.map((item) => (0, redactSecrets_1.redactSecrets)(item)) }
48
+ : existing?.files
49
+ ? { files: existing.files }
50
+ : {}),
51
+ ...(input.decisions?.length
52
+ ? { decisions: input.decisions.map((item) => (0, redactSecrets_1.redactSecrets)(item)) }
53
+ : existing?.decisions
54
+ ? { decisions: existing.decisions }
55
+ : {}),
56
+ ...(input.risks?.length
57
+ ? { risks: input.risks.map((item) => (0, redactSecrets_1.redactSecrets)(item)) }
58
+ : existing?.risks
59
+ ? { risks: existing.risks }
60
+ : {}),
61
+ ...(existing?.tmuxSessionName
62
+ ? { tmuxSessionName: existing.tmuxSessionName }
63
+ : {}),
64
+ ...(existing?.tmuxWindowName
65
+ ? { tmuxWindowName: existing.tmuxWindowName }
66
+ : {}),
67
+ ...(typeof existing?.tmuxWindowIndex === "number"
68
+ ? { tmuxWindowIndex: existing.tmuxWindowIndex }
69
+ : {}),
70
+ ...(existing?.tmuxPaneId ? { tmuxPaneId: existing.tmuxPaneId } : {}),
71
+ ...(typeof existing?.tmuxPaneIndex === "number"
72
+ ? { tmuxPaneIndex: existing.tmuxPaneIndex }
73
+ : {}),
74
+ ...(existing?.tmuxTarget ? { tmuxTarget: existing.tmuxTarget } : {}),
75
+ ...(existing?.lastTmuxNudgeAt
76
+ ? { lastTmuxNudgeAt: existing.lastTmuxNudgeAt }
77
+ : {}),
78
+ ...(existing?.lastSeenToolsHash
79
+ ? { lastSeenToolsHash: existing.lastSeenToolsHash }
80
+ : {}),
81
+ ...(existing?.lastNotifiedToolsHash
82
+ ? { lastNotifiedToolsHash: existing.lastNotifiedToolsHash }
83
+ : {}),
84
+ updatedAt,
85
+ });
86
+ this.projectIdentityResolver.persistSessionMarker({
87
+ cwd: resolved.cwd,
88
+ sessionId: resolved.sessionId,
89
+ sessionLabel: input.session_label?.trim() || resolved.sessionLabel,
90
+ });
91
+ this.logger.info("Session context saved", {
92
+ sessionId: resolved.sessionId,
93
+ sessionLabel: resolved.sessionLabel,
94
+ sessionIdDerived: resolved.sessionIdDerived,
95
+ sessionLabelDerived: resolved.sessionLabelDerived,
96
+ hasBinding: Boolean(binding),
97
+ fileCount: input.files?.length ?? 0,
98
+ decisionCount: input.decisions?.length ?? 0,
99
+ riskCount: input.risks?.length ?? 0,
100
+ });
101
+ return {
102
+ saved: true,
103
+ session_id: resolved.sessionId,
104
+ updated_at: updatedAt,
105
+ has_binding: Boolean(binding),
106
+ };
107
+ }
108
+ async renameSession(input) {
109
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
110
+ const existing = await this.sessionStore.getSession(resolved.sessionId);
111
+ const updatedAt = new Date().toISOString();
112
+ const label = (0, redactSecrets_1.redactSecrets)(input.title);
113
+ await this.sessionStore.setSession({
114
+ sessionId: resolved.sessionId,
115
+ label,
116
+ ...(existing?.cwd ? { cwd: existing.cwd } : { cwd: resolved.cwd }),
117
+ ...(existing?.linkedSessionId
118
+ ? { linkedSessionId: existing.linkedSessionId }
119
+ : {}),
120
+ ...(existing?.activeProjectUuid
121
+ ? { activeProjectUuid: existing.activeProjectUuid }
122
+ : {}),
123
+ ...(existing?.activeProjectName
124
+ ? { activeProjectName: existing.activeProjectName }
125
+ : {}),
126
+ ...(existing?.task ? { task: existing.task } : {}),
127
+ ...(existing?.summary ? { summary: existing.summary } : {}),
128
+ ...(existing?.files ? { files: existing.files } : {}),
129
+ ...(existing?.decisions ? { decisions: existing.decisions } : {}),
130
+ ...(existing?.risks ? { risks: existing.risks } : {}),
131
+ ...(existing?.tmuxSessionName
132
+ ? { tmuxSessionName: existing.tmuxSessionName }
133
+ : {}),
134
+ ...(existing?.tmuxWindowName
135
+ ? { tmuxWindowName: existing.tmuxWindowName }
136
+ : {}),
137
+ ...(typeof existing?.tmuxWindowIndex === "number"
138
+ ? { tmuxWindowIndex: existing.tmuxWindowIndex }
139
+ : {}),
140
+ ...(existing?.tmuxPaneId ? { tmuxPaneId: existing.tmuxPaneId } : {}),
141
+ ...(typeof existing?.tmuxPaneIndex === "number"
142
+ ? { tmuxPaneIndex: existing.tmuxPaneIndex }
143
+ : {}),
144
+ ...(existing?.tmuxTarget ? { tmuxTarget: existing.tmuxTarget } : {}),
145
+ ...(existing?.lastTmuxNudgeAt
146
+ ? { lastTmuxNudgeAt: existing.lastTmuxNudgeAt }
147
+ : {}),
148
+ ...(existing?.lastSeenToolsHash
149
+ ? { lastSeenToolsHash: existing.lastSeenToolsHash }
150
+ : {}),
151
+ ...(existing?.lastNotifiedToolsHash
152
+ ? { lastNotifiedToolsHash: existing.lastNotifiedToolsHash }
153
+ : {}),
154
+ updatedAt,
155
+ });
156
+ this.projectIdentityResolver.persistSessionMarker({
157
+ cwd: resolved.cwd,
158
+ sessionId: resolved.sessionId,
159
+ sessionLabel: label,
160
+ });
161
+ this.logger.info("Session renamed", {
162
+ sessionId: resolved.sessionId,
163
+ sessionIdDerived: resolved.sessionIdDerived,
164
+ sessionLabel: label,
165
+ });
166
+ return {
167
+ renamed: true,
168
+ session_id: resolved.sessionId,
169
+ session_label: label,
170
+ updated_at: updatedAt,
171
+ };
172
+ }
173
+ async getContext(input) {
174
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
175
+ const session = await this.sessionStore.getSession(resolved.sessionId);
176
+ const binding = await this.bindingStore.getBinding(resolved.sessionId);
177
+ const linkedSession = session?.linkedSessionId
178
+ ? await this.sessionStore.getSession(session.linkedSessionId)
179
+ : null;
180
+ this.logger.debug("Session context requested", {
181
+ sessionId: resolved.sessionId,
182
+ sessionIdDerived: resolved.sessionIdDerived,
183
+ exists: Boolean(session),
184
+ hasBinding: Boolean(binding),
185
+ });
186
+ const statusMessage = binding
187
+ ? session?.tmuxTarget
188
+ ? "Telegram pairing is active for this session. A tmux target is configured, so ordinary Telegram messages can wake the agent through tmux nudges."
189
+ : "Telegram pairing is active for this session. No tmux target is configured, so inbox handling requires passive MCP checks."
190
+ : session
191
+ ? "Session metadata exists, but Telegram pairing is not active."
192
+ : "Session metadata and Telegram pairing are both absent.";
193
+ return {
194
+ session_id: resolved.sessionId,
195
+ exists: Boolean(session),
196
+ has_binding: Boolean(binding),
197
+ status_message: statusMessage,
198
+ ...(session
199
+ ? {
200
+ context: {
201
+ ...(session.label ? { session_label: session.label } : {}),
202
+ ...(session.cwd ? { cwd: session.cwd } : {}),
203
+ ...(session.linkedSessionId
204
+ ? { linked_session_id: session.linkedSessionId }
205
+ : {}),
206
+ ...(linkedSession?.label
207
+ ? { linked_session_label: linkedSession.label }
208
+ : {}),
209
+ ...(session.activeProjectUuid
210
+ ? { active_project_uuid: session.activeProjectUuid }
211
+ : {}),
212
+ ...(session.activeProjectName
213
+ ? { active_project_name: session.activeProjectName }
214
+ : {}),
215
+ ...(session.task ? { task: session.task } : {}),
216
+ ...(session.summary ? { summary: session.summary } : {}),
217
+ ...(session.files ? { files: session.files } : {}),
218
+ ...(session.decisions ? { decisions: session.decisions } : {}),
219
+ ...(session.risks ? { risks: session.risks } : {}),
220
+ updated_at: session.updatedAt,
221
+ },
222
+ }
223
+ : {}),
224
+ ...(binding
225
+ ? {
226
+ binding: {
227
+ telegram_chat_id: binding.telegramChatId,
228
+ telegram_user_id: binding.telegramUserId,
229
+ ...(binding.telegramUsername
230
+ ? { telegram_username: binding.telegramUsername }
231
+ : {}),
232
+ linked_at: binding.linkedAt,
233
+ },
234
+ }
235
+ : {}),
236
+ ...(session
237
+ ? {
238
+ tmux: {
239
+ configured: Boolean(session.tmuxTarget),
240
+ ...(session.tmuxSessionName
241
+ ? { tmux_session_name: session.tmuxSessionName }
242
+ : {}),
243
+ ...(session.tmuxWindowName
244
+ ? { tmux_window_name: session.tmuxWindowName }
245
+ : {}),
246
+ ...(typeof session.tmuxWindowIndex === "number"
247
+ ? { tmux_window_index: session.tmuxWindowIndex }
248
+ : {}),
249
+ ...(session.tmuxPaneId
250
+ ? { tmux_pane_id: session.tmuxPaneId }
251
+ : {}),
252
+ ...(typeof session.tmuxPaneIndex === "number"
253
+ ? { tmux_pane_index: session.tmuxPaneIndex }
254
+ : {}),
255
+ ...(session.tmuxTarget
256
+ ? { tmux_target: session.tmuxTarget }
257
+ : {}),
258
+ ...(session.lastTmuxNudgeAt
259
+ ? { last_nudge_at: session.lastTmuxNudgeAt }
260
+ : {}),
261
+ },
262
+ }
263
+ : {}),
264
+ };
265
+ }
266
+ async clearContext(input) {
267
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
268
+ const existing = await this.sessionStore.getSession(resolved.sessionId);
269
+ await this.sessionStore.clearSession(resolved.sessionId);
270
+ await this.bindingStore.clearBinding(resolved.sessionId);
271
+ this.projectIdentityResolver.removeSessionMarker(existing?.cwd || resolved.cwd);
272
+ this.logger.info("Session context cleared", {
273
+ sessionId: resolved.sessionId,
274
+ sessionIdDerived: resolved.sessionIdDerived,
275
+ clearedPairing: true,
276
+ });
277
+ return {
278
+ cleared: true,
279
+ session_id: resolved.sessionId,
280
+ cleared_pairing: true,
281
+ };
282
+ }
283
+ async setTmuxTarget(input) {
284
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
285
+ const existing = await this.sessionStore.getSession(resolved.sessionId);
286
+ const updatedAt = new Date().toISOString();
287
+ const sanitizedTarget = (0, redactSecrets_1.redactSecrets)(input.tmux_target);
288
+ const sanitizedSessionName = input.tmux_session_name
289
+ ? (0, redactSecrets_1.redactSecrets)(input.tmux_session_name)
290
+ : existing?.tmuxSessionName;
291
+ const sanitizedWindowName = input.tmux_window_name
292
+ ? (0, redactSecrets_1.redactSecrets)(input.tmux_window_name)
293
+ : existing?.tmuxWindowName;
294
+ const sanitizedPaneId = input.tmux_pane_id
295
+ ? (0, redactSecrets_1.redactSecrets)(input.tmux_pane_id)
296
+ : existing?.tmuxPaneId;
297
+ await this.sessionStore.setSession({
298
+ sessionId: resolved.sessionId,
299
+ ...(existing?.label
300
+ ? { label: existing.label }
301
+ : resolved.sessionLabel
302
+ ? { label: (0, redactSecrets_1.redactSecrets)(resolved.sessionLabel) }
303
+ : {}),
304
+ ...(existing?.cwd ? { cwd: existing.cwd } : { cwd: resolved.cwd }),
305
+ ...(existing?.linkedSessionId
306
+ ? { linkedSessionId: existing.linkedSessionId }
307
+ : {}),
308
+ ...(existing?.activeProjectUuid
309
+ ? { activeProjectUuid: existing.activeProjectUuid }
310
+ : {}),
311
+ ...(existing?.activeProjectName
312
+ ? { activeProjectName: existing.activeProjectName }
313
+ : {}),
314
+ ...(existing?.task ? { task: existing.task } : {}),
315
+ ...(existing?.summary ? { summary: existing.summary } : {}),
316
+ ...(existing?.files ? { files: existing.files } : {}),
317
+ ...(existing?.decisions ? { decisions: existing.decisions } : {}),
318
+ ...(existing?.risks ? { risks: existing.risks } : {}),
319
+ ...(sanitizedSessionName
320
+ ? { tmuxSessionName: sanitizedSessionName }
321
+ : {}),
322
+ ...(sanitizedWindowName ? { tmuxWindowName: sanitizedWindowName } : {}),
323
+ ...(typeof input.tmux_window_index === "number"
324
+ ? { tmuxWindowIndex: input.tmux_window_index }
325
+ : typeof existing?.tmuxWindowIndex === "number"
326
+ ? { tmuxWindowIndex: existing.tmuxWindowIndex }
327
+ : {}),
328
+ ...(sanitizedPaneId ? { tmuxPaneId: sanitizedPaneId } : {}),
329
+ ...(typeof input.tmux_pane_index === "number"
330
+ ? { tmuxPaneIndex: input.tmux_pane_index }
331
+ : typeof existing?.tmuxPaneIndex === "number"
332
+ ? { tmuxPaneIndex: existing.tmuxPaneIndex }
333
+ : {}),
334
+ tmuxTarget: sanitizedTarget,
335
+ ...(existing?.lastTmuxNudgeAt
336
+ ? { lastTmuxNudgeAt: existing.lastTmuxNudgeAt }
337
+ : {}),
338
+ updatedAt,
339
+ });
340
+ this.projectIdentityResolver.persistSessionMarker({
341
+ cwd: resolved.cwd,
342
+ sessionId: resolved.sessionId,
343
+ sessionLabel: existing?.label || resolved.sessionLabel,
344
+ });
345
+ this.logger.info("Session tmux target saved", {
346
+ sessionId: resolved.sessionId,
347
+ sessionIdDerived: resolved.sessionIdDerived,
348
+ tmuxSessionName: sanitizedSessionName,
349
+ tmuxWindowName: sanitizedWindowName,
350
+ tmuxWindowIndex: input.tmux_window_index,
351
+ tmuxPaneId: sanitizedPaneId,
352
+ tmuxPaneIndex: input.tmux_pane_index,
353
+ tmuxTarget: sanitizedTarget,
354
+ });
355
+ return {
356
+ session_id: resolved.sessionId,
357
+ tmux_target: sanitizedTarget,
358
+ ...(sanitizedSessionName
359
+ ? { tmux_session_name: sanitizedSessionName }
360
+ : {}),
361
+ ...(sanitizedWindowName
362
+ ? { tmux_window_name: sanitizedWindowName }
363
+ : {}),
364
+ ...(typeof input.tmux_window_index === "number"
365
+ ? { tmux_window_index: input.tmux_window_index }
366
+ : {}),
367
+ ...(sanitizedPaneId ? { tmux_pane_id: sanitizedPaneId } : {}),
368
+ ...(typeof input.tmux_pane_index === "number"
369
+ ? { tmux_pane_index: input.tmux_pane_index }
370
+ : {}),
371
+ status_message: "tmux target saved for this session. For a paired session, the service can now nudge this tmux pane when a new non-reply Telegram message is stored in inbox.",
372
+ };
373
+ }
374
+ async getTmuxTarget(input) {
375
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
376
+ const session = await this.sessionStore.getSession(resolved.sessionId);
377
+ const configured = Boolean(session?.tmuxTarget);
378
+ this.logger.debug("Session tmux target requested", {
379
+ sessionId: resolved.sessionId,
380
+ sessionIdDerived: resolved.sessionIdDerived,
381
+ configured,
382
+ });
383
+ return {
384
+ session_id: resolved.sessionId,
385
+ configured,
386
+ ...(session?.tmuxTarget ? { tmux_target: session.tmuxTarget } : {}),
387
+ ...(session?.tmuxSessionName
388
+ ? { tmux_session_name: session.tmuxSessionName }
389
+ : {}),
390
+ ...(session?.tmuxWindowName
391
+ ? { tmux_window_name: session.tmuxWindowName }
392
+ : {}),
393
+ ...(typeof session?.tmuxWindowIndex === "number"
394
+ ? { tmux_window_index: session.tmuxWindowIndex }
395
+ : {}),
396
+ ...(session?.tmuxPaneId ? { tmux_pane_id: session.tmuxPaneId } : {}),
397
+ ...(typeof session?.tmuxPaneIndex === "number"
398
+ ? { tmux_pane_index: session.tmuxPaneIndex }
399
+ : {}),
400
+ ...(session?.lastTmuxNudgeAt
401
+ ? { last_nudge_at: session.lastTmuxNudgeAt }
402
+ : {}),
403
+ status_message: configured
404
+ ? "tmux target is configured for this session."
405
+ : "tmux target is not configured for this session.",
406
+ };
407
+ }
408
+ }
409
+ exports.SessionContextService = SessionContextService;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SetSessionContextTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify(output, null, 2),
10
+ },
11
+ ];
12
+ }
13
+ class SetSessionContextTool {
14
+ sessionContextService;
15
+ constructor(sessionContextService) {
16
+ this.sessionContextService = sessionContextService;
17
+ }
18
+ register(server) {
19
+ server.registerTool("set_session_context", {
20
+ title: "Set Session Context",
21
+ description: "Save compact reusable context for the session: current task, summary, decisions, files, and risks. Use this to leave a short durable working state for later Telegram-driven continuation.",
22
+ inputSchema: schema_1.setSessionContextInputSchema,
23
+ outputSchema: schema_1.setSessionContextOutputSchema,
24
+ }, async (args) => {
25
+ const output = await this.sessionContextService.setContext(args);
26
+ return {
27
+ content: createContent(output),
28
+ structuredContent: output,
29
+ };
30
+ });
31
+ }
32
+ }
33
+ exports.SetSessionContextTool = SetSessionContextTool;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SetTmuxTargetTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify(output, null, 2),
10
+ },
11
+ ];
12
+ }
13
+ class SetTmuxTargetTool {
14
+ sessionContextService;
15
+ constructor(sessionContextService) {
16
+ this.sessionContextService = sessionContextService;
17
+ }
18
+ register(server) {
19
+ server.registerTool("set_tmux_target", {
20
+ title: "Set tmux Target",
21
+ description: "Use this to repair or complete Telegram session setup after pairing, or when tmux pane/session changed. Store the tmux pane target for a session so the long-running MCP service can nudge the agent to check Telegram inbox when new ordinary Telegram messages arrive.",
22
+ inputSchema: schema_1.setTmuxTargetInputSchema,
23
+ outputSchema: schema_1.setTmuxTargetOutputSchema,
24
+ }, async (args) => {
25
+ const output = await this.sessionContextService.setTmuxTarget(args);
26
+ return {
27
+ content: createContent(output),
28
+ structuredContent: output,
29
+ };
30
+ });
31
+ }
32
+ }
33
+ exports.SetTmuxTargetTool = SetTmuxTargetTool;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RefreshToolsMarkdownService = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ function normalizeGatewayBaseUrl(value) {
8
+ const url = new URL(value);
9
+ url.pathname = url.pathname.replace(/\/+$/u, "");
10
+ if (!url.pathname.endsWith("/gateway")) {
11
+ url.pathname = `${url.pathname}/gateway`.replace(/\/{2,}/gu, "/");
12
+ }
13
+ return url;
14
+ }
15
+ function computeContentHash(content) {
16
+ return (0, node_crypto_1.createHash)("sha256").update(content).digest("hex");
17
+ }
18
+ class RefreshToolsMarkdownService {
19
+ config;
20
+ sessionStore;
21
+ logger;
22
+ projectIdentityResolver;
23
+ constructor(config, sessionStore, logger, projectIdentityResolver) {
24
+ this.config = config;
25
+ this.sessionStore = sessionStore;
26
+ this.logger = logger;
27
+ this.projectIdentityResolver = projectIdentityResolver;
28
+ }
29
+ async refresh(input = {}) {
30
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
31
+ const session = await this.sessionStore.getSession(resolved.sessionId);
32
+ const workspaceDir = input.cwd?.trim()
33
+ ? (0, node_path_1.resolve)(input.cwd.trim())
34
+ : session?.cwd?.trim()
35
+ ? (0, node_path_1.resolve)(session.cwd.trim())
36
+ : undefined;
37
+ const saveLocally = input.save_locally !== false;
38
+ const gatewayToolsPath = (0, node_path_1.join)(process.cwd(), "TOOLS.md");
39
+ let source = "local";
40
+ let content;
41
+ if (this.config.distributed.gatewayPublicUrl) {
42
+ const url = normalizeGatewayBaseUrl(this.config.distributed.gatewayPublicUrl);
43
+ url.pathname = `${url.pathname}/tools-md`.replace(/\/{2,}/gu, "/");
44
+ const response = await fetch(url, {
45
+ method: "GET",
46
+ headers: {
47
+ ...(this.config.distributed.gatewayAuthToken
48
+ ? { authorization: `Bearer ${this.config.distributed.gatewayAuthToken}` }
49
+ : {}),
50
+ },
51
+ });
52
+ if (!response.ok) {
53
+ const message = await response.text();
54
+ throw new Error(`Gateway TOOLS.md request failed with status ${response.status}: ${message || response.statusText}`);
55
+ }
56
+ content = await response.text();
57
+ source = "gateway";
58
+ }
59
+ else {
60
+ content = (0, node_fs_1.readFileSync)(gatewayToolsPath, "utf8");
61
+ }
62
+ if (!workspaceDir) {
63
+ throw new Error("Could not resolve target workspace for TOOLS.md. Pair the session with cwd first or pass cwd/session_id explicitly.");
64
+ }
65
+ const toolsPath = (0, node_path_1.join)(workspaceDir, "TOOLS.md");
66
+ if (saveLocally) {
67
+ (0, node_fs_1.writeFileSync)(toolsPath, content, "utf8");
68
+ const appliedHash = computeContentHash(content);
69
+ await this.sessionStore.setSession({
70
+ sessionId: resolved.sessionId,
71
+ ...(session?.label ? { label: session.label } : {}),
72
+ ...(session?.cwd ? { cwd: session.cwd } : { cwd: workspaceDir }),
73
+ ...(session?.linkedSessionId
74
+ ? { linkedSessionId: session.linkedSessionId }
75
+ : {}),
76
+ ...(session?.activeProjectUuid
77
+ ? { activeProjectUuid: session.activeProjectUuid }
78
+ : {}),
79
+ ...(session?.activeProjectName
80
+ ? { activeProjectName: session.activeProjectName }
81
+ : {}),
82
+ ...(session?.task ? { task: session.task } : {}),
83
+ ...(session?.summary ? { summary: session.summary } : {}),
84
+ ...(session?.files ? { files: session.files } : {}),
85
+ ...(session?.decisions ? { decisions: session.decisions } : {}),
86
+ ...(session?.risks ? { risks: session.risks } : {}),
87
+ ...(session?.tmuxSessionName
88
+ ? { tmuxSessionName: session.tmuxSessionName }
89
+ : {}),
90
+ ...(session?.tmuxWindowName
91
+ ? { tmuxWindowName: session.tmuxWindowName }
92
+ : {}),
93
+ ...(typeof session?.tmuxWindowIndex === "number"
94
+ ? { tmuxWindowIndex: session.tmuxWindowIndex }
95
+ : {}),
96
+ ...(session?.tmuxPaneId ? { tmuxPaneId: session.tmuxPaneId } : {}),
97
+ ...(typeof session?.tmuxPaneIndex === "number"
98
+ ? { tmuxPaneIndex: session.tmuxPaneIndex }
99
+ : {}),
100
+ ...(session?.tmuxTarget ? { tmuxTarget: session.tmuxTarget } : {}),
101
+ ...(session?.lastTmuxNudgeAt
102
+ ? { lastTmuxNudgeAt: session.lastTmuxNudgeAt }
103
+ : {}),
104
+ lastSeenToolsHash: appliedHash,
105
+ lastNotifiedToolsHash: appliedHash,
106
+ updatedAt: new Date().toISOString(),
107
+ });
108
+ }
109
+ this.logger.info("TOOLS.md refreshed", {
110
+ source,
111
+ saved: saveLocally,
112
+ path: toolsPath,
113
+ bytes: Buffer.byteLength(content, "utf8"),
114
+ });
115
+ return {
116
+ source,
117
+ saved: saveLocally,
118
+ bytes: Buffer.byteLength(content, "utf8"),
119
+ ...(saveLocally ? { path: toolsPath } : {}),
120
+ };
121
+ }
122
+ }
123
+ exports.RefreshToolsMarkdownService = RefreshToolsMarkdownService;