mirror of
https://github.com/wassname/pi-telegram.git
synced 2026-06-27 16:16:14 +08:00
tool verbose
This commit is contained in:
+39
-3
@@ -62,6 +62,8 @@ export interface TelegramMenuEffectPort {
|
||||
setCurrentModel: (model: Model<any>) => void;
|
||||
setThinkingLevel: (level: ThinkingLevel) => void;
|
||||
getCurrentThinkingLevel: () => ThinkingLevel;
|
||||
setTraceVisible: (traceVisible: boolean) => void;
|
||||
getTraceVisible: () => boolean;
|
||||
stagePendingModelSwitch: (selection: ScopedTelegramModel) => void;
|
||||
restartInterruptedTelegramTurn: (
|
||||
selection: ScopedTelegramModel,
|
||||
@@ -70,7 +72,12 @@ export interface TelegramMenuEffectPort {
|
||||
|
||||
export type TelegramStatusMenuCallbackDeps = Pick<
|
||||
TelegramMenuEffectPort,
|
||||
"updateModelMenuMessage" | "updateThinkingMenuMessage" | "answerCallbackQuery"
|
||||
| "updateModelMenuMessage"
|
||||
| "updateThinkingMenuMessage"
|
||||
| "updateStatusMessage"
|
||||
| "setTraceVisible"
|
||||
| "getTraceVisible"
|
||||
| "answerCallbackQuery"
|
||||
>;
|
||||
|
||||
export type TelegramThinkingMenuCallbackDeps = Pick<
|
||||
@@ -121,7 +128,7 @@ export interface BuildTelegramModelMenuStateParams {
|
||||
|
||||
export type TelegramMenuCallbackAction =
|
||||
| { kind: "ignore" }
|
||||
| { kind: "status"; action: "model" | "thinking" }
|
||||
| { kind: "status"; action: "model" | "thinking" | "trace" }
|
||||
| { kind: "thinking:set"; level: string }
|
||||
| {
|
||||
kind: "model";
|
||||
@@ -451,6 +458,9 @@ export function parseTelegramMenuCallbackAction(
|
||||
if (data === "status:thinking") {
|
||||
return { kind: "status", action: "thinking" };
|
||||
}
|
||||
if (data === "status:trace") {
|
||||
return { kind: "status", action: "trace" };
|
||||
}
|
||||
if (data?.startsWith("thinking:set:")) {
|
||||
return {
|
||||
kind: "thinking:set",
|
||||
@@ -665,6 +675,16 @@ export async function handleTelegramStatusMenuCallbackAction(
|
||||
await deps.answerCallbackQuery(callbackQueryId);
|
||||
return true;
|
||||
}
|
||||
if (action.kind === "status" && action.action === "trace") {
|
||||
const nextTraceVisible = !deps.getTraceVisible();
|
||||
deps.setTraceVisible(nextTraceVisible);
|
||||
await deps.updateStatusMessage();
|
||||
await deps.answerCallbackQuery(
|
||||
callbackQueryId,
|
||||
`Trace: ${nextTraceVisible ? "on" : "off"}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (!(action.kind === "status" && action.action === "thinking")) {
|
||||
return false;
|
||||
}
|
||||
@@ -793,6 +813,7 @@ export function buildThinkingMenuReplyMarkup(
|
||||
export function buildStatusReplyMarkup(
|
||||
activeModel: Model<any> | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
traceVisible: boolean,
|
||||
): TelegramReplyMarkup {
|
||||
const rows: Array<Array<{ text: string; callback_data: string }>> = [];
|
||||
rows.push([
|
||||
@@ -804,6 +825,12 @@ export function buildStatusReplyMarkup(
|
||||
callback_data: "status:model",
|
||||
},
|
||||
]);
|
||||
rows.push([
|
||||
{
|
||||
text: formatStatusButtonLabel("Trace", traceVisible ? "on" : "off"),
|
||||
callback_data: "status:trace",
|
||||
},
|
||||
]);
|
||||
if (activeModel?.reasoning) {
|
||||
rows.push([
|
||||
{
|
||||
@@ -847,12 +874,17 @@ export function buildTelegramStatusMenuRenderPayload(
|
||||
statusText: string,
|
||||
activeModel: Model<any> | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
traceVisible: boolean,
|
||||
): TelegramMenuRenderPayload {
|
||||
return {
|
||||
nextMode: "status",
|
||||
text: statusText,
|
||||
mode: "html",
|
||||
replyMarkup: buildStatusReplyMarkup(activeModel, currentThinkingLevel),
|
||||
replyMarkup: buildStatusReplyMarkup(
|
||||
activeModel,
|
||||
currentThinkingLevel,
|
||||
traceVisible,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -897,12 +929,14 @@ export async function updateTelegramStatusMessage(
|
||||
statusText: string,
|
||||
activeModel: Model<any> | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
traceVisible: boolean,
|
||||
deps: TelegramMenuMessageRuntimeDeps,
|
||||
): Promise<void> {
|
||||
const payload = buildTelegramStatusMenuRenderPayload(
|
||||
statusText,
|
||||
activeModel,
|
||||
currentThinkingLevel,
|
||||
traceVisible,
|
||||
);
|
||||
state.mode = payload.nextMode;
|
||||
await deps.editInteractiveMessage(
|
||||
@@ -919,12 +953,14 @@ export async function sendTelegramStatusMessage(
|
||||
statusText: string,
|
||||
activeModel: Model<any> | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
traceVisible: boolean,
|
||||
deps: TelegramMenuMessageRuntimeDeps,
|
||||
): Promise<number | undefined> {
|
||||
const payload = buildTelegramStatusMenuRenderPayload(
|
||||
statusText,
|
||||
activeModel,
|
||||
currentThinkingLevel,
|
||||
traceVisible,
|
||||
);
|
||||
state.mode = payload.nextMode;
|
||||
return deps.sendInteractiveMessage(
|
||||
|
||||
+19
-7
@@ -8,11 +8,28 @@ import type {
|
||||
ExtensionCommandContext,
|
||||
ExtensionContext,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
||||
import { queueTelegramAttachments } from "./attachments.ts";
|
||||
import type { PendingTelegramTurn } from "./queue.ts";
|
||||
|
||||
function buildAttachmentParametersSchema(maxAttachmentsPerTurn: number) {
|
||||
return {
|
||||
type: "object",
|
||||
properties: {
|
||||
paths: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
description: "Local file path to attach",
|
||||
},
|
||||
minItems: 1,
|
||||
maxItems: maxAttachmentsPerTurn,
|
||||
},
|
||||
},
|
||||
required: ["paths"],
|
||||
};
|
||||
}
|
||||
|
||||
// --- Tool Registration ---
|
||||
|
||||
export interface TelegramAttachmentToolRegistrationDeps {
|
||||
@@ -34,12 +51,7 @@ export function registerTelegramAttachmentTool(
|
||||
promptGuidelines: [
|
||||
"When handling a [telegram] message and the user asked for a file or generated artifact, call telegram_attach with the local path instead of only mentioning the path in text.",
|
||||
],
|
||||
parameters: Type.Object({
|
||||
paths: Type.Array(
|
||||
Type.String({ description: "Local file path to attach" }),
|
||||
{ minItems: 1, maxItems: deps.maxAttachmentsPerTurn },
|
||||
),
|
||||
}),
|
||||
parameters: buildAttachmentParametersSchema(deps.maxAttachmentsPerTurn),
|
||||
async execute(_toolCallId, params) {
|
||||
return queueTelegramAttachments({
|
||||
activeTurn: deps.getActiveTurn(),
|
||||
|
||||
@@ -5,6 +5,100 @@
|
||||
|
||||
export const MAX_MESSAGE_LENGTH = 4096;
|
||||
|
||||
export type TelegramAssistantDisplayBlock =
|
||||
| { type: "text"; text: string }
|
||||
| { type: "thinking"; text: string }
|
||||
| { type: "tool_call"; name: string; argsText?: string };
|
||||
|
||||
function truncateDisplayText(text: string, maxLength: number): string {
|
||||
if (text.length <= maxLength) return text;
|
||||
return `${text.slice(0, Math.max(0, maxLength - 1))}…`;
|
||||
}
|
||||
|
||||
function normalizePreviewInlineText(text: string): string {
|
||||
return renderMarkdownPreviewText(text).replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function renderTracePreviewLine(block: TelegramAssistantDisplayBlock): string | undefined {
|
||||
if (block.type === "text") return undefined;
|
||||
if (block.type === "thinking") {
|
||||
const summary = normalizePreviewInlineText(block.text);
|
||||
if (!summary) return undefined;
|
||||
return `[thinking] ${truncateDisplayText(summary, 120)}`;
|
||||
}
|
||||
const parts = [`[tool] ${block.name}`];
|
||||
if (block.argsText?.trim()) {
|
||||
const summary = normalizePreviewInlineText(block.argsText);
|
||||
if (summary) parts.push(summary);
|
||||
}
|
||||
return truncateDisplayText(parts.join(" "), 160);
|
||||
}
|
||||
|
||||
function renderMarkdownQuote(text: string): string {
|
||||
return text
|
||||
.split(/\r?\n/)
|
||||
.map((line) => `> ${line.length > 0 ? line : "\u00A0"}`)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function renderToolArgsMarkdown(argsText: string): string {
|
||||
const trimmed = argsText.trim();
|
||||
if (trimmed.length === 0) return "";
|
||||
if (trimmed.includes("\n") || trimmed.length > 120) {
|
||||
return `\n\n\`\`\`json\n${trimmed}\n\`\`\``;
|
||||
}
|
||||
return ` ${"`"}${trimmed}${"`"}`;
|
||||
}
|
||||
|
||||
export function buildTelegramAssistantPreviewText(
|
||||
blocks: TelegramAssistantDisplayBlock[],
|
||||
traceVisible: boolean,
|
||||
): string {
|
||||
const sections: string[] = [];
|
||||
const text = blocks
|
||||
.filter((block): block is Extract<TelegramAssistantDisplayBlock, { type: "text" }> => block.type === "text")
|
||||
.map((block) => block.text)
|
||||
.join("")
|
||||
.trim();
|
||||
if (text) {
|
||||
sections.push(text);
|
||||
}
|
||||
if (traceVisible) {
|
||||
const traceLines = blocks
|
||||
.map(renderTracePreviewLine)
|
||||
.filter((line): line is string => !!line);
|
||||
if (traceLines.length > 0) {
|
||||
sections.push(traceLines.join("\n"));
|
||||
}
|
||||
}
|
||||
return sections.join("\n\n").trim();
|
||||
}
|
||||
|
||||
export function buildTelegramAssistantTranscriptMarkdown(
|
||||
blocks: TelegramAssistantDisplayBlock[],
|
||||
traceVisible: boolean,
|
||||
): string {
|
||||
const sections: string[] = [];
|
||||
for (const block of blocks) {
|
||||
if (block.type === "text") {
|
||||
const trimmed = block.text.trim();
|
||||
if (trimmed) sections.push(trimmed);
|
||||
continue;
|
||||
}
|
||||
if (!traceVisible) continue;
|
||||
if (block.type === "thinking") {
|
||||
const trimmed = block.text.trim();
|
||||
if (!trimmed) continue;
|
||||
sections.push(`**Thinking**\n${renderMarkdownQuote(trimmed)}`);
|
||||
continue;
|
||||
}
|
||||
sections.push(
|
||||
`**Tool call** ${"`"}${block.name}${"`"}${block.argsText ? renderToolArgsMarkdown(block.argsText) : ""}`,
|
||||
);
|
||||
}
|
||||
return sections.join("\n\n").trim();
|
||||
}
|
||||
|
||||
// --- Escaping ---
|
||||
|
||||
function escapeHtml(text: string): string {
|
||||
|
||||
@@ -87,6 +87,7 @@ function buildContextSummary(
|
||||
export function buildStatusHtml(
|
||||
ctx: ExtensionContext,
|
||||
activeModel: Model<any> | undefined,
|
||||
traceVisible: boolean,
|
||||
): string {
|
||||
const stats = collectUsageStats(ctx);
|
||||
const usesSubscription = activeModel
|
||||
@@ -102,6 +103,7 @@ export function buildStatusHtml(
|
||||
lines.push(buildStatusRow("Cost", costSummary));
|
||||
}
|
||||
lines.push(buildStatusRow("Context", buildContextSummary(ctx, activeModel)));
|
||||
lines.push(buildStatusRow("Trace", traceVisible ? "on" : "off"));
|
||||
if (lines.length === 0) {
|
||||
lines.push(buildStatusRow("Status", "No usage data yet."));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user