@llblab/pi-telegram 0.27.11 → 0.28.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 +150 -258
- package/BACKLOG.md +1 -169
- package/CHANGELOG.md +396 -441
- package/README.md +9 -6
- package/api/updates.ts +5 -0
- package/docs/architecture.md +71 -26
- package/docs/multi-instance-bus.md +23 -5
- package/docs/public-api.md +7 -6
- package/docs/ui-style.md +6 -6
- package/docs/updates.md +29 -11
- package/index.ts +356 -246
- package/lib/activity-verbosity.ts +26 -0
- package/lib/bindings.ts +240 -2
- package/lib/bus-follower.ts +436 -238
- package/lib/bus-leader.ts +395 -42
- package/lib/bus.ts +994 -153
- package/lib/commands.ts +184 -30
- package/lib/config.ts +23 -2
- package/lib/journal.ts +3140 -0
- package/lib/lifecycle.ts +4 -0
- package/lib/locks.ts +21 -21
- package/lib/media.ts +71 -32
- package/lib/menu-queue.ts +31 -17
- package/lib/menu.ts +5 -3
- package/lib/model.ts +51 -24
- package/lib/ownership.ts +42 -7
- package/lib/paths.ts +35 -0
- package/lib/polling.ts +591 -106
- package/lib/prompts.ts +17 -0
- package/lib/queue.ts +732 -143
- package/lib/routing.ts +291 -64
- package/lib/runtime.ts +26 -10
- package/lib/status.ts +257 -18
- package/lib/sync.ts +131 -5
- package/lib/telegram-api.ts +41 -11
- package/lib/text-groups.ts +75 -35
- package/lib/threads.ts +112 -4
- package/lib/turns.ts +79 -14
- package/lib/updates.ts +3771 -223
- package/package.json +3 -3
- package/scripts/check-downgrade.mjs +435 -0
|
@@ -294,6 +294,32 @@ export interface TelegramActivityVerbosityRuntime {
|
|
|
294
294
|
waitForIdle: () => Promise<void>;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
export interface TelegramActivityVerbosityBinding
|
|
298
|
+
extends TelegramActivityVerbosityRuntime {
|
|
299
|
+
bind: (runtime: TelegramActivityVerbosityRuntime) => void;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function createTelegramActivityVerbosityBinding(): TelegramActivityVerbosityBinding {
|
|
303
|
+
let runtime: TelegramActivityVerbosityRuntime | undefined;
|
|
304
|
+
return {
|
|
305
|
+
bind(next) {
|
|
306
|
+
runtime = next;
|
|
307
|
+
},
|
|
308
|
+
accept(event) {
|
|
309
|
+
runtime?.accept(event);
|
|
310
|
+
},
|
|
311
|
+
reset() {
|
|
312
|
+
runtime?.reset();
|
|
313
|
+
},
|
|
314
|
+
stop() {
|
|
315
|
+
runtime?.stop();
|
|
316
|
+
},
|
|
317
|
+
waitForIdle() {
|
|
318
|
+
return runtime?.waitForIdle() ?? Promise.resolve();
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
297
323
|
export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
|
|
298
324
|
getActivityMode: () => "quiet" | "thinking" | "tools" | "verbose";
|
|
299
325
|
refreshActivityMode?: () => Promise<void>;
|
package/lib/bindings.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as Activity from "./activity.ts";
|
|
8
|
-
import
|
|
8
|
+
import * as ActivityVerbosity from "./activity-verbosity.ts";
|
|
9
9
|
import * as CommandTemplates from "./command-templates.ts";
|
|
10
10
|
import * as Commands from "./commands.ts";
|
|
11
11
|
import * as Config from "./config.ts";
|
|
@@ -25,6 +25,7 @@ import * as Runtime from "./runtime.ts";
|
|
|
25
25
|
import * as Setup from "./setup.ts";
|
|
26
26
|
import * as Status from "./status.ts";
|
|
27
27
|
import * as TelegramApi from "./telegram-api.ts";
|
|
28
|
+
import type { TelegramTarget } from "./target.ts";
|
|
28
29
|
|
|
29
30
|
type ActivePiModel = NonNullable<Pi.ExtensionContext["model"]>;
|
|
30
31
|
|
|
@@ -37,6 +38,167 @@ type TelegramRuntimeEventRecorder = (
|
|
|
37
38
|
type TelegramBridgeStatusUpdater =
|
|
38
39
|
Status.TelegramStatusRuntime<Pi.ExtensionContext>["updateStatus"];
|
|
39
40
|
|
|
41
|
+
type TelegramAgentTargetResolver = NonNullable<
|
|
42
|
+
OutboundAttachments.TelegramOutboundMessageToolRegistrationDeps["resolveAgentTarget"]
|
|
43
|
+
>;
|
|
44
|
+
type TelegramAgentMessageRouter = NonNullable<
|
|
45
|
+
OutboundAttachments.TelegramOutboundMessageToolRegistrationDeps["routeAgentMessage"]
|
|
46
|
+
>;
|
|
47
|
+
|
|
48
|
+
export interface TelegramQueueBindingRuntime<TContext> {
|
|
49
|
+
mutation: Queue.TelegramQueueMutationController<TContext>;
|
|
50
|
+
dispatchNext: (ctx: TContext) => void;
|
|
51
|
+
watchdog: Queue.TelegramQueueDispatchWatchdogRuntime<TContext>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createTelegramQueueBindingRuntime<TContext>(deps: {
|
|
55
|
+
store: Queue.TelegramQueueStateStore<TContext>;
|
|
56
|
+
queue: Pick<
|
|
57
|
+
Runtime.TelegramBridgeRuntime["queue"],
|
|
58
|
+
"getNextPriorityReactionOrder" | "incrementNextPriorityReactionOrder"
|
|
59
|
+
>;
|
|
60
|
+
lifecycle: Pick<
|
|
61
|
+
Runtime.TelegramBridgeRuntime["lifecycle"],
|
|
62
|
+
"isCompactionInProgress" | "hasDispatchPending"
|
|
63
|
+
>;
|
|
64
|
+
activeTurn: Pick<Queue.TelegramActiveTurnStore, "has">;
|
|
65
|
+
admission: {
|
|
66
|
+
getSettlement: () =>
|
|
67
|
+
| {
|
|
68
|
+
onItemsDiscarded: (
|
|
69
|
+
items: readonly Queue.TelegramQueueItem<TContext>[],
|
|
70
|
+
ctx: TContext,
|
|
71
|
+
) => void;
|
|
72
|
+
isItemReady: (item: Queue.TelegramQueueItem<TContext>) => boolean;
|
|
73
|
+
onControlSettled: (
|
|
74
|
+
item: Queue.PendingTelegramControlItem<TContext>,
|
|
75
|
+
ctx: TContext,
|
|
76
|
+
) => void;
|
|
77
|
+
}
|
|
78
|
+
| undefined;
|
|
79
|
+
hasPendingQueueMutationForItem: (
|
|
80
|
+
item: Queue.TelegramQueueItem<TContext>,
|
|
81
|
+
) => boolean;
|
|
82
|
+
};
|
|
83
|
+
transportStamp: Pick<Queue.TelegramTransportStampRuntime, "isActive">;
|
|
84
|
+
deferredDispatch: Pick<
|
|
85
|
+
Queue.TelegramDeferredQueueDispatchRuntime<TContext>,
|
|
86
|
+
"isBound" | "getGeneration" | "isGenerationActive"
|
|
87
|
+
>;
|
|
88
|
+
promptDispatch: Runtime.TelegramPromptDispatchRuntime<TContext>;
|
|
89
|
+
isIdle: (ctx: TContext) => boolean;
|
|
90
|
+
hasPendingMessages: (ctx: TContext) => boolean;
|
|
91
|
+
updateStatus: (ctx: TContext, error?: string) => void;
|
|
92
|
+
sendTextReply: Queue.TelegramQueueDispatchRuntimeDeps<TContext>["sendTextReply"];
|
|
93
|
+
sendUserMessage: Queue.TelegramQueueDispatchRuntimeDeps<TContext>["sendUserMessage"];
|
|
94
|
+
recordRuntimeEvent?: TelegramRuntimeEventRecorder;
|
|
95
|
+
}): TelegramQueueBindingRuntime<TContext> {
|
|
96
|
+
const mutation = Queue.createTelegramQueueMutationController({
|
|
97
|
+
...deps.store,
|
|
98
|
+
getNextPriorityReactionOrder: deps.queue.getNextPriorityReactionOrder,
|
|
99
|
+
incrementNextPriorityReactionOrder:
|
|
100
|
+
deps.queue.incrementNextPriorityReactionOrder,
|
|
101
|
+
onItemsDiscarded(items, ctx) {
|
|
102
|
+
deps.admission.getSettlement()?.onItemsDiscarded(items, ctx);
|
|
103
|
+
},
|
|
104
|
+
updateStatus: deps.updateStatus,
|
|
105
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
106
|
+
});
|
|
107
|
+
const dispatchNext = Queue.createTelegramQueueDispatchRuntime({
|
|
108
|
+
...deps.store,
|
|
109
|
+
isCompactionInProgress: deps.lifecycle.isCompactionInProgress,
|
|
110
|
+
hasActiveTurn: deps.activeTurn.has,
|
|
111
|
+
hasDispatchPending: deps.lifecycle.hasDispatchPending,
|
|
112
|
+
isIdle: deps.isIdle,
|
|
113
|
+
hasPendingMessages: deps.hasPendingMessages,
|
|
114
|
+
hasDispatchContext: deps.deferredDispatch.isBound,
|
|
115
|
+
getDispatchGeneration: deps.deferredDispatch.getGeneration,
|
|
116
|
+
isDispatchGenerationActive: deps.deferredDispatch.isGenerationActive,
|
|
117
|
+
isQueueItemTransportActive(item) {
|
|
118
|
+
return deps.transportStamp.isActive(item.transportStamp);
|
|
119
|
+
},
|
|
120
|
+
hasPendingInboundQueueMutationForItem(item) {
|
|
121
|
+
return deps.admission.hasPendingQueueMutationForItem(item);
|
|
122
|
+
},
|
|
123
|
+
isQueueItemAdmissionReady(item) {
|
|
124
|
+
return (
|
|
125
|
+
deps.admission.getSettlement()?.isItemReady(item) ??
|
|
126
|
+
(item.admissionReceipts?.length ?? 0) === 0
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
onControlSettled(item, ctx) {
|
|
130
|
+
deps.admission.getSettlement()?.onControlSettled(item, ctx);
|
|
131
|
+
},
|
|
132
|
+
updateStatus: deps.updateStatus,
|
|
133
|
+
sendTextReply: deps.sendTextReply,
|
|
134
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
135
|
+
...deps.promptDispatch,
|
|
136
|
+
sendUserMessage: deps.sendUserMessage,
|
|
137
|
+
}).dispatchNext;
|
|
138
|
+
return {
|
|
139
|
+
mutation,
|
|
140
|
+
dispatchNext,
|
|
141
|
+
watchdog: Queue.createTelegramQueueDispatchWatchdogRuntime({
|
|
142
|
+
hasQueuedItems: deps.store.hasQueuedItems,
|
|
143
|
+
dispatchNextQueuedTelegramTurn: dispatchNext,
|
|
144
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface TelegramAgentMessageToolRoutingRuntime {
|
|
150
|
+
resolveAgentTarget: TelegramAgentTargetResolver;
|
|
151
|
+
routeAgentMessage: TelegramAgentMessageRouter;
|
|
152
|
+
canSendDirect: () => boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function createTelegramAgentMessageToolRoutingRuntime(deps: {
|
|
156
|
+
ownsLeader: () => boolean;
|
|
157
|
+
ownsDirectDelivery: () => boolean;
|
|
158
|
+
isFollowerRegistered: () => boolean;
|
|
159
|
+
getSourceTarget: () => TelegramTarget | undefined;
|
|
160
|
+
getSourceThreadName: () => string | undefined;
|
|
161
|
+
local: {
|
|
162
|
+
resolveTarget: (
|
|
163
|
+
selector: Parameters<TelegramAgentTargetResolver>[0],
|
|
164
|
+
sourceTarget?: TelegramTarget,
|
|
165
|
+
) => Awaited<ReturnType<TelegramAgentTargetResolver>> | undefined;
|
|
166
|
+
route: (input: {
|
|
167
|
+
sourceTarget?: TelegramTarget;
|
|
168
|
+
sourceThreadName?: string;
|
|
169
|
+
message: Parameters<TelegramAgentMessageRouter>[0];
|
|
170
|
+
}) => Promise<void>;
|
|
171
|
+
};
|
|
172
|
+
follower: {
|
|
173
|
+
resolveTarget: TelegramAgentTargetResolver;
|
|
174
|
+
routeMessage: TelegramAgentMessageRouter;
|
|
175
|
+
};
|
|
176
|
+
}): TelegramAgentMessageToolRoutingRuntime {
|
|
177
|
+
return {
|
|
178
|
+
async resolveAgentTarget(selector) {
|
|
179
|
+
if (!deps.ownsLeader()) return deps.follower.resolveTarget(selector);
|
|
180
|
+
const target = deps.local.resolveTarget(selector, deps.getSourceTarget());
|
|
181
|
+
if (!target) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
"Telegram agent target is unavailable, ambiguous, or not live.",
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return target;
|
|
187
|
+
},
|
|
188
|
+
async routeAgentMessage(message) {
|
|
189
|
+
if (!deps.ownsLeader()) return deps.follower.routeMessage(message);
|
|
190
|
+
await deps.local.route({
|
|
191
|
+
sourceTarget: deps.getSourceTarget(),
|
|
192
|
+
sourceThreadName: deps.getSourceThreadName(),
|
|
193
|
+
message,
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
canSendDirect() {
|
|
197
|
+
return deps.ownsDirectDelivery() || deps.isFollowerRegistered();
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
40
202
|
export interface TelegramAssistantOutputBindingRuntime<TTransportStamp> {
|
|
41
203
|
runtime: Activity.TelegramAssistantOutputRuntime;
|
|
42
204
|
observeEvent: (event: Activity.TelegramActivityEvent) => void;
|
|
@@ -97,6 +259,76 @@ export function createTelegramAssistantOutputBindingRuntime<
|
|
|
97
259
|
};
|
|
98
260
|
}
|
|
99
261
|
|
|
262
|
+
type TelegramAssistantOutputAuthority<TTransportStamp> = ReturnType<
|
|
263
|
+
Routing.TelegramAssistantOutputAuthorityRuntime<TTransportStamp>["captureAuthority"]
|
|
264
|
+
>;
|
|
265
|
+
|
|
266
|
+
export interface TelegramActivityBindingRuntime {
|
|
267
|
+
activityRuntime: Activity.TelegramActivityRuntime;
|
|
268
|
+
activityVerbosityRuntime: ActivityVerbosity.TelegramActivityVerbosityRuntime;
|
|
269
|
+
assistantOutputRuntime: Activity.TelegramAssistantOutputRuntime;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Compose public activity fanout, verbosity, and assistant output ordering. */
|
|
273
|
+
export function createTelegramActivityBindingRuntime<TTransportStamp>(deps: {
|
|
274
|
+
generation: string;
|
|
275
|
+
assistantOutput: Omit<
|
|
276
|
+
Parameters<
|
|
277
|
+
typeof createTelegramAssistantOutputBindingRuntime<TTransportStamp>
|
|
278
|
+
>[0],
|
|
279
|
+
"waitForActivityIdle"
|
|
280
|
+
>;
|
|
281
|
+
activityVerbosity: Omit<
|
|
282
|
+
Parameters<
|
|
283
|
+
typeof ActivityVerbosity.createTelegramActivityVerbosityRuntime<
|
|
284
|
+
TelegramAssistantOutputAuthority<TTransportStamp>
|
|
285
|
+
>
|
|
286
|
+
>[0],
|
|
287
|
+
"captureAuthority" | "isAuthorityActive" | "recordFailure"
|
|
288
|
+
>;
|
|
289
|
+
}): TelegramActivityBindingRuntime {
|
|
290
|
+
const activityVerbosityBinding =
|
|
291
|
+
ActivityVerbosity.createTelegramActivityVerbosityBinding();
|
|
292
|
+
const assistantOutputBinding =
|
|
293
|
+
createTelegramAssistantOutputBindingRuntime({
|
|
294
|
+
...deps.assistantOutput,
|
|
295
|
+
waitForActivityIdle: activityVerbosityBinding.waitForIdle,
|
|
296
|
+
});
|
|
297
|
+
const activityVerbosityRuntime =
|
|
298
|
+
ActivityVerbosity.createTelegramActivityVerbosityRuntime({
|
|
299
|
+
...deps.activityVerbosity,
|
|
300
|
+
captureAuthority: assistantOutputBinding.authority.captureAuthority,
|
|
301
|
+
isAuthorityActive: assistantOutputBinding.authority.isAuthorityActive,
|
|
302
|
+
recordFailure(operation, event, error) {
|
|
303
|
+
deps.assistantOutput.recordRuntimeEvent("activity", error, {
|
|
304
|
+
operation,
|
|
305
|
+
eventType: event.type,
|
|
306
|
+
activityId: event.activityId,
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
activityVerbosityBinding.bind(activityVerbosityRuntime);
|
|
311
|
+
const activityRuntime = Activity.createTelegramActivityBridgeRuntime({
|
|
312
|
+
generation: deps.generation,
|
|
313
|
+
observeEvent(event) {
|
|
314
|
+
assistantOutputBinding.observeEvent(event);
|
|
315
|
+
activityVerbosityRuntime.accept(event);
|
|
316
|
+
},
|
|
317
|
+
recordFailure(handlerId, event, error) {
|
|
318
|
+
deps.assistantOutput.recordRuntimeEvent("activity", error, {
|
|
319
|
+
handlerId,
|
|
320
|
+
eventType: event.type,
|
|
321
|
+
activityId: event.activityId,
|
|
322
|
+
});
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
activityRuntime,
|
|
327
|
+
activityVerbosityRuntime,
|
|
328
|
+
assistantOutputRuntime: assistantOutputBinding.runtime,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
100
332
|
interface TelegramCommandsAndToolsBindingDeps {
|
|
101
333
|
pi: Pi.ExtensionAPI;
|
|
102
334
|
configStore: Config.TelegramConfigStore;
|
|
@@ -291,7 +523,7 @@ export function registerTelegramCommandsAndTools({
|
|
|
291
523
|
interface TelegramLifecycleBindingDeps {
|
|
292
524
|
pi: Pi.ExtensionAPI;
|
|
293
525
|
activityRuntime: Activity.TelegramActivityRuntime;
|
|
294
|
-
activityVerbosityRuntime?: TelegramActivityVerbosityRuntime;
|
|
526
|
+
activityVerbosityRuntime?: ActivityVerbosity.TelegramActivityVerbosityRuntime;
|
|
295
527
|
assistantOutputRuntime: Pick<
|
|
296
528
|
Activity.TelegramAssistantOutputRuntime,
|
|
297
529
|
"start" | "waitForIdle" | "stop"
|
|
@@ -344,6 +576,10 @@ interface TelegramLifecycleBindingDeps {
|
|
|
344
576
|
>["sendTextReply"] &
|
|
345
577
|
NonNullable<OutboundHandlers.TelegramVoiceReplySenderDeps["sendTextReply"]>;
|
|
346
578
|
dispatchNextQueuedTelegramTurn: (ctx: Pi.ExtensionContext) => void;
|
|
579
|
+
onPromptHandedOff?: (
|
|
580
|
+
turn: Queue.PendingTelegramTurn,
|
|
581
|
+
ctx: Pi.ExtensionContext,
|
|
582
|
+
) => void;
|
|
347
583
|
answerGuestQuery: TelegramApi.TelegramBridgeApiRuntime["answerGuestQuery"];
|
|
348
584
|
deleteMessage: TelegramApi.TelegramBridgeApiRuntime["deleteMessage"];
|
|
349
585
|
sendGuestReply: NonNullable<
|
|
@@ -400,6 +636,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
400
636
|
sendMarkdownReply,
|
|
401
637
|
sendTextReply,
|
|
402
638
|
dispatchNextQueuedTelegramTurn,
|
|
639
|
+
onPromptHandedOff,
|
|
403
640
|
answerGuestQuery,
|
|
404
641
|
deleteMessage,
|
|
405
642
|
sendGuestReply,
|
|
@@ -554,6 +791,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
554
791
|
clearDispatchPending: lifecycle.clearDispatchPending,
|
|
555
792
|
setFoldQueuedPromptsIntoHistory: lifecycle.setFoldQueuedPromptsIntoHistory,
|
|
556
793
|
setActiveTurn: activeTurnRuntime.set,
|
|
794
|
+
onPromptHandedOff,
|
|
557
795
|
createPreviewState: previewRuntime.resetState,
|
|
558
796
|
startTypingLoop: (ctx) => {
|
|
559
797
|
const turn = activeTurnRuntime.get();
|