@llblab/pi-kit 0.10.6 → 0.10.8
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/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +8 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -1
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/ownership.ts +28 -7
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +25 -16
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +12 -7
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +40 -20
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +3 -10
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@llblab/pi-kit` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.10.8 - 2026-09-13
|
|
6
|
+
|
|
7
|
+
- `Follower Forwarding`: Advances the exact Telegram pin to `0.45.8`, so cached-ownership forwarding uses current registration protocol evidence for voice/message and edit retries, message-only callbacks, and reactions. Exact generation, binding, protocol, and durable-receipt checks remain enforced. The package set, resource inventory, and load order remain unchanged.
|
|
8
|
+
|
|
9
|
+
## 0.10.7 - 2026-09-12
|
|
10
|
+
|
|
11
|
+
- `Queue Enqueue`: Advances the exact Telegram pin to `0.45.7`, preventing asynchronous voice/file preparation from restoring consumed prompts or overwriting newer queue changes. Surviving history and receipts follow current queue state; an already-wedged in-memory queue still requires separately authorized recovery. The package set, resource inventory, and load order remain unchanged.
|
|
12
|
+
|
|
5
13
|
## 0.10.6 - 2026-09-12
|
|
6
14
|
|
|
7
15
|
- `Guest Placeholder And Delivery`: Advances the exact Telegram pin to `0.45.6`, so the guest ACK rotates the globe placeholder in whole cycles and holds `🌏 Working on it...`, guest answers are still delivered when a companion extension suppresses the final assistant message, `429` retry waits are recorded as `api` runtime events, the Thread display settings card drops the manual `/name` hint, and empty-queue headings carry no trailing period. The package set, resource inventory, and explicit load order remain unchanged.
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Package links lead to the owning repositories for usage, documentation, issues,
|
|
|
15
15
|
| [`@llblab/pi-codex-usage`](https://github.com/llblab/pi-codex-usage) | `0.9.4` | Compact Codex/Spark subscription-limit status |
|
|
16
16
|
| [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.8.1` | Visible continuation scheduling and bounded worker Skills |
|
|
17
17
|
| [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.9.6` | Atomic scoped state cohorts with final-eligibility resolution |
|
|
18
|
-
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.45.
|
|
18
|
+
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.45.8` | Telegram companion, queues, files, voice, controls, and Generative Apps guidance |
|
|
19
19
|
| [`@llblab/skills`](https://github.com/llblab/skills) | `1.15.0` | Portable workflows for engineering, review, design, context maintenance, and other focused tasks |
|
|
20
20
|
|
|
21
21
|
Versions are exact by design. An upstream release does not change an installed kit until this repository explicitly advances the dependency and publishes a new kit version. Runtime defects and package-specific feature requests belong in the linked repository; package selection and kit installation issues belong here.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
_This file owns unresolved project work only. Completed behavior belongs in `CHANGELOG.md`; durable contracts belong in `AGENTS.md` and `/docs`._
|
|
4
4
|
|
|
5
|
+
- [ ] `Prompt enqueue hotfix` (`optional`, `operator-gated`): Optional nonblocking operator-authorized disposable-follower smoke: overlap delayed voice processing with turn completion and confirm one-time ordered consumption and truthful counts against the [queue contract](./docs/architecture.md#queue-and-dispatch-safety). Separately authorized supported recovery investigation remains open: prevention does not repair an already-wedged in-memory queue; establish the exact recovery path and preservation/discard consequences before mutation, otherwise report the blocker. No journal/ownership edits, replay of settled input, implicit queue clearing, or restart; live activation requires separate operator authorization.
|
|
5
6
|
- [ ] `Channel multimedia posts` (`0.45.1`, live-acceptance-gated): `telegram_message` channel delivery accepts one local `.jpg`/`.jpeg`/`.png`/`.webp` photo or `.mp4` video, uploads it through the multipart transport as `sendPhoto`/`sendVideo` with `text` as the HTML caption, validates kind and size (photo ≤ 10 MiB, video ≤ 50 MiB) plus ≤ 1024 visible caption characters before issuance, and rejects unsupported types and albums instead of downgrading them to links. The channel-post journal binds kind/file name/byte size/SHA-256 and caption, so duplicate requests and lost acknowledgements never re-upload; media-post edits replace the caption through `editMessageCaption`, and Markdown spoilers render as `<tg-spoiler>`. Live image publication passed on `@llb_log`. Regressions cover confirmed publication, duplicate requests, lost ACK, pre-issuance rejection, caption edits, and reconnect replacement. Remaining: operator-authorized disposable-channel acceptance of rejected upload, duplicate request, and caption edit.
|
|
6
7
|
- [ ] `Manual Thread naming` (`gated-but-preparable`, release priority): Local bot-owned `/name Name` and bare `/name` flows avoid model dispatch. One expiring exact-target input dialog immediately accepts the next valid name, always offers cancel, and offers **Reset to automatic** only while a manual override exists; duplicate/stale callbacks cannot repeat mutation. Durable manual override supersedes every automatic display mode, reset is leader/follower generation- and target-fenced, and Letters remains the default without rewriting recovery identity. Local review findings are remediated, including Bot-API-wait target-replacement regressions for leader/follower rename and reset. Remaining: disposable live acceptance for command-menu ordering, dialog, invalid input, duplicate callbacks, leader/follower rename and reset.
|
|
7
8
|
- [ ] `OMP schema acceptance` ([#267](https://github.com/llblab/pi-telegram/issues/267), `human-/environment-gated`): Local emitted-schema, Pi process, and llama.cpp source checks now prove explicit recursive JSON values, root `$defs`, only supported local `#/...` references, and no bare boolean schema. Confirm one connected `telegram_bind` request through the reporter's OMP + llama-server build before closing interoperability acceptance; do not treat this progressive external check as a Pi release blocker.
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.45.8: Follower Forwarding Hotfix
|
|
8
|
+
|
|
9
|
+
- `Follower Forwarding`: Message-ownership lookups now project the matching live registration's protocol identity before forwarding. Voice/message and edit retries, message-only callbacks, and reactions no longer stall on an incomplete cached owner after a rejection or re-registration. Exact generation, binding, protocol, and durable-receipt checks remain enforced.
|
|
10
|
+
|
|
11
|
+
## 0.45.7: Queue Enqueue Race Hotfix
|
|
12
|
+
|
|
13
|
+
- `Queue Enqueue`: Asynchronous voice/file preparation no longer restores consumed prompts or overwrites newer queue changes, preventing a settled phantom head from blocking accepted work. Final assembly uses current queue state and allocates order at commit; abort-history folding retains only surviving intended prompts and receipts while preserving a handed-off head until `agent_start`.
|
|
14
|
+
|
|
7
15
|
## 0.45.6: Guest Placeholder And Delivery Hotfix
|
|
8
16
|
|
|
9
17
|
- `Guest Placeholder`: The guest ACK answers with the first bold globe frame (`🌎 Working on it.`) and rotates `🌍`/`🌏` once per second with the dots growing every two seconds. Rotation now completes whole 6-frame cycles and stops only after the 20 s minimum, so a pending answer holds the cycle's final frame (`🌏 Working on it...`) instead of cutting mid-step; a 26 s safety bound stays clear of the ~28 s Telegram flood-control wall, and error backoff and pre-replacement cancellation are unchanged.
|
|
@@ -429,7 +429,7 @@ The initial `offset: -1` cursor bootstrap is allowed only when both cursor and j
|
|
|
429
429
|
|
|
430
430
|
Threaded Mode forwarding is a two-journal handoff. Only peers that mutually advertise protocol v1 and `durable-follower-admission-v1` may route or become election-eligible. The follower validates its exact binding and registration generation, durably appends the source-bound delivery, and only then returns the exact receipt. The leader classifies each attempt as `accepted`, `retryable`, or `terminal-rejected` with its delivery identity and failure class; only `accepted` with the expected `deliveryId` and `sourceUpdateId` may complete leader journal authority. Missing, negative, stale-generation, or mismatched-receipt acknowledgements remain durable, and a callback error answer is only an operator-facing side effect.
|
|
431
431
|
|
|
432
|
-
Delivery ids derive only from envelope kind, source `update_id`, and stable recipient binding. Live registration generation remains a separate attempt fence
|
|
432
|
+
Delivery ids derive only from envelope kind, source `update_id`, and stable recipient binding. Live registration generation remains a separate attempt fence. Message ownership carries the stable binding and rebinds to its current authenticated follower registration after replacement. `ownership.getForwardOwnership()` projects the protocol identity from the exact matching live instance, registration generation, and binding on every lookup; protocol is not cached with message history. The same port serves messages, edits, callbacks, and reactions, including callbacks without a Thread ID. If a cache lookup returns a foreign record but no matching durable-capable protocol can be projected, that record remains foreign and fails forwarding validation rather than falling through to local execution. The final bus validator independently rechecks generation, binding, and complete protocol identity before transport. Lost acknowledgements therefore retain the same delivery identity; only the exact durable receipt completes the source. Package build skew is allowed only while protocol version and capabilities remain compatible.
|
|
433
433
|
|
|
434
434
|
Worker execution ownership is per `update_id` across same-runtime generations. Aborting a generation ends its authority but does not prove its handler settled; replacement replay remains blocked on that exact settlement. Late success and failure are both diagnostic events. Public and built-in handlers receive the same optional execution fence (`signal`, generation/update identity, and pre-effect assertion); the runtime binds it non-enumerably to every internal update carrier and checks it before routing-plan effects. Prompt construction rechecks after downloads and inbound handlers before queue mutation, pairing rechecks around persistence, command/menu and extension-command delegation retain the source fence across detached effects, lifecycle sync rechecks after store load before reconciliation, and reroute clones carry the source fence through forwarding, thread replacement, cleanup, persistence, and Bot API rename boundaries. Legacy handlers remain source-compatible but must not commit unfenced late effects.
|
|
435
435
|
|
|
@@ -480,6 +480,8 @@ Menu and reaction controls share the same canonical queue state. A menu Keep can
|
|
|
480
480
|
|
|
481
481
|
Admission and planning validate lane contracts. Invalid lane/kind pairings fail predictably instead of being silently coerced.
|
|
482
482
|
|
|
483
|
+
Prompt preparation completes downloads, inbound handlers, and binary image reads before synchronous final assembly and queue commit. Enqueue captures only intended abort-history identities before preparation; afterward it reads the current queue, folds surviving selected prompts using their current text and receipts, allocates the new turn's order, and appends without another asynchronous boundary. The handed-off head remains in place until `agent_start` consumes it, even if preparation finishes first. Concurrent arrivals, removals, and lane/reaction edits remain intact; preparation failure or stale-generation completion leaves intervening queue changes untouched.
|
|
484
|
+
|
|
483
485
|
Dispatch requires:
|
|
484
486
|
|
|
485
487
|
- No active Telegram turn.
|
|
@@ -934,7 +934,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
934
934
|
},
|
|
935
935
|
getAdmissionScope: getTelegramUpdateAdmissionScope,
|
|
936
936
|
getAdmissionJournalBinding: getTelegramQueueJournalBinding,
|
|
937
|
-
getMessageOwnership: messageOwnershipRuntime.
|
|
937
|
+
getMessageOwnership: messageOwnershipRuntime.getForwardOwnership,
|
|
938
938
|
recordMessageOwnership: messageOwnershipRuntime.recordRouted,
|
|
939
939
|
...inboundBusProjectionRuntime,
|
|
940
940
|
getDisplayTitle: getThreadDisplayTitle,
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram message ownership helpers
|
|
3
3
|
* Zones: telegram routing, multi-instance bus, in-memory coordination
|
|
4
|
-
* Owns
|
|
4
|
+
* Owns message-id ownership and live forwarding projections so inbound updates resolve back to the instance/target that owns them
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION,
|
|
9
|
+
type TelegramBusFollowerView,
|
|
10
|
+
type TelegramBusForwardOwnership,
|
|
11
|
+
} from "./bus.ts";
|
|
7
12
|
import { getTelegramTargetKey, type TelegramTarget } from "./target.ts";
|
|
8
13
|
|
|
9
14
|
export interface TelegramMessageOwnershipRecord {
|
|
@@ -44,15 +49,17 @@ export interface TelegramMessageOwnershipStore {
|
|
|
44
49
|
clear: () => void;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
connectedAtMs
|
|
50
|
-
|
|
51
|
-
registrationGeneration?: string;
|
|
52
|
-
}
|
|
52
|
+
export type TelegramFollowerOwnershipView = Pick<
|
|
53
|
+
TelegramBusFollowerView,
|
|
54
|
+
"instanceId" | "connectedAtMs" | "profileKey" | "registrationGeneration" | "protocol"
|
|
55
|
+
>;
|
|
53
56
|
|
|
54
57
|
export interface TelegramBusMessageOwnershipRuntime {
|
|
55
58
|
store: TelegramMessageOwnershipStore;
|
|
59
|
+
getForwardOwnership(
|
|
60
|
+
chatId: number,
|
|
61
|
+
messageId: number,
|
|
62
|
+
): TelegramBusForwardOwnership | undefined;
|
|
56
63
|
recordLocal(input: {
|
|
57
64
|
chatId: number;
|
|
58
65
|
messageId: number;
|
|
@@ -137,6 +144,20 @@ export function createTelegramBusMessageOwnershipRuntime(deps: {
|
|
|
137
144
|
};
|
|
138
145
|
return {
|
|
139
146
|
store,
|
|
147
|
+
getForwardOwnership(chatId, messageId) {
|
|
148
|
+
const record = store.get(chatId, messageId);
|
|
149
|
+
if (!record?.ownerGeneration || !record.recipientBindingKey) return record;
|
|
150
|
+
const follower = deps.listFollowers().find((candidate) =>
|
|
151
|
+
candidate.instanceId === record.instanceId &&
|
|
152
|
+
candidate.registrationGeneration === record.ownerGeneration &&
|
|
153
|
+
candidate.profileKey === record.recipientBindingKey,
|
|
154
|
+
);
|
|
155
|
+
// Protocol is live registration authority, not message-cache history.
|
|
156
|
+
// Keep an incomplete known owner foreign so the forwarder fails closed.
|
|
157
|
+
return follower?.protocol?.capabilities.includes(
|
|
158
|
+
TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION,
|
|
159
|
+
) ? { ...record, protocolIdentity: follower.protocol } : record;
|
|
160
|
+
},
|
|
140
161
|
recordLocal(input) {
|
|
141
162
|
return store.record({ ...input, instanceId: deps.instanceId });
|
|
142
163
|
},
|
|
@@ -2349,16 +2349,18 @@ export interface TelegramControlQueueController<TContext> {
|
|
|
2349
2349
|
) => void;
|
|
2350
2350
|
}
|
|
2351
2351
|
|
|
2352
|
+
export type TelegramPreparedPromptTurn = (
|
|
2353
|
+
historyTurns: PendingTelegramTurn[],
|
|
2354
|
+
) => PendingTelegramTurn;
|
|
2355
|
+
|
|
2352
2356
|
export interface TelegramPromptEnqueueRuntimeDeps<
|
|
2353
2357
|
TMessage,
|
|
2354
2358
|
TContext = unknown,
|
|
2355
2359
|
> extends TelegramQueueStore<TContext> {
|
|
2360
|
+
hasPendingDispatch: () => boolean;
|
|
2356
2361
|
getFoldQueuedPromptsIntoHistory: () => boolean;
|
|
2357
2362
|
setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
|
|
2358
|
-
|
|
2359
|
-
messages: TMessage[],
|
|
2360
|
-
historyTurns: PendingTelegramTurn[],
|
|
2361
|
-
) => Promise<PendingTelegramTurn>;
|
|
2363
|
+
prepareTurn: (messages: TMessage[]) => Promise<TelegramPreparedPromptTurn>;
|
|
2362
2364
|
updateStatus: () => void;
|
|
2363
2365
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
2364
2366
|
assertExecutionCurrent?: () => void;
|
|
@@ -2369,13 +2371,13 @@ export interface TelegramPromptEnqueueControllerDeps<
|
|
|
2369
2371
|
TMessage,
|
|
2370
2372
|
TContext = unknown,
|
|
2371
2373
|
> extends TelegramQueueStore<TContext> {
|
|
2374
|
+
hasPendingDispatch: () => boolean;
|
|
2372
2375
|
getFoldQueuedPromptsIntoHistory: () => boolean;
|
|
2373
2376
|
setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
|
|
2374
|
-
|
|
2377
|
+
prepareTurn: (
|
|
2375
2378
|
messages: TMessage[],
|
|
2376
|
-
historyTurns: PendingTelegramTurn[],
|
|
2377
2379
|
ctx: TContext,
|
|
2378
|
-
) => Promise<
|
|
2380
|
+
) => Promise<TelegramPreparedPromptTurn>;
|
|
2379
2381
|
updateStatus: (ctx: TContext) => void;
|
|
2380
2382
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
2381
2383
|
assertExecutionCurrent?: (messages: TMessage[]) => void;
|
|
@@ -2727,17 +2729,25 @@ export async function enqueueTelegramPromptTurnRuntime<
|
|
|
2727
2729
|
messages: TMessage[],
|
|
2728
2730
|
deps: TelegramPromptEnqueueRuntimeDeps<TMessage, TContext>,
|
|
2729
2731
|
): Promise<PendingTelegramTurn> {
|
|
2730
|
-
|
|
2732
|
+
deps.assertExecutionCurrent?.();
|
|
2733
|
+
const historyOrders = new Set(planTelegramPromptEnqueue(
|
|
2731
2734
|
deps.getQueuedItems(),
|
|
2732
2735
|
deps.getFoldQueuedPromptsIntoHistory(),
|
|
2733
|
-
);
|
|
2734
|
-
deps.assertExecutionCurrent?.();
|
|
2736
|
+
).historyTurns.map((turn) => turn.queueOrder));
|
|
2735
2737
|
deps.setFoldQueuedPromptsIntoHistory(false);
|
|
2736
|
-
const
|
|
2738
|
+
const buildTurn = await deps.prepareTurn(messages);
|
|
2737
2739
|
deps.assertExecutionCurrent?.();
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2740
|
+
// Preserve the Pi-owned head until agent_start, plus later arrivals and current edits/reactions.
|
|
2741
|
+
const pendingDispatch = deps.hasPendingDispatch();
|
|
2742
|
+
const historyTurns: PendingTelegramTurn[] = [];
|
|
2743
|
+
const remainingItems = deps.getQueuedItems().filter((item, index) => {
|
|
2744
|
+
if ((pendingDispatch && index === 0) || !isPendingTelegramTurn(item) ||
|
|
2745
|
+
!historyOrders.has(item.queueOrder)) return true;
|
|
2746
|
+
historyTurns.push(item);
|
|
2747
|
+
return false;
|
|
2748
|
+
});
|
|
2749
|
+
const turn = buildTurn(historyTurns);
|
|
2750
|
+
deps.setQueuedItems(appendTelegramQueueItem(remainingItems, turn));
|
|
2741
2751
|
deps.onQueued?.(turn);
|
|
2742
2752
|
deps.updateStatus();
|
|
2743
2753
|
deps.dispatchNextQueuedTelegramTurn();
|
|
@@ -2754,8 +2764,7 @@ export function createTelegramPromptEnqueueController<
|
|
|
2754
2764
|
enqueue: (messages, ctx, onQueued) =>
|
|
2755
2765
|
enqueueTelegramPromptTurnRuntime(messages, {
|
|
2756
2766
|
...deps,
|
|
2757
|
-
|
|
2758
|
-
deps.createTurn(nextMessages, historyTurns, ctx),
|
|
2767
|
+
prepareTurn: (nextMessages) => deps.prepareTurn(nextMessages, ctx),
|
|
2759
2768
|
updateStatus: () => deps.updateStatus(ctx),
|
|
2760
2769
|
dispatchNextQueuedTelegramTurn: () =>
|
|
2761
2770
|
deps.dispatchNextQueuedTelegramTurn(ctx),
|
|
@@ -2095,7 +2095,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2095
2095
|
}
|
|
2096
2096
|
await menuCallbackHandler(query, ctx);
|
|
2097
2097
|
};
|
|
2098
|
-
const
|
|
2098
|
+
const preparePromptTurn = Turns.createTelegramPromptTurnRuntimePreparer<
|
|
2099
2099
|
TMessage,
|
|
2100
2100
|
TContext
|
|
2101
2101
|
>({
|
|
@@ -2124,7 +2124,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2124
2124
|
text: "continue",
|
|
2125
2125
|
caption: undefined,
|
|
2126
2126
|
} as TMessage;
|
|
2127
|
-
const
|
|
2127
|
+
const buildTurn = await preparePromptTurn([continueMessage], ctx);
|
|
2128
|
+
const turn = buildTurn([]);
|
|
2128
2129
|
const continueTurn = {
|
|
2129
2130
|
...turn,
|
|
2130
2131
|
queueLane: "control" as const,
|
|
@@ -2262,15 +2263,19 @@ export function createTelegramInboundRouteRuntime<
|
|
|
2262
2263
|
const promptEnqueueController =
|
|
2263
2264
|
Queue.createTelegramPromptEnqueueController<TMessage, TContext>({
|
|
2264
2265
|
...deps.telegramQueueStore,
|
|
2266
|
+
hasPendingDispatch: deps.bridgeRuntime.lifecycle.hasDispatchPending,
|
|
2265
2267
|
getFoldQueuedPromptsIntoHistory:
|
|
2266
2268
|
deps.bridgeRuntime.lifecycle.shouldFoldQueuedPromptsIntoHistory,
|
|
2267
2269
|
setFoldQueuedPromptsIntoHistory:
|
|
2268
2270
|
deps.bridgeRuntime.lifecycle.setFoldQueuedPromptsIntoHistory,
|
|
2269
|
-
|
|
2270
|
-
const
|
|
2271
|
-
return
|
|
2272
|
-
|
|
2273
|
-
|
|
2271
|
+
prepareTurn: async (messages, turnCtx) => {
|
|
2272
|
+
const buildTurn = await preparePromptTurn(messages, turnCtx);
|
|
2273
|
+
return (historyTurns) => {
|
|
2274
|
+
const turn = buildTurn(historyTurns);
|
|
2275
|
+
return turn.replyToMessageId > 0
|
|
2276
|
+
? turn
|
|
2277
|
+
: { ...turn, replyToMessageId: 0 };
|
|
2278
|
+
};
|
|
2274
2279
|
},
|
|
2275
2280
|
updateStatus: deps.updateStatus,
|
|
2276
2281
|
dispatchNextQueuedTelegramTurn: requestDispatchNextQueuedTelegramTurn,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
createTelegramQueueAdmissionReceipt,
|
|
27
27
|
truncateTelegramQueueSummary,
|
|
28
28
|
type PendingTelegramTurn,
|
|
29
|
+
type TelegramPreparedPromptTurn,
|
|
29
30
|
type TelegramPromptContent,
|
|
30
31
|
type TelegramQueueAdmissionReceipt,
|
|
31
32
|
type TelegramQueueItem,
|
|
@@ -454,17 +455,16 @@ export interface TelegramPromptTurnRuntimeBuilderDeps<
|
|
|
454
455
|
assertExecutionCurrent?: (message: TelegramTurnMessage) => void;
|
|
455
456
|
}
|
|
456
457
|
|
|
457
|
-
export function
|
|
458
|
+
export function createTelegramPromptTurnRuntimePreparer<
|
|
458
459
|
TMessage extends TelegramTurnMessage & TelegramMediaMessage,
|
|
459
460
|
TContext = unknown,
|
|
460
461
|
>(
|
|
461
462
|
deps: TelegramPromptTurnRuntimeBuilderDeps<TContext>,
|
|
462
463
|
): (
|
|
463
464
|
messages: TMessage[],
|
|
464
|
-
historyTurns?: PendingTelegramTurn[],
|
|
465
465
|
ctx?: TContext,
|
|
466
|
-
) => Promise<
|
|
467
|
-
return async (messages,
|
|
466
|
+
) => Promise<TelegramPreparedPromptTurn> {
|
|
467
|
+
return async (messages, ctx) => {
|
|
468
468
|
const rawText = extractTelegramMessagesText(messages);
|
|
469
469
|
const firstMessage = messages[0];
|
|
470
470
|
if (firstMessage) deps.assertExecutionCurrent?.(firstMessage);
|
|
@@ -577,11 +577,10 @@ export function createTelegramPromptTurnRuntimeBuilder<
|
|
|
577
577
|
thread: threadLabel,
|
|
578
578
|
"from-thread": firstMessage?.pi_telegram_agent_source_thread,
|
|
579
579
|
});
|
|
580
|
-
|
|
580
|
+
const buildTurn = await prepareTelegramPromptTurn({
|
|
581
581
|
telegramPrefix,
|
|
582
582
|
messages,
|
|
583
|
-
|
|
584
|
-
queueOrder: deps.allocateQueueOrder(),
|
|
583
|
+
readBinaryFile: readFile,
|
|
585
584
|
rawText: promptRawText,
|
|
586
585
|
sourceContext,
|
|
587
586
|
statusText: processed.rawText,
|
|
@@ -602,6 +601,10 @@ export function createTelegramPromptTurnRuntimeBuilder<
|
|
|
602
601
|
admissionScope: deps.getAdmissionScope?.(),
|
|
603
602
|
admissionJournalBinding: deps.getAdmissionJournalBinding?.(),
|
|
604
603
|
});
|
|
604
|
+
return (historyTurns) => {
|
|
605
|
+
if (firstMessage) deps.assertExecutionCurrent?.(firstMessage);
|
|
606
|
+
return buildTurn(deps.allocateQueueOrder(), historyTurns);
|
|
607
|
+
};
|
|
605
608
|
};
|
|
606
609
|
}
|
|
607
610
|
|
|
@@ -670,9 +673,29 @@ function collectTelegramTurnAdmissionReceipts(
|
|
|
670
673
|
return [...receipts.values()];
|
|
671
674
|
}
|
|
672
675
|
|
|
673
|
-
|
|
676
|
+
async function prepareTelegramPromptTurn(
|
|
677
|
+
options: Omit<BuildTelegramPromptTurnOptions, "queueOrder" | "historyTurns">,
|
|
678
|
+
): Promise<(queueOrder: number, historyTurns: PendingTelegramTurn[]) => PendingTelegramTurn> {
|
|
679
|
+
const images: TelegramPromptContent[] = [];
|
|
680
|
+
for (const file of options.files) {
|
|
681
|
+
if (!file.isImage) continue;
|
|
682
|
+
const mediaType = file.mimeType || options.inferImageMimeType(file.path);
|
|
683
|
+
if (!mediaType) continue;
|
|
684
|
+
const buffer = await options.readBinaryFile(file.path);
|
|
685
|
+
images.push({
|
|
686
|
+
type: "image",
|
|
687
|
+
data: Buffer.from(buffer).toString("base64"),
|
|
688
|
+
mimeType: mediaType,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
return (queueOrder, historyTurns) =>
|
|
692
|
+
buildPreparedTelegramPromptTurn({ ...options, queueOrder, historyTurns }, images);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function buildPreparedTelegramPromptTurn(
|
|
674
696
|
options: BuildTelegramPromptTurnOptions,
|
|
675
|
-
|
|
697
|
+
images: TelegramPromptContent[],
|
|
698
|
+
): PendingTelegramTurn {
|
|
676
699
|
const firstMessage = options.messages[0];
|
|
677
700
|
if (!firstMessage) {
|
|
678
701
|
throw new Error("Missing Telegram message for turn creation");
|
|
@@ -703,18 +726,8 @@ export async function buildTelegramPromptTurn(
|
|
|
703
726
|
),
|
|
704
727
|
}),
|
|
705
728
|
},
|
|
729
|
+
...images,
|
|
706
730
|
];
|
|
707
|
-
for (const file of options.files) {
|
|
708
|
-
if (!file.isImage) continue;
|
|
709
|
-
const mediaType = file.mimeType || options.inferImageMimeType(file.path);
|
|
710
|
-
if (!mediaType) continue;
|
|
711
|
-
const buffer = await options.readBinaryFile(file.path);
|
|
712
|
-
content.push({
|
|
713
|
-
type: "image",
|
|
714
|
-
data: Buffer.from(buffer).toString("base64"),
|
|
715
|
-
mimeType: mediaType,
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
731
|
if (options.voicePromptContribution?.trim()) {
|
|
719
732
|
const textItem = content.find((c) => c.type === "text") as
|
|
720
733
|
{ type: "text"; text: string } | undefined;
|
|
@@ -759,6 +772,13 @@ export async function buildTelegramPromptTurn(
|
|
|
759
772
|
};
|
|
760
773
|
}
|
|
761
774
|
|
|
775
|
+
export async function buildTelegramPromptTurn(
|
|
776
|
+
options: BuildTelegramPromptTurnOptions,
|
|
777
|
+
): Promise<PendingTelegramTurn> {
|
|
778
|
+
const buildTurn = await prepareTelegramPromptTurn(options);
|
|
779
|
+
return buildTurn(options.queueOrder, options.historyTurns ?? []);
|
|
780
|
+
}
|
|
781
|
+
|
|
762
782
|
export async function buildTelegramPromptTurnRuntime(
|
|
763
783
|
options: BuildTelegramPromptTurnRuntimeOptions,
|
|
764
784
|
): Promise<PendingTelegramTurn> {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import type {
|
|
15
15
|
TelegramBusEnvelope,
|
|
16
16
|
TelegramBusFollowerView,
|
|
17
|
+
TelegramBusForwardOwnership,
|
|
17
18
|
TelegramBusForeignUpdateSettlement,
|
|
18
19
|
TelegramProcessLiveness,
|
|
19
20
|
} from "./bus.ts";
|
|
@@ -345,22 +346,14 @@ export function getAuthorizedTelegramGuestMessage(
|
|
|
345
346
|
|
|
346
347
|
// --- Flow ---
|
|
347
348
|
|
|
348
|
-
export
|
|
349
|
-
instanceId: string;
|
|
350
|
-
ownerGeneration?: string;
|
|
351
|
-
recipientBindingKey?: string;
|
|
352
|
-
}
|
|
349
|
+
export type TelegramMessageOwnershipView = TelegramBusForwardOwnership;
|
|
353
350
|
|
|
354
351
|
export type TelegramMessageOwnershipLookup = (
|
|
355
352
|
chatId: number,
|
|
356
353
|
messageId: number,
|
|
357
354
|
) => TelegramMessageOwnershipView | undefined;
|
|
358
355
|
|
|
359
|
-
export
|
|
360
|
-
instanceId: string;
|
|
361
|
-
ownerGeneration?: string;
|
|
362
|
-
recipientBindingKey?: string;
|
|
363
|
-
}
|
|
356
|
+
export type TelegramTargetOwnershipView = TelegramBusForwardOwnership;
|
|
364
357
|
|
|
365
358
|
export type TelegramTargetOwnershipLookup = (
|
|
366
359
|
target: TelegramTarget,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@llblab/pi-codex-usage": "0.9.4",
|
|
46
46
|
"@llblab/pi-grow-loop": "0.8.1",
|
|
47
47
|
"@llblab/pi-state-flow": "0.9.6",
|
|
48
|
-
"@llblab/pi-telegram": "0.45.
|
|
48
|
+
"@llblab/pi-telegram": "0.45.8",
|
|
49
49
|
"@llblab/skills": "1.15.0"
|
|
50
50
|
},
|
|
51
51
|
"bundledDependencies": [
|