@llblab/pi-kit 0.1.11 → 0.1.13
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 +9 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-codex-usage/BACKLOG.md +1 -6
- package/node_modules/@llblab/pi-codex-usage/CHANGELOG.md +4 -0
- package/node_modules/@llblab/pi-codex-usage/README.md +1 -1
- package/node_modules/@llblab/pi-codex-usage/index.ts +1 -1
- package/node_modules/@llblab/pi-codex-usage/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +12 -0
- package/node_modules/@llblab/pi-telegram/docs/activity.md +9 -2
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -2
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +29 -0
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +87 -9
- package/node_modules/@llblab/pi-telegram/lib/inbound.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +50 -0
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +15 -0
- package/node_modules/@llblab/pi-telegram/package.json +4 -4
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@llblab/pi-kit` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.1.13 - 2026-08-30
|
|
6
|
+
|
|
7
|
+
- `Telegram Minor Release`: Advances `@llblab/pi-telegram` to `0.40.0`, requiring Pi 0.84.4 and adopting native compaction-failure, mid-run compaction, terminal-message, and local UI-prompt lifecycle contracts while increasing bounded inbound text output to 24,000 characters.
|
|
8
|
+
|
|
9
|
+
## 0.1.12 - 2026-08-28
|
|
10
|
+
|
|
11
|
+
- `Codex Usage Release`: Advances `@llblab/pi-codex-usage` to `0.9.4`, reducing automatic OpenAI quota polling by increasing the refresh interval from 30 to 60 seconds while preserving the local countdown responsiveness.
|
|
12
|
+
- `Telegram Hotfix`: Advances `@llblab/pi-telegram` to `0.39.5`, preserving terminal-equivalent chronology by delivering an active turn's final Telegram answer before automatic-compaction start and completion notices.
|
|
13
|
+
|
|
5
14
|
## 0.1.11 - 2026-08-28
|
|
6
15
|
|
|
7
16
|
- `Telegram Hotfix`: Advances `@llblab/pi-telegram` to `0.39.4`, preventing repeated extension errors when print, RPC, ACP, or other headless hosts refresh lifecycle status without an initialized theme while preserving interactive status rendering.
|
package/README.md
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
| Package | Version | Resources |
|
|
8
8
|
| --- | ---: | --- |
|
|
9
9
|
| `@llblab/pi-actors` | `0.50.0` | Extension and Skills |
|
|
10
|
-
| `@llblab/pi-codex-usage` | `0.9.
|
|
10
|
+
| `@llblab/pi-codex-usage` | `0.9.4` | Extension |
|
|
11
11
|
| `@llblab/pi-grow-loop` | `0.7.3` | Extension and Skills |
|
|
12
|
-
| `@llblab/pi-telegram` | `0.
|
|
12
|
+
| `@llblab/pi-telegram` | `0.40.0` | 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
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
# Backlog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- [x] Fix stale extension context usage after session replacement or reload. Source: GitHub issue #1. Timers must not keep using captured stale `ctx` after Pi session lifecycle changes.
|
|
6
|
-
- [x] Make usage refresh cache/in-flight state model-aware for Codex vs Spark. A regular Codex cache or request must not temporarily render Spark as `n/a` after model switching.
|
|
7
|
-
- [x] Preserve the active `codex`/`spark` label for failure states. Spark failures should render `spark n/a` or `spark error`, not `codex ...`.
|
|
8
|
-
- [x] Replace the visually full startup/request placeholder with counter-moving upper/lower in-bar loading markers. Keep the last usable active-bucket bar visible during ordinary refreshes, and hold provisional zero-usage reports behind the loader until confirmed stable.
|
|
3
|
+
No open work.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.4: Responsive Quota Polling
|
|
4
|
+
|
|
5
|
+
- Increased the automatic Codex quota refresh interval from 30 to 60 seconds. Impact: status values remain responsive down to the locally rendered seconds countdown while reducing repeated OpenAI requests, especially when several Pi instances are open.
|
|
6
|
+
|
|
3
7
|
## 0.9.3: Trusted Release Automation
|
|
4
8
|
|
|
5
9
|
- Added tag-gated release automation with exact package/lock/tag identity checks, full package validation, npm Trusted Publisher provenance, public registry verification, and workflow-owned GitHub Release creation through `.github/workflows/release.yml`.
|
|
@@ -14,7 +14,7 @@ This repository is a minimal fork of [`narumiruna/pi-extensions/extensions/pi-co
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
|
-
- Shows two counter-moving half-height markers in the statusline bar while the active Codex/Spark quota bucket is loading, then refreshes every
|
|
17
|
+
- Shows two counter-moving half-height markers in the statusline bar while the active Codex/Spark quota bucket is loading, then refreshes every 60 seconds
|
|
18
18
|
- Keeps the last usable active-bucket bar visible during ordinary refreshes instead of replacing known quota with a loading state
|
|
19
19
|
- Statusline output adapts to the response: weekly-only limits show an explicit remaining percentage and reset countdown, while dual-window limits keep the compact themed bar
|
|
20
20
|
- Regular Codex subscription models show the primary `codex` quota bucket
|
|
@@ -16,7 +16,7 @@ const HOUR_MS = 60 * MINUTE_MS;
|
|
|
16
16
|
const HOUR_TENTH_MS = 6 * MINUTE_MS;
|
|
17
17
|
const DAY_MS = 24 * HOUR_MS;
|
|
18
18
|
const DAY_TENTH_MS = 144 * MINUTE_MS;
|
|
19
|
-
const REFRESH_INTERVAL_MS =
|
|
19
|
+
const REFRESH_INTERVAL_MS = 60 * SECOND_MS;
|
|
20
20
|
const PROVISIONAL_RETRY_MS = SECOND_MS;
|
|
21
21
|
const FULL_AVAILABILITY_CONFIRMATION_MS = 15 * SECOND_MS;
|
|
22
22
|
const LOADING_FRAME_MS = 30;
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## 0.40.0: Pi 0.84.4 Lifecycle Alignment
|
|
6
|
+
|
|
7
|
+
- `Pi Compatibility`: Requires `@earendil-works/pi-coding-agent`, `pi-agent-core`, and `pi-ai` 0.84.4 or newer, aligning the bridge and its validation runtime with native compaction-failure, mid-run compaction, message-finalization, and UI-prompt lifecycle contracts.
|
|
8
|
+
- `Compaction Failure`: Handles `session_compact_failed` immediately by clearing compacting state, stopping observer-owned typing, releasing deferred queue work, abandoning stale activity, and reporting automatic failure or cancellation without duplicating manual `/compact` errors.
|
|
9
|
+
- `Compaction Chronology`: Distinguishes terminal assistant output awaiting Telegram delivery from mid-run tool continuation. Post-answer notices remain ordered after the final reply, while tool-result threshold compaction is reported in place before the next assistant response.
|
|
10
|
+
- `Local UI Waiting`: Pauses Telegram typing while Pi waits on extension-owned local select, confirm, input, editor, or custom prompts and emits coalesced `ui-prompt-start` / `ui-prompt-end` Activity boundaries before resuming active-turn typing.
|
|
11
|
+
- `Inbound Context`: Raises successful inbound handler and built-in text attachment output from 12,000 to 24,000 characters while retaining the 1 MB built-in file admission ceiling and explicit truncation evidence.
|
|
12
|
+
|
|
13
|
+
## 0.39.5: Auto-Compaction Notice Chronology Hotfix
|
|
14
|
+
|
|
15
|
+
- `Turn Chronology`: Defers observed automatic-compaction notices while a Telegram turn is still active, preserving the same causal order shown by the terminal: final answer, compaction started, then compaction completed. Abandoned compactions and session shutdown discard deferred notices instead of leaking them into a later turn.
|
|
16
|
+
|
|
5
17
|
## 0.39.4: Headless Status Bar Hotfix
|
|
6
18
|
|
|
7
19
|
- `Headless Hosts`: Skips status-bar rendering when a print, RPC, ACP, or other non-interactive host has not initialized its theme, preventing repeated extension errors during lifecycle status refreshes while preserving normal interactive status updates and error propagation.
|
|
@@ -96,7 +96,7 @@ Source classification follows evidence, not guesses:
|
|
|
96
96
|
|
|
97
97
|
Automatic retries, overflow compaction retries, and tool continuations inherit the current activity identity/source until `agent_settled`. A new unrelated `agent_start` after settlement allocates a new activity id.
|
|
98
98
|
|
|
99
|
-
A standalone compaction owns a temporary activity
|
|
99
|
+
A standalone compaction owns a temporary activity until `session_compact` or `session_compact_failed`. Native failure or cancellation immediately abandons that identity, clears compacting state, releases deferred queue work, and stops observer-owned typing; the five-minute observer timeout remains a compatibility fallback for a host that never emits a terminal compaction event. A late `session_compact` after abandonment is ignored and cannot attach to the next run. Compaction inside an existing agent activity never clears that agent's identity.
|
|
100
100
|
|
|
101
101
|
## Event Contract
|
|
102
102
|
|
|
@@ -151,6 +151,12 @@ export type TelegramActivityEvent = TelegramActivityEnvelope & (
|
|
|
151
151
|
type: "compaction-end";
|
|
152
152
|
reason: "manual" | "threshold" | "overflow" | "unknown";
|
|
153
153
|
}
|
|
154
|
+
| {
|
|
155
|
+
type: "ui-prompt-start";
|
|
156
|
+
kind: "select" | "confirm" | "input" | "editor" | "custom";
|
|
157
|
+
title?: string;
|
|
158
|
+
}
|
|
159
|
+
| { type: "ui-prompt-end" }
|
|
154
160
|
| { type: "agent-end" }
|
|
155
161
|
| { type: "agent-settled" }
|
|
156
162
|
);
|
|
@@ -250,7 +256,8 @@ The bridge maps Pi hooks as follows:
|
|
|
250
256
|
- `agent_start`: allocate or reuse activity identity and emit `agent-start` after Telegram queue consumption establishes active-turn ownership.
|
|
251
257
|
- `message_update.assistantMessageEvent`: normalize text/reasoning/provider boundaries.
|
|
252
258
|
- `tool_execution_start/update/end`: emit executed tool events.
|
|
253
|
-
- `session_before_compact` / `session_compact`: emit compaction boundaries and preserve activity identity across retry compaction
|
|
259
|
+
- `session_before_compact` / `session_compact` / `session_compact_failed`: emit successful compaction boundaries, abandon failed or cancelled work immediately, and preserve activity identity across retry compaction. Mid-run threshold compaction stays between tool results and the next assistant response, while terminal assistant output awaiting transport keeps later notices behind its final reply. A missing or unrecognized reason maps to `unknown` rather than guessing.
|
|
260
|
+
- `ui_prompt_start` / `ui_prompt_end`: emit one coalesced waiting span for extension-owned local UI, pause Telegram typing while Pi waits for the operator, and resume active-turn typing when the prompt closes.
|
|
254
261
|
- `agent_end`: emit low-level run completion but keep identity alive for retry/follow-up work.
|
|
255
262
|
- `agent_settled`: emit terminal settlement, flush pending terminal segments, and release activity identity.
|
|
256
263
|
- `session_shutdown`: stop dispatch, clear pending normalization state, and invalidate delivery generation through the existing delivery lifecycle.
|
|
@@ -315,14 +315,15 @@ Queue and menu mutations are reachable through Telegram updates handled by the c
|
|
|
315
315
|
|
|
316
316
|
### Compaction And Typing Status
|
|
317
317
|
|
|
318
|
-
Manual `/compact` requires inline confirmation because accidental taps are disruptive. Confirmed manual compaction and auto-compaction both set the bridge compaction flag, block queued prompt dispatch, retain that flag in explicit diagnostics, and clear it on compact completion, timeout fallback, or session shutdown. Pi owns its terminal compaction lifecycle; pi-telegram keeps `Active` scoped to Telegram-owned work and otherwise preserves the stable connected/leader/follower role.
|
|
318
|
+
Manual `/compact` requires inline confirmation because accidental taps are disruptive. Confirmed manual compaction and auto-compaction both set the bridge compaction flag, block queued prompt dispatch, retain that flag in explicit diagnostics, and clear it on native compact completion or failure, timeout fallback, or session shutdown. Pi owns its terminal compaction lifecycle; pi-telegram keeps `Active` scoped to Telegram-owned work and otherwise preserves the stable connected/leader/follower role. Mid-run threshold compaction reports notices in place between tool output and the next assistant response; compaction observed after terminal assistant output waits for final Telegram delivery so transport chronology matches the terminal.
|
|
319
319
|
|
|
320
320
|
Native typing during compaction follows connected-instance activity rather than terminal status:
|
|
321
321
|
|
|
322
322
|
- Confirmed manual `/compact` starts a native `typing` keepalive in the command target and stops it on completion/failure.
|
|
323
323
|
- Automatic/session compaction with an active Telegram turn reuses that turn's target.
|
|
324
324
|
- Automatic/session compaction without an active Telegram turn uses the connected instance's assigned target; an unconnected instance sends nothing.
|
|
325
|
-
- Thread-targeted typing is sent to the concrete thread and mirrored to `All` as the aggregate activity surface; completion, timeout, and shutdown stop the keyed loop.
|
|
325
|
+
- Thread-targeted typing is sent to the concrete thread and mirrored to `All` as the aggregate activity surface; completion, native failure, timeout, and shutdown stop the keyed loop.
|
|
326
|
+
- Pi `ui_prompt_start` pauses active-turn typing while an extension-owned local prompt waits for the operator; `ui_prompt_end` emits the matching Activity boundary and resumes typing.
|
|
326
327
|
|
|
327
328
|
At every connected instance `agent_start`, the lifecycle binding starts Telegram's native `…typing` indicator in that instance's assigned target, whether the run came from Telegram, the local TUI, or an autonomous continuation such as Grow Loop. Terminal `Active` remains Telegram-turn-specific; the native indicator answers the separate question of whether the instance is doing agent work. Each loop keeps one action in flight, while the leader API runtime coalesces identical chat/thread/action calls across local and follower traffic for two seconds; expired gates prune opportunistically and at most 256 currently active keys are retained. A Telegram 429 response opens the exact action's shared `retry_after` suppression window without scheduling delayed retries or projecting expected activity throttling as a terminal status error. Assistant message start/update hooks still re-arm it during Telegram-owned turns so transient provider/model errors do not leave a continuing run without activity feedback, and agent/session completion stops it.
|
|
328
329
|
|
|
@@ -85,6 +85,12 @@ export type TelegramActivityPayload =
|
|
|
85
85
|
type: "compaction-end";
|
|
86
86
|
reason: "manual" | "threshold" | "overflow" | "unknown";
|
|
87
87
|
}
|
|
88
|
+
| {
|
|
89
|
+
type: "ui-prompt-start";
|
|
90
|
+
kind: "select" | "confirm" | "input" | "editor" | "custom";
|
|
91
|
+
title?: string;
|
|
92
|
+
}
|
|
93
|
+
| { type: "ui-prompt-end" }
|
|
88
94
|
| { type: "agent-end" }
|
|
89
95
|
| { type: "agent-settled" };
|
|
90
96
|
|
|
@@ -440,6 +446,12 @@ export function createTelegramActivityBridgeRuntime(deps: {
|
|
|
440
446
|
onCompactionAbandoned() {
|
|
441
447
|
getRuntime()?.onCompactionAbandoned();
|
|
442
448
|
},
|
|
449
|
+
onUiPromptStart(kind, title) {
|
|
450
|
+
getRuntime()?.onUiPromptStart(kind, title);
|
|
451
|
+
},
|
|
452
|
+
onUiPromptEnd() {
|
|
453
|
+
getRuntime()?.onUiPromptEnd();
|
|
454
|
+
},
|
|
443
455
|
onAgentEnd() {
|
|
444
456
|
getRuntime()?.onAgentEnd();
|
|
445
457
|
},
|
|
@@ -502,6 +514,11 @@ export interface TelegramActivityRuntime {
|
|
|
502
514
|
reason: "manual" | "threshold" | "overflow" | "unknown",
|
|
503
515
|
) => void;
|
|
504
516
|
onCompactionAbandoned: () => void;
|
|
517
|
+
onUiPromptStart: (
|
|
518
|
+
kind: "select" | "confirm" | "input" | "editor" | "custom",
|
|
519
|
+
title?: string,
|
|
520
|
+
) => void;
|
|
521
|
+
onUiPromptEnd: () => void;
|
|
505
522
|
onAgentEnd: () => void;
|
|
506
523
|
onAgentSettled: () => void;
|
|
507
524
|
onSessionShutdown: () => void;
|
|
@@ -533,6 +550,7 @@ export function createTelegramActivityRuntime(deps: {
|
|
|
533
550
|
let pendingAssistantSegment: PendingAssistantSegment | undefined;
|
|
534
551
|
let compactionInProgress = false;
|
|
535
552
|
let compactionOwnedActivity = false;
|
|
553
|
+
let uiPromptInProgress = false;
|
|
536
554
|
const ensureActivity = (
|
|
537
555
|
activeTelegramTarget?: TelegramActivityTarget,
|
|
538
556
|
): string => {
|
|
@@ -592,6 +610,7 @@ export function createTelegramActivityRuntime(deps: {
|
|
|
592
610
|
pendingAssistantSegment = undefined;
|
|
593
611
|
compactionInProgress = false;
|
|
594
612
|
compactionOwnedActivity = false;
|
|
613
|
+
uiPromptInProgress = false;
|
|
595
614
|
};
|
|
596
615
|
const abandonCompaction = (): void => {
|
|
597
616
|
if (!compactionInProgress) return;
|
|
@@ -685,6 +704,16 @@ export function createTelegramActivityRuntime(deps: {
|
|
|
685
704
|
onCompactionAbandoned() {
|
|
686
705
|
abandonCompaction();
|
|
687
706
|
},
|
|
707
|
+
onUiPromptStart(kind, title) {
|
|
708
|
+
if (!activityId || uiPromptInProgress) return;
|
|
709
|
+
uiPromptInProgress = true;
|
|
710
|
+
emit({ type: "ui-prompt-start", kind, title });
|
|
711
|
+
},
|
|
712
|
+
onUiPromptEnd() {
|
|
713
|
+
if (!activityId || !uiPromptInProgress) return;
|
|
714
|
+
uiPromptInProgress = false;
|
|
715
|
+
emit({ type: "ui-prompt-end" });
|
|
716
|
+
},
|
|
688
717
|
onAgentEnd() {
|
|
689
718
|
if (activityId) emit({ type: "agent-end" });
|
|
690
719
|
},
|
|
@@ -1012,8 +1012,9 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1012
1012
|
const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(
|
|
1013
1013
|
agentLifecycleHooks.onAgentStart,
|
|
1014
1014
|
);
|
|
1015
|
+
let uiPromptActive = false;
|
|
1015
1016
|
const startAgentActivityTypingLoop = (ctx: Pi.ExtensionContext): boolean => {
|
|
1016
|
-
if (!canSendAgentActivity(ctx)) return false;
|
|
1017
|
+
if (uiPromptActive || !canSendAgentActivity(ctx)) return false;
|
|
1017
1018
|
const turn = activeTurnRuntime.get();
|
|
1018
1019
|
const target = turn?.target ?? proactivePushTargetGetter();
|
|
1019
1020
|
promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId ?? target?.chatId, {
|
|
@@ -1022,12 +1023,16 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1022
1023
|
return true;
|
|
1023
1024
|
};
|
|
1024
1025
|
const startActiveTurnTypingLoop = (ctx: Pi.ExtensionContext): void => {
|
|
1026
|
+
if (uiPromptActive) return;
|
|
1025
1027
|
const turn = activeTurnRuntime.get();
|
|
1026
1028
|
promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId, {
|
|
1027
1029
|
target: turn?.target,
|
|
1028
1030
|
});
|
|
1029
1031
|
};
|
|
1030
1032
|
let observedAutomaticCompaction = false;
|
|
1033
|
+
let agentRunActive = false;
|
|
1034
|
+
let terminalAssistantMessagePendingDelivery = false;
|
|
1035
|
+
const deferredAutomaticCompactionNotices: string[] = [];
|
|
1031
1036
|
const sendCompactionNotice = async (text: string): Promise<void> => {
|
|
1032
1037
|
const turn = activeTurnRuntime.get();
|
|
1033
1038
|
const target = turn?.target ?? proactivePushTargetGetter?.();
|
|
@@ -1042,6 +1047,10 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1042
1047
|
});
|
|
1043
1048
|
}
|
|
1044
1049
|
};
|
|
1050
|
+
const flushDeferredAutomaticCompactionNotices = async (): Promise<void> => {
|
|
1051
|
+
const notices = deferredAutomaticCompactionNotices.splice(0);
|
|
1052
|
+
for (const notice of notices) await sendCompactionNotice(notice);
|
|
1053
|
+
};
|
|
1045
1054
|
const compactionObserver = Lifecycle.createTelegramCompactionObserverRuntime({
|
|
1046
1055
|
isContextActive: isSessionContextActive,
|
|
1047
1056
|
setCompactionInProgress: lifecycle.setCompactionInProgress,
|
|
@@ -1054,6 +1063,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1054
1063
|
recordRuntimeEvent,
|
|
1055
1064
|
onCompactionAbandoned: () => {
|
|
1056
1065
|
observedAutomaticCompaction = false;
|
|
1066
|
+
deferredAutomaticCompactionNotices.length = 0;
|
|
1057
1067
|
activityRuntime.onCompactionAbandoned();
|
|
1058
1068
|
},
|
|
1059
1069
|
});
|
|
@@ -1088,6 +1098,10 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1088
1098
|
activityVerbosityRuntime?.reset();
|
|
1089
1099
|
assistantOutputRuntime.stop();
|
|
1090
1100
|
observedAutomaticCompaction = false;
|
|
1101
|
+
agentRunActive = false;
|
|
1102
|
+
terminalAssistantMessagePendingDelivery = false;
|
|
1103
|
+
uiPromptActive = false;
|
|
1104
|
+
deferredAutomaticCompactionNotices.length = 0;
|
|
1091
1105
|
compactionObserver.onSessionShutdown();
|
|
1092
1106
|
if (event.reason === "quit" && disconnectOnQuit) {
|
|
1093
1107
|
try {
|
|
@@ -1109,11 +1123,20 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1109
1123
|
activityRuntime.onCompactionStart(Pi.getSessionCompactionReason(event));
|
|
1110
1124
|
compactionObserver.onSessionBeforeCompact(event, ctx);
|
|
1111
1125
|
if (shouldNotify) {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1126
|
+
if (
|
|
1127
|
+
activeTurnRuntime.has() &&
|
|
1128
|
+
terminalAssistantMessagePendingDelivery
|
|
1129
|
+
) {
|
|
1130
|
+
deferredAutomaticCompactionNotices.push(
|
|
1131
|
+
Commands.TELEGRAM_COMPACTION_STARTED_MARKDOWN,
|
|
1132
|
+
);
|
|
1133
|
+
} else {
|
|
1134
|
+
await waitForActiveTurnDelivery();
|
|
1135
|
+
if (!isSessionContextActive(ctx)) return;
|
|
1136
|
+
await sendCompactionNotice(
|
|
1137
|
+
Commands.TELEGRAM_COMPACTION_STARTED_MARKDOWN,
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1117
1140
|
}
|
|
1118
1141
|
},
|
|
1119
1142
|
async onSessionCompact(event, ctx) {
|
|
@@ -1122,13 +1145,39 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1122
1145
|
compactionObserver.onSessionCompact(event, ctx);
|
|
1123
1146
|
if (observedAutomaticCompaction) {
|
|
1124
1147
|
observedAutomaticCompaction = false;
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1148
|
+
if (deferredAutomaticCompactionNotices.length > 0) {
|
|
1149
|
+
deferredAutomaticCompactionNotices.push(
|
|
1150
|
+
Commands.TELEGRAM_COMPACTION_COMPLETED_MARKDOWN,
|
|
1151
|
+
);
|
|
1152
|
+
} else {
|
|
1153
|
+
await sendCompactionNotice(
|
|
1154
|
+
Commands.TELEGRAM_COMPACTION_COMPLETED_MARKDOWN,
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
async onSessionCompactFailed(event, ctx) {
|
|
1160
|
+
if (!isSessionContextActive(ctx)) return;
|
|
1161
|
+
const shouldNotify = observedAutomaticCompaction;
|
|
1162
|
+
const deferredNotices = deferredAutomaticCompactionNotices.splice(0);
|
|
1163
|
+
const shouldDefer =
|
|
1164
|
+
deferredNotices.length > 0 ||
|
|
1165
|
+
(activeTurnRuntime.has() && terminalAssistantMessagePendingDelivery);
|
|
1166
|
+
compactionObserver.onSessionCompactFailed(event, ctx);
|
|
1167
|
+
if (!shouldNotify) return;
|
|
1168
|
+
const notice = event.aborted
|
|
1169
|
+
? "**⚠️ Compaction cancelled.**"
|
|
1170
|
+
: "**⚠️ Compaction failed.**";
|
|
1171
|
+
if (shouldDefer) {
|
|
1172
|
+
deferredAutomaticCompactionNotices.push(...deferredNotices, notice);
|
|
1173
|
+
} else {
|
|
1174
|
+
await sendCompactionNotice(notice);
|
|
1128
1175
|
}
|
|
1129
1176
|
},
|
|
1130
1177
|
async onAgentStart(event, ctx) {
|
|
1131
1178
|
if (!isSessionContextActive(ctx)) return;
|
|
1179
|
+
agentRunActive = true;
|
|
1180
|
+
terminalAssistantMessagePendingDelivery = false;
|
|
1132
1181
|
await agentStartWithDedupReset(event, ctx);
|
|
1133
1182
|
activityRuntime.onAgentStart(activeTurnRuntime.get()?.target);
|
|
1134
1183
|
startAgentActivityTypingLoop(ctx);
|
|
@@ -1173,10 +1222,39 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
1173
1222
|
}
|
|
1174
1223
|
await messageActivityHooks.onMessageUpdate(event, ctx);
|
|
1175
1224
|
},
|
|
1225
|
+
onMessageEnd(event, ctx) {
|
|
1226
|
+
if (!isSessionContextActive(ctx)) return;
|
|
1227
|
+
terminalAssistantMessagePendingDelivery =
|
|
1228
|
+
event.message.role === "assistant" &&
|
|
1229
|
+
event.message.stopReason !== "toolUse" &&
|
|
1230
|
+
event.message.stopReason !== "error" &&
|
|
1231
|
+
event.message.stopReason !== "aborted";
|
|
1232
|
+
},
|
|
1233
|
+
onUiPromptStart(event, ctx) {
|
|
1234
|
+
if (!isSessionContextActive(ctx)) return;
|
|
1235
|
+
uiPromptActive = true;
|
|
1236
|
+
typing.stop();
|
|
1237
|
+
activityRuntime.onUiPromptStart(event.kind, event.title);
|
|
1238
|
+
updateStatus(ctx);
|
|
1239
|
+
},
|
|
1240
|
+
onUiPromptEnd(_event, ctx) {
|
|
1241
|
+
if (!isSessionContextActive(ctx)) return;
|
|
1242
|
+
uiPromptActive = false;
|
|
1243
|
+
activityRuntime.onUiPromptEnd();
|
|
1244
|
+
if (agentRunActive) startAgentActivityTypingLoop(ctx);
|
|
1245
|
+
updateStatus(ctx);
|
|
1246
|
+
},
|
|
1176
1247
|
async onAgentEnd(event, ctx) {
|
|
1177
1248
|
if (!isSessionContextActive(ctx)) return;
|
|
1249
|
+
agentRunActive = false;
|
|
1178
1250
|
activityRuntime.onAgentEnd();
|
|
1179
1251
|
await agentLifecycleHooks.onAgentEnd(event, ctx);
|
|
1252
|
+
if (deferredAutomaticCompactionNotices.length > 0) {
|
|
1253
|
+
await waitForActiveTurnDelivery();
|
|
1254
|
+
if (!isSessionContextActive(ctx)) return;
|
|
1255
|
+
await flushDeferredAutomaticCompactionNotices();
|
|
1256
|
+
}
|
|
1257
|
+
terminalAssistantMessagePendingDelivery = false;
|
|
1180
1258
|
},
|
|
1181
1259
|
async onAgentSettled(event, ctx) {
|
|
1182
1260
|
if (!isSessionContextActive(ctx)) return;
|
|
@@ -19,7 +19,7 @@ import { getTelegramVoiceTranscriptionProviders } from "./voice.ts";
|
|
|
19
19
|
|
|
20
20
|
const DEFAULT_INBOUND_HANDLER_TIMEOUT_MS = 120_000;
|
|
21
21
|
const INBOUND_HANDLER_REGISTRY_KEY = "__piTelegramInboundHandlers__";
|
|
22
|
-
const MAX_INBOUND_HANDLER_OUTPUT_LENGTH =
|
|
22
|
+
const MAX_INBOUND_HANDLER_OUTPUT_LENGTH = 24_000;
|
|
23
23
|
const MAX_INBOUND_HANDLER_FAILURE_STREAM_LENGTH = 4_000;
|
|
24
24
|
|
|
25
25
|
type TelegramInboundCommandTemplateConfig =
|
|
@@ -16,13 +16,17 @@ import type {
|
|
|
16
16
|
ExtensionAPI,
|
|
17
17
|
ExtensionContext,
|
|
18
18
|
InputEvent,
|
|
19
|
+
MessageEndEvent,
|
|
19
20
|
SessionBeforeCompactEvent,
|
|
20
21
|
SessionCompactEvent,
|
|
22
|
+
SessionCompactFailedEvent,
|
|
21
23
|
SessionShutdownEvent,
|
|
22
24
|
SessionStartEvent,
|
|
23
25
|
ToolExecutionEndEvent,
|
|
24
26
|
ToolExecutionStartEvent,
|
|
25
27
|
ToolExecutionUpdateEvent,
|
|
28
|
+
UIPromptEndEvent,
|
|
29
|
+
UIPromptStartEvent,
|
|
26
30
|
} from "./pi.ts";
|
|
27
31
|
|
|
28
32
|
let resetTransportReplyDedupFn: (() => void) | undefined;
|
|
@@ -78,6 +82,10 @@ export interface TelegramLifecycleRegistrationDeps {
|
|
|
78
82
|
event: SessionCompactEvent,
|
|
79
83
|
ctx: ExtensionContext,
|
|
80
84
|
) => Promise<void> | void;
|
|
85
|
+
onSessionCompactFailed?: (
|
|
86
|
+
event: SessionCompactFailedEvent,
|
|
87
|
+
ctx: ExtensionContext,
|
|
88
|
+
) => Promise<void> | void;
|
|
81
89
|
onBeforeAgentStart: (
|
|
82
90
|
event: TelegramBeforeAgentStartEvent,
|
|
83
91
|
ctx: ExtensionContext,
|
|
@@ -113,6 +121,18 @@ export interface TelegramLifecycleRegistrationDeps {
|
|
|
113
121
|
},
|
|
114
122
|
ctx: ExtensionContext,
|
|
115
123
|
) => Promise<void>;
|
|
124
|
+
onMessageEnd?: (
|
|
125
|
+
event: MessageEndEvent,
|
|
126
|
+
ctx: ExtensionContext,
|
|
127
|
+
) => Promise<void> | void;
|
|
128
|
+
onUiPromptStart?: (
|
|
129
|
+
event: UIPromptStartEvent,
|
|
130
|
+
ctx: ExtensionContext,
|
|
131
|
+
) => Promise<void> | void;
|
|
132
|
+
onUiPromptEnd?: (
|
|
133
|
+
event: UIPromptEndEvent,
|
|
134
|
+
ctx: ExtensionContext,
|
|
135
|
+
) => Promise<void> | void;
|
|
116
136
|
onAgentEnd: (event: AgentEndEvent, ctx: ExtensionContext) => Promise<void>;
|
|
117
137
|
onAgentSettled?: (
|
|
118
138
|
event: AgentSettledEvent,
|
|
@@ -416,6 +436,10 @@ export interface TelegramCompactionObserverRuntime<TContext> {
|
|
|
416
436
|
ctx: TContext,
|
|
417
437
|
) => void;
|
|
418
438
|
onSessionCompact: (event: SessionCompactEvent, ctx: TContext) => void;
|
|
439
|
+
onSessionCompactFailed: (
|
|
440
|
+
event: SessionCompactFailedEvent,
|
|
441
|
+
ctx: TContext,
|
|
442
|
+
) => void;
|
|
419
443
|
onSessionShutdown: () => void;
|
|
420
444
|
}
|
|
421
445
|
|
|
@@ -471,6 +495,16 @@ export function createTelegramCompactionObserverRuntime<TContext>(
|
|
|
471
495
|
deps.updateStatus(ctx);
|
|
472
496
|
requestDispatch();
|
|
473
497
|
},
|
|
498
|
+
onSessionCompactFailed: (_event, ctx) => {
|
|
499
|
+
clearFallbackTimer();
|
|
500
|
+
if (deps.isContextActive && !deps.isContextActive(ctx)) return;
|
|
501
|
+
deps.setCompactionInProgress(false);
|
|
502
|
+
if (typingStartedByObserver) deps.stopTypingLoop?.();
|
|
503
|
+
typingStartedByObserver = false;
|
|
504
|
+
deps.updateStatus(ctx);
|
|
505
|
+
deps.onCompactionAbandoned?.();
|
|
506
|
+
requestDispatch();
|
|
507
|
+
},
|
|
474
508
|
onSessionShutdown: () => {
|
|
475
509
|
clearFallbackTimer();
|
|
476
510
|
if (typingStartedByObserver) deps.stopTypingLoop?.();
|
|
@@ -590,6 +624,10 @@ export function registerTelegramLifecycleHooks(
|
|
|
590
624
|
if (!isActive(ctx)) return;
|
|
591
625
|
await deps.onSessionCompact?.(event, ctx);
|
|
592
626
|
});
|
|
627
|
+
pi.on("session_compact_failed", async (event, ctx) => {
|
|
628
|
+
if (!isActive(ctx)) return;
|
|
629
|
+
await deps.onSessionCompactFailed?.(event, ctx);
|
|
630
|
+
});
|
|
593
631
|
// The Pi SDK still types this result as a string; compatible runtimes may
|
|
594
632
|
// preserve ordered system prompt blocks through the same public hook.
|
|
595
633
|
const registerBeforeAgentStart = pi.on.bind(pi) as unknown as (
|
|
@@ -630,6 +668,18 @@ export function registerTelegramLifecycleHooks(
|
|
|
630
668
|
if (!isActive(ctx)) return;
|
|
631
669
|
await deps.onMessageUpdate(event, ctx);
|
|
632
670
|
});
|
|
671
|
+
pi.on("message_end", async (event, ctx) => {
|
|
672
|
+
if (!isActive(ctx)) return;
|
|
673
|
+
await deps.onMessageEnd?.(event, ctx);
|
|
674
|
+
});
|
|
675
|
+
pi.on("ui_prompt_start", async (event, ctx) => {
|
|
676
|
+
if (!isActive(ctx)) return;
|
|
677
|
+
await deps.onUiPromptStart?.(event, ctx);
|
|
678
|
+
});
|
|
679
|
+
pi.on("ui_prompt_end", async (event, ctx) => {
|
|
680
|
+
if (!isActive(ctx)) return;
|
|
681
|
+
await deps.onUiPromptEnd?.(event, ctx);
|
|
682
|
+
});
|
|
633
683
|
pi.on("agent_end", async (event, ctx) => {
|
|
634
684
|
if (!isActive(ctx)) return;
|
|
635
685
|
await deps.onAgentEnd(event, ctx);
|
|
@@ -14,11 +14,14 @@ import {
|
|
|
14
14
|
type ExtensionCommandContext,
|
|
15
15
|
type ExtensionContext,
|
|
16
16
|
type InputEvent,
|
|
17
|
+
type MessageEndEvent,
|
|
17
18
|
type SessionBeforeCompactEvent,
|
|
18
19
|
type SessionCompactEvent,
|
|
19
20
|
type SessionShutdownEvent,
|
|
20
21
|
type SessionStartEvent,
|
|
21
22
|
type SlashCommandInfo,
|
|
23
|
+
type UIPromptEndEvent,
|
|
24
|
+
type UIPromptStartEvent,
|
|
22
25
|
SettingsManager,
|
|
23
26
|
} from "@earendil-works/pi-coding-agent";
|
|
24
27
|
|
|
@@ -32,13 +35,25 @@ export type {
|
|
|
32
35
|
ExtensionCommandContext,
|
|
33
36
|
ExtensionContext,
|
|
34
37
|
InputEvent,
|
|
38
|
+
MessageEndEvent,
|
|
35
39
|
SessionBeforeCompactEvent,
|
|
36
40
|
SessionCompactEvent,
|
|
37
41
|
SessionShutdownEvent,
|
|
38
42
|
SessionStartEvent,
|
|
39
43
|
SlashCommandInfo,
|
|
44
|
+
UIPromptEndEvent,
|
|
45
|
+
UIPromptStartEvent,
|
|
40
46
|
};
|
|
41
47
|
|
|
48
|
+
export interface SessionCompactFailedEvent {
|
|
49
|
+
type: "session_compact_failed";
|
|
50
|
+
reason: "manual" | "threshold" | "overflow";
|
|
51
|
+
errorMessage?: string;
|
|
52
|
+
aborted: boolean;
|
|
53
|
+
willRetry: boolean;
|
|
54
|
+
fromExtension: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
export interface ToolExecutionStartEvent {
|
|
43
58
|
type: "tool_execution_start";
|
|
44
59
|
toolCallId: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
"image": "https://raw.githubusercontent.com/llblab/pi-telegram/main/screenshot.png"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@earendil-works/pi-agent-core": ">=0.
|
|
75
|
-
"@earendil-works/pi-ai": ">=0.
|
|
76
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
74
|
+
"@earendil-works/pi-agent-core": ">=0.84.4",
|
|
75
|
+
"@earendil-works/pi-ai": ">=0.84.4",
|
|
76
|
+
"@earendil-works/pi-coding-agent": ">=0.84.4",
|
|
77
77
|
"@sinclair/typebox": "*"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@llblab/pi-actors": "0.50.0",
|
|
44
|
-
"@llblab/pi-codex-usage": "0.9.
|
|
44
|
+
"@llblab/pi-codex-usage": "0.9.4",
|
|
45
45
|
"@llblab/pi-grow-loop": "0.7.3",
|
|
46
|
-
"@llblab/pi-telegram": "0.
|
|
46
|
+
"@llblab/pi-telegram": "0.40.0"
|
|
47
47
|
},
|
|
48
48
|
"bundledDependencies": [
|
|
49
49
|
"@llblab/pi-actors",
|