@llblab/pi-telegram 0.18.1 → 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 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,19 @@
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
+
13
+ ## 0.18.2: setup pairing start hotfix
14
+
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.
16
+
3
17
  ## 0.18.1: Windows setup transport hotfix
4
18
 
5
19
  - `[Setup]` `/telegram-setup` token validation now uses the same fallback-aware Telegram transport as normal API calls. Impact: Windows/QEMU hosts that fail native `fetch` during bot-token validation can retry through IPv4 fallback instead of failing before config is saved.
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: { target: TelegramTarget; slot?: string }) => void;
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({ target: ownTarget.target, slot: ownTarget.slot });
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
- rows.push(
1027
- menuPage.pageCount > 1
1028
- ? [
1029
- { text: "⬅️", callback_data: `model:page:${previousPage}` },
1030
- {
1031
- text: `${menuPage.page + 1}/${menuPage.pageCount}`,
1032
- callback_data: "model:pages",
1033
- },
1034
- { text: "➡️", callback_data: `model:page:${nextPage}` },
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
- deps.dispatchNextQueuedTelegramTurn(ctx);
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
- deps.dispatchNextQueuedTelegramTurn(context);
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
- deps.dispatchNextQueuedTelegramTurn(ctx);
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: deps.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
- deps.dispatchNextQueuedTelegramTurn(ctx);
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 existing = deps.threadStore.list().find((r) => {
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",
package/lib/setup.ts CHANGED
@@ -172,19 +172,21 @@ export function createTelegramSetupPromptRuntime<
172
172
  return async (ctx: TContext): Promise<void> => {
173
173
  if (!ctx.hasUI || !deps.setupGuard.start()) return;
174
174
  try {
175
- const nextConfig = await runTelegramSetup({
175
+ await runTelegramSetup({
176
176
  hasUI: ctx.hasUI,
177
177
  env: deps.env ?? process.env,
178
178
  config: deps.getConfig(),
179
179
  promptInput: (label, value) => ctx.ui.input(label, value),
180
180
  promptEditor: (label, value) => ctx.ui.editor(label, value),
181
181
  getMe: deps.getMe,
182
- persistConfig: deps.persistConfig,
182
+ persistConfig: async (config) => {
183
+ await deps.persistConfig(config);
184
+ deps.setConfig(config);
185
+ },
183
186
  notify: (message, level) => ctx.ui.notify(message, level),
184
187
  startPolling: () => deps.startPolling(ctx),
185
188
  updateStatus: () => deps.updateStatus(ctx),
186
189
  });
187
- if (nextConfig) deps.setConfig(nextConfig);
188
190
  } catch (error) {
189
191
  deps.recordRuntimeEvent?.("setup", error);
190
192
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"