Files
dsh-telegram-channel/lib/commands.d.ts
T
RRRosmontis cde6979f2d feat: resilient delivery, live progress visibility, TG-side answering, /stop /mission /new
Delivery & visibility:
- per-chunk retry with capped backoff on rich/HTML sends; rich failure now
  falls back to HTML instead of dropping the reply
- typing heartbeat: refresh sendChatAction every 4s for the whole turn
  (a single action expires in ~5s, long turns looked dead)
- turn/end error / abort / crash-recovery notices reach Telegram
  (API failures were previously invisible)
- tool/call progress summaries, tool/result failure notices, todo progress
- thinking indicator (one notice per reasoning phase, status only)
- notices prefixed '> ' render as Telegram blockquotes; plain-text fallback
- non-text messages get a hint instead of being silently dropped
- busy feedback: a followup queued mid-turn gets an explicit ack

TG-side answering (dual-path race; UI path untouched, first answer wins):
- ask_user_question questions render with letter options on TG; reply A /
  [A] / A,B (multi) / free text / '1: A' per-line for multi-question
- approval requests (approval/request waterfall) answerable from TG:
  [A] allow once / [B] reject; /cancel closes only the TG side

Commands: /stop (cancel current turn), /mission (todo snapshot + busy state),
/new (create a session in the current workspace and attach)

Persistence: chat→session bindings stored at $DSH_HOME/
telegram-channel-bindings.json (override: DSH_TELEGRAM_BINDINGS_FILE) and
restored on start — hot reloads and restarts no longer drop attachments.

Host seams without public surface yet are reached with documented structural
casts (userQuestions provider wrap, approval/request waterfall).

Tests: 41/41 pass. Two pre-existing environment failures in bridge.test.ts
(/last history mirror, assistant mirror) now pass: the former was fixed by
the rich→HTML fallback, the latter needed an event-loop flush after the
(void) listener call since resilient delivery lengthens the chain.
2026-09-01 16:06:00 +08:00

69 lines
2.5 KiB
TypeScript

export declare const MSG: {
readonly DENIED: "无权限。";
readonly WELCOME: string;
readonly HELP: string;
readonly NEED_BIND: "尚未绑定本机会话。请先发送 /sessions 选择一个。";
readonly NO_SESSIONS: "当前没有可附着的本机会话(已排除归档与空白会话)。请先在 Web(dsh web)打开或继续一个对话,再发 /sessions。";
readonly NO_SESSIONS_IN_WS: (title: string) => string;
/** @deprecated use NO_SESSIONS */
readonly NO_LIVE: "当前没有可附着的本机会话。请先在 Web(dsh web)打开或继续一个对话,再发 /sessions。";
readonly PICKER_STALE: "列表已过期,请重新发送 /sessions。";
readonly RESUME_FAILED: "无法附着该会话(resume 失败)。请确认会话存在于 Web,或先在电脑打开后再试。";
readonly BOUND: (label: string) => string;
readonly UNBOUND: "已断开绑定。本机会话仍在运行。";
readonly STATUS_NONE: "当前未绑定任何本机会话。发送 /sessions 选择。";
readonly STATUS_BOUND: (label: string) => string;
readonly STATUS_BOUND_COLD: (label: string) => string;
readonly GONE: "绑定的会话已不可用。请重新 /sessions。";
readonly LAST_FAILED: "无法读取上次对话。请确认已绑定,且本机 dsh web / apiProxy 可用。";
readonly MODEL_UNAVAILABLE: (detail?: string) => string;
readonly MODEL_UNROUTABLE: (current: string) => string;
readonly MODEL_EMPTY: (current: string) => string;
readonly MODEL_SET: (selected: string) => string;
readonly MODEL_FAILED: (detail?: string) => string;
readonly unknown: (command: string) => string;
};
export type ParsedCommand = {
type: 'start';
text: string;
} | {
type: 'help';
text: string;
} | {
type: 'sessions';
text: string;
} | {
type: 'last';
text: string;
} | {
type: 'model';
text: string;
} | {
type: 'status';
text: string;
} | {
type: 'unbind';
text: string;
} | {
type: 'stop';
text: string;
} | {
type: 'mission';
text: string;
} | {
type: 'new';
text: string;
} | {
type: 'unknown';
command: string;
text: string;
} | {
type: 'plain';
text: string;
};
export declare function parseCommand(text: string): ParsedCommand;
/** @deprecated Prefer short index callbacks (ws:/sid:); kept for old messages. */
export declare const BIND_CB_PREFIX = "bind:";
/** Inline button: fetch last Q/A for the bound session. */
export declare const LAST_CB = "last";