dsh-connect 0.5.0 → 0.5.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.
@@ -0,0 +1,389 @@
1
+ import { messages } from "./i18n.js";
2
+ function delay(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ /**
6
+ * Decode a free-text chat reply into the answer for one question. Exact label
7
+ * match wins; otherwise space/comma separated numbers are mapped onto the
8
+ * options (multi-select accumulates them); anything else becomes free text.
9
+ */
10
+ export function decodeTextAnswer(q, text) {
11
+ const opts = q.options ?? [];
12
+ const trimmed = text.trim();
13
+ if (opts.length === 0)
14
+ return { selected: [], custom: trimmed };
15
+ const exact = opts.findIndex((o) => o.label === trimmed);
16
+ if (exact !== -1)
17
+ return { selected: [opts[exact].label] };
18
+ const parts = trimmed
19
+ .split(/[,,、;;\s]+/)
20
+ .map((s) => s.trim())
21
+ .filter((s) => s !== "");
22
+ const labels = [...new Set(parts.filter((p) => opts.some((o) => o.label === p)))];
23
+ const nums = parts.map((p) => Number(p)).filter((n) => Number.isInteger(n) && n >= 1 && n <= opts.length);
24
+ const picked = q.multiSelect
25
+ ? [...new Set([...labels, ...nums.map((n) => opts[n - 1].label)])]
26
+ : [...new Set([...labels, ...(nums.length > 0 ? [opts[nums[0] - 1].label] : [])])].slice(0, 1);
27
+ if (picked.length > 0)
28
+ return { selected: picked };
29
+ return { selected: [], custom: trimmed };
30
+ }
31
+ /** Decode a button choice (`q:<questionId>:<optionIndex>`) back into an answer. */
32
+ function decodeChoice(q, choice) {
33
+ const prefix = `q:${q.id}:`;
34
+ if (!choice.startsWith(prefix))
35
+ return undefined;
36
+ const idx = Number(choice.slice(prefix.length));
37
+ const opt = q.options?.[idx];
38
+ if (opt === undefined)
39
+ return undefined;
40
+ return { selected: [opt.label] };
41
+ }
42
+ /**
43
+ * Bridge user choices and permission approvals from DSH into the chat channel.
44
+ * Subscribes once to the host mux stream; frames for connect-bound sessions are
45
+ * rendered as interactive cards and answered via `apiProxy.respond`.
46
+ */
47
+ export class InteractionBridge {
48
+ ctx;
49
+ adapters;
50
+ bindings;
51
+ config;
52
+ pending = new Map();
53
+ started = false;
54
+ apiProxy;
55
+ controller;
56
+ constructor(ctx, adapters, bindings, config) {
57
+ this.ctx = ctx;
58
+ this.adapters = adapters;
59
+ this.bindings = bindings;
60
+ this.config = config;
61
+ this.ctx.effect(() => () => this.controller?.abort(), "connect: interaction bridge");
62
+ }
63
+ /** Open the mux subscription (idempotent). No-op when the host api-proxy is absent. */
64
+ start() {
65
+ if (this.started)
66
+ return;
67
+ const apiProxy = this.ctx.get("apiProxy");
68
+ if (apiProxy?.events?.mux === undefined || apiProxy.respond === undefined)
69
+ return;
70
+ this.started = true;
71
+ this.apiProxy = apiProxy;
72
+ this.controller = new AbortController();
73
+ void this.run(this.controller.signal);
74
+ }
75
+ /** Whether a question/approval is waiting on an answer in this chat. */
76
+ pendingFor(chatKey) {
77
+ return this.pending.has(chatKey);
78
+ }
79
+ /** Deliver a plain-text chat reply as the answer to the pending question. */
80
+ answerText(chatKey, text) {
81
+ const p = this.pending.get(chatKey);
82
+ if (p === undefined || p.kind !== "question")
83
+ return false;
84
+ const q = p.questions[p.current];
85
+ if (q === undefined)
86
+ return false;
87
+ const answer = decodeTextAnswer(q, text);
88
+ if (answer.selected.length === 0 && (answer.custom === undefined || answer.custom.trim() === ""))
89
+ return false;
90
+ this.recordAnswer(p, q.id, answer);
91
+ return true;
92
+ }
93
+ async run(signal) {
94
+ const stream = this.apiProxy.events.mux({ rpcId: "connect-interaction", payload: {} }, signal);
95
+ try {
96
+ for await (const { rpcId, payload } of stream) {
97
+ try {
98
+ this.onFrame(rpcId, payload);
99
+ }
100
+ catch (error) {
101
+ this.log(`connect: interaction frame handler error: ${String(error)}`);
102
+ }
103
+ }
104
+ }
105
+ catch (error) {
106
+ this.log(`connect: interaction mux stream closed: ${String(error)}`);
107
+ }
108
+ }
109
+ onFrame(rpcId, payload) {
110
+ switch (payload.type) {
111
+ case "question/requested": {
112
+ const sessionId = String(payload.sessionId ?? "");
113
+ const questions = Array.isArray(payload.questions) ? payload.questions : [];
114
+ if (sessionId === "" || questions.length === 0)
115
+ return;
116
+ for (const target of this.targetsFor(sessionId)) {
117
+ this.presentQuestion(target, rpcId, sessionId, questions);
118
+ }
119
+ break;
120
+ }
121
+ case "approval/requested": {
122
+ const sessionId = String(payload.sessionId ?? "");
123
+ const approvalId = String(payload.approvalId ?? "");
124
+ if (sessionId === "" || approvalId === "")
125
+ return;
126
+ const toolName = String(payload.toolName ?? "?");
127
+ const reason = typeof payload.reason === "string" ? payload.reason : undefined;
128
+ for (const target of this.targetsFor(sessionId)) {
129
+ this.presentApproval(target, rpcId, sessionId, approvalId, toolName, reason);
130
+ }
131
+ break;
132
+ }
133
+ case "question/resolved": {
134
+ // Someone (Web GUI or this bridge) answered/cancelled the ask.
135
+ const questionRpcId = String(payload.questionRpcId ?? "");
136
+ for (const p of [...this.pending.values()]) {
137
+ if (p.kind === "question" && p.rpcId === questionRpcId)
138
+ this.settle(p);
139
+ }
140
+ break;
141
+ }
142
+ case "session/event": {
143
+ const event = payload.event;
144
+ if (event?.type !== "turn/end")
145
+ return;
146
+ const sessionId = String(payload.sessionId ?? "");
147
+ for (const p of [...this.pending.values()]) {
148
+ if (p.sessionId === sessionId)
149
+ this.settle(p);
150
+ }
151
+ break;
152
+ }
153
+ default:
154
+ break;
155
+ }
156
+ }
157
+ /** Every connect-bound chat that currently uses this session (active or mirrored). */
158
+ targetsFor(sessionId) {
159
+ const out = [];
160
+ for (const binding of this.bindings.list()) {
161
+ if (binding.sessionId !== sessionId && binding.webMirrorSessionId !== sessionId)
162
+ continue;
163
+ const adapter = this.adapters.get(binding.channel);
164
+ if (adapter === undefined)
165
+ continue;
166
+ out.push({ binding, adapter });
167
+ }
168
+ return out;
169
+ }
170
+ presentQuestion(target, rpcId, sessionId, questions) {
171
+ // One pending ask per session — a replayed frame after the mux reopens
172
+ // must not stack duplicate cards.
173
+ for (const p of this.pending.values()) {
174
+ if (p.sessionId === sessionId)
175
+ return;
176
+ }
177
+ const interaction = {
178
+ kind: "question",
179
+ chatKey: target.binding.chatKey,
180
+ chatType: target.binding.chatType,
181
+ adapter: target.adapter,
182
+ sessionId,
183
+ rpcId,
184
+ language: target.binding.language ?? this.config.language,
185
+ questions,
186
+ answers: new Map(),
187
+ current: 0,
188
+ controller: new AbortController(),
189
+ settled: false,
190
+ };
191
+ this.pending.set(interaction.chatKey, interaction);
192
+ this.stepQuestion(interaction);
193
+ }
194
+ presentApproval(target, rpcId, sessionId, approvalId, toolName, reason) {
195
+ for (const p of this.pending.values()) {
196
+ if (p.sessionId === sessionId)
197
+ return;
198
+ }
199
+ const interaction = {
200
+ kind: "approval",
201
+ chatKey: target.binding.chatKey,
202
+ chatType: target.binding.chatType,
203
+ adapter: target.adapter,
204
+ sessionId,
205
+ rpcId,
206
+ approvalId,
207
+ toolName,
208
+ ...(reason === undefined ? {} : { reason }),
209
+ language: target.binding.language ?? this.config.language,
210
+ controller: new AbortController(),
211
+ settled: false,
212
+ };
213
+ this.pending.set(interaction.chatKey, interaction);
214
+ void this.presentApprovalLoop(interaction);
215
+ }
216
+ /** Advance to the next unanswered question, or settle once all are answered. */
217
+ stepQuestion(interaction) {
218
+ while (interaction.current < interaction.questions.length &&
219
+ interaction.answers.has(interaction.questions[interaction.current].id)) {
220
+ interaction.current += 1;
221
+ }
222
+ if (interaction.current >= interaction.questions.length) {
223
+ void this.settleQuestion(interaction);
224
+ return;
225
+ }
226
+ const q = interaction.questions[interaction.current];
227
+ const hasOptions = Array.isArray(q.options) && q.options.length > 0;
228
+ if (hasOptions) {
229
+ void this.presentQuestionButtons(interaction, q);
230
+ }
231
+ else {
232
+ void this.presentQuestionText(interaction, q);
233
+ }
234
+ }
235
+ async presentQuestionButtons(interaction, q) {
236
+ const t = messages(interaction.language);
237
+ const options = (q.options ?? []).map((o, i) => ({
238
+ id: `q:${q.id}:${i}`,
239
+ label: o.label,
240
+ }));
241
+ const prompt = {
242
+ title: t.questionCardTitle,
243
+ description: this.questionPromptText(interaction, q),
244
+ options,
245
+ footer: q.multiSelect === true ? `${t.questionCardHint}\n${t.questionMultiHint}` : t.questionCardHint,
246
+ };
247
+ // One answered question steps the interaction to the next one; settling is
248
+ // the flow's job (stepQuestion → settleQuestion), not this loop's.
249
+ await this.presentLoop(interaction, prompt, (choice) => {
250
+ if (choice === undefined)
251
+ return undefined;
252
+ const answer = decodeChoice(q, choice);
253
+ if (answer === undefined)
254
+ return undefined;
255
+ this.recordAnswer(interaction, q.id, answer);
256
+ return "answered";
257
+ });
258
+ }
259
+ async presentQuestionText(interaction, q) {
260
+ const t = messages(interaction.language);
261
+ const target = this.outbound(interaction);
262
+ await interaction.adapter.sendText(target, t.questionTextHint(q.question)).catch(() => undefined);
263
+ while (!interaction.controller.signal.aborted && !interaction.settled) {
264
+ if (interaction.answers.has(q.id))
265
+ return;
266
+ await delay(60_000);
267
+ if (interaction.controller.signal.aborted || interaction.settled || interaction.answers.has(q.id))
268
+ return;
269
+ await interaction.adapter.sendText(target, t.questionWaiting(q.question)).catch(() => undefined);
270
+ }
271
+ }
272
+ async presentApprovalLoop(interaction) {
273
+ const t = messages(interaction.language);
274
+ const prompt = {
275
+ title: t.approvalCardTitle,
276
+ description: t.approvalAsk(interaction.toolName, interaction.reason),
277
+ options: [
278
+ { id: "approval:allow", label: t.approveLabel },
279
+ { id: "approval:reject", label: t.rejectLabel },
280
+ ],
281
+ };
282
+ const result = await this.presentLoop(interaction, prompt, (choice) => {
283
+ if (choice !== "approval:allow" && choice !== "approval:reject")
284
+ return undefined;
285
+ const outcome = choice === "approval:allow" ? "allowed-once" : "rejected";
286
+ void this.respondThen(interaction, {
287
+ sessionId: interaction.sessionId,
288
+ approvalId: interaction.approvalId,
289
+ outcome,
290
+ });
291
+ return "answered";
292
+ });
293
+ if (result === "answered")
294
+ this.settle(interaction);
295
+ }
296
+ /**
297
+ * Keep one interactive card alive until the user answers (or the ask is
298
+ * cancelled). The channel's choice prompt expires after its own idle
299
+ * timeout; on expiry the same card is re-presented in place so the choice
300
+ * stays clickable for as long as the agent waits. Resolves `"answered"`
301
+ * when `onChoice` accepted a choice, `"stopped"` otherwise.
302
+ */
303
+ async presentLoop(interaction, prompt, onChoice) {
304
+ while (!interaction.controller.signal.aborted && !interaction.settled) {
305
+ try {
306
+ const { choice, messageId } = await interaction.adapter.promptChoice(this.outbound(interaction), prompt, interaction.messageId);
307
+ interaction.messageId = messageId;
308
+ if (onChoice(choice) === "answered")
309
+ return "answered";
310
+ }
311
+ catch {
312
+ return "stopped";
313
+ }
314
+ await delay(1200);
315
+ }
316
+ return "stopped";
317
+ }
318
+ questionPromptText(interaction, q) {
319
+ const t = messages(interaction.language);
320
+ const total = interaction.questions.length;
321
+ const prefix = total > 1 ? `${t.questionStep(interaction.current + 1, total)}\n` : "";
322
+ const header = q.header === undefined || q.header === "" ? "" : `**${q.header}**\n`;
323
+ const detail = q.detail === undefined || q.detail === "" ? "" : `\n${q.detail}`;
324
+ const multi = q.multiSelect === true ? `\n${t.questionMultiHint}` : "";
325
+ return `${prefix}${header}${q.question}${detail}${multi}`;
326
+ }
327
+ recordAnswer(interaction, qid, answer) {
328
+ if (interaction.answers.has(qid))
329
+ return;
330
+ interaction.answers.set(qid, answer);
331
+ this.stepQuestion(interaction);
332
+ }
333
+ /** All questions answered — deliver the answers through the host respond path. */
334
+ async settleQuestion(interaction) {
335
+ if (interaction.settled)
336
+ return;
337
+ interaction.settled = true;
338
+ const answers = interaction.questions.map((q) => {
339
+ const a = interaction.answers.get(q.id);
340
+ return {
341
+ id: q.id,
342
+ selected: a?.selected ?? [],
343
+ ...(a?.custom !== undefined && a.custom.trim() !== "" ? { custom: a.custom.trim() } : {}),
344
+ };
345
+ });
346
+ const accepted = await this.respond(interaction.rpcId, {
347
+ sessionId: interaction.sessionId,
348
+ answer: { answers },
349
+ });
350
+ if (accepted) {
351
+ const t = messages(interaction.language);
352
+ await interaction.adapter.sendText(this.outbound(interaction), t.answerReceived).catch(() => undefined);
353
+ }
354
+ this.settle(interaction);
355
+ }
356
+ async respondThen(interaction, value) {
357
+ await this.respond(interaction.rpcId, value);
358
+ }
359
+ async respond(rpcId, value) {
360
+ if (this.apiProxy?.respond === undefined)
361
+ return false;
362
+ try {
363
+ const res = await this.apiProxy.respond({
364
+ type: "client-response",
365
+ rpcId,
366
+ result: { ok: true, value },
367
+ });
368
+ return res?.accepted === true;
369
+ }
370
+ catch (error) {
371
+ this.log(`connect: interaction respond failed: ${String(error)}`);
372
+ return false;
373
+ }
374
+ }
375
+ /** Stop presenting and drop the interaction (answered, cancelled, or the turn ended). */
376
+ settle(interaction) {
377
+ if (this.pending.get(interaction.chatKey) === interaction)
378
+ this.pending.delete(interaction.chatKey);
379
+ interaction.settled = true;
380
+ interaction.controller.abort();
381
+ }
382
+ outbound(p) {
383
+ return { chatKey: p.chatKey, chatType: p.chatType };
384
+ }
385
+ log(message) {
386
+ this.ctx.get("logger")?.info?.(message);
387
+ }
388
+ }
389
+ //# sourceMappingURL=interaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../src/interaction.ts"],"names":[],"mappings":"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"}
package/lib/runner.d.ts CHANGED
@@ -20,6 +20,14 @@ export interface ConnectConfig {
20
20
  allowUsers?: string[];
21
21
  allowChats?: string[];
22
22
  stateDir?: string;
23
+ /** Automatically create Web mirror for new sessions (default: true). */
24
+ autoMirror?: boolean;
25
+ /** Liveness heartbeat interval ms for the streaming card; 0 disables it (default: 60000). */
26
+ streamHeartbeatMs?: number;
27
+ /** Default notification level for streaming replies (default: `important`). */
28
+ notifyLevel?: NotifyLevel;
29
+ /** Proactive progress-notice interval ms: when a turn goes silent for this long, a standalone status card is sent (default: 300000 = 5 min; 0 disables). */
30
+ progressTimeoutMs?: number;
23
31
  }
24
32
  export interface ResolvedConnectConfig {
25
33
  agentPreset?: string;
@@ -33,8 +41,56 @@ export interface ResolvedConnectConfig {
33
41
  allowUsers: string[];
34
42
  allowChats: string[];
35
43
  stateDir?: string;
44
+ autoMirror: boolean;
45
+ streamHeartbeatMs: number;
46
+ notifyLevel: NotifyLevel;
47
+ progressTimeoutMs: number;
36
48
  }
37
49
  export declare function resolveConnectConfig(config: ConnectConfig): ResolvedConnectConfig;
50
+ /** Per-turn streaming assembly state mutated by {@link applyStreamChunk}. */
51
+ export interface StreamState {
52
+ chunks: {
53
+ push(text: string): void;
54
+ };
55
+ lastText: string;
56
+ reasoning: boolean;
57
+ hintPushed: boolean;
58
+ lastIndex: number | undefined;
59
+ pushedAny: boolean;
60
+ lastPushAt: number;
61
+ }
62
+ /**
63
+ * How much of the agent's live progress the streaming card shows.
64
+ * - `full` — everything: reasoning text, tool calls, heartbeats, answer.
65
+ * - `important` — key milestones only: thinking hint, tool calls, heartbeats, answer (no reasoning text).
66
+ * - `result` — only the final answer.
67
+ */
68
+ export type NotifyLevel = "full" | "important" | "result";
69
+ /** One assistant chunk event of interest to the streaming bridge. */
70
+ export interface StreamChunkLike {
71
+ type: string;
72
+ index?: number;
73
+ text?: string;
74
+ block?: {
75
+ type?: string;
76
+ text?: string;
77
+ };
78
+ }
79
+ export declare function applyStreamChunk(state: StreamState, thinkingHint: string, chunk: StreamChunkLike, level?: NotifyLevel): void;
80
+ /**
81
+ * Push a localized tool-call status line into the streaming queue so long
82
+ * tool-execution stretches stay visibly alive (the Web GUI shows tool calls;
83
+ * the chat card should too). Ends the reasoning phase and drops the block
84
+ * index so the next content block gets its own separator instead of stacking
85
+ * one on top of the tool line's own blank lines.
86
+ * @param state - the turn's mutable assembly state.
87
+ * @param label - the localized status line, e.g. "🔧 调用工具 `pwsh` — …".
88
+ */
89
+ export declare function applyToolCall(state: StreamState, label: string): void;
90
+ /** Pull a short human summary out of a tool call's `arguments` JSON (whitespace folded to one line). */
91
+ export declare function toolCallSummary(argumentsJson: unknown): string | undefined;
92
+ /** Pull the first question's text out of an `ask_user_question` arguments JSON (whitespace folded to one line). */
93
+ export declare function questionTextOf(argumentsJson: unknown): string;
38
94
  export declare function summarizeTurn(events: readonly SessionEvent[], firstSeq: number): TurnOutcome;
39
95
  export declare class AgentRunner {
40
96
  private readonly ctx;
@@ -44,6 +100,7 @@ export declare class AgentRunner {
44
100
  private readonly chatType;
45
101
  private readonly adapter;
46
102
  private readonly bindings;
103
+ private readonly adapters?;
47
104
  private readonly queue;
48
105
  private running;
49
106
  private agent?;
@@ -52,8 +109,11 @@ export declare class AgentRunner {
52
109
  private workDir;
53
110
  private streamEnabled;
54
111
  private summaryEnabled;
55
- private readonly t;
56
- constructor(ctx: Context, config: ResolvedConnectConfig, channel: string, chatKey: string, chatType: "p2p" | "group", adapter: ChannelAdapter, bindings: BindingStore);
112
+ private language;
113
+ private notifyLevel;
114
+ private progressTimeoutMs;
115
+ private t;
116
+ constructor(ctx: Context, config: ResolvedConnectConfig, channel: string, chatKey: string, chatType: "p2p" | "group", adapter: ChannelAdapter, bindings: BindingStore, adapters?: Map<string, ChannelAdapter> | undefined);
57
117
  private menuTitle;
58
118
  private rootMenuSections;
59
119
  private reasonLabel;
@@ -66,10 +126,33 @@ export declare class AgentRunner {
66
126
  private drain;
67
127
  private runTurn;
68
128
  private ensureAgent;
129
+ /**
130
+ * Attach a session to the DSH workspace matching its work directory so the
131
+ * Web GUI groups it under the same workspace the Feishu side uses.
132
+ * Best-effort: without a workspace registry this is a no-op.
133
+ */
134
+ private attachToWorkspace;
69
135
  private get agents();
70
136
  private get sessions();
71
137
  private defaultSelection;
138
+ /**
139
+ * Compose the preset-only agent setup.
140
+ *
141
+ * Deliberately does NOT install a static model selection on the agent. The
142
+ * DSH Web GUI switches models through the host api-proxy's session-scoped
143
+ * selection (`selectionFor` + `installModelSelection`), which is installed
144
+ * lazily when a session's model directory is loaded and wins the
145
+ * `agent/request` waterfall. A static selection captured here at create /
146
+ * resume time would take precedence over that and silently pin every request
147
+ * to the default model captured at composition — so a model the user picks
148
+ * in the Web GUI would appear to succeed (the seat and `session.models`
149
+ * report it) while the actual LLM requests keep using the old default.
150
+ * The default model still seeds the agent through `agentOptions` in
151
+ * `ensureAgent`, which `buildRequest` uses as its fallback route.
152
+ */
72
153
  private composeSetup;
154
+ /** Agents this runner has already attached a `session/event` listener to. */
155
+ private readonly watchedAgents;
73
156
  private watchAgent;
74
157
  private driveAgent;
75
158
  /**
@@ -81,6 +164,8 @@ export declare class AgentRunner {
81
164
  private buildUserContent;
82
165
  /** Copy images into `<workDir>/.dsh-connect-images` so the agent's tools can reach them. */
83
166
  private stageImages;
167
+ /** Copy non-image attachments into `<workDir>/.dsh-connect-files` so the agent's tools can reach them. */
168
+ private stageFiles;
84
169
  private imageBlocks;
85
170
  private describeImages;
86
171
  private modelSupportsVision;
@@ -89,9 +174,22 @@ export declare class AgentRunner {
89
174
  private readTodos;
90
175
  private touchBinding;
91
176
  private sendSummary;
177
+ /** Context-window usage at or above this percentage suggests compaction. */
178
+ private static readonly COMPACT_THRESHOLD_PCT;
179
+ /**
180
+ * Send a compact task-end card: the model used, input/output tokens, elapsed
181
+ * time, and whether compaction is advisable given context-window usage.
182
+ */
183
+ private sendTurnStats;
92
184
  private handleCommand;
93
185
  private disposeAgent;
94
186
  private recordSession;
187
+ /**
188
+ * Automatically create a Web mirror for the session.
189
+ * This enables DSH Web GUI to view and interact with the conversation
190
+ * without requiring manual `/mirror` command.
191
+ */
192
+ private autoCreateWebMirror;
95
193
  private newChat;
96
194
  private clearChat;
97
195
  private switchTo;
@@ -99,6 +197,8 @@ export declare class AgentRunner {
99
197
  private openMenu;
100
198
  private menuItems;
101
199
  private showStatus;
200
+ /** Extract the most recent turn's completion info from session events. */
201
+ private getLastTurnInfo;
102
202
  private showTasks;
103
203
  private showSettings;
104
204
  private modelMenuItems;
@@ -106,6 +206,22 @@ export declare class AgentRunner {
106
206
  private setModel;
107
207
  private reasoningMenuItems;
108
208
  private setReasoning;
209
+ /** Switch the user-facing language for this chat, persisted in its binding. */
210
+ private setLanguage;
211
+ /**
212
+ * Switch the notification level for this chat (persisted in its binding and
213
+ * effective immediately for the next streaming reply).
214
+ */
215
+ private setNotifyLevel;
216
+ /** Show the three-level notification picker (used by the `/notify` command). */
217
+ private openNotifyPicker;
218
+ /**
219
+ * Switch the proactive progress-notice interval for this chat (persisted in
220
+ * its binding and effective for the next turn; 0 disables the watchdog).
221
+ */
222
+ private setProgressTimeout;
223
+ /** Show the progress-interval picker (used by the `/progress` command). */
224
+ private openProgressPicker;
109
225
  private showPlugins;
110
226
  private createWorkspace;
111
227
  private compact;
@@ -114,5 +230,56 @@ export declare class AgentRunner {
114
230
  private goalPhaseLabel;
115
231
  private showSchedule;
116
232
  private showAllWorkspaces;
233
+ /**
234
+ * Create or show Web mirror session for this chat.
235
+ * The mirror shares the same DSH session but enforces mutual exclusion:
236
+ * when Feishu is executing a task, Web is read-only, and vice versa.
237
+ */
238
+ private handleMirror;
239
+ /**
240
+ * Manually release session lock.
241
+ */
242
+ private handleUnlock;
243
+ /**
244
+ * Renew the current session lock timeout.
245
+ * Extends the lock by another full timeout period from now.
246
+ */
247
+ private handleRenewLock;
248
+ /**
249
+ * Export conversation history to Markdown or PDF.
250
+ */
251
+ private handleExport;
252
+ /**
253
+ * Generate Markdown representation of the conversation history.
254
+ */
255
+ private generateMarkdown;
256
+ /**
257
+ * Check if the current channel has write access to the session.
258
+ * Returns true if allowed to send messages, false if read-only.
259
+ */
260
+ private canWrite;
261
+ /**
262
+ * Acquire session lock for the given channel.
263
+ * Returns true if lock acquired, false if already locked by another channel.
264
+ */
265
+ private acquireLock;
266
+ /**
267
+ * Queue a message for later execution when lock is released.
268
+ * Returns the position in queue.
269
+ */
270
+ private queueMessage;
271
+ /**
272
+ * Process all queued messages after lock release.
273
+ * Re-enqueues them into the runner's message queue for actual execution.
274
+ */
275
+ private processQueuedMessages;
276
+ /** Release session lock. */
277
+ private releaseLock;
278
+ /** Check if lock has timed out. */
279
+ private isLockTimedOut;
280
+ /** Check and release timed-out locks. */
281
+ private checkAndReleaseTimeoutLock;
282
+ /** Release a timed-out lock and notify. */
283
+ private releaseTimeoutLock;
117
284
  }
118
285
  //# sourceMappingURL=runner.d.ts.map