@deadragdoll/tellymcp 0.0.10 → 0.0.11

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 (153) hide show
  1. package/.env.example.client +42 -52
  2. package/.env.example.gateway +48 -62
  3. package/CHANGELOG.md +3 -3
  4. package/README-ru.md +205 -384
  5. package/README.md +195 -1194
  6. package/TOOLS.md +294 -377
  7. package/VERSION.md +11 -11
  8. package/config/codex/plugins/telly-workflows/.codex-plugin/plugin.json +18 -0
  9. package/config/codex/plugins/telly-workflows/references/invariants.md +10 -0
  10. package/config/codex/plugins/telly-workflows/skills/telly-browser-screenshot/SKILL.md +27 -0
  11. package/config/codex/plugins/telly-workflows/skills/telly-collab-artifact/SKILL.md +22 -0
  12. package/config/codex/plugins/telly-workflows/skills/telly-human-telegram/SKILL.md +31 -0
  13. package/config/codex/plugins/telly-workflows/skills/telly-partner-note/SKILL.md +36 -0
  14. package/config/templates/env.both.template +39 -20
  15. package/config/templates/env.client.template +34 -19
  16. package/config/templates/env.gateway.template +37 -22
  17. package/dist/cli.js +322 -75
  18. package/dist/codexPluginInstaller.js +215 -0
  19. package/dist/lib/mixins/session.errors.js +34 -1
  20. package/dist/lib/pinoTargets.js +2 -2
  21. package/dist/moleculer.config.js +7 -5
  22. package/dist/services/features/telegram-mcp/browser.service.js +94 -1
  23. package/dist/services/features/telegram-mcp/collaboration.service.js +40 -3
  24. package/dist/services/features/telegram-mcp/ensuredb.service.js +145 -26
  25. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +285 -103
  26. package/dist/services/features/telegram-mcp/gateway-socket.service.js +897 -85
  27. package/dist/services/features/telegram-mcp/gateway.service.js +876 -81
  28. package/dist/services/features/telegram-mcp/mcp-http.service.js +8 -0
  29. package/dist/services/features/telegram-mcp/mcp-server.service.js +17 -26
  30. package/dist/services/features/telegram-mcp/notify.service.js +127 -1
  31. package/dist/services/features/telegram-mcp/session-context.service.js +28 -1
  32. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +132 -10
  33. package/dist/services/features/telegram-mcp/src/app/config/env.js +158 -52
  34. package/dist/services/features/telegram-mcp/src/app/http.js +375 -42
  35. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +386 -58
  36. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +3 -0
  37. package/dist/services/features/telegram-mcp/src/app/webapp/terminal.js +12 -0
  38. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +116 -104
  39. package/dist/services/features/telegram-mcp/src/entities/xchange/model/types.js +2 -0
  40. package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +1 -1
  41. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
  42. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +249 -19
  43. package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +71 -0
  44. package/dist/services/features/telegram-mcp/src/features/collaboration/model/listGatewaySessionsTool.js +33 -0
  45. package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +93 -82
  46. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +47 -5
  47. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +1 -1
  48. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +1 -1
  49. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +82 -0
  50. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +22 -14
  51. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +649 -75
  52. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +76 -0
  53. package/dist/services/features/telegram-mcp/src/features/embedded-runtime/model/embeddedRuntimeBroker.js +92 -0
  54. package/dist/services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js +192 -0
  55. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +382 -3
  56. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +1 -1
  57. package/dist/services/features/telegram-mcp/src/features/notify/model/sendFileToTelegramTool.js +33 -0
  58. package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +1 -1
  59. package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +1 -1
  60. package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +1 -1
  61. package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +42 -200
  62. package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +1 -1
  63. package/dist/services/features/telegram-mcp/src/features/terminal-buffer/model/terminalBufferService.js +96 -0
  64. package/dist/services/features/telegram-mcp/src/features/terminal-input/model/terminalInputService.js +97 -0
  65. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +107 -58
  66. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +1 -1
  67. package/dist/services/features/telegram-mcp/src/features/xchange/model/getXchangeRecordTool.js +28 -0
  68. package/dist/services/features/telegram-mcp/src/features/xchange/model/listXchangeRecordsTool.js +28 -0
  69. package/dist/services/features/telegram-mcp/src/features/xchange/model/markXchangeRecordReadTool.js +28 -0
  70. package/dist/services/features/telegram-mcp/src/features/xchange/model/xchangeService.js +169 -0
  71. package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +5 -4
  72. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +139 -42
  73. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +135 -38
  74. package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalSessionStore.js +27 -0
  75. package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +11 -8
  76. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +42 -71
  77. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +21 -22
  78. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +415 -7101
  79. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportAttachmentStore.js +93 -0
  80. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportBroadcastActions.js +385 -0
  81. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +143 -0
  82. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +633 -0
  83. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContent.js +84 -0
  84. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContext.js +78 -0
  85. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportDocumentActions.js +36 -0
  86. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportEventActions.js +292 -0
  87. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +352 -0
  88. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFormatting.js +75 -0
  89. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportGatewayActions.js +44 -0
  90. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLifecycleActions.js +161 -0
  91. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLive.js +56 -0
  92. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLiveActions.js +77 -0
  93. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuCallbacks.js +254 -0
  94. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFactories.js +538 -0
  95. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFingerprints.js +93 -0
  96. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFlow.js +344 -0
  97. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +62 -0
  98. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuState.js +408 -0
  99. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuText.js +216 -0
  100. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +452 -0
  101. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportOutputActions.js +189 -0
  102. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPartnerActions.js +286 -0
  103. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +101 -0
  104. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectActions.js +463 -0
  105. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEntryActions.js +202 -0
  106. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEvents.js +99 -0
  107. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectMenus.js +138 -0
  108. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +308 -0
  109. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectView.js +426 -0
  110. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportRequestFlow.js +278 -0
  111. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportSessionActions.js +143 -0
  112. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +468 -0
  113. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +171 -0
  114. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTypes.js +2 -0
  115. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js +330 -0
  116. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportXchangeState.js +107 -0
  117. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +255 -0
  118. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +543 -0
  119. package/dist/services/features/telegram-mcp/src/shared/integrations/xchange/sqliteRecordStore.js +223 -0
  120. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +24 -0
  121. package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +6 -0
  122. package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +147 -76
  123. package/dist/services/features/telegram-mcp/src/shared/lib/telegramXchangeRecords.js +72 -0
  124. package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +237 -0
  125. package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +129 -1
  126. package/dist/services/features/telegram-mcp/src/shared/lib/xchangeRecordHints.js +98 -0
  127. package/dist/services/features/telegram-mcp/standalone-http.service.js +12 -0
  128. package/dist/services/features/telegram-mcp/terminal-buffer.service.js +42 -0
  129. package/dist/services/features/telegram-mcp/terminal-input.service.js +41 -0
  130. package/dist/services/features/telegram-mcp/tools-sync.service.js +15 -1
  131. package/dist/services/features/telegram-mcp/xchange.service.js +123 -0
  132. package/docs/STANDALONE-ru.md +172 -0
  133. package/docs/STANDALONE.md +172 -0
  134. package/package.json +10 -5
  135. package/scripts/build-package-artifact.sh +27 -0
  136. package/scripts/deploy-gateway.sh +64 -0
  137. package/scripts/deploy-onebot-branch.sh +27 -0
  138. package/scripts/postinstall.js +11 -36
  139. package/STANDALONE-ru.md +0 -274
  140. package/STANDALONE.md +0 -274
  141. package/dist/services/features/telegram-mcp/inbox.service.js +0 -33
  142. package/dist/services/features/telegram-mcp/pair.service.js +0 -33
  143. package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +0 -10
  144. package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +0 -33
  145. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +0 -33
  146. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +0 -33
  147. package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +0 -77
  148. package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +0 -33
  149. package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +0 -33
  150. package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +0 -202
  151. package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +0 -33
  152. package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +0 -33
  153. package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +0 -363
@@ -5,8 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.LocalCollaborationBackend = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
- const ids_1 = require("../../../shared/lib/ids/ids");
9
- const client_1 = require("../../../shared/integrations/tmux/client");
8
+ const xchangeRecordHints_1 = require("../../../shared/lib/xchangeRecordHints");
9
+ const client_1 = require("../../../shared/integrations/terminal/client");
10
+ const sqliteRecordStore_1 = require("../../../shared/integrations/xchange/sqliteRecordStore");
10
11
  function slugify(input) {
11
12
  return input
12
13
  .trim()
@@ -49,7 +50,6 @@ const SOURCE_ARTIFACT_EXTENSIONS = new Set([
49
50
  ".xml",
50
51
  ".zsh",
51
52
  ]);
52
- const PARTNER_INDEX_FILE_NAME = "SHARED_INDEX.md";
53
53
  function trimOptional(value) {
54
54
  const trimmed = value?.trim();
55
55
  return trimmed ? trimmed : undefined;
@@ -78,35 +78,6 @@ function renderYamlArray(values) {
78
78
  }
79
79
  return `\n${values.map((value) => ` - ${JSON.stringify(value)}`).join("\n")}`;
80
80
  }
81
- function buildPartnerInboxText(input) {
82
- const kindTitle = input.kind === "question"
83
- ? "Partner question received."
84
- : input.kind === "reply"
85
- ? "Partner reply received."
86
- : input.kind === "request"
87
- ? "Partner request received."
88
- : input.kind === "handoff"
89
- ? "Partner handoff received."
90
- : "Partner update received.";
91
- return [
92
- kindTitle,
93
- `From: ${input.fromLabel}`,
94
- `Summary: ${input.summary}`,
95
- "",
96
- `Immediate action: read ${PARTNER_INDEX_FILE_NAME} and then open the note below.`,
97
- `Note: ${input.notePath}`,
98
- ...(input.copiedArtifacts.length > 0
99
- ? ["", "Artifacts:", ...input.copiedArtifacts.map((item) => `- ${item}`)]
100
- : []),
101
- ...(input.requiresReply
102
- ? [
103
- "",
104
- "Reply through send_partner_note after you finish the requested work.",
105
- "The task is not complete until send_partner_note succeeds.",
106
- ]
107
- : []),
108
- ].join("\n");
109
- }
110
81
  function buildNoteContent(input) {
111
82
  const lines = ["---"];
112
83
  lines.push(`kind: ${JSON.stringify(input.kind)}`);
@@ -129,38 +100,26 @@ function buildNoteContent(input) {
129
100
  lines.push("", "# Expected Reply", input.expectedReply.trim());
130
101
  }
131
102
  if (input.requiresReply) {
132
- lines.push("", "# Action Required", "You must send a reply via send_partner_note.", "Your task is not complete until send_partner_note succeeds.", "Do not stop after local analysis or a chat explanation.", "Use the current partner route for the reply.", "Only after the tool succeeds may you say that the reply was sent.", "", "# Reply Tool Call Example", "send_partner_note(", ` session_id=${JSON.stringify(input.targetSessionId)},`, ` kind=${JSON.stringify("reply")},`, " summary=\"Короткий итог\",", " message=\"Подробный ответ\"", ")");
103
+ lines.push("", "# Action Required", "You must send a reply via send_partner_note.", "Do not stop after reading, listing files, or explaining what you found.", "Your task is not complete until send_partner_note succeeds.", "Do not stop after local analysis or a chat explanation.", "Only after send_partner_note succeeds may you mark the record as read or say the work is done.", "Use the current partner route for the reply.", "Only after the tool succeeds may you say that the reply was sent.", "", "# Reply Tool Call Example", "send_partner_note(", ` session_id=${JSON.stringify(input.targetSessionId)},`, ` kind=${JSON.stringify("reply")},`, " summary=\"Короткий итог\",", " message=\"Подробный ответ\"", ")");
133
104
  }
134
105
  if (input.copiedArtifacts.length > 0) {
135
106
  lines.push("", "# Artifacts", ...input.copiedArtifacts.map((artifact) => `- ${artifact}`));
136
107
  }
137
108
  return `${lines.join("\n")}\n`;
138
109
  }
139
- function buildShareIndexLine(input) {
140
- return [
141
- "-",
142
- `[${input.createdAt}]`,
143
- `${input.sourceLabel} → ${input.targetLabel}`,
144
- `| ${input.kind} |`,
145
- `${input.summary}`,
146
- `| \`${input.relativeNotePath}\``,
147
- ].join(" ");
148
- }
149
110
  class LocalCollaborationBackend {
150
111
  config;
151
112
  sessionStore;
152
113
  bindingStore;
153
- inboxStore;
154
114
  xchangeFileMetaStore;
155
115
  objectStore;
156
116
  telegramTransport;
157
117
  logger;
158
118
  textEncoder = new TextEncoder();
159
- constructor(config, sessionStore, bindingStore, inboxStore, xchangeFileMetaStore, objectStore, telegramTransport, logger) {
119
+ constructor(config, sessionStore, bindingStore, xchangeFileMetaStore, objectStore, telegramTransport, logger) {
160
120
  this.config = config;
161
121
  this.sessionStore = sessionStore;
162
122
  this.bindingStore = bindingStore;
163
- this.inboxStore = inboxStore;
164
123
  this.xchangeFileMetaStore = xchangeFileMetaStore;
165
124
  this.objectStore = objectStore;
166
125
  this.telegramTransport = telegramTransport;
@@ -169,11 +128,11 @@ class LocalCollaborationBackend {
169
128
  async sendPartnerNote(input, resolved) {
170
129
  const sourceSession = await this.sessionStore.getSession(resolved.sessionId);
171
130
  if (!sourceSession) {
172
- throw new Error(`Session ${resolved.sessionId} was not found. Pair the session before collaborating.`);
131
+ throw new Error(`Session ${resolved.sessionId} was not found.`);
173
132
  }
174
- const targetSessionId = trimOptional(input.target_session_id) ?? sourceSession.linkedSessionId;
133
+ const targetSessionId = trimOptional(input.target_session_id);
175
134
  if (!targetSessionId) {
176
- throw new Error("This session has no linked partner. Link another session in Telegram first.");
135
+ throw new Error("target_session_id is required for send_partner_note.");
177
136
  }
178
137
  const targetSession = await this.sessionStore.getSession(targetSessionId);
179
138
  if (!targetSession) {
@@ -181,7 +140,7 @@ class LocalCollaborationBackend {
181
140
  }
182
141
  const targetBinding = await this.bindingStore.getBinding(targetSession.sessionId);
183
142
  if (!targetBinding) {
184
- throw new Error(`Linked partner session ${targetSession.sessionId} is not paired with Telegram.`);
143
+ throw new Error(`Target session ${targetSession.sessionId} has no active Telegram route.`);
185
144
  }
186
145
  const now = new Date();
187
146
  const createdAt = now.toISOString();
@@ -210,7 +169,7 @@ class LocalCollaborationBackend {
210
169
  message: input.message,
211
170
  ...(expectedReply ? { expectedReply } : {}),
212
171
  });
213
- const notePath = await (0, client_1.writeXchangeRelativeFile)(this.config.tmux, targetWorkspaceDir, this.config.exchange.dir, relativeNotePath, this.textEncoder.encode(noteContent));
172
+ const notePath = await (0, client_1.writeXchangeRelativeFile)(this.config.terminal, targetWorkspaceDir, this.config.exchange.dir, relativeNotePath, this.textEncoder.encode(noteContent));
214
173
  await this.xchangeFileMetaStore.setXchangeFileMeta({
215
174
  sessionId: targetSession.sessionId,
216
175
  filePath: notePath,
@@ -220,32 +179,81 @@ class LocalCollaborationBackend {
220
179
  mimeType: "text/markdown",
221
180
  sizeBytes: this.textEncoder.encode(noteContent).byteLength,
222
181
  });
223
- const shareIndexPath = await (0, client_1.writeXchangeRelativeFile)(this.config.tmux, targetWorkspaceDir, this.config.exchange.dir, PARTNER_INDEX_FILE_NAME, this.textEncoder.encode(`${buildShareIndexLine({
224
- createdAt,
225
- sourceLabel,
226
- targetLabel,
182
+ const targetActionDesc = (0, xchangeRecordHints_1.buildIncomingPartnerActionDesc)(input.kind, requiresReply);
183
+ const targetTools = (0, xchangeRecordHints_1.buildIncomingPartnerTools)(input.kind, requiresReply);
184
+ await (0, sqliteRecordStore_1.upsertXchangeRecord)(this.config.terminal, targetWorkspaceDir, this.config.exchange.dir, {
185
+ record_id: shareId,
186
+ session_id: targetSession.sessionId,
187
+ category: "partner_note",
188
+ direction: "incoming",
189
+ status: "new",
227
190
  kind: input.kind,
228
191
  summary: input.summary.trim(),
229
- relativeNotePath,
230
- })}\n`), { append: true });
231
- const inboxMessage = {
232
- id: (0, ids_1.createInboxMessageId)(now),
233
- sessionId: targetSession.sessionId,
234
- telegramChatId: targetBinding.telegramChatId,
235
- telegramUserId: targetBinding.telegramUserId,
236
- sourceTelegramMessageId: now.getTime(),
237
- text: buildPartnerInboxText({
238
- kind: input.kind,
239
- fromLabel: sourceLabel,
240
- summary: input.summary.trim(),
241
- notePath,
242
- requiresReply,
243
- copiedArtifacts,
244
- }),
245
- attachments: [notePath, ...copiedArtifacts],
246
- receivedAt: createdAt,
247
- };
248
- await this.inboxStore.createInboxMessage(inboxMessage);
192
+ body_text: noteContent,
193
+ action_desc: targetActionDesc,
194
+ tools: targetTools,
195
+ note_path: notePath,
196
+ note_relative_path: relativeNotePath,
197
+ source_session_id: sourceSession.sessionId,
198
+ source_label: sourceLabel,
199
+ target_session_id: targetSession.sessionId,
200
+ target_label: targetLabel,
201
+ requires_reply: requiresReply,
202
+ ...(inReplyTo ? { in_reply_to: inReplyTo } : {}),
203
+ ...(expectedReply ? { expected_reply: expectedReply } : {}),
204
+ attachments: [
205
+ {
206
+ file_path: notePath,
207
+ relative_path: relativeNotePath,
208
+ original_name: node_path_1.default.basename(relativeNotePath),
209
+ mime_type: "text/markdown",
210
+ size_bytes: this.textEncoder.encode(noteContent).byteLength,
211
+ },
212
+ ...copiedArtifacts.map((filePath) => ({
213
+ file_path: filePath,
214
+ })),
215
+ ],
216
+ tags: [
217
+ "partner",
218
+ input.kind,
219
+ ...(requiresReply ? ["requires-reply"] : []),
220
+ ],
221
+ created_at: createdAt,
222
+ updated_at: createdAt,
223
+ });
224
+ await (0, sqliteRecordStore_1.upsertXchangeRecord)(this.config.terminal, sourceWorkspaceDir, this.config.exchange.dir, {
225
+ record_id: shareId,
226
+ session_id: sourceSession.sessionId,
227
+ category: "partner_note",
228
+ direction: "outgoing",
229
+ status: "read",
230
+ kind: input.kind,
231
+ summary: input.summary.trim(),
232
+ body_text: noteContent,
233
+ action_desc: (0, xchangeRecordHints_1.buildOutgoingPartnerActionDesc)(input.kind, requiresReply),
234
+ tools: (0, xchangeRecordHints_1.buildOutgoingPartnerTools)(input.kind, requiresReply),
235
+ note_path: notePath,
236
+ note_relative_path: relativeNotePath,
237
+ source_session_id: sourceSession.sessionId,
238
+ source_label: sourceLabel,
239
+ target_session_id: targetSession.sessionId,
240
+ target_label: targetLabel,
241
+ requires_reply: requiresReply,
242
+ ...(inReplyTo ? { in_reply_to: inReplyTo } : {}),
243
+ ...(expectedReply ? { expected_reply: expectedReply } : {}),
244
+ attachments: copiedArtifacts.map((filePath) => ({
245
+ file_path: filePath,
246
+ })),
247
+ tags: [
248
+ "partner",
249
+ "outgoing",
250
+ input.kind,
251
+ ...(requiresReply ? ["awaiting-reply"] : []),
252
+ ],
253
+ created_at: createdAt,
254
+ updated_at: createdAt,
255
+ read_at: createdAt,
256
+ });
249
257
  await this.telegramTransport.sendNotification({
250
258
  sessionId: targetSession.sessionId,
251
259
  sessionLabel: sourceLabel,
@@ -280,10 +288,13 @@ class LocalCollaborationBackend {
280
288
  ].join("\n"),
281
289
  });
282
290
  try {
283
- await this.telegramTransport.nudgeSessionPartnerNote(targetSession.sessionId);
291
+ await this.telegramTransport.nudgeSessionPartnerNote(targetSession.sessionId, {
292
+ kind: input.kind,
293
+ requiresReply,
294
+ });
284
295
  }
285
296
  catch (error) {
286
- this.logger.warn("tmux nudge failed after local partner delivery", {
297
+ this.logger.warn("terminal nudge failed after local partner delivery", {
287
298
  sessionId: targetSession.sessionId,
288
299
  partnerSessionId: sourceSession.sessionId,
289
300
  shareId,
@@ -307,9 +318,9 @@ class LocalCollaborationBackend {
307
318
  share_id: shareId,
308
319
  delivery_status: "delivered",
309
320
  note_path: notePath,
310
- share_index_path: shareIndexPath,
321
+ xchange_record_id: shareId,
311
322
  copied_artifacts: copiedArtifacts,
312
- inbox_message_id: inboxMessage.id,
323
+ inbox_message_id: shareId,
313
324
  requires_reply: requiresReply,
314
325
  };
315
326
  }
@@ -318,7 +329,7 @@ class LocalCollaborationBackend {
318
329
  if (workspaceDir) {
319
330
  return workspaceDir;
320
331
  }
321
- return process.cwd();
332
+ throw new Error(`Workspace cwd is not registered for console '${session.sessionId}'.`);
322
333
  }
323
334
  async copyArtifactsToPartner(sourceSession, targetSession, sourceWorkspaceDir, targetWorkspaceDir, shareId, uploadedAt, artifacts) {
324
335
  const copied = [];
@@ -351,8 +362,8 @@ class LocalCollaborationBackend {
351
362
  source: sourceMeta.source,
352
363
  })
353
364
  : artifactPath;
354
- const content = await (0, client_1.readWorkspaceFile)(this.config.tmux, sourceWorkspaceDir, ensuredArtifactPath);
355
- const materializedArtifactPath = await (0, client_1.writeXchangeRelativeFile)(this.config.tmux, targetWorkspaceDir, this.config.exchange.dir, relativeArtifactPath, content);
365
+ const content = await (0, client_1.readWorkspaceFile)(this.config.terminal, sourceWorkspaceDir, ensuredArtifactPath);
366
+ const materializedArtifactPath = await (0, client_1.writeXchangeRelativeFile)(this.config.terminal, targetWorkspaceDir, this.config.exchange.dir, relativeArtifactPath, content);
356
367
  await this.xchangeFileMetaStore.setXchangeFileMeta({
357
368
  sessionId: targetSession.sessionId,
358
369
  filePath: materializedArtifactPath,
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SendPartnerFileService = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const mime_types_1 = require("mime-types");
9
- const client_1 = require("../../../shared/integrations/tmux/client");
9
+ const client_1 = require("../../../shared/integrations/terminal/client");
10
10
  function resolveWorkspaceDir(input) {
11
11
  if (input.inputCwd?.trim()) {
12
12
  return node_path_1.default.resolve(input.inputCwd.trim());
@@ -36,33 +36,53 @@ function normalizeWorkspaceRelativePath(workspaceDir, filePath) {
36
36
  class SendPartnerFileService {
37
37
  config;
38
38
  sessionStore;
39
+ maintenanceStore;
39
40
  logger;
40
41
  projectIdentityResolver;
41
42
  collaborationService;
42
- constructor(config, sessionStore, logger, projectIdentityResolver, collaborationService) {
43
+ remoteConsoleInvoker;
44
+ constructor(config, sessionStore, maintenanceStore, logger, projectIdentityResolver, collaborationService, remoteConsoleInvoker) {
43
45
  this.config = config;
44
46
  this.sessionStore = sessionStore;
47
+ this.maintenanceStore = maintenanceStore;
45
48
  this.logger = logger;
46
49
  this.projectIdentityResolver = projectIdentityResolver;
47
50
  this.collaborationService = collaborationService;
51
+ this.remoteConsoleInvoker = remoteConsoleInvoker;
48
52
  }
49
53
  async send(input) {
50
54
  const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
51
- const session = await this.sessionStore.getSession(resolved.sessionId);
55
+ const sessionId = await this.normalizeSessionIdForWorkspace(resolved.sessionId);
56
+ const remote = this.config.distributed.mode !== "client"
57
+ ? await this.remoteConsoleInvoker?.invokeForRelaySession(sessionId, "telegramMcp.collaboration.sendPartnerFileRemote", {
58
+ ...input,
59
+ session_id: sessionId,
60
+ })
61
+ : null;
62
+ if (remote) {
63
+ return remote;
64
+ }
65
+ const session = await this.sessionStore.getSession(sessionId);
52
66
  const workspaceDir = resolveWorkspaceDir({
53
67
  inputCwd: input.cwd,
54
68
  sessionCwd: session?.cwd,
55
69
  resolvedCwd: resolved.cwd,
56
70
  });
57
71
  const relativeFilePath = normalizeWorkspaceRelativePath(workspaceDir, input.file_path);
58
- const fileContent = await (0, client_1.readWorkspaceFile)(this.config.tmux, workspaceDir, relativeFilePath);
72
+ const fileContent = await (0, client_1.readWorkspaceFile)(this.config.terminal, workspaceDir, relativeFilePath);
59
73
  const originalName = node_path_1.default.basename(relativeFilePath);
60
74
  const mimeType = (0, mime_types_1.lookup)(originalName) || "application/octet-stream";
61
75
  const output = await this.collaborationService.sendPartnerNote({
62
- session_id: resolved.sessionId,
76
+ session_id: sessionId,
63
77
  ...(input.target_session_id?.trim()
64
78
  ? { target_session_id: input.target_session_id.trim() }
65
79
  : {}),
80
+ ...(input.target_client_uuid?.trim()
81
+ ? { target_client_uuid: input.target_client_uuid.trim() }
82
+ : {}),
83
+ ...(input.target_local_session_id?.trim()
84
+ ? { target_local_session_id: input.target_local_session_id.trim() }
85
+ : {}),
66
86
  ...(input.project_uuid?.trim()
67
87
  ? { project_uuid: input.project_uuid.trim() }
68
88
  : {}),
@@ -98,5 +118,27 @@ class SendPartnerFileService {
98
118
  });
99
119
  return output;
100
120
  }
121
+ async normalizeSessionIdForWorkspace(sessionId) {
122
+ const trimmed = sessionId.trim();
123
+ if (!trimmed) {
124
+ return trimmed;
125
+ }
126
+ const direct = await this.sessionStore.getSession(trimmed);
127
+ if (direct) {
128
+ return trimmed;
129
+ }
130
+ const separatorIndex = trimmed.indexOf(":");
131
+ if (separatorIndex <= 0) {
132
+ return trimmed;
133
+ }
134
+ const localClientUuid = await this.maintenanceStore.getGatewayClientUuid();
135
+ const clientUuid = trimmed.slice(0, separatorIndex).trim();
136
+ const localSessionId = trimmed.slice(separatorIndex + 1).trim();
137
+ if (!localClientUuid || clientUuid !== localClientUuid || !localSessionId) {
138
+ return trimmed;
139
+ }
140
+ const localSession = await this.sessionStore.getSession(localSessionId);
141
+ return localSession ? localSessionId : trimmed;
142
+ }
101
143
  }
102
144
  exports.SendPartnerFileService = SendPartnerFileService;
@@ -18,7 +18,7 @@ class SendPartnerFileTool {
18
18
  register(server) {
19
19
  server.registerTool("send_partner_file", {
20
20
  title: "Send Partner File",
21
- description: "Use this when the result must include a real local workspace file for another session. Prefer this over send_partner_note when you already have an existing file path such as sample.txt, report.pdf, or screenshot.png. The tool reads the file from the current session workspace, attaches it as an artifact, and sends the partner note in one step.",
21
+ description: "Use this when the result must include a real local workspace file for another console. Prefer this over send_partner_note when you already have an existing file path such as sample.txt, report.pdf, or screenshot.png. For project/collab routing, use target_session_id. For direct gateway-wide routing outside a single collab project, first call list_gateway_sessions and then pass target_client_uuid plus target_local_session_id. The tool reads the file from the current console workspace, attaches it as an artifact, and sends the partner note in one step.",
22
22
  inputSchema: schema_1.sendPartnerFileInputSchema,
23
23
  outputSchema: schema_1.sendPartnerNoteOutputSchema,
24
24
  }, async (args) => {
@@ -18,7 +18,7 @@ class SendPartnerNoteTool {
18
18
  register(server) {
19
19
  server.registerTool("send_partner_note", {
20
20
  title: "Send Partner Note",
21
- description: "Use this for agent-to-agent collaboration: ask another session to do work, send a reply, or share results. Resolve the target explicitly for project/collab work with target_session_id and, if available, project_uuid. If the result includes an existing local file, prefer send_partner_file; mentioning the filename in message text is not enough. For required replies, the task is not complete until this tool succeeds.",
21
+ description: "Use this for agent-to-agent collaboration: ask another console to do work, send a reply, or share results. The canonical gateway-facing console id is session_id in the format client_uuid:local_session_id. For direct routing outside a collab project, pass that canonical console id in target_session_id. Only use target_client_uuid plus target_local_session_id when you already have those exact direct fields separately. For project/collab routing, use target_session_id equal to the project session id and, if available, project_uuid. If the result includes an existing local file, prefer send_partner_file; mentioning the filename in message text is not enough. For required replies, the task is not complete until this tool succeeds.",
22
22
  inputSchema: schema_1.sendPartnerNoteInputSchema,
23
23
  outputSchema: schema_1.sendPartnerNoteOutputSchema,
24
24
  }, async (args) => {
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.normalizeGatewayBaseUrl = normalizeGatewayBaseUrl;
7
+ exports.callGatewayJson = callGatewayJson;
8
+ exports.ensureGatewayClientUuid = ensureGatewayClientUuid;
9
+ const node_os_1 = __importDefault(require("node:os"));
10
+ function normalizeGatewayBaseUrl(value) {
11
+ const url = new URL(value);
12
+ url.pathname = url.pathname.replace(/\/+$/u, "");
13
+ if (!url.pathname.endsWith("/gateway")) {
14
+ url.pathname = `${url.pathname}/gateway`.replace(/\/{2,}/gu, "/");
15
+ }
16
+ return url;
17
+ }
18
+ async function callGatewayJson(input) {
19
+ const url = normalizeGatewayBaseUrl(input.gatewayPublicUrl);
20
+ url.pathname = `${url.pathname}${input.endpointPath}`.replace(/\/{2,}/gu, "/");
21
+ const response = await fetch(url, {
22
+ method: "POST",
23
+ headers: {
24
+ "content-type": "application/json",
25
+ ...(input.gatewayAuthToken
26
+ ? { authorization: `Bearer ${input.gatewayAuthToken}` }
27
+ : {}),
28
+ },
29
+ body: JSON.stringify(input.body),
30
+ });
31
+ if (!response.ok) {
32
+ const message = await response.text();
33
+ throw new Error(`Gateway request failed with status ${response.status}: ${message || response.statusText}`);
34
+ }
35
+ return (await response.json());
36
+ }
37
+ async function ensureGatewayClientUuid(input) {
38
+ const namespace = input.namespace || process.env.NAMESPACE || undefined;
39
+ const nodeId = input.nodeId || process.env.NODE_ID || undefined;
40
+ const systemUsername = input.systemUsername ||
41
+ process.env.USER ||
42
+ process.env.LOGNAME ||
43
+ (() => {
44
+ try {
45
+ return node_os_1.default.userInfo().username;
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ })();
51
+ const existing = await input.maintenanceStore.getGatewayClientUuid();
52
+ if (!input.gatewayPublicUrl) {
53
+ throw new Error("Gateway client registration requires GATEWAY_PUBLIC_URL.");
54
+ }
55
+ const response = await callGatewayJson({
56
+ gatewayPublicUrl: input.gatewayPublicUrl,
57
+ ...(input.gatewayAuthToken
58
+ ? { gatewayAuthToken: input.gatewayAuthToken }
59
+ : {}),
60
+ endpointPath: "/client/register",
61
+ body: {
62
+ ...(existing ? { client_uuid: existing } : {}),
63
+ client_label: input.projectName ||
64
+ [namespace, nodeId].filter(Boolean).join("/") ||
65
+ input.botUsername ||
66
+ "tellymcp client",
67
+ ...(input.botUsername ? { bot_username: input.botUsername } : {}),
68
+ ...(input.gatewayToken ? { gateway_token: input.gatewayToken } : {}),
69
+ ...(input.gatewayUserUuid ? { owner_user_uuid: input.gatewayUserUuid } : {}),
70
+ meta: {
71
+ ...(namespace ? { namespace } : {}),
72
+ ...(nodeId ? { node_id: nodeId } : {}),
73
+ ...(systemUsername ? { system_username: systemUsername } : {}),
74
+ ...(input.gatewayUserUuid
75
+ ? { gateway_user_uuid: input.gatewayUserUuid }
76
+ : {}),
77
+ },
78
+ },
79
+ });
80
+ await input.maintenanceStore.setGatewayClientUuid(response.client_uuid);
81
+ return response.client_uuid;
82
+ }
@@ -1,35 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GatewayCollaborationBackend = void 0;
4
- function normalizeGatewayBaseUrl(value) {
5
- const url = new URL(value);
6
- url.pathname = url.pathname.replace(/\/+$/u, "");
7
- if (!url.pathname.endsWith("/gateway")) {
8
- url.pathname = `${url.pathname}/gateway`.replace(/\/{2,}/gu, "/");
9
- }
10
- return url;
11
- }
4
+ const gatewayClientAccess_1 = require("./gatewayClientAccess");
12
5
  class GatewayCollaborationBackend {
13
6
  logger;
14
7
  maintenanceStore;
15
8
  gatewayPublicUrl;
16
9
  gatewayAuthToken;
17
- constructor(logger, maintenanceStore, gatewayPublicUrl, gatewayAuthToken) {
10
+ gatewayToken;
11
+ gatewayUserUuid;
12
+ projectName;
13
+ botUsername;
14
+ constructor(logger, maintenanceStore, gatewayPublicUrl, gatewayAuthToken, gatewayToken, gatewayUserUuid, projectName, botUsername) {
18
15
  this.logger = logger;
19
16
  this.maintenanceStore = maintenanceStore;
20
17
  this.gatewayPublicUrl = gatewayPublicUrl;
21
18
  this.gatewayAuthToken = gatewayAuthToken;
19
+ this.gatewayToken = gatewayToken;
20
+ this.gatewayUserUuid = gatewayUserUuid;
21
+ this.projectName = projectName;
22
+ this.botUsername = botUsername;
22
23
  }
23
24
  async sendPartnerNote(input, resolved) {
24
25
  if (!this.gatewayPublicUrl) {
25
26
  throw new Error("Gateway collaboration backend requires GATEWAY_PUBLIC_URL.");
26
27
  }
27
- const url = normalizeGatewayBaseUrl(this.gatewayPublicUrl);
28
+ const url = (0, gatewayClientAccess_1.normalizeGatewayBaseUrl)(this.gatewayPublicUrl);
28
29
  url.pathname = `${url.pathname}/partner-note`.replace(/\/{2,}/gu, "/");
29
- const clientUuid = await this.maintenanceStore.getGatewayClientUuid();
30
- if (!clientUuid) {
31
- throw new Error("Gateway collaboration requires a registered client_uuid. Join or create a project first.");
32
- }
30
+ const clientUuid = await (0, gatewayClientAccess_1.ensureGatewayClientUuid)({
31
+ maintenanceStore: this.maintenanceStore,
32
+ gatewayPublicUrl: this.gatewayPublicUrl,
33
+ ...(this.gatewayAuthToken
34
+ ? { gatewayAuthToken: this.gatewayAuthToken }
35
+ : {}),
36
+ ...(this.gatewayToken ? { gatewayToken: this.gatewayToken } : {}),
37
+ ...(this.gatewayUserUuid ? { gatewayUserUuid: this.gatewayUserUuid } : {}),
38
+ ...(this.projectName ? { projectName: this.projectName } : {}),
39
+ ...(this.botUsername ? { botUsername: this.botUsername } : {}),
40
+ });
33
41
  const response = await fetch(url, {
34
42
  method: "POST",
35
43
  headers: {