@songsid/agend 2.3.3 → 2.3.4-beta.10
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 +3 -3
- package/README.zh-TW.md +3 -3
- package/THIRD_PARTY_NOTICES.md +37 -0
- package/dist/agend-home.d.ts +13 -0
- package/dist/agend-home.js +42 -0
- package/dist/agend-home.js.map +1 -1
- package/dist/agent-cli-instructions.md +2 -2
- package/dist/agent-credentials.d.ts +24 -0
- package/dist/agent-credentials.js +66 -0
- package/dist/agent-credentials.js.map +1 -0
- package/dist/agent-endpoint.d.ts +3 -1
- package/dist/agent-endpoint.js +13 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/auth-api.d.ts +16 -3
- package/dist/auth-api.js +32 -8
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/session-authorizations.d.ts +28 -4
- package/dist/authority/session-authorizations.js +63 -17
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +15 -1
- package/dist/authority/web-authority.js +27 -10
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/antigravity.js +3 -1
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +3 -0
- package/dist/backend/claude-code.js +100 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-footer-hints.d.ts +4 -0
- package/dist/backend/codex-footer-hints.js +27 -0
- package/dist/backend/codex-footer-hints.js.map +1 -0
- package/dist/backend/codex-session-lookup.d.ts +35 -0
- package/dist/backend/codex-session-lookup.js +93 -0
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/codex.d.ts +27 -0
- package/dist/backend/codex.js +136 -23
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +2 -0
- package/dist/backend/kiro-identity.js +13 -6
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +3 -0
- package/dist/backend/kiro.js +68 -5
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.js +5 -3
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +13 -1
- package/dist/backend/types.js +3 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-availability.d.ts +6 -0
- package/dist/backend-availability.js +13 -5
- package/dist/backend-availability.js.map +1 -1
- package/dist/cache-ledger.d.ts +85 -0
- package/dist/cache-ledger.js +236 -1
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-service.d.ts +5 -0
- package/dist/cache-service.js +16 -4
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.js +27 -4
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +51 -0
- package/dist/channel/adapters/telegram.js +238 -18
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/inbox-temp-name.d.ts +9 -0
- package/dist/channel/inbox-temp-name.js +13 -0
- package/dist/channel/inbox-temp-name.js.map +1 -0
- package/dist/channel/mcp-tools.js +3 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +2 -0
- package/dist/chat-log-ids.d.ts +48 -0
- package/dist/chat-log-ids.js +182 -0
- package/dist/chat-log-ids.js.map +1 -0
- package/dist/classic-channel-manager.d.ts +8 -1
- package/dist/classic-channel-manager.js +24 -2
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +4 -6
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +18 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-notice.d.ts +10 -0
- package/dist/cross-instance-notice.js +67 -5
- package/dist/cross-instance-notice.js.map +1 -1
- package/dist/daemon.d.ts +52 -0
- package/dist/daemon.js +538 -35
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-consumed-watch.d.ts +23 -1
- package/dist/delivery-consumed-watch.js +30 -3
- package/dist/delivery-consumed-watch.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +7 -0
- package/dist/delivery-reconciliation.js +9 -1
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/dialog-screen-text.d.ts +16 -0
- package/dist/dialog-screen-text.js +51 -0
- package/dist/dialog-screen-text.js.map +1 -0
- package/dist/env-file.d.ts +2 -0
- package/dist/env-file.js +13 -8
- package/dist/env-file.js.map +1 -1
- package/dist/export-import.d.ts +7 -0
- package/dist/export-import.js +44 -10
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-context.d.ts +2 -0
- package/dist/fleet-manager.d.ts +172 -12
- package/dist/fleet-manager.js +897 -139
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-yaml-patch.d.ts +3 -0
- package/dist/fleet-yaml-patch.js +103 -0
- package/dist/fleet-yaml-patch.js.map +1 -0
- package/dist/general-knowledge/skills/concepts/SKILL.md +393 -0
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +2 -1
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -1
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +41 -4
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +2 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +14 -4
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
- package/dist/install-stream.d.ts +53 -0
- package/dist/install-stream.js +134 -0
- package/dist/install-stream.js.map +1 -0
- package/dist/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +22 -2
- package/dist/instance-lifecycle.js +103 -19
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instance-topic-delete.d.ts +77 -0
- package/dist/instance-topic-delete.js +123 -0
- package/dist/instance-topic-delete.js.map +1 -0
- package/dist/instructions.d.ts +2 -2
- package/dist/instructions.js +2 -2
- package/dist/instructions.js.map +1 -1
- package/dist/interaction-observation.d.ts +1 -0
- package/dist/interaction-observation.js +1 -1
- package/dist/interaction-observation.js.map +1 -1
- package/dist/kiro-engine-status.d.ts +11 -0
- package/dist/kiro-engine-status.js +6 -0
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-resume-summary.d.ts +37 -0
- package/dist/kiro-resume-summary.js +85 -0
- package/dist/kiro-resume-summary.js.map +1 -0
- package/dist/locale.js +130 -6
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +30 -0
- package/dist/log-service.js +36 -3
- package/dist/log-service.js.map +1 -1
- package/dist/log-steps.d.ts +4 -3
- package/dist/log-steps.js +32 -7
- package/dist/log-steps.js.map +1 -1
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +43 -1
- package/dist/logger.js.map +1 -1
- package/dist/login-controller.js +9 -1
- package/dist/login-controller.js.map +1 -1
- package/dist/login-flows.d.ts +2 -0
- package/dist/login-flows.js +5 -2
- package/dist/login-flows.js.map +1 -1
- package/dist/login-manager.d.ts +6 -0
- package/dist/login-manager.js +10 -1
- package/dist/login-manager.js.map +1 -1
- package/dist/missed-offline.d.ts +25 -0
- package/dist/missed-offline.js +79 -0
- package/dist/missed-offline.js.map +1 -0
- package/dist/needs-you-hub.d.ts +6 -1
- package/dist/needs-you-hub.js +31 -8
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +22 -2
- package/dist/needs-you.js +17 -3
- package/dist/needs-you.js.map +1 -1
- package/dist/next-steps.d.ts +12 -2
- package/dist/next-steps.js +19 -3
- package/dist/next-steps.js.map +1 -1
- package/dist/office-share-page.d.ts +1 -1
- package/dist/office-share-page.js +6 -2
- package/dist/office-share-page.js.map +1 -1
- package/dist/office-share-service.d.ts +57 -0
- package/dist/office-share-service.js +76 -11
- package/dist/office-share-service.js.map +1 -1
- package/dist/office-share.d.ts +8 -0
- package/dist/office-share.js +34 -0
- package/dist/office-share.js.map +1 -1
- package/dist/outbound-handlers.js +59 -3
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +1 -0
- package/dist/outbound-schemas.js +10 -4
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/package-preimage.js +27 -2
- package/dist/package-preimage.js.map +1 -1
- package/dist/page-share.d.ts +122 -0
- package/dist/page-share.js +199 -0
- package/dist/page-share.js.map +1 -0
- package/dist/public-preview.d.ts +4 -0
- package/dist/public-preview.js +5 -4
- package/dist/public-preview.js.map +1 -1
- package/dist/public-web-gateway.js +5 -0
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/queued-handoffs.d.ts +33 -0
- package/dist/queued-handoffs.js +75 -0
- package/dist/queued-handoffs.js.map +1 -0
- package/dist/quickstart-api.d.ts +5 -0
- package/dist/quickstart-api.js +1 -1
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +40 -0
- package/dist/quickstart.js +174 -28
- package/dist/quickstart.js.map +1 -1
- package/dist/remote-card-answer.js +2 -0
- package/dist/remote-card-answer.js.map +1 -1
- package/dist/remote-card-prompt.js +4 -2
- package/dist/remote-card-prompt.js.map +1 -1
- package/dist/reply-buttons-controller.js +10 -0
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/scheduler/db.d.ts +2 -1
- package/dist/scheduler/db.js +23 -9
- package/dist/scheduler/db.js.map +1 -1
- package/dist/selection-card.d.ts +20 -0
- package/dist/selection-card.js +202 -0
- package/dist/selection-card.js.map +1 -0
- package/dist/settings-api.d.ts +13 -0
- package/dist/settings-api.js +16 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +5 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/setup-host.js +2 -2
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/stall-reporter.d.ts +21 -0
- package/dist/stall-reporter.js +82 -0
- package/dist/stall-reporter.js.map +1 -0
- package/dist/tmux-control.d.ts +2 -2
- package/dist/tmux-control.js +4 -2
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.d.ts +6 -3
- package/dist/tmux-exec-helper.js +115 -53
- package/dist/tmux-exec-helper.js.map +1 -1
- package/dist/tmux-manager.d.ts +6 -0
- package/dist/tmux-manager.js +17 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +5 -2
- package/dist/tool-permissions.js +6 -5
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tool-progress.d.ts +2 -0
- package/dist/tool-progress.js +19 -3
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.js +4 -0
- package/dist/topic-commands.js.map +1 -1
- package/dist/tunnel/cloudflared-install.d.ts +4 -0
- package/dist/tunnel/cloudflared-install.js +20 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +8 -1
- package/dist/tunnel/cloudflared.js +34 -6
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/lease.d.ts +3 -3
- package/dist/tunnel/lease.js +23 -19
- package/dist/tunnel/lease.js.map +1 -1
- package/dist/tunnel/manager.js +6 -7
- package/dist/tunnel/manager.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/ui/chat-store.js +28 -4
- package/dist/ui/chat-strings.js +40 -4
- package/dist/ui/chat-thread.js +52 -10
- package/dist/ui/fleet-canvas.js +4 -3
- package/dist/ui/fleet-office-model.js +55 -22
- package/dist/ui/fleet-office-overview.js +6 -38
- package/dist/ui/fleet-office-pack.js +4 -3
- package/dist/ui/fleet-office-skin.js +114 -21
- package/dist/ui/fleet-office-store.js +5 -0
- package/dist/ui/fleet-office-zones.js +2 -2
- package/dist/ui/fleet-office.js +41 -9
- package/dist/ui/fleet-usage.js +90 -27
- package/dist/ui/page-share-dialog.js +191 -0
- package/dist/ui/panel-chat.js +16 -0
- package/dist/ui/panel-log.js +7 -1
- package/dist/ui/panel-needs.js +17 -8
- package/dist/ui/panel-notices.js +98 -0
- package/dist/ui/panel-settings.js +9 -2
- package/dist/ui/preview-pages.js +41 -0
- package/dist/ui/preview-panel-actions.js +161 -0
- package/dist/ui/preview-panel.js +209 -28
- package/dist/ui/preview-versions.js +155 -0
- package/dist/ui/preview.js +88 -3
- package/dist/ui/settings-registry.js +1 -0
- package/dist/ui/settings-strings.js +6 -2
- package/dist/ui/shared/app-i18n.js +6 -6
- package/dist/ui/shared/app-needs.js +1 -1
- package/dist/ui/shared/app-route.js +4 -0
- package/dist/ui/shared/app-shell.js +9 -5
- package/dist/ui/shared/app-store.js +71 -1
- package/dist/ui/shared/app-stream.js +27 -1
- package/dist/ui/shared/app.css +94 -4
- package/dist/ui/shared/app.js +22 -1
- package/dist/ui/shared/office/guild.png +0 -0
- package/dist/ui/shared/office/school.png +0 -0
- package/dist/ui/shared/panel-view.js +339 -17
- package/dist/ui/shared/signin.js +12 -1
- package/dist/ui/shared/ui-icons.js +3 -0
- package/dist/ui/shared/usage-dialog.js +18 -1
- package/dist/ui/shared/view-history.css +17 -0
- package/dist/ui/shared/view-history.js +438 -0
- package/dist/ui/shared/view-strings.js +42 -0
- package/dist/ui/shared/web-authorize.js +3 -1
- package/dist/unattended-install.d.ts +6 -0
- package/dist/unattended-install.js +79 -0
- package/dist/unattended-install.js.map +1 -0
- package/dist/usage/capacity.d.ts +69 -0
- package/dist/usage/capacity.js +119 -0
- package/dist/usage/capacity.js.map +1 -0
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +3 -0
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/profile-timeline.d.ts +16 -0
- package/dist/usage/profile-timeline.js +58 -0
- package/dist/usage/profile-timeline.js.map +1 -0
- package/dist/usage/quota-history.d.ts +43 -0
- package/dist/usage/quota-history.js +168 -0
- package/dist/usage/quota-history.js.map +1 -0
- package/dist/usage/usage-api.d.ts +31 -0
- package/dist/usage/usage-api.js +80 -1
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/usage-report.d.ts +25 -1
- package/dist/usage-report.js +55 -4
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.d.ts +4 -1
- package/dist/usage-rollup.js +12 -3
- package/dist/usage-rollup.js.map +1 -1
- package/dist/view-api.d.ts +31 -0
- package/dist/view-api.js +186 -41
- package/dist/view-api.js.map +1 -1
- package/dist/view-history.d.ts +84 -0
- package/dist/view-history.js +116 -0
- package/dist/view-history.js.map +1 -0
- package/dist/web-api.d.ts +32 -0
- package/dist/web-api.js +195 -5
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +7 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +20 -0
- package/dist/web-chat-history.js +21 -0
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-guest-access.js +5 -1
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-login.d.ts +6 -3
- package/dist/web-login.js +26 -15
- package/dist/web-login.js.map +1 -1
- package/dist/web-notices.d.ts +51 -0
- package/dist/web-notices.js +118 -0
- package/dist/web-notices.js.map +1 -0
- package/dist/web-office-pack-tools.d.ts +1 -1
- package/dist/web-office-pack-tools.js +1 -1
- package/dist/web-office-pack-tools.js.map +1 -1
- package/dist/web-only-fleet.d.ts +22 -0
- package/dist/web-only-fleet.js +13 -0
- package/dist/web-only-fleet.js.map +1 -0
- package/dist/web-preview.d.ts +32 -3
- package/dist/web-preview.js +52 -19
- package/dist/web-preview.js.map +1 -1
- package/dist/web-session-authorization.d.ts +13 -2
- package/dist/web-session-authorization.js +10 -3
- package/dist/web-session-authorization.js.map +1 -1
- package/dist/web-session.d.ts +12 -0
- package/dist/web-session.js +22 -8
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +2 -0
- package/dist/web-shell-routes.js +3 -0
- package/dist/web-shell-routes.js.map +1 -1
- package/launcher/agend-install.cjs +16 -2
- package/launcher/private-home.cjs +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { nativeHumanOrigin } from "../../human-delivery-receipts.js";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { inboxTempName } from "../inbox-temp-name.js";
|
|
4
5
|
import { Agent as HttpAgent } from "node:http";
|
|
5
6
|
import { Agent as HttpsAgent } from "node:https";
|
|
6
|
-
import { createReadStream, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
7
|
+
import { createReadStream, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
7
8
|
import { join, extname, basename } from "node:path";
|
|
9
|
+
import { atomicWriteFileSync } from "../../atomic-write.js";
|
|
8
10
|
import { Bot, GrammyError, HttpError, InputFile } from "grammy";
|
|
9
11
|
import { InlineKeyboard } from "grammy";
|
|
10
12
|
import { stripPlaceRefs } from "../../topic-link.js";
|
|
@@ -51,9 +53,34 @@ export const TELEGRAM_ALLOWED_UPDATES = [
|
|
|
51
53
|
"callback_query",
|
|
52
54
|
"message_reaction",
|
|
53
55
|
];
|
|
56
|
+
/**
|
|
57
|
+
* Pending Telegram updates older than this are filed as a Needs-you item
|
|
58
|
+
* ("missed while AgEnD was offline") and never auto-injected into an agent.
|
|
59
|
+
* Telegram itself keeps the getUpdates queue for about 24 hours.
|
|
60
|
+
*/
|
|
61
|
+
export const TELEGRAM_PENDING_LIVE_MAX_MS = 2 * 60 * 60 * 1000;
|
|
62
|
+
/**
|
|
63
|
+
* Telegram may pick a new random `update_id` after about a week with no
|
|
64
|
+
* updates (Bot API). A floor older than this is ignored so inbound is not
|
|
65
|
+
* dropped forever.
|
|
66
|
+
*/
|
|
67
|
+
export const TELEGRAM_UPDATE_ID_FLOOR_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
68
|
+
/** Telegram's default getUpdates page size; a shorter page means the queue drained. */
|
|
69
|
+
export const TELEGRAM_GETUPDATES_LIMIT = 100;
|
|
70
|
+
/** Crash-safe last processed `update_id` for this adapter (inside inboxDir; prune skips it). */
|
|
71
|
+
export function telegramLastUpdateIdPath(inboxDir, adapterId) {
|
|
72
|
+
const safe = adapterId.replace(/[^A-Za-z0-9._-]+/g, "_") || "adapter";
|
|
73
|
+
return join(inboxDir, `telegram-${safe}-last-update-id`);
|
|
74
|
+
}
|
|
54
75
|
function errorMessage(err) {
|
|
55
76
|
return err instanceof Error ? err.message : String(err);
|
|
56
77
|
}
|
|
78
|
+
function telegramUpdateAgeMs(update, now) {
|
|
79
|
+
const date = update.message?.date ?? update.callback_query?.message?.date;
|
|
80
|
+
if (typeof date !== "number" || !Number.isFinite(date))
|
|
81
|
+
return null;
|
|
82
|
+
return now - date * 1000;
|
|
83
|
+
}
|
|
57
84
|
/** Remove Bot API credentials from nested node-fetch errors before logging. */
|
|
58
85
|
export function redactTelegramSecrets(value, botToken = "") {
|
|
59
86
|
let redacted = value.replace(/\/bot[^/\s]+(?=\/)/gi, "/bot[REDACTED]");
|
|
@@ -246,6 +273,14 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
246
273
|
/** Topics the probe itself is addressing right now — their failures are results, not hints. */
|
|
247
274
|
probingThreads = new Set();
|
|
248
275
|
topicGoneHintAt = new Map();
|
|
276
|
+
/** Last processed `update_id`; 0 means no floor. Telegram ids are usually monotonic. */
|
|
277
|
+
lastUpdateId = 0;
|
|
278
|
+
floorBotId;
|
|
279
|
+
floorRecordedAt = 0;
|
|
280
|
+
/** False until a getUpdates page shorter than TELEGRAM_GETUPDATES_LIMIT (the queue drained). */
|
|
281
|
+
backlogDrained = false;
|
|
282
|
+
backlogUpdateIds = new Set();
|
|
283
|
+
persistFloorTimer;
|
|
249
284
|
constructor(opts) {
|
|
250
285
|
super();
|
|
251
286
|
this.id = opts.id;
|
|
@@ -296,6 +331,7 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
296
331
|
}
|
|
297
332
|
},
|
|
298
333
|
});
|
|
334
|
+
this.loadFloor();
|
|
299
335
|
this._registerHandlers();
|
|
300
336
|
}
|
|
301
337
|
/**
|
|
@@ -313,11 +349,21 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
313
349
|
if (!this.bot.api.config?.use)
|
|
314
350
|
return;
|
|
315
351
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
352
|
+
// grammY.stop sends a final offset ACK, and an already-fetched batch can finish later.
|
|
353
|
+
// Once fenced, neither path may confirm or admit updates for the stopping fleet.
|
|
354
|
+
if (method === "getUpdates" && this.stopped)
|
|
355
|
+
return { ok: true, result: [] };
|
|
316
356
|
const requestGeneration = this.httpAgentGeneration;
|
|
317
357
|
try {
|
|
318
|
-
|
|
358
|
+
const result = await prev(method, payload, signal);
|
|
359
|
+
if (method === "getUpdates" && this.stopped)
|
|
360
|
+
return { ok: true, result: [] };
|
|
361
|
+
this.noteGetUpdatesBacklog(method, result);
|
|
362
|
+
return result;
|
|
319
363
|
}
|
|
320
364
|
catch (err) {
|
|
365
|
+
if (method === "getUpdates" && this.stopped)
|
|
366
|
+
return { ok: true, result: [] };
|
|
321
367
|
if (!(err instanceof HttpError))
|
|
322
368
|
throw err;
|
|
323
369
|
const firstDetail = telegramNetworkErrorDetails(err, this.botToken);
|
|
@@ -325,6 +371,8 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
325
371
|
console.warn(`[telegram] ${method} transport failed; HTTP pool ${reset ? "reset" : "reset suppressed by cooldown"}: ${firstDetail}`);
|
|
326
372
|
if (isDefinitelyPreDeliveryNetworkError(err) && !signal?.aborted) {
|
|
327
373
|
await new Promise(resolve => setTimeout(resolve, TELEGRAM_NETWORK_RETRY_MS));
|
|
374
|
+
if (method === "getUpdates" && this.stopped)
|
|
375
|
+
return { ok: true, result: [] };
|
|
328
376
|
const retryGeneration = this.httpAgentGeneration;
|
|
329
377
|
try {
|
|
330
378
|
return await prev(method, payload, signal);
|
|
@@ -369,14 +417,42 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
369
417
|
return true;
|
|
370
418
|
}
|
|
371
419
|
_registerHandlers() {
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
// Put this ahead of rich-message middleware as well as ordinary messages.
|
|
420
|
+
// Dedup, own-message skip, and the downtime age cap run before any handler.
|
|
421
|
+
// Record the update_id before acting so a crash mid-batch cannot inject twice.
|
|
375
422
|
this.bot.use(async (ctx, next) => {
|
|
376
|
-
|
|
423
|
+
if (this.stopped)
|
|
424
|
+
return; // Before the durable floor as well as handler dispatch.
|
|
425
|
+
const update = ctx.update;
|
|
426
|
+
const updateId = update.update_id;
|
|
427
|
+
this.discardFloorIfStale(Date.now());
|
|
428
|
+
if (typeof updateId === "number") {
|
|
429
|
+
if (this.lastUpdateId > 0 && updateId <= this.lastUpdateId)
|
|
430
|
+
return;
|
|
431
|
+
this.lastUpdateId = updateId;
|
|
432
|
+
this.floorRecordedAt = Date.now();
|
|
433
|
+
if (this.bot.botInfo?.id != null)
|
|
434
|
+
this.floorBotId = this.bot.botInfo.id;
|
|
435
|
+
this.schedulePersistFloor();
|
|
436
|
+
}
|
|
437
|
+
const message = update.message;
|
|
377
438
|
const sender = message?.from?.id;
|
|
378
439
|
if (sender != null && sender === this.bot.botInfo?.id)
|
|
379
440
|
return;
|
|
441
|
+
// Age cap / stale callbacks apply to the downtime queue (pages until a short getUpdates).
|
|
442
|
+
const isBacklog = typeof updateId === "number" && this.backlogUpdateIds.has(updateId);
|
|
443
|
+
if (isBacklog) {
|
|
444
|
+
if (update.callback_query) {
|
|
445
|
+
// The card's date is not the click's; queued callbacks are stale by being in the backlog.
|
|
446
|
+
ctx.answerCallbackQuery().catch(() => { });
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const ageMs = telegramUpdateAgeMs(update, Date.now());
|
|
450
|
+
if (ageMs != null && ageMs > TELEGRAM_PENDING_LIVE_MAX_MS
|
|
451
|
+
&& message && !this._isServiceMessage(message)) {
|
|
452
|
+
this.emitMissedOffline(updateId ?? 0, message);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
380
456
|
return next();
|
|
381
457
|
});
|
|
382
458
|
// Middleware: catch Rich Message updates that grammy doesn't route to bot.on("message")
|
|
@@ -639,6 +715,8 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
639
715
|
authRejected = false;
|
|
640
716
|
/** #1537 review: stop() was called (and no start() since). */
|
|
641
717
|
stopped = false;
|
|
718
|
+
pollingGeneration = 0;
|
|
719
|
+
receiveStop = null;
|
|
642
720
|
connectionEvidence() {
|
|
643
721
|
return { ready: this.pollingReady, authRejected: this.authRejected, stopped: this.stopped };
|
|
644
722
|
}
|
|
@@ -646,6 +724,10 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
646
724
|
// #1537 review: a new start borrows nothing from the last one — not ready until its own onStart, and not stopped.
|
|
647
725
|
this.pollingReady = false;
|
|
648
726
|
this.stopped = false;
|
|
727
|
+
this.receiveStop = null;
|
|
728
|
+
const generation = ++this.pollingGeneration, current = () => !this.stopped && this.pollingGeneration === generation;
|
|
729
|
+
this.backlogDrained = false;
|
|
730
|
+
this.backlogUpdateIds.clear();
|
|
649
731
|
this.queue.start();
|
|
650
732
|
this._pruneInbox();
|
|
651
733
|
this.cleanupTimer = setInterval(() => this._pruneInbox(), 60 * 60 * 1000);
|
|
@@ -662,15 +744,18 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
662
744
|
const MAX_RECONNECT_RETRIES = 10; // auto-reconnect on non-409 errors
|
|
663
745
|
void (async () => {
|
|
664
746
|
let reconnects = 0;
|
|
665
|
-
while (
|
|
666
|
-
for (let attempt = 1; attempt <= MAX_409_RETRIES; attempt++) {
|
|
747
|
+
while (current()) {
|
|
748
|
+
for (let attempt = 1; current() && attempt <= MAX_409_RETRIES; attempt++) {
|
|
667
749
|
try {
|
|
668
750
|
await this.bot.start({
|
|
669
751
|
// Keep the server-side long poll comfortably below both 30s
|
|
670
752
|
// client deadlines. Equal 30s deadlines race the normal empty
|
|
671
753
|
// getUpdates response and would churn the HTTP pool while idle.
|
|
672
754
|
timeout: TELEGRAM_LONG_POLL_TIMEOUT_SECONDS,
|
|
673
|
-
|
|
755
|
+
// Telegram queues ~24h of updates. Dropping them on a cold start
|
|
756
|
+
// discards messages sent while AgEnD was stopped. Keep them; the
|
|
757
|
+
// adapter delivers recent ones and files older ones as Needs-you.
|
|
758
|
+
drop_pending_updates: false,
|
|
674
759
|
// Telegram's DEFAULT allowed_updates excludes message_reaction, so
|
|
675
760
|
// reactions cannot arrive unless the set is listed explicitly. Passing
|
|
676
761
|
// this list also FREEZES the set: any update type omitted here stops
|
|
@@ -680,15 +765,20 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
680
765
|
// added here too, or it will never fire.
|
|
681
766
|
allowed_updates: TELEGRAM_ALLOWED_UPDATES,
|
|
682
767
|
onStart: (info) => {
|
|
768
|
+
if (!current())
|
|
769
|
+
return;
|
|
683
770
|
reconnects = 0; // reset on successful start
|
|
684
771
|
this.pollingReady = true;
|
|
685
772
|
this.authRejected = false;
|
|
773
|
+
this.discardFloorIfStale(Date.now(), info.id);
|
|
686
774
|
this.emit("started", info.username, String(info.id));
|
|
687
775
|
},
|
|
688
776
|
});
|
|
689
777
|
return; // bot.stop() was called — clean exit
|
|
690
778
|
}
|
|
691
779
|
catch (err) {
|
|
780
|
+
if (!current())
|
|
781
|
+
return;
|
|
692
782
|
// #1519 P6: what the connection's status reports — not polling now, and whether Telegram refused the token.
|
|
693
783
|
this.pollingReady = false;
|
|
694
784
|
if (err instanceof GrammyError && err.error_code === 401)
|
|
@@ -719,10 +809,19 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
719
809
|
}
|
|
720
810
|
})();
|
|
721
811
|
}
|
|
722
|
-
|
|
723
|
-
|
|
812
|
+
stopReceiving() {
|
|
813
|
+
if (this.receiveStop)
|
|
814
|
+
return this.receiveStop;
|
|
815
|
+
// Set before bot.stop (and its final getUpdates ACK); do not close the outbound queue yet.
|
|
724
816
|
this.pollingReady = false;
|
|
725
817
|
this.stopped = true;
|
|
818
|
+
this.pollingGeneration++;
|
|
819
|
+
this.flushPersistFloor();
|
|
820
|
+
this.receiveStop = this.bot.stop();
|
|
821
|
+
return this.receiveStop;
|
|
822
|
+
}
|
|
823
|
+
async stop() {
|
|
824
|
+
const receivingStopped = this.stopReceiving();
|
|
726
825
|
// Invalidate queued status work before any await. Re-created adapters must
|
|
727
826
|
// not inherit proof from this object's pending requests.
|
|
728
827
|
this.reactionGeneration = (this.reactionGeneration ?? 0) + 1;
|
|
@@ -732,9 +831,126 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
732
831
|
if (this.cleanupTimer)
|
|
733
832
|
clearInterval(this.cleanupTimer);
|
|
734
833
|
this.queue.stop();
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
834
|
+
try {
|
|
835
|
+
await receivingStopped;
|
|
836
|
+
}
|
|
837
|
+
finally {
|
|
838
|
+
this.httpAgent.destroy();
|
|
839
|
+
this.httpsAgent.destroy();
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
lastUpdateIdFile() {
|
|
843
|
+
return telegramLastUpdateIdPath(this.inboxDir, this.id);
|
|
844
|
+
}
|
|
845
|
+
loadFloor() {
|
|
846
|
+
try {
|
|
847
|
+
const parsed = JSON.parse(readFileSync(this.lastUpdateIdFile(), "utf8"));
|
|
848
|
+
if (!parsed || typeof parsed !== "object")
|
|
849
|
+
return;
|
|
850
|
+
const rec = parsed;
|
|
851
|
+
const id = rec.update_id;
|
|
852
|
+
if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 0)
|
|
853
|
+
return;
|
|
854
|
+
this.lastUpdateId = id;
|
|
855
|
+
this.floorBotId = typeof rec.bot_id === "number" && Number.isSafeInteger(rec.bot_id) ? rec.bot_id : undefined;
|
|
856
|
+
this.floorRecordedAt = typeof rec.recorded_at === "number" && Number.isFinite(rec.recorded_at) ? rec.recorded_at : 0;
|
|
857
|
+
this.discardFloorIfStale(Date.now());
|
|
858
|
+
}
|
|
859
|
+
catch { /* no floor yet */ }
|
|
860
|
+
}
|
|
861
|
+
clearFloor() {
|
|
862
|
+
this.lastUpdateId = 0;
|
|
863
|
+
this.floorBotId = undefined;
|
|
864
|
+
this.floorRecordedAt = 0;
|
|
865
|
+
}
|
|
866
|
+
/** Drop a floor that belongs to another bot, or that is older than a week of silence. */
|
|
867
|
+
discardFloorIfStale(now, botId) {
|
|
868
|
+
if (this.lastUpdateId <= 0)
|
|
869
|
+
return;
|
|
870
|
+
if (this.floorRecordedAt > 0 && now - this.floorRecordedAt > TELEGRAM_UPDATE_ID_FLOOR_MAX_AGE_MS) {
|
|
871
|
+
this.clearFloor();
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const id = botId ?? (this.bot.isInited() ? this.bot.botInfo.id : undefined);
|
|
875
|
+
if (this.floorBotId != null && id != null && this.floorBotId !== id)
|
|
876
|
+
this.clearFloor();
|
|
877
|
+
}
|
|
878
|
+
schedulePersistFloor() {
|
|
879
|
+
if (this.persistFloorTimer)
|
|
880
|
+
return;
|
|
881
|
+
this.persistFloorTimer = setImmediate(() => {
|
|
882
|
+
this.persistFloorTimer = undefined;
|
|
883
|
+
this.writeFloor();
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
flushPersistFloor() {
|
|
887
|
+
if (this.persistFloorTimer) {
|
|
888
|
+
clearImmediate(this.persistFloorTimer);
|
|
889
|
+
this.persistFloorTimer = undefined;
|
|
890
|
+
}
|
|
891
|
+
this.writeFloor();
|
|
892
|
+
}
|
|
893
|
+
writeFloor() {
|
|
894
|
+
try {
|
|
895
|
+
const rec = { update_id: this.lastUpdateId };
|
|
896
|
+
if (this.floorBotId != null)
|
|
897
|
+
rec.bot_id = this.floorBotId;
|
|
898
|
+
if (this.floorRecordedAt > 0)
|
|
899
|
+
rec.recorded_at = this.floorRecordedAt;
|
|
900
|
+
atomicWriteFileSync(this.lastUpdateIdFile(), `${JSON.stringify(rec)}\n`, { mode: 0o600 });
|
|
901
|
+
}
|
|
902
|
+
catch (err) {
|
|
903
|
+
console.warn(`[telegram:${this.id}] could not persist last update_id (${errorMessage(err)})`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
noteGetUpdatesBacklog(method, result) {
|
|
907
|
+
if (method !== "getUpdates" || this.backlogDrained)
|
|
908
|
+
return;
|
|
909
|
+
if (result && typeof result === "object" && "ok" in result && result.ok !== true)
|
|
910
|
+
return;
|
|
911
|
+
// Transformers see the raw Bot API envelope `{ ok, result }`; callApi unwraps after.
|
|
912
|
+
const updates = result && typeof result === "object" && "result" in result
|
|
913
|
+
? result.result
|
|
914
|
+
: result;
|
|
915
|
+
if (!Array.isArray(updates))
|
|
916
|
+
return;
|
|
917
|
+
const ids = [];
|
|
918
|
+
for (const item of updates) {
|
|
919
|
+
if (item && typeof item === "object" && typeof item.update_id === "number") {
|
|
920
|
+
ids.push(item.update_id);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
// A new random sequence after a week of silence can land entirely below the stored floor.
|
|
924
|
+
if (ids.length > 0 && this.lastUpdateId > 0 && Math.max(...ids) < this.lastUpdateId) {
|
|
925
|
+
this.clearFloor();
|
|
926
|
+
this.schedulePersistFloor();
|
|
927
|
+
}
|
|
928
|
+
for (const id of ids)
|
|
929
|
+
this.backlogUpdateIds.add(id);
|
|
930
|
+
if (updates.length < TELEGRAM_GETUPDATES_LIMIT)
|
|
931
|
+
this.backlogDrained = true;
|
|
932
|
+
}
|
|
933
|
+
emitMissedOffline(updateId, msg) {
|
|
934
|
+
const userId = msg.from?.id;
|
|
935
|
+
if (userId == null || msg.chat?.id == null)
|
|
936
|
+
return;
|
|
937
|
+
const text = String(msg.text ?? msg.caption ?? "").replace(/\s+/g, " ").trim();
|
|
938
|
+
const preview = text.length > 160 ? `${text.slice(0, 159)}…` : text;
|
|
939
|
+
const threadId = msg.message_thread_id != null ? String(msg.message_thread_id) : undefined;
|
|
940
|
+
const payload = {
|
|
941
|
+
source: "telegram",
|
|
942
|
+
adapterId: this.id,
|
|
943
|
+
updateId,
|
|
944
|
+
chatId: String(msg.chat.id),
|
|
945
|
+
...(threadId !== undefined ? { threadId } : {}),
|
|
946
|
+
messageId: String(msg.message_id ?? ""),
|
|
947
|
+
userId: String(userId),
|
|
948
|
+
username: msg.from?.username ?? msg.from?.first_name ?? String(userId),
|
|
949
|
+
preview,
|
|
950
|
+
timestamp: new Date((msg.date ?? 0) * 1000),
|
|
951
|
+
...(msg.chat.title ? { chatTitle: String(msg.chat.title) } : {}),
|
|
952
|
+
};
|
|
953
|
+
this.emit("missed_offline", payload);
|
|
738
954
|
}
|
|
739
955
|
/** Delete inbox files older than 1 hour */
|
|
740
956
|
_pruneInbox() {
|
|
@@ -742,6 +958,8 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
742
958
|
const now = Date.now();
|
|
743
959
|
try {
|
|
744
960
|
for (const name of readdirSync(this.inboxDir)) {
|
|
961
|
+
if (/^telegram-.+-last-update-id$/.test(name))
|
|
962
|
+
continue;
|
|
745
963
|
const filePath = join(this.inboxDir, name);
|
|
746
964
|
try {
|
|
747
965
|
const mtime = statSync(filePath).mtimeMs;
|
|
@@ -1413,7 +1631,8 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
1413
1631
|
// Construct the download URL
|
|
1414
1632
|
const token = this.bot.token;
|
|
1415
1633
|
const url = `${this.apiRoot}/file/bot${token}/${filePath}`;
|
|
1416
|
-
|
|
1634
|
+
// #2959: unique per download — the inbox is shared by every bot on the host.
|
|
1635
|
+
const filename = inboxTempName(fileId, filePath.split("/").pop() ?? fileId);
|
|
1417
1636
|
const localPath = join(this.inboxDir, filename);
|
|
1418
1637
|
// Download using fetch
|
|
1419
1638
|
const response = await fetch(url);
|
|
@@ -1496,12 +1715,13 @@ export class TelegramAdapter extends EventEmitter {
|
|
|
1496
1715
|
return res.message_thread_id;
|
|
1497
1716
|
}
|
|
1498
1717
|
async deleteTopic(topicId) {
|
|
1499
|
-
// Telegram's General topic is the forum root, not a deletable topic.
|
|
1718
|
+
// Telegram's General topic is the forum root, not a deletable topic. Refused, never "done": a caller reports what
|
|
1719
|
+
// happened (delete_instance's topic_deleted), and silence would read as a deletion.
|
|
1500
1720
|
if (String(topicId) === "1")
|
|
1501
|
-
|
|
1721
|
+
throw new Error("the General topic is never deleted");
|
|
1502
1722
|
const chatId = this.getChatId();
|
|
1503
1723
|
if (!chatId)
|
|
1504
|
-
|
|
1724
|
+
throw new Error("the Telegram bot is not connected to its group");
|
|
1505
1725
|
await this.bot.api.raw.deleteForumTopic({ chat_id: Number(chatId), message_thread_id: Number(topicId) });
|
|
1506
1726
|
}
|
|
1507
1727
|
async probeTopicPresence(topicId) {
|