@llblab/pi-kit 0.27.6 → 0.28.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/BACKLOG.md +2 -5
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
- package/node_modules/@llblab/pi-telegram/README.md +15 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
- package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
- package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
- package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
- package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
- package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
- package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
- package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
- package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
- package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
- package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
- package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
- package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
- package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
- package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
- package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
- package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
- package/node_modules/@llblab/pi-telegram/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
- package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/package.json +2 -2
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
* Zones: telemetry paths, filesystem, runtime identity
|
|
4
4
|
* Owns agent-dir detection and extension-local path derivation
|
|
5
5
|
*
|
|
6
|
-
* This domain is
|
|
7
|
-
*
|
|
8
|
-
* state, or import
|
|
6
|
+
* This domain is path-only: it resolves directories and file paths from
|
|
7
|
+
* environment and runtime identity. Its only filesystem read canonicalizes the
|
|
8
|
+
* existing agent-dir prefix; it does not read config, manage state, or import
|
|
9
|
+
* broader Telegram domains.
|
|
9
10
|
*/
|
|
10
11
|
import { createHash } from "node:crypto";
|
|
12
|
+
import { realpathSync } from "node:fs";
|
|
11
13
|
import { homedir } from "node:os";
|
|
12
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
14
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
15
|
export const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
14
16
|
/**
|
|
15
17
|
* Resolve the agent data directory for the current Pi-compatible runtime.
|
|
@@ -50,11 +52,53 @@ export function requireTelegramStoragePathReference(path, expectedPath) {
|
|
|
50
52
|
export function resolveTelegramConfigPath() {
|
|
51
53
|
return join(resolveAgentDir(), "telegram.json");
|
|
52
54
|
}
|
|
53
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Resolve symlinks in the longest existing prefix of `path` and keep the missing suffix. Strict journal reads require
|
|
57
|
+
* canonical anchors, so a symlinked agent directory (for example macOS `/var` → `/private/var`) must not leak in.
|
|
58
|
+
* Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. Windows keeps its
|
|
59
|
+
* spelling: strict journal reads are POSIX-only there, and resolving a drive-less root would add a drive letter.
|
|
60
|
+
*/
|
|
61
|
+
function canonicalizeExistingPrefix(path) {
|
|
62
|
+
if (!isAbsolute(path) || process.platform === "win32")
|
|
63
|
+
return path;
|
|
64
|
+
const absolute = resolve(path);
|
|
65
|
+
const missing = [];
|
|
66
|
+
for (let current = absolute;; current = dirname(current)) {
|
|
67
|
+
try {
|
|
68
|
+
return join(realpathSync(current), ...missing.reverse());
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error.code !== "ENOENT" || dirname(current) === current)
|
|
72
|
+
return absolute;
|
|
73
|
+
missing.push(basename(current));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Telegram bridge temporary directory (<agentDir>/tmp/pi-telegram); releases before 0.52.0 used `tmp/telegram`. */
|
|
54
78
|
export function resolveTelegramTempDir(agentDir = resolveAgentDir()) {
|
|
55
|
-
return join(agentDir, "tmp", "telegram");
|
|
79
|
+
return join(canonicalizeExistingPrefix(agentDir), "tmp", "pi-telegram");
|
|
80
|
+
}
|
|
81
|
+
/** Consolidated-root service artifacts (transaction guards, staging and IPC), never session custody. */
|
|
82
|
+
export function resolveTelegramRuntimeDir(agentDir = resolveAgentDir()) {
|
|
83
|
+
return join(resolveTelegramTempDir(agentDir), "runtime");
|
|
84
|
+
}
|
|
85
|
+
/** Rotated shared runtime event segments, outside the persistent root-file census. */
|
|
86
|
+
export function resolveTelegramRuntimeLogsDir(agentDir = resolveAgentDir()) {
|
|
87
|
+
return join(resolveTelegramTempDir(agentDir), "logs");
|
|
88
|
+
}
|
|
89
|
+
/** Consolidated runtime state for every logical profile (`tmp/pi-telegram/state.json`). */
|
|
90
|
+
export function resolveTelegramStatePath(agentDir = resolveAgentDir()) {
|
|
91
|
+
return join(resolveTelegramTempDir(agentDir), "state.json");
|
|
92
|
+
}
|
|
93
|
+
/** Flat attachment scratch directory (<agentDir>/tmp/pi-telegram/attachments). */
|
|
94
|
+
export function resolveTelegramAttachmentsDir(agentDir = resolveAgentDir()) {
|
|
95
|
+
return join(resolveTelegramTempDir(agentDir), "attachments");
|
|
56
96
|
}
|
|
57
|
-
/**
|
|
97
|
+
/** Read-only pre-0.52.0 ownership file, consulted only to refuse a second poller beside a live older leader. */
|
|
98
|
+
export function resolveLegacyTelegramOwnersPath(agentDir = resolveAgentDir()) {
|
|
99
|
+
return join(agentDir, "tmp", "telegram", "owners.json");
|
|
100
|
+
}
|
|
101
|
+
/** Telegram transport ownership store (<agentDir>/tmp/pi-telegram/owners.json). */
|
|
58
102
|
export function resolveTelegramOwnersPath() {
|
|
59
103
|
return join(resolveTelegramTempDir(), "owners.json");
|
|
60
104
|
}
|
|
@@ -66,15 +110,13 @@ export function getTelegramProfilePathSuffix(profileName) {
|
|
|
66
110
|
export function resolveTelegramProfileTempFilePath(baseName, extension, agentDir = resolveAgentDir(), profileName) {
|
|
67
111
|
return join(resolveTelegramTempDir(agentDir), `${baseName}${getTelegramProfilePathSuffix(profileName)}.${extension}`);
|
|
68
112
|
}
|
|
69
|
-
export function getTelegramDiagnosticsDisplayPaths(
|
|
70
|
-
const suffix = getTelegramProfilePathSuffix(profileName);
|
|
71
|
-
const profileSlug = suffix.slice(1);
|
|
113
|
+
export function getTelegramDiagnosticsDisplayPaths(_profileName) {
|
|
72
114
|
return {
|
|
73
|
-
state:
|
|
74
|
-
logs:
|
|
115
|
+
state: "~/.pi/agent/tmp/pi-telegram/state.json",
|
|
116
|
+
logs: "~/.pi/agent/tmp/pi-telegram/logs.jsonl",
|
|
75
117
|
};
|
|
76
118
|
}
|
|
77
|
-
/** Durable Workspace admission ledger (<agentDir>/tmp/telegram/workspace-admission[.<profile>].json). */
|
|
119
|
+
/** Durable Workspace admission ledger (<agentDir>/tmp/pi-telegram/workspace-admission[.<profile>].json). */
|
|
78
120
|
export function resolveTelegramWorkspaceAdmissionPath(agentDir = resolveAgentDir(), profileName) {
|
|
79
121
|
return resolveTelegramProfileTempFilePath("workspace-admission", "json", agentDir, profileName);
|
|
80
122
|
}
|
|
@@ -82,6 +124,24 @@ export function resolveTelegramWorkspaceAdmissionPath(agentDir = resolveAgentDir
|
|
|
82
124
|
export function resolveTelegramWorkspaceAdmissionPathForProfile(profileName) {
|
|
83
125
|
return resolveTelegramWorkspaceAdmissionPath(resolveAgentDir(), profileName);
|
|
84
126
|
}
|
|
127
|
+
/** Consolidated non-session service journal (`journals/<kind>.<profile sha256>.json`) and its runtime namespace. */
|
|
128
|
+
export function resolveTelegramServiceJournalStorage(kind, agentDir = resolveAgentDir(), profileName = TELEGRAM_DEFAULT_PROFILE_NAME) {
|
|
129
|
+
const profileHash = createHash("sha256").update(profileName).digest("hex");
|
|
130
|
+
return {
|
|
131
|
+
path: join(resolveTelegramTempDir(agentDir), "journals", `${kind}.${profileHash}.json`),
|
|
132
|
+
runtimeDir: resolveTelegramRuntimeDir(agentDir),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** Bind one non-session journal to its exact colocated-root service namespace, never a format fallback. */
|
|
136
|
+
export function getTelegramJournalPublicationPaths(path, runtimeDir) {
|
|
137
|
+
if (runtimeDir === undefined)
|
|
138
|
+
return { transactionPath: `${path}.transaction`, temporaryBasePath: path };
|
|
139
|
+
requireTelegramStoragePathReference(path, resolve(path));
|
|
140
|
+
requireTelegramStoragePathReference(runtimeDir, join(dirname(dirname(path)), "runtime"));
|
|
141
|
+
if (basename(dirname(path)) !== "journals")
|
|
142
|
+
throw new Error("Telegram service journal must use its journals namespace.");
|
|
143
|
+
return { transactionPath: join(runtimeDir, `${basename(path)}.transaction`), temporaryBasePath: join(runtimeDir, basename(path)) };
|
|
144
|
+
}
|
|
85
145
|
/** Durable inactive Thread cleanup work-set journal. */
|
|
86
146
|
export function resolveTelegramThreadCleanupWorkPath(agentDir = resolveAgentDir(), profileName) {
|
|
87
147
|
return resolveTelegramProfileTempFilePath("thread-cleanup", "json", agentDir, profileName);
|
|
@@ -90,7 +150,7 @@ export function resolveTelegramThreadCleanupWorkPath(agentDir = resolveAgentDir(
|
|
|
90
150
|
export function resolveTelegramChannelPostJournalPath(agentDir = resolveAgentDir(), profileName) {
|
|
91
151
|
return resolveTelegramProfileTempFilePath("channel-posts", "json", agentDir, profileName);
|
|
92
152
|
}
|
|
93
|
-
/** Durable inbound update journal (<agentDir>/tmp/telegram/inbox[.<profile>].json). */
|
|
153
|
+
/** Durable inbound update journal (<agentDir>/tmp/pi-telegram/inbox[.<profile>].json). */
|
|
94
154
|
export function resolveTelegramUpdateJournalPath(agentDir = resolveAgentDir(), profileName) {
|
|
95
155
|
return resolveTelegramProfileTempFilePath("inbox", "json", agentDir, profileName);
|
|
96
156
|
}
|
|
@@ -109,7 +169,85 @@ export function resolveTelegramFollowerJournalPath(recipientBindingKey, agentDir
|
|
|
109
169
|
.slice(0, 16);
|
|
110
170
|
return resolveTelegramProfileTempFilePath(`follower-inbox-${bindingHash}`, "json", agentDir, profileName);
|
|
111
171
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
172
|
+
const TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH = 200;
|
|
173
|
+
const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\..*)?$/u;
|
|
174
|
+
/**
|
|
175
|
+
* Directory name for a session id. Lowercase letters, digits, `_`, `-` and inner `.` stay verbatim, so every real
|
|
176
|
+
* (UUID-shaped) id is unchanged; any other UTF-8 byte is percent-encoded. Uppercase is encoded so case-insensitive
|
|
177
|
+
* filesystems never merge two sessions; a leading or trailing dot and Windows reserved names are encoded so the name
|
|
178
|
+
* can never be `.`/`..`, hidden, or a device. The encoding is reversible; an empty or over-long result is refused.
|
|
179
|
+
*/
|
|
180
|
+
export function encodeTelegramSessionDirectoryName(sessionId) {
|
|
181
|
+
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
182
|
+
return undefined;
|
|
183
|
+
const bytes = Buffer.from(sessionId, "utf8");
|
|
184
|
+
if (bytes.toString("utf8") !== sessionId)
|
|
185
|
+
return undefined;
|
|
186
|
+
let name = "";
|
|
187
|
+
bytes.forEach((byte, index) => {
|
|
188
|
+
const char = String.fromCharCode(byte);
|
|
189
|
+
const safe = /[a-z0-9_-]/u.test(char) || (char === "." && index > 0 && index < bytes.length - 1);
|
|
190
|
+
name += safe ? char : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
191
|
+
});
|
|
192
|
+
if (WINDOWS_RESERVED_NAME.test(name))
|
|
193
|
+
name = `%${name.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}${name.slice(1)}`;
|
|
194
|
+
return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH ? name : undefined;
|
|
195
|
+
}
|
|
196
|
+
/** Inverse of `encodeTelegramSessionDirectoryName`; undefined unless the name is exactly canonical. */
|
|
197
|
+
export function decodeTelegramSessionDirectoryName(name) {
|
|
198
|
+
if (!/^[a-z0-9_.%A-F-]+$/u.test(name))
|
|
199
|
+
return undefined;
|
|
200
|
+
const bytes = [];
|
|
201
|
+
for (let index = 0; index < name.length; index++) {
|
|
202
|
+
if (name[index] === "%") {
|
|
203
|
+
const hex = name.slice(index + 1, index + 3);
|
|
204
|
+
if (!/^[0-9A-F]{2}$/u.test(hex))
|
|
205
|
+
return undefined;
|
|
206
|
+
bytes.push(Number.parseInt(hex, 16));
|
|
207
|
+
index += 2;
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
bytes.push(name.charCodeAt(index));
|
|
211
|
+
}
|
|
212
|
+
const decoded = Buffer.from(bytes).toString("utf8");
|
|
213
|
+
return encodeTelegramSessionDirectoryName(decoded) === name ? decoded : undefined;
|
|
214
|
+
}
|
|
215
|
+
/** Root of every session folder (<agentDir>/tmp/pi-telegram/sessions). */
|
|
216
|
+
export function resolveTelegramSessionsDir(agentDir = resolveAgentDir()) {
|
|
217
|
+
return join(resolveTelegramTempDir(agentDir), "sessions");
|
|
218
|
+
}
|
|
219
|
+
/** One session's folder (<agentDir>/tmp/pi-telegram/sessions/<session id>). */
|
|
220
|
+
export function resolveTelegramSessionDir(sessionId, agentDir = resolveAgentDir()) {
|
|
221
|
+
const name = encodeTelegramSessionDirectoryName(sessionId);
|
|
222
|
+
if (!name)
|
|
223
|
+
throw new Error("Telegram session directory requires a usable session id.");
|
|
224
|
+
return join(resolveTelegramSessionsDir(agentDir), name);
|
|
225
|
+
}
|
|
226
|
+
/** Hash naming one recipient incarnation inside its session folder; unchanged from the former follower inbox. */
|
|
227
|
+
export function getTelegramRecipientJournalHash(recipientBindingKey) {
|
|
228
|
+
if (!recipientBindingKey)
|
|
229
|
+
throw new Error("Telegram session journal binding key is required.");
|
|
230
|
+
return createHash("sha256").update(recipientBindingKey).digest("hex").slice(0, 16);
|
|
231
|
+
}
|
|
232
|
+
/** Durable per-session journal (`sessions/<session id>/journal.<hash>[.<profile>].json`); the name never reveals a role. */
|
|
233
|
+
export function resolveTelegramSessionJournalPath(sessionId, recipientBindingKey, agentDir = resolveAgentDir(), profileName) {
|
|
234
|
+
return join(resolveTelegramSessionDir(sessionId, agentDir), `journal.${getTelegramRecipientJournalHash(recipientBindingKey)}${getTelegramProfilePathSuffix(profileName)}.json`);
|
|
235
|
+
}
|
|
236
|
+
/** Leader polling journal hosted by the session that first led (`sessions/<id>/inbox[.<profile>].json`); owners.json names it. */
|
|
237
|
+
export function resolveTelegramSessionPollingJournalPath(sessionId, agentDir = resolveAgentDir(), profileName) {
|
|
238
|
+
return join(resolveTelegramSessionDir(sessionId, agentDir), `inbox${getTelegramProfilePathSuffix(profileName)}.json`);
|
|
239
|
+
}
|
|
240
|
+
/** Accept only a canonical `sessions/<id>/inbox[.<profile>].json` beneath this agent's runtime. */
|
|
241
|
+
export function isTelegramSessionPollingJournalPath(path, agentDir = resolveAgentDir()) {
|
|
242
|
+
const parts = relative(resolveTelegramSessionsDir(agentDir), path).split(sep);
|
|
243
|
+
return isAbsolute(path) && parts.length === 2 && decodeTelegramSessionDirectoryName(parts[0]) !== undefined &&
|
|
244
|
+
/^inbox(?:\.[a-zA-Z0-9._-]+)?\.json$/u.test(parts[1]);
|
|
245
|
+
}
|
|
246
|
+
/** Runtime event log (<agentDir>/tmp/pi-telegram/logs.jsonl). */
|
|
247
|
+
export function resolveTelegramRuntimeLogPath(agentDir = resolveAgentDir()) {
|
|
248
|
+
return resolveTelegramProfileTempFilePath("logs", "jsonl", agentDir);
|
|
249
|
+
}
|
|
250
|
+
/** Bounded previous shared log segment below `logs/`. */
|
|
251
|
+
export function resolveTelegramPreviousSharedRuntimeLogPath(agentDir = resolveAgentDir()) {
|
|
252
|
+
return join(resolveTelegramRuntimeLogsDir(agentDir), "logs._prev.jsonl");
|
|
115
253
|
}
|
|
@@ -68,6 +68,7 @@ export declare function createScopedModelPatternPersister(deps: {
|
|
|
68
68
|
export declare function getExtensionContextModel(ctx: ExtensionContext): ExtensionContext["model"];
|
|
69
69
|
export declare function getExtensionContextCwd(ctx: ExtensionContext): string;
|
|
70
70
|
export declare function getExtensionContextSessionId(ctx: ExtensionContext): string;
|
|
71
|
+
export declare function getExtensionContextSessionId(ctx: ExtensionContext | undefined): string | undefined;
|
|
71
72
|
export declare function isExtensionContextIdle(ctx: ExtensionContext): boolean;
|
|
72
73
|
export declare function hasExtensionContextPendingMessages(ctx: ExtensionContext): boolean;
|
|
73
74
|
export declare function compactExtensionContext(ctx: ExtensionContext, callbacks: Parameters<ExtensionContext["compact"]>[0]): ReturnType<ExtensionContext["compact"]>;
|
|
@@ -109,7 +109,8 @@ export function getExtensionContextCwd(ctx) {
|
|
|
109
109
|
return ctx.cwd;
|
|
110
110
|
}
|
|
111
111
|
export function getExtensionContextSessionId(ctx) {
|
|
112
|
-
|
|
112
|
+
// Minimal hosts and stale contexts may omit a session manager; absence is not an invented ID.
|
|
113
|
+
return ctx?.sessionManager?.getSessionId?.();
|
|
113
114
|
}
|
|
114
115
|
export function isExtensionContextIdle(ctx) {
|
|
115
116
|
return ctx.isIdle();
|
|
@@ -11,7 +11,6 @@ export interface TelegramUpdate {
|
|
|
11
11
|
update_id: number;
|
|
12
12
|
}
|
|
13
13
|
export declare const TELEGRAM_GET_UPDATES_CONFLICT_STOP_LIMIT = 10;
|
|
14
|
-
export declare const TELEGRAM_GET_UPDATES_GRACE_MS = 10000;
|
|
15
14
|
export declare const TELEGRAM_ALLOWED_UPDATES: readonly ["message", "edited_message", "callback_query", "message_reaction", "guest_message"];
|
|
16
15
|
export declare function buildTelegramInitialSyncRequest(): {
|
|
17
16
|
offset: number;
|
|
@@ -57,7 +56,6 @@ export interface TelegramPollingControllerState extends TelegramPollingStateSnap
|
|
|
57
56
|
pollingController?: AbortController;
|
|
58
57
|
}
|
|
59
58
|
export declare function createTelegramPollingControllerState(): TelegramPollingControllerState;
|
|
60
|
-
export declare function getTelegramPollingStateSnapshot(state: TelegramPollingControllerState): TelegramPollingStateSnapshot;
|
|
61
59
|
export declare function createTelegramPollingStateReader(state: TelegramPollingControllerState): () => TelegramPollingStateSnapshot;
|
|
62
60
|
export declare function isTelegramPollingControllerActive(state: TelegramPollingControllerState): boolean;
|
|
63
61
|
export declare function createTelegramPollingActivityReader(state: TelegramPollingControllerState): () => boolean;
|
|
@@ -271,11 +269,8 @@ export interface TelegramThreadCapabilityOrchestration<TContext, TOwner> {
|
|
|
271
269
|
}
|
|
272
270
|
export declare function createTelegramThreadCapabilityStateRuntime(): TelegramThreadCapabilityStateRuntime;
|
|
273
271
|
export declare function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(deps: TelegramThreadCapabilityOrchestrationDeps<TContext, TOwner>): TelegramThreadCapabilityOrchestration<TContext, TOwner>;
|
|
274
|
-
export declare function readTelegramThreadCapability(deps: TelegramThreadCapabilityReaderDeps): Promise<boolean | undefined>;
|
|
275
|
-
export declare function probeTelegramStartupThreadCapability(deps: TelegramStartupThreadCapabilityProbeDeps, isCurrent?: () => boolean): Promise<boolean | undefined>;
|
|
276
272
|
export declare function applyTelegramThreadCapability<TContext>(ctx: TContext, threadModeEnabled: boolean, phase: string, deps: TelegramThreadCapabilityRuntimeDeps<TContext>, isCurrent?: () => boolean): Promise<void>;
|
|
277
273
|
export declare function createTelegramThreadAwarePollingPorts<TContext, TOwner>(deps: TelegramThreadAwarePollingDeps<TContext, TOwner>): TelegramThreadAwarePollingPorts<TContext, TOwner>;
|
|
278
|
-
export declare function createTelegramThreadTargetObservationHandler<TContext>(deps: TelegramThreadCapabilityRuntimeDeps<TContext>): TelegramThreadTargetObservationHandler<TContext>;
|
|
279
274
|
export declare function canProbeTelegramThreadCapability<TContext>(ctx: TContext, deps: Pick<TelegramThreadCapabilityRuntimeDeps<TContext>, "ownsLock" | "isFollowerRegistered">): boolean;
|
|
280
275
|
export declare function createTelegramThreadCapabilityMonitor<TContext>(deps: TelegramThreadCapabilityRuntimeDeps<TContext>): TelegramThreadCapabilityMonitor<TContext>;
|
|
281
276
|
export declare class TelegramPollingBatchValidationError extends Error {
|
|
@@ -15,7 +15,7 @@ const TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_LIMIT = 3;
|
|
|
15
15
|
const TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_MS = 1_000;
|
|
16
16
|
const TELEGRAM_GET_UPDATES_CONFLICT_SLOW_RETRY_MS = 3_000;
|
|
17
17
|
const TELEGRAM_POLLING_RETRY_MS = 3_000;
|
|
18
|
-
|
|
18
|
+
const TELEGRAM_GET_UPDATES_GRACE_MS = 10_000;
|
|
19
19
|
// Standard Telegram DM polling does not expose ordinary message-deletion events.
|
|
20
20
|
// Business deletions belong to a separate namespace and default routing ignores them.
|
|
21
21
|
export const TELEGRAM_ALLOWED_UPDATES = [
|
|
@@ -80,7 +80,7 @@ export function createTelegramPollingControllerState() {
|
|
|
80
80
|
stopReason: "not-started",
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
function getTelegramPollingStateSnapshot(state) {
|
|
84
84
|
return {
|
|
85
85
|
phase: state.phase,
|
|
86
86
|
phaseStartedAtMs: state.phaseStartedAtMs,
|
|
@@ -481,7 +481,7 @@ export function createTelegramThreadCapabilityOrchestration(deps) {
|
|
|
481
481
|
}),
|
|
482
482
|
};
|
|
483
483
|
}
|
|
484
|
-
|
|
484
|
+
async function readTelegramThreadCapability(deps) {
|
|
485
485
|
const bot = await deps.callApi("getMe", {});
|
|
486
486
|
if (bot.has_topics_enabled === true)
|
|
487
487
|
return true;
|
|
@@ -489,7 +489,7 @@ export async function readTelegramThreadCapability(deps) {
|
|
|
489
489
|
return false;
|
|
490
490
|
return undefined;
|
|
491
491
|
}
|
|
492
|
-
|
|
492
|
+
async function probeTelegramStartupThreadCapability(deps, isCurrent = () => true) {
|
|
493
493
|
if (!isCurrent())
|
|
494
494
|
return;
|
|
495
495
|
const threadModeEnabled = await readTelegramThreadCapability(deps);
|
|
@@ -770,7 +770,7 @@ export function createTelegramThreadAwarePollingPorts(deps) {
|
|
|
770
770
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
771
771
|
};
|
|
772
772
|
}
|
|
773
|
-
|
|
773
|
+
function createTelegramThreadTargetObservationHandler(deps) {
|
|
774
774
|
let transitionPending = false;
|
|
775
775
|
return async (ctx) => {
|
|
776
776
|
if (transitionPending)
|
|
@@ -156,7 +156,6 @@ export declare function createTelegramNativeMarkdownPreviewFinalizer<TReplyMarku
|
|
|
156
156
|
}) => Promise<boolean>;
|
|
157
157
|
export declare function createTelegramAssistantPreviewRuntime<TMessage, TReplyMarkup = TelegramPreviewReplyMarkup>(deps: TelegramAssistantPreviewRuntimeDeps<TMessage, TReplyMarkup>): TelegramAssistantPreviewRuntime<TMessage, TReplyMarkup>;
|
|
158
158
|
export declare function createTelegramPreviewController(deps: TelegramPreviewControllerDeps): TelegramPreviewController;
|
|
159
|
-
export declare function createTelegramAssistantMessagePreviewHooks<TMessage>(deps: TelegramAssistantMessagePreviewHookDeps<TMessage>): TelegramAssistantMessagePreviewHooks<TMessage>;
|
|
160
159
|
/**
|
|
161
160
|
* Returns true when the active turn is a Telegram Guest Mode query. A guest
|
|
162
161
|
* query allows exactly one answer within a limited Telegram response window,
|
|
@@ -165,8 +164,6 @@ export declare function createTelegramAssistantMessagePreviewHooks<TMessage>(dep
|
|
|
165
164
|
export declare function shouldSuppressPreviewForGuestTurn(turn: {
|
|
166
165
|
guestQueryId?: string;
|
|
167
166
|
} | null | undefined): boolean;
|
|
168
|
-
export declare function handleTelegramAssistantMessagePreviewStart<TMessage>(message: TMessage, deps: TelegramAssistantMessagePreviewStartDeps<TMessage>): Promise<void>;
|
|
169
|
-
export declare function handleTelegramAssistantMessagePreviewUpdate<TMessage>(message: TMessage, deps: TelegramAssistantMessagePreviewUpdateDeps<TMessage>): Promise<void>;
|
|
170
167
|
export declare function buildTelegramPreviewFinalText(state: TelegramPreviewState): string | undefined;
|
|
171
168
|
export declare function createTelegramPreviewRuntimeState(): TelegramPreviewRuntimeState;
|
|
172
169
|
export declare function allocateTelegramDraftId(currentDraftId: number, maxDraftId: number): number;
|
|
@@ -168,7 +168,7 @@ export function createTelegramPreviewController(deps) {
|
|
|
168
168
|
finalize: (chatId, _replyToMessageId, options) => finalizeTelegramPreview(chatId, getRuntimeDeps(), options),
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
-
|
|
171
|
+
function createTelegramAssistantMessagePreviewHooks(deps) {
|
|
172
172
|
return {
|
|
173
173
|
onMessageStart: async (event) => {
|
|
174
174
|
await handleTelegramAssistantMessagePreviewStart(event.message, deps);
|
|
@@ -186,7 +186,7 @@ export function createTelegramAssistantMessagePreviewHooks(deps) {
|
|
|
186
186
|
export function shouldSuppressPreviewForGuestTurn(turn) {
|
|
187
187
|
return !!turn?.guestQueryId;
|
|
188
188
|
}
|
|
189
|
-
|
|
189
|
+
async function handleTelegramAssistantMessagePreviewStart(message, deps) {
|
|
190
190
|
const turn = deps.getActiveTurn();
|
|
191
191
|
if (!turn || !deps.isAssistantMessage(message))
|
|
192
192
|
return;
|
|
@@ -211,7 +211,7 @@ export async function handleTelegramAssistantMessagePreviewStart(message, deps)
|
|
|
211
211
|
next.precedingFlush = state?.publicationPromise ?? state?.flushPromise ?? state?.precedingFlush;
|
|
212
212
|
deps.setState(next);
|
|
213
213
|
}
|
|
214
|
-
|
|
214
|
+
async function handleTelegramAssistantMessagePreviewUpdate(message, deps) {
|
|
215
215
|
const turn = deps.getActiveTurn();
|
|
216
216
|
if (!turn || !deps.isAssistantMessage(message))
|
|
217
217
|
return;
|
|
@@ -12,7 +12,6 @@ export interface TelegramPromptTemplateCommand {
|
|
|
12
12
|
export type TelegramPromptTemplateReader = (path: string) => string;
|
|
13
13
|
export declare function parsePromptTemplateArgs(argsString: string): string[];
|
|
14
14
|
export declare function substitutePromptTemplateArgs(content: string, args: readonly string[]): string;
|
|
15
|
-
export declare function isTelegramPromptTemplateCommandName(name: string): boolean;
|
|
16
15
|
export declare function mapPiPromptTemplateNameToTelegramCommandName(name: string): string | undefined;
|
|
17
16
|
export interface TelegramPromptTemplateCommandGetterDeps {
|
|
18
17
|
getCommands: () => readonly PiSlashCommandInfo[];
|
|
@@ -63,7 +63,7 @@ export function substitutePromptTemplateArgs(content, args) {
|
|
|
63
63
|
const allArgs = args.join(" ");
|
|
64
64
|
return result.replace(/\$ARGUMENTS/g, allArgs).replace(/\$@/g, allArgs);
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
function isTelegramPromptTemplateCommandName(name) {
|
|
67
67
|
return TELEGRAM_BOT_COMMAND_NAME_PATTERN.test(name);
|
|
68
68
|
}
|
|
69
69
|
export function mapPiPromptTemplateNameToTelegramCommandName(name) {
|
|
@@ -98,7 +98,6 @@ export interface PendingTelegramControlItem<TContext = unknown> extends Telegram
|
|
|
98
98
|
}
|
|
99
99
|
export type TelegramQueueItem<TContext = unknown> = PendingTelegramTurn | PendingTelegramControlItem<TContext>;
|
|
100
100
|
export declare const TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES: number;
|
|
101
|
-
export declare const TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS = 256;
|
|
102
101
|
export interface TelegramQueueHandoffBase {
|
|
103
102
|
chatId: number;
|
|
104
103
|
target?: TelegramQueueTarget;
|
|
@@ -132,6 +131,8 @@ export interface TelegramQueueHandoff {
|
|
|
132
131
|
handoffToken: string;
|
|
133
132
|
payload: TelegramQueueHandoffPayload;
|
|
134
133
|
}
|
|
134
|
+
/** Data decoder shared by authenticated IPC and cold storage; decoding grants no admission. */
|
|
135
|
+
export declare function parseTelegramQueueHandoffPayload(value: unknown): TelegramQueueHandoffPayload | undefined;
|
|
135
136
|
export interface TelegramQueueHandoffStageReceipt {
|
|
136
137
|
status: "staged";
|
|
137
138
|
receiptId: string;
|
|
@@ -192,14 +193,12 @@ export declare function getTelegramQueueLaneContract(lane: TelegramQueueLane): T
|
|
|
192
193
|
export declare function getTelegramQueueItemAdmissionMode(item: Pick<TelegramQueueItem, "queueLane">): TelegramQueueAdmissionMode;
|
|
193
194
|
export declare function isTelegramQueueItemAdmissionValid(item: Pick<TelegramQueueItem, "kind" | "queueLane">): boolean;
|
|
194
195
|
export declare function assertTelegramQueueItemAdmissionValid(item: Pick<TelegramQueueItem, "kind" | "queueLane" | "admissionReceipts">): void;
|
|
195
|
-
export declare function isPendingTelegramTurn<TContext = unknown>(item: TelegramQueueItem<TContext>): item is PendingTelegramTurn;
|
|
196
196
|
export declare function createTelegramQueueStore<TContext = unknown>(initialItems?: TelegramQueueItem<TContext>[]): TelegramQueueStateStore<TContext>;
|
|
197
197
|
export declare function createTelegramTransportStampRuntime(deps: {
|
|
198
198
|
getProfileName(): string | undefined;
|
|
199
199
|
getBotToken(): string | undefined;
|
|
200
200
|
}): TelegramTransportStampRuntime;
|
|
201
201
|
export declare function createTelegramTransportStampedQueueStore<TContext>(store: TelegramQueueStateStore<TContext>, getTransportStamp: () => TelegramTransportStamp): TelegramQueueStateStore<TContext>;
|
|
202
|
-
export declare function isTelegramQueueItemSkipped<TContext = unknown>(item: TelegramQueueItem<TContext>): boolean;
|
|
203
202
|
export declare function countExecutableTelegramQueueItems<TContext = unknown>(items: readonly TelegramQueueItem<TContext>[]): number;
|
|
204
203
|
export declare function createTelegramQueueItemCountGetter<TContext = unknown>(store: Pick<TelegramQueueStore<TContext>, "getQueuedItems">): () => number;
|
|
205
204
|
export declare function createTelegramActiveTurnStore<TTurn extends PendingTelegramTurn = PendingTelegramTurn>(): TelegramActiveTurnStore<TTurn>;
|
|
@@ -246,11 +245,6 @@ export declare function removeTelegramQueueItemsByMessageIds<TContext = unknown>
|
|
|
246
245
|
removedItems: TelegramQueueItem<TContext>[];
|
|
247
246
|
removedCount: number;
|
|
248
247
|
};
|
|
249
|
-
export declare function removeTelegramQueuedGuestPromptByOrder<TContext = unknown>(items: TelegramQueueItem<TContext>[], queueOrder: number): {
|
|
250
|
-
items: TelegramQueueItem<TContext>[];
|
|
251
|
-
removedItems: PendingTelegramTurn[];
|
|
252
|
-
removedCount: number;
|
|
253
|
-
};
|
|
254
248
|
export declare function applyTelegramQueuePromptReactionDisposition<TContext = unknown>(items: TelegramQueueItem<TContext>[], messageId: number, disposition: TelegramQueueReactionDisposition, destinationLaneOrder?: number, scope?: TelegramQueueMessageScope): {
|
|
255
249
|
items: TelegramQueueItem<TContext>[];
|
|
256
250
|
changed: boolean;
|
|
@@ -680,10 +674,8 @@ export declare function createTelegramSessionLifecycleHooks<TContext, TQueueItem
|
|
|
680
674
|
onSessionShutdown: (_event?: TelegramSessionLifecycleHookEvent, ctx?: TContext) => Promise<void>;
|
|
681
675
|
};
|
|
682
676
|
export declare function createTelegramQueueMutationController<TContext>(deps: TelegramQueueMutationControllerDeps<TContext>): TelegramQueueMutationController<TContext>;
|
|
683
|
-
export declare function reorderTelegramQueueItemsRuntime<TContext>(deps: TelegramQueueMutationRuntimeDeps<TContext>): void;
|
|
684
677
|
export declare function clearTelegramQueueItemsRuntime<TContext>(deps: TelegramQueueMutationRuntimeDeps<TContext>): number;
|
|
685
678
|
export declare function removeTelegramQueueItemsByMessageIdsRuntime<TContext>(messageIds: number[], deps: TelegramQueueMutationRuntimeDeps<TContext>, scope?: TelegramQueueMessageScope): number;
|
|
686
|
-
export declare function removeTelegramQueuedGuestPromptByOrderRuntime<TContext>(queueOrder: number, deps: TelegramQueueMutationRuntimeDeps<TContext>): boolean;
|
|
687
679
|
export declare function applyTelegramQueuePromptReactionDispositionRuntime<TContext>(messageId: number, disposition: TelegramQueueReactionDisposition, deps: TelegramQueueMutationRuntimeDeps<TContext>, scope?: TelegramQueueMessageScope): boolean;
|
|
688
680
|
export declare function enqueueTelegramPromptTurnRuntime<TMessage, TContext = unknown>(messages: TMessage[], deps: TelegramPromptEnqueueRuntimeDeps<TMessage, TContext>): Promise<PendingTelegramTurn>;
|
|
689
681
|
export declare function createTelegramPromptEnqueueController<TMessage, TContext = unknown>(deps: TelegramPromptEnqueueControllerDeps<TMessage, TContext>): TelegramPromptEnqueueController<TMessage, TContext>;
|
|
@@ -695,6 +687,7 @@ export interface TelegramControlRuntimeDeps<TContext> extends TelegramRuntimeEve
|
|
|
695
687
|
ctx: TContext;
|
|
696
688
|
sendTextReply: (chatId: number, replyToMessageId: number, text: string, options?: {
|
|
697
689
|
target?: TelegramQueueTarget;
|
|
690
|
+
parseMode?: "HTML";
|
|
698
691
|
}) => Promise<number | undefined>;
|
|
699
692
|
onSettled: (item: PendingTelegramControlItem<TContext>) => void;
|
|
700
693
|
}
|
|
@@ -712,11 +705,6 @@ export interface TelegramDeferredQueueDispatchRuntime<TContext = unknown> {
|
|
|
712
705
|
isGenerationActive: (generation: number) => boolean;
|
|
713
706
|
request: (dispatchNextQueuedTelegramTurn: (ctx: TContext) => void) => void;
|
|
714
707
|
}
|
|
715
|
-
/**
|
|
716
|
-
* Production debounce for deferred queue dispatch; the factory defaults to this
|
|
717
|
-
* so the entrypoint wires ports instead of policy constants.
|
|
718
|
-
*/
|
|
719
|
-
export declare const TELEGRAM_DEFERRED_DISPATCH_DELAY_MS = 50;
|
|
720
708
|
export declare function createTelegramDeferredQueueDispatchRuntime<TContext = unknown>(deps?: TelegramDeferredQueueDispatchRuntimeDeps): TelegramDeferredQueueDispatchRuntime<TContext>;
|
|
721
709
|
export interface TelegramQueueDispatchWatchdogRuntime<TContext = unknown> {
|
|
722
710
|
start: (ctx: TContext) => void;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { isVoiceTurn } from "./voice.js";
|
|
8
|
+
import { isWireRecord as isQueuePayloadRecord } from "./wire.js";
|
|
8
9
|
import { isTelegramApiCommitUnknownError } from "./telegram-api.js";
|
|
9
10
|
export const TELEGRAM_QUEUE_LANE_CONTRACTS = [
|
|
10
11
|
// Control lane intentionally accepts both direct controls and resume prompts.
|
|
@@ -30,7 +31,67 @@ export const TELEGRAM_QUEUE_LANE_CONTRACTS = [
|
|
|
30
31
|
},
|
|
31
32
|
];
|
|
32
33
|
export const TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
33
|
-
|
|
34
|
+
const TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS = 256;
|
|
35
|
+
function parseQueuePayloadReceipt(value, kind) {
|
|
36
|
+
if (!isQueuePayloadRecord(value) || value.queueKind !== kind ||
|
|
37
|
+
typeof value.receiptId !== "string" || !value.receiptId ||
|
|
38
|
+
(value.journalBindingKey !== undefined && (typeof value.journalBindingKey !== "string" || !value.journalBindingKey.trim())) ||
|
|
39
|
+
!Array.isArray(value.sourceUpdateIds) || value.sourceUpdateIds.length === 0 ||
|
|
40
|
+
value.sourceUpdateIds.some((id, index) => !Number.isSafeInteger(id) || id < 0 ||
|
|
41
|
+
(index > 0 && id <= value.sourceUpdateIds[index - 1])))
|
|
42
|
+
return undefined;
|
|
43
|
+
return { queueKind: kind, receiptId: value.receiptId, sourceUpdateIds: value.sourceUpdateIds,
|
|
44
|
+
...(typeof value.journalBindingKey === "string" ? { journalBindingKey: value.journalBindingKey } : {}) };
|
|
45
|
+
}
|
|
46
|
+
/** Data decoder shared by authenticated IPC and cold storage; decoding grants no admission. */
|
|
47
|
+
export function parseTelegramQueueHandoffPayload(value) {
|
|
48
|
+
if (!isQueuePayloadRecord(value) || (value.kind !== "prompt" && value.kind !== "control"))
|
|
49
|
+
return undefined;
|
|
50
|
+
const target = isQueuePayloadRecord(value.target) && typeof value.target.chatId === "number"
|
|
51
|
+
? { chatId: value.target.chatId, ...(typeof value.target.threadId === "number" ? { threadId: value.target.threadId } : {}) } : undefined;
|
|
52
|
+
const transportStamp = isQueuePayloadRecord(value.transportStamp) &&
|
|
53
|
+
typeof value.transportStamp.profile === "string" && typeof value.transportStamp.generation === "string"
|
|
54
|
+
? { profile: value.transportStamp.profile, generation: value.transportStamp.generation } : undefined;
|
|
55
|
+
if (!Number.isSafeInteger(value.chatId) || (value.target !== undefined && !target) ||
|
|
56
|
+
(value.transportStamp !== undefined && !transportStamp) || !Number.isSafeInteger(value.replyToMessageId) ||
|
|
57
|
+
(value.guestQueryId !== undefined && typeof value.guestQueryId !== "string") ||
|
|
58
|
+
(value.guestInlineMessageId !== undefined && typeof value.guestInlineMessageId !== "string") ||
|
|
59
|
+
!Number.isSafeInteger(value.queueOrder) || (value.queueLane !== "control" && value.queueLane !== "priority" && value.queueLane !== "default") ||
|
|
60
|
+
!Number.isSafeInteger(value.laneOrder) || typeof value.statusSummary !== "string" || !Array.isArray(value.admissionReceipts))
|
|
61
|
+
return undefined;
|
|
62
|
+
const admissionReceipts = value.admissionReceipts.map(receipt => parseQueuePayloadReceipt(receipt, value.kind));
|
|
63
|
+
if (admissionReceipts.length === 0 || admissionReceipts.length > TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS ||
|
|
64
|
+
admissionReceipts.some(receipt => !receipt))
|
|
65
|
+
return undefined;
|
|
66
|
+
const base = { chatId: value.chatId, ...(target ? { target } : {}), ...(transportStamp ? { transportStamp } : {}),
|
|
67
|
+
replyToMessageId: value.replyToMessageId,
|
|
68
|
+
...(typeof value.guestQueryId === "string" ? { guestQueryId: value.guestQueryId } : {}),
|
|
69
|
+
...(typeof value.guestInlineMessageId === "string" ? { guestInlineMessageId: value.guestInlineMessageId } : {}),
|
|
70
|
+
queueOrder: value.queueOrder, queueLane: value.queueLane,
|
|
71
|
+
laneOrder: value.laneOrder, statusSummary: value.statusSummary,
|
|
72
|
+
admissionReceipts: admissionReceipts };
|
|
73
|
+
if (value.kind === "control") {
|
|
74
|
+
return value.queueLane === "control" && (value.controlType === "status" || value.controlType === "model")
|
|
75
|
+
? { kind: "control", controlType: value.controlType, ...base } : undefined;
|
|
76
|
+
}
|
|
77
|
+
if (value.queueLane === "control" || !Array.isArray(value.sourceMessageIds) || value.sourceMessageIds.some(id => !Number.isSafeInteger(id)) ||
|
|
78
|
+
!Array.isArray(value.queuedAttachments) || value.queuedAttachments.some(attachment => !isQueuePayloadRecord(attachment) ||
|
|
79
|
+
typeof attachment.path !== "string" || typeof attachment.fileName !== "string") ||
|
|
80
|
+
!Array.isArray(value.content) || value.content.some(content => !isQueuePayloadRecord(content) ||
|
|
81
|
+
(content.type === "text" ? typeof content.text !== "string" : content.type === "image"
|
|
82
|
+
? typeof content.data !== "string" || typeof content.mimeType !== "string" : true)) ||
|
|
83
|
+
typeof value.historyText !== "string" || (value.priorityEmoji !== undefined && typeof value.priorityEmoji !== "string") ||
|
|
84
|
+
(value.reactionSuppressionEmoji !== undefined && typeof value.reactionSuppressionEmoji !== "string") ||
|
|
85
|
+
(value.voiceReplyPreferred !== undefined && typeof value.voiceReplyPreferred !== "boolean") ||
|
|
86
|
+
(value.voiceReplyRequired !== undefined && typeof value.voiceReplyRequired !== "boolean"))
|
|
87
|
+
return undefined;
|
|
88
|
+
return { kind: "prompt", ...base, sourceMessageIds: value.sourceMessageIds,
|
|
89
|
+
queuedAttachments: value.queuedAttachments, content: value.content, historyText: value.historyText,
|
|
90
|
+
...(typeof value.priorityEmoji === "string" ? { priorityEmoji: value.priorityEmoji } : {}),
|
|
91
|
+
...(typeof value.reactionSuppressionEmoji === "string" ? { reactionSuppressionEmoji: value.reactionSuppressionEmoji } : {}),
|
|
92
|
+
...(typeof value.voiceReplyPreferred === "boolean" ? { voiceReplyPreferred: value.voiceReplyPreferred } : {}),
|
|
93
|
+
...(typeof value.voiceReplyRequired === "boolean" ? { voiceReplyRequired: value.voiceReplyRequired } : {}) };
|
|
94
|
+
}
|
|
34
95
|
export function createTelegramQueueAdmissionReceipt(options) {
|
|
35
96
|
if (options.sourceUpdateIds.length === 0)
|
|
36
97
|
return undefined;
|
|
@@ -100,7 +161,7 @@ export function assertTelegramQueueItemAdmissionValid(item) {
|
|
|
100
161
|
function getTelegramQueueLaneRank(lane) {
|
|
101
162
|
return getTelegramQueueLaneContract(lane).dispatchRank;
|
|
102
163
|
}
|
|
103
|
-
|
|
164
|
+
function isPendingTelegramTurn(item) {
|
|
104
165
|
return item.kind === "prompt";
|
|
105
166
|
}
|
|
106
167
|
export function createTelegramQueueStore(initialItems = []) {
|
|
@@ -148,7 +209,7 @@ export function createTelegramTransportStampedQueueStore(store, getTransportStam
|
|
|
148
209
|
},
|
|
149
210
|
};
|
|
150
211
|
}
|
|
151
|
-
|
|
212
|
+
function isTelegramQueueItemSkipped(item) {
|
|
152
213
|
return item.kind === "prompt" && Boolean(item.reactionSuppressionEmoji);
|
|
153
214
|
}
|
|
154
215
|
export function countExecutableTelegramQueueItems(items) {
|
|
@@ -503,7 +564,7 @@ export function removeTelegramQueueItemsByMessageIds(items, messageIds, scope) {
|
|
|
503
564
|
removedCount: removedItems.length,
|
|
504
565
|
};
|
|
505
566
|
}
|
|
506
|
-
|
|
567
|
+
function removeTelegramQueuedGuestPromptByOrder(items, queueOrder) {
|
|
507
568
|
const index = items.findIndex((item) => {
|
|
508
569
|
return isPendingTelegramTurn(item) &&
|
|
509
570
|
item.guestQueryId !== undefined &&
|
|
@@ -1499,7 +1560,7 @@ function appendTelegramQueueItemRuntime(item, deps) {
|
|
|
1499
1560
|
return;
|
|
1500
1561
|
commitReorderedTelegramQueueItemsRuntime(nextItems, deps);
|
|
1501
1562
|
}
|
|
1502
|
-
|
|
1563
|
+
function reorderTelegramQueueItemsRuntime(deps) {
|
|
1503
1564
|
commitReorderedTelegramQueueItemsRuntime(deps.getQueuedItems(), deps);
|
|
1504
1565
|
}
|
|
1505
1566
|
export function clearTelegramQueueItemsRuntime(deps) {
|
|
@@ -1528,7 +1589,7 @@ export function removeTelegramQueueItemsByMessageIdsRuntime(messageIds, deps, sc
|
|
|
1528
1589
|
updateTelegramQueueStatusRuntime(deps);
|
|
1529
1590
|
return removedCount;
|
|
1530
1591
|
}
|
|
1531
|
-
|
|
1592
|
+
function removeTelegramQueuedGuestPromptByOrderRuntime(queueOrder, deps) {
|
|
1532
1593
|
const { items, removedItems, removedCount } = removeTelegramQueuedGuestPromptByOrder(deps.getQueuedItems(), queueOrder);
|
|
1533
1594
|
if (removedCount === 0)
|
|
1534
1595
|
return false;
|
|
@@ -1639,7 +1700,7 @@ export async function executeTelegramControlItemRuntime(item, deps) {
|
|
|
1639
1700
|
* Production debounce for deferred queue dispatch; the factory defaults to this
|
|
1640
1701
|
* so the entrypoint wires ports instead of policy constants.
|
|
1641
1702
|
*/
|
|
1642
|
-
|
|
1703
|
+
const TELEGRAM_DEFERRED_DISPATCH_DELAY_MS = 50;
|
|
1643
1704
|
export function createTelegramDeferredQueueDispatchRuntime(deps = {}) {
|
|
1644
1705
|
let boundContext;
|
|
1645
1706
|
let generation = 0;
|
|
@@ -2004,7 +2065,7 @@ export function createTelegramQueueDispatchController(deps) {
|
|
|
2004
2065
|
const announcementGeneration = nextDispatchAnnouncementGeneration;
|
|
2005
2066
|
const dispatchGeneration = deps.getDispatchGeneration?.();
|
|
2006
2067
|
deps.updateStatus(ctx);
|
|
2007
|
-
void deps.sendTextReply(dispatchPlan.item.chatId, dispatchPlan.item.replyToMessageId, "<b>⏩ Dispatching next queued turn.</b>", { target: dispatchPlan.item.target }).catch((error) => {
|
|
2068
|
+
void deps.sendTextReply(dispatchPlan.item.chatId, dispatchPlan.item.replyToMessageId, "<b>⏩ Dispatching next queued turn.</b>", { target: dispatchPlan.item.target, parseMode: "HTML" }).catch((error) => {
|
|
2008
2069
|
deps.recordRuntimeEvent?.("dispatch", error, {
|
|
2009
2070
|
phase: "next-announcement",
|
|
2010
2071
|
});
|