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.
40 lines
1.1 KiB
TypeScript
40 lines
1.1 KiB
TypeScript
import type { Context } from '@deepseek-ai/cordis';
|
|
/** Loose ApiProxy surface used by catalog + model helpers. */
|
|
export type ApiProxySessions = {
|
|
list?: (req: {
|
|
rpcId: string;
|
|
payload: unknown;
|
|
}) => Promise<unknown>;
|
|
history?: (req: {
|
|
rpcId: string;
|
|
payload: unknown;
|
|
}) => Promise<unknown>;
|
|
models?: (req: {
|
|
rpcId: string;
|
|
payload: unknown;
|
|
}) => Promise<unknown>;
|
|
selectModel?: (req: {
|
|
rpcId: string;
|
|
payload: unknown;
|
|
}) => Promise<unknown>;
|
|
/** Create a session (used by /new; payload: workspaceId | cwd). */
|
|
create?: (req: {
|
|
rpcId: string;
|
|
payload: unknown;
|
|
}) => Promise<unknown>;
|
|
};
|
|
export type ApiProxyLike = {
|
|
workspace?: {
|
|
list?: (req: {
|
|
rpcId: string;
|
|
payload: unknown;
|
|
}) => Promise<unknown>;
|
|
};
|
|
sessions?: ApiProxySessions;
|
|
};
|
|
/**
|
|
* Resolve host apiProxy without requiring Cordis inject.
|
|
* Direct `ctx.apiProxy` throws "cannot get property without inject" on real fibers.
|
|
*/
|
|
export declare function resolveApiProxy(ctx: Context): ApiProxyLike | undefined;
|