@asermax/tachikoma 2.0.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 +64 -0
- package/dist/agent/adapter.d.ts +8 -0
- package/dist/agent/adapter.js +86 -0
- package/dist/agent/adapter.js.map +1 -0
- package/dist/agent/manager.d.ts +35 -0
- package/dist/agent/manager.js +76 -0
- package/dist/agent/manager.js.map +1 -0
- package/dist/agent/models.d.ts +46 -0
- package/dist/agent/models.js +96 -0
- package/dist/agent/models.js.map +1 -0
- package/dist/agent/side-run.d.ts +42 -0
- package/dist/agent/side-run.js +83 -0
- package/dist/agent/side-run.js.map +1 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.js +79 -0
- package/dist/app.js.map +1 -0
- package/dist/channels/types.d.ts +37 -0
- package/dist/channels/types.js +5 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/config/default-template.d.ts +1 -0
- package/dist/config/default-template.js +49 -0
- package/dist/config/default-template.js.map +1 -0
- package/dist/config/load.d.ts +8 -0
- package/dist/config/load.js +28 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/parse.d.ts +5 -0
- package/dist/config/parse.js +18 -0
- package/dist/config/parse.js.map +1 -0
- package/dist/config/schema.d.ts +29 -0
- package/dist/config/schema.js +35 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/coordinator.d.ts +54 -0
- package/dist/coordinator.js +344 -0
- package/dist/coordinator.js.map +1 -0
- package/dist/db/core-schema.d.ts +250 -0
- package/dist/db/core-schema.js +19 -0
- package/dist/db/core-schema.js.map +1 -0
- package/dist/db/index.d.ts +8 -0
- package/dist/db/index.js +16 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +4 -0
- package/dist/db/schema.js +7 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/state.d.ts +10 -0
- package/dist/db/state.js +36 -0
- package/dist/db/state.js.map +1 -0
- package/dist/domain/agent-events.d.ts +26 -0
- package/dist/domain/agent-events.js +2 -0
- package/dist/domain/agent-events.js.map +1 -0
- package/dist/domain/message.d.ts +25 -0
- package/dist/domain/message.js +17 -0
- package/dist/domain/message.js.map +1 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.js +27 -0
- package/dist/events.js.map +1 -0
- package/dist/extensions/api.d.ts +118 -0
- package/dist/extensions/api.js +7 -0
- package/dist/extensions/api.js.map +1 -0
- package/dist/extensions/boundary/detector.d.ts +20 -0
- package/dist/extensions/boundary/detector.js +57 -0
- package/dist/extensions/boundary/detector.js.map +1 -0
- package/dist/extensions/boundary/idle.d.ts +10 -0
- package/dist/extensions/boundary/idle.js +28 -0
- package/dist/extensions/boundary/idle.js.map +1 -0
- package/dist/extensions/boundary/index.d.ts +12 -0
- package/dist/extensions/boundary/index.js +65 -0
- package/dist/extensions/boundary/index.js.map +1 -0
- package/dist/extensions/boundary/summary.d.ts +5 -0
- package/dist/extensions/boundary/summary.js +33 -0
- package/dist/extensions/boundary/summary.js.map +1 -0
- package/dist/extensions/commands/index.d.ts +7 -0
- package/dist/extensions/commands/index.js +21 -0
- package/dist/extensions/commands/index.js.map +1 -0
- package/dist/extensions/context/index.d.ts +7 -0
- package/dist/extensions/context/index.js +65 -0
- package/dist/extensions/context/index.js.map +1 -0
- package/dist/extensions/context/processor.d.ts +27 -0
- package/dist/extensions/context/processor.js +228 -0
- package/dist/extensions/context/processor.js.map +1 -0
- package/dist/extensions/detached-processes/index.d.ts +12 -0
- package/dist/extensions/detached-processes/index.js +51 -0
- package/dist/extensions/detached-processes/index.js.map +1 -0
- package/dist/extensions/detached-processes/limits.d.ts +27 -0
- package/dist/extensions/detached-processes/limits.js +55 -0
- package/dist/extensions/detached-processes/limits.js.map +1 -0
- package/dist/extensions/detached-processes/output.d.ts +2 -0
- package/dist/extensions/detached-processes/output.js +26 -0
- package/dist/extensions/detached-processes/output.js.map +1 -0
- package/dist/extensions/detached-processes/reconcile.d.ts +31 -0
- package/dist/extensions/detached-processes/reconcile.js +71 -0
- package/dist/extensions/detached-processes/reconcile.js.map +1 -0
- package/dist/extensions/detached-processes/repository.d.ts +33 -0
- package/dist/extensions/detached-processes/repository.js +62 -0
- package/dist/extensions/detached-processes/repository.js.map +1 -0
- package/dist/extensions/detached-processes/schema.d.ts +252 -0
- package/dist/extensions/detached-processes/schema.js +23 -0
- package/dist/extensions/detached-processes/schema.js.map +1 -0
- package/dist/extensions/detached-processes/spawn.d.ts +40 -0
- package/dist/extensions/detached-processes/spawn.js +137 -0
- package/dist/extensions/detached-processes/spawn.js.map +1 -0
- package/dist/extensions/detached-processes/tools.d.ts +41 -0
- package/dist/extensions/detached-processes/tools.js +243 -0
- package/dist/extensions/detached-processes/tools.js.map +1 -0
- package/dist/extensions/detached-processes/watcher.d.ts +7 -0
- package/dist/extensions/detached-processes/watcher.js +19 -0
- package/dist/extensions/detached-processes/watcher.js.map +1 -0
- package/dist/extensions/external/index.d.ts +11 -0
- package/dist/extensions/external/index.js +40 -0
- package/dist/extensions/external/index.js.map +1 -0
- package/dist/extensions/external/installs.d.ts +39 -0
- package/dist/extensions/external/installs.js +98 -0
- package/dist/extensions/external/installs.js.map +1 -0
- package/dist/extensions/external/loader.d.ts +19 -0
- package/dist/extensions/external/loader.js +70 -0
- package/dist/extensions/external/loader.js.map +1 -0
- package/dist/extensions/external/tools.d.ts +17 -0
- package/dist/extensions/external/tools.js +112 -0
- package/dist/extensions/external/tools.js.map +1 -0
- package/dist/extensions/git/commit.d.ts +19 -0
- package/dist/extensions/git/commit.js +44 -0
- package/dist/extensions/git/commit.js.map +1 -0
- package/dist/extensions/git/git.d.ts +11 -0
- package/dist/extensions/git/git.js +29 -0
- package/dist/extensions/git/git.js.map +1 -0
- package/dist/extensions/git/hooks.d.ts +10 -0
- package/dist/extensions/git/hooks.js +88 -0
- package/dist/extensions/git/hooks.js.map +1 -0
- package/dist/extensions/git/index.d.ts +11 -0
- package/dist/extensions/git/index.js +28 -0
- package/dist/extensions/git/index.js.map +1 -0
- package/dist/extensions/git/processor.d.ts +13 -0
- package/dist/extensions/git/processor.js +52 -0
- package/dist/extensions/git/processor.js.map +1 -0
- package/dist/extensions/git/sync.d.ts +44 -0
- package/dist/extensions/git/sync.js +189 -0
- package/dist/extensions/git/sync.js.map +1 -0
- package/dist/extensions/git/tools.d.ts +21 -0
- package/dist/extensions/git/tools.js +101 -0
- package/dist/extensions/git/tools.js.map +1 -0
- package/dist/extensions/host.d.ts +31 -0
- package/dist/extensions/host.js +75 -0
- package/dist/extensions/host.js.map +1 -0
- package/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/index.js +32 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/memory/archive.d.ts +8 -0
- package/dist/extensions/memory/archive.js +46 -0
- package/dist/extensions/memory/archive.js.map +1 -0
- package/dist/extensions/memory/dates.d.ts +2 -0
- package/dist/extensions/memory/dates.js +7 -0
- package/dist/extensions/memory/dates.js.map +1 -0
- package/dist/extensions/memory/extraction.d.ts +17 -0
- package/dist/extensions/memory/extraction.js +218 -0
- package/dist/extensions/memory/extraction.js.map +1 -0
- package/dist/extensions/memory/index.d.ts +20 -0
- package/dist/extensions/memory/index.js +67 -0
- package/dist/extensions/memory/index.js.map +1 -0
- package/dist/extensions/memory/indexes.d.ts +14 -0
- package/dist/extensions/memory/indexes.js +64 -0
- package/dist/extensions/memory/indexes.js.map +1 -0
- package/dist/extensions/memory/layout.d.ts +20 -0
- package/dist/extensions/memory/layout.js +79 -0
- package/dist/extensions/memory/layout.js.map +1 -0
- package/dist/extensions/memory/maintenance.d.ts +21 -0
- package/dist/extensions/memory/maintenance.js +357 -0
- package/dist/extensions/memory/maintenance.js.map +1 -0
- package/dist/extensions/memory/prompts.d.ts +8 -0
- package/dist/extensions/memory/prompts.js +125 -0
- package/dist/extensions/memory/prompts.js.map +1 -0
- package/dist/extensions/memory/transcript.d.ts +18 -0
- package/dist/extensions/memory/transcript.js +79 -0
- package/dist/extensions/memory/transcript.js.map +1 -0
- package/dist/extensions/notifications/format.d.ts +5 -0
- package/dist/extensions/notifications/format.js +17 -0
- package/dist/extensions/notifications/format.js.map +1 -0
- package/dist/extensions/notifications/index.d.ts +13 -0
- package/dist/extensions/notifications/index.js +29 -0
- package/dist/extensions/notifications/index.js.map +1 -0
- package/dist/extensions/notifications/payload.d.ts +22 -0
- package/dist/extensions/notifications/payload.js +29 -0
- package/dist/extensions/notifications/payload.js.map +1 -0
- package/dist/extensions/notifications/router.d.ts +29 -0
- package/dist/extensions/notifications/router.js +55 -0
- package/dist/extensions/notifications/router.js.map +1 -0
- package/dist/extensions/notifications/tools.d.ts +12 -0
- package/dist/extensions/notifications/tools.js +41 -0
- package/dist/extensions/notifications/tools.js.map +1 -0
- package/dist/extensions/projects/context-provider.d.ts +9 -0
- package/dist/extensions/projects/context-provider.js +37 -0
- package/dist/extensions/projects/context-provider.js.map +1 -0
- package/dist/extensions/projects/git.d.ts +28 -0
- package/dist/extensions/projects/git.js +91 -0
- package/dist/extensions/projects/git.js.map +1 -0
- package/dist/extensions/projects/hooks.d.ts +7 -0
- package/dist/extensions/projects/hooks.js +42 -0
- package/dist/extensions/projects/hooks.js.map +1 -0
- package/dist/extensions/projects/index.d.ts +11 -0
- package/dist/extensions/projects/index.js +30 -0
- package/dist/extensions/projects/index.js.map +1 -0
- package/dist/extensions/projects/processor.d.ts +13 -0
- package/dist/extensions/projects/processor.js +63 -0
- package/dist/extensions/projects/processor.js.map +1 -0
- package/dist/extensions/projects/tools.d.ts +21 -0
- package/dist/extensions/projects/tools.js +118 -0
- package/dist/extensions/projects/tools.js.map +1 -0
- package/dist/extensions/registrations.d.ts +21 -0
- package/dist/extensions/registrations.js +12 -0
- package/dist/extensions/registrations.js.map +1 -0
- package/dist/extensions/repl/index.d.ts +2 -0
- package/dist/extensions/repl/index.js +85 -0
- package/dist/extensions/repl/index.js.map +1 -0
- package/dist/extensions/skills/agents.d.ts +17 -0
- package/dist/extensions/skills/agents.js +77 -0
- package/dist/extensions/skills/agents.js.map +1 -0
- package/dist/extensions/skills/delegate.d.ts +22 -0
- package/dist/extensions/skills/delegate.js +54 -0
- package/dist/extensions/skills/delegate.js.map +1 -0
- package/dist/extensions/skills/index.d.ts +11 -0
- package/dist/extensions/skills/index.js +43 -0
- package/dist/extensions/skills/index.js.map +1 -0
- package/dist/extensions/skills/reload.d.ts +8 -0
- package/dist/extensions/skills/reload.js +38 -0
- package/dist/extensions/skills/reload.js.map +1 -0
- package/dist/extensions/tasks/executor.d.ts +43 -0
- package/dist/extensions/tasks/executor.js +179 -0
- package/dist/extensions/tasks/executor.js.map +1 -0
- package/dist/extensions/tasks/expiration.d.ts +12 -0
- package/dist/extensions/tasks/expiration.js +17 -0
- package/dist/extensions/tasks/expiration.js.map +1 -0
- package/dist/extensions/tasks/generation.d.ts +14 -0
- package/dist/extensions/tasks/generation.js +70 -0
- package/dist/extensions/tasks/generation.js.map +1 -0
- package/dist/extensions/tasks/index.d.ts +14 -0
- package/dist/extensions/tasks/index.js +75 -0
- package/dist/extensions/tasks/index.js.map +1 -0
- package/dist/extensions/tasks/repository.d.ts +53 -0
- package/dist/extensions/tasks/repository.js +147 -0
- package/dist/extensions/tasks/repository.js.map +1 -0
- package/dist/extensions/tasks/schedule.d.ts +13 -0
- package/dist/extensions/tasks/schedule.js +32 -0
- package/dist/extensions/tasks/schedule.js.map +1 -0
- package/dist/extensions/tasks/schema.d.ts +423 -0
- package/dist/extensions/tasks/schema.js +45 -0
- package/dist/extensions/tasks/schema.js.map +1 -0
- package/dist/extensions/tasks/session-delivery.d.ts +18 -0
- package/dist/extensions/tasks/session-delivery.js +39 -0
- package/dist/extensions/tasks/session-delivery.js.map +1 -0
- package/dist/extensions/tasks/tools.d.ts +38 -0
- package/dist/extensions/tasks/tools.js +181 -0
- package/dist/extensions/tasks/tools.js.map +1 -0
- package/dist/extensions/telegram/buttons.d.ts +14 -0
- package/dist/extensions/telegram/buttons.js +49 -0
- package/dist/extensions/telegram/buttons.js.map +1 -0
- package/dist/extensions/telegram/channel.d.ts +39 -0
- package/dist/extensions/telegram/channel.js +201 -0
- package/dist/extensions/telegram/channel.js.map +1 -0
- package/dist/extensions/telegram/chunking.d.ts +7 -0
- package/dist/extensions/telegram/chunking.js +67 -0
- package/dist/extensions/telegram/chunking.js.map +1 -0
- package/dist/extensions/telegram/inbound.d.ts +7 -0
- package/dist/extensions/telegram/inbound.js +29 -0
- package/dist/extensions/telegram/inbound.js.map +1 -0
- package/dist/extensions/telegram/index.d.ts +13 -0
- package/dist/extensions/telegram/index.js +67 -0
- package/dist/extensions/telegram/index.js.map +1 -0
- package/dist/extensions/telegram/media.d.ts +39 -0
- package/dist/extensions/telegram/media.js +223 -0
- package/dist/extensions/telegram/media.js.map +1 -0
- package/dist/extensions/telegram/mutex.d.ts +9 -0
- package/dist/extensions/telegram/mutex.js +14 -0
- package/dist/extensions/telegram/mutex.js.map +1 -0
- package/dist/extensions/telegram/sending.d.ts +48 -0
- package/dist/extensions/telegram/sending.js +119 -0
- package/dist/extensions/telegram/sending.js.map +1 -0
- package/dist/extensions/telegram/streaming.d.ts +46 -0
- package/dist/extensions/telegram/streaming.js +140 -0
- package/dist/extensions/telegram/streaming.js.map +1 -0
- package/dist/extensions/telegram/tools.d.ts +80 -0
- package/dist/extensions/telegram/tools.js +232 -0
- package/dist/extensions/telegram/tools.js.map +1 -0
- package/dist/extensions/workflows/cleanup.d.ts +10 -0
- package/dist/extensions/workflows/cleanup.js +38 -0
- package/dist/extensions/workflows/cleanup.js.map +1 -0
- package/dist/extensions/workflows/index.d.ts +11 -0
- package/dist/extensions/workflows/index.js +42 -0
- package/dist/extensions/workflows/index.js.map +1 -0
- package/dist/extensions/workflows/loader.d.ts +27 -0
- package/dist/extensions/workflows/loader.js +90 -0
- package/dist/extensions/workflows/loader.js.map +1 -0
- package/dist/extensions/workflows/model.d.ts +19 -0
- package/dist/extensions/workflows/model.js +7 -0
- package/dist/extensions/workflows/model.js.map +1 -0
- package/dist/extensions/workflows/repository.d.ts +24 -0
- package/dist/extensions/workflows/repository.js +61 -0
- package/dist/extensions/workflows/repository.js.map +1 -0
- package/dist/extensions/workflows/schema.d.ts +193 -0
- package/dist/extensions/workflows/schema.js +20 -0
- package/dist/extensions/workflows/schema.js.map +1 -0
- package/dist/extensions/workflows/tools.d.ts +27 -0
- package/dist/extensions/workflows/tools.js +285 -0
- package/dist/extensions/workflows/tools.js.map +1 -0
- package/dist/log.d.ts +8 -0
- package/dist/log.js +15 -0
- package/dist/log.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +27 -0
- package/dist/main.js.map +1 -0
- package/dist/migration/ask.d.ts +8 -0
- package/dist/migration/ask.js +24 -0
- package/dist/migration/ask.js.map +1 -0
- package/dist/migration/config.d.ts +10 -0
- package/dist/migration/config.js +122 -0
- package/dist/migration/config.js.map +1 -0
- package/dist/migration/context.d.ts +3 -0
- package/dist/migration/context.js +24 -0
- package/dist/migration/context.js.map +1 -0
- package/dist/migration/database.d.ts +8 -0
- package/dist/migration/database.js +51 -0
- package/dist/migration/database.js.map +1 -0
- package/dist/migration/fs.d.ts +1 -0
- package/dist/migration/fs.js +11 -0
- package/dist/migration/fs.js.map +1 -0
- package/dist/migration/index.d.ts +11 -0
- package/dist/migration/index.js +28 -0
- package/dist/migration/index.js.map +1 -0
- package/dist/migration/skills.d.ts +19 -0
- package/dist/migration/skills.js +77 -0
- package/dist/migration/skills.js.map +1 -0
- package/dist/scheduler.d.ts +17 -0
- package/dist/scheduler.js +53 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/sessions/registry.d.ts +15 -0
- package/dist/sessions/registry.js +42 -0
- package/dist/sessions/registry.js.map +1 -0
- package/dist/workspace.d.ts +13 -0
- package/dist/workspace.js +32 -0
- package/dist/workspace.js.map +1 -0
- package/drizzle/0000_init.sql +19 -0
- package/drizzle/0001_extensions.sql +63 -0
- package/drizzle/meta/0000_snapshot.json +134 -0
- package/drizzle/meta/0001_snapshot.json +526 -0
- package/drizzle/meta/_journal.json +20 -0
- package/package.json +63 -0
- package/skills/skill-authoring/SKILL.md +168 -0
- package/skills/workflow-authoring/SKILL.md +251 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { splitMessage } from "./chunking.js";
|
|
2
|
+
// Telegram keeps a chat action visible for ~5 seconds, so refresh just before it expires.
|
|
3
|
+
const TYPING_REFRESH_MS = 5000;
|
|
4
|
+
const DELETE_RETRY_ATTEMPTS = 3;
|
|
5
|
+
const errorDetail = (error) => {
|
|
6
|
+
if (typeof error !== "object" || error == null)
|
|
7
|
+
return "";
|
|
8
|
+
const description = error.description;
|
|
9
|
+
return typeof description === "string"
|
|
10
|
+
? description
|
|
11
|
+
: error instanceof Error
|
|
12
|
+
? error.message
|
|
13
|
+
: "";
|
|
14
|
+
};
|
|
15
|
+
export const isMarkdownParseError = (error) => /can't parse entities/i.test(errorDetail(error));
|
|
16
|
+
/** Telegram rejects edits whose content matches the current message — benign. */
|
|
17
|
+
export const isMessageNotModifiedError = (error) => /message is not modified/i.test(errorDetail(error));
|
|
18
|
+
/** Try Markdown first; on a Telegram entity-parse rejection resend as plain text. */
|
|
19
|
+
export const sendWithMarkdownFallback = async (api, chatId, text, options = {}) => {
|
|
20
|
+
const base = options.silent === true ? { disable_notification: true } : {};
|
|
21
|
+
try {
|
|
22
|
+
const sent = await api.sendMessage(chatId, text, { ...base, parse_mode: "Markdown" });
|
|
23
|
+
return sent.message_id;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (!isMarkdownParseError(error))
|
|
27
|
+
throw error;
|
|
28
|
+
const sent = await api.sendMessage(chatId, text, base);
|
|
29
|
+
return sent.message_id;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Edit a message trying Markdown first, falling back to plain text on a parse
|
|
34
|
+
* rejection. "Message is not modified" rejections are swallowed — the visible
|
|
35
|
+
* content already matches.
|
|
36
|
+
*/
|
|
37
|
+
export const editWithMarkdownFallback = async (api, chatId, messageId, text) => {
|
|
38
|
+
try {
|
|
39
|
+
await api.editMessageText(chatId, messageId, text, { parse_mode: "Markdown" });
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (isMessageNotModifiedError(error))
|
|
43
|
+
return;
|
|
44
|
+
if (!isMarkdownParseError(error))
|
|
45
|
+
throw error;
|
|
46
|
+
try {
|
|
47
|
+
await api.editMessageText(chatId, messageId, text);
|
|
48
|
+
}
|
|
49
|
+
catch (fallbackError) {
|
|
50
|
+
if (!isMessageNotModifiedError(fallbackError))
|
|
51
|
+
throw fallbackError;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/** Send text as one or more messages, split at Telegram's length limit. */
|
|
56
|
+
export const sendChunked = async (api, chatId, text, options = {}) => {
|
|
57
|
+
if (text.trim().length === 0)
|
|
58
|
+
return [];
|
|
59
|
+
const ids = [];
|
|
60
|
+
for (const chunk of splitMessage(text)) {
|
|
61
|
+
ids.push(await sendWithMarkdownFallback(api, chatId, chunk, options));
|
|
62
|
+
}
|
|
63
|
+
return ids;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Copy+delete a silently-sent message so the copy fires a push notification.
|
|
67
|
+
* Copy-first ordering keeps the
|
|
68
|
+
* text safe: on copy failure the original is preserved and no delete runs; on
|
|
69
|
+
* delete failure the duplicate is accepted after retries.
|
|
70
|
+
*/
|
|
71
|
+
export const notifyViaCopyDelete = async (api, chatId, messageId, log, retryDelayMs = 500) => {
|
|
72
|
+
let copied;
|
|
73
|
+
try {
|
|
74
|
+
copied = await api.copyMessage(chatId, chatId, messageId);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
log.warn({ err: error, messageId }, "copy for push notification failed — keeping original");
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
for (let attempt = 0; attempt < DELETE_RETRY_ATTEMPTS; attempt += 1) {
|
|
81
|
+
try {
|
|
82
|
+
await api.deleteMessage(chatId, messageId);
|
|
83
|
+
return copied.message_id;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (attempt < DELETE_RETRY_ATTEMPTS - 1) {
|
|
87
|
+
await sleep(retryDelayMs);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
log.warn({ err: error, messageId }, "delete after copy failed — duplicate left visible");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return copied.message_id;
|
|
95
|
+
};
|
|
96
|
+
/** Full background-delivery flow: silent chunked send, then copy+delete to fire the push. */
|
|
97
|
+
export const deliverText = async (api, chatId, text, pushNotifications, log, retryDelayMs = 500) => {
|
|
98
|
+
const ids = await sendChunked(api, chatId, text, { silent: pushNotifications });
|
|
99
|
+
const lastId = ids.at(-1);
|
|
100
|
+
if (lastId == null)
|
|
101
|
+
return null;
|
|
102
|
+
if (!pushNotifications)
|
|
103
|
+
return lastId;
|
|
104
|
+
return (await notifyViaCopyDelete(api, chatId, lastId, log, retryDelayMs)) ?? lastId;
|
|
105
|
+
};
|
|
106
|
+
/** Show a typing indicator until the returned stop function is called. */
|
|
107
|
+
export const startTyping = (api, chatId, log) => {
|
|
108
|
+
const send = () => {
|
|
109
|
+
api
|
|
110
|
+
.sendChatAction(chatId, "typing")
|
|
111
|
+
.catch((error) => log.debug({ err: error }, "typing chat action failed"));
|
|
112
|
+
};
|
|
113
|
+
send();
|
|
114
|
+
const timer = setInterval(send, TYPING_REFRESH_MS);
|
|
115
|
+
timer.unref();
|
|
116
|
+
return () => clearInterval(timer);
|
|
117
|
+
};
|
|
118
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
119
|
+
//# sourceMappingURL=sending.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sending.js","sourceRoot":"","sources":["../../../src/extensions/telegram/sending.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Logger } from "../../log.ts";
|
|
2
|
+
import { type SendApi } from "./sending.ts";
|
|
3
|
+
export declare const EDIT_THROTTLE_MS = 1500;
|
|
4
|
+
export type StreamApi = Pick<SendApi, "sendMessage" | "editMessageText" | "deleteMessage">;
|
|
5
|
+
/**
|
|
6
|
+
* Progressive renderer for one agent exchange: sends a Telegram message early
|
|
7
|
+
* and edits it as text streams in, throttled and skipping no-op edits. Tool
|
|
8
|
+
* activity and pipeline status show as a transient italic line that the next
|
|
9
|
+
* text replaces. When the accumulated text outgrows Telegram's edit limit the
|
|
10
|
+
* current message is finalized in place and streaming continues in a new one.
|
|
11
|
+
* Edit failures degrade to the plain final-send behavior.
|
|
12
|
+
*/
|
|
13
|
+
export declare class StreamRenderer {
|
|
14
|
+
private readonly api;
|
|
15
|
+
private readonly chatId;
|
|
16
|
+
private readonly log;
|
|
17
|
+
private buffer;
|
|
18
|
+
private transient;
|
|
19
|
+
private messageId;
|
|
20
|
+
private lastRendered;
|
|
21
|
+
private lastEditAt;
|
|
22
|
+
private broken;
|
|
23
|
+
constructor(api: StreamApi, chatId: number, log: Logger);
|
|
24
|
+
appendText(text: string): Promise<void>;
|
|
25
|
+
/** Show a transient italic line (tool marker, status) below the streamed text. */
|
|
26
|
+
showTransient(line: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Flush the remaining text bypassing the throttle, upgrading the streaming
|
|
29
|
+
* message to its final chunked form. Returns the last message id sent, or
|
|
30
|
+
* null when the exchange produced no text.
|
|
31
|
+
*/
|
|
32
|
+
finalize(): Promise<number | null>;
|
|
33
|
+
private flush;
|
|
34
|
+
/**
|
|
35
|
+
* When the buffer exceeds the edit limit, finalize every full chunk (the
|
|
36
|
+
* first one in place, via edit) and keep only the tail streaming.
|
|
37
|
+
*/
|
|
38
|
+
private commitOverflow;
|
|
39
|
+
private compose;
|
|
40
|
+
/**
|
|
41
|
+
* Fallback path after a streaming failure: the partial message may hold
|
|
42
|
+
* stale or duplicated content, so drop it and send the full remainder fresh.
|
|
43
|
+
*/
|
|
44
|
+
private finalizeBroken;
|
|
45
|
+
private deleteCurrentMessage;
|
|
46
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { splitMessage, TELEGRAM_MAX_MESSAGE_LENGTH } from "./chunking.js";
|
|
2
|
+
import { editWithMarkdownFallback, sendChunked, sendWithMarkdownFallback, } from "./sending.js";
|
|
3
|
+
export const EDIT_THROTTLE_MS = 1500;
|
|
4
|
+
/**
|
|
5
|
+
* Progressive renderer for one agent exchange: sends a Telegram message early
|
|
6
|
+
* and edits it as text streams in, throttled and skipping no-op edits. Tool
|
|
7
|
+
* activity and pipeline status show as a transient italic line that the next
|
|
8
|
+
* text replaces. When the accumulated text outgrows Telegram's edit limit the
|
|
9
|
+
* current message is finalized in place and streaming continues in a new one.
|
|
10
|
+
* Edit failures degrade to the plain final-send behavior.
|
|
11
|
+
*/
|
|
12
|
+
export class StreamRenderer {
|
|
13
|
+
api;
|
|
14
|
+
chatId;
|
|
15
|
+
log;
|
|
16
|
+
buffer = "";
|
|
17
|
+
transient = null;
|
|
18
|
+
messageId = null;
|
|
19
|
+
lastRendered = "";
|
|
20
|
+
lastEditAt = 0;
|
|
21
|
+
broken = false;
|
|
22
|
+
constructor(api, chatId, log) {
|
|
23
|
+
this.api = api;
|
|
24
|
+
this.chatId = chatId;
|
|
25
|
+
this.log = log;
|
|
26
|
+
}
|
|
27
|
+
async appendText(text) {
|
|
28
|
+
this.transient = null;
|
|
29
|
+
this.buffer += text;
|
|
30
|
+
await this.flush(false);
|
|
31
|
+
}
|
|
32
|
+
/** Show a transient italic line (tool marker, status) below the streamed text. */
|
|
33
|
+
async showTransient(line) {
|
|
34
|
+
this.transient = line;
|
|
35
|
+
await this.flush(false);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Flush the remaining text bypassing the throttle, upgrading the streaming
|
|
39
|
+
* message to its final chunked form. Returns the last message id sent, or
|
|
40
|
+
* null when the exchange produced no text.
|
|
41
|
+
*/
|
|
42
|
+
async finalize() {
|
|
43
|
+
this.transient = null;
|
|
44
|
+
if (this.broken)
|
|
45
|
+
return this.finalizeBroken();
|
|
46
|
+
if (this.buffer.trim().length === 0) {
|
|
47
|
+
await this.deleteCurrentMessage();
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
let lastId = this.messageId;
|
|
51
|
+
for (const [index, chunk] of splitMessage(this.buffer).entries()) {
|
|
52
|
+
if (index === 0 && this.messageId != null) {
|
|
53
|
+
try {
|
|
54
|
+
await editWithMarkdownFallback(this.api, this.chatId, this.messageId, chunk);
|
|
55
|
+
lastId = this.messageId;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
this.log.warn({ err: error }, "final edit failed — sending as a new message");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
lastId = await sendWithMarkdownFallback(this.api, this.chatId, chunk);
|
|
63
|
+
}
|
|
64
|
+
return lastId;
|
|
65
|
+
}
|
|
66
|
+
async flush(force) {
|
|
67
|
+
if (this.broken)
|
|
68
|
+
return;
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
if (!force && now - this.lastEditAt < EDIT_THROTTLE_MS)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
await this.commitOverflow();
|
|
74
|
+
const display = this.compose();
|
|
75
|
+
if (display.length === 0 || display === this.lastRendered)
|
|
76
|
+
return;
|
|
77
|
+
if (this.messageId == null) {
|
|
78
|
+
this.messageId = await sendWithMarkdownFallback(this.api, this.chatId, display);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await editWithMarkdownFallback(this.api, this.chatId, this.messageId, display);
|
|
82
|
+
}
|
|
83
|
+
this.lastRendered = display;
|
|
84
|
+
this.lastEditAt = now;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// Stop streaming entirely — finalize() falls back to plain chunked sends.
|
|
88
|
+
this.broken = true;
|
|
89
|
+
this.log.warn({ err: error }, "streaming send/edit failed — falling back to final send");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* When the buffer exceeds the edit limit, finalize every full chunk (the
|
|
94
|
+
* first one in place, via edit) and keep only the tail streaming.
|
|
95
|
+
*/
|
|
96
|
+
async commitOverflow() {
|
|
97
|
+
if (this.buffer.length <= TELEGRAM_MAX_MESSAGE_LENGTH)
|
|
98
|
+
return;
|
|
99
|
+
const chunks = splitMessage(this.buffer);
|
|
100
|
+
this.buffer = chunks.at(-1) ?? "";
|
|
101
|
+
for (const chunk of chunks.slice(0, -1)) {
|
|
102
|
+
if (this.messageId != null) {
|
|
103
|
+
await editWithMarkdownFallback(this.api, this.chatId, this.messageId, chunk);
|
|
104
|
+
this.messageId = null;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
await sendWithMarkdownFallback(this.api, this.chatId, chunk);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.lastRendered = "";
|
|
111
|
+
}
|
|
112
|
+
compose() {
|
|
113
|
+
if (this.transient == null)
|
|
114
|
+
return this.buffer;
|
|
115
|
+
const marker = `_${this.transient}_`;
|
|
116
|
+
if (this.buffer.length === 0)
|
|
117
|
+
return marker;
|
|
118
|
+
const joined = `${this.buffer}\n\n${marker}`;
|
|
119
|
+
return joined.length <= TELEGRAM_MAX_MESSAGE_LENGTH ? joined : this.buffer;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fallback path after a streaming failure: the partial message may hold
|
|
123
|
+
* stale or duplicated content, so drop it and send the full remainder fresh.
|
|
124
|
+
*/
|
|
125
|
+
async finalizeBroken() {
|
|
126
|
+
await this.deleteCurrentMessage();
|
|
127
|
+
const ids = await sendChunked(this.api, this.chatId, this.buffer);
|
|
128
|
+
return ids.at(-1) ?? null;
|
|
129
|
+
}
|
|
130
|
+
async deleteCurrentMessage() {
|
|
131
|
+
const messageId = this.messageId;
|
|
132
|
+
if (messageId == null)
|
|
133
|
+
return;
|
|
134
|
+
this.messageId = null;
|
|
135
|
+
await this.api
|
|
136
|
+
.deleteMessage(this.chatId, messageId)
|
|
137
|
+
.catch((error) => this.log.warn({ err: error }, "streaming message cleanup failed"));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=streaming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../../src/extensions/telegram/streaming.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { InputFile } from "grammy";
|
|
3
|
+
import type { InlineKeyboardMarkup, ReactionTypeEmoji } from "grammy/types";
|
|
4
|
+
import { type Static, Type } from "typebox";
|
|
5
|
+
/** Narrow grammY API surface the tools call — fakeable in tests. */
|
|
6
|
+
export interface ToolApi {
|
|
7
|
+
sendMessage(chatId: number, text: string, other?: {
|
|
8
|
+
reply_markup?: InlineKeyboardMarkup;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
message_id: number;
|
|
11
|
+
}>;
|
|
12
|
+
sendPhoto(chatId: number, photo: InputFile, other?: {
|
|
13
|
+
caption?: string;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
message_id: number;
|
|
16
|
+
}>;
|
|
17
|
+
sendAudio(chatId: number, audio: InputFile, other?: {
|
|
18
|
+
caption?: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
message_id: number;
|
|
21
|
+
}>;
|
|
22
|
+
sendVideo(chatId: number, video: InputFile, other?: {
|
|
23
|
+
caption?: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
message_id: number;
|
|
26
|
+
}>;
|
|
27
|
+
sendDocument(chatId: number, document: InputFile, other?: {
|
|
28
|
+
caption?: string;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
message_id: number;
|
|
31
|
+
}>;
|
|
32
|
+
setMessageReaction(chatId: number, messageId: number, reaction: ReactionTypeEmoji[]): Promise<unknown>;
|
|
33
|
+
pinChatMessage(chatId: number, messageId: number, other?: {
|
|
34
|
+
disable_notification?: boolean;
|
|
35
|
+
}): Promise<unknown>;
|
|
36
|
+
unpinChatMessage(chatId: number, messageId: number): Promise<unknown>;
|
|
37
|
+
}
|
|
38
|
+
export interface ToolDeps {
|
|
39
|
+
api: ToolApi;
|
|
40
|
+
chatId: number;
|
|
41
|
+
workspaceRoot: string;
|
|
42
|
+
/** Resolved, deduplicated roots that send_telegram_file accepts. */
|
|
43
|
+
allowedRoots: string[];
|
|
44
|
+
getLastInboundMessageId: () => number | null;
|
|
45
|
+
getLastOutboundMessageId: () => number | null;
|
|
46
|
+
}
|
|
47
|
+
declare const MEDIA_TYPES: {
|
|
48
|
+
readonly photo: readonly [".png", ".jpg", ".jpeg", ".gif", ".webp"];
|
|
49
|
+
readonly audio: readonly [".mp3", ".ogg", ".wav", ".flac"];
|
|
50
|
+
readonly video: readonly [".mp4", ".avi", ".mov", ".webm"];
|
|
51
|
+
};
|
|
52
|
+
type OutboundMediaType = keyof typeof MEDIA_TYPES | "document";
|
|
53
|
+
export declare const detectMediaType: (path: string) => OutboundMediaType;
|
|
54
|
+
export declare const validateFilePath: (filePath: string, workspaceRoot: string, allowedRoots: string[]) => Promise<string>;
|
|
55
|
+
declare const SendFileParams: Type.TObject<{
|
|
56
|
+
filePath: Type.TString;
|
|
57
|
+
caption: Type.TOptional<Type.TString>;
|
|
58
|
+
}>;
|
|
59
|
+
export declare const handleSendFile: (deps: Pick<ToolDeps, "api" | "chatId" | "workspaceRoot" | "allowedRoots">, params: Static<typeof SendFileParams>) => Promise<string>;
|
|
60
|
+
declare const ReactParams: Type.TObject<{
|
|
61
|
+
emoji: Type.TString;
|
|
62
|
+
messageId: Type.TOptional<Type.TNumber>;
|
|
63
|
+
}>;
|
|
64
|
+
export declare const handleReactToMessage: (deps: Pick<ToolDeps, "api" | "chatId" | "getLastInboundMessageId">, params: Static<typeof ReactParams>) => Promise<string>;
|
|
65
|
+
export declare const handlePinMessage: (deps: Pick<ToolDeps, "api" | "chatId" | "getLastOutboundMessageId">) => Promise<string>;
|
|
66
|
+
declare const UnpinParams: Type.TObject<{
|
|
67
|
+
messageId: Type.TNumber;
|
|
68
|
+
}>;
|
|
69
|
+
export declare const handleUnpinMessage: (deps: Pick<ToolDeps, "api" | "chatId">, params: Static<typeof UnpinParams>) => Promise<string>;
|
|
70
|
+
declare const ButtonsParams: Type.TObject<{
|
|
71
|
+
prompt: Type.TString;
|
|
72
|
+
buttons: Type.TArray<Type.TArray<Type.TObject<{
|
|
73
|
+
label: Type.TString;
|
|
74
|
+
value: Type.TString;
|
|
75
|
+
}>>>;
|
|
76
|
+
singleUse: Type.TOptional<Type.TBoolean>;
|
|
77
|
+
}>;
|
|
78
|
+
export declare const handleSendMessageWithButtons: (deps: Pick<ToolDeps, "api" | "chatId">, params: Static<typeof ButtonsParams>) => Promise<string>;
|
|
79
|
+
export declare const registerTelegramTools: (pi: ExtensionAPI, deps: ToolDeps) => void;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { basename, extname, isAbsolute, join, resolve, sep } from "node:path";
|
|
3
|
+
import { InputFile } from "grammy";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
import { buildInlineKeyboard, validateButtons } from "./buttons.js";
|
|
6
|
+
// ---- send_telegram_file -------------------------------------------------------
|
|
7
|
+
const MEDIA_TYPES = {
|
|
8
|
+
photo: [".png", ".jpg", ".jpeg", ".gif", ".webp"],
|
|
9
|
+
audio: [".mp3", ".ogg", ".wav", ".flac"],
|
|
10
|
+
video: [".mp4", ".avi", ".mov", ".webm"],
|
|
11
|
+
};
|
|
12
|
+
export const detectMediaType = (path) => {
|
|
13
|
+
const suffix = extname(path).toLowerCase();
|
|
14
|
+
for (const [category, extensions] of Object.entries(MEDIA_TYPES)) {
|
|
15
|
+
if (extensions.includes(suffix)) {
|
|
16
|
+
return category;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return "document";
|
|
20
|
+
};
|
|
21
|
+
const isWithin = (root, path) => path === root || path.startsWith(root.endsWith(sep) ? root : `${root}${sep}`);
|
|
22
|
+
export const validateFilePath = async (filePath, workspaceRoot, allowedRoots) => {
|
|
23
|
+
const resolved = resolve(isAbsolute(filePath) ? filePath : join(workspaceRoot, filePath));
|
|
24
|
+
let stats;
|
|
25
|
+
try {
|
|
26
|
+
stats = await stat(resolved);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new Error(`File not found: ${resolved}`);
|
|
30
|
+
}
|
|
31
|
+
if (!stats.isFile())
|
|
32
|
+
throw new Error(`Path is not a regular file: ${resolved}`);
|
|
33
|
+
if (!allowedRoots.some((root) => isWithin(root, resolved))) {
|
|
34
|
+
throw new Error(`File must be under one of the allowed roots: ${allowedRoots.join(", ")} (got ${resolved})`);
|
|
35
|
+
}
|
|
36
|
+
return resolved;
|
|
37
|
+
};
|
|
38
|
+
const SendFileParams = Type.Object({
|
|
39
|
+
filePath: Type.String({
|
|
40
|
+
description: "Path to the file — workspace-relative, or absolute under the workspace, " +
|
|
41
|
+
"the system temporary directory, or a configured extra root",
|
|
42
|
+
}),
|
|
43
|
+
caption: Type.Optional(Type.String({ maxLength: 1024, description: "Brief description of the file" })),
|
|
44
|
+
});
|
|
45
|
+
export const handleSendFile = async (deps, params) => {
|
|
46
|
+
const resolved = await validateFilePath(params.filePath, deps.workspaceRoot, deps.allowedRoots);
|
|
47
|
+
const file = new InputFile(resolved);
|
|
48
|
+
const other = params.caption != null ? { caption: params.caption } : {};
|
|
49
|
+
switch (detectMediaType(resolved)) {
|
|
50
|
+
case "photo":
|
|
51
|
+
await deps.api.sendPhoto(deps.chatId, file, other);
|
|
52
|
+
break;
|
|
53
|
+
case "audio":
|
|
54
|
+
await deps.api.sendAudio(deps.chatId, file, other);
|
|
55
|
+
break;
|
|
56
|
+
case "video":
|
|
57
|
+
await deps.api.sendVideo(deps.chatId, file, other);
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
await deps.api.sendDocument(deps.chatId, file, other);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return `File sent: ${basename(resolved)}`;
|
|
64
|
+
};
|
|
65
|
+
// ---- react_to_message ---------------------------------------------------------
|
|
66
|
+
const ReactParams = Type.Object({
|
|
67
|
+
emoji: Type.String({
|
|
68
|
+
description: 'Reaction emoji, e.g. "👍" (Telegram supports a fixed set of reaction emoji)',
|
|
69
|
+
}),
|
|
70
|
+
messageId: Type.Optional(Type.Number({
|
|
71
|
+
description: "Telegram message ID to react to; defaults to the user's last message",
|
|
72
|
+
})),
|
|
73
|
+
});
|
|
74
|
+
export const handleReactToMessage = async (deps, params) => {
|
|
75
|
+
const messageId = params.messageId ?? deps.getLastInboundMessageId();
|
|
76
|
+
if (messageId == null)
|
|
77
|
+
throw new Error("No message available to react to");
|
|
78
|
+
// Telegram restricts reactions to a fixed emoji set; rather than hardcoding
|
|
79
|
+
// that evolving list, pass the string through and surface the API rejection.
|
|
80
|
+
await deps.api.setMessageReaction(deps.chatId, messageId, [
|
|
81
|
+
{ type: "emoji", emoji: params.emoji },
|
|
82
|
+
]);
|
|
83
|
+
return `Reacted to message ${messageId} with ${params.emoji}`;
|
|
84
|
+
};
|
|
85
|
+
// ---- pin_message / unpin_message ------------------------------------------------
|
|
86
|
+
export const handlePinMessage = async (deps) => {
|
|
87
|
+
const messageId = deps.getLastOutboundMessageId();
|
|
88
|
+
if (messageId == null)
|
|
89
|
+
throw new Error("No message available to pin");
|
|
90
|
+
// An audible pin is the point: it delivers the push notification.
|
|
91
|
+
await deps.api.pinChatMessage(deps.chatId, messageId, { disable_notification: false });
|
|
92
|
+
return `Message pinned (ID: ${messageId})`;
|
|
93
|
+
};
|
|
94
|
+
const UnpinParams = Type.Object({
|
|
95
|
+
messageId: Type.Number({ description: "The Telegram message ID to unpin" }),
|
|
96
|
+
});
|
|
97
|
+
export const handleUnpinMessage = async (deps, params) => {
|
|
98
|
+
await deps.api.unpinChatMessage(deps.chatId, params.messageId);
|
|
99
|
+
return `Message unpinned (ID: ${params.messageId})`;
|
|
100
|
+
};
|
|
101
|
+
// ---- send_message_with_buttons ---------------------------------------------------
|
|
102
|
+
const ButtonsParams = Type.Object({
|
|
103
|
+
prompt: Type.String({ description: "The message text shown above the buttons" }),
|
|
104
|
+
buttons: Type.Array(Type.Array(Type.Object({
|
|
105
|
+
label: Type.String({ description: "Text shown on the button" }),
|
|
106
|
+
value: Type.String({
|
|
107
|
+
description: "Machine-readable value you receive back on tap (max 58 UTF-8 bytes)",
|
|
108
|
+
}),
|
|
109
|
+
})), { description: "Rows of buttons; each row is a list of {label, value} objects" }),
|
|
110
|
+
singleUse: Type.Optional(Type.Boolean({
|
|
111
|
+
description: "Remove the keyboard after the first tap (default true)",
|
|
112
|
+
})),
|
|
113
|
+
});
|
|
114
|
+
export const handleSendMessageWithButtons = async (deps, params) => {
|
|
115
|
+
validateButtons(params.buttons);
|
|
116
|
+
const sent = await deps.api.sendMessage(deps.chatId, params.prompt, {
|
|
117
|
+
reply_markup: buildInlineKeyboard(params.buttons, params.singleUse ?? true),
|
|
118
|
+
});
|
|
119
|
+
return `Buttons sent (message_id: ${sent.message_id})`;
|
|
120
|
+
};
|
|
121
|
+
// ---- registration -----------------------------------------------------------------
|
|
122
|
+
const SEND_FILE_DESCRIPTION = `Send a file to the user via Telegram.
|
|
123
|
+
|
|
124
|
+
Supported media types (auto-detected from extension):
|
|
125
|
+
- Images (.png, .jpg, .jpeg, .gif, .webp) → sent as photo
|
|
126
|
+
- Audio (.mp3, .ogg, .wav, .flac) → sent as audio
|
|
127
|
+
- Video (.mp4, .avi, .mov, .webm) → sent as video
|
|
128
|
+
- All other files → sent as document
|
|
129
|
+
|
|
130
|
+
The file must exist on disk and be a regular file under one of the allowed roots
|
|
131
|
+
(the workspace, the system temporary directory, or a configured extra root).
|
|
132
|
+
Allowed roots are enumerated in any rejection error. Telegram enforces a 50MB
|
|
133
|
+
upload limit.`;
|
|
134
|
+
const REACT_DESCRIPTION = `React to a Telegram message with an emoji.
|
|
135
|
+
|
|
136
|
+
Defaults to the user's most recent message when messageId is omitted. Telegram
|
|
137
|
+
supports a fixed set of reaction emoji (e.g. 👍 ❤️ 🔥 🎉 🤔 👀); unsupported emoji
|
|
138
|
+
are rejected by the API.`;
|
|
139
|
+
const PIN_DESCRIPTION = `Pin the most recent response message in the Telegram chat.
|
|
140
|
+
|
|
141
|
+
The pin triggers a push notification so the user sees the pinned message promptly.
|
|
142
|
+
Returns the pinned message's Telegram ID on success. Fails when no response has
|
|
143
|
+
been sent yet or when pinning fails. Idempotent: pinning an already-pinned message
|
|
144
|
+
succeeds.`;
|
|
145
|
+
const UNPIN_DESCRIPTION = `Unpin a previously pinned message in the Telegram chat.
|
|
146
|
+
|
|
147
|
+
Fails when the message ID does not exist or unpinning fails. Idempotent:
|
|
148
|
+
unpinning a non-pinned message succeeds.`;
|
|
149
|
+
const BUTTONS_DESCRIPTION = `Present a Telegram inline keyboard of tappable buttons in the chat.
|
|
150
|
+
|
|
151
|
+
buttons is a list of rows; each row is a list of {label, value} buttons. label is
|
|
152
|
+
shown on the button; value is the machine-readable identifier you receive back on
|
|
153
|
+
tap. When singleUse is true (default) the keyboard is removed from the message
|
|
154
|
+
after any button is tapped.
|
|
155
|
+
|
|
156
|
+
When the user taps a button, you will receive a turn explicitly framed as
|
|
157
|
+
"The user tapped the option \`<value>\` out of the options you displayed.",
|
|
158
|
+
so you can distinguish taps from typed input. Use this for structured prompts
|
|
159
|
+
like yes/no, multiple-choice, or confirm/cancel.
|
|
160
|
+
|
|
161
|
+
Per-button value must be at most 58 UTF-8 bytes; labels must be non-empty; at
|
|
162
|
+
least one row with at least one button is required; at most 100 buttons total.`;
|
|
163
|
+
const textResult = (text) => ({
|
|
164
|
+
content: [{ type: "text", text }],
|
|
165
|
+
details: undefined,
|
|
166
|
+
});
|
|
167
|
+
export const registerTelegramTools = (pi, deps) => {
|
|
168
|
+
pi.registerTool({
|
|
169
|
+
name: "send_telegram_file",
|
|
170
|
+
label: "Send Telegram file",
|
|
171
|
+
description: SEND_FILE_DESCRIPTION,
|
|
172
|
+
promptSnippet: "Send a file from disk to the user via Telegram",
|
|
173
|
+
promptGuidelines: [
|
|
174
|
+
"Use send_telegram_file to deliver files (images, audio, video, documents) to the user instead of pasting their contents.",
|
|
175
|
+
],
|
|
176
|
+
parameters: SendFileParams,
|
|
177
|
+
async execute(_toolCallId, params) {
|
|
178
|
+
return textResult(await handleSendFile(deps, params));
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
pi.registerTool({
|
|
182
|
+
name: "react_to_message",
|
|
183
|
+
label: "React to message",
|
|
184
|
+
description: REACT_DESCRIPTION,
|
|
185
|
+
promptSnippet: "React to a Telegram message with an emoji",
|
|
186
|
+
promptGuidelines: [
|
|
187
|
+
"Use react_to_message for lightweight acknowledgements (e.g. 👍 on a quick confirmation) instead of a full reply.",
|
|
188
|
+
],
|
|
189
|
+
parameters: ReactParams,
|
|
190
|
+
async execute(_toolCallId, params) {
|
|
191
|
+
return textResult(await handleReactToMessage(deps, params));
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
pi.registerTool({
|
|
195
|
+
name: "pin_message",
|
|
196
|
+
label: "Pin message",
|
|
197
|
+
description: PIN_DESCRIPTION,
|
|
198
|
+
promptSnippet: "Pin the most recent response message in the Telegram chat",
|
|
199
|
+
promptGuidelines: [
|
|
200
|
+
"Use pin_message when the user should be able to find the last response again easily (reminders, important info).",
|
|
201
|
+
],
|
|
202
|
+
parameters: Type.Object({}),
|
|
203
|
+
async execute() {
|
|
204
|
+
return textResult(await handlePinMessage(deps));
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
pi.registerTool({
|
|
208
|
+
name: "unpin_message",
|
|
209
|
+
label: "Unpin message",
|
|
210
|
+
description: UNPIN_DESCRIPTION,
|
|
211
|
+
promptSnippet: "Unpin a previously pinned Telegram message",
|
|
212
|
+
promptGuidelines: ["Use unpin_message when a pinned message is no longer relevant."],
|
|
213
|
+
parameters: UnpinParams,
|
|
214
|
+
async execute(_toolCallId, params) {
|
|
215
|
+
return textResult(await handleUnpinMessage(deps, params));
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
pi.registerTool({
|
|
219
|
+
name: "send_message_with_buttons",
|
|
220
|
+
label: "Send message with buttons",
|
|
221
|
+
description: BUTTONS_DESCRIPTION,
|
|
222
|
+
promptSnippet: "Present tappable inline buttons to the user via Telegram",
|
|
223
|
+
promptGuidelines: [
|
|
224
|
+
"Use send_message_with_buttons for structured choices (yes/no, multiple-choice, confirm/cancel) instead of asking the user to type an option.",
|
|
225
|
+
],
|
|
226
|
+
parameters: ButtonsParams,
|
|
227
|
+
async execute(_toolCallId, params) {
|
|
228
|
+
return textResult(await handleSendMessageWithButtons(deps, params));
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/extensions/telegram/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PostProcessor } from "../api.ts";
|
|
2
|
+
import type { WorkflowStateRepository } from "./repository.ts";
|
|
3
|
+
export declare const DEFAULT_STALE_HOURS = 24;
|
|
4
|
+
export type StaleRepository = Pick<WorkflowStateRepository, "listStale" | "softDelete">;
|
|
5
|
+
/**
|
|
6
|
+
* Post-processor that expires workflow instances abandoned across sessions:
|
|
7
|
+
* soft-deletes records untouched for longer than the threshold and removes
|
|
8
|
+
* their scratchpad files.
|
|
9
|
+
*/
|
|
10
|
+
export declare const createStaleWorkflowCleanup: (repository: StaleRepository, staleHours?: number) => PostProcessor;
|