@llblab/pi-telegram 0.27.12 → 0.29.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 +152 -258
- package/BACKLOG.md +1 -169
- package/CHANGELOG.md +398 -443
- package/README.md +10 -7
- package/api/updates.ts +5 -0
- package/docs/architecture.md +73 -28
- package/docs/multi-instance-bus.md +23 -5
- package/docs/outbound.md +2 -2
- package/docs/public-api.md +8 -7
- package/docs/ui-style.md +6 -6
- package/docs/updates.md +29 -11
- package/index.ts +358 -246
- package/lib/activity-verbosity.ts +26 -0
- package/lib/bindings.ts +240 -5
- 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 +4 -2
- 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 +20 -89
- package/lib/queue.ts +732 -143
- package/lib/routing.ts +291 -64
- package/lib/runtime.ts +26 -10
- package/lib/skills.ts +21 -0
- 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 +7 -3
- package/scripts/check-downgrade.mjs +435 -0
- package/skills/button-console/SKILL.md +139 -0
- package/skills/telegram-bridge/SKILL.md +138 -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;
|
|
@@ -172,9 +404,6 @@ export function registerTelegramCommandsAndTools({
|
|
|
172
404
|
sendMarkdownReply(chatId, undefined, markdown, options),
|
|
173
405
|
recordRuntimeEvent,
|
|
174
406
|
});
|
|
175
|
-
Prompts.registerTelegramHelpTool(pi, {
|
|
176
|
-
getActiveProfileName: configStore.getActiveProfileName,
|
|
177
|
-
});
|
|
178
407
|
Commands.registerTelegramBridgeCommands(pi, {
|
|
179
408
|
promptForConfig: async (ctx, profileName) => {
|
|
180
409
|
const nextProfileName = profileName ?? undefined;
|
|
@@ -291,7 +520,7 @@ export function registerTelegramCommandsAndTools({
|
|
|
291
520
|
interface TelegramLifecycleBindingDeps {
|
|
292
521
|
pi: Pi.ExtensionAPI;
|
|
293
522
|
activityRuntime: Activity.TelegramActivityRuntime;
|
|
294
|
-
activityVerbosityRuntime?: TelegramActivityVerbosityRuntime;
|
|
523
|
+
activityVerbosityRuntime?: ActivityVerbosity.TelegramActivityVerbosityRuntime;
|
|
295
524
|
assistantOutputRuntime: Pick<
|
|
296
525
|
Activity.TelegramAssistantOutputRuntime,
|
|
297
526
|
"start" | "waitForIdle" | "stop"
|
|
@@ -344,6 +573,10 @@ interface TelegramLifecycleBindingDeps {
|
|
|
344
573
|
>["sendTextReply"] &
|
|
345
574
|
NonNullable<OutboundHandlers.TelegramVoiceReplySenderDeps["sendTextReply"]>;
|
|
346
575
|
dispatchNextQueuedTelegramTurn: (ctx: Pi.ExtensionContext) => void;
|
|
576
|
+
onPromptHandedOff?: (
|
|
577
|
+
turn: Queue.PendingTelegramTurn,
|
|
578
|
+
ctx: Pi.ExtensionContext,
|
|
579
|
+
) => void;
|
|
347
580
|
answerGuestQuery: TelegramApi.TelegramBridgeApiRuntime["answerGuestQuery"];
|
|
348
581
|
deleteMessage: TelegramApi.TelegramBridgeApiRuntime["deleteMessage"];
|
|
349
582
|
sendGuestReply: NonNullable<
|
|
@@ -400,6 +633,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
400
633
|
sendMarkdownReply,
|
|
401
634
|
sendTextReply,
|
|
402
635
|
dispatchNextQueuedTelegramTurn,
|
|
636
|
+
onPromptHandedOff,
|
|
403
637
|
answerGuestQuery,
|
|
404
638
|
deleteMessage,
|
|
405
639
|
sendGuestReply,
|
|
@@ -554,6 +788,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
554
788
|
clearDispatchPending: lifecycle.clearDispatchPending,
|
|
555
789
|
setFoldQueuedPromptsIntoHistory: lifecycle.setFoldQueuedPromptsIntoHistory,
|
|
556
790
|
setActiveTurn: activeTurnRuntime.set,
|
|
791
|
+
onPromptHandedOff,
|
|
557
792
|
createPreviewState: previewRuntime.resetState,
|
|
558
793
|
startTypingLoop: (ctx) => {
|
|
559
794
|
const turn = activeTurnRuntime.get();
|