@llblab/pi-kit 0.1.6 → 0.1.7

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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  All notable changes to `@llblab/pi-kit` are documented here.
4
4
 
5
+ ## 0.1.7 - 2026-08-22
6
+
7
+ - `Telegram Hotfix`: Advances `@llblab/pi-telegram` to `0.36.11`, bringing at-most-once durable prompt dispatch that prevents session or process replacement from automatically replaying an already-admitted Telegram prompt.
8
+
5
9
  ## 0.1.6 - 2026-08-22
6
10
 
7
11
  - `Extension Releases`: Advances `@llblab/pi-actors` to `0.50.0` and `@llblab/pi-telegram` to `0.36.10`, bringing their latest released runtime, Skill, and Telegram integration updates into the bundled kit.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  | `@llblab/pi-actors` | `0.50.0` | Extension and Skills |
10
10
  | `@llblab/pi-codex-usage` | `0.9.3` | Extension |
11
11
  | `@llblab/pi-grow-loop` | `0.7.2` | Extension and Skills |
12
- | `@llblab/pi-telegram` | `0.36.10` | Extension and Skills |
12
+ | `@llblab/pi-telegram` | `0.36.11` | Extension and Skills |
13
13
 
14
14
  Versions are exact by design. Updating an extension does not change an installed kit until this repository explicitly advances that dependency and publishes a new kit version.
15
15
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Each release keeps at most 8 outcome records of at most 512 characters.
4
4
 
5
+ ## 0.36.11: At-Most-Once Prompt Dispatch Hotfix
6
+
7
+ - `At-Most-Once Prompt Dispatch`: Commits each exact durable Telegram receipt synchronously before Pi model admission and blocks dispatch when that commit fails, closing the session/process-replacement replay window that could deliver an already-processed old prompt again; the narrow commit-before-admission crash boundary now favors no duplicate over retry.
8
+
5
9
  ## 0.36.10: Transport-Fenced Typing Hotfix
6
10
 
7
11
  - `Typing Authority Fence`: Starts and continues Telegram typing activity only while the Pi instance has direct ownership or a live follower registration, stopping quietly when authority disappears so classic takeover cannot re-arm a stale loop or flood diagnostics with expected follower-registration errors.
@@ -198,7 +198,7 @@ All inbound updates are gated by the configured authorized user id.
198
198
  5. Coalesce media groups, likely split long text, and one adjacent forward-plus-comment pair in either order when needed.
199
199
  6. Download files with size limits and partial-download cleanup, then run configured/programmatic inbound handlers.
200
200
  7. Build a prompt or control queue item carrying an exact durable receipt for every contributing update id.
201
- 8. Remove journal authority only after prompt handoff, control settlement, or durable follower acknowledgement; execution failures remain durable until automatic replay succeeds.
201
+ 8. Remove local prompt journal authority synchronously immediately before `sendUserMessage`, so session/process replacement can lose an unstarted prompt at that narrow crash boundary but can never replay a prompt already admitted to Pi; controls and foreign forwarding retain their explicit settlement boundaries.
202
202
  9. Handle `edited_message` updates separately while the original turn is still queued and dispatch only when all safety gates are clear.
203
203
 
204
204
  #### Durable Admission And Recovery
@@ -286,7 +286,7 @@ Dispatch requires:
286
286
 
287
287
  A dispatched prompt remains queued until `agent_start` consumes it. This keeps the active Telegram turn bound for previews, attachments, aborts, and final replies. A low-level `agent_end` error also retains that active turn because Pi may retry automatically; a later successful `agent_end` delivers through the original target and metadata, while `agent_settled` proves that an unrecovered error can be finalized once before queue dispatch resumes.
288
288
 
289
- Post-agent-end queue dispatch uses a session-bound deferred dispatcher. It is activated on session start, clears timers on shutdown, and skips callbacks from older generations before touching `ExtensionContext`. Dispatch stays session-bound after polling ownership moves elsewhere. When a queued Telegram prompt is forwarded into Pi, it uses a normal `sendUserMessage(content)` turn after the bridge's idle/dispatch guards pass; it does not use Pi's `followUp` delivery option or inject terminal input.
289
+ Post-agent-end queue dispatch uses a session-bound deferred dispatcher. It is activated on session start, clears timers on shutdown, and skips callbacks from older generations before touching `ExtensionContext`. Dispatch stays session-bound after polling ownership moves elsewhere. When a queued Telegram prompt is forwarded into Pi, the bridge synchronously commits its exact durable receipt before calling normal `sendUserMessage(content)`; failed receipt commitment blocks dispatch, while the unavoidable crash window between commitment and Pi admission favors at-most-once execution over replay. It does not use Pi's `followUp` delivery option or inject terminal input.
290
290
 
291
291
  One monotonic session generation also fences agent/tool/message events, compaction callbacks, preview state, scheduled final delivery, controls, and shutdown. Distinct Pi context objects observed within one session adopt that generation; contexts already observed under an older generation remain stale after replacement. Session start invalidates pending preview work, delayed finals check their captured context before delivery, and shutdown rechecks after asynchronous polling/preview boundaries with a bounded preview-clear wait.
292
292
 
@@ -68,6 +68,10 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
68
68
  ctx: TContext,
69
69
  ) => void;
70
70
  isItemReady: (item: Queue.TelegramQueueItem<TContext>) => boolean;
71
+ onPromptHandedOff?: (
72
+ item: Queue.PendingTelegramTurn,
73
+ ctx: TContext,
74
+ ) => void;
71
75
  onControlSettled: (
72
76
  item: Queue.PendingTelegramControlItem<TContext>,
73
77
  ctx: TContext,
@@ -122,6 +126,13 @@ export function createTelegramQueueBindingRuntime<TContext>(deps: {
122
126
  (item.admissionReceipts?.length ?? 0) === 0
123
127
  );
124
128
  },
129
+ commitPromptDispatch(item, ctx) {
130
+ if ((item.admissionReceipts?.length ?? 0) === 0) return true;
131
+ const settlement = deps.admission.getSettlement();
132
+ if (!settlement?.onPromptHandedOff) return false;
133
+ settlement.onPromptHandedOff(item, ctx);
134
+ return !settlement.isItemReady(item);
135
+ },
125
136
  onControlSettled(item, ctx) {
126
137
  deps.admission.getSettlement()?.onControlSettled(item, ctx);
127
138
  },
@@ -2836,6 +2836,9 @@ export interface TelegramDispatchRuntimeDeps<TContext = unknown> {
2836
2836
  >["item"],
2837
2837
  ) => void;
2838
2838
  onPromptDispatchStart: (chatId: number) => void;
2839
+ commitPromptDispatch?: (
2840
+ item: Extract<TelegramQueueDispatchAction<TContext>, { kind: "prompt" }>["item"],
2841
+ ) => boolean;
2839
2842
  sendUserMessage: (
2840
2843
  content: Extract<
2841
2844
  TelegramQueueDispatchAction,
@@ -2859,6 +2862,10 @@ export interface TelegramQueueDispatchControllerDeps<
2859
2862
  updateStatus: (ctx: TContext, error?: string) => void;
2860
2863
  sendTextReply: TelegramControlRuntimeDeps<TContext>["sendTextReply"];
2861
2864
  onPromptDispatchStart: (ctx: TContext, chatId: number) => void;
2865
+ commitPromptDispatch?: (
2866
+ item: Extract<TelegramQueueDispatchAction<TContext>, { kind: "prompt" }>["item"],
2867
+ ctx: TContext,
2868
+ ) => boolean;
2862
2869
  sendUserMessage: TelegramDispatchRuntimeDeps<TContext>["sendUserMessage"];
2863
2870
  onPromptDispatchFailure: (ctx: TContext, message: string) => void;
2864
2871
  isQueueItemTransportActive?: (item: TelegramQueueItem<TContext>) => boolean;
@@ -2890,6 +2897,9 @@ export function executeTelegramQueueDispatchPlan<TContext = unknown>(
2890
2897
  }
2891
2898
  deps.onPromptDispatchStart(plan.item.chatId);
2892
2899
  try {
2900
+ if (deps.commitPromptDispatch && !deps.commitPromptDispatch(plan.item)) {
2901
+ throw new Error("Telegram prompt dispatch could not be committed durably.");
2902
+ }
2893
2903
  deps.sendUserMessage(plan.item.content);
2894
2904
  } catch (error) {
2895
2905
  const message = getTelegramQueueErrorMessage(error);
@@ -2922,6 +2932,7 @@ export function createTelegramQueueDispatchRuntime<TContext = unknown>(
2922
2932
  updateStatus: deps.updateStatus,
2923
2933
  sendTextReply: deps.sendTextReply,
2924
2934
  onPromptDispatchStart: deps.onPromptDispatchStart,
2935
+ commitPromptDispatch: deps.commitPromptDispatch,
2925
2936
  sendUserMessage: deps.sendUserMessage,
2926
2937
  onPromptDispatchFailure: deps.onPromptDispatchFailure,
2927
2938
  isQueueItemTransportActive: deps.isQueueItemTransportActive,
@@ -3061,6 +3072,9 @@ export function createTelegramQueueDispatchController<TContext = unknown>(
3061
3072
  onPromptDispatchStart: (chatId) => {
3062
3073
  deps.onPromptDispatchStart(ctx, chatId);
3063
3074
  },
3075
+ commitPromptDispatch: deps.commitPromptDispatch
3076
+ ? (item) => deps.commitPromptDispatch!(item, ctx)
3077
+ : undefined,
3064
3078
  sendUserMessage: deps.sendUserMessage,
3065
3079
  onPromptDispatchFailure: (message) => {
3066
3080
  deps.onPromptDispatchFailure(ctx, message);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.36.10",
3
+ "version": "0.36.11",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-kit",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -43,7 +43,7 @@
43
43
  "@llblab/pi-actors": "0.50.0",
44
44
  "@llblab/pi-codex-usage": "0.9.3",
45
45
  "@llblab/pi-grow-loop": "0.7.2",
46
- "@llblab/pi-telegram": "0.36.10"
46
+ "@llblab/pi-telegram": "0.36.11"
47
47
  },
48
48
  "bundledDependencies": [
49
49
  "@llblab/pi-actors",