@llblab/pi-telegram 0.48.3 → 0.50.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 +1 -1
- package/BACKLOG.md +1 -0
- package/CHANGELOG.md +12 -0
- package/README.md +2 -1
- package/dist/lib/activity-verbosity.d.ts +3 -1
- package/dist/lib/activity-verbosity.js +40 -9
- package/dist/lib/bindings.d.ts +2 -1
- package/dist/lib/bindings.js +2 -1
- package/dist/lib/commands.d.ts +135 -2
- package/dist/lib/commands.js +299 -16
- package/dist/lib/delivery.d.ts +2 -0
- package/dist/lib/delivery.js +11 -0
- package/dist/lib/extension.js +68 -8
- package/dist/lib/menu-settings.d.ts +4 -3
- package/dist/lib/menu-settings.js +17 -13
- package/dist/lib/pi.d.ts +2 -1
- package/dist/lib/pi.js +1 -0
- package/dist/lib/routing.d.ts +1 -0
- package/dist/lib/routing.js +55 -5
- package/dist/lib/thread-display.d.ts +23 -0
- package/dist/lib/thread-display.js +20 -0
- package/dist/lib/threads.d.ts +18 -0
- package/dist/lib/threads.js +152 -6
- package/dist/lib/updates.d.ts +1 -0
- package/dist/lib/updates.js +5 -3
- package/dist/package.json +1 -1
- package/docs/architecture.md +4 -4
- package/docs/callback-namespaces.md +1 -1
- package/docs/public-api.md +4 -3
- package/docs/sections.md +2 -2
- package/docs/ui-style.md +2 -1
- package/docs/updates.md +1 -1
- package/lib/activity-verbosity.ts +46 -9
- package/lib/bindings.ts +3 -0
- package/lib/commands.ts +476 -21
- package/lib/delivery.ts +15 -0
- package/lib/extension.ts +77 -9
- package/lib/menu-settings.ts +25 -10
- package/lib/pi.ts +3 -0
- package/lib/routing.ts +84 -17
- package/lib/thread-display.ts +30 -0
- package/lib/threads.ts +199 -6
- package/lib/updates.ts +5 -2
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -121,7 +121,7 @@ The detailed map is canonical in [`docs/architecture.md`](./docs/architecture.md
|
|
|
121
121
|
- Command templates remain compact and shell-free. Use string leaves or ordered `template` arrays; shell operators are not an execution contract. Examples use portable executable placeholders, never machine-local paths.
|
|
122
122
|
- `telegram_attach` is the canonical file path and `telegram_message` the direct Markdown text/buttons path. Both require current direct or registered-follower authority and must not replace the normal active-turn reply.
|
|
123
123
|
- Inbound handlers transform text/media before queueing; outbound handlers precede programmatic/provider fallbacks. Public contracts and ordering live in `docs/inbound.md`, `docs/outbound.md`, and `docs/public-api.md`.
|
|
124
|
-
- Pi integration uses public hooks and APIs.
|
|
124
|
+
- Pi integration uses public hooks and APIs. Telegram `/new` is scheduled against the exact durable update, dispatched only after that update is removed from the journal, and then routed through the single `/telegram-internal` Pi gateway via `pi.sendUserMessage(..., { expandPromptTemplates: true })`; only a runtime-armed typed action may execute, manual invocation reports that the command cannot be run manually, and the handler receives the real `ExtensionCommandContext` before calling `ctx.newSession()`. Before replacement, CAS-publish one exact expiring handoff in the profile target snapshot. `workspace-thread` successors re-key the matching Workspace binding; `classic-chat` successors preserve Profile/CWD/session/chat continuity without creating a binding or invoking topic APIs. Both atomically claim the intent before one terminal result, and the old `withSession` path never publishes the same success. Never replace the session while inbound authority is unsettled, store stale command contexts, accept an expired or mismatched handoff, inject terminal input, spawn a shadow Pi process, or mutate session files.
|
|
125
125
|
|
|
126
126
|
## 7. Engineering Conventions
|
|
127
127
|
|
package/BACKLOG.md
CHANGED
|
@@ -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
|
+
- [ ] `Unified Telegram /new continuity` (`local-actionable`, current priority; closes [#81](https://github.com/llblab/pi-telegram/issues/81)): The existing one-shot intent now has strict `workspace-thread | classic-chat` continuity. Classic preparation persists exact Profile/CWD/session/chat identity without requiring or fabricating a Workspace binding; its codec round-trip and claim path leave Workspace bindings untouched. Threaded preparation and successor re-key retain the existing slot/name path. Local implementation and docs are complete. Classic preparation-to-successor settlement passes with one terminal send and no Workspace mutation against both the same store instance and a freshly reopened process store; duplicate settlement sends nothing. Threaded re-key, mismatch, expiry, failed-claim, full-suite, invariants, and Domain DAG regressions pass under the discriminated codec. Final `dist` is rebuilt; Classic Settings also hides both Thread-specific controls rather than exposing only cleanup. Remaining: operator-authorized Classic and Threaded live smoke. Do not close or post to #81 before release authority. Both modes delete confirmation before replacement, CAS-claim before terminal send, and preserve at-most-once cross-process result semantics; do not close or post to #81 before release authority.
|
|
5
6
|
- [ ] `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.
|
|
6
7
|
- [ ] `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.
|
|
7
8
|
- [ ] `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.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.50.0: Internal lifecycle gateway and thinking cadence
|
|
8
|
+
|
|
9
|
+
- `Internal lifecycle gateway`: Replaces the session-specific technical command with one `/telegram-internal` gateway for runtime-armed typed actions. Manual invocation performs no action and explains that the command cannot be run manually; settled `/new` replacement retains the same lifecycle and terminal-result guarantees.
|
|
10
|
+
- `Thinking cadence`: Thinking now follows the same cadence as answer drafts: it accumulates the opening frame for two seconds, then updates at most once every two seconds while still flushing buffered reasoning when the block completes.
|
|
11
|
+
|
|
12
|
+
## 0.49.0: Unified fresh-session continuity
|
|
13
|
+
|
|
14
|
+
- `/new`: Classic and Threaded Mode now share one confirmed fresh-session flow. The settled callback deletes its dialog and publishes an expiring exact-target intent; a same- or cross-process successor preserves the classic chat or re-keys the Thread/slot/name, atomically claims once, then sends one terminal result. Identity mismatches fail closed.
|
|
15
|
+
- `Command menus`: Both Telegram command menus now keep the primary sequence `/start`, `/compact`, `/new`, `/continue`, `/next`; the niche `/name` command remains available but moves out of menus into the Thread display Settings guidance.
|
|
16
|
+
- `Classic Settings`: Hides both Thread display and Thread cleanup controls when Threaded Mode is unavailable, rather than exposing only half of the Thread-specific surface.
|
|
17
|
+
- `Thread display`: A current Thread with a manual `/name` override now appears as `custom` on the Settings row and detail heading; choosing any automatic mode clears the current Thread's override, reapplies that projection, and selects it again. Successful interactive rename/reset now settles its source before asynchronously publishing the result, so immediate `/new` cannot replay the consumed name after replacement; cancellation reports only its completed result.
|
|
18
|
+
|
|
7
19
|
## 0.48.3: Assistant publication identity hotfix
|
|
8
20
|
|
|
9
21
|
- `Thread restore display identity`: Restoring or reclaiming a Thread now renames the destination from its current Workspace display title when available, rather than leaking the internal baked slot name such as `Moss` into directory-title mode.
|
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ Enable the optional capabilities the bridge needs in the [@BotFather](https://t.
|
|
|
126
126
|
| Model and thinking | Switch model or thinking level from Telegram through safe continuation flows. | Mobile control can adjust execution strategy without tearing down the current session. |
|
|
127
127
|
| Compaction | Confirm `/compact`, show native active status during compaction, and preserve Telegram-owned turn semantics. | Context maintenance is visible and safe from the phone. |
|
|
128
128
|
| Draft previews | Show Telegram's native `…typing` indicator whenever the connected instance is doing agent work, or enable Rich Draft previews for streamed answer text. | Local prompts, Telegram turns, and autonomous continuations remain visibly active while draft visibility stays independent from final rendering. |
|
|
129
|
-
| Activity | Keep the default `verbose` technical surface, show only `thinking`, show only `tools`, or select `quiet` for answer-only delivery. Every instance reloads this shared file-backed choice before a new agent run; thinking
|
|
129
|
+
| Activity | Keep the default `verbose` technical surface, show only `thinking`, show only `tools`, or select `quiet` for answer-only delivery. Every instance reloads this shared file-backed choice before a new agent run; thinking accumulates for two seconds and then updates at most every two seconds in a headerless expandable quote, while each tool uses one iconless closed root row containing nested evidence details. | Persistent collapsed technical activity minimizes chat height and stays bounded, redacted, target-fenced, free of URL previews, and visually separate from semantic assistant answers. |
|
|
130
130
|
| Assistant rendering | Choose Native Rich Markdown or legacy Markdown-to-HTML for final assistant replies. | Renderer compatibility is explicit instead of being conflated with draft previews. |
|
|
131
131
|
| Bridge UI rendering | Render thinking through headerless expandable HTML with inline emphasis/code, render each tool as an iconless native Rich root details tree with immediately visible arguments and collapsed secondary evidence, and keep menus, queue controls, status, settings, diagnostics, and sections on Telegram HTML/plain UI. | Harness-owned surfaces remain operationally predictable and visually distinct from model-authored answers. |
|
|
132
132
|
| Inbound files | Download inbound files to the Pi agent temp directory with size limits. | Screenshots, PDFs, datasets, and artifacts enter Pi as inspectable local files. |
|
|
@@ -164,6 +164,7 @@ Use these in the bot DM.
|
|
|
164
164
|
| --- | --- |
|
|
165
165
|
| `/start` | Pair when needed and open the main operator menu |
|
|
166
166
|
| `/name [Name]` | Set a manual Thread title immediately, or open rename/reset controls when Name is omitted |
|
|
167
|
+
| `/new` | Start a new Pi session in the current classic chat or Thread after confirming the bridge is idle |
|
|
167
168
|
| `/compact` | Confirm and run session compaction when safe |
|
|
168
169
|
| `/next` | Dispatch the next queued turn, aborting first if needed |
|
|
169
170
|
| `/continue` | Enqueue a priority continuation prompt |
|
|
@@ -11,7 +11,7 @@ export declare const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3900;
|
|
|
11
11
|
export declare const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
|
|
12
12
|
export declare const TELEGRAM_REASONING_MESSAGE_MAX_FRAMES = 24;
|
|
13
13
|
export declare const TELEGRAM_REASONING_BUFFER_MAX_CHARS = 1200;
|
|
14
|
-
export declare const TELEGRAM_REASONING_MIN_INTERVAL_MS =
|
|
14
|
+
export declare const TELEGRAM_REASONING_MIN_INTERVAL_MS = 2000;
|
|
15
15
|
export declare const TELEGRAM_TOOL_UPDATE_MAX_ENTRIES = 4;
|
|
16
16
|
interface ToolActivity {
|
|
17
17
|
id: string;
|
|
@@ -41,6 +41,8 @@ export declare function createTelegramActivityVerbosityRuntime<TAuthority>(deps:
|
|
|
41
41
|
getActivityMode: () => "quiet" | "thinking" | "tools" | "verbose";
|
|
42
42
|
refreshActivityMode?: () => Promise<void>;
|
|
43
43
|
getNowMs?: () => number;
|
|
44
|
+
setReasoningTimeout?: (callback: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
|
|
45
|
+
clearReasoningTimeout?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
44
46
|
resolveTarget: (event: TelegramActivityEvent) => TelegramTarget | undefined;
|
|
45
47
|
captureAuthority: () => TAuthority;
|
|
46
48
|
isAuthorityActive: (authority: TAuthority) => boolean;
|
|
@@ -9,7 +9,9 @@ export const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3_900;
|
|
|
9
9
|
export const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
|
|
10
10
|
export const TELEGRAM_REASONING_MESSAGE_MAX_FRAMES = 24;
|
|
11
11
|
export const TELEGRAM_REASONING_BUFFER_MAX_CHARS = 1_200;
|
|
12
|
-
|
|
12
|
+
// Match native answer drafts: accumulate the opening frame for one full
|
|
13
|
+
// interval, then publish at most one updated frame per interval.
|
|
14
|
+
export const TELEGRAM_REASONING_MIN_INTERVAL_MS = 2_000;
|
|
13
15
|
export const TELEGRAM_TOOL_UPDATE_MAX_ENTRIES = 4;
|
|
14
16
|
function targetEquals(left, right) {
|
|
15
17
|
return left.chatId === right.chatId && left.threadId === right.threadId;
|
|
@@ -234,10 +236,18 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
234
236
|
let reasoningMessage;
|
|
235
237
|
let reasoningBlocked = false;
|
|
236
238
|
let lastReasoningPublishMs = 0;
|
|
239
|
+
let reasoningFlushTimer;
|
|
237
240
|
let toolMessage;
|
|
238
241
|
const tools = new Map();
|
|
239
242
|
const toolOrder = [];
|
|
243
|
+
const clearReasoningFlushTimer = () => {
|
|
244
|
+
if (!reasoningFlushTimer)
|
|
245
|
+
return;
|
|
246
|
+
(deps.clearReasoningTimeout ?? clearTimeout)(reasoningFlushTimer);
|
|
247
|
+
reasoningFlushTimer = undefined;
|
|
248
|
+
};
|
|
240
249
|
const clearActivity = () => {
|
|
250
|
+
clearReasoningFlushTimer();
|
|
241
251
|
activityId = undefined;
|
|
242
252
|
authority = undefined;
|
|
243
253
|
target = undefined;
|
|
@@ -330,6 +340,30 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
330
340
|
deps.recordFailure?.(canEdit ? "reasoning-edit" : "reasoning-send", event, error);
|
|
331
341
|
}
|
|
332
342
|
};
|
|
343
|
+
const scheduleReasoningPublish = (event, acceptedGeneration) => {
|
|
344
|
+
if (reasoningFlushTimer || reasoningBlocked)
|
|
345
|
+
return;
|
|
346
|
+
const elapsed = reasoningMessageFrames === 0
|
|
347
|
+
? 0
|
|
348
|
+
: getNowMs() - lastReasoningPublishMs;
|
|
349
|
+
const delayMs = Math.max(0, TELEGRAM_REASONING_MIN_INTERVAL_MS - elapsed);
|
|
350
|
+
const schedule = deps.setReasoningTimeout ?? setTimeout;
|
|
351
|
+
reasoningFlushTimer = schedule(() => {
|
|
352
|
+
reasoningFlushTimer = undefined;
|
|
353
|
+
const admittedAuthority = authority;
|
|
354
|
+
const task = async () => {
|
|
355
|
+
if (!isCurrent(acceptedGeneration, admittedAuthority) ||
|
|
356
|
+
reasoningChars <= lastReasoningMessageChars)
|
|
357
|
+
return;
|
|
358
|
+
await publishReasoning(event, acceptedGeneration);
|
|
359
|
+
};
|
|
360
|
+
const enqueue = deps.enqueue ?? ((next) => tail.then(next));
|
|
361
|
+
tail = enqueue(task).catch((error) => {
|
|
362
|
+
deps.recordFailure?.("reasoning-send", event, error);
|
|
363
|
+
});
|
|
364
|
+
}, delayMs);
|
|
365
|
+
reasoningFlushTimer?.unref?.();
|
|
366
|
+
};
|
|
333
367
|
const publishTool = async (event, tool, acceptedGeneration) => {
|
|
334
368
|
const admittedAuthority = authority;
|
|
335
369
|
if (!isCurrent(acceptedGeneration, admittedAuthority) || !target) {
|
|
@@ -461,12 +495,8 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
461
495
|
return;
|
|
462
496
|
reasoningChars += event.delta.length;
|
|
463
497
|
reasoningBuffer = `${reasoningBuffer}${event.delta}`.slice(-TELEGRAM_REASONING_BUFFER_MAX_CHARS);
|
|
464
|
-
if (reasoningMessageFrames < TELEGRAM_REASONING_MESSAGE_MAX_FRAMES
|
|
465
|
-
(
|
|
466
|
-
(getNowMs() - lastReasoningPublishMs >=
|
|
467
|
-
TELEGRAM_REASONING_MIN_INTERVAL_MS &&
|
|
468
|
-
reasoningChars - lastReasoningMessageChars >= 160))) {
|
|
469
|
-
await publishReasoning(event, acceptedGeneration);
|
|
498
|
+
if (reasoningMessageFrames < TELEGRAM_REASONING_MESSAGE_MAX_FRAMES) {
|
|
499
|
+
scheduleReasoningPublish(event, acceptedGeneration);
|
|
470
500
|
}
|
|
471
501
|
return;
|
|
472
502
|
}
|
|
@@ -477,6 +507,7 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
477
507
|
reasoningChars = event.text.length;
|
|
478
508
|
reasoningBuffer = event.text.slice(-TELEGRAM_REASONING_BUFFER_MAX_CHARS);
|
|
479
509
|
}
|
|
510
|
+
clearReasoningFlushTimer();
|
|
480
511
|
if (reasoningChars > 0 &&
|
|
481
512
|
reasoningChars > lastReasoningMessageChars &&
|
|
482
513
|
!reasoningBlocked) {
|
|
@@ -550,8 +581,8 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
550
581
|
return;
|
|
551
582
|
}
|
|
552
583
|
if (event.type === "agent-end" || event.type === "agent-settled") {
|
|
553
|
-
|
|
554
|
-
|
|
584
|
+
clearReasoningFlushTimer();
|
|
585
|
+
if (reasoningChars > lastReasoningMessageChars &&
|
|
555
586
|
!reasoningBlocked) {
|
|
556
587
|
await publishReasoning(event, acceptedGeneration);
|
|
557
588
|
}
|
package/dist/lib/bindings.d.ts
CHANGED
|
@@ -216,6 +216,7 @@ interface TelegramLifecycleBindingDeps {
|
|
|
216
216
|
deferredQueueDispatchRuntime: Queue.TelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>;
|
|
217
217
|
modelContextAvailabilityRuntime: Prompts.TelegramModelContextAvailabilityRuntime;
|
|
218
218
|
disconnectOnQuit?: () => Promise<unknown>;
|
|
219
|
+
onSessionStarted?: (event: Pi.SessionStartEvent, ctx: Pi.ExtensionContext) => void;
|
|
219
220
|
shutdownGenerativeAppLiveSurfaces?: () => void;
|
|
220
221
|
resolveAutomaticThreadCleanupEnabled?: () => boolean | Promise<boolean>;
|
|
221
222
|
buttonActionStore: OutboundHandlers.TelegramButtonActionStore;
|
|
@@ -246,5 +247,5 @@ interface TelegramLifecycleBindingDeps {
|
|
|
246
247
|
updateStatus: TelegramBridgeStatusUpdater;
|
|
247
248
|
recordRuntimeEvent: TelegramRuntimeEventRecorder;
|
|
248
249
|
}
|
|
249
|
-
export declare function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime, activityRuntime, activityVerbosityRuntime, assistantOutputRuntime, sessionLifecycleRuntime, configStore, abort, typing, lifecycle, activeTurnRuntime, telegramQueueStore, modelSwitchController, previewRuntime, promptDispatchRuntime, deferredQueueDispatchRuntime, modelContextAvailabilityRuntime, disconnectOnQuit, shutdownGenerativeAppLiveSurfaces, resolveAutomaticThreadCleanupEnabled, buttonActionStore, callMultipart, sendChatAction, sendRecordVoiceAction, sendMarkdownReply, sendTextReply, dispatchNextQueuedTelegramTurn, onPromptHandedOff, answerGuestQuery, deleteMessage, sendGuestReply, editGuestReply, stopGuestPlaceholder, preparePreviewDelivery, finalizeMarkdownPreview, proactivePushTargetGetter, getAssistantRenderingMode, recordMessageOwnership, canSendAgentActivity, isSessionContextActive, isTurnTransportActive, updateStatus, recordRuntimeEvent, }: TelegramLifecycleBindingDeps): void;
|
|
250
|
+
export declare function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime, activityRuntime, activityVerbosityRuntime, assistantOutputRuntime, sessionLifecycleRuntime, configStore, abort, typing, lifecycle, activeTurnRuntime, telegramQueueStore, modelSwitchController, previewRuntime, promptDispatchRuntime, deferredQueueDispatchRuntime, modelContextAvailabilityRuntime, disconnectOnQuit, onSessionStarted, shutdownGenerativeAppLiveSurfaces, resolveAutomaticThreadCleanupEnabled, buttonActionStore, callMultipart, sendChatAction, sendRecordVoiceAction, sendMarkdownReply, sendTextReply, dispatchNextQueuedTelegramTurn, onPromptHandedOff, answerGuestQuery, deleteMessage, sendGuestReply, editGuestReply, stopGuestPlaceholder, preparePreviewDelivery, finalizeMarkdownPreview, proactivePushTargetGetter, getAssistantRenderingMode, recordMessageOwnership, canSendAgentActivity, isSessionContextActive, isTurnTransportActive, updateStatus, recordRuntimeEvent, }: TelegramLifecycleBindingDeps): void;
|
|
250
251
|
export {};
|
package/dist/lib/bindings.js
CHANGED
|
@@ -499,7 +499,7 @@ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, pe
|
|
|
499
499
|
},
|
|
500
500
|
});
|
|
501
501
|
}
|
|
502
|
-
export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime, activityRuntime, activityVerbosityRuntime, assistantOutputRuntime, sessionLifecycleRuntime, configStore, abort, typing, lifecycle, activeTurnRuntime, telegramQueueStore, modelSwitchController, previewRuntime, promptDispatchRuntime, deferredQueueDispatchRuntime, modelContextAvailabilityRuntime, disconnectOnQuit, shutdownGenerativeAppLiveSurfaces, resolveAutomaticThreadCleanupEnabled, buttonActionStore, callMultipart, sendChatAction, sendRecordVoiceAction, sendMarkdownReply, sendTextReply, dispatchNextQueuedTelegramTurn, onPromptHandedOff, answerGuestQuery, deleteMessage, sendGuestReply, editGuestReply, stopGuestPlaceholder, preparePreviewDelivery, finalizeMarkdownPreview, proactivePushTargetGetter, getAssistantRenderingMode, recordMessageOwnership, canSendAgentActivity, isSessionContextActive = () => true, isTurnTransportActive, updateStatus, recordRuntimeEvent, }) {
|
|
502
|
+
export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime, activityRuntime, activityVerbosityRuntime, assistantOutputRuntime, sessionLifecycleRuntime, configStore, abort, typing, lifecycle, activeTurnRuntime, telegramQueueStore, modelSwitchController, previewRuntime, promptDispatchRuntime, deferredQueueDispatchRuntime, modelContextAvailabilityRuntime, disconnectOnQuit, onSessionStarted, shutdownGenerativeAppLiveSurfaces, resolveAutomaticThreadCleanupEnabled, buttonActionStore, callMultipart, sendChatAction, sendRecordVoiceAction, sendMarkdownReply, sendTextReply, dispatchNextQueuedTelegramTurn, onPromptHandedOff, answerGuestQuery, deleteMessage, sendGuestReply, editGuestReply, stopGuestPlaceholder, preparePreviewDelivery, finalizeMarkdownPreview, proactivePushTargetGetter, getAssistantRenderingMode, recordMessageOwnership, canSendAgentActivity, isSessionContextActive = () => true, isTurnTransportActive, updateStatus, recordRuntimeEvent, }) {
|
|
503
503
|
const agentEndResetter = Runtime.createTelegramAgentEndResetter({
|
|
504
504
|
abort,
|
|
505
505
|
typing,
|
|
@@ -755,6 +755,7 @@ export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime,
|
|
|
755
755
|
activityVerbosityRuntime?.reset();
|
|
756
756
|
modelContextAvailabilityRuntime.reconcile();
|
|
757
757
|
await sessionLifecycleRuntime.onSessionStart(event, ctx);
|
|
758
|
+
onSessionStarted?.(event, ctx);
|
|
758
759
|
},
|
|
759
760
|
async onSessionShutdown(event, ctx) {
|
|
760
761
|
if (!isSessionContextActive(ctx))
|
package/dist/lib/commands.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram command routing helpers
|
|
3
3
|
* Zones: telegram controls, pi agent commands, queue controls
|
|
4
|
-
* Owns Telegram slash-command normalization, bot command metadata,
|
|
4
|
+
* Owns Telegram slash-command normalization, bot command metadata, pi-side command registration, and command-initiated session replacement orchestration behind runtime ports
|
|
5
5
|
*/
|
|
6
6
|
import { type TelegramConfigStore } from "./config.ts";
|
|
7
|
+
import type * as Pi from "./pi.ts";
|
|
7
8
|
import type { ExtensionAPI, ExtensionCommandContext } from "./pi.ts";
|
|
8
9
|
import type { TelegramBridgeStatusLineOptions } from "./status.ts";
|
|
10
|
+
import type { TelegramSessionReplacementIntent } from "./threads.ts";
|
|
9
11
|
import { type PendingTelegramControlItem, type TelegramQueueAdmissionReceipt } from "./queue.ts";
|
|
10
12
|
export interface ParsedTelegramCommand {
|
|
11
13
|
name: string;
|
|
@@ -60,6 +62,7 @@ export declare const TELEGRAM_COMMAND_EMOJI: {
|
|
|
60
62
|
readonly abort: "⏹️";
|
|
61
63
|
readonly stop: "🟥";
|
|
62
64
|
readonly name: "🏷️";
|
|
65
|
+
readonly new: "🆕";
|
|
63
66
|
};
|
|
64
67
|
export type TelegramCommandEmojiName = keyof typeof TELEGRAM_COMMAND_EMOJI;
|
|
65
68
|
export declare function getTelegramCommandEmoji(command: TelegramCommandEmojiName): string;
|
|
@@ -144,7 +147,7 @@ export declare function createTelegramThreadDisplayNameRenameBinding(): {
|
|
|
144
147
|
};
|
|
145
148
|
export declare function parseTelegramRequestedThreadName(args: string): string | undefined;
|
|
146
149
|
export declare function registerTelegramBridgeCommands(pi: ExtensionAPI, deps: TelegramBridgeCommandRegistrationDeps): void;
|
|
147
|
-
export declare const TELEGRAM_RESERVED_COMMAND_NAMES: readonly ["stop", "name", "abort", "next", "continue", "status", "queue", "compact", "model", "thinking", "settings", "help", "start"];
|
|
150
|
+
export declare const TELEGRAM_RESERVED_COMMAND_NAMES: readonly ["stop", "name", "new", "abort", "next", "continue", "status", "queue", "compact", "model", "thinking", "settings", "help", "start"];
|
|
148
151
|
export type TelegramReservedCommandName = (typeof TELEGRAM_RESERVED_COMMAND_NAMES)[number];
|
|
149
152
|
export declare function isTelegramReservedCommandName(commandName: string | undefined): commandName is TelegramReservedCommandName;
|
|
150
153
|
export type TelegramCommandAction = {
|
|
@@ -156,6 +159,9 @@ export type TelegramCommandAction = {
|
|
|
156
159
|
} | {
|
|
157
160
|
kind: "name";
|
|
158
161
|
executionMode: "immediate";
|
|
162
|
+
} | {
|
|
163
|
+
kind: "new";
|
|
164
|
+
executionMode: "immediate";
|
|
159
165
|
} | {
|
|
160
166
|
kind: "abort";
|
|
161
167
|
executionMode: "immediate";
|
|
@@ -192,6 +198,7 @@ export type TelegramCommandExecutionMode = "ignored" | "immediate";
|
|
|
192
198
|
export interface TelegramCommandActionDeps<TMessage, TContext> {
|
|
193
199
|
handleStop: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
194
200
|
handleName: (message: TMessage, ctx: TContext, name: string) => Promise<void>;
|
|
201
|
+
handleNew: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
195
202
|
handleAbort: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
196
203
|
handleNext: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
197
204
|
handleContinue: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
@@ -264,6 +271,13 @@ export interface TelegramCompactConfirmationCallbackQuery {
|
|
|
264
271
|
message_thread_id?: number;
|
|
265
272
|
};
|
|
266
273
|
}
|
|
274
|
+
export interface TelegramNewConfirmationCallbackDeps<TContext> {
|
|
275
|
+
ctx: TContext;
|
|
276
|
+
answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
|
|
277
|
+
editInteractiveMessage: (chatId: number, messageId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramCompactConfirmationReplyMarkup) => Promise<void>;
|
|
278
|
+
deleteMessage: (chatId: number, messageId: number) => Promise<void>;
|
|
279
|
+
runNew: (ctx: TContext) => Promise<void>;
|
|
280
|
+
}
|
|
267
281
|
export interface TelegramCompactConfirmationCallbackDeps<TContext> {
|
|
268
282
|
ctx: TContext;
|
|
269
283
|
answerCallbackQuery: (callbackQueryId: string, text?: string) => Promise<void>;
|
|
@@ -390,6 +404,7 @@ export interface TelegramCommandRuntimeDeps<TMessage extends TelegramCommandRunt
|
|
|
390
404
|
}) => void;
|
|
391
405
|
stopTypingLoop?: () => void;
|
|
392
406
|
enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
407
|
+
requestNewSession?: (message: TMessage) => void;
|
|
393
408
|
compact: (ctx: TContext, callbacks: {
|
|
394
409
|
onComplete: () => void;
|
|
395
410
|
onError: (error: unknown) => void;
|
|
@@ -434,6 +449,10 @@ export declare const TELEGRAM_COMMAND_ACTIONS: {
|
|
|
434
449
|
readonly kind: "name";
|
|
435
450
|
readonly executionMode: "immediate";
|
|
436
451
|
};
|
|
452
|
+
readonly new: {
|
|
453
|
+
readonly kind: "new";
|
|
454
|
+
readonly executionMode: "immediate";
|
|
455
|
+
};
|
|
437
456
|
readonly abort: {
|
|
438
457
|
readonly kind: "abort";
|
|
439
458
|
readonly executionMode: "immediate";
|
|
@@ -515,10 +534,27 @@ export declare function handleTelegramNextCommand(deps: {
|
|
|
515
534
|
export declare function handleTelegramContinueCommand<TMessage, TContext>(message: TMessage, ctx: TContext, deps: {
|
|
516
535
|
enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
517
536
|
}): Promise<void>;
|
|
537
|
+
export declare function buildTelegramNewConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup;
|
|
538
|
+
export declare function getTelegramNewConfirmationHtml(): string;
|
|
539
|
+
export declare function openTelegramNewConfirmation(target: TelegramCommandMessageTarget, deps: TelegramCompactConfirmationDeps): Promise<void>;
|
|
540
|
+
export declare function handleTelegramNewConfirmationCallback<TContext>(query: TelegramCompactConfirmationCallbackQuery, deps: TelegramNewConfirmationCallbackDeps<TContext>): Promise<boolean>;
|
|
518
541
|
export declare function buildTelegramCompactConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup;
|
|
519
542
|
export declare function getTelegramCompactConfirmationHtml(): string;
|
|
520
543
|
export declare function openTelegramCompactConfirmation(target: TelegramCommandMessageTarget, deps: TelegramCompactConfirmationDeps): Promise<void>;
|
|
521
544
|
export declare function handleTelegramCompactConfirmationCallback<TContext>(query: TelegramCompactConfirmationCallbackQuery, deps: TelegramCompactConfirmationCallbackDeps<TContext>): Promise<boolean>;
|
|
545
|
+
export interface TelegramNewCommandDeps extends TelegramRuntimeEventRecorderPort {
|
|
546
|
+
isIdle: () => boolean;
|
|
547
|
+
hasPendingMessages: () => boolean;
|
|
548
|
+
hasActiveTelegramTurn: () => boolean;
|
|
549
|
+
hasDispatchPending: () => boolean;
|
|
550
|
+
hasQueuedTelegramItems: () => boolean;
|
|
551
|
+
isCompactionInProgress: () => boolean;
|
|
552
|
+
requestNewSession?: () => void;
|
|
553
|
+
sendTextReply: (text: string, options?: {
|
|
554
|
+
parseMode?: "HTML";
|
|
555
|
+
}) => Promise<void>;
|
|
556
|
+
}
|
|
557
|
+
export declare function handleTelegramNewCommand(deps: TelegramNewCommandDeps): Promise<void>;
|
|
522
558
|
export declare function handleTelegramCompactCommand(deps: TelegramCompactCommandDeps): Promise<void>;
|
|
523
559
|
export declare function handleTelegramStatusCommand<TContext>(deps: {
|
|
524
560
|
ctx: TContext;
|
|
@@ -538,4 +574,101 @@ export declare function createTelegramCommandHandler<TMessage extends TelegramCo
|
|
|
538
574
|
export declare function createTelegramCommandOrPromptRuntime<TMessage, TContext>(deps: TelegramCommandOrPromptRuntimeDeps<TMessage, TContext>): {
|
|
539
575
|
dispatchMessages: (messages: TMessage[], ctx: TContext) => Promise<void>;
|
|
540
576
|
};
|
|
577
|
+
export declare const TELEGRAM_INTERNAL_COMMAND_NAME = "telegram-internal";
|
|
578
|
+
export declare const TELEGRAM_INTERNAL_COMMAND_DESCRIPTION = "(internal) dispatch one settled Telegram lifecycle action";
|
|
579
|
+
export declare const TELEGRAM_INTERNAL_MANUAL_USE_MESSAGE = "This internal Telegram command cannot be run manually.";
|
|
580
|
+
export declare function delayTelegramSessionAction(delayMs: number): Promise<void>;
|
|
581
|
+
export interface TelegramSessionActionRuntimeDeps {
|
|
582
|
+
registerCommand: Pi.ExtensionAPI["registerCommand"];
|
|
583
|
+
sendUserMessage: Pi.ExtensionAPI["sendUserMessage"];
|
|
584
|
+
notifyResult: (target: {
|
|
585
|
+
chatId: number;
|
|
586
|
+
threadId?: number;
|
|
587
|
+
messageId: number;
|
|
588
|
+
}, result: "success" | "cancelled" | "failure") => Promise<void>;
|
|
589
|
+
prepareReplacement?: (ctx: Pi.ExtensionCommandContext, updateId: number, target: {
|
|
590
|
+
chatId: number;
|
|
591
|
+
threadId?: number;
|
|
592
|
+
messageId: number;
|
|
593
|
+
}) => Promise<void>;
|
|
594
|
+
recordRuntimeEvent?: (category: string, error: unknown) => void;
|
|
595
|
+
}
|
|
596
|
+
export interface TelegramSessionReplacementSettlementDeps {
|
|
597
|
+
getIntent: () => Promise<TelegramSessionReplacementIntent | undefined>;
|
|
598
|
+
hasSuccessorContinuity: (intent: TelegramSessionReplacementIntent) => boolean;
|
|
599
|
+
editSuccess: (intent: TelegramSessionReplacementIntent) => Promise<{
|
|
600
|
+
ok: boolean;
|
|
601
|
+
retryable?: boolean;
|
|
602
|
+
message?: string;
|
|
603
|
+
}>;
|
|
604
|
+
clearIntent: (intent: TelegramSessionReplacementIntent) => Promise<boolean>;
|
|
605
|
+
profileName: string | undefined;
|
|
606
|
+
cwd: string;
|
|
607
|
+
sessionId: string;
|
|
608
|
+
now?: () => number;
|
|
609
|
+
sleep?: (delayMs: number) => Promise<void>;
|
|
610
|
+
isCurrent?: () => boolean;
|
|
611
|
+
}
|
|
612
|
+
export declare function settleTelegramSessionReplacement(deps: TelegramSessionReplacementSettlementDeps): Promise<"none" | "settled" | "expired" | "failed" | "stale">;
|
|
613
|
+
export declare function createTelegramSessionReplacementSettlementRuntime<TContext>(deps: {
|
|
614
|
+
resolve: (ctx: TContext) => TelegramSessionReplacementSettlementDeps | undefined;
|
|
615
|
+
onResult?: (result: "none" | "settled" | "expired" | "failed" | "stale") => void;
|
|
616
|
+
onError?: (error: unknown) => void;
|
|
617
|
+
}): {
|
|
618
|
+
onSessionStart: (ctx: TContext) => void;
|
|
619
|
+
};
|
|
620
|
+
export interface TelegramSessionActionAssemblyDeps {
|
|
621
|
+
registerCommand: Pi.ExtensionAPI["registerCommand"];
|
|
622
|
+
sendUserMessage: Pi.ExtensionAPI["sendUserMessage"];
|
|
623
|
+
store: {
|
|
624
|
+
load: () => Promise<void>;
|
|
625
|
+
refresh?: () => Promise<void>;
|
|
626
|
+
getWorkspaceBindingByTarget: (target: {
|
|
627
|
+
chatId: number;
|
|
628
|
+
threadId?: number;
|
|
629
|
+
}, sessionId?: string) => {
|
|
630
|
+
cwd: string;
|
|
631
|
+
sessionId?: string;
|
|
632
|
+
slot?: string;
|
|
633
|
+
threadName?: string;
|
|
634
|
+
manualThreadName?: string;
|
|
635
|
+
target: {
|
|
636
|
+
chatId: number;
|
|
637
|
+
threadId: number;
|
|
638
|
+
};
|
|
639
|
+
} | undefined;
|
|
640
|
+
getSessionReplacementIntent: () => TelegramSessionReplacementIntent | undefined;
|
|
641
|
+
commitSessionReplacementIntent: (intent: TelegramSessionReplacementIntent, isCurrent: () => boolean) => Promise<boolean>;
|
|
642
|
+
removeSessionReplacementIntent: (intent: TelegramSessionReplacementIntent, isCurrent: () => boolean) => Promise<boolean>;
|
|
643
|
+
};
|
|
644
|
+
getProfileName: () => string | undefined;
|
|
645
|
+
ownsPersistence: () => boolean;
|
|
646
|
+
sendResult: (target: {
|
|
647
|
+
chatId: number;
|
|
648
|
+
threadId?: number;
|
|
649
|
+
}, html: string) => Promise<{
|
|
650
|
+
ok: boolean;
|
|
651
|
+
retryable?: boolean;
|
|
652
|
+
}>;
|
|
653
|
+
handoffTtlMs: number;
|
|
654
|
+
now?: () => number;
|
|
655
|
+
recordRuntimeEvent?: (category: string, error: unknown) => void;
|
|
656
|
+
}
|
|
657
|
+
export declare function createTelegramSessionActionAssembly(deps: TelegramSessionActionAssemblyDeps): {
|
|
658
|
+
action: TelegramSessionActionRuntime;
|
|
659
|
+
settlement: {
|
|
660
|
+
onSessionStart: (ctx: Pi.ExtensionContext) => void;
|
|
661
|
+
};
|
|
662
|
+
};
|
|
663
|
+
export interface TelegramSessionActionRuntime {
|
|
664
|
+
register: () => void;
|
|
665
|
+
scheduleAfterUpdate: (updateId: number, target: {
|
|
666
|
+
chatId: number;
|
|
667
|
+
threadId?: number;
|
|
668
|
+
messageId: number;
|
|
669
|
+
}) => boolean;
|
|
670
|
+
onUpdateCompleted: (updateId: number) => void;
|
|
671
|
+
hasPending: () => boolean;
|
|
672
|
+
}
|
|
673
|
+
export declare function createTelegramSessionActionRuntime(deps: TelegramSessionActionRuntimeDeps): TelegramSessionActionRuntime;
|
|
541
674
|
export {};
|