@llblab/pi-kit 0.36.3 → 0.37.1
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 +10 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-telegram/AGENTS.md +4 -4
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +9 -11
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +19 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +5 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +36 -35
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +2 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +20 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +6 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +23 -65
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/prompts.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.js +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +4 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +29 -20
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/SKILL.md +5 -5
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +6 -6
- package/node_modules/@llblab/pi-telegram/docs/compact-matrix-literal.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +8 -8
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +5 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +4 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +53 -43
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1 -3
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +17 -15
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +28 -68
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +3 -3
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/runtime.ts +2 -1
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +31 -18
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +5 -5
- package/package.json +2 -2
|
@@ -34,7 +34,7 @@ export const TELEGRAM_MESSAGE_PROMPT_GUIDELINES = [
|
|
|
34
34
|
"For an explicitly requested channel post, pass its exact numeric id or public @username as chat_id; no local channel registry is required, and Telegram remains the authority on the bot's posting permission.",
|
|
35
35
|
"For an explicitly requested channel media post, pass one local .jpg/.jpeg/.png/.webp photo or .mp4 video as media; the text becomes its caption (max 1024 characters), and albums or other media types are rejected.",
|
|
36
36
|
"For a live Pi thread target, provide thread as its case-insensitive name or numeric id; the bridge sends visibly and admits one attributed turn to that live instance. Unknown, ambiguous, same, or offline targets fail before sending.",
|
|
37
|
-
"Add buttons by embedding the same
|
|
37
|
+
"Add buttons by embedding the same telegram_button HTML comments used in normal Telegram replies; Telegram does not support standalone buttons.",
|
|
38
38
|
"During an active Telegram turn, omit telegram_message for the current target and answer normally; use thread only when the user requests delivery to a different live Pi thread.",
|
|
39
39
|
] as const;
|
|
40
40
|
|
|
@@ -360,7 +360,7 @@ export interface TelegramTypingLoopStarterDeps<
|
|
|
360
360
|
getDefaultChatId: () => number | undefined;
|
|
361
361
|
sendTypingAction: (
|
|
362
362
|
chatId: number,
|
|
363
|
-
options?: { message_thread_id?: number; assertAuthority?: () => void },
|
|
363
|
+
options?: { message_thread_id?: number; assertAuthority?: () => void; isCancelled?: () => boolean },
|
|
364
364
|
) => Promise<unknown>;
|
|
365
365
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
366
366
|
isContextActive?: (ctx: TContext) => boolean;
|
|
@@ -454,6 +454,7 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
454
454
|
try {
|
|
455
455
|
await deps.sendTypingAction(targetChatId, {
|
|
456
456
|
...actionOptions,
|
|
457
|
+
isCancelled: () => !active,
|
|
457
458
|
// This stays process-local; adapters recheck it before HTTP/IPC issuance, not just loop ticks.
|
|
458
459
|
assertAuthority() {
|
|
459
460
|
if (!active || !canContinue()) throw new Error("Typing context authority changed.");
|
|
@@ -407,8 +407,8 @@ export interface TelegramApiCallOptions {
|
|
|
407
407
|
|
|
408
408
|
export class TelegramApiAuthorityError extends Error {
|
|
409
409
|
readonly requestIssued: boolean;
|
|
410
|
-
constructor(requestIssued: boolean) {
|
|
411
|
-
super("Telegram API call authority is unavailable.");
|
|
410
|
+
constructor(requestIssued: boolean, options?: ErrorOptions) {
|
|
411
|
+
super("Telegram API call authority is unavailable.", options);
|
|
412
412
|
this.name = "TelegramApiAuthorityError";
|
|
413
413
|
this.requestIssued = requestIssued;
|
|
414
414
|
}
|
|
@@ -418,11 +418,12 @@ export class TelegramApiAuthorityError extends Error {
|
|
|
418
418
|
export function assertTelegramApiCallAuthority(
|
|
419
419
|
assertAuthority: (() => void) | undefined,
|
|
420
420
|
requestIssued: boolean,
|
|
421
|
+
errorOptions?: ErrorOptions,
|
|
421
422
|
): void {
|
|
422
423
|
try {
|
|
423
424
|
assertAuthority?.();
|
|
424
425
|
} catch {
|
|
425
|
-
throw new TelegramApiAuthorityError(requestIssued);
|
|
426
|
+
throw new TelegramApiAuthorityError(requestIssued, errorOptions);
|
|
426
427
|
}
|
|
427
428
|
}
|
|
428
429
|
|
|
@@ -847,6 +848,8 @@ export interface TelegramBridgeApiRuntimeDeps {
|
|
|
847
848
|
/** Typing issuance guard is local authority, never Bot API body or IPC data. */
|
|
848
849
|
export interface TelegramTypingActionOptions extends Pick<TelegramApiCallOptions, "assertAuthority"> {
|
|
849
850
|
message_thread_id?: number;
|
|
851
|
+
/** Diagnostic observation only: an ended loop does not grant issuance, success or retry. */
|
|
852
|
+
isCancelled?: () => boolean;
|
|
850
853
|
}
|
|
851
854
|
|
|
852
855
|
export interface TelegramBridgeApiRuntime extends Pick<
|
|
@@ -1649,7 +1652,7 @@ async function callTelegramWithRetry<TResponse>(
|
|
|
1649
1652
|
} catch (error) {
|
|
1650
1653
|
// Authority refusal is never a transport failure, retry grant or acknowledged success.
|
|
1651
1654
|
if (error instanceof TelegramApiAuthorityError) throw error;
|
|
1652
|
-
assertTelegramApiCallAuthority(assertAuthority, requestIssued);
|
|
1655
|
+
assertTelegramApiCallAuthority(assertAuthority, requestIssued, { cause: error });
|
|
1653
1656
|
const retryable =
|
|
1654
1657
|
isRetryableTelegramApiError(error) &&
|
|
1655
1658
|
!(
|
|
@@ -2090,7 +2093,8 @@ export function createTelegramBridgeApiRuntime(
|
|
|
2090
2093
|
notBeforeMs: number;
|
|
2091
2094
|
};
|
|
2092
2095
|
const chatActionGates = new Map<string, ChatActionGate>();
|
|
2093
|
-
|
|
2096
|
+
// Pin shared cooldowns until every concurrent Thread settles; this state never owns their sends.
|
|
2097
|
+
const chatActionChatGates = new Map<string, { pendingRequests: number; notBeforeMs: number }>();
|
|
2094
2098
|
const getChatActionKeys = (
|
|
2095
2099
|
method: string,
|
|
2096
2100
|
body: Record<string, unknown>,
|
|
@@ -2130,7 +2134,7 @@ export function createTelegramBridgeApiRuntime(
|
|
|
2130
2134
|
}
|
|
2131
2135
|
}
|
|
2132
2136
|
for (const [key, candidate] of chatActionChatGates) {
|
|
2133
|
-
if (
|
|
2137
|
+
if (candidate.pendingRequests === 0 && nowMs >= candidate.notBeforeMs) {
|
|
2134
2138
|
chatActionChatGates.delete(key);
|
|
2135
2139
|
}
|
|
2136
2140
|
}
|
|
@@ -2146,11 +2150,10 @@ export function createTelegramBridgeApiRuntime(
|
|
|
2146
2150
|
if (chatActionChatGates.size >= chatActionMaxGates) {
|
|
2147
2151
|
return true as TResponse;
|
|
2148
2152
|
}
|
|
2149
|
-
chatGate = { notBeforeMs: 0 };
|
|
2153
|
+
chatGate = { pendingRequests: 0, notBeforeMs: 0 };
|
|
2150
2154
|
chatActionChatGates.set(chatActionKeys.chat, chatGate);
|
|
2151
2155
|
}
|
|
2152
2156
|
if (gate.inFlight) return (await gate.inFlight) as TResponse;
|
|
2153
|
-
if (chatGate.inFlight) return true as TResponse;
|
|
2154
2157
|
if (nowMs < gate.notBeforeMs || nowMs < chatGate.notBeforeMs) {
|
|
2155
2158
|
return true as TResponse;
|
|
2156
2159
|
}
|
|
@@ -2189,19 +2192,22 @@ export function createTelegramBridgeApiRuntime(
|
|
|
2189
2192
|
);
|
|
2190
2193
|
return true as TResponse;
|
|
2191
2194
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2195
|
+
const diagnosticError = error instanceof TelegramApiAuthorityError && Object.hasOwn(error, "cause")
|
|
2196
|
+
? error.cause : error;
|
|
2197
|
+
if (!options?.isExpectedError?.(error))
|
|
2198
|
+
deps.recordRuntimeEvent(
|
|
2199
|
+
"api",
|
|
2200
|
+
diagnosticError,
|
|
2201
|
+
withTelegramTransportDiagnostics(diagnosticError, { method }),
|
|
2202
|
+
);
|
|
2197
2203
|
throw error;
|
|
2198
2204
|
})
|
|
2199
2205
|
.finally(() => {
|
|
2200
2206
|
if (gate.inFlight === request) gate.inFlight = undefined;
|
|
2201
|
-
|
|
2207
|
+
chatGate.pendingRequests--;
|
|
2202
2208
|
});
|
|
2203
2209
|
gate.inFlight = request;
|
|
2204
|
-
chatGate.
|
|
2210
|
+
chatGate.pendingRequests++;
|
|
2205
2211
|
return request;
|
|
2206
2212
|
}
|
|
2207
2213
|
try {
|
|
@@ -2311,14 +2317,21 @@ export function createTelegramBridgeApiRuntime(
|
|
|
2311
2317
|
: {}),
|
|
2312
2318
|
}),
|
|
2313
2319
|
sendTypingAction: createTelegramChatActionSender(
|
|
2314
|
-
(chatId, action, options) =>
|
|
2315
|
-
|
|
2320
|
+
(chatId, action, options) => {
|
|
2321
|
+
const isCancelled = options?.isCancelled;
|
|
2322
|
+
return callRecorded<boolean>("sendChatAction", {
|
|
2316
2323
|
chat_id: chatId,
|
|
2317
2324
|
action,
|
|
2318
2325
|
...(options?.message_thread_id !== undefined
|
|
2319
2326
|
? { message_thread_id: options.message_thread_id }
|
|
2320
2327
|
: {}),
|
|
2321
|
-
}, {
|
|
2328
|
+
}, {
|
|
2329
|
+
assertAuthority: options?.assertAuthority,
|
|
2330
|
+
...(isCancelled ? {
|
|
2331
|
+
isExpectedError: (error: unknown) => error instanceof TelegramApiAuthorityError && !Object.hasOwn(error, "cause") && isCancelled() === true,
|
|
2332
|
+
} : {}),
|
|
2333
|
+
});
|
|
2334
|
+
},
|
|
2322
2335
|
"typing",
|
|
2323
2336
|
),
|
|
2324
2337
|
sendRecordVoiceAction: createTelegramChatActionSender(
|
|
@@ -16,12 +16,12 @@ Use Telegram as a mobile companion to the current Pi session. Preserve the exact
|
|
|
16
16
|
| Explicitly send from local/TUI to Telegram | `telegram_message` or `telegram_attach` |
|
|
17
17
|
| Explicitly send to a different live Thread | `telegram_message(thread=...)` |
|
|
18
18
|
| Add buttons | Hidden comment for footer; `telegram_button` fence for in-body rows |
|
|
19
|
-
| Add explicit voice |
|
|
19
|
+
| Add explicit voice | Hidden action comment |
|
|
20
20
|
| Build a repeated deterministic interaction | Follow `generative-apps` |
|
|
21
21
|
|
|
22
22
|
A connected Telegram session proves capability, not user intent. Use Telegram features on Telegram-originated turns or explicit Telegram delivery requests only. Never call `telegram_message` for the current active target.
|
|
23
23
|
|
|
24
|
-
For
|
|
24
|
+
For delivery, Thread routing, configuration or diagnosis, read the applicable reference under [Conditional References](#conditional-references).
|
|
25
25
|
|
|
26
26
|
## Turn Context
|
|
27
27
|
|
|
@@ -40,7 +40,7 @@ Reply in concise, phone-width Telegram Rich Markdown. Use `$...$` and `$$...$$`
|
|
|
40
40
|
|
|
41
41
|
## Assistant Actions
|
|
42
42
|
|
|
43
|
-
`telegram_button` and `telegram_voice` are markup, not tools.
|
|
43
|
+
`telegram_button` and `telegram_voice` are markup, not tools. Complete comments activate regardless of surrounding Markdown. Escape `<!--` as `<!--` for literal examples; backticks and backslashes do not disable actions. Comments create footer buttons or voice artifacts. In-body fences: ≥3 backticks/tildes, optional indentation/whitespace, exact `telegram_button`; separate same-marker closure, no shorter. Both wrappers accept a singleton JSON/CML cell or mixed matrix; the wrapper determines placement. Telegram removes every assistant-authored HTML comment from previews and final replies regardless of owner or Markdown position; only complete recognized comments or valid button fences activate actions; comment-only output sends no text message.
|
|
44
44
|
|
|
45
45
|
### Shared Encoding Rule
|
|
46
46
|
|
|
@@ -61,7 +61,7 @@ Every enabled button has a self-contained prompt and an optional selection style
|
|
|
61
61
|
- `{label|prompt|selected_style}` and `{|prompt|selected_style}` accept `primary`, `success`, or `danger`.
|
|
62
62
|
- Fourth-position `1`/`true` disables, `0`/`false` enables; omission means enabled. JSON uses boolean `disabled`. `{|Next||1}` omits label/style; `{Next|||1}` omits prompt/style; `{|||1}` is blank (Telegram receives a non-breaking space). Prefer meaningful labels and retain a useful enabled action. Enabled CML requires a prompt. Disabled controls stay visible but have no callback, queued prompt, or bound-method invocation.
|
|
63
63
|
- Both wrappers concatenate rows from adjacent JSON/CML cells or matrices, with optional commas. Cells are vertical; horizontal peers require a nested row: `[[{A|a}{B|b}]]`.
|
|
64
|
-
- Prefer one matrix per related group. Fenced blocks stay in place in Rich mode; HTML compatibility moves them to the footer. Native rows allow eight buttons. Malformed/oversized/unclosed blocks activate nothing; drafts hide them.
|
|
64
|
+
- Prefer one matrix per related group. Fenced blocks stay in place in Rich mode; HTML compatibility moves them to the footer. Native rows allow eight buttons. Malformed/oversized/unclosed blocks activate nothing; drafts hide them. Enclosing code fences and quoted/list-prefixed button fences remain literal.
|
|
65
65
|
- Both placements share prompt/app routing. In-body clicks acknowledge without recoloring; selected-style highlighting remains footer-only.
|
|
66
66
|
|
|
67
67
|
A single in-body button (the four-backtick wrapper below makes this a literal example):
|
|
@@ -124,6 +124,6 @@ Before replying:
|
|
|
124
124
|
|
|
125
125
|
- Use the ordinary path for the current target and direct tools only for explicit other delivery.
|
|
126
126
|
- Attach requested files rather than only mentioning them.
|
|
127
|
-
- Keep
|
|
127
|
+
- Keep comments complete and button fences top-level and canonical: CML first, JSON when necessary; either syntax may coexist within one matrix.
|
|
128
128
|
- Give every enabled button a self-contained prompt; preserve confirmation for dangerous actions.
|
|
129
129
|
- Expose no secret or hidden reasoning.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@llblab/pi-codex-usage": "0.13.2",
|
|
51
51
|
"@llblab/pi-grow-loop": "0.9.0",
|
|
52
52
|
"@llblab/pi-state-flow": "0.26.5",
|
|
53
|
-
"@llblab/pi-telegram": "0.
|
|
53
|
+
"@llblab/pi-telegram": "0.58.3",
|
|
54
54
|
"@llblab/skills": "1.15.0"
|
|
55
55
|
},
|
|
56
56
|
"bundledDependencies": [
|