@llblab/pi-telegram 0.52.2 → 0.53.1
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/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +15 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +65 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +252 -127
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +128 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +516 -301
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/dist/lib/bus-api.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Zones: multi-instance bus, telegram api transport, live instance routing
|
|
4
4
|
* Wraps the direct Telegram Bot API runtime so follower instances can route outbound calls through the bus leader
|
|
5
5
|
*/
|
|
6
|
-
import { markTelegramBusCrossTargetDelivery, stripTelegramBusApiMetadata, } from "./bus.js";
|
|
7
|
-
import { buildTelegramAnswerGuestQueryBody,
|
|
6
|
+
import { markTelegramBusCrossTargetDelivery, parseTelegramBusSelectedMenuTextEffect, stripTelegramBusApiMetadata, } from "./bus.js";
|
|
7
|
+
import { buildTelegramAnswerGuestQueryBody, isTelegramApiCommitUnknownError, isTelegramMessageNotModifiedError, TelegramApiAuthorityError, TelegramApiCommitUnknownError, } from "./telegram-api.js";
|
|
8
8
|
function asRecord(value) {
|
|
9
9
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
10
10
|
? value
|
|
@@ -37,28 +37,147 @@ function markFollowerCrossTargetDelivery(body, defaultTarget) {
|
|
|
37
37
|
function rejectTelegramDirectOwnership(method) {
|
|
38
38
|
return Promise.reject(new Error(`Telegram ${method} requires direct transport ownership.`));
|
|
39
39
|
}
|
|
40
|
+
/** Bound selected-menu projection only; no ordinary/raw API fallback and no activation during preparation. */
|
|
41
|
+
export function createTelegramSelectedMenuTextApi(deps) {
|
|
42
|
+
const { operationId, registrationGeneration, assertAuthority, deliver } = deps, target = { ...deps.target };
|
|
43
|
+
if (typeof assertAuthority !== "function" ||
|
|
44
|
+
typeof deliver !== "function" ||
|
|
45
|
+
!operationId?.trim() ||
|
|
46
|
+
operationId.length > 128 ||
|
|
47
|
+
!registrationGeneration?.trim() ||
|
|
48
|
+
registrationGeneration.length > 128 ||
|
|
49
|
+
!Number.isSafeInteger(target.chatId) ||
|
|
50
|
+
target.chatId <= 0 ||
|
|
51
|
+
!Number.isSafeInteger(target.threadId) ||
|
|
52
|
+
target.threadId <= 0)
|
|
53
|
+
throw new TelegramApiAuthorityError(false);
|
|
54
|
+
const invoke = async (edit, raw, options) => {
|
|
55
|
+
let issued = false;
|
|
56
|
+
const assertCurrent = () => {
|
|
57
|
+
try {
|
|
58
|
+
assertAuthority();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
throw new TelegramApiAuthorityError(issued);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
if (options?.assertAuthority !== assertAuthority ||
|
|
66
|
+
Reflect.ownKeys(raw).some((key) => typeof key !== "string" ||
|
|
67
|
+
![
|
|
68
|
+
"chat_id",
|
|
69
|
+
"message_thread_id",
|
|
70
|
+
"text",
|
|
71
|
+
"parse_mode",
|
|
72
|
+
"reply_markup",
|
|
73
|
+
...(edit ? ["message_id"] : ["reply_parameters"]),
|
|
74
|
+
].includes(key)))
|
|
75
|
+
throw new TelegramApiAuthorityError(false);
|
|
76
|
+
const body = structuredClone(raw), reply = body.reply_parameters;
|
|
77
|
+
if (body.chat_id !== target.chatId ||
|
|
78
|
+
body.message_thread_id !== target.threadId ||
|
|
79
|
+
(reply !== undefined &&
|
|
80
|
+
(!reply ||
|
|
81
|
+
typeof reply !== "object" ||
|
|
82
|
+
Array.isArray(reply) ||
|
|
83
|
+
Object.keys(reply).some((key) => !["message_id", "allow_sending_without_reply"].includes(key)) ||
|
|
84
|
+
reply.allow_sending_without_reply !==
|
|
85
|
+
true ||
|
|
86
|
+
!Number.isSafeInteger(reply.message_id) ||
|
|
87
|
+
reply.message_id <= 0)))
|
|
88
|
+
throw new TelegramApiAuthorityError(false);
|
|
89
|
+
const effect = parseTelegramBusSelectedMenuTextEffect({
|
|
90
|
+
kind: edit ? "edit-text" : "send-text",
|
|
91
|
+
text: body.text,
|
|
92
|
+
...(body.parse_mode !== undefined
|
|
93
|
+
? { parseMode: body.parse_mode }
|
|
94
|
+
: {}),
|
|
95
|
+
...(body.reply_markup !== undefined
|
|
96
|
+
? { replyMarkup: body.reply_markup }
|
|
97
|
+
: {}),
|
|
98
|
+
...(edit
|
|
99
|
+
? { messageId: body.message_id }
|
|
100
|
+
: reply !== undefined
|
|
101
|
+
? {
|
|
102
|
+
replyToMessageId: reply.message_id,
|
|
103
|
+
}
|
|
104
|
+
: {}),
|
|
105
|
+
});
|
|
106
|
+
if (!effect)
|
|
107
|
+
throw new TelegramApiAuthorityError(false);
|
|
108
|
+
assertCurrent();
|
|
109
|
+
issued = true;
|
|
110
|
+
const observed = await deliver.call(deps, effect, assertAuthority);
|
|
111
|
+
assertCurrent();
|
|
112
|
+
if (observed.operationId !== operationId ||
|
|
113
|
+
observed.registrationGeneration !== registrationGeneration ||
|
|
114
|
+
observed.effect !== effect.kind ||
|
|
115
|
+
observed.recipient.target.chatId !== target.chatId ||
|
|
116
|
+
observed.recipient.target.threadId !== target.threadId ||
|
|
117
|
+
!Number.isSafeInteger(observed.messageId) ||
|
|
118
|
+
observed.messageId <= 0 ||
|
|
119
|
+
(effect.kind === "edit-text" && observed.messageId !== effect.messageId))
|
|
120
|
+
throw new Error("Selected-menu transport result is unconfirmed.");
|
|
121
|
+
assertCurrent();
|
|
122
|
+
return observed.messageId;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error instanceof TelegramApiAuthorityError ||
|
|
126
|
+
isTelegramApiCommitUnknownError(error))
|
|
127
|
+
throw error;
|
|
128
|
+
if (issued)
|
|
129
|
+
throw new TelegramApiCommitUnknownError(edit ? "editMessageText" : "sendMessage", error);
|
|
130
|
+
throw new TelegramApiAuthorityError(false);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
sendMessage: async (body, options) => ({
|
|
135
|
+
message_id: await invoke(false, body, options),
|
|
136
|
+
}),
|
|
137
|
+
editMessageText: async (body, options) => {
|
|
138
|
+
await invoke(true, body, options);
|
|
139
|
+
return "edited";
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** Followers route every chat action through the leader's generic `sendChatAction` call. */
|
|
144
|
+
function sendTelegramFollowerChatAction(deps, chatId, action, options) {
|
|
145
|
+
const body = withDefaultThreadTarget({
|
|
146
|
+
chat_id: chatId,
|
|
147
|
+
action,
|
|
148
|
+
...(options?.message_thread_id !== undefined
|
|
149
|
+
? { message_thread_id: options.message_thread_id }
|
|
150
|
+
: {}),
|
|
151
|
+
}, deps.getDefaultTarget?.());
|
|
152
|
+
return deps.callFollowerApi("call", ["sendChatAction", body]).then(asBoolean);
|
|
153
|
+
}
|
|
40
154
|
export function createTelegramBusAwareApiRuntime(deps) {
|
|
41
155
|
return {
|
|
42
156
|
call(method, body, options) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
157
|
+
if (deps.ownsDirect())
|
|
158
|
+
return deps.directRuntime.call(method, body, options);
|
|
159
|
+
// A process-local authority callback cannot cross IPC as an enforceable grant.
|
|
160
|
+
if (options?.assertAuthority)
|
|
161
|
+
return Promise.reject(new TelegramApiAuthorityError(false));
|
|
162
|
+
return deps.callFollowerApi("call", [
|
|
163
|
+
method,
|
|
164
|
+
body,
|
|
165
|
+
options,
|
|
166
|
+
]);
|
|
50
167
|
},
|
|
51
168
|
callMultipart(method, fields, fileField, filePath, fileName, options) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
169
|
+
if (deps.ownsDirect())
|
|
170
|
+
return deps.directRuntime.callMultipart(method, fields, fileField, filePath, fileName, options);
|
|
171
|
+
if (options?.assertAuthority)
|
|
172
|
+
return Promise.reject(new TelegramApiAuthorityError(false));
|
|
173
|
+
return deps.callFollowerApi("callMultipart", [
|
|
174
|
+
method,
|
|
175
|
+
fields,
|
|
176
|
+
fileField,
|
|
177
|
+
filePath,
|
|
178
|
+
fileName,
|
|
179
|
+
options,
|
|
180
|
+
]);
|
|
62
181
|
},
|
|
63
182
|
downloadFile(fileId, suggestedName, source) {
|
|
64
183
|
// The source names the file (kind-scope-message); dropping it falls back to the bare generated name.
|
|
@@ -80,54 +199,29 @@ export function createTelegramBusAwareApiRuntime(deps) {
|
|
|
80
199
|
? deps.directRuntime.getUpdates(body, signal)
|
|
81
200
|
: rejectTelegramDirectOwnership("getUpdates");
|
|
82
201
|
},
|
|
83
|
-
setMyCommands(commands) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
202
|
+
setMyCommands(commands, options) {
|
|
203
|
+
if (deps.ownsDirect())
|
|
204
|
+
return deps.directRuntime.setMyCommands(commands, options);
|
|
205
|
+
if (options?.assertAuthority)
|
|
206
|
+
return Promise.reject(new TelegramApiAuthorityError(false));
|
|
207
|
+
return deps
|
|
208
|
+
.callFollowerApi("call", ["setMyCommands", { commands }])
|
|
209
|
+
.then(asBoolean);
|
|
89
210
|
},
|
|
90
211
|
sendChatAction(chatId, action, options) {
|
|
91
|
-
const body = withDefaultThreadTarget({
|
|
92
|
-
chat_id: chatId,
|
|
93
|
-
action,
|
|
94
|
-
...(options?.message_thread_id !== undefined
|
|
95
|
-
? { message_thread_id: options.message_thread_id }
|
|
96
|
-
: {}),
|
|
97
|
-
}, deps.getDefaultTarget?.());
|
|
98
212
|
return deps.ownsDirect()
|
|
99
213
|
? deps.directRuntime.sendChatAction(chatId, action, options)
|
|
100
|
-
: deps
|
|
101
|
-
.callFollowerApi("call", ["sendChatAction", body])
|
|
102
|
-
.then(asBoolean);
|
|
214
|
+
: sendTelegramFollowerChatAction(deps, chatId, action, options);
|
|
103
215
|
},
|
|
104
216
|
sendTypingAction(chatId, options) {
|
|
105
|
-
const body = withDefaultThreadTarget({
|
|
106
|
-
chat_id: chatId,
|
|
107
|
-
action: "typing",
|
|
108
|
-
...(options?.message_thread_id !== undefined
|
|
109
|
-
? { message_thread_id: options.message_thread_id }
|
|
110
|
-
: {}),
|
|
111
|
-
}, deps.getDefaultTarget?.());
|
|
112
217
|
return deps.ownsDirect()
|
|
113
218
|
? deps.directRuntime.sendTypingAction(chatId, options)
|
|
114
|
-
: deps
|
|
115
|
-
.callFollowerApi("call", ["sendChatAction", body])
|
|
116
|
-
.then(asBoolean);
|
|
219
|
+
: sendTelegramFollowerChatAction(deps, chatId, "typing", options);
|
|
117
220
|
},
|
|
118
221
|
sendRecordVoiceAction(chatId, options) {
|
|
119
|
-
const body = withDefaultThreadTarget({
|
|
120
|
-
chat_id: chatId,
|
|
121
|
-
action: "record_voice",
|
|
122
|
-
...(options?.message_thread_id !== undefined
|
|
123
|
-
? { message_thread_id: options.message_thread_id }
|
|
124
|
-
: {}),
|
|
125
|
-
}, deps.getDefaultTarget?.());
|
|
126
222
|
return deps.ownsDirect()
|
|
127
223
|
? deps.directRuntime.sendRecordVoiceAction(chatId, options)
|
|
128
|
-
: deps
|
|
129
|
-
.callFollowerApi("call", ["sendChatAction", body])
|
|
130
|
-
.then(asBoolean);
|
|
224
|
+
: sendTelegramFollowerChatAction(deps, chatId, "record_voice", options);
|
|
131
225
|
},
|
|
132
226
|
sendMessageDraft(chatId, draftId, text, options) {
|
|
133
227
|
const body = {
|
|
@@ -150,25 +244,29 @@ export function createTelegramBusAwareApiRuntime(deps) {
|
|
|
150
244
|
.callFollowerApi("call", ["sendMessageDraft", scopedBody])
|
|
151
245
|
.then(asBoolean);
|
|
152
246
|
},
|
|
153
|
-
sendMessage(body) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
247
|
+
sendMessage(body, options) {
|
|
248
|
+
if (deps.ownsDirect())
|
|
249
|
+
return deps.directRuntime.sendMessage(stripTelegramBusApiMetadata(body), options);
|
|
250
|
+
if (options?.assertAuthority)
|
|
251
|
+
return Promise.reject(new TelegramApiAuthorityError(false));
|
|
252
|
+
return deps
|
|
253
|
+
.callFollowerApi("call", [
|
|
254
|
+
"sendMessage",
|
|
255
|
+
markFollowerCrossTargetDelivery(body, deps.getDefaultTarget?.()),
|
|
256
|
+
])
|
|
257
|
+
.then(asSentMessage);
|
|
162
258
|
},
|
|
163
|
-
sendRichMessage(body) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
259
|
+
sendRichMessage(body, options) {
|
|
260
|
+
if (deps.ownsDirect())
|
|
261
|
+
return deps.directRuntime.sendRichMessage(stripTelegramBusApiMetadata(body), options);
|
|
262
|
+
if (options?.assertAuthority)
|
|
263
|
+
return Promise.reject(new TelegramApiAuthorityError(false));
|
|
264
|
+
return deps
|
|
265
|
+
.callFollowerApi("call", [
|
|
266
|
+
"sendRichMessage",
|
|
267
|
+
markFollowerCrossTargetDelivery(body, deps.getDefaultTarget?.()),
|
|
268
|
+
])
|
|
269
|
+
.then(asSentMessage);
|
|
172
270
|
},
|
|
173
271
|
sendRichMessageDraft(body) {
|
|
174
272
|
return deps.ownsDirect()
|
|
@@ -177,9 +275,11 @@ export function createTelegramBusAwareApiRuntime(deps) {
|
|
|
177
275
|
.callFollowerApi("call", ["sendRichMessageDraft", body])
|
|
178
276
|
.then(asBoolean);
|
|
179
277
|
},
|
|
180
|
-
async editMessageText(body) {
|
|
278
|
+
async editMessageText(body, options) {
|
|
181
279
|
if (deps.ownsDirect())
|
|
182
|
-
return deps.directRuntime.editMessageText(body);
|
|
280
|
+
return deps.directRuntime.editMessageText(body, options);
|
|
281
|
+
if (options?.assertAuthority)
|
|
282
|
+
throw new TelegramApiAuthorityError(false);
|
|
183
283
|
try {
|
|
184
284
|
await deps.callFollowerApi("call", ["editMessageText", body]);
|
|
185
285
|
return "edited";
|
|
@@ -200,17 +300,20 @@ export function createTelegramBusAwareApiRuntime(deps) {
|
|
|
200
300
|
{ chat_id: chatId, message_id: messageId, reply_markup: replyMarkup },
|
|
201
301
|
]);
|
|
202
302
|
},
|
|
203
|
-
async answerCallbackQuery(callbackQueryId, text) {
|
|
204
|
-
const
|
|
303
|
+
async answerCallbackQuery(callbackQueryId, text, options) {
|
|
304
|
+
const assertAuthority = options?.assertAuthority;
|
|
205
305
|
if (deps.ownsDirect()) {
|
|
206
|
-
|
|
306
|
+
const authorityOptions = assertAuthority ? [{ assertAuthority }] : [];
|
|
307
|
+
await deps.directRuntime.answerCallbackQuery(callbackQueryId, text, ...authorityOptions);
|
|
207
308
|
return;
|
|
208
309
|
}
|
|
310
|
+
if (assertAuthority)
|
|
311
|
+
throw new TelegramApiAuthorityError(false);
|
|
209
312
|
await deps.callFollowerApi("call", [
|
|
210
313
|
"answerCallbackQuery",
|
|
211
314
|
{
|
|
212
315
|
callback_query_id: callbackQueryId,
|
|
213
|
-
...(
|
|
316
|
+
...(text !== undefined ? { text } : {}),
|
|
214
317
|
},
|
|
215
318
|
]);
|
|
216
319
|
},
|
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
* heartbeat, forwarded-update receiving, and follower-routed API calls.
|
|
6
6
|
* It must not spawn Pi processes or create hidden Telegram-originated instances.
|
|
7
7
|
*/
|
|
8
|
+
import type { TelegramPreparedHeldCommand } from "./commands.ts";
|
|
8
9
|
import type { TelegramWorkspaceRestoreExecutor, TelegramWorkspaceRestoreIntent } from "./threads.ts";
|
|
9
|
-
import
|
|
10
|
+
import type { TelegramLiveSourceCompletionReadiness, TelegramUpdateAdmissionLifecycleRuntime } from "./updates.ts";
|
|
11
|
+
import { TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY, TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE, type TelegramBusAgentMessage, type TelegramBusAgentTargetSelector, type TelegramBusEnvelope, type TelegramBusFollowerQueueHandoffOffer, type TelegramBusForwardOwnership, type TelegramBusLiveRebindApplyObservation, type TelegramBusLiveRebindCommandObservation, type TelegramBusLiveRebindSaveObservation, type TelegramBusLiveRebindSettlementObservation, type TelegramBusLiveRebindWorkObservation, type TelegramBusLiveRebindWorkState, type TelegramBusProtocolIdentity, type TelegramBusSelectedMenuDeliveryObservation, type TelegramBusSelectedMenuTextEffect, type TelegramBusSocketPathSource, type TelegramBusWorkspaceRestoreObservation } from "./bus.ts";
|
|
12
|
+
import type { TelegramConfigStore, TelegramThreadDisplayMode } from "./config.ts";
|
|
10
13
|
import { type TelegramUpdateJournalStoreOptions } from "./journal.ts";
|
|
11
14
|
import { type TelegramLockEntry, type TelegramLockState } from "./locks.ts";
|
|
12
|
-
import type {
|
|
13
|
-
import type
|
|
14
|
-
import
|
|
15
|
-
import type { TelegramConfigStore, TelegramThreadDisplayMode } from "./config.ts";
|
|
15
|
+
import type { TelegramQueueHandoffStageResult } from "./queue.ts";
|
|
16
|
+
import { type TelegramTarget } from "./target.ts";
|
|
17
|
+
import * as Threads from "./threads.ts";
|
|
16
18
|
import { type TelegramWorkspaceAdmissionLedger } from "./workspace-admission.ts";
|
|
17
19
|
export declare const TELEGRAM_BUS_FOLLOWER_HEARTBEAT_TIMEOUT_MS = 8000;
|
|
18
20
|
export interface TelegramFollowerSessionHandoff {
|
|
@@ -233,6 +235,8 @@ export interface TelegramBusForwardedUpdateReceiverRuntimeDeps<TContext> {
|
|
|
233
235
|
getAuthSecret?: () => string | undefined;
|
|
234
236
|
getRegistrationGeneration: () => string | undefined;
|
|
235
237
|
getRecipientBindingKey: () => string | undefined;
|
|
238
|
+
/** Live preparation names the current journal, not the stable forwarding/profile identity. */
|
|
239
|
+
getLiveRebindJournalBindingKey?: () => string | undefined;
|
|
236
240
|
durableAdmission: TelegramBusFollowerDurableAdmissionPort<TContext>;
|
|
237
241
|
sourceReferenceAdmission?: TelegramBusFollowerDurableAdmissionPort<TContext>;
|
|
238
242
|
isSourceReferenceAdmissionEnabled?: () => boolean;
|
|
@@ -244,6 +248,23 @@ export interface TelegramBusForwardedUpdateReceiverRuntimeDeps<TContext> {
|
|
|
244
248
|
handleQueueHandoff?: (envelope: Extract<TelegramBusEnvelope, {
|
|
245
249
|
kind: "leader.offerQueueHandoff";
|
|
246
250
|
}>, ctx: TContext) => Promise<TelegramQueueHandoffStageResult> | TelegramQueueHandoffStageResult;
|
|
251
|
+
getSessionId?: () => string | undefined;
|
|
252
|
+
getLeaderProtocol?: () => TelegramBusProtocolIdentity | undefined;
|
|
253
|
+
getLocalProtocol?: () => TelegramBusProtocolIdentity | undefined;
|
|
254
|
+
isLiveRebindSaveEnabled?: () => boolean;
|
|
255
|
+
isLiveRebindApplyEnabled?: () => boolean;
|
|
256
|
+
isLiveRebindSettleEnabled?: () => boolean;
|
|
257
|
+
handleLiveRebindSettle?: (envelope: Extract<TelegramBusEnvelope, {
|
|
258
|
+
kind: "leader.settleLiveRebind";
|
|
259
|
+
}>, ctx: TContext, isCurrent: () => boolean) => Promise<TelegramBusLiveRebindSettlementObservation | TelegramBusLiveRebindWorkObservation | TelegramBusLiveRebindCommandObservation>;
|
|
260
|
+
handleLiveRebindApply?: (envelope: Extract<TelegramBusEnvelope, {
|
|
261
|
+
kind: "leader.applyLiveRebind";
|
|
262
|
+
}>, ctx: TContext, isCurrent: () => boolean) => Promise<TelegramBusLiveRebindApplyObservation>;
|
|
263
|
+
/** Availability only; never future recipient activation or a command execution grant. */
|
|
264
|
+
isLiveRebindCommandSetEnabled?: () => boolean;
|
|
265
|
+
handleLiveRebindSave?: (envelope: Extract<TelegramBusEnvelope, {
|
|
266
|
+
kind: "leader.prepareLiveRebind";
|
|
267
|
+
}>, ctx: TContext, isCurrent: () => boolean) => TelegramBusLiveRebindSaveObservation | Promise<TelegramBusLiveRebindSaveObservation>;
|
|
247
268
|
isWorkspaceRestoreEnabled?: () => boolean;
|
|
248
269
|
handleWorkspaceRestore?: (input: {
|
|
249
270
|
operationId: string;
|
|
@@ -292,22 +313,47 @@ export declare function createTelegramBusFollowerRestoreContextGetter<TContext>(
|
|
|
292
313
|
getLeaderState: () => TelegramLockState;
|
|
293
314
|
getAuthenticatedSecret: () => string | undefined;
|
|
294
315
|
getLeaderProtocol: () => TelegramBusProtocolIdentity | undefined;
|
|
316
|
+
capability?: typeof TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE | typeof TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_APPLY | typeof TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY;
|
|
295
317
|
}): (ctx: TContext) => TelegramBusFollowerRestoreContext | undefined;
|
|
296
|
-
/**
|
|
318
|
+
/** Shared canonical/local target owner and scoped live-carrier hooks; caller authenticates transport, no canonical writes or cleanup. */
|
|
297
319
|
export declare function createTelegramBusFollowerWorkspaceRestoreHandler<TContext>(deps: {
|
|
298
320
|
instanceId: string;
|
|
299
321
|
/** Captures actual Pi lifetime and authenticated leader/profile authority, never request-derived values. */
|
|
300
322
|
getContextAuthority: (ctx: TContext) => TelegramBusFollowerRestoreContext | undefined;
|
|
301
323
|
readRestoreIntent: (operationId: string, profileBindingKey: string) => TelegramWorkspaceRestoreIntent | undefined;
|
|
302
|
-
|
|
324
|
+
readLiveRebindIntent?: (operationId: string, profileBindingKey: string) => Threads.TelegramWorkspaceLiveRebindIntent | undefined;
|
|
325
|
+
topicTargetStore: Pick<Threads.TelegramTopicTargetStore, "load" | "withWorkspaceRestoreSnapshot"> & Partial<Pick<Threads.TelegramTopicTargetStore, "withWorkspaceLiveRebindSnapshot">>;
|
|
303
326
|
registrationState: Pick<TelegramBusFollowerRegistrationState, "getTarget" | "getSlot" | "getGeneration" | "setRegistered" | "getLeaderProtocol">;
|
|
304
327
|
getWorkspaceAdmission: NonNullable<TelegramBusFollowerWorkspaceAdmissionDeps["getWorkspaceAdmission"]>;
|
|
305
328
|
recordRuntimeEvent?: TelegramBusFollowerWorkspaceAdmissionDeps["recordRuntimeEvent"];
|
|
306
|
-
}): (input: {
|
|
329
|
+
}): ((input: {
|
|
307
330
|
operationId: string;
|
|
308
331
|
registrationGeneration: string;
|
|
309
332
|
mode: "apply" | "inspect";
|
|
310
|
-
|
|
333
|
+
liveRebind?: {
|
|
334
|
+
sourceUpdateIds: readonly number[];
|
|
335
|
+
isCurrent(): boolean;
|
|
336
|
+
/** Captured prepared-branch target; equality must precede local apply or release grants. */
|
|
337
|
+
expectedTarget?: TelegramTarget & {
|
|
338
|
+
threadId: number;
|
|
339
|
+
};
|
|
340
|
+
release?: (canRelease: () => boolean) => void;
|
|
341
|
+
observeWork?: (oldTarget: TelegramTarget & {
|
|
342
|
+
threadId: number;
|
|
343
|
+
}) => void;
|
|
344
|
+
};
|
|
345
|
+
}, ctx: TContext) => Promise<TelegramBusWorkspaceRestoreObservation>) & {
|
|
346
|
+
/** Capture availability only; asserting current recipient requires canonical released/local-applied ownership. */
|
|
347
|
+
prepareLiveCommandRecipient(input: {
|
|
348
|
+
operationId: string;
|
|
349
|
+
registrationGeneration: string;
|
|
350
|
+
sessionId: string;
|
|
351
|
+
sourceUpdateIds: readonly number[];
|
|
352
|
+
target: TelegramTarget & {
|
|
353
|
+
threadId: number;
|
|
354
|
+
};
|
|
355
|
+
}, ctx: TContext): Pick<TelegramBusFollowerCommandOwner<TContext>, "isCurrent" | "assertRecipientCurrent"> | undefined;
|
|
356
|
+
};
|
|
311
357
|
export declare function createTelegramBusFollowerClientRuntime<TContext, TReactionUpdate, TCallbackQuery, TMessage = unknown>(deps: TelegramBusFollowerClientRuntimeDeps<TMessage>): {
|
|
312
358
|
createRequestId: () => string;
|
|
313
359
|
callApi: (method: string, args: unknown[]) => Promise<unknown>;
|
|
@@ -339,29 +385,29 @@ export declare function createTelegramBusFollowerClientRuntime<TContext, TReacti
|
|
|
339
385
|
ctx: TContext;
|
|
340
386
|
}) => Promise<import("./bus.ts").TelegramBusForeignUpdateSettlement>;
|
|
341
387
|
};
|
|
342
|
-
queueHandoff: (input:
|
|
343
|
-
recipientInstanceId: string;
|
|
344
|
-
recipientRegistrationGeneration: string;
|
|
345
|
-
donorProcessId: number;
|
|
346
|
-
donorProcessBirthId: string;
|
|
347
|
-
donorSessionGeneration: number;
|
|
348
|
-
donorAcquisitionId: string;
|
|
349
|
-
donorAcquiredAtMs: number;
|
|
350
|
-
handoffToken: string;
|
|
351
|
-
payload: TelegramQueueHandoffPayload;
|
|
352
|
-
}) => Promise<TelegramQueueHandoffStageResult>;
|
|
388
|
+
queueHandoff: (input: TelegramBusFollowerQueueHandoffOffer) => Promise<TelegramQueueHandoffStageResult>;
|
|
353
389
|
};
|
|
354
|
-
export declare function createTelegramBusFollowerQueueHandoffClient(deps: TelegramBusFollowerApiCallerDeps): (input:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
390
|
+
export declare function createTelegramBusFollowerQueueHandoffClient(deps: TelegramBusFollowerApiCallerDeps): (input: TelegramBusFollowerQueueHandoffOffer) => Promise<TelegramQueueHandoffStageResult>;
|
|
391
|
+
/** Private selected text-effect caller; not a guarded ordinary API adapter or follower command admission grant. */
|
|
392
|
+
export declare function createTelegramBusFollowerSelectedMenuCaller<TContext>(deps: {
|
|
393
|
+
client: TelegramBusFollowerApiCallerDeps;
|
|
394
|
+
protocolIdentity: TelegramBusProtocolIdentity;
|
|
395
|
+
recipient: {
|
|
396
|
+
getContextAuthority(ctx: TContext): TelegramBusFollowerRestoreContext | undefined;
|
|
397
|
+
getJournalBindingKey(ctx: TContext): string | undefined;
|
|
398
|
+
getProcessIdentity(): {
|
|
399
|
+
processId: number;
|
|
400
|
+
processBirthId: string;
|
|
401
|
+
};
|
|
402
|
+
registrationState: Pick<TelegramBusFollowerRegistrationState, "getTarget" | "getSlot" | "getGeneration" | "getLeaderProtocol">;
|
|
403
|
+
};
|
|
404
|
+
}): (input: {
|
|
405
|
+
ctx: TContext;
|
|
406
|
+
operationId: string;
|
|
407
|
+
registrationGeneration: string;
|
|
408
|
+
effect: TelegramBusSelectedMenuTextEffect;
|
|
409
|
+
assertAuthority: () => void;
|
|
410
|
+
}) => Promise<TelegramBusSelectedMenuDeliveryObservation>;
|
|
365
411
|
export declare function createTelegramBusFollowerApiCaller(deps: TelegramBusFollowerApiCallerDeps): (method: string, args: unknown[]) => Promise<unknown>;
|
|
366
412
|
export declare function createTelegramBusFollowerSessionReplacementSuspender(deps: TelegramBusFollowerSessionReplacementSuspenderDeps): (preserveTarget?: boolean) => Promise<void>;
|
|
367
413
|
export declare function createTelegramBusFollowerSessionRefreshHook<TContext>(deps: TelegramBusFollowerSessionRefreshHookDeps<TContext>): (_event: unknown, ctx: TContext) => Promise<void>;
|
|
@@ -383,6 +429,40 @@ export declare function createTelegramBusFollowerPairedAdmission(deps: {
|
|
|
383
429
|
export declare function prepareTelegramBusFollowerJournaledUpdateForExecution<TUpdate extends {
|
|
384
430
|
message?: unknown;
|
|
385
431
|
} & Record<string, unknown>>(update: TUpdate, prepareForwardedMessage: (message: NonNullable<TUpdate["message"]>, position: "comment" | "forward") => void): TUpdate;
|
|
432
|
+
/** Existing Commands/menu assembly preflights the copied input and captures independent recipient/transport owners. */
|
|
433
|
+
export interface TelegramBusFollowerCommandOwner<TContext> {
|
|
434
|
+
isCurrent(): boolean;
|
|
435
|
+
assertRecipientCurrent(): void;
|
|
436
|
+
prepare(readiness: TelegramLiveSourceCompletionReadiness, ctx: TContext, input: {
|
|
437
|
+
target: TelegramTarget & {
|
|
438
|
+
threadId: number;
|
|
439
|
+
};
|
|
440
|
+
assertSourceCurrent(): void;
|
|
441
|
+
assertRecipientCurrent(): void;
|
|
442
|
+
}): TelegramPreparedHeldCommand | undefined;
|
|
443
|
+
}
|
|
444
|
+
/** One current warm preparation, not a prompt queue or restart recovery record. */
|
|
445
|
+
export declare function createTelegramBusFollowerLiveRebindRuntime<TContext>(deps: {
|
|
446
|
+
getAdmission(ctx: TContext): Pick<TelegramUpdateAdmissionLifecycleRuntime<TContext>, "prepareLiveInput" | "appendBatch" | "getJournalBindingKey"> | undefined;
|
|
447
|
+
applyTarget?: (...args: Parameters<ReturnType<typeof createTelegramBusFollowerWorkspaceRestoreHandler<TContext>>>) => ReturnType<ReturnType<typeof createTelegramBusFollowerWorkspaceRestoreHandler<TContext>>>;
|
|
448
|
+
observeWork?: (oldTarget: TelegramTarget & {
|
|
449
|
+
threadId: number;
|
|
450
|
+
}, ctx: TContext) => TelegramBusLiveRebindWorkState;
|
|
451
|
+
/** Pure preflight: undefined refuses before hold/append; this port never activates future recipient authority. */
|
|
452
|
+
getCommandOwner?: (input: Readonly<Omit<Extract<TelegramBusEnvelope, {
|
|
453
|
+
kind: "leader.prepareLiveRebind";
|
|
454
|
+
}>, "kind" | "auth" | "requestId" | "sentAtMs">>, ctx: TContext) => TelegramBusFollowerCommandOwner<TContext> | undefined;
|
|
455
|
+
}): {
|
|
456
|
+
save(envelope: Extract<TelegramBusEnvelope, {
|
|
457
|
+
kind: "leader.prepareLiveRebind";
|
|
458
|
+
}>, ctx: TContext, isCurrent: () => boolean): TelegramBusLiveRebindSaveObservation;
|
|
459
|
+
apply(envelope: Extract<TelegramBusEnvelope, {
|
|
460
|
+
kind: "leader.applyLiveRebind";
|
|
461
|
+
}>, ctx: TContext, isCurrent: () => boolean): Promise<TelegramBusLiveRebindApplyObservation>;
|
|
462
|
+
settle(envelope: Extract<TelegramBusEnvelope, {
|
|
463
|
+
kind: "leader.settleLiveRebind";
|
|
464
|
+
}>, ctx: TContext, isCurrent: () => boolean): Promise<TelegramBusLiveRebindSettlementObservation | TelegramBusLiveRebindWorkObservation | TelegramBusLiveRebindCommandObservation>;
|
|
465
|
+
};
|
|
386
466
|
export declare function createTelegramBusFollowerDurableAdmissionRuntime<TContext>(deps: {
|
|
387
467
|
journal: {
|
|
388
468
|
appendBatch(updates: readonly ({
|