@mono-agent/agent-app 0.13.0 → 0.15.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/README.md +164 -37
- package/dist/adapter-send-tools-main.js +0 -1
- package/dist/adapter-send-tools-main.js.map +1 -1
- package/dist/adapter-send-tools.d.ts +8 -5
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +133 -287
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +3 -0
- package/dist/app-config.js.map +1 -1
- package/dist/app-controller-channels.d.ts +41 -7
- package/dist/app-controller-channels.d.ts.map +1 -1
- package/dist/app-controller-channels.js +4 -4
- package/dist/app-controller-channels.js.map +1 -1
- package/dist/app-controller-continuation.d.ts +43 -11
- package/dist/app-controller-continuation.d.ts.map +1 -1
- package/dist/app-controller-continuation.js +1 -5
- package/dist/app-controller-continuation.js.map +1 -1
- package/dist/app-controller-lifecycle.d.ts +34 -5
- package/dist/app-controller-lifecycle.d.ts.map +1 -1
- package/dist/app-controller-lifecycle.js.map +1 -1
- package/dist/app-controller-maintenance.d.ts +46 -8
- package/dist/app-controller-maintenance.d.ts.map +1 -1
- package/dist/app-controller-maintenance.js +64 -1
- package/dist/app-controller-maintenance.js.map +1 -1
- package/dist/app-controller-memory-health.d.ts +39 -10
- package/dist/app-controller-memory-health.d.ts.map +1 -1
- package/dist/app-controller-memory-health.js.map +1 -1
- package/dist/app-controller-memory.d.ts +27 -5
- package/dist/app-controller-memory.d.ts.map +1 -1
- package/dist/app-controller-memory.js +0 -1
- package/dist/app-controller-memory.js.map +1 -1
- package/dist/app-controller-responder.d.ts +53 -7
- package/dist/app-controller-responder.d.ts.map +1 -1
- package/dist/app-controller-responder.js +5 -5
- package/dist/app-controller-responder.js.map +1 -1
- package/dist/app-controller-traceability.d.ts +63 -14
- package/dist/app-controller-traceability.d.ts.map +1 -1
- package/dist/app-controller-traceability.js.map +1 -1
- package/dist/app-controller-types.d.ts +63 -0
- package/dist/app-controller-types.d.ts.map +1 -0
- package/dist/app-controller-types.js +2 -0
- package/dist/app-controller-types.js.map +1 -0
- package/dist/app-controller-utils.d.ts +1 -1
- package/dist/app-controller-utils.d.ts.map +1 -1
- package/dist/app-controller-utils.js +1 -1
- package/dist/app-controller-utils.js.map +1 -1
- package/dist/app-controller.d.ts +5 -62
- package/dist/app-controller.d.ts.map +1 -1
- package/dist/app-controller.js +0 -6
- package/dist/app-controller.js.map +1 -1
- package/dist/background-environment.d.ts +1 -0
- package/dist/background-environment.d.ts.map +1 -1
- package/dist/background-environment.js +1 -0
- package/dist/background-environment.js.map +1 -1
- package/dist/background-lifecycle-utils.d.ts +27 -0
- package/dist/background-lifecycle-utils.d.ts.map +1 -0
- package/dist/background-lifecycle-utils.js +69 -0
- package/dist/background-lifecycle-utils.js.map +1 -0
- package/dist/background-log-maintenance.d.ts +6 -0
- package/dist/background-log-maintenance.d.ts.map +1 -0
- package/dist/background-log-maintenance.js +185 -0
- package/dist/background-log-maintenance.js.map +1 -0
- package/dist/background-runtime.d.ts +13 -2
- package/dist/background-runtime.d.ts.map +1 -1
- package/dist/background-runtime.js +27 -3
- package/dist/background-runtime.js.map +1 -1
- package/dist/background.d.ts +28 -5
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +229 -297
- package/dist/background.js.map +1 -1
- package/dist/channel-drivers/cron.js +8 -1
- package/dist/channel-drivers/cron.js.map +1 -1
- package/dist/channel-drivers/native-notify.d.ts +2 -0
- package/dist/channel-drivers/native-notify.d.ts.map +1 -1
- package/dist/channel-drivers/native-notify.js +14 -2
- package/dist/channel-drivers/native-notify.js.map +1 -1
- package/dist/channel-drivers/slack.d.ts.map +1 -1
- package/dist/channel-drivers/slack.js +49 -0
- package/dist/channel-drivers/slack.js.map +1 -1
- package/dist/channel-drivers/telegram.d.ts.map +1 -1
- package/dist/channel-drivers/telegram.js +7 -18
- package/dist/channel-drivers/telegram.js.map +1 -1
- package/dist/channel-drivers/tui.d.ts.map +1 -1
- package/dist/channel-drivers/tui.js +8 -0
- package/dist/channel-drivers/tui.js.map +1 -1
- package/dist/channel-plugins.d.ts +2 -11
- package/dist/channel-plugins.d.ts.map +1 -1
- package/dist/channel-plugins.js +63 -60
- package/dist/channel-plugins.js.map +1 -1
- package/dist/channel-status-display.js +2 -6
- package/dist/channel-status-display.js.map +1 -1
- package/dist/channels.d.ts +5 -6
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +7 -5
- package/dist/channels.js.map +1 -1
- package/dist/cli-args.d.ts +8 -2
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +98 -27
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-background-command.d.ts.map +1 -1
- package/dist/cli-background-command.js +90 -15
- package/dist/cli-background-command.js.map +1 -1
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +0 -14
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +1 -7
- package/dist/cli-help.js.map +1 -1
- package/dist/cli-runs-command.d.ts +0 -3
- package/dist/cli-runs-command.d.ts.map +1 -1
- package/dist/cli-runs-command.js +1 -5
- package/dist/cli-runs-command.js.map +1 -1
- package/dist/cli-validate-config-command.d.ts.map +1 -1
- package/dist/cli-validate-config-command.js +12 -8
- package/dist/cli-validate-config-command.js.map +1 -1
- package/dist/config-reference.d.ts +4 -1
- package/dist/config-reference.d.ts.map +1 -1
- package/dist/config-reference.js +245 -69
- package/dist/config-reference.js.map +1 -1
- package/dist/configured-agent.d.ts +2 -8
- package/dist/configured-agent.d.ts.map +1 -1
- package/dist/configured-agent.js +3 -25
- package/dist/configured-agent.js.map +1 -1
- package/dist/consumer-contract.d.ts.map +1 -1
- package/dist/consumer-contract.js +2 -4
- package/dist/consumer-contract.js.map +1 -1
- package/dist/doctor-observability.d.ts +7 -0
- package/dist/doctor-observability.d.ts.map +1 -0
- package/dist/doctor-observability.js +80 -0
- package/dist/doctor-observability.js.map +1 -0
- package/dist/doctor-types.d.ts +17 -0
- package/dist/doctor-types.d.ts.map +1 -0
- package/dist/doctor-types.js +2 -0
- package/dist/doctor-types.js.map +1 -0
- package/dist/doctor.d.ts +2 -12
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +8 -108
- package/dist/doctor.js.map +1 -1
- package/dist/first-run-readiness.d.ts +1 -1
- package/dist/first-run-readiness.d.ts.map +1 -1
- package/dist/first-run-readiness.js +1 -1
- package/dist/first-run-readiness.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/interaction-bridge.d.ts +5 -22
- package/dist/interaction-bridge.d.ts.map +1 -1
- package/dist/interaction-bridge.js +251 -141
- package/dist/interaction-bridge.js.map +1 -1
- package/dist/launchd.d.ts +33 -1
- package/dist/launchd.d.ts.map +1 -1
- package/dist/launchd.js +129 -2
- package/dist/launchd.js.map +1 -1
- package/dist/managed-web-logs.d.ts +16 -0
- package/dist/managed-web-logs.d.ts.map +1 -0
- package/dist/managed-web-logs.js +153 -0
- package/dist/managed-web-logs.js.map +1 -0
- package/dist/modules/catalog.js +1 -1
- package/dist/modules/catalog.js.map +1 -1
- package/dist/modules/known-tools.d.ts +1 -1
- package/dist/modules/known-tools.d.ts.map +1 -1
- package/dist/modules/known-tools.js +0 -3
- package/dist/modules/known-tools.js.map +1 -1
- package/dist/posted-reply-history.d.ts.map +1 -1
- package/dist/posted-reply-history.js +3 -0
- package/dist/posted-reply-history.js.map +1 -1
- package/dist/run-history.d.ts +6 -2
- package/dist/run-history.d.ts.map +1 -1
- package/dist/run-history.js +476 -96
- package/dist/run-history.js.map +1 -1
- package/dist/ui.js +1 -1
- package/dist/ui.js.map +1 -1
- package/dist/web-command.d.ts +4 -1
- package/dist/web-command.d.ts.map +1 -1
- package/dist/web-command.js +31 -1
- package/dist/web-command.js.map +1 -1
- package/dist/wizard/prompts.d.ts.map +1 -1
- package/dist/wizard/prompts.js +3 -4
- package/dist/wizard/prompts.js.map +1 -1
- package/dist/wizard/run.js +3 -3
- package/dist/wizard/run.js.map +1 -1
- package/package.json +16 -16
- package/schema/mono-agent.config.schema.json +282 -164
- package/skills/mono-agent-composer/SKILL.md +5 -5
- package/skills/mono-agent-composer/references/config-blueprint.md +6 -13
- package/skills/mono-agent-composer/references/discovery-questions.md +1 -1
- package/skills/mono-agent-composer/references/feature-coverage.md +7 -8
- package/skills/mono-agent-composer/references/package-map.md +5 -6
- package/skills/mono-agent-composer/references/playbooks.md +3 -3
- package/skills/mono-agent-composer/references/validation.md +21 -1
|
@@ -29,13 +29,8 @@ const ASK_BRIDGE_CLEANUP_TIMEOUT_MS = 1_000;
|
|
|
29
29
|
export const ADAPTER_SEND_TOOLS_MCP_SERVER_NAME = "mono-agent-adapter-send";
|
|
30
30
|
export async function resolveAdapterSendToolsSettings(input, options = {}) {
|
|
31
31
|
const telegramSendAllowed = isAdapterToolAllowed("TelegramSendMessage", options);
|
|
32
|
-
const telegramAskAllowed = options.suppressInteractionTools !== true
|
|
33
|
-
&& isAdapterToolAllowed("TelegramAskButtons", options);
|
|
34
32
|
const telegramFileAllowed = isAdapterToolAllowed("TelegramSendFile", options);
|
|
35
|
-
const telegramAnyAllowed = telegramSendAllowed ||
|
|
36
|
-
const telegramAskBridge = telegramAskAllowed
|
|
37
|
-
? resolveAskUserToolSettings(input.env, options.interaction)
|
|
38
|
-
: undefined;
|
|
33
|
+
const telegramAnyAllowed = telegramSendAllowed || telegramFileAllowed;
|
|
39
34
|
const [slack, telegram] = await Promise.all([
|
|
40
35
|
isAdapterToolAllowed("SlackSendMessage", options)
|
|
41
36
|
? resolveSlackSendToolSettings(input, options)
|
|
@@ -43,9 +38,8 @@ export async function resolveAdapterSendToolsSettings(input, options = {}) {
|
|
|
43
38
|
telegramAnyAllowed
|
|
44
39
|
? resolveTelegramSendToolSettings(input, options, {
|
|
45
40
|
send: telegramSendAllowed,
|
|
46
|
-
ask: telegramAskAllowed,
|
|
47
41
|
file: telegramFileAllowed,
|
|
48
|
-
}
|
|
42
|
+
})
|
|
49
43
|
: undefined,
|
|
50
44
|
]);
|
|
51
45
|
const askUser = options.suppressInteractionTools !== true && isAdapterToolAllowed("AskUser", options)
|
|
@@ -74,13 +68,15 @@ function resolveAskUserToolSettings(env, interaction) {
|
|
|
74
68
|
const timeoutRaw = Number(optionalString(env.MONO_AGENT_ASK_USER_TIMEOUT_MS));
|
|
75
69
|
const timeoutMs = interaction?.timeoutMs
|
|
76
70
|
?? (Number.isFinite(timeoutRaw) && timeoutRaw >= 1000 ? timeoutRaw : 600_000);
|
|
77
|
-
const
|
|
71
|
+
const producerConversationId = optionalString(env.MONO_AGENT_ADAPTER_TOOLS_PRODUCING_CONVERSATION_ID);
|
|
72
|
+
const interactionConversationId = optionalString(env.MONO_AGENT_ADAPTER_TOOLS_INTERACTION_CONVERSATION_ID);
|
|
78
73
|
const runId = optionalString(env.MONO_AGENT_ADAPTER_TOOLS_PRODUCING_RUN_ID);
|
|
79
74
|
return {
|
|
80
75
|
bridgeUrl,
|
|
81
76
|
bridgeToken,
|
|
82
77
|
timeoutMs,
|
|
83
|
-
...(
|
|
78
|
+
...(producerConversationId === undefined ? {} : { producerConversationId }),
|
|
79
|
+
...(interactionConversationId === undefined ? {} : { interactionConversationId }),
|
|
84
80
|
...(runId === undefined ? {} : { runId }),
|
|
85
81
|
};
|
|
86
82
|
}
|
|
@@ -92,9 +88,6 @@ export function adapterSendToolNames(settings) {
|
|
|
92
88
|
if (settings.telegram?.tools.send === true) {
|
|
93
89
|
names.push("TelegramSendMessage");
|
|
94
90
|
}
|
|
95
|
-
if (settings.telegram?.tools.ask === true) {
|
|
96
|
-
names.push("TelegramAskButtons");
|
|
97
|
-
}
|
|
98
91
|
if (settings.telegram?.tools.file === true) {
|
|
99
92
|
names.push("TelegramSendFile");
|
|
100
93
|
}
|
|
@@ -118,6 +111,9 @@ export function adapterSendToolsMcpEnv(configPath, allowedTools, context, intera
|
|
|
118
111
|
...(context?.conversationId === undefined
|
|
119
112
|
? {}
|
|
120
113
|
: { MONO_AGENT_ADAPTER_TOOLS_PRODUCING_CONVERSATION_ID: context.conversationId }),
|
|
114
|
+
...(context?.interactionConversationId === undefined
|
|
115
|
+
? {}
|
|
116
|
+
: { MONO_AGENT_ADAPTER_TOOLS_INTERACTION_CONVERSATION_ID: context.interactionConversationId }),
|
|
121
117
|
...(context?.runId === undefined
|
|
122
118
|
? {}
|
|
123
119
|
: { MONO_AGENT_ADAPTER_TOOLS_PRODUCING_RUN_ID: context.runId }),
|
|
@@ -199,6 +195,7 @@ export function adapterSendToolsMcpServerSpec(configPath, cwd, allowedTools, con
|
|
|
199
195
|
export function createAdapterSendToolsRuntimeExtension(configPath, cwd, allowedTools, indexPath, interaction, runOutputRoot, deliveryHistoryCapabilityIssuer) {
|
|
200
196
|
return async (input) => {
|
|
201
197
|
const conversationId = input?.request?.conversationId;
|
|
198
|
+
const interactionConversationId = input?.request?.replyTo?.conversationId ?? conversationId;
|
|
202
199
|
const runId = input?.runId;
|
|
203
200
|
const hasConversation = typeof conversationId === "string" && conversationId.trim().length > 0;
|
|
204
201
|
const hasRunId = typeof runId === "string" && runId.trim().length > 0;
|
|
@@ -224,6 +221,9 @@ export function createAdapterSendToolsRuntimeExtension(configPath, cwd, allowedT
|
|
|
224
221
|
const context = hasConversation || hasRunId
|
|
225
222
|
? {
|
|
226
223
|
...(hasConversation ? { conversationId } : {}),
|
|
224
|
+
...(typeof interactionConversationId === "string" && interactionConversationId.trim().length > 0
|
|
225
|
+
? { interactionConversationId }
|
|
226
|
+
: {}),
|
|
227
227
|
...(hasRunId ? { runId } : {}),
|
|
228
228
|
...(indexPath === undefined ? {} : { indexPath }),
|
|
229
229
|
...(runOutput === undefined
|
|
@@ -281,24 +281,24 @@ export async function createAdapterSendToolsServer(settings, clients, indexing,
|
|
|
281
281
|
registerSlackSendTool(server, settings.slack, clients.slack, adapter.formatMarkdownForSlack, indexing, options.deliveryHistory, options.fetchImpl ?? globalThis.fetch);
|
|
282
282
|
}
|
|
283
283
|
if (settings.telegram !== undefined && clients.telegram !== undefined) {
|
|
284
|
-
// Loaded once here (not per-register-call) because registerTelegramAskTool
|
|
285
|
-
// needs TELEGRAM_ASK_MAX_OPTIONS synchronously while building its zod
|
|
286
|
-
// schema, at registration time — not deferred into the request handler.
|
|
287
284
|
const adapter = await loadTelegramModule();
|
|
288
|
-
if (settings.telegram.tools.send) {
|
|
289
|
-
registerTelegramSendTool(server, settings.telegram, clients.telegram, options.deliveryHistory, options.fetchImpl ?? globalThis.fetch);
|
|
290
|
-
}
|
|
291
|
-
if (settings.telegram.tools.ask) {
|
|
292
|
-
registerTelegramAskTool(server, settings.telegram, clients.telegram, adapter, options.fetchImpl ?? globalThis.fetch);
|
|
285
|
+
if (settings.telegram.tools.send && clients.telegram.sendMessage !== undefined) {
|
|
286
|
+
registerTelegramSendTool(server, settings.telegram, { sendMessage: clients.telegram.sendMessage }, adapter, options.deliveryHistory, options.fetchImpl ?? globalThis.fetch);
|
|
293
287
|
}
|
|
294
|
-
if (settings.telegram.tools.file
|
|
288
|
+
if (settings.telegram.tools.file
|
|
289
|
+
&& (clients.telegram.sendDocument !== undefined || clients.telegram.sendPhoto !== undefined)) {
|
|
295
290
|
registerTelegramSendFileTool(server, settings.telegram, clients.telegram, adapter);
|
|
296
291
|
}
|
|
297
292
|
}
|
|
298
293
|
// AskUser needs a target conversation; the parent app process resolves the
|
|
299
294
|
// settings without one (for tool-name gating) and must not register the tool.
|
|
300
|
-
if (settings.askUser?.
|
|
301
|
-
|
|
295
|
+
if (settings.askUser?.producerConversationId !== undefined
|
|
296
|
+
&& settings.askUser.interactionConversationId !== undefined) {
|
|
297
|
+
registerAskUserTool(server, {
|
|
298
|
+
...settings.askUser,
|
|
299
|
+
producerConversationId: settings.askUser.producerConversationId,
|
|
300
|
+
interactionConversationId: settings.askUser.interactionConversationId,
|
|
301
|
+
}, options.fetchImpl ?? globalThis.fetch);
|
|
302
302
|
}
|
|
303
303
|
return server;
|
|
304
304
|
}
|
|
@@ -307,17 +307,26 @@ const ASK_USER_POLL_WAIT_MS = 20_000;
|
|
|
307
307
|
function registerAskUserTool(server, settings, fetchImpl) {
|
|
308
308
|
server.registerTool("AskUser", {
|
|
309
309
|
title: "Ask the user and wait",
|
|
310
|
-
description: "Ask the user
|
|
310
|
+
description: "Ask 1–5 related questions and WAIT for the user to answer them in the current conversation. Each question must offer 2–3 concise options with descriptions; the UI also permits a custom reply. Use multiSelect only when several choices may be combined. Put long decision context or a draft in message. A second concurrent AskUser call fails. If the wait expires, finish gracefully using the returned partial answers and state any assumptions.",
|
|
311
311
|
inputSchema: {
|
|
312
|
-
|
|
312
|
+
message: z.string().min(1).max(4_096).optional().describe("Optional context or draft shown above the questions."),
|
|
313
|
+
questions: z.array(z.object({
|
|
314
|
+
header: z.string().min(1).max(12).describe("Short section label, at most 12 characters."),
|
|
315
|
+
question: z.string().min(1).max(1_000).describe("The decision or information needed."),
|
|
316
|
+
options: z.array(z.object({
|
|
317
|
+
label: z.string().min(1).max(75).describe("Concise selectable answer."),
|
|
318
|
+
description: z.string().min(1).max(300).describe("What choosing this option means."),
|
|
319
|
+
})).min(2).max(3),
|
|
320
|
+
multiSelect: z.boolean().optional().describe("Allow selecting more than one proposed answer before Done."),
|
|
321
|
+
})).min(1).max(5),
|
|
313
322
|
},
|
|
314
323
|
}, async (args, extra) => {
|
|
315
324
|
const created = await askBridgeRequest(settings, fetchImpl, "POST", "/v1/asks", {
|
|
316
|
-
conversationId: settings.
|
|
317
|
-
producerConversationId: settings.
|
|
325
|
+
conversationId: settings.interactionConversationId,
|
|
326
|
+
producerConversationId: settings.producerConversationId,
|
|
318
327
|
runId: settings.runId,
|
|
319
|
-
|
|
320
|
-
|
|
328
|
+
message: args.message,
|
|
329
|
+
questions: args.questions,
|
|
321
330
|
timeoutMs: settings.timeoutMs,
|
|
322
331
|
}, extra.signal);
|
|
323
332
|
if (created.status === 409) {
|
|
@@ -326,42 +335,48 @@ function registerAskUserTool(server, settings, fetchImpl) {
|
|
|
326
335
|
if (created.status === 501) {
|
|
327
336
|
return askToolResult("This conversation's channel does not support interactive asks. Ask your question in your final reply instead.", { answered: false, reason: "unsupported_channel" });
|
|
328
337
|
}
|
|
329
|
-
if (created.status !== 201 || typeof created.body.
|
|
338
|
+
if (created.status !== 201 || typeof created.body.interactionId !== "string") {
|
|
330
339
|
throw new Error(`AskUser: the interaction bridge rejected the ask (HTTP ${String(created.status)}).`);
|
|
331
340
|
}
|
|
332
|
-
return await awaitBridgeAsk(settings, created.body.
|
|
341
|
+
return await awaitBridgeAsk(settings, created.body.interactionId, extra, fetchImpl, extra.signal);
|
|
333
342
|
});
|
|
334
343
|
}
|
|
335
|
-
function askToolResult(text, structured
|
|
344
|
+
function askToolResult(text, structured) {
|
|
336
345
|
return {
|
|
337
346
|
content: [{ type: "text", text }],
|
|
338
|
-
structuredContent: { ok: true, ...
|
|
347
|
+
structuredContent: { ok: true, ...structured },
|
|
339
348
|
};
|
|
340
349
|
}
|
|
341
|
-
async function awaitBridgeAsk(settings,
|
|
350
|
+
async function awaitBridgeAsk(settings, interactionId, extra, fetchImpl, signal) {
|
|
342
351
|
const startedMs = Date.now();
|
|
343
352
|
try {
|
|
344
353
|
for (;;) {
|
|
345
|
-
const poll = await askBridgeRequest(settings, fetchImpl, "GET", `/v1/asks/${encodeURIComponent(
|
|
354
|
+
const poll = await askBridgeRequest(settings, fetchImpl, "GET", `/v1/asks/${encodeURIComponent(interactionId)}?waitMs=${String(ASK_USER_POLL_WAIT_MS)}`, undefined, signal);
|
|
346
355
|
if (poll.status !== 200) {
|
|
347
|
-
throw new Error(
|
|
356
|
+
throw new Error(`AskUser: lost the pending ask (HTTP ${String(poll.status)}).`);
|
|
348
357
|
}
|
|
349
358
|
// Keep-alive: progress notifications reset the runtime's MCP inactivity
|
|
350
359
|
// timeout so a long human wait cannot kill the tool call.
|
|
351
360
|
await sendAskProgress(extra, Math.round((Date.now() - startedMs) / 1000));
|
|
352
361
|
const status = poll.body.status;
|
|
353
|
-
if (status === "answered"
|
|
354
|
-
const
|
|
355
|
-
return askToolResult(
|
|
362
|
+
if (status === "answered") {
|
|
363
|
+
const answer = formatBridgeAskAnswers(poll.body);
|
|
364
|
+
return askToolResult(`The user answered:\n${answer.text}`, {
|
|
365
|
+
answered: true,
|
|
366
|
+
interactionId,
|
|
367
|
+
answers: answer.answers,
|
|
368
|
+
});
|
|
356
369
|
}
|
|
357
370
|
if (status === "expired") {
|
|
358
|
-
|
|
371
|
+
const partial = formatBridgeAskAnswers(poll.body);
|
|
372
|
+
return askToolResult(`${partial.answers.length === 0 ? "The user did not answer" : `The user answered only:\n${partial.text}\n\nThe remaining questions were not answered`} within the wait window. Their next message will be a new turn; finish gracefully and state any assumptions.`, { answered: false, reason: "timeout", interactionId, answers: partial.answers });
|
|
359
373
|
}
|
|
360
374
|
if (status === "cancelled") {
|
|
361
375
|
return askToolResult("The user cancelled the current run. Stop this task.", {
|
|
362
376
|
answered: false,
|
|
363
377
|
reason: "cancelled",
|
|
364
|
-
|
|
378
|
+
interactionId,
|
|
379
|
+
});
|
|
365
380
|
}
|
|
366
381
|
}
|
|
367
382
|
}
|
|
@@ -369,11 +384,56 @@ async function awaitBridgeAsk(settings, askId, toolName, extra, fetchImpl, signa
|
|
|
369
384
|
// A cancelled MCP call must not leave a bridge ask pending until its full
|
|
370
385
|
// human timeout. Cleanup gets its own bounded signal (not the aborted call
|
|
371
386
|
// signal), and its failure must never replace the primary tool failure.
|
|
372
|
-
await cleanupBridgeAskBestEffort(settings, fetchImpl,
|
|
387
|
+
await cleanupBridgeAskBestEffort(settings, fetchImpl, interactionId);
|
|
373
388
|
throw error;
|
|
374
389
|
}
|
|
375
390
|
}
|
|
376
|
-
|
|
391
|
+
function formatBridgeAskAnswers(body) {
|
|
392
|
+
const questions = Array.isArray(body.questions) ? body.questions : [];
|
|
393
|
+
const answers = Array.isArray(body.answers) ? body.answers : [];
|
|
394
|
+
const normalized = [];
|
|
395
|
+
const lines = [];
|
|
396
|
+
for (const rawAnswer of answers) {
|
|
397
|
+
if (typeof rawAnswer !== "object" || rawAnswer === null || Array.isArray(rawAnswer))
|
|
398
|
+
continue;
|
|
399
|
+
const answer = rawAnswer;
|
|
400
|
+
const questionId = typeof answer.questionId === "string" ? answer.questionId : undefined;
|
|
401
|
+
if (questionId === undefined)
|
|
402
|
+
continue;
|
|
403
|
+
const question = questions.find((candidate) => typeof candidate === "object"
|
|
404
|
+
&& candidate !== null
|
|
405
|
+
&& !Array.isArray(candidate)
|
|
406
|
+
&& candidate.id === questionId);
|
|
407
|
+
const options = Array.isArray(question?.options) ? question.options : [];
|
|
408
|
+
const selectedOptionIds = Array.isArray(answer.selectedOptionIds)
|
|
409
|
+
? answer.selectedOptionIds.filter((value) => typeof value === "string")
|
|
410
|
+
: [];
|
|
411
|
+
const selectedOptions = selectedOptionIds.flatMap((optionId) => {
|
|
412
|
+
const option = options.find((candidate) => typeof candidate === "object"
|
|
413
|
+
&& candidate !== null
|
|
414
|
+
&& !Array.isArray(candidate)
|
|
415
|
+
&& candidate.id === optionId);
|
|
416
|
+
return typeof option?.label === "string"
|
|
417
|
+
? [{ id: optionId, label: option.label, ...(typeof option.description === "string" ? { description: option.description } : {}) }]
|
|
418
|
+
: [];
|
|
419
|
+
});
|
|
420
|
+
const customReply = typeof answer.customReply === "string" ? answer.customReply : undefined;
|
|
421
|
+
const header = typeof question?.header === "string" ? question.header : questionId;
|
|
422
|
+
const rendered = [
|
|
423
|
+
...selectedOptions.map((option) => option.label),
|
|
424
|
+
...(customReply === undefined ? [] : [customReply]),
|
|
425
|
+
].join(", ");
|
|
426
|
+
lines.push(`- ${header}: ${rendered}`);
|
|
427
|
+
normalized.push({
|
|
428
|
+
questionId,
|
|
429
|
+
header,
|
|
430
|
+
selectedOptions,
|
|
431
|
+
...(customReply === undefined ? {} : { customReply }),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return { text: lines.join("\n") || "(no answers)", answers: normalized };
|
|
435
|
+
}
|
|
436
|
+
async function cleanupBridgeAskBestEffort(settings, fetchImpl, interactionId) {
|
|
377
437
|
const controller = new AbortController();
|
|
378
438
|
let deadlineTimer;
|
|
379
439
|
const deadline = new Promise((resolve) => {
|
|
@@ -385,7 +445,7 @@ async function cleanupBridgeAskBestEffort(settings, fetchImpl, askId) {
|
|
|
385
445
|
});
|
|
386
446
|
// Attach the rejection handler before racing. If a test seam or nonstandard
|
|
387
447
|
// fetch ignores abort and rejects later, it cannot become an unhandled error.
|
|
388
|
-
const cleanup = askBridgeRequest(settings, fetchImpl, "DELETE", `/v1/asks/${encodeURIComponent(
|
|
448
|
+
const cleanup = askBridgeRequest(settings, fetchImpl, "DELETE", `/v1/asks/${encodeURIComponent(interactionId)}`, undefined, controller.signal).then(() => undefined, () => undefined);
|
|
389
449
|
try {
|
|
390
450
|
await Promise.race([cleanup, deadline]);
|
|
391
451
|
}
|
|
@@ -564,16 +624,22 @@ function registerSlackSendTool(server, settings, client, formatMarkdownForSlack,
|
|
|
564
624
|
};
|
|
565
625
|
});
|
|
566
626
|
}
|
|
567
|
-
function registerTelegramSendTool(server, settings, client, deliveryHistory, fetchImpl = globalThis.fetch) {
|
|
627
|
+
function registerTelegramSendTool(server, settings, client, adapter, deliveryHistory, fetchImpl = globalThis.fetch) {
|
|
568
628
|
server.registerTool("TelegramSendMessage", {
|
|
569
629
|
title: "Send Telegram message",
|
|
570
|
-
description: "Send a message to an allowed Telegram chat
|
|
630
|
+
description: "Send a message to an allowed Telegram chat. Optionally add 2–8 non-blocking reply buttons; a tap arrives later as a new user turn.",
|
|
571
631
|
inputSchema: {
|
|
572
632
|
chat_id: z.union([z.string().min(1), z.number().int()]).describe("Telegram chat id from the adapter allowlist."),
|
|
573
633
|
text: z.string().min(1).describe("Message text to send."),
|
|
574
634
|
parse_mode: z.string().min(1).optional().describe("Optional Telegram parse mode, e.g. MarkdownV2 or HTML."),
|
|
575
635
|
reply_to_message_id: z.number().int().optional().describe("Optional message id to reply to."),
|
|
576
636
|
disable_web_page_preview: z.boolean().optional().describe("Disable Telegram link previews."),
|
|
637
|
+
reply_options: z
|
|
638
|
+
.array(z.string().min(1).max(75))
|
|
639
|
+
.min(2)
|
|
640
|
+
.max(8)
|
|
641
|
+
.optional()
|
|
642
|
+
.describe("Optional non-blocking reply button labels. Use AskUser when this run must wait for the selection."),
|
|
577
643
|
},
|
|
578
644
|
}, async (args, extra) => {
|
|
579
645
|
assertTelegramChatAllowed(settings, args.chat_id, "TelegramSendMessage");
|
|
@@ -583,6 +649,16 @@ function registerTelegramSendTool(server, settings, client, deliveryHistory, fet
|
|
|
583
649
|
...(args.parse_mode === undefined ? {} : { parse_mode: args.parse_mode }),
|
|
584
650
|
...(args.reply_to_message_id === undefined ? {} : { reply_to_message_id: args.reply_to_message_id }),
|
|
585
651
|
...(args.disable_web_page_preview === undefined ? {} : { disable_web_page_preview: args.disable_web_page_preview }),
|
|
652
|
+
...(args.reply_options === undefined
|
|
653
|
+
? {}
|
|
654
|
+
: {
|
|
655
|
+
reply_markup: {
|
|
656
|
+
inline_keyboard: args.reply_options.map((label, index) => [{
|
|
657
|
+
text: label,
|
|
658
|
+
callback_data: adapter.telegramReplyCallbackData(index),
|
|
659
|
+
}]),
|
|
660
|
+
},
|
|
661
|
+
}),
|
|
586
662
|
}, { signal: extra.signal });
|
|
587
663
|
const history = await recordAdapterDeliveryHistory({
|
|
588
664
|
settings: deliveryHistory,
|
|
@@ -598,246 +674,12 @@ function registerTelegramSendTool(server, settings, client, deliveryHistory, fet
|
|
|
598
674
|
ok: true,
|
|
599
675
|
chat_id: result.chat.id,
|
|
600
676
|
message_id: result.message_id,
|
|
677
|
+
...(args.reply_options === undefined ? {} : { reply_options: args.reply_options }),
|
|
601
678
|
...(history === undefined ? {} : { history }),
|
|
602
679
|
},
|
|
603
680
|
};
|
|
604
681
|
});
|
|
605
682
|
}
|
|
606
|
-
/** Marker appended (buttons kept) when a Telegram ask times out. */
|
|
607
|
-
const TELEGRAM_ASK_EXPIRED_MARKER = "⌛ Expired — tapping a button still reaches me and starts a new turn.";
|
|
608
|
-
/** Marker appended (buttons kept) when a Telegram ask is cancelled. */
|
|
609
|
-
const TELEGRAM_ASK_CANCELLED_MARKER = "🚫 Cancelled — tapping a button still reaches me and starts a new turn.";
|
|
610
|
-
/** Bounded own-signal budget for the best-effort expiry edit — never the aborted call signal. */
|
|
611
|
-
const TELEGRAM_ASK_EXPIRY_EDIT_TIMEOUT_MS = 2_000;
|
|
612
|
-
/** A Telegram 400 is the parse-entity rejection we fall back from (raw text, no parse_mode). */
|
|
613
|
-
function isTelegramParseError(error, adapter) {
|
|
614
|
-
return error instanceof adapter.TelegramApiError && error.errorCode === 400;
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Send the ask keyboard rendered as MarkdownV2, retrying ONCE with the raw source and
|
|
618
|
-
* no parse_mode when Telegram rejects the entities with a 400 parse error. Any other
|
|
619
|
-
* failure propagates so the caller can clean up a pending bridge ask.
|
|
620
|
-
*/
|
|
621
|
-
async function sendTelegramAskKeyboard(client, adapter, params) {
|
|
622
|
-
try {
|
|
623
|
-
return await client.sendMessage({
|
|
624
|
-
chat_id: params.chat_id,
|
|
625
|
-
text: params.rendered,
|
|
626
|
-
parse_mode: "MarkdownV2",
|
|
627
|
-
reply_markup: params.replyMarkup,
|
|
628
|
-
}, { signal: params.signal });
|
|
629
|
-
}
|
|
630
|
-
catch (error) {
|
|
631
|
-
if (!isTelegramParseError(error, adapter)) {
|
|
632
|
-
throw error;
|
|
633
|
-
}
|
|
634
|
-
return await client.sendMessage({
|
|
635
|
-
chat_id: params.chat_id,
|
|
636
|
-
text: params.rawSource,
|
|
637
|
-
reply_markup: params.replyMarkup,
|
|
638
|
-
}, { signal: params.signal });
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Best-effort: append an expiry/cancel note to the buttons message while KEEPING the
|
|
643
|
-
* inline keyboard (Telegram strips the keyboard on `editMessageText` unless reply_markup
|
|
644
|
-
* is re-sent). Renders through the same MarkdownV2-with-plain-fallback path. Uses its own
|
|
645
|
-
* bounded signal — never the aborted call signal — and swallows every failure so a failed
|
|
646
|
-
* edit can never replace the primary ask result.
|
|
647
|
-
*/
|
|
648
|
-
async function editExpiredTelegramAskMessage(client, adapter, params) {
|
|
649
|
-
try {
|
|
650
|
-
const source = `${params.rawSource}\n\n${params.marker}`;
|
|
651
|
-
const rendered = adapter.renderTelegramMarkdown(source);
|
|
652
|
-
const signal = AbortSignal.timeout(TELEGRAM_ASK_EXPIRY_EDIT_TIMEOUT_MS);
|
|
653
|
-
try {
|
|
654
|
-
await client.editMessageText({
|
|
655
|
-
chat_id: params.chat_id,
|
|
656
|
-
message_id: params.message_id,
|
|
657
|
-
text: rendered,
|
|
658
|
-
parse_mode: "MarkdownV2",
|
|
659
|
-
reply_markup: params.replyMarkup,
|
|
660
|
-
}, { signal });
|
|
661
|
-
}
|
|
662
|
-
catch (error) {
|
|
663
|
-
if (!isTelegramParseError(error, adapter)) {
|
|
664
|
-
throw error;
|
|
665
|
-
}
|
|
666
|
-
await client.editMessageText({
|
|
667
|
-
chat_id: params.chat_id,
|
|
668
|
-
message_id: params.message_id,
|
|
669
|
-
text: source,
|
|
670
|
-
reply_markup: params.replyMarkup,
|
|
671
|
-
}, { signal });
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
catch {
|
|
675
|
-
// Best-effort: a failed expiry edit must never replace the ask result.
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Best-effort: remove a keyboard that was sent but could not be registered with
|
|
680
|
-
* the interaction bridge. Re-editing without reply_markup clears the buttons;
|
|
681
|
-
* use an independent bounded signal and preserve the primary bridge failure.
|
|
682
|
-
*/
|
|
683
|
-
async function dismissUnregisteredTelegramAskKeyboard(client, adapter, params) {
|
|
684
|
-
try {
|
|
685
|
-
const rendered = adapter.renderTelegramMarkdown(params.rawSource);
|
|
686
|
-
const signal = AbortSignal.timeout(TELEGRAM_ASK_EXPIRY_EDIT_TIMEOUT_MS);
|
|
687
|
-
try {
|
|
688
|
-
await client.editMessageText({
|
|
689
|
-
chat_id: params.chat_id,
|
|
690
|
-
message_id: params.message_id,
|
|
691
|
-
text: rendered,
|
|
692
|
-
parse_mode: "MarkdownV2",
|
|
693
|
-
}, { signal });
|
|
694
|
-
}
|
|
695
|
-
catch (error) {
|
|
696
|
-
if (!isTelegramParseError(error, adapter)) {
|
|
697
|
-
throw error;
|
|
698
|
-
}
|
|
699
|
-
await client.editMessageText({
|
|
700
|
-
chat_id: params.chat_id,
|
|
701
|
-
message_id: params.message_id,
|
|
702
|
-
text: params.rawSource,
|
|
703
|
-
}, { signal });
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
catch {
|
|
707
|
-
// Best-effort: keyboard cleanup must not replace the bridge failure.
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
function registerTelegramAskTool(server, settings, client, adapter, fetchImpl) {
|
|
711
|
-
server.registerTool("TelegramAskButtons", {
|
|
712
|
-
title: "Ask via Telegram buttons",
|
|
713
|
-
description: "Ask the owner a question in an allowed Telegram chat with tappable inline-keyboard buttons (e.g. a confirmation or a multiple-choice). By default WAITS for either a button tap or the owner's next plain-text custom reply (up to the ask timeout) and returns that answer; a second concurrent ask in the same chat fails. On a proactive or scheduled turn (cron/heartbeat) set `wait:false`: the buttons are sent and the tool returns immediately, and the tap arrives later as a new message quoting the question.",
|
|
714
|
-
inputSchema: {
|
|
715
|
-
chat_id: z.union([z.string().min(1), z.number().int()]).describe("Telegram chat id from the adapter allowlist."),
|
|
716
|
-
question: z.string().min(1).describe("The question to show above the buttons."),
|
|
717
|
-
options: z
|
|
718
|
-
.array(z.string().min(1).max(100))
|
|
719
|
-
.min(2)
|
|
720
|
-
.max(adapter.TELEGRAM_ASK_MAX_OPTIONS)
|
|
721
|
-
.describe("Button labels (2–8). A tap returns its label; while waiting, plain text is accepted as a custom answer."),
|
|
722
|
-
note: z.string().min(1).optional().describe("Optional extra context shown beneath the question."),
|
|
723
|
-
wait: z
|
|
724
|
-
.boolean()
|
|
725
|
-
.optional()
|
|
726
|
-
.describe("Default true: block for a button tap or custom text reply (up to the ask timeout). Set false on proactive/scheduled (cron/heartbeat) turns — send the buttons and return immediately; a tapped answer arrives later as a new message quoting the question."),
|
|
727
|
-
},
|
|
728
|
-
}, async (args, extra) => {
|
|
729
|
-
assertTelegramChatAllowed(settings, args.chat_id, "TelegramAskButtons");
|
|
730
|
-
const inlineKeyboard = args.options.map((label, index) => [
|
|
731
|
-
{ text: label, callback_data: adapter.telegramAskCallbackData(index) },
|
|
732
|
-
]);
|
|
733
|
-
const replyMarkup = { inline_keyboard: inlineKeyboard };
|
|
734
|
-
// Raw source is what the bridge records and what the plain-text fallback (and the
|
|
735
|
-
// expiry edit) re-send; the rendered form is only for the MarkdownV2 send.
|
|
736
|
-
const source = args.note === undefined ? args.question : `${args.question}\n\n${args.note}`;
|
|
737
|
-
const rendered = adapter.renderTelegramMarkdown(source);
|
|
738
|
-
// Non-blocking mode (proactive/scheduled turns): send the buttons and return.
|
|
739
|
-
// No bridge is consulted — this must work even with no bridge configured.
|
|
740
|
-
if (args.wait === false) {
|
|
741
|
-
const sent = await sendTelegramAskKeyboard(client, adapter, {
|
|
742
|
-
chat_id: args.chat_id,
|
|
743
|
-
rendered,
|
|
744
|
-
rawSource: source,
|
|
745
|
-
replyMarkup,
|
|
746
|
-
signal: extra.signal,
|
|
747
|
-
});
|
|
748
|
-
return askToolResult(`Sent the question with ${String(args.options.length)} buttons to ${String(sent.chat.id)} (message ${String(sent.message_id)}). Not waiting — the tapped answer will arrive as a new message.`, { answered: false, reason: "not_waiting" }, { chat_id: sent.chat.id, message_id: sent.message_id, options: args.options });
|
|
749
|
-
}
|
|
750
|
-
// Blocking mode requires the interaction bridge to correlate the tap.
|
|
751
|
-
const bridge = settings.askBridge;
|
|
752
|
-
if (bridge === undefined) {
|
|
753
|
-
return askToolResult("TelegramAskButtons requires a live interaction bridge, but none is configured.", {
|
|
754
|
-
answered: false,
|
|
755
|
-
reason: "unsupported_channel",
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
const conversationId = `telegram:${String(args.chat_id)}`;
|
|
759
|
-
const created = await askBridgeRequest(bridge, fetchImpl, "POST", "/v1/asks", {
|
|
760
|
-
conversationId,
|
|
761
|
-
producerConversationId: bridge.conversationId,
|
|
762
|
-
runId: bridge.runId,
|
|
763
|
-
toolName: "TelegramAskButtons",
|
|
764
|
-
question: source,
|
|
765
|
-
options: args.options,
|
|
766
|
-
timeoutMs: bridge.timeoutMs,
|
|
767
|
-
postQuestion: false,
|
|
768
|
-
answerKind: "callback",
|
|
769
|
-
}, extra.signal);
|
|
770
|
-
if (created.status === 409) {
|
|
771
|
-
return askToolResult("A question is already pending for this Telegram chat. Wait for its answer instead of asking again.", { answered: false, reason: "already_pending" }, { chat_id: args.chat_id });
|
|
772
|
-
}
|
|
773
|
-
if (created.status === 501) {
|
|
774
|
-
return askToolResult("This Telegram chat is not registered with the interaction bridge. Ask your question in your final reply instead.", { answered: false, reason: "unsupported_channel" }, { chat_id: args.chat_id });
|
|
775
|
-
}
|
|
776
|
-
if (created.status !== 201 || typeof created.body.askId !== "string") {
|
|
777
|
-
throw new Error(`TelegramAskButtons: the interaction bridge rejected the ask (HTTP ${String(created.status)}).`);
|
|
778
|
-
}
|
|
779
|
-
const askId = created.body.askId;
|
|
780
|
-
let result;
|
|
781
|
-
try {
|
|
782
|
-
result = await sendTelegramAskKeyboard(client, adapter, {
|
|
783
|
-
chat_id: args.chat_id,
|
|
784
|
-
rendered,
|
|
785
|
-
rawSource: source,
|
|
786
|
-
replyMarkup,
|
|
787
|
-
signal: extra.signal,
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
catch (error) {
|
|
791
|
-
await cleanupBridgeAskBestEffort(bridge, fetchImpl, askId);
|
|
792
|
-
throw error;
|
|
793
|
-
}
|
|
794
|
-
const presentation = await (async () => {
|
|
795
|
-
try {
|
|
796
|
-
return await askBridgeRequest(bridge, fetchImpl, "PUT", `/v1/asks/${encodeURIComponent(askId)}/presentation`, { messageRef: String(result.message_id) }, extra.signal);
|
|
797
|
-
}
|
|
798
|
-
catch (error) {
|
|
799
|
-
await Promise.all([
|
|
800
|
-
cleanupBridgeAskBestEffort(bridge, fetchImpl, askId),
|
|
801
|
-
dismissUnregisteredTelegramAskKeyboard(client, adapter, {
|
|
802
|
-
chat_id: result.chat.id,
|
|
803
|
-
message_id: result.message_id,
|
|
804
|
-
rawSource: source,
|
|
805
|
-
}),
|
|
806
|
-
]);
|
|
807
|
-
throw error;
|
|
808
|
-
}
|
|
809
|
-
})();
|
|
810
|
-
if (presentation.status !== 204) {
|
|
811
|
-
await Promise.all([
|
|
812
|
-
cleanupBridgeAskBestEffort(bridge, fetchImpl, askId),
|
|
813
|
-
dismissUnregisteredTelegramAskKeyboard(client, adapter, {
|
|
814
|
-
chat_id: result.chat.id,
|
|
815
|
-
message_id: result.message_id,
|
|
816
|
-
rawSource: source,
|
|
817
|
-
}),
|
|
818
|
-
]);
|
|
819
|
-
throw new Error(`TelegramAskButtons: the interaction bridge rejected the keyboard reference (HTTP ${String(presentation.status)}).`);
|
|
820
|
-
}
|
|
821
|
-
const askResult = await awaitBridgeAsk(bridge, askId, "TelegramAskButtons", extra, fetchImpl, extra.signal, {
|
|
822
|
-
chat_id: result.chat.id,
|
|
823
|
-
message_id: result.message_id,
|
|
824
|
-
options: args.options,
|
|
825
|
-
});
|
|
826
|
-
// An expired/cancelled ask leaves a live-looking keyboard forever; append a note
|
|
827
|
-
// while keeping the buttons (a tap still starts a fresh turn). Best-effort only.
|
|
828
|
-
const reason = askResult.structuredContent.reason;
|
|
829
|
-
if (reason === "timeout" || reason === "cancelled") {
|
|
830
|
-
await editExpiredTelegramAskMessage(client, adapter, {
|
|
831
|
-
chat_id: result.chat.id,
|
|
832
|
-
message_id: result.message_id,
|
|
833
|
-
rawSource: source,
|
|
834
|
-
marker: reason === "timeout" ? TELEGRAM_ASK_EXPIRED_MARKER : TELEGRAM_ASK_CANCELLED_MARKER,
|
|
835
|
-
replyMarkup,
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
return askResult;
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
683
|
/**
|
|
842
684
|
* Register the single `TelegramSendFile` tool. A required `kind` param selects
|
|
843
685
|
* `"document"` (any file, shown as a downloadable document) or `"photo"` (an image
|
|
@@ -872,6 +714,11 @@ function registerTelegramSendFileTool(server, settings, client, adapter) {
|
|
|
872
714
|
}, async (args, extra) => {
|
|
873
715
|
extra.signal.throwIfAborted();
|
|
874
716
|
const kind = args.kind;
|
|
717
|
+
const sendDocument = client.sendDocument;
|
|
718
|
+
const sendPhoto = client.sendPhoto;
|
|
719
|
+
if ((kind === "document" && sendDocument === undefined) || (kind === "photo" && sendPhoto === undefined)) {
|
|
720
|
+
throw new Error(`TelegramSendFile: the ${kind} sender is unavailable.`);
|
|
721
|
+
}
|
|
875
722
|
const requestedChatId = "chat_id" in args ? args.chat_id : undefined;
|
|
876
723
|
const chatId = resolveTelegramSendFileChatId(settings, requestedChatId);
|
|
877
724
|
if ((args.data !== undefined) === (args.path !== undefined)) {
|
|
@@ -898,7 +745,7 @@ function registerTelegramSendFileTool(server, settings, client, adapter) {
|
|
|
898
745
|
throw new Error(`file exceeds the ${String(maxUploadBytes)}-byte upload cap.`);
|
|
899
746
|
}
|
|
900
747
|
try {
|
|
901
|
-
const sent = await
|
|
748
|
+
const sent = await sendDocument({
|
|
902
749
|
chat_id: chatId,
|
|
903
750
|
document: pathToFileURL(uploadPath).href,
|
|
904
751
|
...(args.caption === undefined ? {} : { caption: args.caption }),
|
|
@@ -923,13 +770,13 @@ function registerTelegramSendFileTool(server, settings, client, adapter) {
|
|
|
923
770
|
signal: extra.signal,
|
|
924
771
|
});
|
|
925
772
|
const result = kind === "document"
|
|
926
|
-
? await
|
|
773
|
+
? await sendDocument({
|
|
927
774
|
chat_id: chatId,
|
|
928
775
|
document: bytes,
|
|
929
776
|
filename,
|
|
930
777
|
...(args.caption === undefined ? {} : { caption: args.caption }),
|
|
931
778
|
}, { signal: extra.signal })
|
|
932
|
-
: await
|
|
779
|
+
: await sendPhoto({
|
|
933
780
|
chat_id: chatId,
|
|
934
781
|
photo: bytes,
|
|
935
782
|
filename,
|
|
@@ -1125,7 +972,7 @@ async function resolveSlackSendToolSettings(input, options) {
|
|
|
1125
972
|
return undefined;
|
|
1126
973
|
}
|
|
1127
974
|
}
|
|
1128
|
-
async function resolveTelegramSendToolSettings(input, options, tools
|
|
975
|
+
async function resolveTelegramSendToolSettings(input, options, tools) {
|
|
1129
976
|
try {
|
|
1130
977
|
const adapter = await loadTelegramModule();
|
|
1131
978
|
const config = await adapter.loadTelegramAdapterConfig({ env: input.env, jsonPath: input.configPath });
|
|
@@ -1142,7 +989,6 @@ async function resolveTelegramSendToolSettings(input, options, tools, askBridge)
|
|
|
1142
989
|
...(config.apiRoot === undefined ? {} : { apiRoot: config.apiRoot }),
|
|
1143
990
|
maxUploadBytes: config.attachments?.maxUploadBytes ?? adapter.DEFAULT_ATTACHMENT_MAX_BYTES,
|
|
1144
991
|
tools,
|
|
1145
|
-
...(askBridge === undefined ? {} : { askBridge }),
|
|
1146
992
|
...(config.sendTools === undefined ? {} : { sendTools: config.sendTools }),
|
|
1147
993
|
...(producingConversationId === undefined ? {} : { producingConversationId }),
|
|
1148
994
|
...(runOutputDir === undefined ? {} : { runOutputDir }),
|