dsh-client-auto-continue 0.2.2 → 0.3.0

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/README.md CHANGED
@@ -32,6 +32,15 @@
32
32
 
33
33
  For [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh web`): whenever a request in the web GUI gets interrupted by a **non-human cause**, the plugin simulates the user typing **「继续」** and sends it, so the agent keeps working without manual intervention. The message enters the session log exactly like a manual prompt — the model sees it, and the interrupted work resumes.
34
34
 
35
+ ![demo](docs/demo.svg)
36
+
37
+ **Smart recovery** (all configurable):
38
+
39
+ - **Error classification** — transient failures (network / timeout / 5xx…) are auto-resumed; permanent ones (auth, balance, unknown model, context overflow) are **skipped** and notified, because retrying them never helps
40
+ - **Adaptive backoff** — consecutive failures wait longer each time (e.g. 20s → 40s → 80s), instead of hammering a broken upstream
41
+ - **Templated continue text** — `continueText` supports `{code}` `{message}` `{status}` `{tool}` `{turn}` placeholders, so the resume message can carry the failure context ("继续 (git push failed: UPSTREAM)")
42
+ - **Browser notifications** — optional alerts when auto-continue fires, gives up, or hits a permanent error
43
+
35
44
  It watches the live event streams and reacts to:
36
45
 
37
46
  | Event | Meaning |
@@ -147,6 +156,12 @@ Everything is configurable from the GUI — no file or console edits needed. Ope
147
156
  | Reconnect scan delay (ms) | `5000` | Delay before scanning after a reconnect |
148
157
  | Reconnect backoff (ms) | `3000` | SSE reconnect backoff |
149
158
  | Verbose logs | `on` | `[auto-continue]` console logs |
159
+ | Classify errors | `on` | Auto-resume transient failures only; auth / balance / model errors are skipped and notified |
160
+ | Backoff factor | `2` | Cooldown multiplier per consecutive failure (2 = 20s → 40s → 80s…) |
161
+ | Max backoff (ms) | `300000` | Cap on the adaptive backoff interval |
162
+ | Browser notifications | `off` | Notify when auto-continue fires, gives up, or hits a permanent error |
163
+
164
+ `continueText` accepts the placeholders `{code}`, `{message}`, `{status}`, `{tool}` (last tool call before the failure) and `{turn}` — e.g. `继续 ({tool}: {code})` becomes `继续 (git push: UPSTREAM)`.
150
165
 
151
166
  ---
152
167
 
@@ -163,6 +178,12 @@ While `npm run watch` runs, the profile's client-hmr row polls `lib/client.js` e
163
178
 
164
179
  ---
165
180
 
181
+ ## Activity
182
+
183
+ [![HsiangNianian/dsh-auto-continue GitStock K-Line Chart](https://gitstock.org/HsiangNianian/dsh-auto-continue/stock.svg)](https://gitstock.org/HsiangNianian/dsh-auto-continue)
184
+
185
+ ---
186
+
166
187
  ## Links
167
188
 
168
189
  - **Repository**: [github.com/HsiangNianian/dsh-auto-continue](https://github.com/HsiangNianian/dsh-auto-continue)
package/README.zh.md CHANGED
@@ -32,6 +32,15 @@
32
32
 
33
33
  适用于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh web`): 当 webui 里的请求因为**非人为因素**中断时, 插件模拟用户输入 **「继续」** 并自动发送, 让 Agent 继续干活, 无需手动干预。消息与手动输入完全等价——进入会话日志、对模型可见, 中断的任务随即恢复。
34
34
 
35
+ ![demo](docs/demo-zh.svg)
36
+
37
+ **智能恢复**(全部可配置):
38
+
39
+ - **错误分类** — 临时性错误(网络 / 超时 / 5xx 等)自动续跑; 永久性错误(认证、余额不足、模型不存在、上下文超限)跳过并通知, 因为重试也没用
40
+ - **自适应退避** — 连续失败时等待时间递增(如 20s → 40s → 80s), 不再对故障上游狂轰滥炸
41
+ - **模板化继续文本** — `continueText` 支持 `{code}` `{message}` `{status}` `{tool}` `{turn}` 占位符, 续跑消息可携带失败上下文(如「继续 (git push 失败: UPSTREAM)」)
42
+ - **浏览器通知** — 可选: 自动继续成功 / 放弃 / 遇到永久性错误时弹出提醒
43
+
35
44
  插件监听实时事件流, 对以下情况作出反应:
36
45
 
37
46
  | 事件 | 含义 |
@@ -144,6 +153,12 @@ dsh web
144
153
  | 重连扫描延迟 (ms) | `5000` | 重连后等待宿主恢复再扫描 |
145
154
  | 重连退避 (ms) | `3000` | SSE 流断开后的重连间隔 |
146
155
  | 详细日志 | 开 | 控制台输出 `[auto-continue]` 日志 |
156
+ | 错误分类 | 开 | 仅自动恢复临时性错误; 认证 / 余额 / 模型等永久性错误跳过并通知 |
157
+ | 退避系数 | `2` | 连续失败时冷却间隔的倍率(2 = 20s → 40s → 80s…) |
158
+ | 最大退避间隔 (ms) | `300000` | 自适应退避的上限 |
159
+ | 浏览器通知 | 关 | 自动继续成功 / 放弃 / 遇到永久性错误时弹通知 |
160
+
161
+ `continueText` 支持占位符 `{code}`、`{message}`、`{status}`、`{tool}`(失败前最后一次工具调用)和 `{turn}`——例如 `继续 ({tool}: {code})` 会变成 `继续 (git push: UPSTREAM)`。
147
162
 
148
163
  ---
149
164
 
@@ -160,6 +175,12 @@ npm run test # node tests/simulate.mjs — 8 个行为场景
160
175
 
161
176
  ---
162
177
 
178
+ ## 活跃度
179
+
180
+ [![HsiangNianian/dsh-auto-continue GitStock K-Line Chart](https://gitstock.org/HsiangNianian/dsh-auto-continue/stock.svg)](https://gitstock.org/HsiangNianian/dsh-auto-continue)
181
+
182
+ ---
183
+
163
184
  ## 链接
164
185
 
165
186
  - **仓库**: [github.com/HsiangNianian/dsh-auto-continue](https://github.com/HsiangNianian/dsh-auto-continue)
package/lib/client.js CHANGED
@@ -42,7 +42,11 @@ var DEFAULT_CONFIG = {
42
42
  freshMs: 15 * 60 * 1e3,
43
43
  reconnectScanDelayMs: 5e3,
44
44
  reconnectBackoffMs: 3e3,
45
- verbose: true
45
+ verbose: true,
46
+ classify: true,
47
+ backoffFactor: 2,
48
+ backoffMaxMs: 3e5,
49
+ notify: false
46
50
  };
47
51
  function numberOr(value, fallback) {
48
52
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
@@ -63,12 +67,46 @@ function resolveConfig(section) {
63
67
  freshMs: numberOr(value.freshMs, DEFAULT_CONFIG.freshMs),
64
68
  reconnectScanDelayMs: numberOr(value.reconnectScanDelayMs, DEFAULT_CONFIG.reconnectScanDelayMs),
65
69
  reconnectBackoffMs: numberOr(value.reconnectBackoffMs, DEFAULT_CONFIG.reconnectBackoffMs),
66
- verbose: booleanOr(value.verbose, DEFAULT_CONFIG.verbose)
70
+ verbose: booleanOr(value.verbose, DEFAULT_CONFIG.verbose),
71
+ classify: booleanOr(value.classify, DEFAULT_CONFIG.classify),
72
+ backoffFactor: Math.max(1, numberOr(value.backoffFactor, DEFAULT_CONFIG.backoffFactor)),
73
+ backoffMaxMs: numberOr(value.backoffMaxMs, DEFAULT_CONFIG.backoffMaxMs),
74
+ notify: booleanOr(value.notify, DEFAULT_CONFIG.notify)
67
75
  };
68
76
  }
69
77
  function isNonHumanReason(kind) {
70
78
  return kind === "error" || kind === "interrupted" || kind === "max-tokens";
71
79
  }
80
+ function isTransientFailure(failure) {
81
+ const haystack = `${failure.code} ${failure.message}`.toLowerCase();
82
+ const status = failure.status;
83
+ if (status !== void 0 && (status === 401 || status === 403)) return false;
84
+ const permanent = /auth|unauthor|forbidden|credential|api[_-]?key|permission/i.test(haystack) || /insufficient.*(balance|quota)|billing|payment|quota.*exceeded.*(?!retry)/i.test(haystack) || /model.*not[_-]?found|unknown[_-]?model|model[_-]?not[_-]?found|not.*support.*model/i.test(haystack) || /context.*(length|limit|overflow|exceed)|token.*limit|max.*context/i.test(haystack) || /invalid[_-]?request|bad[_-]?request/i.test(haystack);
85
+ return !permanent;
86
+ }
87
+ function notify(title, body) {
88
+ try {
89
+ const N = globalThis.Notification;
90
+ if (typeof N === "undefined") return;
91
+ const permission = N.permission;
92
+ if (permission === "granted") {
93
+ new N(title, { body });
94
+ } else if (permission === "default") {
95
+ void N.requestPermission?.().then((result) => {
96
+ if (result === "granted") new N(title, { body });
97
+ }).catch(() => {
98
+ });
99
+ }
100
+ } catch {
101
+ }
102
+ }
103
+ function fillTemplate(template, facts, tool, turn) {
104
+ return template.replace(/\{code\}/g, facts?.code ?? "").replace(/\{message\}/g, facts?.message ?? "").replace(/\{status\}/g, facts?.status !== void 0 ? String(facts.status) : "").replace(/\{tool\}/g, tool ?? "").replace(/\{turn\}/g, turn !== void 0 ? String(turn) : "");
105
+ }
106
+ function effectiveCooldown(consecutive, base, factor, max) {
107
+ const multiplier = Math.pow(factor, consecutive);
108
+ return Math.min(Math.max(base, base * multiplier), Math.max(base, max));
109
+ }
72
110
  function sleep(ms) {
73
111
  return new Promise((resolve) => setTimeout(resolve, ms));
74
112
  }
@@ -118,7 +156,10 @@ var freshState = () => ({
118
156
  pendingTimer: void 0,
119
157
  running: void 0,
120
158
  queued: 0,
121
- subagent: false
159
+ subagent: false,
160
+ lastFailure: void 0,
161
+ lastTool: void 0,
162
+ lastTurn: void 0
122
163
  });
123
164
  function isOurEcho(state, event) {
124
165
  if (event.type !== "user/message") return false;
@@ -220,6 +261,10 @@ var AutoContinueRunner = class {
220
261
  onMuxFrame(frame) {
221
262
  switch (frame.type) {
222
263
  case "session/event":
264
+ if (frame.event.type === "tool/call") {
265
+ const name = frame.event.data.name;
266
+ if (typeof name === "string") this.state(frame.sessionId).lastTool = name;
267
+ }
223
268
  this.onSessionEvent(frame.sessionId, frame.event);
224
269
  break;
225
270
  case "session/queue":
@@ -246,9 +291,19 @@ var AutoContinueRunner = class {
246
291
  const reason = event.data.reason;
247
292
  if (reason.kind === "completed") {
248
293
  state.consecutive = 0;
294
+ state.lastFailure = void 0;
249
295
  } else if (reason.kind === "aborted") {
250
296
  state.consecutive = 0;
251
297
  } else if (reason.kind === "blocked") {
298
+ } else if (reason.kind === "error") {
299
+ const error = reason.error;
300
+ state.lastFailure = {
301
+ code: typeof error.code === "string" ? error.code : "UNKNOWN",
302
+ message: typeof error.message === "string" ? error.message : String(error),
303
+ ...typeof error.status === "number" ? { status: error.status } : {}
304
+ };
305
+ state.lastTurn = event.data.turn;
306
+ this.onTurnFailure(sessionId, "turn/end:error", state.lastFailure);
252
307
  } else if (isNonHumanReason(reason.kind)) {
253
308
  this.schedule(sessionId, `turn/end:${reason.kind}`);
254
309
  }
@@ -289,12 +344,35 @@ var AutoContinueRunner = class {
289
344
  }
290
345
  }
291
346
  // ---------- 调度 ----------
347
+ /** 回合失败入口: 先做错误分类, 永久性失败跳过并通知, 临时性失败走正常调度。 */
348
+ onTurnFailure(sessionId, reason, failure) {
349
+ const config = this.getConfig();
350
+ if (config.classify && !isTransientFailure(failure)) {
351
+ const summary = `${failure.code}${failure.status !== void 0 ? ` (HTTP ${failure.status})` : ""}`;
352
+ this.log(`跳过 ${sessionId}(${reason}): 永久性失败 ${summary} — ${failure.message}`);
353
+ if (config.notify) {
354
+ notify("dsh-auto-continue: 未自动继续", `${sessionId}: 永久性错误 ${summary},需要人工处理`);
355
+ }
356
+ return;
357
+ }
358
+ this.schedule(sessionId, reason);
359
+ }
360
+ /** 本会话当前生效的冷却间隔(自适应退避)。 */
361
+ cooldownFor(state) {
362
+ const config = this.getConfig();
363
+ return effectiveCooldown(
364
+ state.consecutive,
365
+ config.cooldownMs,
366
+ config.backoffFactor,
367
+ config.backoffMaxMs
368
+ );
369
+ }
292
370
  schedule(sessionId, reason) {
293
371
  const state = this.state(sessionId);
294
372
  const config = this.getConfig();
295
373
  if (state.subagent) return;
296
374
  if (state.pendingTimer !== void 0) return;
297
- if (Date.now() - state.lastAttemptAt < config.cooldownMs) return;
375
+ if (Date.now() - state.lastAttemptAt < this.cooldownFor(state)) return;
298
376
  if (state.consecutive >= config.maxConsecutive) {
299
377
  this.log(
300
378
  `跳过 ${sessionId}(${reason}): 已连续自动继续 ${state.consecutive} 次, 等待用户介入或成功回合`
@@ -337,7 +415,7 @@ var AutoContinueRunner = class {
337
415
  this.log(`跳过 ${sessionId}: 已有排队消息`);
338
416
  return;
339
417
  }
340
- if (Date.now() - readLastSend(sessionId) < config.cooldownMs) {
418
+ if (Date.now() - readLastSend(sessionId) < this.cooldownFor(state)) {
341
419
  this.log(`跳过 ${sessionId}: 其他标签页刚发送过`);
342
420
  return;
343
421
  }
@@ -345,7 +423,7 @@ var AutoContinueRunner = class {
345
423
  this.log(`跳过 ${sessionId}: 其他标签页正在发送`);
346
424
  return;
347
425
  }
348
- const text = config.continueText;
426
+ const text = fillTemplate(config.continueText, state.lastFailure, state.lastTool, state.lastTurn);
349
427
  const zone = clientTimeZone();
350
428
  state.lastAttemptAt = Date.now();
351
429
  try {
@@ -362,6 +440,15 @@ var AutoContinueRunner = class {
362
440
  state.lastSentText = text;
363
441
  writeLastSend(sessionId, now);
364
442
  this.log(`已自动发送「${text}」到 ${sessionId}(${reason}), 第 ${state.consecutive} 次连续`);
443
+ if (config.notify) {
444
+ notify("dsh-auto-continue: 已自动继续", `${sessionId}: 已发送「${text}」(第 ${state.consecutive} 次连续)`);
445
+ }
446
+ if (state.consecutive >= config.maxConsecutive) {
447
+ this.log(`达到连续上限 ${config.maxConsecutive} 次, 停止自动继续 ${sessionId}`);
448
+ if (config.notify) {
449
+ notify("dsh-auto-continue: 已停止自动继续", `${sessionId}: 连续失败 ${state.consecutive} 次, 需要人工介入`);
450
+ }
451
+ }
365
452
  } else {
366
453
  this.log(
367
454
  `发送失败 ${sessionId}: ${response.result.error.code} ${response.result.error.message}`
@@ -429,7 +516,7 @@ var AutoContinueRunner = class {
429
516
  const state = this.state(summary.sessionId);
430
517
  if (state.pendingTimer !== void 0) continue;
431
518
  if (state.consecutive >= config.maxConsecutive) continue;
432
- if (now - state.lastAttemptAt < config.cooldownMs) continue;
519
+ if (now - state.lastAttemptAt < this.cooldownFor(state)) continue;
433
520
  let events;
434
521
  try {
435
522
  const page = await this.api.sessions.history({
@@ -493,6 +580,14 @@ var zh = {
493
580
  "field.reconnectBackoffMsHint": "事件流断开后的重连间隔。",
494
581
  "field.verbose": "详细日志",
495
582
  "field.verboseHint": "在浏览器控制台输出 [auto-continue] 日志。",
583
+ "field.classify": "错误分类",
584
+ "field.classifyHint": "仅自动恢复临时性错误(网络/超时/5xx 等); 认证/余额/模型不存在等永久性错误跳过并通知。",
585
+ "field.backoffFactor": "退避系数",
586
+ "field.backoffFactorHint": "连续失败时冷却间隔的倍率(如 2 表示 20s→40s→80s 递增)。",
587
+ "field.backoffMaxMs": "最大退避间隔 (ms)",
588
+ "field.backoffMaxMsHint": "自适应退避的上限, 防止等待过久。",
589
+ "field.notify": "浏览器通知",
590
+ "field.notifyHint": "自动继续成功/放弃/遇到永久性错误时弹出浏览器通知。",
496
591
  "chrome.collapse": "收起设置",
497
592
  "chrome.expand": "展开设置",
498
593
  "chrome.unsaved": "未保存",
@@ -531,6 +626,14 @@ var en = {
531
626
  "field.reconnectBackoffMsHint": "Interval between event-stream reconnect attempts.",
532
627
  "field.verbose": "Verbose logs",
533
628
  "field.verboseHint": "Log [auto-continue] lines to the browser console.",
629
+ "field.classify": "Classify errors",
630
+ "field.classifyHint": "Auto-resume transient failures only (network/timeout/5xx…); auth, balance and model errors are skipped and notified.",
631
+ "field.backoffFactor": "Backoff factor",
632
+ "field.backoffFactorHint": "Cooldown multiplier per consecutive failure (2 = 20s→40s→80s…).",
633
+ "field.backoffMaxMs": "Max backoff (ms)",
634
+ "field.backoffMaxMsHint": "Cap on the adaptive backoff interval.",
635
+ "field.notify": "Browser notifications",
636
+ "field.notifyHint": "Notify when auto-continue fires, gives up, or hits a permanent error.",
534
637
  "chrome.collapse": "Hide settings",
535
638
  "chrome.expand": "Show settings",
536
639
  "chrome.unsaved": "Unsaved",
@@ -892,7 +995,11 @@ var AutoContinueSettingsCardController = class {
892
995
  numberField("freshMs", 0),
893
996
  numberField("reconnectScanDelayMs", 0),
894
997
  numberField("reconnectBackoffMs", 0),
895
- booleanField("verbose")
998
+ booleanField("verbose"),
999
+ booleanField("classify"),
1000
+ numberField("backoffFactor", 1),
1001
+ numberField("backoffMaxMs", 0),
1002
+ booleanField("notify")
896
1003
  ]);
897
1004
  this.store = this.form.bind(() => this.projection(), import_client.createSnapshotStore);
898
1005
  }
@@ -908,7 +1015,11 @@ var AutoContinueSettingsCardController = class {
908
1015
  freshMs: this.form.field("freshMs"),
909
1016
  reconnectScanDelayMs: this.form.field("reconnectScanDelayMs"),
910
1017
  reconnectBackoffMs: this.form.field("reconnectBackoffMs"),
911
- verbose: this.form.field("verbose")
1018
+ verbose: this.form.field("verbose"),
1019
+ classify: this.form.field("classify"),
1020
+ backoffFactor: this.form.field("backoffFactor"),
1021
+ backoffMaxMs: this.form.field("backoffMaxMs"),
1022
+ notify: this.form.field("notify")
912
1023
  };
913
1024
  }
914
1025
  /**
@@ -1159,6 +1270,56 @@ function AutoContinueSettingsCard(props) {
1159
1270
  onEdit: (text) => props.edit("verbose", text),
1160
1271
  onReset: () => props.resetField("verbose")
1161
1272
  }
1273
+ ),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1275
+ BooleanField,
1276
+ {
1277
+ id: "auto-continue-classify",
1278
+ label: t("field.classify"),
1279
+ hint: t("field.classifyHint"),
1280
+ ...shared,
1281
+ ...state.classify,
1282
+ onEdit: (text) => props.edit("classify", text),
1283
+ onReset: () => props.resetField("classify")
1284
+ }
1285
+ ),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1287
+ ValueField,
1288
+ {
1289
+ id: "auto-continue-backoff-factor",
1290
+ label: t("field.backoffFactor"),
1291
+ hint: t("field.backoffFactorHint"),
1292
+ numeric: true,
1293
+ ...shared,
1294
+ ...state.backoffFactor,
1295
+ onEdit: (text) => props.edit("backoffFactor", text),
1296
+ onReset: () => props.resetField("backoffFactor")
1297
+ }
1298
+ ),
1299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1300
+ ValueField,
1301
+ {
1302
+ id: "auto-continue-backoff-max",
1303
+ label: t("field.backoffMaxMs"),
1304
+ hint: t("field.backoffMaxMsHint"),
1305
+ numeric: true,
1306
+ ...shared,
1307
+ ...state.backoffMaxMs,
1308
+ onEdit: (text) => props.edit("backoffMaxMs", text),
1309
+ onReset: () => props.resetField("backoffMaxMs")
1310
+ }
1311
+ ),
1312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1313
+ BooleanField,
1314
+ {
1315
+ id: "auto-continue-notify",
1316
+ label: t("field.notify"),
1317
+ hint: t("field.notifyHint"),
1318
+ ...shared,
1319
+ ...state.notify,
1320
+ onEdit: (text) => props.edit("notify", text),
1321
+ onReset: () => props.resetField("notify")
1322
+ }
1162
1323
  )
1163
1324
  ]
1164
1325
  }
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["src/client/index.ts", "src/client/engine.ts", "src/client/locales.ts", "src/client/settings-card.tsx", "src/client/settings-form.ts", "src/client/styles.ts"],
4
- "sourcesContent": ["/**\n * Auto-continue plugin, browser half.\n *\n * - Runs the auto-continue engine over the live mux + host event streams.\n * - Registers the `auto-continue` settings card into the plugin-configuration\n * section (`settings.plugin.item`), editing the same namespace the engine\n * reads — every behavior knob is configurable from the GUI.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client';\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client';\n// Type-only: pulls the settings-surface SlotMap merge and ctx.settingsScope.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client';\n// Type-only: pulls the `settings.plugin.item` SlotMap merge.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client';\nimport { AutoContinueRunner, resolveConfig, type AutoContinueSettings } from './engine.ts';\nimport { en, zh, type SettingsCardKey } from './locales.ts';\nimport {\n AutoContinueSettingsCard,\n AutoContinueSettingsCardController,\n} from './settings-card.tsx';\n\n/** 客户端根上下文的 connection 服务(由 dsh-client-connection 挂载)。 */\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n connection: ConnectionHandle;\n }\n}\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'auto-continue';\n\n/** Settings namespace the engine reads and the settings card edits. */\nconst SETTINGS_NS = 'auto-continue';\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** auto-continue settings-card copy. */\n 'auto-continue': SettingsCardKey;\n }\n}\n\n/** Services required by this plugin. */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope'];\n\n/** 当前 runner(HMR 重载时先销毁旧的再建新的)。 */\nlet current: AutoContinueRunner | null = null;\n\n/**\n * Plugin body: mount the engine and the settings card.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'auto-continue: dictionaries');\n\n // Engine: reads the settings scope live, so GUI changes apply immediately.\n const scope = ctx.settingsScope.bind<AutoContinueSettings>({ namespace: SETTINGS_NS });\n current?.dispose();\n current = new AutoContinueRunner(ctx.connection.api, () => resolveConfig(scope.getSnapshot().value));\n\n // Plugin configuration card: one staged form over the `auto-continue`\n // settings namespace, contributed to the plugin-configuration section.\n const controller = new AutoContinueSettingsCardController(scope);\n ctx.slots.inject('settings.plugin.item', () =>\n ctx.slots.register(\n {\n name: 'settings.plugin.item',\n id: 'auto-continue',\n order: 90,\n locale: NS,\n inject: () => controller.inject(),\n },\n AutoContinueSettingsCard,\n ),\n );\n}\n", "/**\n * Auto-continue engine — browser half core.\n *\n * Watches the two live event streams of the dsh web GUI (mux + host):\n * - turns ended for a non-human reason (`turn/end` reason ∈ error / interrupted / max-tokens)\n * - host-reported agent failures with no turn position (`host/agent-error`)\n * After a grace period it sends a queued prompt (default 「继续」) to that\n * session — exactly equivalent to the user typing it manually.\n *\n * All behavior is driven by the `auto-continue` settings namespace (see the\n * plugin's settings card); every knob below is user-configurable there.\n */\n\nimport type {\n HostFrame,\n IApiClient,\n MuxFrame,\n SessionId,\n SessionSummary,\n} from '@deepseek-ai/dsh-client-connection/client';\nimport type { SessionEvent } from '@deepseek-ai/dsh-session/types';\n\n/** The `auto-continue` settings section (all fields optional on the wire; the host schema carries defaults). */\nexport interface AutoContinueSettings {\n /** Text automatically sent after an interruption. */\n continueText?: string;\n /** Grace period after an interruption before auto-sending (ms). */\n graceMs?: number;\n /** Minimum interval between two auto-continues per session (ms). */\n cooldownMs?: number;\n /** Max consecutive auto-continues per session before stopping. */\n maxConsecutive?: number;\n /** Scan recently interrupted sessions on page load / reconnect. */\n scanOnBoot?: boolean;\n /** Max sessions the scan checks (most recently updated). */\n scanLimit?: number;\n /** Scan only considers interruptions inside this window (ms). */\n freshMs?: number;\n /** Delay before scanning after a reconnect (ms). */\n reconnectScanDelayMs?: number;\n /** SSE reconnect backoff (ms). */\n reconnectBackoffMs?: number;\n /** Log `[auto-continue]` lines to the browser console. */\n verbose?: boolean;\n}\n\n/** Fully resolved configuration (built-in defaults + user overrides). */\nexport type AutoContinueConfig = Required<AutoContinueSettings>;\n\n/** Built-in defaults — must match the host schema defaults in src/index.ts. */\nexport const DEFAULT_CONFIG: AutoContinueConfig = {\n continueText: '继续',\n graceMs: 3000,\n cooldownMs: 20000,\n maxConsecutive: 3,\n scanOnBoot: true,\n scanLimit: 8,\n freshMs: 15 * 60 * 1000,\n reconnectScanDelayMs: 5000,\n reconnectBackoffMs: 3000,\n verbose: true,\n};\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\nfunction booleanOr(value: unknown, fallback: boolean): boolean {\n return typeof value === 'boolean' ? value : fallback;\n}\n\n/** Resolve a (possibly partial / not-yet-loaded) settings section to a full config. */\nexport function resolveConfig(section: AutoContinueSettings | undefined): AutoContinueConfig {\n const value = section ?? {};\n const text =\n typeof value.continueText === 'string' && value.continueText.trim() !== ''\n ? value.continueText\n : DEFAULT_CONFIG.continueText;\n return {\n continueText: text,\n graceMs: numberOr(value.graceMs, DEFAULT_CONFIG.graceMs),\n cooldownMs: numberOr(value.cooldownMs, DEFAULT_CONFIG.cooldownMs),\n maxConsecutive: Math.max(1, numberOr(value.maxConsecutive, DEFAULT_CONFIG.maxConsecutive)),\n scanOnBoot: booleanOr(value.scanOnBoot, DEFAULT_CONFIG.scanOnBoot),\n scanLimit: Math.max(1, numberOr(value.scanLimit, DEFAULT_CONFIG.scanLimit)),\n freshMs: numberOr(value.freshMs, DEFAULT_CONFIG.freshMs),\n reconnectScanDelayMs: numberOr(value.reconnectScanDelayMs, DEFAULT_CONFIG.reconnectScanDelayMs),\n reconnectBackoffMs: numberOr(value.reconnectBackoffMs, DEFAULT_CONFIG.reconnectBackoffMs),\n verbose: booleanOr(value.verbose, DEFAULT_CONFIG.verbose),\n };\n}\n\n/** 视为「非人为中断」的回合结束原因。aborted(用户停止)与 blocked(策略拒绝)不在其中。 */\ntype NonHumanReason = 'error' | 'interrupted' | 'max-tokens';\n\nfunction isNonHumanReason(kind: string): kind is NonHumanReason {\n return kind === 'error' || kind === 'interrupted' || kind === 'max-tokens';\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** 浏览器当前 IANA 时区; 不可用时省略(宿主允许省略)。 */\nfunction clientTimeZone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\n/** 跨标签页互斥与冷却记录(仅浏览器本地, 不落盘到宿主)。 */\nconst lockPrefix = 'dsh-auto-continue:';\nconst lockKey = (sessionId: SessionId) => `${lockPrefix}lock:${sessionId}`;\nconst stampKey = (sessionId: SessionId) => `${lockPrefix}last:${sessionId}`;\n\n/** 尝试独占本次发送: 两个标签页同时触发时只有一个成功。 */\nfunction claimSend(sessionId: SessionId): boolean {\n try {\n const token = `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n localStorage.setItem(lockKey(sessionId), token);\n return localStorage.getItem(lockKey(sessionId)) === token;\n } catch {\n return true; // 存储不可用(隐私模式等)时放行\n }\n}\n\nfunction releaseSend(sessionId: SessionId): void {\n try {\n localStorage.removeItem(lockKey(sessionId));\n } catch {\n /* ignore */\n }\n}\n\n/** 读/写「上次自动发送」时间戳(跨标签页冷却)。 */\nfunction readLastSend(sessionId: SessionId): number {\n try {\n return Number(localStorage.getItem(stampKey(sessionId)) ?? 0) || 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeLastSend(sessionId: SessionId, at: number): void {\n try {\n localStorage.setItem(stampKey(sessionId), String(at));\n } catch {\n /* ignore */\n }\n}\n\n/** 每会话运行时状态。 */\ninterface SessionState {\n /** 连续自动「继续」次数; 成功回合或用户手动介入后归零。 */\n consecutive: number;\n /** 上次自动「继续」时间戳。 */\n lastAutoAt: number;\n /** 上次自动「继续」尝试(成功或失败)时间戳; 防止失败场景下的快速重试循环。 */\n lastAttemptAt: number;\n /** 我们上次自动发送的文本(用于识别自己的回显)。 */\n lastSentText: string;\n /** 宽限期定时器(进行中的待发送)。 */\n pendingTimer: number | undefined;\n /** 宿主权威 running 位(来自 host/session-status 与回合事件)。 */\n running: boolean | undefined;\n /** 当前排队消息数(来自 session/queue 帧)。 */\n queued: number;\n /** 子代理会话(host/session-added 带 parentSessionId)。 */\n subagent: boolean;\n}\n\nconst freshState = (): SessionState => ({\n consecutive: 0,\n lastAutoAt: 0,\n lastAttemptAt: 0,\n lastSentText: '',\n pendingTimer: undefined,\n running: undefined,\n queued: 0,\n subagent: false,\n});\n\n/** 判定一条 user/message 是否是我们自己自动发送的回显。 */\nfunction isOurEcho(state: SessionState, event: SessionEvent): boolean {\n if (event.type !== 'user/message') return false;\n const message = event.data;\n if (message.source.kind !== 'user') return false;\n if (state.lastSentText === '') return false;\n if (Date.now() - state.lastAutoAt > 30000) return false;\n const text = message.content\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('');\n return text === state.lastSentText;\n}\n\n/** SSE 帧外壳: `{ rpcId, payload }`。 */\ntype FrameEnvelope<T> = { payload: T };\n\n/**\n * 事件流泵: 带指数退避的 SSE 重连循环。\n * - 从未收到任何帧(宿主未就绪): 退避重试, 不触发扫描\n * - 曾连上后断开: 重连, 并通过 onReconnect 通知外层(宿主可能崩溃重启过)\n */\nasync function pumpStream<T>(\n open: (signal: AbortSignal) => AsyncIterable<FrameEnvelope<T>>,\n onFrame: (payload: T) => void,\n onReconnect: () => void,\n getBackoff: () => number,\n log: (message: string) => void,\n signal: AbortSignal,\n): Promise<void> {\n let backoff = getBackoff();\n while (!signal.aborted) {\n let connected = false;\n try {\n for await (const envelope of open(signal)) {\n connected = true;\n onFrame(envelope.payload);\n }\n if (signal.aborted) return;\n } catch (error) {\n if (signal.aborted) return;\n log(`stream error: ${error instanceof Error ? error.message : String(error)}`);\n }\n if (!connected) {\n // 从未连上(宿主未就绪): 指数退避重试\n await sleep(backoff);\n backoff = Math.min(backoff * 2, 15000);\n continue;\n }\n // 曾连上后断开 → 重连并触发外层扫描\n backoff = getBackoff();\n onReconnect();\n await sleep(backoff);\n }\n}\n\n/** 插件主体: 一条 mux 流 + 一条 host 流 + 启动/重连扫描。 */\nexport class AutoContinueRunner {\n private readonly states = new Map<SessionId, SessionState>();\n private readonly muxAbort = new AbortController();\n private readonly hostAbort = new AbortController();\n private disposed = false;\n private reconnectScans = 0;\n\n /**\n * @param api - shared wire client (ctx.connection.api).\n * @param getConfig - read the current resolved configuration (settings scope).\n */\n constructor(\n private readonly api: IApiClient,\n private readonly getConfig: () => AutoContinueConfig,\n ) {\n const config = this.getConfig();\n void this.runMux();\n void this.runHost();\n if (config.scanOnBoot) {\n // 启动时连接可能尚未建立, 循环重试直到成功。\n void this.bootScanLoop();\n }\n this.log(\n `已启动(文本=\"${config.continueText}\", 宽限 ${config.graceMs}ms, ` +\n `冷却 ${config.cooldownMs}ms, 最多连续 ${config.maxConsecutive} 次)`,\n );\n }\n\n private log(message: string): void {\n if (this.getConfig().verbose) console.info(`[auto-continue] ${message}`);\n }\n\n dispose(): void {\n this.disposed = true;\n this.muxAbort.abort();\n this.hostAbort.abort();\n for (const state of this.states.values()) {\n if (state.pendingTimer !== undefined) clearTimeout(state.pendingTimer);\n }\n this.states.clear();\n }\n\n private state(sessionId: SessionId): SessionState {\n let state = this.states.get(sessionId);\n if (state === undefined) {\n state = freshState();\n this.states.set(sessionId, state);\n }\n return state;\n }\n\n private runMux(): Promise<void> {\n return pumpStream<MuxFrame>(\n (signal) => this.api.events.mux({}, signal),\n (payload) => this.onMuxFrame(payload),\n () => this.scheduleReconnectScan(),\n () => this.getConfig().reconnectBackoffMs,\n (m) => this.log(m),\n this.muxAbort.signal,\n );\n }\n\n private runHost(): Promise<void> {\n return pumpStream<HostFrame>(\n (signal) => this.api.events.host({}, signal),\n (payload) => this.onHostFrame(payload),\n () => this.scheduleReconnectScan(),\n () => this.getConfig().reconnectBackoffMs,\n (m) => this.log(m),\n this.hostAbort.signal,\n );\n }\n\n // ---------- mux 帧 ----------\n\n private onMuxFrame(frame: MuxFrame): void {\n switch (frame.type) {\n case 'session/event':\n this.onSessionEvent(frame.sessionId, frame.event);\n break;\n case 'session/queue':\n this.state(frame.sessionId).queued = frame.items.length;\n if (frame.items.length > 0) this.cancelPending(frame.sessionId, '出现排队消息');\n break;\n case 'stream/error':\n this.log(`mux stream/error: ${frame.error.code} ${frame.error.message}`);\n break;\n default:\n break; // session/subscribed、approval/*、question/*、session/jobs、session/projection 与本插件无关\n }\n }\n\n private onSessionEvent(sessionId: SessionId, event: SessionEvent): void {\n const state = this.state(sessionId);\n switch (event.type) {\n case 'turn/start':\n state.running = true;\n this.cancelPending(sessionId, '宿主自行开启新回合');\n break;\n case 'turn/end': {\n state.running = false;\n this.cancelPending(sessionId, '收到新的 turn/end');\n const reason = event.data.reason;\n if (reason.kind === 'completed') {\n // 成功回合: 恢复健康状态\n state.consecutive = 0;\n } else if (reason.kind === 'aborted') {\n // 用户主动停止: 不自动继续, 视为用户介入\n state.consecutive = 0;\n } else if (reason.kind === 'blocked') {\n // 策略拒绝: 不自动继续\n } else if (isNonHumanReason(reason.kind)) {\n this.schedule(sessionId, `turn/end:${reason.kind}`);\n }\n break;\n }\n case 'user/message':\n if (isOurEcho(state, event)) break; // 我们自己的回显\n if (event.data.source.kind === 'user') {\n // 用户手动介入\n state.consecutive = 0;\n this.cancelPending(sessionId, '用户手动发送消息');\n }\n break;\n default:\n break;\n }\n }\n\n // ---------- host 帧 ----------\n\n private onHostFrame(frame: HostFrame): void {\n switch (frame.type) {\n case 'host/session-status':\n this.state(frame.sessionId).running = frame.running;\n if (frame.running) this.cancelPending(frame.sessionId, '宿主报告会话开始运行');\n break;\n case 'host/session-added':\n this.state(frame.sessionId).subagent = frame.parentSessionId !== undefined;\n break;\n case 'host/agent-error':\n if (this.state(frame.sessionId).subagent) break;\n this.log(`host/agent-error(${frame.sessionId}): ${frame.message}`);\n this.schedule(frame.sessionId, 'host/agent-error');\n break;\n case 'host/session-removed':\n this.cancelPending(frame.sessionId, '会话已移除');\n this.states.delete(frame.sessionId);\n break;\n default:\n break;\n }\n }\n\n // ---------- 调度 ----------\n\n private schedule(sessionId: SessionId, reason: string): void {\n const state = this.state(sessionId);\n const config = this.getConfig();\n if (state.subagent) return; // 子代理会话由父代理处理, 不抢跑\n if (state.pendingTimer !== undefined) return; // 已有待发送\n if (Date.now() - state.lastAttemptAt < config.cooldownMs) return; // 冷却期(含失败尝试)\n if (state.consecutive >= config.maxConsecutive) {\n this.log(\n `跳过 ${sessionId}(${reason}): 已连续自动继续 ${state.consecutive} 次, 等待用户介入或成功回合`,\n );\n return;\n }\n if (state.queued > 0) return; // 已有排队消息, 宿主会自行唤醒\n const timer = setTimeout(() => {\n if (state.pendingTimer !== timer) return;\n state.pendingTimer = undefined;\n void this.fire(sessionId, reason);\n }, config.graceMs);\n state.pendingTimer = timer;\n this.log(\n `检测到非人为中断 ${sessionId}(${reason}), ${config.graceMs}ms 后自动发送「${config.continueText}」`,\n );\n }\n\n private cancelPending(sessionId: SessionId, why: string): void {\n const state = this.state(sessionId);\n if (state.pendingTimer === undefined) return;\n clearTimeout(state.pendingTimer);\n state.pendingTimer = undefined;\n this.log(`取消 ${sessionId} 的自动继续(${why})`);\n }\n\n private async fire(sessionId: SessionId, reason: string): Promise<void> {\n if (this.disposed) return;\n const state = this.state(sessionId);\n const config = this.getConfig();\n // 权威 running 检查: 优先用 host 帧, 未知时回退到 session.list\n if (state.running === undefined) {\n const running = await this.runningViaList(sessionId);\n if (running === undefined || running) {\n this.log(`跳过 ${sessionId}: 无法确认空闲(${running === undefined ? '未知' : '运行中'})`);\n return;\n }\n } else if (state.running) {\n this.log(`跳过 ${sessionId}: 会话仍在运行`);\n return;\n }\n if (state.queued > 0) {\n this.log(`跳过 ${sessionId}: 已有排队消息`);\n return;\n }\n // 跨标签页冷却\n if (Date.now() - readLastSend(sessionId) < config.cooldownMs) {\n this.log(`跳过 ${sessionId}: 其他标签页刚发送过`);\n return;\n }\n if (!claimSend(sessionId)) {\n this.log(`跳过 ${sessionId}: 其他标签页正在发送`);\n return;\n }\n const text = config.continueText;\n const zone = clientTimeZone();\n state.lastAttemptAt = Date.now(); // 先记账: 无论成败, 本次尝试都进入冷却\n try {\n const response = await this.api.sessions.prompt({\n sessionId,\n mode: 'queue',\n content: [{ type: 'text', text }],\n ...(zone === undefined ? {} : { clientTimeZone: zone }),\n });\n if (response.result.ok) {\n const now = Date.now();\n state.consecutive += 1;\n state.lastAutoAt = now;\n state.lastSentText = text;\n writeLastSend(sessionId, now);\n this.log(`已自动发送「${text}」到 ${sessionId}(${reason}), 第 ${state.consecutive} 次连续`);\n } else {\n this.log(\n `发送失败 ${sessionId}: ${response.result.error.code} ${response.result.error.message}`,\n );\n }\n } catch (error) {\n this.log(`发送异常 ${sessionId}: ${error instanceof Error ? error.message : String(error)}`);\n } finally {\n releaseSend(sessionId);\n }\n }\n\n private async runningViaList(sessionId: SessionId): Promise<boolean | undefined> {\n try {\n const response = await this.api.sessions.list({});\n if (!response.result.ok) return undefined;\n const item = response.result.value.items.find(\n (summary: SessionSummary) => summary.sessionId === sessionId,\n );\n return item === undefined ? undefined : item.running;\n } catch {\n return undefined;\n }\n }\n\n // ---------- 启动/重连扫描 ----------\n\n private scheduleReconnectScan(): void {\n this.reconnectScans += 1;\n const scan = this.reconnectScans;\n setTimeout(() => {\n if (scan !== this.reconnectScans || this.disposed) return;\n void this.scanLoop(6, this.getConfig().reconnectScanDelayMs);\n }, this.getConfig().reconnectScanDelayMs);\n }\n\n private async bootScanLoop(): Promise<void> {\n await this.scanLoop(Infinity, 3000);\n }\n\n /** 反复尝试扫描, 直到成功(宿主就绪)或达到次数上限。 */\n private async scanLoop(attempts: number, delayMs: number): Promise<void> {\n for (let attempt = 0; attempt < attempts && !this.disposed; attempt += 1) {\n try {\n if (await this.scanInterrupted()) return;\n } catch (error) {\n if (this.disposed) return;\n // 宿主未就绪时每 3s 重试; 只节流记录日志, 避免刷屏。\n if (attempt % 10 === 0) {\n this.log(\n `扫描失败(${attempt + 1}/${attempts === Infinity ? '∞' : attempts}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n if (attempt + 1 < attempts) await sleep(delayMs);\n }\n }\n\n /**\n * 扫描最近中断过的会话: 最后回合以非人为原因结束, 且其后没有新回合或用户消息。\n * @returns 是否成功完成一次扫描(宿主就绪)。\n */\n private async scanInterrupted(): Promise<boolean> {\n const config = this.getConfig();\n const response = await this.api.sessions.list({});\n if (!response.result.ok) return false;\n const items = response.result.value.items;\n const candidates = items\n .filter((summary) => !summary.running && summary.parentSessionId === undefined)\n .slice(0, config.scanLimit);\n const now = Date.now();\n for (const summary of candidates) {\n if (this.disposed) return true;\n const state = this.state(summary.sessionId);\n if (state.pendingTimer !== undefined) continue;\n if (state.consecutive >= config.maxConsecutive) continue;\n if (now - state.lastAttemptAt < config.cooldownMs) continue;\n let events;\n try {\n const page = await this.api.sessions.history({\n sessionId: summary.sessionId,\n maxMessages: 30,\n });\n if (!page.result.ok) continue;\n events = page.result.value.events;\n } catch {\n continue; // 会话可能刚被移除\n }\n // 从尾部找最后一个 turn/end(在分支内完成收窄)\n let lastEnd: SessionEvent<'turn/end'> | undefined;\n for (let i = events.length - 1; i >= 0; i -= 1) {\n const event = events[i]?.event;\n if (event !== undefined && event.type === 'turn/end') {\n lastEnd = event;\n break;\n }\n }\n if (lastEnd === undefined) continue;\n const reason = lastEnd.data.reason;\n if (!isNonHumanReason(reason.kind)) continue;\n if (lastEnd.time < now - config.freshMs) continue; // 太久远, 不翻旧账\n // 该 turn/end 之后不能有新回合或用户消息(说明已被处理)\n let superseded = false;\n for (const entry of events) {\n const event = entry.event;\n if (event.seq <= lastEnd.seq) continue;\n if (event.type === 'turn/start') superseded = true;\n if (event.type === 'user/message' && event.data.source.kind === 'user') superseded = true;\n if (superseded) break;\n }\n if (superseded) continue;\n this.log(`扫描发现中断 ${summary.sessionId}(turn/end:${reason.kind}), 安排自动继续`);\n this.schedule(summary.sessionId, `scan:turn/end:${reason.kind}`);\n }\n return true;\n }\n}\n", "/**\n * `auto-continue` namespace dictionaries: copy for the plugin settings card\n * registered into the `settings.plugin.item` seat of the plugin-configuration\n * section. Includes the card-chrome keys the card component reads.\n */\n\n/** 简体中文词典(键集的事实来源)。 */\nexport const zh = {\n 'card.title': '自动继续',\n 'card.description': '请求因网络等原因(非人为)中断后, 自动发送「继续」续跑。',\n 'field.continueText': '继续文本',\n 'field.continueTextHint': '中断后自动发送的消息内容。',\n 'field.graceMs': '宽限期 (ms)',\n 'field.graceMsHint': '检测到中断后等待的时长; 期间宿主自行恢复则取消。',\n 'field.cooldownMs': '冷却时间 (ms)',\n 'field.cooldownMsHint': '同一会话两次自动「继续」的最小间隔, 失败尝试也计入。',\n 'field.maxConsecutive': '最大连续次数',\n 'field.maxConsecutiveHint': '同一会话连续自动「继续」的上限; 超过后停止, 直到用户手动介入或出现成功回合。',\n 'field.scanOnBoot': '启动/重连扫描',\n 'field.scanOnBootHint': '页面启动或重连时扫描最近中断的会话并自动续跑(如浏览器关闭期间宿主崩溃)。',\n 'field.scanLimit': '扫描会话数',\n 'field.scanLimitHint': '最多检查多少个最近更新的会话(不含运行中与子代理会话)。',\n 'field.freshMs': '扫描时间窗 (ms)',\n 'field.freshMsHint': '扫描只处理该时间窗内的中断。',\n 'field.reconnectScanDelayMs': '重连扫描延迟 (ms)',\n 'field.reconnectScanDelayMsHint': '重连后等待宿主完成恢复再扫描。',\n 'field.reconnectBackoffMs': '重连退避 (ms)',\n 'field.reconnectBackoffMsHint': '事件流断开后的重连间隔。',\n 'field.verbose': '详细日志',\n 'field.verboseHint': '在浏览器控制台输出 [auto-continue] 日志。',\n 'chrome.collapse': '收起设置',\n 'chrome.expand': '展开设置',\n 'chrome.unsaved': '未保存',\n 'chrome.readOnly': '当前部署的设置只读。',\n 'chrome.saveFailed': '部署未接受这些值, 已保留供你修改。',\n 'chrome.discard': '放弃',\n 'chrome.saving': '保存中…',\n 'chrome.save': '保存',\n 'chrome.overridden': '已覆盖',\n 'chrome.reset': '恢复默认',\n 'chrome.invalidNumber': '请输入数字, 留空则使用默认值。',\n 'chrome.inherit': '继承',\n 'chrome.on': '开',\n 'chrome.off': '关',\n} satisfies Record<string, string>;\n\n/** 本插件的键联合。 */\nexport type SettingsCardKey = keyof typeof zh;\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: Record<SettingsCardKey, string> = {\n 'card.title': 'Auto continue',\n 'card.description': 'When a request is interrupted by a non-human cause, automatically send 「继续」 to resume.',\n 'field.continueText': 'Continue text',\n 'field.continueTextHint': 'Message automatically sent after an interruption.',\n 'field.graceMs': 'Grace period (ms)',\n 'field.graceMsHint': 'Wait after an interruption; cancelled if the host recovers on its own.',\n 'field.cooldownMs': 'Cooldown (ms)',\n 'field.cooldownMsHint': 'Minimum interval between auto-continues per session; failed attempts count too.',\n 'field.maxConsecutive': 'Max consecutive',\n 'field.maxConsecutiveHint': 'Max consecutive auto-continues per session; stops until a user intervenes or a turn completes.',\n 'field.scanOnBoot': 'Scan on load / reconnect',\n 'field.scanOnBootHint': 'Scan recently interrupted sessions on page load or reconnect (e.g. the host crashed while the browser was closed).',\n 'field.scanLimit': 'Scan limit',\n 'field.scanLimitHint': 'How many most-recently-updated sessions to check (running / subagent sessions excluded).',\n 'field.freshMs': 'Scan window (ms)',\n 'field.freshMsHint': 'Only interruptions inside this window are considered.',\n 'field.reconnectScanDelayMs': 'Reconnect scan delay (ms)',\n 'field.reconnectScanDelayMsHint': 'Wait for the host to finish recovering before scanning after a reconnect.',\n 'field.reconnectBackoffMs': 'Reconnect backoff (ms)',\n 'field.reconnectBackoffMsHint': 'Interval between event-stream reconnect attempts.',\n 'field.verbose': 'Verbose logs',\n 'field.verboseHint': 'Log [auto-continue] lines to the browser console.',\n 'chrome.collapse': 'Hide settings',\n 'chrome.expand': 'Show settings',\n 'chrome.unsaved': 'Unsaved',\n 'chrome.readOnly': 'This deployment stores settings read-only.',\n 'chrome.saveFailed': 'The deployment did not accept these values; they were left for you to correct.',\n 'chrome.discard': 'Discard',\n 'chrome.saving': 'Saving…',\n 'chrome.save': 'Save',\n 'chrome.overridden': 'Overridden',\n 'chrome.reset': 'Reset to default',\n 'chrome.invalidNumber': 'Enter a number, or leave blank to use the default.',\n 'chrome.inherit': 'Inherit',\n 'chrome.on': 'On',\n 'chrome.off': 'Off',\n};\n", "/**\n * The auto-continue settings card: edits the `auto-continue` namespace fields\n * from the plugin-configuration section (the `settings.plugin.item` seat).\n *\n * Self-contained card chrome (disclosure header, staged fields, save/discard\n * footer) following the plugin-card store pattern of the DSH plugin\n * configuration section; styles live in `styles.ts` and use the DSH design\n * tokens so the card follows the active theme.\n */\nimport { useState, type ReactNode } from 'react';\nimport { createSnapshotStore, type SettingsScope, type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';\nimport type { AutoContinueSettings } from './engine.ts';\nimport type { SettingsCardKey } from './locales.ts';\nimport {\n booleanField,\n CardForm,\n numberField,\n textField,\n type CardActions,\n type CardFieldState,\n type CardShell,\n} from './settings-form.ts';\nimport { injectStyles } from './styles.ts';\n\n// Styles must land during factory materialization so the module system's\n// style bookkeeping (HMR) owns them.\ninjectStyles();\n\n/** What the auto-continue card renders. */\nexport interface AutoContinueSettingsCardState extends CardShell {\n continueText: CardFieldState;\n graceMs: CardFieldState;\n cooldownMs: CardFieldState;\n maxConsecutive: CardFieldState;\n scanOnBoot: CardFieldState;\n scanLimit: CardFieldState;\n freshMs: CardFieldState;\n reconnectScanDelayMs: CardFieldState;\n reconnectBackoffMs: CardFieldState;\n verbose: CardFieldState;\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface AutoContinueSettingsCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as useAutoContinueSettingsCard. */\n autoContinueSettingsCard: SnapshotStore<AutoContinueSettingsCardState>;\n };\n}\n\n/** Bridges the `auto-continue` scope onto the card's staged form. */\nexport class AutoContinueSettingsCardController {\n private readonly form: CardForm<AutoContinueSettings>;\n private readonly store: SnapshotStore<AutoContinueSettingsCardState>;\n\n /**\n * @param scope - the bound settings scope for the `auto-continue` namespace.\n */\n constructor(scope: SettingsScope<AutoContinueSettings>) {\n this.form = new CardForm(scope, [\n textField('continueText'),\n numberField('graceMs', 0),\n numberField('cooldownMs', 0),\n numberField('maxConsecutive', 1),\n booleanField('scanOnBoot'),\n numberField('scanLimit', 1),\n numberField('freshMs', 0),\n numberField('reconnectScanDelayMs', 0),\n numberField('reconnectBackoffMs', 0),\n booleanField('verbose'),\n ]);\n this.store = this.form.bind(() => this.projection(), createSnapshotStore);\n }\n\n private projection(): AutoContinueSettingsCardState {\n return {\n ...this.form.shell(),\n continueText: this.form.field('continueText'),\n graceMs: this.form.field('graceMs'),\n cooldownMs: this.form.field('cooldownMs'),\n maxConsecutive: this.form.field('maxConsecutive'),\n scanOnBoot: this.form.field('scanOnBoot'),\n scanLimit: this.form.field('scanLimit'),\n freshMs: this.form.field('freshMs'),\n reconnectScanDelayMs: this.form.field('reconnectScanDelayMs'),\n reconnectBackoffMs: this.form.field('reconnectBackoffMs'),\n verbose: this.form.field('verbose'),\n };\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and its form actions.\n */\n inject(): AutoContinueSettingsCardFace {\n return { hooks: { autoContinueSettingsCard: this.store }, ...this.form.actions() };\n }\n}\n\n/** Props the renderer binds for the auto-continue card. */\nexport type AutoContinueSettingsCardProps =\n PropsRuntime<'settings.plugin.item'> & PropsLocale<'auto-continue'> & InjectFace<AutoContinueSettingsCardFace>;\n\n/** Card chrome: a disclosure header naming the plugin and what its settings govern, the controls, and the save that writes them. */\nfunction SettingsCard(props: {\n t: (key: SettingsCardKey) => string;\n titleKey: SettingsCardKey;\n descriptionKey: SettingsCardKey;\n state: CardShell;\n onSave: () => void;\n onDiscard: () => void;\n children: ReactNode;\n}) {\n const [open, setOpen] = useState(false);\n const { state } = props;\n if (!state.available) return null;\n const title = props.t(props.titleKey);\n const blocked = !state.dirty || state.invalid || state.saving;\n return (\n <li className={open ? 'dshAcCard dshAcCardOpen' : 'dshAcCard'}>\n <button\n type=\"button\"\n className=\"dshAcHeader\"\n aria-expanded={open}\n aria-label={`${props.t(open ? 'chrome.collapse' : 'chrome.expand')}: ${title}`}\n title={props.t(props.descriptionKey)}\n onClick={() => setOpen(!open)}\n >\n <span className=\"dshAcHeadText\">\n <span className=\"dshAcName\">{title}</span>\n <span className=\"dshAcDescription\">{props.t(props.descriptionKey)}</span>\n </span>\n {state.dirty ? (\n <span className=\"dshAcPending\" title={props.t('chrome.unsaved')}>\n {props.t('chrome.unsaved')}\n </span>\n ) : null}\n <span className={open ? 'dshAcChevron dshAcChevronOpen' : 'dshAcChevron'}>▾</span>\n </button>\n {open ? (\n <div className=\"dshAcBody\">\n {!state.writable ? (\n <p className=\"dshAcReadOnly\" role=\"status\">{props.t('chrome.readOnly')}</p>\n ) : null}\n {props.children}\n <div className=\"dshAcFooter\">\n {state.failed ? (\n <p className=\"dshAcFailed\" role=\"status\">{props.t('chrome.saveFailed')}</p>\n ) : null}\n <button\n type=\"button\"\n className=\"dshAcDiscard\"\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('chrome.discard')}\n </button>\n <button type=\"button\" className=\"dshAcSave\" disabled={blocked} onClick={props.onSave}>\n {props.t(!state.saving ? 'chrome.save' : 'chrome.saving')}\n </button>\n </div>\n </div>\n ) : null}\n </li>\n );\n}\n\n/** Props every field control needs regardless of its value type. */\ninterface FieldProps {\n id: string;\n label: string;\n hint: string;\n text: string;\n overridden: boolean;\n invalid: boolean;\n disabled: boolean;\n t: (key: SettingsCardKey) => string;\n onEdit: (text: string) => void;\n onReset: () => void;\n}\n\n/** A staged value field; `numeric` only hints the keypad, which drafts a field accepts is decided by its spec. */\nfunction ValueField(props: FieldProps & { numeric?: boolean; placeholder?: string }) {\n return (\n <div className=\"dshAcField\">\n <div className=\"dshAcHead\">\n <label className=\"dshAcLabel\" htmlFor={props.id}>{props.label}</label>\n {props.overridden ? (\n <span className=\"dshAcBadges\">\n <span className=\"dshAcBadge\">{props.t('chrome.overridden')}</span>\n <button type=\"button\" className=\"dshAcReset\" disabled={props.disabled} onClick={props.onReset}>\n {props.t('chrome.reset')}\n </button>\n </span>\n ) : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? 'dshAcInput dshAcInputInvalid' : 'dshAcInput'}\n type=\"text\"\n inputMode={props.numeric === true ? 'numeric' : undefined}\n aria-invalid={props.invalid || undefined}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => props.onEdit(event.target.value)}\n />\n <p className={props.invalid ? 'dshAcInvalid' : 'dshAcHint'}>\n {props.invalid ? props.t('chrome.invalidNumber') : props.hint}\n </p>\n </div>\n );\n}\n\n/** A staged boolean field: inherit / on / off. */\nfunction BooleanField(props: FieldProps) {\n return (\n <div className=\"dshAcField\">\n <div className=\"dshAcHead\">\n <label className=\"dshAcLabel\" htmlFor={props.id}>{props.label}</label>\n {props.overridden ? (\n <span className=\"dshAcBadges\">\n <span className=\"dshAcBadge\">{props.t('chrome.overridden')}</span>\n <button type=\"button\" className=\"dshAcReset\" disabled={props.disabled} onClick={props.onReset}>\n {props.t('chrome.reset')}\n </button>\n </span>\n ) : null}\n </div>\n <select\n id={props.id}\n className=\"dshAcSelect\"\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => props.onEdit(event.target.value)}\n >\n <option value=\"\">{props.t('chrome.inherit')}</option>\n <option value=\"true\">{props.t('chrome.on')}</option>\n <option value=\"false\">{props.t('chrome.off')}</option>\n </select>\n <p className=\"dshAcHint\">{props.hint}</p>\n </div>\n );\n}\n\n/**\n * Render the auto-continue card.\n * @param props - locale copy, the card snapshot, and its form actions.\n * @returns the card.\n */\nexport function AutoContinueSettingsCard(props: AutoContinueSettingsCardProps) {\n const { t } = props;\n const state = props.useAutoContinueSettingsCard((snapshot) => snapshot);\n const disabled = !state.writable;\n const shared = { t, disabled };\n return (\n <SettingsCard\n t={t}\n titleKey=\"card.title\"\n descriptionKey=\"card.description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n >\n <ValueField\n id=\"auto-continue-continue-text\"\n label={t('field.continueText')}\n hint={t('field.continueTextHint')}\n {...shared}\n {...state.continueText}\n onEdit={(text) => props.edit('continueText', text)}\n onReset={() => props.resetField('continueText')}\n />\n <ValueField\n id=\"auto-continue-grace-ms\"\n label={t('field.graceMs')}\n hint={t('field.graceMsHint')}\n numeric\n {...shared}\n {...state.graceMs}\n onEdit={(text) => props.edit('graceMs', text)}\n onReset={() => props.resetField('graceMs')}\n />\n <ValueField\n id=\"auto-continue-cooldown-ms\"\n label={t('field.cooldownMs')}\n hint={t('field.cooldownMsHint')}\n numeric\n {...shared}\n {...state.cooldownMs}\n onEdit={(text) => props.edit('cooldownMs', text)}\n onReset={() => props.resetField('cooldownMs')}\n />\n <ValueField\n id=\"auto-continue-max-consecutive\"\n label={t('field.maxConsecutive')}\n hint={t('field.maxConsecutiveHint')}\n numeric\n {...shared}\n {...state.maxConsecutive}\n onEdit={(text) => props.edit('maxConsecutive', text)}\n onReset={() => props.resetField('maxConsecutive')}\n />\n <BooleanField\n id=\"auto-continue-scan-on-boot\"\n label={t('field.scanOnBoot')}\n hint={t('field.scanOnBootHint')}\n {...shared}\n {...state.scanOnBoot}\n onEdit={(text) => props.edit('scanOnBoot', text)}\n onReset={() => props.resetField('scanOnBoot')}\n />\n <ValueField\n id=\"auto-continue-scan-limit\"\n label={t('field.scanLimit')}\n hint={t('field.scanLimitHint')}\n numeric\n {...shared}\n {...state.scanLimit}\n onEdit={(text) => props.edit('scanLimit', text)}\n onReset={() => props.resetField('scanLimit')}\n />\n <ValueField\n id=\"auto-continue-fresh-ms\"\n label={t('field.freshMs')}\n hint={t('field.freshMsHint')}\n numeric\n {...shared}\n {...state.freshMs}\n onEdit={(text) => props.edit('freshMs', text)}\n onReset={() => props.resetField('freshMs')}\n />\n <ValueField\n id=\"auto-continue-reconnect-scan-delay\"\n label={t('field.reconnectScanDelayMs')}\n hint={t('field.reconnectScanDelayMsHint')}\n numeric\n {...shared}\n {...state.reconnectScanDelayMs}\n onEdit={(text) => props.edit('reconnectScanDelayMs', text)}\n onReset={() => props.resetField('reconnectScanDelayMs')}\n />\n <ValueField\n id=\"auto-continue-reconnect-backoff\"\n label={t('field.reconnectBackoffMs')}\n hint={t('field.reconnectBackoffMsHint')}\n numeric\n {...shared}\n {...state.reconnectBackoffMs}\n onEdit={(text) => props.edit('reconnectBackoffMs', text)}\n onReset={() => props.resetField('reconnectBackoffMs')}\n />\n <BooleanField\n id=\"auto-continue-verbose\"\n label={t('field.verbose')}\n hint={t('field.verboseHint')}\n {...shared}\n {...state.verbose}\n onEdit={(text) => props.edit('verbose', text)}\n onReset={() => props.resetField('verbose')}\n />\n </SettingsCard>\n );\n}\n", "/**\n * Staged form model behind the plugin settings card — a self-contained\n * implementation of the plugin-card store pattern used by the DSH plugin\n * configuration section.\n *\n * A card stages what the user types and writes it only when they save. Each\n * settings write is a durable, revision-fenced document mutation, so staging\n * keeps what is on screen exactly what a save would store. A field shows its\n * effective value — the user layer over the composition layer over the schema\n * default — and whether the user layer carries it (presence, not value\n * equality, marks an override).\n */\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite = { kind: 'set'; value: unknown } | { kind: 'clear' };\n\n/** How one field converts between its stored value and its draft text. */\nexport interface CardFieldSpec {\n /** Field name inside the namespace section. */\n field: string;\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string;\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined;\n}\n\n/** One field as the card's control renders it. */\nexport interface CardFieldState {\n /** Draft text the control renders. */\n text: string;\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean;\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean;\n}\n\n/** Form state every plugin card shares. */\nexport interface CardShell {\n /** False while the namespace is not served to this client; the card renders nothing. */\n available: boolean;\n /** Whether the Host document accepts writes. */\n writable: boolean;\n /** Whether the form holds edits that a save would write. */\n dirty: boolean;\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean;\n /** Whether a save is crossing the wire. */\n saving: boolean;\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean;\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void;\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void;\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void;\n /** Drop every staged edit. */\n discard: () => void;\n}\n\n/** A whole-number field. An empty draft clears the field; a non-number or out-of-range draft blocks the save. */\nexport function numberField(field: string, min = 0): CardFieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'number' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim();\n if (trimmed === '') return { kind: 'clear' };\n const parsed = Number(trimmed);\n if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < min) return undefined;\n return { kind: 'set', value: parsed };\n },\n };\n}\n\n/** A free-text field. An empty draft clears the field, so emptying the control and saving is the same gesture as resetting it. */\nexport function textField(field: string): CardFieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'string' ? value : ''),\n parse: (text) => {\n const trimmed = text.trim();\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed };\n },\n };\n}\n\n/** A boolean field, edited through true/false draft text; an empty draft inherits. */\nexport function booleanField(field: string): CardFieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'boolean' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim();\n if (trimmed === '') return { kind: 'clear' };\n if (trimmed === 'true') return { kind: 'set', value: true };\n if (trimmed === 'false') return { kind: 'set', value: false };\n return undefined;\n },\n };\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string;\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean;\n}\n\n/**\n * Stages one card's edits over one settings namespace and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted, so the\n * outcome is read back from the section rather than predicted here. A save\n * that did not land keeps its drafts, so the user can correct them instead of\n * retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, CardFieldSpec>;\n private readonly staged = new Map<string, StagedEdit>();\n private readonly listeners = new Set<() => void>();\n private saving = false;\n private failed = false;\n\n /**\n * @param scope - the bound settings scope for this card's namespace.\n * @param specs - the section fields this card edits.\n */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: CardFieldSpec[],\n ) {\n this.specs = new Map(specs.map((spec) => [spec.field, spec]));\n this.scope.subscribe(() => this.publish());\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S, createStore: (init: S) => SnapshotStore<S>): SnapshotStore<S> {\n const store = createStore(project());\n this.listeners.add(() => store.set(project()));\n return store;\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot();\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: this.plan().length > 0,\n invalid: this.plan().some((item) => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n };\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): CardFieldState {\n const spec = this.specOf(field);\n const staged = this.staged.get(field);\n if (staged === undefined) {\n return {\n text: spec.format(this.sectionValue(field)),\n overridden: this.stored(field),\n invalid: false,\n };\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text);\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n };\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => this.stage(field, { text, clear: false }),\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true });\n },\n save: () => void this.save(),\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return;\n this.staged.clear();\n this.failed = false;\n this.publish();\n },\n };\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan();\n const writes = plan.flatMap((item) => (item.run === undefined ? [] : [item.run]));\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return;\n // Snapshot the fields this save writes, so edits staged while it is in\n // flight survive: only the staged keys this save actually wrote are cleared.\n const fields = new Set(plan.map((item) => item.field));\n this.saving = true;\n this.failed = false;\n this.publish();\n let landed = true;\n for (const write of writes) {\n landed = (await write()) && landed;\n }\n if (landed) {\n for (const field of fields) this.staged.delete(field);\n }\n this.saving = false;\n this.failed = !landed;\n this.publish();\n }\n\n /**\n * Every staged edit a save would write. An entry whose draft is not a value\n * its field accepts carries no write: the form is still dirty, and the save\n * refuses rather than dropping the edit. A staged edit that matches the\n * effective section is not a write at all.\n */\n private plan(): { field: string; run: (() => Promise<boolean>) | undefined }[] {\n const plan: { field: string; run: (() => Promise<boolean>) | undefined }[] = [];\n for (const [field, staged] of this.staged) {\n const spec = this.specOf(field);\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, run: () => this.clear(field) });\n continue;\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue;\n const write = spec.parse(staged.text);\n if (write === undefined) plan.push({ field, run: undefined });\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) });\n else plan.push({ field, run: () => this.store(field, write.value) });\n }\n return plan;\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field);\n return !this.stored(field);\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value);\n return this.userLayer()?.[field] === value;\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit);\n this.failed = false;\n this.publish();\n }\n\n private specOf(field: string): CardFieldSpec {\n const spec = this.specs.get(field);\n if (spec === undefined) throw new Error(`settings card has no field ${field}`);\n return spec;\n }\n\n private sectionValue(field: string): unknown {\n return (this.scope.getSnapshot().value as Record<string, unknown> | undefined)?.[field];\n }\n\n private baseValue(field: string): unknown {\n return (this.scope.getSnapshot().base as Record<string, unknown> | undefined)?.[field];\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.scope.getSnapshot().user as Record<string, unknown> | undefined;\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer();\n return user !== undefined && Object.prototype.hasOwnProperty.call(user, field);\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener();\n }\n}\n", "/**\n * Styles for the auto-continue settings card, injected at factory\n * materialization so the client module system's style bookkeeping (HMR) owns\n * them. Uses the DSH design tokens (`--dsw-alias-*`) so the card follows the\n * active theme.\n */\n\nconst css = `\n.dshAcCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n.dshAcCard:hover { border-color: var(--dsw-alias-label-dimmed); }\n.dshAcCardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dshAcHeader {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n.dshAcHeader:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }\n.dshAcHeadText { flex-direction: column; flex: 1; gap: 4px; min-width: 0; display: flex; }\n.dshAcName { color: var(--dsw-alias-label-primary); font-size: 15px; font-weight: 600; line-height: 1.4; }\n.dshAcDescription { color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 1.5; }\n.dshAcChevron { color: var(--dsw-alias-label-tertiary); flex: none; transition: transform .16s; }\n.dshAcChevronOpen { transform: rotate(180deg); }\n.dshAcBody { border-top: 1px solid var(--dsw-alias-border-l2); margin: 0 16px; padding-bottom: 8px; }\n.dshAcReadOnly { color: var(--dsw-alias-label-tertiary); margin: 12px 0 0; font-size: 12px; line-height: 1.5; }\n.dshAcPending {\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n border-radius: 999px;\n flex: none;\n padding: 1px 8px;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n}\n.dshAcFooter {\n border-top: 1px solid var(--dsw-alias-border-l2);\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n padding: 12px 0 4px;\n display: flex;\n}\n.dshAcFailed { min-width: 0; color: var(--dsw-alias-label-error); flex: 1; margin: 0; font-size: 12px; line-height: 1.5; }\n.dshAcDiscard, .dshAcSave {\n appearance: none;\n font: inherit;\n cursor: pointer;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font-size: 13px;\n line-height: 1.5;\n}\n.dshAcDiscard { border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-secondary); background: none; }\n.dshAcDiscard:hover:not(:disabled) { color: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-label-dimmed); }\n.dshAcSave { background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-3); }\n.dshAcDiscard:disabled, .dshAcSave:disabled { opacity: .4; cursor: default; }\n.dshAcDiscard:focus-visible, .dshAcSave:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }\n.dshAcField { flex-direction: column; gap: 6px; padding: 12px 0; display: flex; }\n.dshAcField + .dshAcField { border-top: 1px solid var(--dsw-alias-border-l2); }\n.dshAcHead { align-items: center; gap: 8px; display: flex; }\n.dshAcLabel { min-width: 0; color: var(--dsw-alias-label-primary); flex: 1; font-size: 13px; font-weight: 500; line-height: 1.5; }\n.dshAcBadges { align-items: center; gap: 8px; display: inline-flex; }\n.dshAcBadge {\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n}\n.dshAcReset {\n font: inherit;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n font-size: 12px;\n line-height: 1.5;\n}\n.dshAcReset:hover:not(:disabled) { color: var(--dsw-alias-label-primary); }\n.dshAcReset:disabled { cursor: default; }\n.dshAcInput {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n height: 34px;\n font: inherit;\n color: var(--dsw-alias-label-primary);\n border-radius: 8px;\n padding: 0 12px;\n font-size: 13px;\n line-height: 1.5;\n}\n.dshAcInput:focus-visible { border-color: var(--dsw-alias-brand-primary); outline: none; }\n.dshAcInput:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n.dshAcInputInvalid { border-color: var(--dsw-alias-label-error); }\n.dshAcSelect {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n height: 34px;\n font: inherit;\n color: var(--dsw-alias-label-primary);\n border-radius: 8px;\n padding: 0 8px;\n font-size: 13px;\n line-height: 1.5;\n}\n.dshAcSelect:focus-visible { border-color: var(--dsw-alias-brand-primary); outline: none; }\n.dshAcSelect:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n.dshAcInvalid { color: var(--dsw-alias-label-error); margin: 0; font-size: 12px; line-height: 1.5; }\n.dshAcHint { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 1.5; }\n`;\n\n/** Inject the stylesheet once; a no-op outside a browser environment. */\nexport function injectStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.querySelector('style[data-plugin-css=\"auto-continue/card\"]') !== null) return;\n const tag = document.createElement('style');\n tag.dataset.plugin = 'dsh-client-auto-continue';\n tag.dataset.pluginCss = 'auto-continue/card';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Auto-continue plugin, browser half.\n *\n * - Runs the auto-continue engine over the live mux + host event streams.\n * - Registers the `auto-continue` settings card into the plugin-configuration\n * section (`settings.plugin.item`), editing the same namespace the engine\n * reads — every behavior knob is configurable from the GUI.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client';\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client';\n// Type-only: pulls the settings-surface SlotMap merge and ctx.settingsScope.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client';\n// Type-only: pulls the `settings.plugin.item` SlotMap merge.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client';\nimport { AutoContinueRunner, resolveConfig, type AutoContinueSettings } from './engine.ts';\nimport { en, zh, type SettingsCardKey } from './locales.ts';\nimport {\n AutoContinueSettingsCard,\n AutoContinueSettingsCardController,\n} from './settings-card.tsx';\n\n/** 客户端根上下文的 connection 服务(由 dsh-client-connection 挂载)。 */\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n connection: ConnectionHandle;\n }\n}\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'auto-continue';\n\n/** Settings namespace the engine reads and the settings card edits. */\nconst SETTINGS_NS = 'auto-continue';\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** auto-continue settings-card copy. */\n 'auto-continue': SettingsCardKey;\n }\n}\n\n/** Services required by this plugin. */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope'];\n\n/** 当前 runner(HMR 重载时先销毁旧的再建新的)。 */\nlet current: AutoContinueRunner | null = null;\n\n/**\n * Plugin body: mount the engine and the settings card.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'auto-continue: dictionaries');\n\n // Engine: reads the settings scope live, so GUI changes apply immediately.\n const scope = ctx.settingsScope.bind<AutoContinueSettings>({ namespace: SETTINGS_NS });\n current?.dispose();\n current = new AutoContinueRunner(ctx.connection.api, () => resolveConfig(scope.getSnapshot().value));\n\n // Plugin configuration card: one staged form over the `auto-continue`\n // settings namespace, contributed to the plugin-configuration section.\n const controller = new AutoContinueSettingsCardController(scope);\n ctx.slots.inject('settings.plugin.item', () =>\n ctx.slots.register(\n {\n name: 'settings.plugin.item',\n id: 'auto-continue',\n order: 90,\n locale: NS,\n inject: () => controller.inject(),\n },\n AutoContinueSettingsCard,\n ),\n );\n}\n", "/**\n * Auto-continue engine — browser half core.\n *\n * Watches the two live event streams of the dsh web GUI (mux + host):\n * - turns ended for a non-human reason (`turn/end` reason ∈ error / interrupted / max-tokens)\n * - host-reported agent failures with no turn position (`host/agent-error`)\n * After a grace period it sends a queued prompt (default 「继续」) to that\n * session — exactly equivalent to the user typing it manually.\n *\n * All behavior is driven by the `auto-continue` settings namespace (see the\n * plugin's settings card); every knob below is user-configurable there.\n */\n\nimport type {\n HostFrame,\n IApiClient,\n MuxFrame,\n SessionId,\n SessionSummary,\n} from '@deepseek-ai/dsh-client-connection/client';\nimport type { SessionEvent } from '@deepseek-ai/dsh-session/types';\n\n/** The `auto-continue` settings section (all fields optional on the wire; the host schema carries defaults). */\nexport interface AutoContinueSettings {\n /** Text automatically sent after an interruption. */\n continueText?: string;\n /** Grace period after an interruption before auto-sending (ms). */\n graceMs?: number;\n /** Minimum interval between two auto-continues per session (ms). */\n cooldownMs?: number;\n /** Max consecutive auto-continues per session before stopping. */\n maxConsecutive?: number;\n /** Scan recently interrupted sessions on page load / reconnect. */\n scanOnBoot?: boolean;\n /** Max sessions the scan checks (most recently updated). */\n scanLimit?: number;\n /** Scan only considers interruptions inside this window (ms). */\n freshMs?: number;\n /** Delay before scanning after a reconnect (ms). */\n reconnectScanDelayMs?: number;\n /** SSE reconnect backoff (ms). */\n reconnectBackoffMs?: number;\n /** Log `[auto-continue]` lines to the browser console. */\n verbose?: boolean;\n /** Classify failures: auto-continue transient errors only; permanent ones (auth/balance/model) are skipped and notified. */\n classify?: boolean;\n /** Cooldown multiplier per consecutive failure (adaptive backoff). */\n backoffFactor?: number;\n /** Cap on the effective backoff interval (ms). */\n backoffMaxMs?: number;\n /** Show browser notifications for auto-continue events. */\n notify?: boolean;\n}\n\n/** Fully resolved configuration (built-in defaults + user overrides). */\nexport type AutoContinueConfig = Required<AutoContinueSettings>;\n\n/** Built-in defaults — must match the host schema defaults in src/index.ts. */\nexport const DEFAULT_CONFIG: AutoContinueConfig = {\n continueText: '继续',\n graceMs: 3000,\n cooldownMs: 20000,\n maxConsecutive: 3,\n scanOnBoot: true,\n scanLimit: 8,\n freshMs: 15 * 60 * 1000,\n reconnectScanDelayMs: 5000,\n reconnectBackoffMs: 3000,\n verbose: true,\n classify: true,\n backoffFactor: 2,\n backoffMaxMs: 300000,\n notify: false,\n};\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\nfunction booleanOr(value: unknown, fallback: boolean): boolean {\n return typeof value === 'boolean' ? value : fallback;\n}\n\n/** Resolve a (possibly partial / not-yet-loaded) settings section to a full config. */\nexport function resolveConfig(section: AutoContinueSettings | undefined): AutoContinueConfig {\n const value = section ?? {};\n const text =\n typeof value.continueText === 'string' && value.continueText.trim() !== ''\n ? value.continueText\n : DEFAULT_CONFIG.continueText;\n return {\n continueText: text,\n graceMs: numberOr(value.graceMs, DEFAULT_CONFIG.graceMs),\n cooldownMs: numberOr(value.cooldownMs, DEFAULT_CONFIG.cooldownMs),\n maxConsecutive: Math.max(1, numberOr(value.maxConsecutive, DEFAULT_CONFIG.maxConsecutive)),\n scanOnBoot: booleanOr(value.scanOnBoot, DEFAULT_CONFIG.scanOnBoot),\n scanLimit: Math.max(1, numberOr(value.scanLimit, DEFAULT_CONFIG.scanLimit)),\n freshMs: numberOr(value.freshMs, DEFAULT_CONFIG.freshMs),\n reconnectScanDelayMs: numberOr(value.reconnectScanDelayMs, DEFAULT_CONFIG.reconnectScanDelayMs),\n reconnectBackoffMs: numberOr(value.reconnectBackoffMs, DEFAULT_CONFIG.reconnectBackoffMs),\n verbose: booleanOr(value.verbose, DEFAULT_CONFIG.verbose),\n classify: booleanOr(value.classify, DEFAULT_CONFIG.classify),\n backoffFactor: Math.max(1, numberOr(value.backoffFactor, DEFAULT_CONFIG.backoffFactor)),\n backoffMaxMs: numberOr(value.backoffMaxMs, DEFAULT_CONFIG.backoffMaxMs),\n notify: booleanOr(value.notify, DEFAULT_CONFIG.notify),\n };\n}\n\n/** 视为「非人为中断」的回合结束原因。aborted(用户停止)与 blocked(策略拒绝)不在其中。 */\ntype NonHumanReason = 'error' | 'interrupted' | 'max-tokens';\n\nfunction isNonHumanReason(kind: string): kind is NonHumanReason {\n return kind === 'error' || kind === 'interrupted' || kind === 'max-tokens';\n}\n\n/** 一次回合失败的机器可读事实(turn/end error 的 LlmFailure 载荷)。 */\nexport interface FailureFacts {\n /** 稳定机器路由码(如 UPSTREAM、RATE_LIMIT_EXCEEDED、INVALID_API_KEY)。 */\n code: string;\n /** 人类可读的失败描述。 */\n message: string;\n /** 供应商 HTTP 状态码(可用时)。 */\n status?: number;\n}\n\n/**\n * 错误分类: 该失败是否值得自动继续。\n * 永久性失败(认证/余额/模型不存在/上下文超限等)重试也不会成功, 应跳过并通知用户;\n * 其余(网络、超时、5xx、429 等)视为临时性失败, 允许自动恢复。\n */\nexport function isTransientFailure(failure: FailureFacts): boolean {\n const haystack = `${failure.code} ${failure.message}`.toLowerCase();\n const status = failure.status;\n if (status !== undefined && (status === 401 || status === 403)) return false;\n const permanent =\n /auth|unauthor|forbidden|credential|api[_-]?key|permission/i.test(haystack) ||\n /insufficient.*(balance|quota)|billing|payment|quota.*exceeded.*(?!retry)/i.test(haystack) ||\n /model.*not[_-]?found|unknown[_-]?model|model[_-]?not[_-]?found|not.*support.*model/i.test(haystack) ||\n /context.*(length|limit|overflow|exceed)|token.*limit|max.*context/i.test(haystack) ||\n /invalid[_-]?request|bad[_-]?request/i.test(haystack);\n return !permanent;\n}\n\n/** 浏览器通知(不可用时静默跳过)。 */\nfunction notify(title: string, body: string): void {\n try {\n const N = (globalThis as { Notification?: unknown }).Notification as\n | (new (t: string, o: { body: string }) => unknown)\n | undefined;\n if (typeof N === 'undefined') return;\n const permission = (N as unknown as { permission?: string }).permission;\n if (permission === 'granted') {\n new N(title, { body });\n } else if (permission === 'default') {\n // 首次使用时请求一次权限, 用户拒绝后不再打扰。\n void (N as unknown as { requestPermission?: () => Promise<string> }).requestPermission?.()\n .then((result) => {\n if (result === 'granted') new N(title, { body });\n })\n .catch(() => {});\n }\n } catch {\n /* 通知失败不影响核心逻辑 */\n }\n}\n\n/** 用失败事实与回合信息填充 continueText 模板占位符({code}/{message}/{status}/{tool}/{turn})。 */\nexport function fillTemplate(\n template: string,\n facts: FailureFacts | undefined,\n tool: string | undefined,\n turn: number | undefined,\n): string {\n return template\n .replace(/\\{code\\}/g, facts?.code ?? '')\n .replace(/\\{message\\}/g, facts?.message ?? '')\n .replace(/\\{status\\}/g, facts?.status !== undefined ? String(facts.status) : '')\n .replace(/\\{tool\\}/g, tool ?? '')\n .replace(/\\{turn\\}/g, turn !== undefined ? String(turn) : '');\n}\n\n/** 自适应退避: 同一会话连续失败时的有效冷却间隔。 */\nexport function effectiveCooldown(\n consecutive: number,\n base: number,\n factor: number,\n max: number,\n): number {\n // consecutive = 已连续自动继续的次数; 第 1 次后开始按 factor 递增\n const multiplier = Math.pow(factor, consecutive);\n return Math.min(Math.max(base, base * multiplier), Math.max(base, max));\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** 浏览器当前 IANA 时区; 不可用时省略(宿主允许省略)。 */\nfunction clientTimeZone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\n/** 跨标签页互斥与冷却记录(仅浏览器本地, 不落盘到宿主)。 */\nconst lockPrefix = 'dsh-auto-continue:';\nconst lockKey = (sessionId: SessionId) => `${lockPrefix}lock:${sessionId}`;\nconst stampKey = (sessionId: SessionId) => `${lockPrefix}last:${sessionId}`;\n\n/** 尝试独占本次发送: 两个标签页同时触发时只有一个成功。 */\nfunction claimSend(sessionId: SessionId): boolean {\n try {\n const token = `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n localStorage.setItem(lockKey(sessionId), token);\n return localStorage.getItem(lockKey(sessionId)) === token;\n } catch {\n return true; // 存储不可用(隐私模式等)时放行\n }\n}\n\nfunction releaseSend(sessionId: SessionId): void {\n try {\n localStorage.removeItem(lockKey(sessionId));\n } catch {\n /* ignore */\n }\n}\n\n/** 读/写「上次自动发送」时间戳(跨标签页冷却)。 */\nfunction readLastSend(sessionId: SessionId): number {\n try {\n return Number(localStorage.getItem(stampKey(sessionId)) ?? 0) || 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeLastSend(sessionId: SessionId, at: number): void {\n try {\n localStorage.setItem(stampKey(sessionId), String(at));\n } catch {\n /* ignore */\n }\n}\n\n/** 每会话运行时状态。 */\ninterface SessionState {\n /** 连续自动「继续」次数; 成功回合或用户手动介入后归零。 */\n consecutive: number;\n /** 上次自动「继续」时间戳。 */\n lastAutoAt: number;\n /** 上次自动「继续」尝试(成功或失败)时间戳; 防止失败场景下的快速重试循环。 */\n lastAttemptAt: number;\n /** 我们上次自动发送的文本(用于识别自己的回显)。 */\n lastSentText: string;\n /** 宽限期定时器(进行中的待发送)。 */\n pendingTimer: number | undefined;\n /** 宿主权威 running 位(来自 host/session-status 与回合事件)。 */\n running: boolean | undefined;\n /** 当前排队消息数(来自 session/queue 帧)。 */\n queued: number;\n /** 子代理会话(host/session-added 带 parentSessionId)。 */\n subagent: boolean;\n /** 最近一次回合失败的事实(用于分类与模板填充)。 */\n lastFailure: FailureFacts | undefined;\n /** 失败前最后一次工具调用的名称(模板 {tool})。 */\n lastTool: string | undefined;\n /** 失败回合的编号(模板 {turn})。 */\n lastTurn: number | undefined;\n}\n\nconst freshState = (): SessionState => ({\n consecutive: 0,\n lastAutoAt: 0,\n lastAttemptAt: 0,\n lastSentText: '',\n pendingTimer: undefined,\n running: undefined,\n queued: 0,\n subagent: false,\n lastFailure: undefined,\n lastTool: undefined,\n lastTurn: undefined,\n});\n\n/** 判定一条 user/message 是否是我们自己自动发送的回显。 */\nfunction isOurEcho(state: SessionState, event: SessionEvent): boolean {\n if (event.type !== 'user/message') return false;\n const message = event.data;\n if (message.source.kind !== 'user') return false;\n if (state.lastSentText === '') return false;\n if (Date.now() - state.lastAutoAt > 30000) return false;\n const text = message.content\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('');\n return text === state.lastSentText;\n}\n\n/** SSE 帧外壳: `{ rpcId, payload }`。 */\ntype FrameEnvelope<T> = { payload: T };\n\n/**\n * 事件流泵: 带指数退避的 SSE 重连循环。\n * - 从未收到任何帧(宿主未就绪): 退避重试, 不触发扫描\n * - 曾连上后断开: 重连, 并通过 onReconnect 通知外层(宿主可能崩溃重启过)\n */\nasync function pumpStream<T>(\n open: (signal: AbortSignal) => AsyncIterable<FrameEnvelope<T>>,\n onFrame: (payload: T) => void,\n onReconnect: () => void,\n getBackoff: () => number,\n log: (message: string) => void,\n signal: AbortSignal,\n): Promise<void> {\n let backoff = getBackoff();\n while (!signal.aborted) {\n let connected = false;\n try {\n for await (const envelope of open(signal)) {\n connected = true;\n onFrame(envelope.payload);\n }\n if (signal.aborted) return;\n } catch (error) {\n if (signal.aborted) return;\n log(`stream error: ${error instanceof Error ? error.message : String(error)}`);\n }\n if (!connected) {\n // 从未连上(宿主未就绪): 指数退避重试\n await sleep(backoff);\n backoff = Math.min(backoff * 2, 15000);\n continue;\n }\n // 曾连上后断开 → 重连并触发外层扫描\n backoff = getBackoff();\n onReconnect();\n await sleep(backoff);\n }\n}\n\n/** 插件主体: 一条 mux 流 + 一条 host 流 + 启动/重连扫描。 */\nexport class AutoContinueRunner {\n private readonly states = new Map<SessionId, SessionState>();\n private readonly muxAbort = new AbortController();\n private readonly hostAbort = new AbortController();\n private disposed = false;\n private reconnectScans = 0;\n\n /**\n * @param api - shared wire client (ctx.connection.api).\n * @param getConfig - read the current resolved configuration (settings scope).\n */\n constructor(\n private readonly api: IApiClient,\n private readonly getConfig: () => AutoContinueConfig,\n ) {\n const config = this.getConfig();\n void this.runMux();\n void this.runHost();\n if (config.scanOnBoot) {\n // 启动时连接可能尚未建立, 循环重试直到成功。\n void this.bootScanLoop();\n }\n this.log(\n `已启动(文本=\"${config.continueText}\", 宽限 ${config.graceMs}ms, ` +\n `冷却 ${config.cooldownMs}ms, 最多连续 ${config.maxConsecutive} 次)`,\n );\n }\n\n private log(message: string): void {\n if (this.getConfig().verbose) console.info(`[auto-continue] ${message}`);\n }\n\n dispose(): void {\n this.disposed = true;\n this.muxAbort.abort();\n this.hostAbort.abort();\n for (const state of this.states.values()) {\n if (state.pendingTimer !== undefined) clearTimeout(state.pendingTimer);\n }\n this.states.clear();\n }\n\n private state(sessionId: SessionId): SessionState {\n let state = this.states.get(sessionId);\n if (state === undefined) {\n state = freshState();\n this.states.set(sessionId, state);\n }\n return state;\n }\n\n private runMux(): Promise<void> {\n return pumpStream<MuxFrame>(\n (signal) => this.api.events.mux({}, signal),\n (payload) => this.onMuxFrame(payload),\n () => this.scheduleReconnectScan(),\n () => this.getConfig().reconnectBackoffMs,\n (m) => this.log(m),\n this.muxAbort.signal,\n );\n }\n\n private runHost(): Promise<void> {\n return pumpStream<HostFrame>(\n (signal) => this.api.events.host({}, signal),\n (payload) => this.onHostFrame(payload),\n () => this.scheduleReconnectScan(),\n () => this.getConfig().reconnectBackoffMs,\n (m) => this.log(m),\n this.hostAbort.signal,\n );\n }\n\n // ---------- mux 帧 ----------\n\n private onMuxFrame(frame: MuxFrame): void {\n switch (frame.type) {\n case 'session/event':\n if (frame.event.type === 'tool/call') {\n const name = frame.event.data.name;\n if (typeof name === 'string') this.state(frame.sessionId).lastTool = name;\n }\n this.onSessionEvent(frame.sessionId, frame.event);\n break;\n case 'session/queue':\n this.state(frame.sessionId).queued = frame.items.length;\n if (frame.items.length > 0) this.cancelPending(frame.sessionId, '出现排队消息');\n break;\n case 'stream/error':\n this.log(`mux stream/error: ${frame.error.code} ${frame.error.message}`);\n break;\n default:\n break; // session/subscribed、approval/*、question/*、session/jobs、session/projection 与本插件无关\n }\n }\n\n private onSessionEvent(sessionId: SessionId, event: SessionEvent): void {\n const state = this.state(sessionId);\n switch (event.type) {\n case 'turn/start':\n state.running = true;\n this.cancelPending(sessionId, '宿主自行开启新回合');\n break;\n case 'turn/end': {\n state.running = false;\n this.cancelPending(sessionId, '收到新的 turn/end');\n const reason = event.data.reason;\n if (reason.kind === 'completed') {\n // 成功回合: 恢复健康状态\n state.consecutive = 0;\n state.lastFailure = undefined;\n } else if (reason.kind === 'aborted') {\n // 用户主动停止: 不自动继续, 视为用户介入\n state.consecutive = 0;\n } else if (reason.kind === 'blocked') {\n // 策略拒绝: 不自动继续\n } else if (reason.kind === 'error') {\n // 记录失败事实(分类与模板填充用), 然后按类型处理\n const error = reason.error;\n state.lastFailure = {\n code: typeof error.code === 'string' ? error.code : 'UNKNOWN',\n message: typeof error.message === 'string' ? error.message : String(error),\n ...(typeof error.status === 'number' ? { status: error.status } : {}),\n };\n state.lastTurn = event.data.turn;\n this.onTurnFailure(sessionId, 'turn/end:error', state.lastFailure);\n } else if (isNonHumanReason(reason.kind)) {\n this.schedule(sessionId, `turn/end:${reason.kind}`);\n }\n break;\n }\n case 'user/message':\n if (isOurEcho(state, event)) break; // 我们自己的回显\n if (event.data.source.kind === 'user') {\n // 用户手动介入\n state.consecutive = 0;\n this.cancelPending(sessionId, '用户手动发送消息');\n }\n break;\n default:\n break;\n }\n }\n\n // ---------- host 帧 ----------\n\n private onHostFrame(frame: HostFrame): void {\n switch (frame.type) {\n case 'host/session-status':\n this.state(frame.sessionId).running = frame.running;\n if (frame.running) this.cancelPending(frame.sessionId, '宿主报告会话开始运行');\n break;\n case 'host/session-added':\n this.state(frame.sessionId).subagent = frame.parentSessionId !== undefined;\n break;\n case 'host/agent-error':\n if (this.state(frame.sessionId).subagent) break;\n this.log(`host/agent-error(${frame.sessionId}): ${frame.message}`);\n this.schedule(frame.sessionId, 'host/agent-error');\n break;\n case 'host/session-removed':\n this.cancelPending(frame.sessionId, '会话已移除');\n this.states.delete(frame.sessionId);\n break;\n default:\n break;\n }\n }\n\n // ---------- 调度 ----------\n\n /** 回合失败入口: 先做错误分类, 永久性失败跳过并通知, 临时性失败走正常调度。 */\n private onTurnFailure(sessionId: SessionId, reason: string, failure: FailureFacts): void {\n const config = this.getConfig();\n if (config.classify && !isTransientFailure(failure)) {\n const summary = `${failure.code}${failure.status !== undefined ? ` (HTTP ${failure.status})` : ''}`;\n this.log(`跳过 ${sessionId}(${reason}): 永久性失败 ${summary} — ${failure.message}`);\n if (config.notify) {\n notify('dsh-auto-continue: 未自动继续', `${sessionId}: 永久性错误 ${summary},需要人工处理`);\n }\n return;\n }\n this.schedule(sessionId, reason);\n }\n\n /** 本会话当前生效的冷却间隔(自适应退避)。 */\n private cooldownFor(state: SessionState): number {\n const config = this.getConfig();\n return effectiveCooldown(\n state.consecutive,\n config.cooldownMs,\n config.backoffFactor,\n config.backoffMaxMs,\n );\n }\n\n private schedule(sessionId: SessionId, reason: string): void {\n const state = this.state(sessionId);\n const config = this.getConfig();\n if (state.subagent) return; // 子代理会话由父代理处理, 不抢跑\n if (state.pendingTimer !== undefined) return; // 已有待发送\n if (Date.now() - state.lastAttemptAt < this.cooldownFor(state)) return; // 冷却期(含失败尝试, 自适应退避)\n if (state.consecutive >= config.maxConsecutive) {\n this.log(\n `跳过 ${sessionId}(${reason}): 已连续自动继续 ${state.consecutive} 次, 等待用户介入或成功回合`,\n );\n return;\n }\n if (state.queued > 0) return; // 已有排队消息, 宿主会自行唤醒\n const timer = setTimeout(() => {\n if (state.pendingTimer !== timer) return;\n state.pendingTimer = undefined;\n void this.fire(sessionId, reason);\n }, config.graceMs);\n state.pendingTimer = timer;\n this.log(\n `检测到非人为中断 ${sessionId}(${reason}), ${config.graceMs}ms 后自动发送「${config.continueText}」`,\n );\n }\n\n private cancelPending(sessionId: SessionId, why: string): void {\n const state = this.state(sessionId);\n if (state.pendingTimer === undefined) return;\n clearTimeout(state.pendingTimer);\n state.pendingTimer = undefined;\n this.log(`取消 ${sessionId} 的自动继续(${why})`);\n }\n\n private async fire(sessionId: SessionId, reason: string): Promise<void> {\n if (this.disposed) return;\n const state = this.state(sessionId);\n const config = this.getConfig();\n // 权威 running 检查: 优先用 host 帧, 未知时回退到 session.list\n if (state.running === undefined) {\n const running = await this.runningViaList(sessionId);\n if (running === undefined || running) {\n this.log(`跳过 ${sessionId}: 无法确认空闲(${running === undefined ? '未知' : '运行中'})`);\n return;\n }\n } else if (state.running) {\n this.log(`跳过 ${sessionId}: 会话仍在运行`);\n return;\n }\n if (state.queued > 0) {\n this.log(`跳过 ${sessionId}: 已有排队消息`);\n return;\n }\n // 跨标签页冷却(自适应退避)\n if (Date.now() - readLastSend(sessionId) < this.cooldownFor(state)) {\n this.log(`跳过 ${sessionId}: 其他标签页刚发送过`);\n return;\n }\n if (!claimSend(sessionId)) {\n this.log(`跳过 ${sessionId}: 其他标签页正在发送`);\n return;\n }\n // 模板填充: continueText 可含 {code}/{message}/{status}/{tool}/{turn} 占位符\n const text = fillTemplate(config.continueText, state.lastFailure, state.lastTool, state.lastTurn);\n const zone = clientTimeZone();\n state.lastAttemptAt = Date.now(); // 先记账: 无论成败, 本次尝试都进入冷却\n try {\n const response = await this.api.sessions.prompt({\n sessionId,\n mode: 'queue',\n content: [{ type: 'text', text }],\n ...(zone === undefined ? {} : { clientTimeZone: zone }),\n });\n if (response.result.ok) {\n const now = Date.now();\n state.consecutive += 1;\n state.lastAutoAt = now;\n state.lastSentText = text;\n writeLastSend(sessionId, now);\n this.log(`已自动发送「${text}」到 ${sessionId}(${reason}), 第 ${state.consecutive} 次连续`);\n if (config.notify) {\n notify('dsh-auto-continue: 已自动继续', `${sessionId}: 已发送「${text}」(第 ${state.consecutive} 次连续)`);\n }\n if (state.consecutive >= config.maxConsecutive) {\n this.log(`达到连续上限 ${config.maxConsecutive} 次, 停止自动继续 ${sessionId}`);\n if (config.notify) {\n notify('dsh-auto-continue: 已停止自动继续', `${sessionId}: 连续失败 ${state.consecutive} 次, 需要人工介入`);\n }\n }\n } else {\n this.log(\n `发送失败 ${sessionId}: ${response.result.error.code} ${response.result.error.message}`,\n );\n }\n } catch (error) {\n this.log(`发送异常 ${sessionId}: ${error instanceof Error ? error.message : String(error)}`);\n } finally {\n releaseSend(sessionId);\n }\n }\n\n private async runningViaList(sessionId: SessionId): Promise<boolean | undefined> {\n try {\n const response = await this.api.sessions.list({});\n if (!response.result.ok) return undefined;\n const item = response.result.value.items.find(\n (summary: SessionSummary) => summary.sessionId === sessionId,\n );\n return item === undefined ? undefined : item.running;\n } catch {\n return undefined;\n }\n }\n\n // ---------- 启动/重连扫描 ----------\n\n private scheduleReconnectScan(): void {\n this.reconnectScans += 1;\n const scan = this.reconnectScans;\n setTimeout(() => {\n if (scan !== this.reconnectScans || this.disposed) return;\n void this.scanLoop(6, this.getConfig().reconnectScanDelayMs);\n }, this.getConfig().reconnectScanDelayMs);\n }\n\n private async bootScanLoop(): Promise<void> {\n await this.scanLoop(Infinity, 3000);\n }\n\n /** 反复尝试扫描, 直到成功(宿主就绪)或达到次数上限。 */\n private async scanLoop(attempts: number, delayMs: number): Promise<void> {\n for (let attempt = 0; attempt < attempts && !this.disposed; attempt += 1) {\n try {\n if (await this.scanInterrupted()) return;\n } catch (error) {\n if (this.disposed) return;\n // 宿主未就绪时每 3s 重试; 只节流记录日志, 避免刷屏。\n if (attempt % 10 === 0) {\n this.log(\n `扫描失败(${attempt + 1}/${attempts === Infinity ? '∞' : attempts}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n if (attempt + 1 < attempts) await sleep(delayMs);\n }\n }\n\n /**\n * 扫描最近中断过的会话: 最后回合以非人为原因结束, 且其后没有新回合或用户消息。\n * @returns 是否成功完成一次扫描(宿主就绪)。\n */\n private async scanInterrupted(): Promise<boolean> {\n const config = this.getConfig();\n const response = await this.api.sessions.list({});\n if (!response.result.ok) return false;\n const items = response.result.value.items;\n const candidates = items\n .filter((summary) => !summary.running && summary.parentSessionId === undefined)\n .slice(0, config.scanLimit);\n const now = Date.now();\n for (const summary of candidates) {\n if (this.disposed) return true;\n const state = this.state(summary.sessionId);\n if (state.pendingTimer !== undefined) continue;\n if (state.consecutive >= config.maxConsecutive) continue;\n if (now - state.lastAttemptAt < this.cooldownFor(state)) continue;\n let events;\n try {\n const page = await this.api.sessions.history({\n sessionId: summary.sessionId,\n maxMessages: 30,\n });\n if (!page.result.ok) continue;\n events = page.result.value.events;\n } catch {\n continue; // 会话可能刚被移除\n }\n // 从尾部找最后一个 turn/end(在分支内完成收窄)\n let lastEnd: SessionEvent<'turn/end'> | undefined;\n for (let i = events.length - 1; i >= 0; i -= 1) {\n const event = events[i]?.event;\n if (event !== undefined && event.type === 'turn/end') {\n lastEnd = event;\n break;\n }\n }\n if (lastEnd === undefined) continue;\n const reason = lastEnd.data.reason;\n if (!isNonHumanReason(reason.kind)) continue;\n if (lastEnd.time < now - config.freshMs) continue; // 太久远, 不翻旧账\n // 该 turn/end 之后不能有新回合或用户消息(说明已被处理)\n let superseded = false;\n for (const entry of events) {\n const event = entry.event;\n if (event.seq <= lastEnd.seq) continue;\n if (event.type === 'turn/start') superseded = true;\n if (event.type === 'user/message' && event.data.source.kind === 'user') superseded = true;\n if (superseded) break;\n }\n if (superseded) continue;\n this.log(`扫描发现中断 ${summary.sessionId}(turn/end:${reason.kind}), 安排自动继续`);\n this.schedule(summary.sessionId, `scan:turn/end:${reason.kind}`);\n }\n return true;\n }\n}\n", "/**\n * `auto-continue` namespace dictionaries: copy for the plugin settings card\n * registered into the `settings.plugin.item` seat of the plugin-configuration\n * section. Includes the card-chrome keys the card component reads.\n */\n\n/** 简体中文词典(键集的事实来源)。 */\nexport const zh = {\n 'card.title': '自动继续',\n 'card.description': '请求因网络等原因(非人为)中断后, 自动发送「继续」续跑。',\n 'field.continueText': '继续文本',\n 'field.continueTextHint': '中断后自动发送的消息内容。',\n 'field.graceMs': '宽限期 (ms)',\n 'field.graceMsHint': '检测到中断后等待的时长; 期间宿主自行恢复则取消。',\n 'field.cooldownMs': '冷却时间 (ms)',\n 'field.cooldownMsHint': '同一会话两次自动「继续」的最小间隔, 失败尝试也计入。',\n 'field.maxConsecutive': '最大连续次数',\n 'field.maxConsecutiveHint': '同一会话连续自动「继续」的上限; 超过后停止, 直到用户手动介入或出现成功回合。',\n 'field.scanOnBoot': '启动/重连扫描',\n 'field.scanOnBootHint': '页面启动或重连时扫描最近中断的会话并自动续跑(如浏览器关闭期间宿主崩溃)。',\n 'field.scanLimit': '扫描会话数',\n 'field.scanLimitHint': '最多检查多少个最近更新的会话(不含运行中与子代理会话)。',\n 'field.freshMs': '扫描时间窗 (ms)',\n 'field.freshMsHint': '扫描只处理该时间窗内的中断。',\n 'field.reconnectScanDelayMs': '重连扫描延迟 (ms)',\n 'field.reconnectScanDelayMsHint': '重连后等待宿主完成恢复再扫描。',\n 'field.reconnectBackoffMs': '重连退避 (ms)',\n 'field.reconnectBackoffMsHint': '事件流断开后的重连间隔。',\n 'field.verbose': '详细日志',\n 'field.verboseHint': '在浏览器控制台输出 [auto-continue] 日志。',\n 'field.classify': '错误分类',\n 'field.classifyHint': '仅自动恢复临时性错误(网络/超时/5xx 等); 认证/余额/模型不存在等永久性错误跳过并通知。',\n 'field.backoffFactor': '退避系数',\n 'field.backoffFactorHint': '连续失败时冷却间隔的倍率(如 2 表示 20s→40s→80s 递增)。',\n 'field.backoffMaxMs': '最大退避间隔 (ms)',\n 'field.backoffMaxMsHint': '自适应退避的上限, 防止等待过久。',\n 'field.notify': '浏览器通知',\n 'field.notifyHint': '自动继续成功/放弃/遇到永久性错误时弹出浏览器通知。',\n 'chrome.collapse': '收起设置',\n 'chrome.expand': '展开设置',\n 'chrome.unsaved': '未保存',\n 'chrome.readOnly': '当前部署的设置只读。',\n 'chrome.saveFailed': '部署未接受这些值, 已保留供你修改。',\n 'chrome.discard': '放弃',\n 'chrome.saving': '保存中…',\n 'chrome.save': '保存',\n 'chrome.overridden': '已覆盖',\n 'chrome.reset': '恢复默认',\n 'chrome.invalidNumber': '请输入数字, 留空则使用默认值。',\n 'chrome.inherit': '继承',\n 'chrome.on': '开',\n 'chrome.off': '关',\n} satisfies Record<string, string>;\n\n/** 本插件的键联合。 */\nexport type SettingsCardKey = keyof typeof zh;\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: Record<SettingsCardKey, string> = {\n 'card.title': 'Auto continue',\n 'card.description': 'When a request is interrupted by a non-human cause, automatically send 「继续」 to resume.',\n 'field.continueText': 'Continue text',\n 'field.continueTextHint': 'Message automatically sent after an interruption.',\n 'field.graceMs': 'Grace period (ms)',\n 'field.graceMsHint': 'Wait after an interruption; cancelled if the host recovers on its own.',\n 'field.cooldownMs': 'Cooldown (ms)',\n 'field.cooldownMsHint': 'Minimum interval between auto-continues per session; failed attempts count too.',\n 'field.maxConsecutive': 'Max consecutive',\n 'field.maxConsecutiveHint': 'Max consecutive auto-continues per session; stops until a user intervenes or a turn completes.',\n 'field.scanOnBoot': 'Scan on load / reconnect',\n 'field.scanOnBootHint': 'Scan recently interrupted sessions on page load or reconnect (e.g. the host crashed while the browser was closed).',\n 'field.scanLimit': 'Scan limit',\n 'field.scanLimitHint': 'How many most-recently-updated sessions to check (running / subagent sessions excluded).',\n 'field.freshMs': 'Scan window (ms)',\n 'field.freshMsHint': 'Only interruptions inside this window are considered.',\n 'field.reconnectScanDelayMs': 'Reconnect scan delay (ms)',\n 'field.reconnectScanDelayMsHint': 'Wait for the host to finish recovering before scanning after a reconnect.',\n 'field.reconnectBackoffMs': 'Reconnect backoff (ms)',\n 'field.reconnectBackoffMsHint': 'Interval between event-stream reconnect attempts.',\n 'field.verbose': 'Verbose logs',\n 'field.verboseHint': 'Log [auto-continue] lines to the browser console.',\n 'field.classify': 'Classify errors',\n 'field.classifyHint': 'Auto-resume transient failures only (network/timeout/5xx…); auth, balance and model errors are skipped and notified.',\n 'field.backoffFactor': 'Backoff factor',\n 'field.backoffFactorHint': 'Cooldown multiplier per consecutive failure (2 = 20s→40s→80s…).',\n 'field.backoffMaxMs': 'Max backoff (ms)',\n 'field.backoffMaxMsHint': 'Cap on the adaptive backoff interval.',\n 'field.notify': 'Browser notifications',\n 'field.notifyHint': 'Notify when auto-continue fires, gives up, or hits a permanent error.',\n 'chrome.collapse': 'Hide settings',\n 'chrome.expand': 'Show settings',\n 'chrome.unsaved': 'Unsaved',\n 'chrome.readOnly': 'This deployment stores settings read-only.',\n 'chrome.saveFailed': 'The deployment did not accept these values; they were left for you to correct.',\n 'chrome.discard': 'Discard',\n 'chrome.saving': 'Saving…',\n 'chrome.save': 'Save',\n 'chrome.overridden': 'Overridden',\n 'chrome.reset': 'Reset to default',\n 'chrome.invalidNumber': 'Enter a number, or leave blank to use the default.',\n 'chrome.inherit': 'Inherit',\n 'chrome.on': 'On',\n 'chrome.off': 'Off',\n};\n", "/**\n * The auto-continue settings card: edits the `auto-continue` namespace fields\n * from the plugin-configuration section (the `settings.plugin.item` seat).\n *\n * Self-contained card chrome (disclosure header, staged fields, save/discard\n * footer) following the plugin-card store pattern of the DSH plugin\n * configuration section; styles live in `styles.ts` and use the DSH design\n * tokens so the card follows the active theme.\n */\nimport { useState, type ReactNode } from 'react';\nimport { createSnapshotStore, type SettingsScope, type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';\nimport type { AutoContinueSettings } from './engine.ts';\nimport type { SettingsCardKey } from './locales.ts';\nimport {\n booleanField,\n CardForm,\n numberField,\n textField,\n type CardActions,\n type CardFieldState,\n type CardShell,\n} from './settings-form.ts';\nimport { injectStyles } from './styles.ts';\n\n// Styles must land during factory materialization so the module system's\n// style bookkeeping (HMR) owns them.\ninjectStyles();\n\n/** What the auto-continue card renders. */\nexport interface AutoContinueSettingsCardState extends CardShell {\n continueText: CardFieldState;\n graceMs: CardFieldState;\n cooldownMs: CardFieldState;\n maxConsecutive: CardFieldState;\n scanOnBoot: CardFieldState;\n scanLimit: CardFieldState;\n freshMs: CardFieldState;\n reconnectScanDelayMs: CardFieldState;\n reconnectBackoffMs: CardFieldState;\n verbose: CardFieldState;\n classify: CardFieldState;\n backoffFactor: CardFieldState;\n backoffMaxMs: CardFieldState;\n notify: CardFieldState;\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface AutoContinueSettingsCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as useAutoContinueSettingsCard. */\n autoContinueSettingsCard: SnapshotStore<AutoContinueSettingsCardState>;\n };\n}\n\n/** Bridges the `auto-continue` scope onto the card's staged form. */\nexport class AutoContinueSettingsCardController {\n private readonly form: CardForm<AutoContinueSettings>;\n private readonly store: SnapshotStore<AutoContinueSettingsCardState>;\n\n /**\n * @param scope - the bound settings scope for the `auto-continue` namespace.\n */\n constructor(scope: SettingsScope<AutoContinueSettings>) {\n this.form = new CardForm(scope, [\n textField('continueText'),\n numberField('graceMs', 0),\n numberField('cooldownMs', 0),\n numberField('maxConsecutive', 1),\n booleanField('scanOnBoot'),\n numberField('scanLimit', 1),\n numberField('freshMs', 0),\n numberField('reconnectScanDelayMs', 0),\n numberField('reconnectBackoffMs', 0),\n booleanField('verbose'),\n booleanField('classify'),\n numberField('backoffFactor', 1),\n numberField('backoffMaxMs', 0),\n booleanField('notify'),\n ]);\n this.store = this.form.bind(() => this.projection(), createSnapshotStore);\n }\n\n private projection(): AutoContinueSettingsCardState {\n return {\n ...this.form.shell(),\n continueText: this.form.field('continueText'),\n graceMs: this.form.field('graceMs'),\n cooldownMs: this.form.field('cooldownMs'),\n maxConsecutive: this.form.field('maxConsecutive'),\n scanOnBoot: this.form.field('scanOnBoot'),\n scanLimit: this.form.field('scanLimit'),\n freshMs: this.form.field('freshMs'),\n reconnectScanDelayMs: this.form.field('reconnectScanDelayMs'),\n reconnectBackoffMs: this.form.field('reconnectBackoffMs'),\n verbose: this.form.field('verbose'),\n classify: this.form.field('classify'),\n backoffFactor: this.form.field('backoffFactor'),\n backoffMaxMs: this.form.field('backoffMaxMs'),\n notify: this.form.field('notify'),\n };\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and its form actions.\n */\n inject(): AutoContinueSettingsCardFace {\n return { hooks: { autoContinueSettingsCard: this.store }, ...this.form.actions() };\n }\n}\n\n/** Props the renderer binds for the auto-continue card. */\nexport type AutoContinueSettingsCardProps =\n PropsRuntime<'settings.plugin.item'> & PropsLocale<'auto-continue'> & InjectFace<AutoContinueSettingsCardFace>;\n\n/** Card chrome: a disclosure header naming the plugin and what its settings govern, the controls, and the save that writes them. */\nfunction SettingsCard(props: {\n t: (key: SettingsCardKey) => string;\n titleKey: SettingsCardKey;\n descriptionKey: SettingsCardKey;\n state: CardShell;\n onSave: () => void;\n onDiscard: () => void;\n children: ReactNode;\n}) {\n const [open, setOpen] = useState(false);\n const { state } = props;\n if (!state.available) return null;\n const title = props.t(props.titleKey);\n const blocked = !state.dirty || state.invalid || state.saving;\n return (\n <li className={open ? 'dshAcCard dshAcCardOpen' : 'dshAcCard'}>\n <button\n type=\"button\"\n className=\"dshAcHeader\"\n aria-expanded={open}\n aria-label={`${props.t(open ? 'chrome.collapse' : 'chrome.expand')}: ${title}`}\n title={props.t(props.descriptionKey)}\n onClick={() => setOpen(!open)}\n >\n <span className=\"dshAcHeadText\">\n <span className=\"dshAcName\">{title}</span>\n <span className=\"dshAcDescription\">{props.t(props.descriptionKey)}</span>\n </span>\n {state.dirty ? (\n <span className=\"dshAcPending\" title={props.t('chrome.unsaved')}>\n {props.t('chrome.unsaved')}\n </span>\n ) : null}\n <span className={open ? 'dshAcChevron dshAcChevronOpen' : 'dshAcChevron'}>▾</span>\n </button>\n {open ? (\n <div className=\"dshAcBody\">\n {!state.writable ? (\n <p className=\"dshAcReadOnly\" role=\"status\">{props.t('chrome.readOnly')}</p>\n ) : null}\n {props.children}\n <div className=\"dshAcFooter\">\n {state.failed ? (\n <p className=\"dshAcFailed\" role=\"status\">{props.t('chrome.saveFailed')}</p>\n ) : null}\n <button\n type=\"button\"\n className=\"dshAcDiscard\"\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('chrome.discard')}\n </button>\n <button type=\"button\" className=\"dshAcSave\" disabled={blocked} onClick={props.onSave}>\n {props.t(!state.saving ? 'chrome.save' : 'chrome.saving')}\n </button>\n </div>\n </div>\n ) : null}\n </li>\n );\n}\n\n/** Props every field control needs regardless of its value type. */\ninterface FieldProps {\n id: string;\n label: string;\n hint: string;\n text: string;\n overridden: boolean;\n invalid: boolean;\n disabled: boolean;\n t: (key: SettingsCardKey) => string;\n onEdit: (text: string) => void;\n onReset: () => void;\n}\n\n/** A staged value field; `numeric` only hints the keypad, which drafts a field accepts is decided by its spec. */\nfunction ValueField(props: FieldProps & { numeric?: boolean; placeholder?: string }) {\n return (\n <div className=\"dshAcField\">\n <div className=\"dshAcHead\">\n <label className=\"dshAcLabel\" htmlFor={props.id}>{props.label}</label>\n {props.overridden ? (\n <span className=\"dshAcBadges\">\n <span className=\"dshAcBadge\">{props.t('chrome.overridden')}</span>\n <button type=\"button\" className=\"dshAcReset\" disabled={props.disabled} onClick={props.onReset}>\n {props.t('chrome.reset')}\n </button>\n </span>\n ) : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? 'dshAcInput dshAcInputInvalid' : 'dshAcInput'}\n type=\"text\"\n inputMode={props.numeric === true ? 'numeric' : undefined}\n aria-invalid={props.invalid || undefined}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => props.onEdit(event.target.value)}\n />\n <p className={props.invalid ? 'dshAcInvalid' : 'dshAcHint'}>\n {props.invalid ? props.t('chrome.invalidNumber') : props.hint}\n </p>\n </div>\n );\n}\n\n/** A staged boolean field: inherit / on / off. */\nfunction BooleanField(props: FieldProps) {\n return (\n <div className=\"dshAcField\">\n <div className=\"dshAcHead\">\n <label className=\"dshAcLabel\" htmlFor={props.id}>{props.label}</label>\n {props.overridden ? (\n <span className=\"dshAcBadges\">\n <span className=\"dshAcBadge\">{props.t('chrome.overridden')}</span>\n <button type=\"button\" className=\"dshAcReset\" disabled={props.disabled} onClick={props.onReset}>\n {props.t('chrome.reset')}\n </button>\n </span>\n ) : null}\n </div>\n <select\n id={props.id}\n className=\"dshAcSelect\"\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => props.onEdit(event.target.value)}\n >\n <option value=\"\">{props.t('chrome.inherit')}</option>\n <option value=\"true\">{props.t('chrome.on')}</option>\n <option value=\"false\">{props.t('chrome.off')}</option>\n </select>\n <p className=\"dshAcHint\">{props.hint}</p>\n </div>\n );\n}\n\n/**\n * Render the auto-continue card.\n * @param props - locale copy, the card snapshot, and its form actions.\n * @returns the card.\n */\nexport function AutoContinueSettingsCard(props: AutoContinueSettingsCardProps) {\n const { t } = props;\n const state = props.useAutoContinueSettingsCard((snapshot) => snapshot);\n const disabled = !state.writable;\n const shared = { t, disabled };\n return (\n <SettingsCard\n t={t}\n titleKey=\"card.title\"\n descriptionKey=\"card.description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n >\n <ValueField\n id=\"auto-continue-continue-text\"\n label={t('field.continueText')}\n hint={t('field.continueTextHint')}\n {...shared}\n {...state.continueText}\n onEdit={(text) => props.edit('continueText', text)}\n onReset={() => props.resetField('continueText')}\n />\n <ValueField\n id=\"auto-continue-grace-ms\"\n label={t('field.graceMs')}\n hint={t('field.graceMsHint')}\n numeric\n {...shared}\n {...state.graceMs}\n onEdit={(text) => props.edit('graceMs', text)}\n onReset={() => props.resetField('graceMs')}\n />\n <ValueField\n id=\"auto-continue-cooldown-ms\"\n label={t('field.cooldownMs')}\n hint={t('field.cooldownMsHint')}\n numeric\n {...shared}\n {...state.cooldownMs}\n onEdit={(text) => props.edit('cooldownMs', text)}\n onReset={() => props.resetField('cooldownMs')}\n />\n <ValueField\n id=\"auto-continue-max-consecutive\"\n label={t('field.maxConsecutive')}\n hint={t('field.maxConsecutiveHint')}\n numeric\n {...shared}\n {...state.maxConsecutive}\n onEdit={(text) => props.edit('maxConsecutive', text)}\n onReset={() => props.resetField('maxConsecutive')}\n />\n <BooleanField\n id=\"auto-continue-scan-on-boot\"\n label={t('field.scanOnBoot')}\n hint={t('field.scanOnBootHint')}\n {...shared}\n {...state.scanOnBoot}\n onEdit={(text) => props.edit('scanOnBoot', text)}\n onReset={() => props.resetField('scanOnBoot')}\n />\n <ValueField\n id=\"auto-continue-scan-limit\"\n label={t('field.scanLimit')}\n hint={t('field.scanLimitHint')}\n numeric\n {...shared}\n {...state.scanLimit}\n onEdit={(text) => props.edit('scanLimit', text)}\n onReset={() => props.resetField('scanLimit')}\n />\n <ValueField\n id=\"auto-continue-fresh-ms\"\n label={t('field.freshMs')}\n hint={t('field.freshMsHint')}\n numeric\n {...shared}\n {...state.freshMs}\n onEdit={(text) => props.edit('freshMs', text)}\n onReset={() => props.resetField('freshMs')}\n />\n <ValueField\n id=\"auto-continue-reconnect-scan-delay\"\n label={t('field.reconnectScanDelayMs')}\n hint={t('field.reconnectScanDelayMsHint')}\n numeric\n {...shared}\n {...state.reconnectScanDelayMs}\n onEdit={(text) => props.edit('reconnectScanDelayMs', text)}\n onReset={() => props.resetField('reconnectScanDelayMs')}\n />\n <ValueField\n id=\"auto-continue-reconnect-backoff\"\n label={t('field.reconnectBackoffMs')}\n hint={t('field.reconnectBackoffMsHint')}\n numeric\n {...shared}\n {...state.reconnectBackoffMs}\n onEdit={(text) => props.edit('reconnectBackoffMs', text)}\n onReset={() => props.resetField('reconnectBackoffMs')}\n />\n <BooleanField\n id=\"auto-continue-verbose\"\n label={t('field.verbose')}\n hint={t('field.verboseHint')}\n {...shared}\n {...state.verbose}\n onEdit={(text) => props.edit('verbose', text)}\n onReset={() => props.resetField('verbose')}\n />\n <BooleanField\n id=\"auto-continue-classify\"\n label={t('field.classify')}\n hint={t('field.classifyHint')}\n {...shared}\n {...state.classify}\n onEdit={(text) => props.edit('classify', text)}\n onReset={() => props.resetField('classify')}\n />\n <ValueField\n id=\"auto-continue-backoff-factor\"\n label={t('field.backoffFactor')}\n hint={t('field.backoffFactorHint')}\n numeric\n {...shared}\n {...state.backoffFactor}\n onEdit={(text) => props.edit('backoffFactor', text)}\n onReset={() => props.resetField('backoffFactor')}\n />\n <ValueField\n id=\"auto-continue-backoff-max\"\n label={t('field.backoffMaxMs')}\n hint={t('field.backoffMaxMsHint')}\n numeric\n {...shared}\n {...state.backoffMaxMs}\n onEdit={(text) => props.edit('backoffMaxMs', text)}\n onReset={() => props.resetField('backoffMaxMs')}\n />\n <BooleanField\n id=\"auto-continue-notify\"\n label={t('field.notify')}\n hint={t('field.notifyHint')}\n {...shared}\n {...state.notify}\n onEdit={(text) => props.edit('notify', text)}\n onReset={() => props.resetField('notify')}\n />\n </SettingsCard>\n );\n}\n", "/**\n * Staged form model behind the plugin settings card — a self-contained\n * implementation of the plugin-card store pattern used by the DSH plugin\n * configuration section.\n *\n * A card stages what the user types and writes it only when they save. Each\n * settings write is a durable, revision-fenced document mutation, so staging\n * keeps what is on screen exactly what a save would store. A field shows its\n * effective value — the user layer over the composition layer over the schema\n * default — and whether the user layer carries it (presence, not value\n * equality, marks an override).\n */\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite = { kind: 'set'; value: unknown } | { kind: 'clear' };\n\n/** How one field converts between its stored value and its draft text. */\nexport interface CardFieldSpec {\n /** Field name inside the namespace section. */\n field: string;\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string;\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined;\n}\n\n/** One field as the card's control renders it. */\nexport interface CardFieldState {\n /** Draft text the control renders. */\n text: string;\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean;\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean;\n}\n\n/** Form state every plugin card shares. */\nexport interface CardShell {\n /** False while the namespace is not served to this client; the card renders nothing. */\n available: boolean;\n /** Whether the Host document accepts writes. */\n writable: boolean;\n /** Whether the form holds edits that a save would write. */\n dirty: boolean;\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean;\n /** Whether a save is crossing the wire. */\n saving: boolean;\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean;\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void;\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void;\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void;\n /** Drop every staged edit. */\n discard: () => void;\n}\n\n/** A whole-number field. An empty draft clears the field; a non-number or out-of-range draft blocks the save. */\nexport function numberField(field: string, min = 0): CardFieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'number' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim();\n if (trimmed === '') return { kind: 'clear' };\n const parsed = Number(trimmed);\n if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < min) return undefined;\n return { kind: 'set', value: parsed };\n },\n };\n}\n\n/** A free-text field. An empty draft clears the field, so emptying the control and saving is the same gesture as resetting it. */\nexport function textField(field: string): CardFieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'string' ? value : ''),\n parse: (text) => {\n const trimmed = text.trim();\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed };\n },\n };\n}\n\n/** A boolean field, edited through true/false draft text; an empty draft inherits. */\nexport function booleanField(field: string): CardFieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'boolean' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim();\n if (trimmed === '') return { kind: 'clear' };\n if (trimmed === 'true') return { kind: 'set', value: true };\n if (trimmed === 'false') return { kind: 'set', value: false };\n return undefined;\n },\n };\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string;\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean;\n}\n\n/**\n * Stages one card's edits over one settings namespace and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted, so the\n * outcome is read back from the section rather than predicted here. A save\n * that did not land keeps its drafts, so the user can correct them instead of\n * retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, CardFieldSpec>;\n private readonly staged = new Map<string, StagedEdit>();\n private readonly listeners = new Set<() => void>();\n private saving = false;\n private failed = false;\n\n /**\n * @param scope - the bound settings scope for this card's namespace.\n * @param specs - the section fields this card edits.\n */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: CardFieldSpec[],\n ) {\n this.specs = new Map(specs.map((spec) => [spec.field, spec]));\n this.scope.subscribe(() => this.publish());\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S, createStore: (init: S) => SnapshotStore<S>): SnapshotStore<S> {\n const store = createStore(project());\n this.listeners.add(() => store.set(project()));\n return store;\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot();\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: this.plan().length > 0,\n invalid: this.plan().some((item) => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n };\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): CardFieldState {\n const spec = this.specOf(field);\n const staged = this.staged.get(field);\n if (staged === undefined) {\n return {\n text: spec.format(this.sectionValue(field)),\n overridden: this.stored(field),\n invalid: false,\n };\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text);\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n };\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => this.stage(field, { text, clear: false }),\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true });\n },\n save: () => void this.save(),\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return;\n this.staged.clear();\n this.failed = false;\n this.publish();\n },\n };\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan();\n const writes = plan.flatMap((item) => (item.run === undefined ? [] : [item.run]));\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return;\n // Snapshot the fields this save writes, so edits staged while it is in\n // flight survive: only the staged keys this save actually wrote are cleared.\n const fields = new Set(plan.map((item) => item.field));\n this.saving = true;\n this.failed = false;\n this.publish();\n let landed = true;\n for (const write of writes) {\n landed = (await write()) && landed;\n }\n if (landed) {\n for (const field of fields) this.staged.delete(field);\n }\n this.saving = false;\n this.failed = !landed;\n this.publish();\n }\n\n /**\n * Every staged edit a save would write. An entry whose draft is not a value\n * its field accepts carries no write: the form is still dirty, and the save\n * refuses rather than dropping the edit. A staged edit that matches the\n * effective section is not a write at all.\n */\n private plan(): { field: string; run: (() => Promise<boolean>) | undefined }[] {\n const plan: { field: string; run: (() => Promise<boolean>) | undefined }[] = [];\n for (const [field, staged] of this.staged) {\n const spec = this.specOf(field);\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, run: () => this.clear(field) });\n continue;\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue;\n const write = spec.parse(staged.text);\n if (write === undefined) plan.push({ field, run: undefined });\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) });\n else plan.push({ field, run: () => this.store(field, write.value) });\n }\n return plan;\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field);\n return !this.stored(field);\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value);\n return this.userLayer()?.[field] === value;\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit);\n this.failed = false;\n this.publish();\n }\n\n private specOf(field: string): CardFieldSpec {\n const spec = this.specs.get(field);\n if (spec === undefined) throw new Error(`settings card has no field ${field}`);\n return spec;\n }\n\n private sectionValue(field: string): unknown {\n return (this.scope.getSnapshot().value as Record<string, unknown> | undefined)?.[field];\n }\n\n private baseValue(field: string): unknown {\n return (this.scope.getSnapshot().base as Record<string, unknown> | undefined)?.[field];\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.scope.getSnapshot().user as Record<string, unknown> | undefined;\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer();\n return user !== undefined && Object.prototype.hasOwnProperty.call(user, field);\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener();\n }\n}\n", "/**\n * Styles for the auto-continue settings card, injected at factory\n * materialization so the client module system's style bookkeeping (HMR) owns\n * them. Uses the DSH design tokens (`--dsw-alias-*`) so the card follows the\n * active theme.\n */\n\nconst css = `\n.dshAcCard {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n.dshAcCard:hover { border-color: var(--dsw-alias-label-dimmed); }\n.dshAcCardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dshAcHeader {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n.dshAcHeader:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }\n.dshAcHeadText { flex-direction: column; flex: 1; gap: 4px; min-width: 0; display: flex; }\n.dshAcName { color: var(--dsw-alias-label-primary); font-size: 15px; font-weight: 600; line-height: 1.4; }\n.dshAcDescription { color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 1.5; }\n.dshAcChevron { color: var(--dsw-alias-label-tertiary); flex: none; transition: transform .16s; }\n.dshAcChevronOpen { transform: rotate(180deg); }\n.dshAcBody { border-top: 1px solid var(--dsw-alias-border-l2); margin: 0 16px; padding-bottom: 8px; }\n.dshAcReadOnly { color: var(--dsw-alias-label-tertiary); margin: 12px 0 0; font-size: 12px; line-height: 1.5; }\n.dshAcPending {\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n border-radius: 999px;\n flex: none;\n padding: 1px 8px;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n}\n.dshAcFooter {\n border-top: 1px solid var(--dsw-alias-border-l2);\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n padding: 12px 0 4px;\n display: flex;\n}\n.dshAcFailed { min-width: 0; color: var(--dsw-alias-label-error); flex: 1; margin: 0; font-size: 12px; line-height: 1.5; }\n.dshAcDiscard, .dshAcSave {\n appearance: none;\n font: inherit;\n cursor: pointer;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font-size: 13px;\n line-height: 1.5;\n}\n.dshAcDiscard { border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-secondary); background: none; }\n.dshAcDiscard:hover:not(:disabled) { color: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-label-dimmed); }\n.dshAcSave { background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-3); }\n.dshAcDiscard:disabled, .dshAcSave:disabled { opacity: .4; cursor: default; }\n.dshAcDiscard:focus-visible, .dshAcSave:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }\n.dshAcField { flex-direction: column; gap: 6px; padding: 12px 0; display: flex; }\n.dshAcField + .dshAcField { border-top: 1px solid var(--dsw-alias-border-l2); }\n.dshAcHead { align-items: center; gap: 8px; display: flex; }\n.dshAcLabel { min-width: 0; color: var(--dsw-alias-label-primary); flex: 1; font-size: 13px; font-weight: 500; line-height: 1.5; }\n.dshAcBadges { align-items: center; gap: 8px; display: inline-flex; }\n.dshAcBadge {\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n}\n.dshAcReset {\n font: inherit;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n font-size: 12px;\n line-height: 1.5;\n}\n.dshAcReset:hover:not(:disabled) { color: var(--dsw-alias-label-primary); }\n.dshAcReset:disabled { cursor: default; }\n.dshAcInput {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n height: 34px;\n font: inherit;\n color: var(--dsw-alias-label-primary);\n border-radius: 8px;\n padding: 0 12px;\n font-size: 13px;\n line-height: 1.5;\n}\n.dshAcInput:focus-visible { border-color: var(--dsw-alias-brand-primary); outline: none; }\n.dshAcInput:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n.dshAcInputInvalid { border-color: var(--dsw-alias-label-error); }\n.dshAcSelect {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n height: 34px;\n font: inherit;\n color: var(--dsw-alias-label-primary);\n border-radius: 8px;\n padding: 0 8px;\n font-size: 13px;\n line-height: 1.5;\n}\n.dshAcSelect:focus-visible { border-color: var(--dsw-alias-brand-primary); outline: none; }\n.dshAcSelect:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }\n.dshAcInvalid { color: var(--dsw-alias-label-error); margin: 0; font-size: 12px; line-height: 1.5; }\n.dshAcHint { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 1.5; }\n`;\n\n/** Inject the stylesheet once; a no-op outside a browser environment. */\nexport function injectStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.querySelector('style[data-plugin-css=\"auto-continue/card\"]') !== null) return;\n const tag = document.createElement('style');\n tag.dataset.plugin = 'dsh-client-auto-continue';\n tag.dataset.pluginCss = 'auto-continue/card';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/lib/index.js CHANGED
@@ -22,7 +22,15 @@ var AutoContinueSchema = z.object({
22
22
  /** SSE reconnect backoff (ms). */
23
23
  reconnectBackoffMs: z.natural().default(3e3),
24
24
  /** Log `[auto-continue]` lines to the browser console. */
25
- verbose: z.boolean().default(true)
25
+ verbose: z.boolean().default(true),
26
+ /** Classify failures: auto-continue transient errors only; permanent ones are skipped and notified. */
27
+ classify: z.boolean().default(true),
28
+ /** Cooldown multiplier per consecutive failure (adaptive backoff). */
29
+ backoffFactor: z.natural().min(1).default(2),
30
+ /** Cap on the effective backoff interval (ms). */
31
+ backoffMaxMs: z.natural().default(3e5),
32
+ /** Show browser notifications for auto-continue events. */
33
+ notify: z.boolean().default(false)
26
34
  });
27
35
  function apply(ctx) {
28
36
  ctx.inject(["settings"], (settingsCtx) => {
@@ -33,6 +33,14 @@ export interface AutoContinueSettings {
33
33
  reconnectBackoffMs?: number;
34
34
  /** Log `[auto-continue]` lines to the browser console. */
35
35
  verbose?: boolean;
36
+ /** Classify failures: auto-continue transient errors only; permanent ones (auth/balance/model) are skipped and notified. */
37
+ classify?: boolean;
38
+ /** Cooldown multiplier per consecutive failure (adaptive backoff). */
39
+ backoffFactor?: number;
40
+ /** Cap on the effective backoff interval (ms). */
41
+ backoffMaxMs?: number;
42
+ /** Show browser notifications for auto-continue events. */
43
+ notify?: boolean;
36
44
  }
37
45
  /** Fully resolved configuration (built-in defaults + user overrides). */
38
46
  export type AutoContinueConfig = Required<AutoContinueSettings>;
@@ -40,6 +48,25 @@ export type AutoContinueConfig = Required<AutoContinueSettings>;
40
48
  export declare const DEFAULT_CONFIG: AutoContinueConfig;
41
49
  /** Resolve a (possibly partial / not-yet-loaded) settings section to a full config. */
42
50
  export declare function resolveConfig(section: AutoContinueSettings | undefined): AutoContinueConfig;
51
+ /** 一次回合失败的机器可读事实(turn/end error 的 LlmFailure 载荷)。 */
52
+ export interface FailureFacts {
53
+ /** 稳定机器路由码(如 UPSTREAM、RATE_LIMIT_EXCEEDED、INVALID_API_KEY)。 */
54
+ code: string;
55
+ /** 人类可读的失败描述。 */
56
+ message: string;
57
+ /** 供应商 HTTP 状态码(可用时)。 */
58
+ status?: number;
59
+ }
60
+ /**
61
+ * 错误分类: 该失败是否值得自动继续。
62
+ * 永久性失败(认证/余额/模型不存在/上下文超限等)重试也不会成功, 应跳过并通知用户;
63
+ * 其余(网络、超时、5xx、429 等)视为临时性失败, 允许自动恢复。
64
+ */
65
+ export declare function isTransientFailure(failure: FailureFacts): boolean;
66
+ /** 用失败事实与回合信息填充 continueText 模板占位符({code}/{message}/{status}/{tool}/{turn})。 */
67
+ export declare function fillTemplate(template: string, facts: FailureFacts | undefined, tool: string | undefined, turn: number | undefined): string;
68
+ /** 自适应退避: 同一会话连续失败时的有效冷却间隔。 */
69
+ export declare function effectiveCooldown(consecutive: number, base: number, factor: number, max: number): number;
43
70
  /** 插件主体: 一条 mux 流 + 一条 host 流 + 启动/重连扫描。 */
44
71
  export declare class AutoContinueRunner {
45
72
  private readonly api;
@@ -62,6 +89,10 @@ export declare class AutoContinueRunner {
62
89
  private onMuxFrame;
63
90
  private onSessionEvent;
64
91
  private onHostFrame;
92
+ /** 回合失败入口: 先做错误分类, 永久性失败跳过并通知, 临时性失败走正常调度。 */
93
+ private onTurnFailure;
94
+ /** 本会话当前生效的冷却间隔(自适应退避)。 */
95
+ private cooldownFor;
65
96
  private schedule;
66
97
  private cancelPending;
67
98
  private fire;
@@ -27,6 +27,14 @@ export declare const zh: {
27
27
  'field.reconnectBackoffMsHint': string;
28
28
  'field.verbose': string;
29
29
  'field.verboseHint': string;
30
+ 'field.classify': string;
31
+ 'field.classifyHint': string;
32
+ 'field.backoffFactor': string;
33
+ 'field.backoffFactorHint': string;
34
+ 'field.backoffMaxMs': string;
35
+ 'field.backoffMaxMsHint': string;
36
+ 'field.notify': string;
37
+ 'field.notifyHint': string;
30
38
  'chrome.collapse': string;
31
39
  'chrome.expand': string;
32
40
  'chrome.unsaved': string;
@@ -14,6 +14,10 @@ export interface AutoContinueSettingsCardState extends CardShell {
14
14
  reconnectScanDelayMs: CardFieldState;
15
15
  reconnectBackoffMs: CardFieldState;
16
16
  verbose: CardFieldState;
17
+ classify: CardFieldState;
18
+ backoffFactor: CardFieldState;
19
+ backoffMaxMs: CardFieldState;
20
+ notify: CardFieldState;
17
21
  }
18
22
  /** The registration-side face the card's slot entry injects. */
19
23
  export interface AutoContinueSettingsCardFace extends CardActions {
@@ -29,6 +29,14 @@ export declare const AutoContinueSchema: z<Schemastery.ObjectS<{
29
29
  reconnectBackoffMs: z<number, number>;
30
30
  /** Log `[auto-continue]` lines to the browser console. */
31
31
  verbose: z<boolean, boolean>;
32
+ /** Classify failures: auto-continue transient errors only; permanent ones are skipped and notified. */
33
+ classify: z<boolean, boolean>;
34
+ /** Cooldown multiplier per consecutive failure (adaptive backoff). */
35
+ backoffFactor: z<number, number>;
36
+ /** Cap on the effective backoff interval (ms). */
37
+ backoffMaxMs: z<number, number>;
38
+ /** Show browser notifications for auto-continue events. */
39
+ notify: z<boolean, boolean>;
32
40
  }>, Schemastery.ObjectT<{
33
41
  /** Text automatically sent after an interruption. */
34
42
  continueText: z<string, string>;
@@ -50,6 +58,14 @@ export declare const AutoContinueSchema: z<Schemastery.ObjectS<{
50
58
  reconnectBackoffMs: z<number, number>;
51
59
  /** Log `[auto-continue]` lines to the browser console. */
52
60
  verbose: z<boolean, boolean>;
61
+ /** Classify failures: auto-continue transient errors only; permanent ones are skipped and notified. */
62
+ classify: z<boolean, boolean>;
63
+ /** Cooldown multiplier per consecutive failure (adaptive backoff). */
64
+ backoffFactor: z<number, number>;
65
+ /** Cap on the effective backoff interval (ms). */
66
+ backoffMaxMs: z<number, number>;
67
+ /** Show browser notifications for auto-continue events. */
68
+ notify: z<boolean, boolean>;
53
69
  }>>;
54
70
  /**
55
71
  * Plugin body: register the settings namespace when a settings provider is
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-client-auto-continue",
3
3
  "description": "DSH Web UI plugin: automatically sends \"继续\" (continue) when a request is interrupted by network errors or other non-human causes",
4
- "version": "0.2.2",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",