@llblab/pi-telegram 0.51.5 → 0.52.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/AGENTS.md +10 -8
- package/BACKLOG.md +1 -8
- package/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +16 -6
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +5 -7
- package/dist/lib/bindings.d.ts +9 -2
- package/dist/lib/bindings.js +22 -13
- package/dist/lib/bus-api.js +4 -2
- package/dist/lib/bus-follower.d.ts +55 -57
- package/dist/lib/bus-follower.js +213 -140
- package/dist/lib/bus-leader.d.ts +21 -2
- package/dist/lib/bus-leader.js +181 -25
- package/dist/lib/bus-transport.d.ts +4 -1
- package/dist/lib/bus-transport.js +28 -4
- package/dist/lib/bus.d.ts +52 -56
- package/dist/lib/bus.js +78 -246
- package/dist/lib/channel-posts.d.ts +1 -3
- package/dist/lib/channel-posts.js +23 -26
- package/dist/lib/command-templates.d.ts +0 -5
- package/dist/lib/command-templates.js +5 -5
- package/dist/lib/commands.d.ts +12 -26
- package/dist/lib/commands.js +152 -98
- package/dist/lib/config.d.ts +0 -17
- package/dist/lib/config.js +12 -14
- package/dist/lib/delivery.d.ts +0 -4
- package/dist/lib/delivery.js +2 -5
- package/dist/lib/extension.js +281 -61
- package/dist/lib/generative-apps.d.ts +0 -19
- package/dist/lib/inbound.d.ts +0 -2
- package/dist/lib/inbound.js +2 -2
- package/dist/lib/journal.d.ts +177 -16
- package/dist/lib/journal.js +1115 -249
- package/dist/lib/keyboard.d.ts +0 -2
- package/dist/lib/keyboard.js +2 -2
- package/dist/lib/lifecycle.d.ts +57 -2
- package/dist/lib/lifecycle.js +110 -5
- package/dist/lib/locks.d.ts +100 -12
- package/dist/lib/locks.js +380 -14
- package/dist/lib/logging.d.ts +30 -12
- package/dist/lib/logging.js +129 -72
- package/dist/lib/media.d.ts +28 -5
- package/dist/lib/media.js +26 -8
- package/dist/lib/menu-model.d.ts +0 -11
- package/dist/lib/menu-model.js +8 -8
- package/dist/lib/menu-settings.d.ts +0 -18
- package/dist/lib/menu-settings.js +18 -18
- package/dist/lib/menu-status.d.ts +4 -4
- package/dist/lib/menu-status.js +12 -7
- package/dist/lib/menu-thinking.d.ts +1 -1
- package/dist/lib/menu-thinking.js +1 -1
- package/dist/lib/menu.d.ts +1 -0
- package/dist/lib/menu.js +4 -3
- package/dist/lib/model.js +2 -4
- package/dist/lib/outbound-attachments.d.ts +0 -1
- package/dist/lib/outbound-attachments.js +1 -1
- package/dist/lib/outbound-buttons.js +1 -8
- package/dist/lib/outbound-markup.d.ts +1 -6
- package/dist/lib/outbound-markup.js +4 -21
- package/dist/lib/outbound.d.ts +0 -8
- package/dist/lib/outbound.js +5 -5
- package/dist/lib/paths.d.ts +50 -7
- package/dist/lib/paths.js +155 -17
- package/dist/lib/pi.d.ts +1 -0
- package/dist/lib/pi.js +2 -1
- package/dist/lib/polling.d.ts +0 -5
- package/dist/lib/polling.js +5 -5
- package/dist/lib/preview.d.ts +0 -3
- package/dist/lib/preview.js +3 -3
- package/dist/lib/prompt-templates.d.ts +0 -1
- package/dist/lib/prompt-templates.js +1 -1
- package/dist/lib/queue.d.ts +3 -15
- package/dist/lib/queue.js +69 -8
- package/dist/lib/recovery.d.ts +29 -9
- package/dist/lib/recovery.js +104 -33
- package/dist/lib/replies.d.ts +0 -4
- package/dist/lib/replies.js +1 -1
- package/dist/lib/routing.d.ts +73 -8
- package/dist/lib/routing.js +2206 -307
- package/dist/lib/sections.d.ts +0 -2
- package/dist/lib/sections.js +1 -1
- package/dist/lib/status.d.ts +46 -9
- package/dist/lib/status.js +179 -27
- package/dist/lib/sync.d.ts +0 -5
- package/dist/lib/sync.js +9 -8
- package/dist/lib/telegram-api.d.ts +11 -3
- package/dist/lib/telegram-api.js +34 -23
- package/dist/lib/thread-cleanup-manager.d.ts +1 -0
- package/dist/lib/thread-cleanup-manager.js +18 -21
- package/dist/lib/thread-naming.d.ts +32 -5
- package/dist/lib/thread-naming.js +191 -7
- package/dist/lib/thread-reconciler.d.ts +2 -0
- package/dist/lib/thread-reconciler.js +1 -1
- package/dist/lib/threads.d.ts +292 -59
- package/dist/lib/threads.js +1664 -373
- package/dist/lib/turns.d.ts +0 -1
- package/dist/lib/turns.js +1 -1
- package/dist/lib/updates.d.ts +184 -28
- package/dist/lib/updates.js +1032 -114
- package/dist/lib/wire.d.ts +12 -0
- package/dist/lib/wire.js +21 -0
- package/dist/lib/workspace-admission.d.ts +25 -17
- package/dist/lib/workspace-admission.js +95 -20
- package/dist/lib/workspace-identity.d.ts +20 -0
- package/dist/lib/workspace-identity.js +103 -0
- package/dist/lib/workspace-retirement.d.ts +45 -10
- package/dist/lib/workspace-retirement.js +196 -64
- package/dist/package.json +6 -6
- package/dist/skills/generated-control-surface/SKILL.md +3 -3
- package/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/docs/README.md +1 -1
- package/docs/activity.md +1 -1
- package/docs/architecture.md +158 -39
- package/docs/callback-namespaces.md +1 -1
- package/docs/delivery.md +1 -1
- package/docs/generative-apps.md +2 -2
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +127 -19
- package/docs/public-api.md +5 -3
- package/docs/ui-style.md +26 -8
- package/docs/updates.md +1 -1
- package/lib/activity-verbosity.ts +5 -9
- package/lib/bindings.ts +23 -10
- package/lib/bus-api.ts +5 -2
- package/lib/bus-follower.ts +251 -226
- package/lib/bus-leader.ts +178 -31
- package/lib/bus-transport.ts +30 -8
- package/lib/bus.ts +110 -334
- package/lib/channel-posts.ts +29 -29
- package/lib/command-templates.ts +5 -5
- package/lib/commands.ts +150 -112
- package/lib/config.ts +12 -17
- package/lib/delivery.ts +2 -8
- package/lib/extension.ts +282 -72
- package/lib/generative-apps.ts +0 -22
- package/lib/inbound.ts +2 -2
- package/lib/journal.ts +1182 -326
- package/lib/keyboard.ts +2 -2
- package/lib/lifecycle.ts +141 -5
- package/lib/locks.ts +402 -23
- package/lib/logging.ts +151 -101
- package/lib/media.ts +51 -9
- package/lib/menu-model.ts +8 -8
- package/lib/menu-settings.ts +18 -18
- package/lib/menu-status.ts +11 -0
- package/lib/menu-thinking.ts +2 -2
- package/lib/menu.ts +5 -1
- package/lib/model.ts +2 -4
- package/lib/outbound-attachments.ts +1 -1
- package/lib/outbound-buttons.ts +1 -10
- package/lib/outbound-markup.ts +5 -24
- package/lib/outbound.ts +5 -5
- package/lib/paths.ts +177 -17
- package/lib/pi.ts +5 -2
- package/lib/polling.ts +5 -5
- package/lib/preview.ts +3 -3
- package/lib/prompt-templates.ts +1 -1
- package/lib/queue.ts +67 -9
- package/lib/recovery.ts +104 -44
- package/lib/replies.ts +1 -1
- package/lib/routing.ts +1955 -375
- package/lib/sections.ts +1 -1
- package/lib/status.ts +185 -36
- package/lib/sync.ts +8 -8
- package/lib/telegram-api.ts +48 -33
- package/lib/thread-cleanup-manager.ts +24 -21
- package/lib/thread-naming.ts +267 -9
- package/lib/thread-reconciler.ts +3 -1
- package/lib/threads.ts +1718 -491
- package/lib/turns.ts +1 -1
- package/lib/updates.ts +1072 -151
- package/lib/wire.ts +28 -0
- package/lib/workspace-admission.ts +97 -47
- package/lib/workspace-identity.ts +147 -0
- package/lib/workspace-retirement.ts +234 -102
- package/package.json +6 -6
- package/scripts/audit-exports.mjs +100 -0
- package/scripts/check-downgrade.mjs +80 -43
- package/skills/generated-control-surface/SKILL.md +3 -3
- package/skills/telegram-bridge/references/diagnosis.md +3 -3
package/lib/keyboard.ts
CHANGED
|
@@ -21,9 +21,9 @@ export interface TelegramInlineKeyboardMarkup {
|
|
|
21
21
|
inline_keyboard: TelegramInlineKeyboardButton[][];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const TELEGRAM_CALLBACK_DATA_MAX_BYTES = 64;
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
function getTelegramCallbackDataByteLength(value: string): number {
|
|
27
27
|
return new TextEncoder().encode(value).byteLength;
|
|
28
28
|
}
|
|
29
29
|
|
package/lib/lifecycle.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram lifecycle hook registration
|
|
2
|
+
* Telegram session lifecycle coordination and hook registration
|
|
3
3
|
* Zones: pi agent lifecycle, telegram session
|
|
4
|
-
*
|
|
4
|
+
* Owns context generations, bounded connect intent across resume, and session sequencing.
|
|
5
|
+
* Transport authority, durable bindings, and queue custody remain with their owners.
|
|
5
6
|
*/
|
|
6
7
|
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { formatTelegramConnectionFailure } from "./status.ts";
|
|
7
10
|
import * as BusFollower from "./bus-follower.ts";
|
|
8
11
|
import * as Queue from "./queue.ts";
|
|
9
12
|
import * as TextGroups from "./text-groups.ts";
|
|
@@ -157,6 +160,130 @@ export interface TelegramSessionLifecycleHooks {
|
|
|
157
160
|
) => Promise<void>;
|
|
158
161
|
}
|
|
159
162
|
|
|
163
|
+
export interface TelegramConnectionIntent {
|
|
164
|
+
id: string;
|
|
165
|
+
cwd: string;
|
|
166
|
+
profileName?: string;
|
|
167
|
+
}
|
|
168
|
+
interface TelegramConnectionHandoff extends TelegramConnectionIntent {
|
|
169
|
+
pid: number;
|
|
170
|
+
targetSessionFile: string;
|
|
171
|
+
expiresAtMs: number;
|
|
172
|
+
}
|
|
173
|
+
export interface TelegramConnectionHandoffStore {
|
|
174
|
+
pending?: TelegramConnectionHandoff;
|
|
175
|
+
}
|
|
176
|
+
const HANDOFF_KEY = Symbol.for("pi-telegram.connection-resume.v1");
|
|
177
|
+
function processHandoffStore(): TelegramConnectionHandoffStore {
|
|
178
|
+
const globals = globalThis as unknown as Record<symbol, TelegramConnectionHandoffStore>;
|
|
179
|
+
return globals[HANDOFF_KEY] ??= {};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface TelegramConnectionLifecycle {
|
|
183
|
+
prepare(event: SessionStartEvent, ctx: ExtensionContext): (() => void) | undefined;
|
|
184
|
+
onSessionShutdown(event: SessionShutdownEvent, ctx: ExtensionContext): void;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function createTelegramConnectionLifecycle(deps: {
|
|
188
|
+
intent: ReturnType<typeof createTelegramConnectionIntentRuntime>;
|
|
189
|
+
getGeneration(): number;
|
|
190
|
+
isCurrent(ctx: ExtensionContext): boolean;
|
|
191
|
+
getProfileName(): string | undefined;
|
|
192
|
+
isConnected(): boolean;
|
|
193
|
+
activateProfile(profileName: string | undefined, isCurrent: () => boolean): Promise<boolean>;
|
|
194
|
+
start(ctx: ExtensionContext): Promise<{ ok: boolean; message?: string }>;
|
|
195
|
+
recordError(error: unknown): void;
|
|
196
|
+
}): TelegramConnectionLifecycle {
|
|
197
|
+
return {
|
|
198
|
+
onSessionShutdown(event, ctx) {
|
|
199
|
+
deps.intent.suspend({ reason: event.reason, cwd: ctx.cwd,
|
|
200
|
+
targetSessionFile: event.targetSessionFile, connected: deps.isConnected(),
|
|
201
|
+
profileName: deps.getProfileName() });
|
|
202
|
+
},
|
|
203
|
+
prepare(event, ctx) {
|
|
204
|
+
const intent = deps.intent.resume({ reason: event.reason, cwd: ctx.cwd,
|
|
205
|
+
sessionFile: event.reason === "resume" ? ctx.sessionManager.getSessionFile() : undefined });
|
|
206
|
+
if (!intent) return undefined;
|
|
207
|
+
const generation = deps.getGeneration();
|
|
208
|
+
const isCurrent = () => deps.intent.isActive(intent.id) &&
|
|
209
|
+
generation === deps.getGeneration() && deps.isCurrent(ctx);
|
|
210
|
+
return () => {
|
|
211
|
+
// Startup remains extension-owned background work; no captured command ctx crosses resume.
|
|
212
|
+
void (async () => {
|
|
213
|
+
if (!isCurrent()) return;
|
|
214
|
+
if (!await deps.activateProfile(intent.profileName, isCurrent)) {
|
|
215
|
+
if (isCurrent()) ctx.ui.notify("Telegram profile unavailable. Run /telegram-setup.", "warning");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (!isCurrent()) return;
|
|
219
|
+
const result = await deps.start(ctx);
|
|
220
|
+
if (!isCurrent()) return;
|
|
221
|
+
if (!result.ok) {
|
|
222
|
+
deps.recordError(new Error(result.message ?? "Telegram resume connection failed."));
|
|
223
|
+
ctx.ui.notify(formatTelegramConnectionFailure(result.message), "warning");
|
|
224
|
+
}
|
|
225
|
+
})().catch((error) => {
|
|
226
|
+
deps.recordError(error);
|
|
227
|
+
if (isCurrent()) ctx.ui.notify(formatTelegramConnectionFailure(error), "warning");
|
|
228
|
+
}).finally(() => deps.intent.finish(intent.id));
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function createTelegramConnectionIntentRuntime(options: {
|
|
235
|
+
store?: TelegramConnectionHandoffStore;
|
|
236
|
+
now?: () => number;
|
|
237
|
+
pid?: number;
|
|
238
|
+
} = {}) {
|
|
239
|
+
const store = options.store ?? processHandoffStore();
|
|
240
|
+
const now = options.now ?? Date.now;
|
|
241
|
+
const pid = options.pid ?? process.pid;
|
|
242
|
+
let active: TelegramConnectionIntent | undefined;
|
|
243
|
+
return {
|
|
244
|
+
begin(cwd: string, profileName?: string): string {
|
|
245
|
+
store.pending = undefined;
|
|
246
|
+
active = { id: randomUUID(), cwd, profileName };
|
|
247
|
+
return active.id;
|
|
248
|
+
},
|
|
249
|
+
isActive(id: string): boolean {
|
|
250
|
+
return active?.id === id;
|
|
251
|
+
},
|
|
252
|
+
finish(id: string): void {
|
|
253
|
+
if (active?.id === id) active = undefined;
|
|
254
|
+
},
|
|
255
|
+
cancel(): void {
|
|
256
|
+
active = undefined;
|
|
257
|
+
store.pending = undefined;
|
|
258
|
+
},
|
|
259
|
+
suspend(input: {
|
|
260
|
+
reason: string;
|
|
261
|
+
cwd: string;
|
|
262
|
+
targetSessionFile?: string;
|
|
263
|
+
connected: boolean;
|
|
264
|
+
profileName?: string;
|
|
265
|
+
}): void {
|
|
266
|
+
const intent = active ?? (input.connected
|
|
267
|
+
? { id: randomUUID(), cwd: input.cwd, profileName: input.profileName }
|
|
268
|
+
: undefined);
|
|
269
|
+
active = undefined;
|
|
270
|
+
store.pending = input.reason === "resume" && input.targetSessionFile &&
|
|
271
|
+
intent?.cwd === input.cwd
|
|
272
|
+
? { ...intent, pid, targetSessionFile: input.targetSessionFile, expiresAtMs: now() + 30_000 }
|
|
273
|
+
: undefined;
|
|
274
|
+
},
|
|
275
|
+
resume(input: { reason: string; cwd: string; sessionFile?: string }): TelegramConnectionIntent | undefined {
|
|
276
|
+
const handoff = store.pending;
|
|
277
|
+
store.pending = undefined;
|
|
278
|
+
if (!handoff || handoff.pid !== pid || now() >= handoff.expiresAtMs ||
|
|
279
|
+
input.reason !== "resume" || input.cwd !== handoff.cwd ||
|
|
280
|
+
input.sessionFile !== handoff.targetSessionFile) return undefined;
|
|
281
|
+
active = { id: randomUUID(), cwd: handoff.cwd, profileName: handoff.profileName };
|
|
282
|
+
return { ...active };
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
160
287
|
export interface TelegramSessionContextStore<TContext> {
|
|
161
288
|
get: () => TContext | undefined;
|
|
162
289
|
getGeneration: () => number;
|
|
@@ -250,6 +377,7 @@ export function createTelegramSessionGenerationFence(
|
|
|
250
377
|
}
|
|
251
378
|
|
|
252
379
|
export interface TelegramBridgeSessionServiceRuntime {
|
|
380
|
+
connection?: TelegramConnectionLifecycle;
|
|
253
381
|
resumeGroupedInput(ctx: ExtensionContext): void;
|
|
254
382
|
suspendGroupedInput(): void;
|
|
255
383
|
delivery: {
|
|
@@ -319,6 +447,7 @@ export interface TelegramBridgeSessionLifecyclePorts<
|
|
|
319
447
|
};
|
|
320
448
|
delivery: TelegramBridgeSessionServiceRuntime["delivery"];
|
|
321
449
|
polling: TelegramBridgeSessionServiceRuntime["polling"];
|
|
450
|
+
connection?: TelegramBridgeSessionServiceRuntime["connection"];
|
|
322
451
|
inboundWorker: TelegramBridgeSessionServiceRuntime["inboundWorker"];
|
|
323
452
|
capabilityMonitor: TelegramBridgeSessionServiceRuntime["capabilityMonitor"];
|
|
324
453
|
queueWatchdog: TelegramBridgeSessionServiceRuntime["queueWatchdog"];
|
|
@@ -348,6 +477,7 @@ export function createTelegramBridgeSessionLifecycleDeps<
|
|
|
348
477
|
}),
|
|
349
478
|
delivery: ports.services.delivery,
|
|
350
479
|
polling: ports.services.polling,
|
|
480
|
+
connection: ports.services.connection,
|
|
351
481
|
inboundWorker: ports.services.inboundWorker,
|
|
352
482
|
capabilityMonitor: ports.services.capabilityMonitor,
|
|
353
483
|
queueWatchdog: ports.services.queueWatchdog,
|
|
@@ -371,19 +501,23 @@ export function createTelegramBridgeSessionLifecycleAssembly<
|
|
|
371
501
|
suspendPolling: deps.follower.suspendPolling,
|
|
372
502
|
recordRuntimeEvent: deps.follower.recordRuntimeEvent,
|
|
373
503
|
});
|
|
504
|
+
let preserveTarget = true;
|
|
374
505
|
const queueLifecycle = Queue.createTelegramSessionLifecycleRuntime({
|
|
375
506
|
...deps.queue,
|
|
376
507
|
isSessionActive,
|
|
377
|
-
stopPolling: suspendForReplacement,
|
|
508
|
+
stopPolling: () => suspendForReplacement(preserveTarget),
|
|
378
509
|
clearPendingMediaGroups: deps.services.suspendGroupedInput,
|
|
379
510
|
});
|
|
380
511
|
const servicesLifecycle: TelegramSessionLifecycleHooks = {
|
|
381
512
|
async onSessionStart(event, ctx) {
|
|
513
|
+
const resumeConnection = deps.services.connection?.prepare(event, ctx);
|
|
382
514
|
await queueLifecycle.onSessionStart(event, ctx);
|
|
383
515
|
if (!isSessionActive(ctx)) return;
|
|
384
516
|
deps.services.resumeGroupedInput(ctx);
|
|
385
517
|
await deps.services.delivery.onSessionStart();
|
|
386
|
-
|
|
518
|
+
if (!isSessionActive(ctx)) return;
|
|
519
|
+
if (resumeConnection) resumeConnection();
|
|
520
|
+
else await deps.services.polling.onSessionStart(event, ctx);
|
|
387
521
|
deps.services.capabilityMonitor.start(ctx);
|
|
388
522
|
deps.services.queueWatchdog.start(ctx);
|
|
389
523
|
},
|
|
@@ -391,6 +525,8 @@ export function createTelegramBridgeSessionLifecycleAssembly<
|
|
|
391
525
|
const generation = deps.contextStore.getGeneration();
|
|
392
526
|
const isCurrent = () => deps.contextStore.isCurrent(ctx, generation);
|
|
393
527
|
if (!isCurrent()) return;
|
|
528
|
+
deps.services.connection?.onSessionShutdown(event, ctx);
|
|
529
|
+
preserveTarget = event.reason !== "resume";
|
|
394
530
|
let preserveThread: (() => Promise<void>) | undefined;
|
|
395
531
|
if (event.reason === "quit") {
|
|
396
532
|
try {
|
|
@@ -515,7 +651,7 @@ export function createTelegramCompactionObserverRuntime<TContext>(
|
|
|
515
651
|
"compact",
|
|
516
652
|
new Error("Compaction observer timed out"),
|
|
517
653
|
);
|
|
518
|
-
|
|
654
|
+
// Observer expiry releases local presence, not Pi's eventual terminal result.
|
|
519
655
|
requestDispatch();
|
|
520
656
|
}, timeoutMs);
|
|
521
657
|
unrefTelegramLifecycleTimer(fallbackTimer);
|