@llblab/pi-kit 0.5.2 → 0.7.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/CHANGELOG.md +8 -0
- package/README.md +5 -5
- package/node_modules/@llblab/pi-actors/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-actors/README.md +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +3 -0
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +14 -1
- package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +45 -3
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +16 -3
- package/node_modules/@llblab/pi-actors/dist/lib/observability.js +92 -7
- package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +0 -1
- package/node_modules/@llblab/pi-actors/dist/lib/pi.js +15 -24
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.d.ts +17 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.js +44 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +4 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +102 -4
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +58 -39
- package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +14 -6
- package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/async-runs.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +18 -23
- package/node_modules/@llblab/pi-actors/lib/async-runs.ts +18 -1
- package/node_modules/@llblab/pi-actors/lib/command-templates.ts +41 -3
- package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +1 -1
- package/node_modules/@llblab/pi-actors/lib/observability.ts +119 -5
- package/node_modules/@llblab/pi-actors/lib/pi.ts +15 -28
- package/node_modules/@llblab/pi-actors/lib/run-delivery-lineage.ts +68 -0
- package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +120 -4
- package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +69 -44
- package/node_modules/@llblab/pi-actors/lib/runtime.ts +17 -6
- package/node_modules/@llblab/pi-actors/package.json +1 -1
- package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-grow-loop/AGENTS.md +2 -2
- package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -0
- package/node_modules/@llblab/pi-grow-loop/README.md +6 -6
- package/node_modules/@llblab/pi-grow-loop/index.ts +6 -3
- package/node_modules/@llblab/pi-grow-loop/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +33 -12
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +125 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +87 -45
- package/node_modules/@llblab/pi-state-flow/README.md +203 -107
- package/node_modules/@llblab/pi-state-flow/docs/README.md +4 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +188 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +42 -0
- package/node_modules/@llblab/pi-state-flow/index.ts +164 -2
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +138 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +273 -0
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +48 -0
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +18 -5
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +268 -0
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +117 -0
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +562 -0
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +24 -12
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +606 -70
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +666 -0
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +95 -0
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +141 -0
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +52 -0
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +88 -0
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +296 -0
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +23 -7
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +79 -0
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +264 -0
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +6 -0
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +99 -7
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +305 -48
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +74 -7
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +125 -6
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +196 -0
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +233 -0
- package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +70 -24
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +254 -29
- package/node_modules/@llblab/pi-state-flow/package.json +8 -2
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +128 -0
- package/node_modules/@llblab/pi-telegram/AGENTS.md +14 -9
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +20 -4
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +22 -5
- package/node_modules/@llblab/pi-telegram/README.md +13 -9
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +220 -18
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +70 -19
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +13 -7
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +13 -5
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +3 -1
- package/node_modules/@llblab/pi-telegram/index.ts +4 -1415
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +19 -5
- package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +6 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +37 -2
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +600 -135
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +962 -55
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +350 -26
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +544 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +234 -11
- package/node_modules/@llblab/pi-telegram/lib/config.ts +178 -25
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1792 -0
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +20 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +2184 -126
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +7 -1
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +38 -1
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +154 -15
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +74 -40
- package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +28 -42
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +18 -14
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +29 -0
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +85 -17
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +115 -70
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +66 -22
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +47 -39
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +305 -112
- package/node_modules/@llblab/pi-telegram/lib/status.ts +10 -0
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +308 -39
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +315 -7
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +664 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +226 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +118 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1686 -129
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1319 -97
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +1643 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +968 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +84 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/screenshot.png +0 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-bus.mjs +83 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +101 -0
- package/node_modules/@llblab/{skills → pi-telegram/skills}/show-me/SKILL.md +28 -6
- package/node_modules/@llblab/pi-telegram/skills/show-me/references/telegram-surfaces.md +43 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/delivery-and-threads.md +1 -1
- package/node_modules/@llblab/skills/abcd-context/AGENTS.md +1 -0
- package/node_modules/@llblab/skills/abcd-context/CHANGELOG.md +6 -2
- package/node_modules/@llblab/skills/abcd-context/SKILL.md +1 -1
- package/node_modules/@llblab/skills/abcd-context/docs/validation-design.md +11 -5
- package/node_modules/@llblab/skills/abcd-context/scripts/_self-test.mjs +61 -0
- package/node_modules/@llblab/skills/abcd-context/scripts/validate-context.mjs +67 -0
- package/node_modules/@llblab/skills/package.json +2 -3
- package/node_modules/@llblab/skills/release-flow/SKILL.md +2 -4
- package/package.json +7 -6
|
@@ -8,7 +8,7 @@ import { unlink } from "node:fs/promises";
|
|
|
8
8
|
import { basename, extname } from "node:path";
|
|
9
9
|
|
|
10
10
|
import { assertTelegramInlineKeyboardCallbackData } from "./keyboard.ts";
|
|
11
|
-
import {
|
|
11
|
+
import { withTelegramReplyParameters } from "./replies.ts";
|
|
12
12
|
import {
|
|
13
13
|
getTelegramTargetThreadParams,
|
|
14
14
|
type TelegramTarget,
|
|
@@ -80,17 +80,6 @@ export interface TelegramVoiceReplySenderPorts<THandler = unknown> {
|
|
|
80
80
|
getProgrammaticVoiceHandlers?: () => TelegramOutboundProgrammaticVoiceHandler[];
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function buildVoiceReplyParameters(
|
|
84
|
-
chatId: number,
|
|
85
|
-
replyToPrompt: boolean | undefined,
|
|
86
|
-
replyToMessageId: number | undefined,
|
|
87
|
-
target?: TelegramTarget,
|
|
88
|
-
): string | undefined {
|
|
89
|
-
if (replyToPrompt === false || replyToMessageId === undefined)
|
|
90
|
-
return undefined;
|
|
91
|
-
return buildTelegramMultipartReplyParameters(chatId, replyToMessageId, target);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
83
|
async function ensureTelegramVoiceFileFormat(
|
|
95
84
|
filePath: string,
|
|
96
85
|
): Promise<string> {
|
|
@@ -131,36 +120,33 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
131
120
|
if (deps.isDeliveryActive?.() === false) return;
|
|
132
121
|
await sendVoiceChatAction(deps, turn.chatId);
|
|
133
122
|
if (deps.isDeliveryActive?.() === false) return;
|
|
134
|
-
|
|
135
|
-
turn.chatId,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"voice",
|
|
162
|
-
voiceFilePath,
|
|
163
|
-
basename(voiceFilePath),
|
|
123
|
+
await withTelegramReplyParameters(
|
|
124
|
+
turn.chatId, options?.replyToPrompt === false ? undefined : turn.replyToMessageId, turn.target,
|
|
125
|
+
(replyParameters) => deps.sendMultipart(
|
|
126
|
+
"sendVoice",
|
|
127
|
+
{
|
|
128
|
+
chat_id: String(turn.chatId),
|
|
129
|
+
...(replyParameters ? { reply_parameters: JSON.stringify(replyParameters) } : {}),
|
|
130
|
+
...(turn.target
|
|
131
|
+
? Object.fromEntries(
|
|
132
|
+
Object.entries(getTelegramTargetThreadParams(turn.target)).map(
|
|
133
|
+
([key, value]) => [key, String(value)],
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
: {}),
|
|
137
|
+
...(options?.replyMarkup !== undefined && options.replyMarkup !== null
|
|
138
|
+
? {
|
|
139
|
+
reply_markup:
|
|
140
|
+
typeof options.replyMarkup === "string"
|
|
141
|
+
? options.replyMarkup
|
|
142
|
+
: JSON.stringify(options.replyMarkup),
|
|
143
|
+
}
|
|
144
|
+
: {}),
|
|
145
|
+
},
|
|
146
|
+
"voice",
|
|
147
|
+
voiceFilePath,
|
|
148
|
+
basename(voiceFilePath),
|
|
149
|
+
),
|
|
164
150
|
);
|
|
165
151
|
};
|
|
166
152
|
|
|
@@ -11,6 +11,7 @@ import { join } from "node:path";
|
|
|
11
11
|
import type { TelegramAssistantSegmentEvent } from "./activity.ts";
|
|
12
12
|
import { resolveTelegramTempDir } from "./paths.ts";
|
|
13
13
|
import * as Replies from "./replies.ts";
|
|
14
|
+
import type { TelegramPreparedPreviewDelivery } from "./preview.ts";
|
|
14
15
|
import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
|
|
15
16
|
import type {
|
|
16
17
|
TelegramEditMessageTextBody,
|
|
@@ -249,12 +250,8 @@ export interface TelegramOutboundTextPreviewRuntimeDeps<
|
|
|
249
250
|
> {
|
|
250
251
|
execCommand: TelegramVoiceReplySenderDeps["execCommand"];
|
|
251
252
|
getHandlers?: () => TelegramOutboundHandlerConfig[] | undefined;
|
|
252
|
-
finalizeMarkdownPreview:
|
|
253
|
-
|
|
254
|
-
markdown: string,
|
|
255
|
-
replyToMessageId: number,
|
|
256
|
-
options?: { replyMarkup?: TReplyMarkup },
|
|
257
|
-
) => Promise<boolean>;
|
|
253
|
+
finalizeMarkdownPreview: TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"];
|
|
254
|
+
preparePreviewDelivery?: (isDeliveryActive: () => boolean) => TelegramPreparedPreviewDelivery<TReplyMarkup>;
|
|
258
255
|
cwd?: string;
|
|
259
256
|
recordRuntimeEvent?: TelegramVoiceReplySenderDeps["recordRuntimeEvent"];
|
|
260
257
|
}
|
|
@@ -729,12 +726,13 @@ export function createTelegramOutboundTextPreviewRuntime<
|
|
|
729
726
|
TReplyMarkup = unknown,
|
|
730
727
|
>(
|
|
731
728
|
deps: TelegramOutboundTextPreviewRuntimeDeps<TReplyMarkup>,
|
|
732
|
-
):
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
729
|
+
): {
|
|
730
|
+
finalizeMarkdownPreview: TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"];
|
|
731
|
+
preparePreviewDelivery: (isDeliveryActive: () => boolean) => TelegramPreparedPreviewDelivery<TReplyMarkup> | undefined;
|
|
732
|
+
} {
|
|
733
|
+
const wrap = (
|
|
734
|
+
finalize: TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"],
|
|
735
|
+
): TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"] => async (
|
|
738
736
|
chatId,
|
|
739
737
|
markdown,
|
|
740
738
|
replyToMessageId,
|
|
@@ -747,7 +745,7 @@ export function createTelegramOutboundTextPreviewRuntime<
|
|
|
747
745
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
748
746
|
replyMarkup: options?.replyMarkup,
|
|
749
747
|
});
|
|
750
|
-
return
|
|
748
|
+
return finalize(
|
|
751
749
|
chatId,
|
|
752
750
|
transformed.text,
|
|
753
751
|
replyToMessageId,
|
|
@@ -758,6 +756,12 @@ export function createTelegramOutboundTextPreviewRuntime<
|
|
|
758
756
|
: {}),
|
|
759
757
|
},
|
|
760
758
|
);
|
|
759
|
+
};
|
|
760
|
+
return {
|
|
761
|
+
finalizeMarkdownPreview: wrap(deps.finalizeMarkdownPreview),
|
|
762
|
+
preparePreviewDelivery(isDeliveryActive) {
|
|
763
|
+
const prepared = deps.preparePreviewDelivery?.(isDeliveryActive);
|
|
764
|
+
return prepared ? { ...prepared, finalizeMarkdownPreview: wrap(prepared.finalizeMarkdownPreview) } : undefined;
|
|
761
765
|
},
|
|
762
766
|
};
|
|
763
767
|
}
|
|
@@ -1018,7 +1022,7 @@ export function createTelegramAssistantOutputSender<
|
|
|
1018
1022
|
};
|
|
1019
1023
|
await outboundRuntime.sendMarkdownReply(
|
|
1020
1024
|
target.chatId,
|
|
1021
|
-
undefined,
|
|
1025
|
+
event.source === "telegram" ? event.replyToMessageId : undefined,
|
|
1022
1026
|
buttonReply.markdown,
|
|
1023
1027
|
{
|
|
1024
1028
|
target,
|
|
@@ -87,6 +87,35 @@ export function getTelegramDiagnosticsDisplayPaths(profileName?: string): {
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** Durable Workspace admission ledger (<agentDir>/tmp/telegram/workspace-admission[.<profile>].json). */
|
|
91
|
+
export function resolveTelegramWorkspaceAdmissionPath(
|
|
92
|
+
agentDir = resolveAgentDir(),
|
|
93
|
+
profileName?: string,
|
|
94
|
+
): string {
|
|
95
|
+
return resolveTelegramProfileTempFilePath(
|
|
96
|
+
"workspace-admission",
|
|
97
|
+
"json",
|
|
98
|
+
agentDir,
|
|
99
|
+
profileName,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Durable inactive Thread cleanup work-set journal. */
|
|
104
|
+
export function resolveTelegramThreadCleanupWorkPath(
|
|
105
|
+
agentDir = resolveAgentDir(),
|
|
106
|
+
profileName?: string,
|
|
107
|
+
): string {
|
|
108
|
+
return resolveTelegramProfileTempFilePath("thread-cleanup", "json", agentDir, profileName);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Durable agent-authored channel post journal. */
|
|
112
|
+
export function resolveTelegramChannelPostJournalPath(
|
|
113
|
+
agentDir = resolveAgentDir(),
|
|
114
|
+
profileName?: string,
|
|
115
|
+
): string {
|
|
116
|
+
return resolveTelegramProfileTempFilePath("channel-posts", "json", agentDir, profileName);
|
|
117
|
+
}
|
|
118
|
+
|
|
90
119
|
/** Durable inbound update journal (<agentDir>/tmp/telegram/inbox[.<profile>].json). */
|
|
91
120
|
export function resolveTelegramUpdateJournalPath(
|
|
92
121
|
agentDir = resolveAgentDir(),
|
|
@@ -280,11 +280,12 @@ export type TelegramDurablePollingRuntimeAssemblyDeps<
|
|
|
280
280
|
"appendUpdateBatch" | "getJournalEntryCount" | "signalUpdateWorker"
|
|
281
281
|
> & {
|
|
282
282
|
canStart?: (ctx: TContext) => boolean;
|
|
283
|
+
prepareUpdateBatch?: (updates: readonly TUpdate[]) => void;
|
|
283
284
|
journal: {
|
|
284
285
|
appendBatch: (
|
|
285
286
|
updates: readonly TUpdate[],
|
|
286
287
|
acceptedThroughUpdateId?: number,
|
|
287
|
-
) =>
|
|
288
|
+
) => { nonExcludedUpdateIds: readonly number[] };
|
|
288
289
|
getAcceptedThroughUpdateId: () => number | undefined;
|
|
289
290
|
prepareCursorCutover?: () => MaybePromise<void>;
|
|
290
291
|
getEntryCount: () => number;
|
|
@@ -303,7 +304,30 @@ export function createTelegramDurablePollingRuntimeAssembly<
|
|
|
303
304
|
): TelegramDurablePollingRuntimeAssembly<TContext> {
|
|
304
305
|
const controller = createTelegramPollingControllerRuntime({
|
|
305
306
|
...deps,
|
|
306
|
-
appendUpdateBatch
|
|
307
|
+
appendUpdateBatch(updates, cursor) {
|
|
308
|
+
const result = deps.journal.appendBatch(updates, cursor);
|
|
309
|
+
if (!deps.prepareUpdateBatch) return result;
|
|
310
|
+
// No await: an already-draining worker must not observe the new batch before preparation.
|
|
311
|
+
try {
|
|
312
|
+
const included = new Set(result.nonExcludedUpdateIds);
|
|
313
|
+
let batch: TUpdate[] = [];
|
|
314
|
+
for (const update of updates) {
|
|
315
|
+
if (included.has(update.update_id)) batch.push(update);
|
|
316
|
+
else if (batch.length > 0) {
|
|
317
|
+
deps.prepareUpdateBatch(batch);
|
|
318
|
+
batch = [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (batch.length > 0) deps.prepareUpdateBatch(batch);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
try {
|
|
324
|
+
deps.recordRuntimeEvent?.("polling", error, { phase: "batch-preparation", updateCount: updates.length });
|
|
325
|
+
} catch {
|
|
326
|
+
// Already-published input must still reach the worker if diagnostics fail.
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
},
|
|
307
331
|
getAcceptedThroughUpdateId: deps.journal.getAcceptedThroughUpdateId,
|
|
308
332
|
getJournalEntryCount: deps.journal.getEntryCount,
|
|
309
333
|
signalUpdateWorker: deps.journal.signalWorker,
|
|
@@ -393,7 +417,6 @@ export function createTelegramPollingControllerRuntime<
|
|
|
393
417
|
getAcceptedThroughUpdateId: deps.getAcceptedThroughUpdateId,
|
|
394
418
|
getJournalEntryCount: deps.getJournalEntryCount,
|
|
395
419
|
signalUpdateWorker: deps.signalUpdateWorker,
|
|
396
|
-
prepareUpdateBatch: deps.prepareUpdateBatch,
|
|
397
420
|
updateStatus: deps.updateStatus,
|
|
398
421
|
sleep: deps.sleep,
|
|
399
422
|
onPhaseChange(phase, currentUpdateId) {
|
|
@@ -651,6 +674,7 @@ export interface TelegramThreadCapabilityRuntimeDeps<
|
|
|
651
674
|
getPollingStartedWithTelegramBus: () => boolean;
|
|
652
675
|
setPollingStartedWithTelegramBus: (started: boolean) => void;
|
|
653
676
|
setTopicModeUnavailable: (unavailable: boolean) => void;
|
|
677
|
+
suspendLiveThreadTarget?: () => void;
|
|
654
678
|
stopFollowerRegistration: () => void;
|
|
655
679
|
startClassicPolling: (ctx: TContext) => MaybePromise<void>;
|
|
656
680
|
stopClassicPolling: () => MaybePromise<void>;
|
|
@@ -682,6 +706,8 @@ export interface TelegramThreadCapabilityStateRuntime {
|
|
|
682
706
|
isBusRuntimeEnabled(): boolean;
|
|
683
707
|
shouldForceFreshLeaderThread(): boolean;
|
|
684
708
|
setForceFreshLeaderThread(forceFresh: boolean): void;
|
|
709
|
+
getRequestedThreadName(): string | undefined;
|
|
710
|
+
setRequestedThreadName(threadName: string | undefined): void;
|
|
685
711
|
}
|
|
686
712
|
|
|
687
713
|
export type TelegramThreadTargetObservationHandler<TContext> = (
|
|
@@ -715,6 +741,10 @@ export interface TelegramThreadAwarePollingPorts<TContext, TOwner> {
|
|
|
715
741
|
ctx: TContext,
|
|
716
742
|
owner: TOwner,
|
|
717
743
|
) => Promise<boolean | undefined>;
|
|
744
|
+
restoreFollowerWithOwner: (
|
|
745
|
+
ctx: TContext,
|
|
746
|
+
owner: TOwner,
|
|
747
|
+
) => Promise<boolean | undefined>;
|
|
718
748
|
stopFollowerRegistration: () => void;
|
|
719
749
|
}
|
|
720
750
|
|
|
@@ -738,6 +768,11 @@ export interface TelegramThreadAwarePollingDeps<
|
|
|
738
768
|
ctx: TContext,
|
|
739
769
|
owner: TOwner,
|
|
740
770
|
) => Promise<boolean | undefined>;
|
|
771
|
+
restoreFollowerWithLeader?: (
|
|
772
|
+
ctx: TContext,
|
|
773
|
+
owner: TOwner,
|
|
774
|
+
) => Promise<boolean | undefined>;
|
|
775
|
+
hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
|
|
741
776
|
stopFollowerRegistration: () => void;
|
|
742
777
|
}
|
|
743
778
|
|
|
@@ -760,6 +795,12 @@ export interface TelegramThreadCapabilityOrchestrationDeps<
|
|
|
760
795
|
ctx: TContext,
|
|
761
796
|
owner: TOwner,
|
|
762
797
|
) => Promise<boolean | undefined>;
|
|
798
|
+
restoreFollowerWithLeader?: (
|
|
799
|
+
ctx: TContext,
|
|
800
|
+
owner: TOwner,
|
|
801
|
+
) => Promise<boolean | undefined>;
|
|
802
|
+
hasRememberedWorkspaceBinding?: (ctx: TContext) => boolean;
|
|
803
|
+
suspendLiveThreadTarget?: () => void;
|
|
763
804
|
stopFollowerRegistration: () => void;
|
|
764
805
|
isTopicModeUnavailableError: (error: unknown) => boolean;
|
|
765
806
|
updateStatus: (ctx: TContext) => void;
|
|
@@ -780,6 +821,7 @@ export function createTelegramThreadCapabilityStateRuntime(): TelegramThreadCapa
|
|
|
780
821
|
let busPollingStarted = false;
|
|
781
822
|
let topicModeUnavailable = false;
|
|
782
823
|
let forceFreshLeaderThread = false;
|
|
824
|
+
let requestedThreadName: string | undefined;
|
|
783
825
|
return {
|
|
784
826
|
isBusPollingStarted: () => busPollingStarted,
|
|
785
827
|
setBusPollingStarted(started) {
|
|
@@ -794,6 +836,10 @@ export function createTelegramThreadCapabilityStateRuntime(): TelegramThreadCapa
|
|
|
794
836
|
setForceFreshLeaderThread(forceFresh) {
|
|
795
837
|
forceFreshLeaderThread = forceFresh;
|
|
796
838
|
},
|
|
839
|
+
getRequestedThreadName: () => requestedThreadName,
|
|
840
|
+
setRequestedThreadName(threadName) {
|
|
841
|
+
requestedThreadName = threadName;
|
|
842
|
+
},
|
|
797
843
|
};
|
|
798
844
|
}
|
|
799
845
|
|
|
@@ -818,6 +864,7 @@ export function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(
|
|
|
818
864
|
getPollingStartedWithTelegramBus: deps.state.isBusPollingStarted,
|
|
819
865
|
setPollingStartedWithTelegramBus: deps.state.setBusPollingStarted,
|
|
820
866
|
setTopicModeUnavailable: deps.state.setTopicModeUnavailable,
|
|
867
|
+
suspendLiveThreadTarget: deps.suspendLiveThreadTarget,
|
|
821
868
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
822
869
|
startClassicPolling: deps.startClassicPolling,
|
|
823
870
|
stopClassicPolling: deps.stopClassicPolling,
|
|
@@ -850,6 +897,8 @@ export function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(
|
|
|
850
897
|
startLeaderHealth: deps.startLeaderHealth,
|
|
851
898
|
stopLeaderHealth: deps.stopLeaderHealth,
|
|
852
899
|
registerFollowerWithLeader: deps.registerFollowerWithLeader,
|
|
900
|
+
restoreFollowerWithLeader: deps.restoreFollowerWithLeader,
|
|
901
|
+
hasRememberedWorkspaceBinding: deps.hasRememberedWorkspaceBinding,
|
|
853
902
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
854
903
|
recordEvent: deps.recordEvent,
|
|
855
904
|
setTopicModeUnavailable: deps.state.setTopicModeUnavailable,
|
|
@@ -958,14 +1007,16 @@ export async function applyTelegramThreadCapability<TContext>(
|
|
|
958
1007
|
if (!isCurrent()) return;
|
|
959
1008
|
deps.setTopicModeUnavailable(true);
|
|
960
1009
|
deps.stopFollowerRegistration();
|
|
1010
|
+
const hadLiveThreadTransport = deps.getPollingStartedWithTelegramBus();
|
|
961
1011
|
if (
|
|
962
|
-
|
|
1012
|
+
hadLiveThreadTransport ||
|
|
963
1013
|
hasTelegramClassicRestoreFailure(previousBotState)
|
|
964
1014
|
) {
|
|
965
1015
|
deps.stopLeaderHealth();
|
|
966
1016
|
await deps.stopBusPolling();
|
|
967
1017
|
if (!isCurrent()) return;
|
|
968
1018
|
deps.setPollingStartedWithTelegramBus(false);
|
|
1019
|
+
if (hadLiveThreadTransport) deps.suspendLiveThreadTarget?.();
|
|
969
1020
|
try {
|
|
970
1021
|
await deps.startClassicPolling(ctx);
|
|
971
1022
|
if (!isCurrent()) return;
|
|
@@ -1110,24 +1161,34 @@ export function createTelegramThreadAwarePollingPorts<TContext, TOwner>(
|
|
|
1110
1161
|
}
|
|
1111
1162
|
await deps.stopClassicPolling();
|
|
1112
1163
|
};
|
|
1113
|
-
const
|
|
1114
|
-
ctx: TContext,
|
|
1115
|
-
owner: TOwner,
|
|
1116
|
-
): Promise<boolean | undefined> => {
|
|
1164
|
+
const refreshFollowerState = async (): Promise<boolean> => {
|
|
1117
1165
|
if (deps.topicTargetStore.refresh) {
|
|
1118
1166
|
await deps.topicTargetStore.refresh();
|
|
1119
1167
|
} else {
|
|
1120
1168
|
await deps.topicTargetStore.load();
|
|
1121
1169
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1170
|
+
return deps.topicTargetStore.getBotState().threadMode === "enabled";
|
|
1171
|
+
};
|
|
1172
|
+
const registerFollowerWithOwner = async (
|
|
1173
|
+
ctx: TContext,
|
|
1174
|
+
owner: TOwner,
|
|
1175
|
+
): Promise<boolean | undefined> => {
|
|
1176
|
+
if (!(await refreshFollowerState())) return undefined;
|
|
1125
1177
|
return deps.registerFollowerWithLeader(ctx, owner);
|
|
1126
1178
|
};
|
|
1179
|
+
const restoreFollowerWithOwner = async (
|
|
1180
|
+
ctx: TContext,
|
|
1181
|
+
owner: TOwner,
|
|
1182
|
+
): Promise<boolean | undefined> => {
|
|
1183
|
+
if (!(await refreshFollowerState())) return undefined;
|
|
1184
|
+
if (!deps.hasRememberedWorkspaceBinding?.(ctx)) return undefined;
|
|
1185
|
+
return deps.restoreFollowerWithLeader?.(ctx, owner);
|
|
1186
|
+
};
|
|
1127
1187
|
return {
|
|
1128
1188
|
startPolling,
|
|
1129
1189
|
stopPolling,
|
|
1130
1190
|
registerFollowerWithOwner,
|
|
1191
|
+
restoreFollowerWithOwner,
|
|
1131
1192
|
stopFollowerRegistration: deps.stopFollowerRegistration,
|
|
1132
1193
|
};
|
|
1133
1194
|
}
|
|
@@ -1179,9 +1240,9 @@ export function createTelegramThreadCapabilityMonitor<TContext>(
|
|
|
1179
1240
|
let consecutiveDisabledProbes = 0;
|
|
1180
1241
|
const stop = (): void => {
|
|
1181
1242
|
generation += 1;
|
|
1182
|
-
deps.lifecycle?.invalidate();
|
|
1183
1243
|
if (interval) clearInterval(interval);
|
|
1184
1244
|
interval = undefined;
|
|
1245
|
+
deps.lifecycle?.invalidate();
|
|
1185
1246
|
};
|
|
1186
1247
|
const check = (ctx: TContext): void => {
|
|
1187
1248
|
if (transitionPromise || !canProbeTelegramThreadCapability(ctx, deps)) {
|
|
@@ -1276,8 +1337,19 @@ export function createTelegramThreadCapabilityMonitor<TContext>(
|
|
|
1276
1337
|
return {
|
|
1277
1338
|
start(ctx) {
|
|
1278
1339
|
stop();
|
|
1340
|
+
const expectedGeneration = generation;
|
|
1279
1341
|
interval = setInterval(() => {
|
|
1280
|
-
|
|
1342
|
+
if (generation !== expectedGeneration) return;
|
|
1343
|
+
try {
|
|
1344
|
+
check(ctx);
|
|
1345
|
+
} catch (error) {
|
|
1346
|
+
try { stop(); } catch { /* Timer shutdown must not escape the callback. */ }
|
|
1347
|
+
try {
|
|
1348
|
+
deps.recordEvent("bus", error, { phase: "capability-monitor" });
|
|
1349
|
+
} catch {
|
|
1350
|
+
// Monitor diagnostics cannot create an uncaught interval exception.
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1281
1353
|
}, intervalMs);
|
|
1282
1354
|
interval.unref?.();
|
|
1283
1355
|
},
|
|
@@ -1416,7 +1488,6 @@ export interface TelegramPollLoopDeps<
|
|
|
1416
1488
|
getAcceptedThroughUpdateId?: () => number | undefined;
|
|
1417
1489
|
getJournalEntryCount: () => number;
|
|
1418
1490
|
signalUpdateWorker: () => void;
|
|
1419
|
-
prepareUpdateBatch?: (updates: readonly TUpdate[]) => void;
|
|
1420
1491
|
onErrorStatus: (message: string) => void;
|
|
1421
1492
|
onStatusReset: () => void;
|
|
1422
1493
|
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
@@ -1446,7 +1517,6 @@ export interface TelegramPollLoopRunnerDeps<
|
|
|
1446
1517
|
getAcceptedThroughUpdateId?: () => number | undefined;
|
|
1447
1518
|
getJournalEntryCount: () => number;
|
|
1448
1519
|
signalUpdateWorker: () => void;
|
|
1449
|
-
prepareUpdateBatch?: (updates: readonly TUpdate[]) => void;
|
|
1450
1520
|
updateStatus: (ctx: TContext, message?: string) => void;
|
|
1451
1521
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
1452
1522
|
onPhaseChange?: (
|
|
@@ -1501,7 +1571,6 @@ export function createTelegramPollLoopRunner<
|
|
|
1501
1571
|
getAcceptedThroughUpdateId: deps.getAcceptedThroughUpdateId,
|
|
1502
1572
|
getJournalEntryCount: deps.getJournalEntryCount,
|
|
1503
1573
|
signalUpdateWorker: deps.signalUpdateWorker,
|
|
1504
|
-
prepareUpdateBatch: deps.prepareUpdateBatch,
|
|
1505
1574
|
onErrorStatus: (message) => {
|
|
1506
1575
|
updateTelegramPollingStatusSafely(deps.updateStatus, ctx, {
|
|
1507
1576
|
message,
|
|
@@ -1702,7 +1771,6 @@ export async function runTelegramPollLoop<
|
|
|
1702
1771
|
reportTelegramPollingPhase(deps, "long-poll");
|
|
1703
1772
|
const updates = await requestTelegramUpdatesWithinBudget(deps, request);
|
|
1704
1773
|
reportTelegramPollingResponse(deps, updates.length);
|
|
1705
|
-
deps.prepareUpdateBatch?.(updates);
|
|
1706
1774
|
consecutiveGetUpdatesConflicts = 0;
|
|
1707
1775
|
currentUpdateId = updates[0]?.update_id;
|
|
1708
1776
|
await admitTelegramPollingUpdateBatch({
|