@llblab/pi-telegram 0.13.0 → 0.13.2
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/CHANGELOG.md +15 -1
- package/index.ts +9 -5
- package/lib/bindings.ts +1 -0
- package/lib/config.ts +35 -2
- package/lib/inbound.ts +27 -7
- package/lib/lifecycle.ts +25 -8
- package/lib/preview.ts +1 -0
- package/lib/rendering.ts +20 -1
- package/lib/routing.ts +2 -2
- package/lib/status.ts +29 -4
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
- Queue admission is explicit and validated: immediate commands, control lane, priority lane, and default lane must preserve allowed kind/lane pairings
|
|
61
61
|
- Dispatch is gated by active turns, pending dispatch, unsettled control work, compaction, `ctx.isIdle()`, and π pending messages; dispatched prompts remain queued until `agent_start` consumes them
|
|
62
62
|
- Telegram `/compact` owns a native `typing` keepalive for the compaction window so phone clients show activity between the started/completed notices; stop it on both completion and failure
|
|
63
|
-
- `/stop`, `/abort`, `/next`, and `/continue` have distinct contracts: reset queue and abort; abort while preserving queue; force next queued turn; enqueue a
|
|
63
|
+
- `/stop`, `/abort`, `/next`, and `/continue` have distinct contracts: reset queue and abort; abort while preserving queue; force next queued turn; enqueue a control-lane `continue` resume prompt without folding queued prompts into history
|
|
64
64
|
- `/start`, `/help`, and `/status` open the unified command-help/status-row/control menu; `/model`, `/thinking`, and `/queue` jump to sections directly; visible bot commands are `/start`, `/compact`, `/next`, `/continue`, `/abort`, `/stop`
|
|
65
65
|
- Command/menu emoji are fixed UI adornments owned by the `commands` map; do not add a persisted emoji toggle or Settings menu until there is a real setting to own
|
|
66
66
|
- Telegram `reply_to_message` context is prompt-only and must not affect slash-command parsing
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.13.2: Config Recovery And Inbound Output Bounds Hotfix
|
|
6
|
+
|
|
7
|
+
- `[Config]` Invalid `telegram.json` now recovers on session startup by renaming the broken file to an `.invalid-*` recovery path, loading safe empty defaults, and recording a runtime diagnostic. Impact: a hand-edited or partially written config no longer bricks `/telegram-setup` or session startup.
|
|
8
|
+
- `[Inbound]` Inbound handler, programmatic handler, voice transcription, and built-in text attachment outputs are now bounded before entering Telegram prompt context. Impact: large OCR, PDF, STT, or text-file outputs cannot silently explode prompt size.
|
|
9
|
+
- `[Diagnostics]` Runtime event messages/details and inbound handler failure stdout/stderr are truncated before storage/rendering. Impact: `/telegram-status` remains useful after noisy provider or handler failures without hiding that truncation happened.
|
|
10
|
+
|
|
11
|
+
## 0.13.1: Rendering, Typing, And Continue Queue Hotfix
|
|
12
|
+
|
|
13
|
+
- `[Rendering]` Fixed Telegram HTML rendering for Markdown bold/italic spans that cross soft line breaks, so assistant replies like `**first line\nsecond line**` render as bold text instead of showing raw asterisks. Added a regression for the guest-mode-style multiline bold reply shape.
|
|
14
|
+
- `[Typing Status]` Hardened assistant message activity hooks so transient preview/provider transport failures are recorded but do not break the native Telegram `typing` keepalive while an active turn continues.
|
|
15
|
+
- `[Continue Queue]` `/continue` now enqueues as a control-lane resume prompt and explicitly clears preserved-abort history mode, so queued Telegram prompts stay separate and the continuation runs ahead of queued prompt work after abort or compaction recovery.
|
|
16
|
+
|
|
17
|
+
## 0.13.0: Command Template Standard, Voice Hardening, And Domain Cleanup
|
|
4
18
|
|
|
5
19
|
- `[Architecture]` Extracted outbound assistant-action markup parsing into `lib/outbound-markup.ts` and removed the temporary Voice/Outbound/Queue import-cycle allowance. Impact: project source imports are fully acyclic again while preserving existing voice and outbound helper exports.
|
|
6
20
|
- `[Tests]` Updated the Pi SDK centralization invariant to guard the current `@earendil-works/*` package scope as well as the legacy scope. Impact: new direct SDK imports outside `lib/pi.ts` are caught again.
|
package/index.ts
CHANGED
|
@@ -48,7 +48,15 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
48
48
|
} = piRuntime;
|
|
49
49
|
const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
|
|
50
50
|
const { abort, lifecycle, queue, setup, typing } = bridgeRuntime;
|
|
51
|
-
|
|
51
|
+
let configStoreForRedaction: Config.TelegramConfigStore | undefined;
|
|
52
|
+
const runtimeEvents = Status.createTelegramRuntimeEventRecorder({
|
|
53
|
+
getBotToken() {
|
|
54
|
+
return configStoreForRedaction?.getBotToken();
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const recordRuntimeEvent = runtimeEvents.record;
|
|
58
|
+
const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
|
|
59
|
+
configStoreForRedaction = configStore;
|
|
52
60
|
Config.bindGlobalTelegramConfigRuntime(configStore);
|
|
53
61
|
const configControls = Config.createTelegramConfigControls(configStore);
|
|
54
62
|
const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>();
|
|
@@ -68,10 +76,6 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
68
76
|
const modelMenuRuntime = Menu.createTelegramModelMenuRuntime<ActivePiModel>();
|
|
69
77
|
const sectionRegistry = Sections.createAndBindTelegramSectionRegistry();
|
|
70
78
|
|
|
71
|
-
const runtimeEvents = Status.createTelegramRuntimeEventRecorder({
|
|
72
|
-
getBotToken: configStore.getBotToken,
|
|
73
|
-
});
|
|
74
|
-
const recordRuntimeEvent = runtimeEvents.record;
|
|
75
79
|
const timeInjectionRuntime = TimeInjection.createTimeInjectionRuntime({
|
|
76
80
|
getConfig: Config.createTelegramTimeConfigGetter(configStore),
|
|
77
81
|
recordRuntimeEvent,
|
package/lib/bindings.ts
CHANGED
|
@@ -279,6 +279,7 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
279
279
|
startTypingLoop: promptDispatchRuntime.startTypingLoop,
|
|
280
280
|
onMessageStart: previewRuntime.onMessageStart,
|
|
281
281
|
onMessageUpdate: previewRuntime.onMessageUpdate,
|
|
282
|
+
recordRuntimeEvent,
|
|
282
283
|
});
|
|
283
284
|
Lifecycle.registerTelegramLifecycleHooks(pi, {
|
|
284
285
|
...sessionLifecycleRuntime,
|
package/lib/config.ts
CHANGED
|
@@ -84,6 +84,17 @@ export interface TelegramConfigStoreOptions {
|
|
|
84
84
|
initialConfig?: TelegramConfig;
|
|
85
85
|
agentDir?: string;
|
|
86
86
|
configPath?: string;
|
|
87
|
+
recordRuntimeEvent?: (
|
|
88
|
+
category: string,
|
|
89
|
+
error: unknown,
|
|
90
|
+
details?: Record<string, unknown>,
|
|
91
|
+
) => void;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface TelegramInvalidConfigRecovery {
|
|
95
|
+
configPath: string;
|
|
96
|
+
recoveryPath: string;
|
|
97
|
+
error: unknown;
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
export interface TelegramConfigRuntime {
|
|
@@ -125,12 +136,26 @@ export function bindGlobalTelegramConfigRuntime(
|
|
|
125
136
|
});
|
|
126
137
|
}
|
|
127
138
|
|
|
139
|
+
function getInvalidTelegramConfigRecoveryPath(configPath: string): string {
|
|
140
|
+
return `${configPath}.invalid-${process.pid}-${Date.now()}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
128
143
|
export async function readTelegramConfig(
|
|
129
144
|
configPath: string,
|
|
145
|
+
options: {
|
|
146
|
+
onInvalidConfig?: (recovery: TelegramInvalidConfigRecovery) => void;
|
|
147
|
+
} = {},
|
|
130
148
|
): Promise<TelegramConfig> {
|
|
131
149
|
if (!existsSync(configPath)) return {};
|
|
132
150
|
const content = await readFile(configPath, "utf8");
|
|
133
|
-
|
|
151
|
+
try {
|
|
152
|
+
return JSON.parse(content) as TelegramConfig;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
const recoveryPath = getInvalidTelegramConfigRecoveryPath(configPath);
|
|
155
|
+
await rename(configPath, recoveryPath);
|
|
156
|
+
options.onInvalidConfig?.({ configPath, recoveryPath, error });
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
134
159
|
}
|
|
135
160
|
|
|
136
161
|
export async function writeTelegramConfig(
|
|
@@ -176,7 +201,15 @@ export function createTelegramConfigStore(
|
|
|
176
201
|
config.allowedUserId = userId;
|
|
177
202
|
},
|
|
178
203
|
load: async () => {
|
|
179
|
-
config = await readTelegramConfig(configPath
|
|
204
|
+
config = await readTelegramConfig(configPath, {
|
|
205
|
+
onInvalidConfig: (recovery) => {
|
|
206
|
+
options.recordRuntimeEvent?.("config", recovery.error, {
|
|
207
|
+
phase: "load",
|
|
208
|
+
configPath: recovery.configPath,
|
|
209
|
+
recoveryPath: recovery.recoveryPath,
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
});
|
|
180
213
|
},
|
|
181
214
|
persist: async (nextConfig = config) => {
|
|
182
215
|
await writeTelegramConfig(agentDir, configPath, nextConfig);
|
package/lib/inbound.ts
CHANGED
|
@@ -19,6 +19,8 @@ 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 = 12_000;
|
|
23
|
+
const MAX_INBOUND_HANDLER_FAILURE_STREAM_LENGTH = 4_000;
|
|
22
24
|
|
|
23
25
|
type TelegramInboundCommandTemplateConfig =
|
|
24
26
|
| string
|
|
@@ -187,12 +189,28 @@ export function clearTelegramInboundHandlers(): void {
|
|
|
187
189
|
getOrCreateInboundHandlerRegistry().handlers.clear();
|
|
188
190
|
}
|
|
189
191
|
|
|
192
|
+
function truncateTelegramInboundText(text: string, maxLength: number): string {
|
|
193
|
+
if (text.length <= maxLength) return text;
|
|
194
|
+
return `${text.slice(0, maxLength).trimEnd()}… [truncated ${text.length - maxLength} chars]`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function truncateTelegramInboundOutput(text: string): string {
|
|
198
|
+
return truncateTelegramInboundText(text, MAX_INBOUND_HANDLER_OUTPUT_LENGTH);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function truncateTelegramInboundFailureStream(text: string): string {
|
|
202
|
+
return truncateTelegramInboundText(
|
|
203
|
+
text.trimEnd(),
|
|
204
|
+
MAX_INBOUND_HANDLER_FAILURE_STREAM_LENGTH,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
190
208
|
function normalizeInboundProgrammaticHandlerText(
|
|
191
209
|
result: TelegramInboundProgrammaticHandlerResult,
|
|
192
210
|
): string | undefined {
|
|
193
211
|
const text = typeof result === "string" ? result : result?.text;
|
|
194
212
|
const normalized = text?.trim();
|
|
195
|
-
return normalized
|
|
213
|
+
return normalized ? truncateTelegramInboundOutput(normalized) : undefined;
|
|
196
214
|
}
|
|
197
215
|
|
|
198
216
|
function normalizeStringList(value: string | string[] | undefined): string[] {
|
|
@@ -401,8 +419,10 @@ function formatTelegramInboundHandlerFailure(
|
|
|
401
419
|
const parts = [
|
|
402
420
|
`Inbound handler exited with code ${result.code}${result.killed ? " (killed)" : ""}`,
|
|
403
421
|
];
|
|
404
|
-
if (result.stderr.trim())
|
|
405
|
-
|
|
422
|
+
if (result.stderr.trim())
|
|
423
|
+
parts.push(`stderr:\n${truncateTelegramInboundFailureStream(result.stderr)}`);
|
|
424
|
+
if (result.stdout.trim())
|
|
425
|
+
parts.push(`stdout:\n${truncateTelegramInboundFailureStream(result.stdout)}`);
|
|
406
426
|
return parts.join("\n\n");
|
|
407
427
|
}
|
|
408
428
|
|
|
@@ -431,7 +451,7 @@ async function executeTelegramInboundHandlerInvocation(
|
|
|
431
451
|
});
|
|
432
452
|
if (result.code !== 0)
|
|
433
453
|
throw new Error(formatTelegramInboundHandlerFailure(result));
|
|
434
|
-
return result.stdout;
|
|
454
|
+
return truncateTelegramInboundOutput(result.stdout);
|
|
435
455
|
}
|
|
436
456
|
|
|
437
457
|
function getTelegramInboundHandlerCompositionSteps(
|
|
@@ -504,7 +524,7 @@ async function executeTelegramTextHandlerInvocation(
|
|
|
504
524
|
});
|
|
505
525
|
if (result.code !== 0)
|
|
506
526
|
throw new Error(formatTelegramInboundHandlerFailure(result));
|
|
507
|
-
return result.stdout;
|
|
527
|
+
return truncateTelegramInboundOutput(result.stdout);
|
|
508
528
|
}
|
|
509
529
|
|
|
510
530
|
async function executeTelegramTextHandler(
|
|
@@ -633,7 +653,7 @@ async function transcribeTelegramVoiceFileWithProviders(
|
|
|
633
653
|
try {
|
|
634
654
|
const result = await provider(file, {});
|
|
635
655
|
const text = typeof result === "string" ? result : result?.text;
|
|
636
|
-
if (text?.trim()) return text.trim();
|
|
656
|
+
if (text?.trim()) return truncateTelegramInboundOutput(text.trim());
|
|
637
657
|
} catch (error) {
|
|
638
658
|
options.recordRuntimeEvent?.("voice-transcription-provider", error, {
|
|
639
659
|
fileName: file.fileName || basename(file.path),
|
|
@@ -656,7 +676,7 @@ async function readBuiltInTelegramTextAttachment(
|
|
|
656
676
|
return undefined;
|
|
657
677
|
}
|
|
658
678
|
const name = file.fileName || basename(file.path);
|
|
659
|
-
return `[${name}]\n${normalized}
|
|
679
|
+
return truncateTelegramInboundOutput(`[${name}]\n${normalized}`);
|
|
660
680
|
}
|
|
661
681
|
|
|
662
682
|
async function executeTelegramInboundHandler(
|
package/lib/lifecycle.ts
CHANGED
|
@@ -182,6 +182,11 @@ export interface TelegramMessageActivityTypingDeps<TContext> {
|
|
|
182
182
|
startTypingLoop: (ctx: TContext) => void;
|
|
183
183
|
onMessageStart: TelegramLifecycleRegistrationDeps["onMessageStart"];
|
|
184
184
|
onMessageUpdate: TelegramLifecycleRegistrationDeps["onMessageUpdate"];
|
|
185
|
+
recordRuntimeEvent?: (
|
|
186
|
+
category: string,
|
|
187
|
+
error: unknown,
|
|
188
|
+
details?: Record<string, unknown>,
|
|
189
|
+
) => void;
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
export function createTelegramMessageActivityTypingHooks<
|
|
@@ -195,15 +200,27 @@ export function createTelegramMessageActivityTypingHooks<
|
|
|
195
200
|
const ensureTyping = (ctx: TContext): void => {
|
|
196
201
|
if (deps.hasActiveTurn()) deps.startTypingLoop(ctx);
|
|
197
202
|
};
|
|
203
|
+
const handleMessageActivity = async (
|
|
204
|
+
phase: "start" | "update",
|
|
205
|
+
event: Parameters<TelegramLifecycleRegistrationDeps["onMessageStart"]>[0],
|
|
206
|
+
ctx: ExtensionContext,
|
|
207
|
+
inner: TelegramLifecycleRegistrationDeps["onMessageStart"],
|
|
208
|
+
): Promise<void> => {
|
|
209
|
+
const typedCtx = ctx as TContext;
|
|
210
|
+
ensureTyping(typedCtx);
|
|
211
|
+
try {
|
|
212
|
+
await inner(event, ctx);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
deps.recordRuntimeEvent?.("message-activity", error, { phase });
|
|
215
|
+
} finally {
|
|
216
|
+
ensureTyping(typedCtx);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
198
219
|
return {
|
|
199
|
-
onMessageStart:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
onMessageUpdate: async (event, ctx) => {
|
|
204
|
-
ensureTyping(ctx as TContext);
|
|
205
|
-
await deps.onMessageUpdate(event, ctx);
|
|
206
|
-
},
|
|
220
|
+
onMessageStart: (event, ctx) =>
|
|
221
|
+
handleMessageActivity("start", event, ctx, deps.onMessageStart),
|
|
222
|
+
onMessageUpdate: (event, ctx) =>
|
|
223
|
+
handleMessageActivity("update", event, ctx, deps.onMessageUpdate),
|
|
207
224
|
};
|
|
208
225
|
}
|
|
209
226
|
|
package/lib/preview.ts
CHANGED
package/lib/rendering.ts
CHANGED
|
@@ -843,7 +843,7 @@ function renderDelimitedInlineStyle(
|
|
|
843
843
|
): string {
|
|
844
844
|
const escapedDelimiter = delimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
845
845
|
const pattern = new RegExp(
|
|
846
|
-
`(^|[^\\p{L}\\p{N}\\\\])(${escapedDelimiter})(?=\\S)(
|
|
846
|
+
`(^|[^\\p{L}\\p{N}\\\\])(${escapedDelimiter})(?=\\S)([\\s\\S]+?)(?<=\\S)\\2(?=[^\\p{L}\\p{N}]|$)`,
|
|
847
847
|
"gu",
|
|
848
848
|
);
|
|
849
849
|
return text.replace(
|
|
@@ -1004,9 +1004,28 @@ function renderMarkdownTextPiece(piece: string): string {
|
|
|
1004
1004
|
return renderInlineMarkdown(piece);
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
|
+
function isPlainInlineMarkdownLine(line: string): boolean {
|
|
1008
|
+
if (line.trim().length === 0) return false;
|
|
1009
|
+
return (
|
|
1010
|
+
matchMarkdownHeadingLine(line) === null &&
|
|
1011
|
+
!/^(\s*)([-*+]|\d+\.)\s+\[([ xX])\]\s+(.+)$/.test(line) &&
|
|
1012
|
+
!/^(\s*)[-*+]\s+(.+)$/.test(line) &&
|
|
1013
|
+
!/^(\s*)(\d+)\.\s+(.+)$/.test(line) &&
|
|
1014
|
+
!/^>\s?(.+)$/.test(line) &&
|
|
1015
|
+
!/^([-*_]\s*){3,}$/.test(line.trim())
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1007
1019
|
function renderMarkdownTextLines(block: string): string[] {
|
|
1008
1020
|
const rendered: string[] = [];
|
|
1009
1021
|
const lines = block.split("\n");
|
|
1022
|
+
const nonBlankLines = lines.filter((line) => line.trim().length > 0);
|
|
1023
|
+
if (
|
|
1024
|
+
nonBlankLines.length > 1 &&
|
|
1025
|
+
nonBlankLines.every(isPlainInlineMarkdownLine)
|
|
1026
|
+
) {
|
|
1027
|
+
return renderInlineMarkdown(nonBlankLines.join("\n")).split("\n");
|
|
1028
|
+
}
|
|
1010
1029
|
for (const line of lines) {
|
|
1011
1030
|
if (line.trim().length === 0) continue;
|
|
1012
1031
|
for (const piece of splitPlainMarkdownLine(line)) {
|
package/lib/routing.ts
CHANGED
|
@@ -351,8 +351,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
351
351
|
const turn = await promptTurnBuilder([continueMessage], [], ctx);
|
|
352
352
|
const continueTurn = {
|
|
353
353
|
...turn,
|
|
354
|
-
queueLane: "
|
|
355
|
-
laneOrder:
|
|
354
|
+
queueLane: "control" as const,
|
|
355
|
+
laneOrder: deps.bridgeRuntime.queue.allocateControlOrder(),
|
|
356
356
|
statusSummary: "continue",
|
|
357
357
|
};
|
|
358
358
|
deps.queueMutationRuntime.append(continueTurn, ctx);
|
package/lib/status.ts
CHANGED
|
@@ -53,6 +53,8 @@ export interface TelegramStatusContext {
|
|
|
53
53
|
export type TelegramRuntimeEventDetailValue = string | number | boolean | null;
|
|
54
54
|
|
|
55
55
|
const MAX_RECENT_TELEGRAM_RUNTIME_EVENTS = 10;
|
|
56
|
+
const MAX_TELEGRAM_RUNTIME_EVENT_MESSAGE_LENGTH = 1000;
|
|
57
|
+
const MAX_TELEGRAM_RUNTIME_EVENT_DETAIL_LENGTH = 1000;
|
|
56
58
|
|
|
57
59
|
export interface TelegramRuntimeEvent {
|
|
58
60
|
at: number;
|
|
@@ -164,12 +166,35 @@ export interface TelegramStatusRuntime<
|
|
|
164
166
|
getStatusLines: () => string[];
|
|
165
167
|
}
|
|
166
168
|
|
|
169
|
+
function truncateTelegramRuntimeEventText(text: string, maxLength: number): string {
|
|
170
|
+
if (text.length <= maxLength) return text;
|
|
171
|
+
return `${text.slice(0, maxLength).trimEnd()}… [truncated ${text.length - maxLength} chars]`;
|
|
172
|
+
}
|
|
173
|
+
|
|
167
174
|
export function redactTelegramRuntimeMessage(
|
|
168
175
|
message: string,
|
|
169
176
|
botToken: string | undefined,
|
|
170
177
|
): string {
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
const redacted = botToken
|
|
179
|
+
? message.split(botToken).join("<redacted-token>")
|
|
180
|
+
: message;
|
|
181
|
+
return truncateTelegramRuntimeEventText(
|
|
182
|
+
redacted,
|
|
183
|
+
MAX_TELEGRAM_RUNTIME_EVENT_MESSAGE_LENGTH,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function redactTelegramRuntimeDetail(
|
|
188
|
+
message: string,
|
|
189
|
+
botToken: string | undefined,
|
|
190
|
+
): string {
|
|
191
|
+
const redacted = botToken
|
|
192
|
+
? message.split(botToken).join("<redacted-token>")
|
|
193
|
+
: message;
|
|
194
|
+
return truncateTelegramRuntimeEventText(
|
|
195
|
+
redacted,
|
|
196
|
+
MAX_TELEGRAM_RUNTIME_EVENT_DETAIL_LENGTH,
|
|
197
|
+
);
|
|
173
198
|
}
|
|
174
199
|
|
|
175
200
|
function normalizeTelegramRuntimeEventDetails(
|
|
@@ -181,7 +206,7 @@ function normalizeTelegramRuntimeEventDetails(
|
|
|
181
206
|
for (const [key, value] of Object.entries(details)) {
|
|
182
207
|
if (value === undefined) continue;
|
|
183
208
|
if (typeof value === "string") {
|
|
184
|
-
normalized[key] =
|
|
209
|
+
normalized[key] = redactTelegramRuntimeDetail(value, botToken);
|
|
185
210
|
continue;
|
|
186
211
|
}
|
|
187
212
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
@@ -192,7 +217,7 @@ function normalizeTelegramRuntimeEventDetails(
|
|
|
192
217
|
normalized[key] = null;
|
|
193
218
|
continue;
|
|
194
219
|
}
|
|
195
|
-
normalized[key] =
|
|
220
|
+
normalized[key] = redactTelegramRuntimeDetail(String(value), botToken);
|
|
196
221
|
}
|
|
197
222
|
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
198
223
|
}
|