@llblab/pi-telegram 0.55.1 → 0.56.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 +5 -4
- package/BACKLOG.md +6 -1
- package/CHANGELOG.md +15 -0
- package/README.md +28 -17
- package/dist/lib/bindings.d.ts +8 -4
- package/dist/lib/bindings.js +41 -1
- package/dist/lib/bus-follower.d.ts +1 -0
- package/dist/lib/bus-follower.js +10 -5
- package/dist/lib/bus-leader.d.ts +2 -0
- package/dist/lib/bus-leader.js +34 -20
- package/dist/lib/bus.d.ts +8 -0
- package/dist/lib/bus.js +21 -7
- package/dist/lib/commands.d.ts +1 -1
- package/dist/lib/commands.js +33 -28
- package/dist/lib/extension.js +18 -3
- package/dist/lib/lifecycle.d.ts +2 -1
- package/dist/lib/lifecycle.js +28 -20
- package/dist/lib/locks.d.ts +6 -2
- package/dist/lib/locks.js +3 -3
- package/dist/lib/menu-queue.d.ts +1 -0
- package/dist/lib/menu-queue.js +102 -99
- package/dist/lib/pi.d.ts +3 -2
- package/dist/lib/pi.js +3 -0
- package/dist/lib/polling.d.ts +6 -2
- package/dist/lib/polling.js +5 -3
- package/dist/lib/queue.d.ts +57 -5
- package/dist/lib/queue.js +313 -25
- package/dist/lib/routing.d.ts +4 -0
- package/dist/lib/routing.js +62 -1
- package/dist/lib/status.d.ts +1 -2
- package/dist/lib/status.js +6 -7
- package/dist/lib/sync.js +11 -1
- package/dist/lib/threads.d.ts +11 -0
- package/dist/lib/threads.js +57 -6
- package/dist/lib/updates.d.ts +12 -7
- package/dist/lib/updates.js +44 -9
- package/dist/package.json +1 -1
- package/docs/README.md +1 -1
- package/docs/architecture.md +24 -13
- package/docs/callback-namespaces.md +4 -0
- package/docs/multi-instance-bus.md +6 -4
- package/docs/ui-style.md +14 -10
- package/lib/bindings.ts +52 -3
- package/lib/bus-follower.ts +13 -5
- package/lib/bus-leader.ts +37 -23
- package/lib/bus.ts +23 -7
- package/lib/commands.ts +52 -47
- package/lib/extension.ts +18 -2
- package/lib/lifecycle.ts +34 -28
- package/lib/locks.ts +5 -2
- package/lib/menu-queue.ts +116 -162
- package/lib/pi.ts +8 -0
- package/lib/polling.ts +7 -2
- package/lib/queue.ts +328 -33
- package/lib/routing.ts +51 -1
- package/lib/status.ts +7 -7
- package/lib/sync.ts +11 -0
- package/lib/threads.ts +57 -5
- package/lib/updates.ts +44 -15
- package/package.json +1 -1
package/dist/lib/bus.js
CHANGED
|
@@ -50,6 +50,9 @@ export function createTelegramBusAuthSecret() {
|
|
|
50
50
|
const TELEGRAM_BUS_PROTOCOL_VERSION = 3;
|
|
51
51
|
export const TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION = "durable-follower-admission-v1";
|
|
52
52
|
export const TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF = "queue-handoff-v1";
|
|
53
|
+
/** Unspent Steer lane codec; independent from baseline receipt handoff support. */
|
|
54
|
+
export const TELEGRAM_BUS_CAPABILITY_QUEUE_STEER = "queue-steer-v1";
|
|
55
|
+
export const TELEGRAM_BUS_CAPABILITY_CONNECT_RECOVERY = "explicit-connect-recovery-v1";
|
|
53
56
|
export const TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE = "input-custody-reference-v1";
|
|
54
57
|
export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME = "workspace-thread-rename-v1";
|
|
55
58
|
export const TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE = "thread-display-mode-v1";
|
|
@@ -77,6 +80,13 @@ export const TELEGRAM_BUS_HELD_COMMAND_CAPABILITIES = [
|
|
|
77
80
|
TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
|
|
78
81
|
TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
|
|
79
82
|
];
|
|
83
|
+
/** Start/help are restricted menu aliases, negotiated separately from the original held-command set. */
|
|
84
|
+
export const TELEGRAM_BUS_CAPABILITY_HELD_MENU_ALIASES = "held-menu-aliases-v1";
|
|
85
|
+
export function getTelegramBusHeldCommandCapabilities(name) {
|
|
86
|
+
return name === "start" || name === "help"
|
|
87
|
+
? [...TELEGRAM_BUS_HELD_COMMAND_CAPABILITIES, TELEGRAM_BUS_CAPABILITY_HELD_MENU_ALIASES]
|
|
88
|
+
: TELEGRAM_BUS_HELD_COMMAND_CAPABILITIES;
|
|
89
|
+
}
|
|
80
90
|
function parseSelectedCommandInput(value) {
|
|
81
91
|
if (!isRecord(value) ||
|
|
82
92
|
!hasOnlyWireKeys(value, ["name", "target"]) ||
|
|
@@ -173,6 +183,10 @@ export function createTelegramCurrentBusProtocolIdentity(capabilities = []) {
|
|
|
173
183
|
export function hasTelegramBusCapability(identity, capability) {
|
|
174
184
|
return identity?.capabilities.includes(capability) ?? false;
|
|
175
185
|
}
|
|
186
|
+
export function hasTelegramBusSteerQueueHandoffCapability(identity) {
|
|
187
|
+
return hasTelegramBusCapability(identity, TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF) &&
|
|
188
|
+
hasTelegramBusCapability(identity, TELEGRAM_BUS_CAPABILITY_QUEUE_STEER);
|
|
189
|
+
}
|
|
176
190
|
export function getTelegramInputCustodyPeerReadiness(followers) {
|
|
177
191
|
return followers.map((follower) => {
|
|
178
192
|
if (!follower.registrationGeneration || !follower.protocol)
|
|
@@ -1310,12 +1324,7 @@ export function createTelegramBusLiveRebindController(deps) {
|
|
|
1310
1324
|
: []),
|
|
1311
1325
|
...(settling || held ? [TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_SETTLE] : []),
|
|
1312
1326
|
...(observingHeld ? [TELEGRAM_BUS_CAPABILITY_PRE_DISPATCH_REBIND_EXECUTION] : []),
|
|
1313
|
-
...(held
|
|
1314
|
-
? [
|
|
1315
|
-
TELEGRAM_BUS_CAPABILITY_LIVE_REBIND_COMMAND_SET,
|
|
1316
|
-
TELEGRAM_BUS_CAPABILITY_SELECTED_MENU_DELIVERY,
|
|
1317
|
-
]
|
|
1318
|
-
: []),
|
|
1327
|
+
...(held ? getTelegramBusHeldCommandCapabilities(held.name) : []),
|
|
1319
1328
|
];
|
|
1320
1329
|
const follower = deps.getFollower(expected.instanceId), secret = deps.getAuthSecret();
|
|
1321
1330
|
if (!isCurrent() ||
|
|
@@ -2138,8 +2147,13 @@ async function handleTelegramBusSocketLine(line, socket, handleEnvelope, recordT
|
|
|
2138
2147
|
}
|
|
2139
2148
|
}
|
|
2140
2149
|
function parseRegisterEnvelope(value, requestId, kind) {
|
|
2150
|
+
if (value.recoverConnection !== undefined &&
|
|
2151
|
+
(kind !== "follower.register" || value.recoverConnection !== true))
|
|
2152
|
+
return undefined;
|
|
2141
2153
|
const registration = parseRegistration(value.registration);
|
|
2142
|
-
return registration ? { kind, requestId, registration
|
|
2154
|
+
return registration ? { kind, requestId, registration,
|
|
2155
|
+
...(kind === "follower.register" && value.recoverConnection === true ? { recoverConnection: true } : {}),
|
|
2156
|
+
} : undefined;
|
|
2143
2157
|
}
|
|
2144
2158
|
function parseFollowerPresenceEnvelope(value, requestId, kind) {
|
|
2145
2159
|
return typeof value.instanceId === "string" &&
|
package/dist/lib/commands.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { type TelegramBridgeStatusLineOptions } from "./status.ts";
|
|
|
13
13
|
import { type TelegramApiCallOptions } from "./telegram-api.ts";
|
|
14
14
|
import type { TelegramSessionReplacementIntent } from "./threads.ts";
|
|
15
15
|
import { type TelegramDeferredSourceEvidence, type TelegramLiveSourceCompletionReadiness, type TelegramQueueAdmissionReceiptLike } from "./updates.ts";
|
|
16
|
-
export type TelegramHeldCommandName = "status" | "abort" | "stop" | "next" | "continue";
|
|
16
|
+
export type TelegramHeldCommandName = "status" | "start" | "help" | "abort" | "stop" | "next" | "continue";
|
|
17
17
|
/** Commands-owned no-fold admission over the ordinary recipient queue; source reporting stays separate from removal. */
|
|
18
18
|
export interface TelegramHeldTurnAdmission {
|
|
19
19
|
assertCurrent(): void;
|
package/dist/lib/commands.js
CHANGED
|
@@ -1446,35 +1446,40 @@ export function createTelegramCommandHandler(deps) {
|
|
|
1446
1446
|
},
|
|
1447
1447
|
};
|
|
1448
1448
|
};
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
},
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1449
|
+
// Held followers are already paired and bound. Start/help open the same restricted menu as status;
|
|
1450
|
+
// they cannot pair an operator, bootstrap another Thread or invoke unrestricted Bot API effects.
|
|
1451
|
+
const heldMenuPlan = {
|
|
1452
|
+
completion: "before-effect",
|
|
1453
|
+
effect: "showStatus",
|
|
1454
|
+
prepare(ctx, effects) {
|
|
1455
|
+
const scopedStatus = effects?.showStatus;
|
|
1456
|
+
const captured = {
|
|
1457
|
+
...deps,
|
|
1458
|
+
showStatus: scopedStatus
|
|
1459
|
+
? (message, context, options) => {
|
|
1460
|
+
const address = getTelegramCommandMessageTarget(message);
|
|
1461
|
+
return scopedStatus.call(effects, address.chatId, address.replyToMessageId, context, address.threadId, options);
|
|
1462
|
+
}
|
|
1463
|
+
: deps.showStatus,
|
|
1464
|
+
};
|
|
1465
|
+
return {
|
|
1466
|
+
captured,
|
|
1467
|
+
portsCurrent: () => !effects || effects.showStatus === scopedStatus,
|
|
1468
|
+
async run({ message, command, assertRecipient }) {
|
|
1469
|
+
const recipientOptions = { assertAuthority: assertRecipient };
|
|
1470
|
+
return handleTelegramCommandRuntime("status", message, ctx, {
|
|
1471
|
+
...captured,
|
|
1472
|
+
assertExecutionCurrent: assertRecipient,
|
|
1473
|
+
showStatus: (value, context) => captured.showStatus(value, context, recipientOptions),
|
|
1474
|
+
}, command.args);
|
|
1475
|
+
},
|
|
1476
|
+
};
|
|
1477
1477
|
},
|
|
1478
|
+
};
|
|
1479
|
+
const heldPlans = {
|
|
1480
|
+
status: heldMenuPlan,
|
|
1481
|
+
start: heldMenuPlan,
|
|
1482
|
+
help: heldMenuPlan,
|
|
1478
1483
|
abort: {
|
|
1479
1484
|
completion: "after-semantics",
|
|
1480
1485
|
effect: "sendTextReply",
|
package/dist/lib/extension.js
CHANGED
|
@@ -59,6 +59,9 @@ import * as WorkspaceRetirement from "./workspace-retirement.js";
|
|
|
59
59
|
const telegramBusProtocolIdentity = Bus.createTelegramCurrentBusProtocolIdentity([
|
|
60
60
|
Bus.TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION,
|
|
61
61
|
Bus.TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF,
|
|
62
|
+
Bus.TELEGRAM_BUS_CAPABILITY_QUEUE_STEER,
|
|
63
|
+
Bus.TELEGRAM_BUS_CAPABILITY_CONNECT_RECOVERY,
|
|
64
|
+
Bus.TELEGRAM_BUS_CAPABILITY_HELD_MENU_ALIASES,
|
|
62
65
|
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT,
|
|
63
66
|
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME,
|
|
64
67
|
Bus.TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE,
|
|
@@ -557,7 +560,6 @@ export default function (pi) {
|
|
|
557
560
|
},
|
|
558
561
|
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
559
562
|
getQueuedItemCount: Queue.countExecutableTelegramQueueItems,
|
|
560
|
-
formatQueuedStatus: Queue.formatQueuedTelegramItemsStatus,
|
|
561
563
|
getRecentRuntimeEvents: runtimeEvents.getEvents,
|
|
562
564
|
getRuntimeLockState: lockRuntime.getStatusLabel,
|
|
563
565
|
...threadStatusProjectionRuntime,
|
|
@@ -796,7 +798,7 @@ export default function (pi) {
|
|
|
796
798
|
editMessageText: editTelegramMessageText,
|
|
797
799
|
},
|
|
798
800
|
});
|
|
799
|
-
const { mutation: queueMutationRuntime, dispatchNext: dispatchNextQueuedTelegramTurn, requestNextDispatchAnnouncement, cancelNextDispatchAnnouncement, watchdog: queueDispatchWatchdogRuntime, } = Bindings.createTelegramQueueBindingRuntime({
|
|
801
|
+
const { mutation: queueMutationRuntime, dispatchNext: dispatchNextQueuedTelegramTurn, requestNextDispatchAnnouncement, cancelNextDispatchAnnouncement, watchdog: queueDispatchWatchdogRuntime, onTurnEnd: onSteerTurnEnd, } = Bindings.createTelegramQueueBindingRuntime({
|
|
800
802
|
store: telegramQueueStore,
|
|
801
803
|
queue,
|
|
802
804
|
lifecycle,
|
|
@@ -807,6 +809,8 @@ export default function (pi) {
|
|
|
807
809
|
promptDispatch: promptDispatchRuntime,
|
|
808
810
|
isIdle,
|
|
809
811
|
hasPendingMessages,
|
|
812
|
+
isContextCurrent: telegramSessionContextStore.isCurrent,
|
|
813
|
+
isContextAborted: Pi.isExtensionContextAbortRequested,
|
|
810
814
|
updateStatus,
|
|
811
815
|
sendTextReply,
|
|
812
816
|
sendUserMessage,
|
|
@@ -881,6 +885,7 @@ export default function (pi) {
|
|
|
881
885
|
});
|
|
882
886
|
// --- Queue And Settings Menus ---
|
|
883
887
|
const queueMenuRuntime = MenuQueue.createTelegramQueueMenuRuntime({
|
|
888
|
+
hasPendingDispatch: lifecycle.hasDispatchPending,
|
|
884
889
|
telegramQueueStore,
|
|
885
890
|
queueMutationRuntime,
|
|
886
891
|
sendInteractiveMessage,
|
|
@@ -973,7 +978,7 @@ export default function (pi) {
|
|
|
973
978
|
isSelectedCommandAvailable(follower, name) {
|
|
974
979
|
return (Bus.hasTelegramBusSharedCapabilities(telegramBusProtocolIdentity, follower.protocol, [
|
|
975
980
|
...Bus.TELEGRAM_BUS_LIVE_REBIND_CAPABILITIES,
|
|
976
|
-
...Bus.
|
|
981
|
+
...Bus.getTelegramBusHeldCommandCapabilities(name),
|
|
977
982
|
]) &&
|
|
978
983
|
typeof selectedMenuDelivery === "function" &&
|
|
979
984
|
typeof inboundRouteRuntime.prepareHeldCommand === "function" &&
|
|
@@ -1222,6 +1227,7 @@ export default function (pi) {
|
|
|
1222
1227
|
threadStore,
|
|
1223
1228
|
runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
|
|
1224
1229
|
liveRebindCleanupSchedule: Routing.TELEGRAM_LIVE_REBIND_CLEANUP_SCHEDULE,
|
|
1230
|
+
liveRebindCommandCompletionTimeoutMs: 8_000,
|
|
1225
1231
|
updateStatus,
|
|
1226
1232
|
isContextActive: telegramSessionContextStore.isCurrent,
|
|
1227
1233
|
dispatchNextQueuedTelegramTurn,
|
|
@@ -1858,6 +1864,14 @@ export default function (pi) {
|
|
|
1858
1864
|
recordRuntimeEvent,
|
|
1859
1865
|
});
|
|
1860
1866
|
queueHandoffReconciliationBinding.set(Updates.createTelegramQueueHandoffReconciliationRuntimeAssembly({
|
|
1867
|
+
hasPendingDispatch: lifecycle.hasDispatchPending,
|
|
1868
|
+
canHandoffSteerWithRecipient(recipient) {
|
|
1869
|
+
const live = telegramBusFollowerRegistry.get(recipient.instanceId);
|
|
1870
|
+
return live?.registrationGeneration === recipient.registrationGeneration &&
|
|
1871
|
+
[telegramBusProtocolIdentity, live?.protocol,
|
|
1872
|
+
...(telegramBusFollowerRegistrationState.isRegistered() ? [telegramBusFollowerRegistrationState.getLeaderProtocol()] : [])]
|
|
1873
|
+
.every(Bus.hasTelegramBusSteerQueueHandoffCapability);
|
|
1874
|
+
},
|
|
1861
1875
|
ownsDirect: lockRuntime.owns,
|
|
1862
1876
|
isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
|
|
1863
1877
|
isBusEnabled: isTelegramBusRuntimeEnabled,
|
|
@@ -2461,6 +2475,7 @@ export default function (pi) {
|
|
|
2461
2475
|
sendMarkdownReply,
|
|
2462
2476
|
sendTextReply,
|
|
2463
2477
|
dispatchNextQueuedTelegramTurn,
|
|
2478
|
+
onSteerTurnEnd,
|
|
2464
2479
|
onPromptHandedOff(turn, ctx) {
|
|
2465
2480
|
updateAdmissionRuntimeBinding
|
|
2466
2481
|
.getSettlement()
|
package/dist/lib/lifecycle.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Transport authority, durable bindings, and queue custody remain with their owners.
|
|
6
6
|
*/
|
|
7
7
|
import * as BusFollower from "./bus-follower.ts";
|
|
8
|
-
import type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AssistantMessageEvent, BeforeAgentStartEvent, ExtensionAPI, ExtensionContext, InputEvent, MessageEndEvent, SessionBeforeCompactEvent, SessionCompactEvent, SessionCompactFailedEvent, SessionShutdownEvent, SessionStartEvent, ToolExecutionEndEvent, ToolExecutionStartEvent, ToolExecutionUpdateEvent, UIPromptEndEvent, UIPromptStartEvent } from "./pi.ts";
|
|
8
|
+
import type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AssistantMessageEvent, BeforeAgentStartEvent, ExtensionAPI, ExtensionContext, InputEvent, MessageEndEvent, SessionBeforeCompactEvent, SessionCompactEvent, SessionCompactFailedEvent, SessionShutdownEvent, SessionStartEvent, ToolExecutionEndEvent, ToolExecutionStartEvent, ToolExecutionUpdateEvent, TurnEndEvent, TurnEndEventResult, UIPromptEndEvent, UIPromptStartEvent } from "./pi.ts";
|
|
9
9
|
import * as Machine from "./machine.ts";
|
|
10
10
|
import * as Queue from "./queue.ts";
|
|
11
11
|
export declare function createAgentStartDedupHook(inner: (event: AgentStartEvent, ctx: ExtensionContext) => Promise<void>, resetReplyDedup: () => void, schedulePublication?: (task: () => Promise<void>) => void): (event: AgentStartEvent, ctx: ExtensionContext) => Promise<void>;
|
|
@@ -30,6 +30,7 @@ export interface TelegramLifecycleRegistrationDeps {
|
|
|
30
30
|
onModelSelect: (event: {
|
|
31
31
|
model: TelegramLifecycleModel;
|
|
32
32
|
}, ctx: ExtensionContext) => Promise<void> | void;
|
|
33
|
+
onTurnEnd?: (event: TurnEndEvent, ctx: ExtensionContext) => TurnEndEventResult | undefined;
|
|
33
34
|
onAgentStart: (event: AgentStartEvent, ctx: ExtensionContext) => Promise<void>;
|
|
34
35
|
onToolExecutionStart: (event: ToolExecutionStartEvent, ctx: ExtensionContext) => Promise<void> | void;
|
|
35
36
|
onToolExecutionUpdate?: (event: ToolExecutionUpdateEvent, ctx: ExtensionContext) => Promise<void> | void;
|
package/dist/lib/lifecycle.js
CHANGED
|
@@ -265,36 +265,47 @@ export function createTelegramBridgeSessionLifecycleDeps(ports) {
|
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
267
|
export function createTelegramBridgeSessionLifecycleAssembly(deps) {
|
|
268
|
-
const isSessionActive = deps.contextStore.isCurrent;
|
|
269
268
|
const suspendForReplacement = BusFollower.createTelegramBusFollowerSessionReplacementSuspender({
|
|
270
269
|
registrationState: deps.follower.registrationState,
|
|
271
270
|
instanceId: deps.follower.instanceId,
|
|
272
271
|
suspendPolling: deps.follower.suspendPolling,
|
|
273
272
|
recordRuntimeEvent: deps.follower.recordRuntimeEvent,
|
|
274
273
|
});
|
|
275
|
-
|
|
276
|
-
const queueLifecycle = Queue.createTelegramSessionLifecycleRuntime({
|
|
274
|
+
// A reused context can be current for a successor; inner awaits need this invocation's generation too.
|
|
275
|
+
const queueLifecycle = (ctx, generation, preserveTarget = true) => Queue.createTelegramSessionLifecycleRuntime({
|
|
277
276
|
...deps.queue,
|
|
278
|
-
isSessionActive,
|
|
277
|
+
isSessionActive: () => deps.contextStore.isCurrent(ctx, generation),
|
|
279
278
|
stopPolling: () => suspendForReplacement(preserveTarget),
|
|
280
279
|
clearPendingMediaGroups: deps.services.suspendGroupedInput,
|
|
281
280
|
});
|
|
282
281
|
const servicesLifecycle = {
|
|
283
282
|
async onSessionStart(event, ctx) {
|
|
283
|
+
const generation = deps.contextStore.getGeneration();
|
|
284
|
+
const isCurrent = () => deps.contextStore.isCurrent(ctx, generation);
|
|
284
285
|
const resumeConnection = deps.services.connection?.prepare(event, ctx);
|
|
285
|
-
await queueLifecycle.onSessionStart(event, ctx);
|
|
286
|
-
if (!
|
|
286
|
+
await queueLifecycle(ctx, generation).onSessionStart(event, ctx);
|
|
287
|
+
if (!isCurrent())
|
|
287
288
|
return;
|
|
288
289
|
deps.services.resumeGroupedInput(ctx);
|
|
289
290
|
await deps.services.delivery.onSessionStart();
|
|
290
|
-
if (!
|
|
291
|
+
if (!isCurrent())
|
|
291
292
|
return;
|
|
292
293
|
if (resumeConnection)
|
|
293
294
|
resumeConnection();
|
|
294
295
|
else
|
|
295
296
|
await deps.services.polling.onSessionStart(event, ctx);
|
|
297
|
+
if (!isCurrent())
|
|
298
|
+
return;
|
|
296
299
|
deps.services.capabilityMonitor.start(ctx);
|
|
297
300
|
deps.services.queueWatchdog.start(ctx);
|
|
301
|
+
await BusFollower.createTelegramBusFollowerSessionRefreshHook({
|
|
302
|
+
registrationState: deps.follower.registrationState,
|
|
303
|
+
registrationRuntime: deps.follower.registrationRuntime,
|
|
304
|
+
getLeaderState: deps.follower.getLeaderState,
|
|
305
|
+
isSessionActive: isCurrent,
|
|
306
|
+
updateStatus: deps.follower.updateStatus,
|
|
307
|
+
recordRuntimeEvent: deps.follower.recordRuntimeEvent,
|
|
308
|
+
})(event, ctx);
|
|
298
309
|
},
|
|
299
310
|
async onSessionShutdown(event, ctx) {
|
|
300
311
|
const generation = deps.contextStore.getGeneration();
|
|
@@ -302,7 +313,7 @@ export function createTelegramBridgeSessionLifecycleAssembly(deps) {
|
|
|
302
313
|
if (!isCurrent())
|
|
303
314
|
return;
|
|
304
315
|
deps.services.connection?.onSessionShutdown(event, ctx);
|
|
305
|
-
preserveTarget = event.reason !== "resume";
|
|
316
|
+
const preserveTarget = event.reason !== "resume";
|
|
306
317
|
let preserveThread;
|
|
307
318
|
if (event.reason === "quit") {
|
|
308
319
|
try {
|
|
@@ -321,7 +332,7 @@ export function createTelegramBridgeSessionLifecycleAssembly(deps) {
|
|
|
321
332
|
return;
|
|
322
333
|
deps.services.queueWatchdog.stop();
|
|
323
334
|
deps.services.capabilityMonitor.stop();
|
|
324
|
-
await queueLifecycle.onSessionShutdown(event, ctx);
|
|
335
|
+
await queueLifecycle(ctx, generation, preserveTarget).onSessionShutdown(event, ctx);
|
|
325
336
|
if (!isCurrent())
|
|
326
337
|
return;
|
|
327
338
|
await deps.services.inboundWorker.onSessionShutdown();
|
|
@@ -337,17 +348,7 @@ export function createTelegramBridgeSessionLifecycleAssembly(deps) {
|
|
|
337
348
|
}
|
|
338
349
|
},
|
|
339
350
|
};
|
|
340
|
-
|
|
341
|
-
onSessionStart: BusFollower.createTelegramBusFollowerSessionRefreshHook({
|
|
342
|
-
registrationState: deps.follower.registrationState,
|
|
343
|
-
registrationRuntime: deps.follower.registrationRuntime,
|
|
344
|
-
getLeaderState: deps.follower.getLeaderState,
|
|
345
|
-
isSessionActive,
|
|
346
|
-
updateStatus: deps.follower.updateStatus,
|
|
347
|
-
recordRuntimeEvent: deps.follower.recordRuntimeEvent,
|
|
348
|
-
}),
|
|
349
|
-
}, isSessionActive);
|
|
350
|
-
return createTelegramSessionGenerationFence(deps.contextStore, followerLifecycle);
|
|
351
|
+
return createTelegramSessionGenerationFence(deps.contextStore, servicesLifecycle);
|
|
351
352
|
}
|
|
352
353
|
function unrefTelegramLifecycleTimer(timer) {
|
|
353
354
|
if (!timer || typeof timer !== "object")
|
|
@@ -605,6 +606,13 @@ export function registerTelegramLifecycleHooks(pi, deps) {
|
|
|
605
606
|
return;
|
|
606
607
|
await deps.onAgentStart(event, ctx);
|
|
607
608
|
});
|
|
609
|
+
if (deps.onTurnEnd) {
|
|
610
|
+
pi.on("turn_end", (event, ctx) => {
|
|
611
|
+
if (!isActive(ctx))
|
|
612
|
+
return;
|
|
613
|
+
return deps.onTurnEnd?.(event, ctx);
|
|
614
|
+
});
|
|
615
|
+
}
|
|
608
616
|
pi.on("tool_execution_start", async (event, ctx) => {
|
|
609
617
|
if (!isActive(ctx))
|
|
610
618
|
return;
|
package/dist/lib/locks.d.ts
CHANGED
|
@@ -259,7 +259,9 @@ export interface TelegramLockedPollingRuntime<TContext extends TelegramLockConte
|
|
|
259
259
|
captureTransportAuthority: (ctx: TContext) => (() => boolean) | undefined;
|
|
260
260
|
onPersistentConflict: (ctx: TContext, count: number) => Promise<void>;
|
|
261
261
|
onSessionStart: (_event: unknown, ctx: TContext) => Promise<void>;
|
|
262
|
-
registerFollowerWithOwner?: (ctx: TContext, owner: TelegramLockEntry
|
|
262
|
+
registerFollowerWithOwner?: (ctx: TContext, owner: TelegramLockEntry, options?: {
|
|
263
|
+
recoverConnection?: boolean;
|
|
264
|
+
}) => boolean | undefined | Promise<boolean | undefined>;
|
|
263
265
|
restoreFollowerWithOwner?: (ctx: TContext, owner: TelegramLockEntry) => boolean | undefined | Promise<boolean | undefined>;
|
|
264
266
|
stopFollowerRegistration?: () => void;
|
|
265
267
|
}
|
|
@@ -274,7 +276,9 @@ interface TelegramLockedPollingRuntimeDeps<TContext extends TelegramLockContext>
|
|
|
274
276
|
formatStartBlockedMessage?: (ctx: TContext) => string;
|
|
275
277
|
startPolling: (ctx: TContext, options?: TelegramLockedPollingStartOptions) => void | Promise<void>;
|
|
276
278
|
stopPolling: () => Promise<void>;
|
|
277
|
-
registerFollowerWithOwner?: (ctx: TContext, owner: TelegramLockEntry
|
|
279
|
+
registerFollowerWithOwner?: (ctx: TContext, owner: TelegramLockEntry, options?: {
|
|
280
|
+
recoverConnection?: boolean;
|
|
281
|
+
}) => boolean | undefined | Promise<boolean | undefined>;
|
|
278
282
|
restoreFollowerWithOwner?: (ctx: TContext, owner: TelegramLockEntry) => boolean | undefined | Promise<boolean | undefined>;
|
|
279
283
|
stopFollowerRegistration?: () => void;
|
|
280
284
|
/** Threaded Mode takeover evidence after failed follower registration; replaces the retired file heartbeat. */
|
package/dist/lib/locks.js
CHANGED
|
@@ -1625,7 +1625,7 @@ export function createTelegramLockedPollingRuntime(deps) {
|
|
|
1625
1625
|
if (deps.registerFollowerWithOwner) {
|
|
1626
1626
|
let failureMessage;
|
|
1627
1627
|
try {
|
|
1628
|
-
const registered = await deps.registerFollowerWithOwner(ctx, acquired.lock);
|
|
1628
|
+
const registered = await deps.registerFollowerWithOwner(ctx, acquired.lock, options.forceFreshLeaderThread ? { recoverConnection: true } : undefined);
|
|
1629
1629
|
if (!isCurrent())
|
|
1630
1630
|
return cancelled;
|
|
1631
1631
|
if (registered) {
|
|
@@ -1838,8 +1838,8 @@ export function createTelegramLockedPollingRuntime(deps) {
|
|
|
1838
1838
|
sessionAutoStartRun = run;
|
|
1839
1839
|
},
|
|
1840
1840
|
registerFollowerWithOwner: deps.registerFollowerWithOwner
|
|
1841
|
-
? async (ctx, owner) => {
|
|
1842
|
-
const registered = await deps.registerFollowerWithOwner?.(ctx, owner);
|
|
1841
|
+
? async (ctx, owner, options) => {
|
|
1842
|
+
const registered = await deps.registerFollowerWithOwner?.(ctx, owner, options);
|
|
1843
1843
|
if (registered)
|
|
1844
1844
|
deps.updateStatus(ctx);
|
|
1845
1845
|
return registered === true;
|
package/dist/lib/menu-queue.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ interface TelegramQueueMenuRuntime<Context> {
|
|
|
24
24
|
handleCallbackQuery: (query: TelegramQueueMenuCallbackQuery, ctx: Context) => Promise<boolean>;
|
|
25
25
|
}
|
|
26
26
|
export declare function createTelegramQueueMenuRuntime<Context, TModel extends MenuModel = MenuModel>(deps: {
|
|
27
|
+
hasPendingDispatch?: () => boolean;
|
|
27
28
|
telegramQueueStore: Queue.TelegramQueueStateStore<Context>;
|
|
28
29
|
queueMutationRuntime: Queue.TelegramQueueMutationController<Context>;
|
|
29
30
|
sendInteractiveMessage: TelegramMenuMessageRuntimeDeps["sendInteractiveMessage"];
|