@llblab/pi-telegram 0.18.2 → 0.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +27 -1
- package/CHANGELOG.md +10 -0
- package/index.ts +22 -1
- package/lib/bus-leader.ts +10 -2
- package/lib/bus.ts +8 -0
- package/lib/menu-model.ts +10 -17
- package/lib/routing.ts +40 -69
- package/package.json +1 -1
package/BACKLOG.md
CHANGED
|
@@ -2,9 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
_Current deterministic status: Threaded Mode implementation, native typing/activity status, regression coverage, docs/context reconciliation, typecheck, full tests, pack check, audit, Domain DAG validation, context validation, and core live Threaded Mode smoke are green. This backlog intentionally tracks only release-relevant remaining work: native Windows smoke, evidence-gated Telegram client follow-ups, and upstream Pi API blockers._
|
|
4
4
|
|
|
5
|
+
## P0 — Live Threaded Mode Regression Sweep
|
|
6
|
+
|
|
7
|
+
Context: live Linux testing exposed regressions around prompt dispatch readiness, visible thread rename noise during automatic leader reclaim, status thread-name fallback flicker, and follower voice/update forwarding health. These are local runtime correctness issues and must be validated before returning to Windows smoke.
|
|
8
|
+
|
|
9
|
+
Open work:
|
|
10
|
+
|
|
11
|
+
- [x] Hide minimal model-menu one-page pagination and keep scope tabs progressive.
|
|
12
|
+
- [x] Add immediate-plus-deferred inbound prompt dispatch so queued prompts do not wait for a later `/reload` or command.
|
|
13
|
+
- [x] Stop automatic leader reclaim/reconciliation paths from visibly calling `editForumTopic` for internal identity restoration.
|
|
14
|
+
- [x] Prevent leader auto-claim of an unknown unbound thread while another live thread target exists, covering same-directory leader/follower smear risk.
|
|
15
|
+
- [x] Prefer local live leader/follower target labels over stale shared thread-store records when building prompt prefixes.
|
|
16
|
+
- [x] Permit follower-safe bot identity reads and own-chat native activity through the leader API proxy without granting cross-thread message/file/topic writes.
|
|
17
|
+
- [x] Preserve leader thread-name fallback in live status state to reduce `Dune`/generic `Telegram` flicker.
|
|
18
|
+
- [ ] Live smoke on Linux with one leader and one follower:
|
|
19
|
+
- [x] clean-state pass after removing `tmp/telegram` so stale diagnostic snapshots do not obscure live behavior;
|
|
20
|
+
- [ ] dirty-state pass with old `state.json`/`logs.jsonl` present to prove live locks, bus registration, target ownership, and reconciliation override stale diagnostics;
|
|
21
|
+
- [x] follower thread receives raw voice/message updates through the leader bus;
|
|
22
|
+
- [x] follower-local handlers from that instance's `telegram.json` process voice independently;
|
|
23
|
+
- [x] same-directory leader/follower sessions keep distinct thread bindings;
|
|
24
|
+
- [ ] leader reload recovers without duplicate visible thread renames;
|
|
25
|
+
- [ ] prompts dispatch without a second command;
|
|
26
|
+
- [ ] status remains stable around thread name and role while active turns start/end.
|
|
27
|
+
- [ ] Add/keep proactive race protections so `state.json` never becomes authoritative over live lock ownership, bus registration, or current target identity.
|
|
28
|
+
|
|
29
|
+
Done when: local Linux live Threaded Mode smoke is stable for leader reload, follower connect, prompt dispatch, voice forwarding, and status naming without visible rename noise.
|
|
30
|
+
|
|
5
31
|
## P0 — Native Windows Threaded Mode Support
|
|
6
32
|
|
|
7
|
-
Context: Threaded Mode uses a local leader/follower IPC bus. Unix-like platforms use Node `net` over Unix sockets; native Windows uses Node `net` named-pipe paths. The product expectation is identical behavior across both transports: leader/follower registration, heartbeats, forwarded Telegram API calls, thread target preservation, lifecycle cleanup, and shutdown semantics should not depend on socket-vs-pipe transport.
|
|
33
|
+
Context: Threaded Mode uses a local leader/follower IPC bus. Unix-like platforms use Node `net` over Unix sockets; native Windows uses Node `net` named-pipe paths. The product expectation is identical behavior across both transports: leader/follower registration, heartbeats, forwarded Telegram API calls, thread target preservation, lifecycle cleanup, and shutdown semantics should not depend on socket-vs-pipe transport. This remains after the local Linux regression sweep is green.
|
|
8
34
|
|
|
9
35
|
Open work:
|
|
10
36
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.18.3: Threaded Mode live hotfix
|
|
6
|
+
|
|
7
|
+
- `[Threaded Mode]` Inbound Telegram prompts now request an immediate dispatch and a session-bound deferred retry. Impact: hosts where Pi is not yet dispatch-ready at update handling time no longer need a later `/reload` or command to process the queued prompt.
|
|
8
|
+
- `[Thread Lifecycle]` Automatic leader reclaim/reconciliation paths no longer call `editForumTopic` just to restore internal thread identity, and an unknown unbound thread is no longer auto-claimed by the leader while another live thread target exists. Prompt thread labels now prefer the local live leader/follower target over stale shared thread-store records. Impact: ordinary prompts and voice messages should not produce duplicate Telegram service messages such as `renamed the thread to ...`, and a same-directory follower thread is less likely to be smeared onto the leader binding or mislabeled in prompts.
|
|
9
|
+
- `[Threaded Mode]` Follower Bot API authorization now permits safe bot identity reads and chat-level typing/activity within the follower's assigned chat, while preserving thread-scoped write restrictions for messages/files/topic mutation. Impact: forwarded follower updates no longer fail just because native activity/capability paths use the aggregate chat surface.
|
|
10
|
+
- `[Status]` Leader target assignment now carries the live thread name into status fallback state. Impact: the status bar is less likely to flicker from `Dune Leader/Active` to generic `Telegram Leader` when the current active turn or thread-store lookup changes.
|
|
11
|
+
- `[Model Menu]` The Telegram model menu now hides one-page pagination controls and keeps scope tabs hidden unless scoped models exist. Impact: the minimal model menu shows only main-menu navigation and the available models.
|
|
12
|
+
|
|
3
13
|
## 0.18.2: setup pairing start hotfix
|
|
4
14
|
|
|
5
15
|
- `[Setup]` `/telegram-setup` now updates the live in-memory config immediately after persisting the validated bot token and before starting polling. Impact: first-time setup no longer shows `Send /start...` followed by `Telegram bot is not configured`, and `/start` can be received without restarting Pi.
|
package/index.ts
CHANGED
|
@@ -73,6 +73,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
73
73
|
BusFollower.createTelegramBusFollowerRegistrationState();
|
|
74
74
|
let telegramBusLeaderTarget: Config.TelegramProactivePushTarget | undefined;
|
|
75
75
|
let telegramBusLeaderSlot: string | undefined;
|
|
76
|
+
let telegramBusLeaderThreadName: string | undefined;
|
|
76
77
|
let telegramTopicModeUnavailable = false;
|
|
77
78
|
let telegramTopicProvisioningCount = 0;
|
|
78
79
|
const messageOwnershipStore = Ownership.createTelegramMessageOwnershipStore();
|
|
@@ -279,7 +280,8 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
279
280
|
if (threadStore.getBotState().threadMode === "disabled") return undefined;
|
|
280
281
|
return (
|
|
281
282
|
getCurrentThreadRecord()?.threadName ??
|
|
282
|
-
telegramBusFollowerRegistrationState.getThreadName()
|
|
283
|
+
telegramBusFollowerRegistrationState.getThreadName() ??
|
|
284
|
+
telegramBusLeaderThreadName
|
|
283
285
|
);
|
|
284
286
|
},
|
|
285
287
|
});
|
|
@@ -647,6 +649,22 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
647
649
|
followers: telegramBusFollowerRegistry.list(),
|
|
648
650
|
});
|
|
649
651
|
},
|
|
652
|
+
getLocalThreadLabelForTarget(target) {
|
|
653
|
+
const followerTarget = telegramBusFollowerRegistrationState.getTarget();
|
|
654
|
+
if (
|
|
655
|
+
followerTarget?.chatId === target.chatId &&
|
|
656
|
+
followerTarget.threadId === target.threadId
|
|
657
|
+
) {
|
|
658
|
+
return telegramBusFollowerRegistrationState.getThreadName();
|
|
659
|
+
}
|
|
660
|
+
if (
|
|
661
|
+
telegramBusLeaderTarget?.chatId === target.chatId &&
|
|
662
|
+
telegramBusLeaderTarget.threadId === target.threadId
|
|
663
|
+
) {
|
|
664
|
+
return telegramBusLeaderThreadName;
|
|
665
|
+
}
|
|
666
|
+
return undefined;
|
|
667
|
+
},
|
|
650
668
|
getCurrentLeaderEpoch,
|
|
651
669
|
getThreadReconciliationMachineState() {
|
|
652
670
|
return threadReconciliationRuntime.getState();
|
|
@@ -681,6 +699,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
681
699
|
dispatchNextQueuedTelegramTurn,
|
|
682
700
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
683
701
|
deferredQueueDispatchRuntime.request,
|
|
702
|
+
hasDeferredDispatchContext: deferredQueueDispatchRuntime.isBound,
|
|
684
703
|
startTypingLoop: promptDispatchRuntime.startTypingLoop,
|
|
685
704
|
stopTypingLoop: typing.stop,
|
|
686
705
|
answerCallbackQuery,
|
|
@@ -880,6 +899,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
880
899
|
setLeaderTarget(input) {
|
|
881
900
|
telegramBusLeaderTarget = input.target;
|
|
882
901
|
telegramBusLeaderSlot = input.slot;
|
|
902
|
+
telegramBusLeaderThreadName = input.threadName;
|
|
883
903
|
},
|
|
884
904
|
onProvisioningStart() {
|
|
885
905
|
telegramTopicProvisioningCount += 1;
|
|
@@ -1099,6 +1119,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1099
1119
|
clearLeaderTarget() {
|
|
1100
1120
|
telegramBusLeaderTarget = undefined;
|
|
1101
1121
|
telegramBusLeaderSlot = undefined;
|
|
1122
|
+
telegramBusLeaderThreadName = undefined;
|
|
1102
1123
|
},
|
|
1103
1124
|
getSyncState() {
|
|
1104
1125
|
return telegramSyncState;
|
package/lib/bus-leader.ts
CHANGED
|
@@ -68,7 +68,11 @@ export interface TelegramBusLeaderTargetProvisionerDeps<TContext> {
|
|
|
68
68
|
) => void;
|
|
69
69
|
getSyncState: () => Sync.TelegramSyncState;
|
|
70
70
|
setSyncState: (state: Sync.TelegramSyncState) => void;
|
|
71
|
-
setLeaderTarget: (input: {
|
|
71
|
+
setLeaderTarget: (input: {
|
|
72
|
+
target: TelegramTarget;
|
|
73
|
+
slot?: string;
|
|
74
|
+
threadName?: string;
|
|
75
|
+
}) => void;
|
|
72
76
|
onProvisioningStart?: () => void;
|
|
73
77
|
onProvisioningEnd?: () => void;
|
|
74
78
|
recordRuntimeEvent: (
|
|
@@ -476,7 +480,11 @@ export function createTelegramBusLeaderTargetProvisioner<TContext>(
|
|
|
476
480
|
deps.onProvisioningEnd?.();
|
|
477
481
|
}
|
|
478
482
|
if (!ownTarget) return;
|
|
479
|
-
deps.setLeaderTarget({
|
|
483
|
+
deps.setLeaderTarget({
|
|
484
|
+
target: ownTarget.target,
|
|
485
|
+
slot: ownTarget.slot,
|
|
486
|
+
threadName: ownTarget.threadName,
|
|
487
|
+
});
|
|
480
488
|
const nowMs = getNowMs();
|
|
481
489
|
let syncState = deps.getSyncState();
|
|
482
490
|
syncState = Sync.markTelegramSyncSliceFresh(syncState, "target-bindings", {
|
package/lib/bus.ts
CHANGED
|
@@ -127,6 +127,12 @@ export function isTelegramFollowerApiCallAllowed(input: {
|
|
|
127
127
|
if (target.threadId === undefined) return true;
|
|
128
128
|
return matchesId(record.message_thread_id, target.threadId);
|
|
129
129
|
};
|
|
130
|
+
const isTargetChatScoped = (body: unknown): boolean => {
|
|
131
|
+
if (!target) return false;
|
|
132
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return false;
|
|
133
|
+
const record = body as Record<string, unknown>;
|
|
134
|
+
return matchesId(record.chat_id, target.chatId);
|
|
135
|
+
};
|
|
130
136
|
if (input.method === "downloadFile") return true;
|
|
131
137
|
if (input.method === "call") {
|
|
132
138
|
const apiMethod = input.args[0];
|
|
@@ -137,6 +143,8 @@ export function isTelegramFollowerApiCallAllowed(input: {
|
|
|
137
143
|
) {
|
|
138
144
|
return true;
|
|
139
145
|
}
|
|
146
|
+
if (apiMethod === "getMe") return true;
|
|
147
|
+
if (apiMethod === "sendChatAction") return isTargetChatScoped(input.args[1]);
|
|
140
148
|
return allowedCallMethods.has(apiMethod) && isTargetScoped(input.args[1]);
|
|
141
149
|
}
|
|
142
150
|
if (input.method === "callMultipart") {
|
package/lib/menu-model.ts
CHANGED
|
@@ -1023,23 +1023,16 @@ export function buildModelMenuReplyMarkup(
|
|
|
1023
1023
|
menuPage.page === 0 ? menuPage.pageCount - 1 : menuPage.page - 1;
|
|
1024
1024
|
const nextPage =
|
|
1025
1025
|
menuPage.page === menuPage.pageCount - 1 ? 0 : menuPage.page + 1;
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
: [
|
|
1037
|
-
{
|
|
1038
|
-
text: `${menuPage.page + 1}/${menuPage.pageCount}`,
|
|
1039
|
-
callback_data: "model:pages",
|
|
1040
|
-
},
|
|
1041
|
-
],
|
|
1042
|
-
);
|
|
1026
|
+
if (menuPage.pageCount > 1) {
|
|
1027
|
+
rows.push([
|
|
1028
|
+
{ text: "⬅️", callback_data: `model:page:${previousPage}` },
|
|
1029
|
+
{
|
|
1030
|
+
text: `${menuPage.page + 1}/${menuPage.pageCount}`,
|
|
1031
|
+
callback_data: "model:pages",
|
|
1032
|
+
},
|
|
1033
|
+
{ text: "➡️", callback_data: `model:page:${nextPage}` },
|
|
1034
|
+
]);
|
|
1035
|
+
}
|
|
1043
1036
|
rows.push(
|
|
1044
1037
|
...menuPage.items.map((entry, index) => [
|
|
1045
1038
|
{
|
package/lib/routing.ts
CHANGED
|
@@ -415,6 +415,9 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
415
415
|
getMessageOwnership?: Updates.TelegramMessageOwnershipLookup;
|
|
416
416
|
getTargetOwnership?: Updates.TelegramTargetOwnershipLookup;
|
|
417
417
|
getLiveThreadTargets?: () => Queue.TelegramQueueTarget[];
|
|
418
|
+
getLocalThreadLabelForTarget?: (
|
|
419
|
+
target: Queue.TelegramQueueTarget,
|
|
420
|
+
) => string | undefined;
|
|
418
421
|
getCurrentLeaderEpoch?: () => number | string | undefined;
|
|
419
422
|
getThreadReconciliationMachineState?: () =>
|
|
420
423
|
| ThreadReconciler.ThreadReconciliationMachineState
|
|
@@ -484,6 +487,7 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
484
487
|
requestDeferredDispatchNextQueuedTelegramTurn?: (
|
|
485
488
|
dispatch: (ctx: TContext) => void,
|
|
486
489
|
) => void;
|
|
490
|
+
hasDeferredDispatchContext?: () => boolean;
|
|
487
491
|
startTypingLoop?: (
|
|
488
492
|
ctx: TContext,
|
|
489
493
|
chatId?: number,
|
|
@@ -593,6 +597,17 @@ export function createTelegramInboundRouteRuntime<
|
|
|
593
597
|
const guidedUnboundTopicKeys = new Set<string>();
|
|
594
598
|
let nextUnboundRerouteId = 0;
|
|
595
599
|
let nextAllTabCommandId = 0;
|
|
600
|
+
const requestDispatchNextQueuedTelegramTurn = (ctx: TContext): void => {
|
|
601
|
+
deps.dispatchNextQueuedTelegramTurn(ctx);
|
|
602
|
+
if (
|
|
603
|
+
deps.requestDeferredDispatchNextQueuedTelegramTurn &&
|
|
604
|
+
deps.hasDeferredDispatchContext?.() !== false
|
|
605
|
+
) {
|
|
606
|
+
deps.requestDeferredDispatchNextQueuedTelegramTurn(
|
|
607
|
+
deps.dispatchNextQueuedTelegramTurn,
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
596
611
|
const prunePendingUnboundReroutes = () => {
|
|
597
612
|
const nowMs = Date.now();
|
|
598
613
|
for (const [id, entry] of pendingUnboundReroutes) {
|
|
@@ -712,7 +727,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
712
727
|
};
|
|
713
728
|
deps.queueMutationRuntime.append(turn, ctx);
|
|
714
729
|
deps.updateStatus(ctx);
|
|
715
|
-
|
|
730
|
+
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
716
731
|
},
|
|
717
732
|
});
|
|
718
733
|
const cloneTelegramMessagesForThread = (
|
|
@@ -1257,7 +1272,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1257
1272
|
context,
|
|
1258
1273
|
);
|
|
1259
1274
|
deps.updateStatus(context);
|
|
1260
|
-
|
|
1275
|
+
requestDispatchNextQueuedTelegramTurn(context);
|
|
1261
1276
|
},
|
|
1262
1277
|
},
|
|
1263
1278
|
);
|
|
@@ -1350,10 +1365,18 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1350
1365
|
const chatId = message.chat.id;
|
|
1351
1366
|
const threadId = message.message_thread_id;
|
|
1352
1367
|
if (!threadId) return undefined;
|
|
1368
|
+
const localLabel = deps.getLocalThreadLabelForTarget?.({
|
|
1369
|
+
chatId,
|
|
1370
|
+
threadId,
|
|
1371
|
+
});
|
|
1372
|
+
if (localLabel) return localLabel;
|
|
1353
1373
|
const records = deps.threadStore.list();
|
|
1374
|
+
const currentInstanceId = deps.getCurrentInstanceId?.();
|
|
1354
1375
|
for (const r of records) {
|
|
1355
1376
|
if (r.target.chatId !== chatId || r.target.threadId !== threadId)
|
|
1356
1377
|
continue;
|
|
1378
|
+
if (currentInstanceId && r.instanceId && r.instanceId !== currentInstanceId)
|
|
1379
|
+
continue;
|
|
1357
1380
|
return r.threadName &&
|
|
1358
1381
|
Threads.isTelegramTopicThreadNameValidForSlot(r.threadName, r.slot)
|
|
1359
1382
|
? r.threadName
|
|
@@ -1380,7 +1403,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1380
1403
|
statusSummary: "continue",
|
|
1381
1404
|
};
|
|
1382
1405
|
deps.queueMutationRuntime.append(continueTurn, ctx);
|
|
1383
|
-
|
|
1406
|
+
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
1384
1407
|
};
|
|
1385
1408
|
const reservedCommandNames = () =>
|
|
1386
1409
|
new Set(Commands.getTelegramReservedCommandNames());
|
|
@@ -1454,7 +1477,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1454
1477
|
: { ...turn, replyToMessageId: 0 };
|
|
1455
1478
|
},
|
|
1456
1479
|
updateStatus: deps.updateStatus,
|
|
1457
|
-
dispatchNextQueuedTelegramTurn:
|
|
1480
|
+
dispatchNextQueuedTelegramTurn: requestDispatchNextQueuedTelegramTurn,
|
|
1458
1481
|
}).enqueue;
|
|
1459
1482
|
const sendUnboundRerouteChooserNow = async (
|
|
1460
1483
|
messages: TMessage[],
|
|
@@ -1831,7 +1854,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1831
1854
|
Queue.appendTelegramQueueItem(items, guestTurn),
|
|
1832
1855
|
);
|
|
1833
1856
|
deps.updateStatus(ctx);
|
|
1834
|
-
|
|
1857
|
+
requestDispatchNextQueuedTelegramTurn(ctx);
|
|
1835
1858
|
};
|
|
1836
1859
|
return Updates.createTelegramPairedUpdateRuntime<TContext, TUpdate>({
|
|
1837
1860
|
getAllowedUserId: deps.configStore.getAllowedUserId,
|
|
@@ -1951,7 +1974,13 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1951
1974
|
]).trim();
|
|
1952
1975
|
const instanceId = deps.getCurrentInstanceId?.();
|
|
1953
1976
|
const leaderProfileKey = getLeaderTopicProfileKey(ctx, instanceId);
|
|
1954
|
-
const
|
|
1977
|
+
const records = deps.threadStore.list();
|
|
1978
|
+
const routableRecords = getTelegramRoutableThreadRecords(
|
|
1979
|
+
records,
|
|
1980
|
+
deps.getLiveThreadTargets?.(),
|
|
1981
|
+
);
|
|
1982
|
+
const hasAnyRoutableThread = routableRecords.length > 0;
|
|
1983
|
+
const existing = records.find((r) => {
|
|
1955
1984
|
return (
|
|
1956
1985
|
r.target.chatId === target.chatId &&
|
|
1957
1986
|
r.target.threadId === target.threadId
|
|
@@ -2003,7 +2032,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2003
2032
|
deps.threadStore.list(),
|
|
2004
2033
|
leaderProfileKey,
|
|
2005
2034
|
instanceId,
|
|
2006
|
-
)
|
|
2035
|
+
) &&
|
|
2036
|
+
!hasAnyRoutableThread
|
|
2007
2037
|
) {
|
|
2008
2038
|
const priorLeaderRecord =
|
|
2009
2039
|
deps.threadStore.getByProfileKey(leaderProfileKey);
|
|
@@ -2039,25 +2069,6 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2039
2069
|
slot,
|
|
2040
2070
|
});
|
|
2041
2071
|
await deps.threadStore.persist();
|
|
2042
|
-
if (deps.callApi) {
|
|
2043
|
-
try {
|
|
2044
|
-
await deps.callApi("editForumTopic", {
|
|
2045
|
-
chat_id: target.chatId,
|
|
2046
|
-
message_thread_id: target.threadId,
|
|
2047
|
-
name: Threads.getTelegramTopicTitleForThreadName(
|
|
2048
|
-
threadName,
|
|
2049
|
-
slot,
|
|
2050
|
-
),
|
|
2051
|
-
});
|
|
2052
|
-
} catch (renameError) {
|
|
2053
|
-
deps.recordRuntimeEvent?.("telegram", renameError, {
|
|
2054
|
-
phase: "leader-topic-reclaim-rename",
|
|
2055
|
-
chatId: target.chatId,
|
|
2056
|
-
threadId: target.threadId,
|
|
2057
|
-
slot,
|
|
2058
|
-
});
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
2072
|
deps.recordRuntimeEvent?.(
|
|
2062
2073
|
"bus",
|
|
2063
2074
|
"Bus leader reclaimed unbound thread",
|
|
@@ -2106,13 +2117,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2106
2117
|
);
|
|
2107
2118
|
return;
|
|
2108
2119
|
}
|
|
2109
|
-
const records = deps.threadStore.list();
|
|
2110
2120
|
const command = getKnownTelegramAllTabCommand(text);
|
|
2111
|
-
if (
|
|
2112
|
-
command &&
|
|
2113
|
-
getTelegramRoutableThreadRecords(records, deps.getLiveThreadTargets?.())
|
|
2114
|
-
.length > 0
|
|
2115
|
-
) {
|
|
2121
|
+
if (command && hasAnyRoutableThread) {
|
|
2116
2122
|
if (
|
|
2117
2123
|
await sendAllTabCommandChooser(command, text, message as TMessage, {
|
|
2118
2124
|
target: { chatId: target.chatId, threadId: target.threadId },
|
|
@@ -2172,23 +2178,6 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2172
2178
|
slot,
|
|
2173
2179
|
});
|
|
2174
2180
|
await deps.threadStore.persist();
|
|
2175
|
-
try {
|
|
2176
|
-
await deps.callApi("editForumTopic", {
|
|
2177
|
-
chat_id: target.chatId,
|
|
2178
|
-
message_thread_id: target.threadId,
|
|
2179
|
-
name: Threads.getTelegramTopicTitleForThreadName(
|
|
2180
|
-
threadName,
|
|
2181
|
-
slot,
|
|
2182
|
-
),
|
|
2183
|
-
});
|
|
2184
|
-
} catch (renameError) {
|
|
2185
|
-
deps.recordRuntimeEvent?.("telegram", renameError, {
|
|
2186
|
-
phase: "leader-topic-unbound-reclaim-rename",
|
|
2187
|
-
chatId: target.chatId,
|
|
2188
|
-
threadId: target.threadId,
|
|
2189
|
-
slot,
|
|
2190
|
-
});
|
|
2191
|
-
}
|
|
2192
2181
|
deps.recordRuntimeEvent?.(
|
|
2193
2182
|
"bus",
|
|
2194
2183
|
"Bus leader reclaimed stale-current unbound thread",
|
|
@@ -2208,7 +2197,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2208
2197
|
}
|
|
2209
2198
|
if (
|
|
2210
2199
|
leaderProfileKey &&
|
|
2211
|
-
!hasActiveLeaderTopic(records, leaderProfileKey, instanceId)
|
|
2200
|
+
!hasActiveLeaderTopic(records, leaderProfileKey, instanceId) &&
|
|
2201
|
+
!hasAnyRoutableThread
|
|
2212
2202
|
) {
|
|
2213
2203
|
const priorLeaderRecord =
|
|
2214
2204
|
deps.threadStore.getByProfileKey(leaderProfileKey);
|
|
@@ -2254,25 +2244,6 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2254
2244
|
slot,
|
|
2255
2245
|
});
|
|
2256
2246
|
await deps.threadStore.persist();
|
|
2257
|
-
if (deps.callApi) {
|
|
2258
|
-
try {
|
|
2259
|
-
await deps.callApi("editForumTopic", {
|
|
2260
|
-
chat_id: target.chatId,
|
|
2261
|
-
message_thread_id: target.threadId,
|
|
2262
|
-
name: Threads.getTelegramTopicTitleForThreadName(
|
|
2263
|
-
threadName,
|
|
2264
|
-
slot,
|
|
2265
|
-
),
|
|
2266
|
-
});
|
|
2267
|
-
} catch (renameError) {
|
|
2268
|
-
deps.recordRuntimeEvent?.("telegram", renameError, {
|
|
2269
|
-
phase: "leader-topic-reclaim-rename",
|
|
2270
|
-
chatId: target.chatId,
|
|
2271
|
-
threadId: target.threadId,
|
|
2272
|
-
slot,
|
|
2273
|
-
});
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
2247
|
deps.recordRuntimeEvent?.(
|
|
2277
2248
|
"bus",
|
|
2278
2249
|
"Bus leader reclaimed unbound thread",
|