dsh-client-auto-continue 0.8.1 → 0.9.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 +15 -3
- package/README.zh.md +15 -3
- package/lib/client.js +50 -4
- package/lib/client.js.map +3 -3
- package/lib/index.js +9 -3
- package/lib/types/client/dsh-store-compat.d.ts +39 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/settings-card.d.ts +2 -1
- package/lib/types/client/settings-form.d.ts +1 -1
- package/lib/types/index.d.ts +4 -0
- package/lib/types/shared/core.d.ts +4 -1
- package/package.json +2 -4
- package/src/client/dsh-store-compat.ts +57 -0
- package/src/client/index.ts +1 -1
- package/src/client/locales.ts +4 -0
- package/src/client/settings-card.tsx +41 -13
- package/src/client/settings-form.ts +1 -1
- package/src/client/styles.ts +1 -0
- package/src/host/engine.ts +1 -1
- package/src/index.ts +2 -0
- package/src/shared/core.ts +16 -3
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ For [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh we
|
|
|
37
37
|
|
|
38
38
|
**Smart recovery** (all configurable):
|
|
39
39
|
|
|
40
|
-
- **Error classification** — transient failures (network / timeout / 5xx / 429…) are auto-resumed; permanent ones are **skipped** and notified, because retrying them never helps. A failure counts as permanent when its HTTP status is 401/403 or its code/message matches auth, credential/API-key, balance/quota, unknown-model, or context-length/overflow keywords.
|
|
40
|
+
- **Error classification** — transient failures (network / timeout / 5xx / 429…) are auto-resumed; permanent ones are **skipped** and notified, because retrying them never helps. A failure counts as permanent when its HTTP status is 401/403 or its code/message matches auth, credential/API-key, balance/quota, unknown-model, or context-length/overflow keywords. Provider-specific exceptions can be opted into with literal custom retryable patterns; turn classification off to resume everything
|
|
41
41
|
- **Adaptive backoff** — consecutive failures wait longer each time (cooldown × factor: 20s → 40s → 80s…), capped at the max backoff, instead of hammering a broken upstream
|
|
42
42
|
- **Templated continue text** — `continueText` supports `{code}` `{message}` `{status}` `{tool}` `{turn}` `{errorCount}` `{sessionTitle}` `{elapsed}` placeholders, so the resume message can carry the failure context ("继续 (git push failed: UPSTREAM)"); a **separate template** fires on `max-tokens` (e.g. "继续输出, 不要重复已生成的内容")
|
|
43
43
|
- **Idempotency guard** — before resuming, the plugin inspects the last tool call: if its result is unconfirmed (the turn died mid-tool, e.g. a `git push` that may have gone through), the resume message tells the model to check state first and not to rerun; if the tool is confirmed done, it says so and asks not to repeat it; a failed tool gets no guard (retrying it is the point). Both guard texts are configurable (`{tool}` / `{result}` placeholders)
|
|
@@ -72,7 +72,7 @@ The browser half is a thin shell: the settings card, plus a status bridge that s
|
|
|
72
72
|
|
|
73
73
|
DSH plugins install into a **profile** (`dsh web` → `web` profile). Install, restart `dsh web`, done.
|
|
74
74
|
|
|
75
|
-
> **
|
|
75
|
+
> **Use the latest DSH (recommended: 0.1.2-alpha.1 or newer).** Run `dsh --version` before installing. Plugin v0.8.2 supports the new 0.1.2 client-store module layout and retains a fallback for DSH 0.1.0-rc.7 through 0.1.1; rc.6 and earlier remain unsupported (`list slot ... requires options.id`). Preview releases may appear on the [official DSH releases page](https://github.com/deepseek-ai/deepseek-harness/releases) before the public npm tag catches up.
|
|
76
76
|
|
|
77
77
|
### From npm (recommended)
|
|
78
78
|
|
|
@@ -164,6 +164,7 @@ auto-continue:
|
|
|
164
164
|
freshMs: 900000
|
|
165
165
|
verbose: true
|
|
166
166
|
classify: true
|
|
167
|
+
retryableErrorPatterns: ''
|
|
167
168
|
backoffFactor: 2
|
|
168
169
|
backoffMaxMs: 300000
|
|
169
170
|
notify: false
|
|
@@ -210,10 +211,21 @@ auto-continue:
|
|
|
210
211
|
| Scan window (ms) | `900000` | Scan only considers interruptions inside this window |
|
|
211
212
|
| Verbose logs | `on` | `[auto-continue]` console logs |
|
|
212
213
|
| Classify errors | `on` | Auto-resume transient failures only; auth / balance / model errors are skipped and notified |
|
|
214
|
+
| Custom retryable errors | empty | One case-insensitive literal per line; matching the error code, HTTP status, or message explicitly overrides the built-in classifier |
|
|
213
215
|
| Backoff factor | `2` | Cooldown multiplier per consecutive failure (2 = 20s → 40s → 80s…) |
|
|
214
216
|
| Max backoff (ms) | `300000` | Cap on the adaptive backoff interval |
|
|
215
217
|
| Browser notifications | `off` | Notify when auto-continue fires, gives up, or hits a permanent error |
|
|
216
218
|
|
|
219
|
+
For a provider-specific error that is safe to resume (confirm first that manually sending "continue" recovers), add a narrow, stable fragment rather than disabling classification globally:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
auto-continue:
|
|
223
|
+
retryableErrorPatterns: |-
|
|
224
|
+
Upstream rejected the request as invalid
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Patterns are literal substrings, not regular expressions. Blank lines are ignored; any matching line wins before the built-in permanent-error rules. Cooldown and consecutive-attempt limits still apply.
|
|
228
|
+
|
|
217
229
|
`continueText` (and `continueTextMaxTokens`) accept the placeholders `{code}`, `{message}`, `{status}`, `{tool}` (last tool call before the failure), `{turn}`, `{errorCount}` (consecutive failures including this one), `{sessionTitle}` (from the session list) and `{elapsed}` (time since the failure, e.g. `1m5s`) — e.g. `继续 ({tool}: {code})` becomes `继续 (git push: UPSTREAM)`. The guard texts accept `{tool}` and `{result}` (a truncated excerpt of the last tool output).
|
|
218
230
|
|
|
219
231
|
---
|
|
@@ -240,7 +252,7 @@ npm run test # node tests/simulate-host.mjs — 15 host-side behavioral s
|
|
|
240
252
|
|
|
241
253
|
While `npm run watch` runs, the profile's client-hmr row polls `lib/client.js` every 500 ms and hot-reloads the plugin in the browser — no server restart needed for code changes.
|
|
242
254
|
|
|
243
|
-
CI runs [dsh-plugin-check](https://github.com/omdsh-dev/dsh-plugin-check)
|
|
255
|
+
CI installs from the lockfile, typechecks, rebuilds and verifies committed artifacts, runs the host and dual-layout client simulations, then runs [dsh-plugin-check](https://github.com/omdsh-dev/dsh-plugin-check). The same health check gates releases.
|
|
244
256
|
|
|
245
257
|
---
|
|
246
258
|
|
package/README.zh.md
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
|
|
38
38
|
**智能恢复**(全部可配置):
|
|
39
39
|
|
|
40
|
-
- **错误分类** — 临时性错误(网络 / 超时 / 5xx / 429 等)自动续跑; 永久性错误跳过并通知, 因为重试也没用。判定为永久性的条件: HTTP 状态码 401/403, 或 code/message 命中认证、凭据/API Key
|
|
40
|
+
- **错误分类** — 临时性错误(网络 / 超时 / 5xx / 429 等)自动续跑; 永久性错误跳过并通知, 因为重试也没用。判定为永久性的条件: HTTP 状态码 401/403, 或 code/message 命中认证、凭据/API Key、余额/配额、模型不存在、上下文长度/超限等关键词。provider 专属例外可由用户显式填写普通文本匹配; 关闭分类后则全部自动继续
|
|
41
41
|
- **自适应退避** — 连续失败时等待时间递增(冷却 × 系数: 20s → 40s → 80s…), 有上限, 不再对故障上游狂轰滥炸
|
|
42
42
|
- **模板化继续文本** — `continueText` 支持 `{code}` `{message}` `{status}` `{tool}` `{turn}` `{errorCount}` `{sessionTitle}` `{elapsed}` 占位符, 续跑消息可携带失败上下文(如「继续 (git push 失败: UPSTREAM)」); 达到 `max-tokens` 时使用**另一套模板**(如「继续输出, 不要重复已生成的内容」)
|
|
43
43
|
- **幂等护栏** — 续跑前检查上一步工具调用: 结果未确认(回合在工具执行中途夭折, 如 `git push` 可能已经推上去了)时, 续跑消息会提示模型先确认状态、不要重复执行; 工具已确认成功时说明已完成、请勿重复; 工具失败则不加护栏(重试本来就是目的)。两段护栏文本都可配置(支持 `{tool}` / `{result}` 占位符)
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
|
|
73
73
|
DSH 插件安装进 **profile**(`dsh web` 对应 `web` profile)。安装后重启 `dsh web` 即可。
|
|
74
74
|
|
|
75
|
-
>
|
|
75
|
+
> **请使用最新 DSH(推荐 0.1.2-alpha.1 或更高版本)。** 安装前先运行 `dsh --version`。插件 v0.8.2 已支持 0.1.2 的新 client-store 模块布局, 同时为 DSH 0.1.0-rc.7 至 0.1.1 保留兼容回退; rc.6 及更早版本仍不支持(`list slot ... requires options.id`)。预览版可能先发布在 [DSH 官方 Releases](https://github.com/deepseek-ai/deepseek-harness/releases), 公开 npm 标签会稍后跟进。
|
|
76
76
|
|
|
77
77
|
### 从 npm 安装(推荐)
|
|
78
78
|
|
|
@@ -164,6 +164,7 @@ auto-continue:
|
|
|
164
164
|
freshMs: 900000
|
|
165
165
|
verbose: true
|
|
166
166
|
classify: true
|
|
167
|
+
retryableErrorPatterns: ''
|
|
167
168
|
backoffFactor: 2
|
|
168
169
|
backoffMaxMs: 300000
|
|
169
170
|
notify: false
|
|
@@ -210,10 +211,21 @@ auto-continue:
|
|
|
210
211
|
| 扫描时间窗 (ms) | `900000` | 扫描只处理该时间窗内的中断 |
|
|
211
212
|
| 详细日志 | 开 | 控制台输出 `[auto-continue]` 日志 |
|
|
212
213
|
| 错误分类 | 开 | 仅自动恢复临时性错误; 认证 / 余额 / 模型等永久性错误跳过并通知 |
|
|
214
|
+
| 自定义可恢复错误 | 空 | 每行一个大小写不敏感的普通文本片段; 命中错误码、HTTP 状态或消息时显式覆盖内置分类 |
|
|
213
215
|
| 退避系数 | `2` | 连续失败时冷却间隔的倍率(2 = 20s → 40s → 80s…) |
|
|
214
216
|
| 最大退避间隔 (ms) | `300000` | 自适应退避的上限 |
|
|
215
217
|
| 浏览器通知 | 关 | 自动继续成功 / 放弃 / 遇到永久性错误时弹通知 |
|
|
216
218
|
|
|
219
|
+
遇到确认可以安全续跑的 provider 专属错误时(先确认手动发送「继续」确实能恢复), 应添加足够具体、稳定的片段, 而不是全局关闭错误分类:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
auto-continue:
|
|
223
|
+
retryableErrorPatterns: |-
|
|
224
|
+
Upstream rejected the request as invalid
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
匹配项是普通子串, 不是正则表达式。空行会被忽略; 任一行命中后会优先于内置永久错误规则。冷却与最大连续次数仍然生效。
|
|
228
|
+
|
|
217
229
|
`continueText`(以及 `continueTextMaxTokens`)支持占位符 `{code}`、`{message}`、`{status}`、`{tool}`(失败前最后一次工具调用)、`{turn}`、`{errorCount}`(连续失败次数, 含本次)、`{sessionTitle}`(来自会话列表)和 `{elapsed}`(距失败经过的时间, 如 `1m5s`)——例如 `继续 ({tool}: {code})` 会变成 `继续 (git push: UPSTREAM)`。护栏文本支持 `{tool}` 与 `{result}`(上一步工具输出的截断摘要)。
|
|
218
230
|
|
|
219
231
|
---
|
|
@@ -240,7 +252,7 @@ npm run test # node tests/simulate-host.mjs — 15 个 host 侧行为场
|
|
|
240
252
|
|
|
241
253
|
`npm run watch` 运行时, profile 的 client-hmr 行每 500ms 轮询 `lib/client.js` 并在浏览器中热重载插件——改代码无需重启服务。
|
|
242
254
|
|
|
243
|
-
CI
|
|
255
|
+
CI 会按锁文件安装依赖, 执行类型检查、重新构建并核对已提交产物, 跑 host 与双模块布局 client 模拟测试, 最后运行 [dsh-plugin-check](https://github.com/omdsh-dev/dsh-plugin-check); 发布也受同一体检门禁约束。
|
|
244
256
|
|
|
245
257
|
---
|
|
246
258
|
|
package/lib/client.js
CHANGED
|
@@ -67,6 +67,8 @@ var zh = {
|
|
|
67
67
|
"field.verboseHint": "在浏览器控制台输出 [auto-continue] 日志。",
|
|
68
68
|
"field.classify": "错误分类",
|
|
69
69
|
"field.classifyHint": "仅自动恢复临时性错误(网络/超时/5xx 等); 认证/余额/模型不存在等永久性错误跳过并通知。",
|
|
70
|
+
"field.retryableErrorPatterns": "自定义可恢复错误",
|
|
71
|
+
"field.retryableErrorPatternsHint": "每行一个大小写不敏感的普通文本片段; 命中错误码、HTTP 状态或消息时覆盖内置分类。请只填 provider 稳定且足够具体的文案, 过宽会重复请求。",
|
|
70
72
|
"field.backoffFactor": "退避系数",
|
|
71
73
|
"field.backoffFactorHint": "连续失败时冷却间隔的倍率(如 2 表示 20s→40s→80s 递增)。",
|
|
72
74
|
"field.backoffMaxMs": "最大退避间隔 (ms)",
|
|
@@ -148,6 +150,8 @@ var en = {
|
|
|
148
150
|
"field.verboseHint": "Log [auto-continue] lines to the browser console.",
|
|
149
151
|
"field.classify": "Classify errors",
|
|
150
152
|
"field.classifyHint": "Auto-resume transient failures only (network/timeout/5xx…); auth, balance and model errors are skipped and notified.",
|
|
153
|
+
"field.retryableErrorPatterns": "Custom retryable errors",
|
|
154
|
+
"field.retryableErrorPatternsHint": "One case-insensitive literal per line. A match in the error code, HTTP status, or message overrides built-in classification. Use only stable, provider-specific text; broad matches can repeat requests.",
|
|
151
155
|
"field.backoffFactor": "Backoff factor",
|
|
152
156
|
"field.backoffFactorHint": "Cooldown multiplier per consecutive failure (2 = 20s→40s→80s…).",
|
|
153
157
|
"field.backoffMaxMs": "Max backoff (ms)",
|
|
@@ -201,7 +205,6 @@ var en = {
|
|
|
201
205
|
|
|
202
206
|
// src/client/settings-card.tsx
|
|
203
207
|
var import_react = require("react");
|
|
204
|
-
var import_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
205
208
|
|
|
206
209
|
// src/shared/core.ts
|
|
207
210
|
var DEFAULT_CONFIG = {
|
|
@@ -218,6 +221,7 @@ var DEFAULT_CONFIG = {
|
|
|
218
221
|
freshMs: 15 * 60 * 1e3,
|
|
219
222
|
verbose: true,
|
|
220
223
|
classify: true,
|
|
224
|
+
retryableErrorPatterns: "",
|
|
221
225
|
backoffFactor: 2,
|
|
222
226
|
backoffMaxMs: 3e5,
|
|
223
227
|
notify: false,
|
|
@@ -233,6 +237,18 @@ var DEFAULT_CONFIG = {
|
|
|
233
237
|
var RECOVERY_WINDOW_MS = 10 * 60 * 1e3;
|
|
234
238
|
var ECHO_WINDOW_MS = 10 * 60 * 1e3;
|
|
235
239
|
|
|
240
|
+
// src/client/dsh-store-compat.ts
|
|
241
|
+
function resolveSnapshotStore() {
|
|
242
|
+
const current = ["@deepseek-ai/dsh-client", "-store"].join("");
|
|
243
|
+
const legacy = ["@deepseek-ai/dsh-client-runtime", "/client"].join("");
|
|
244
|
+
try {
|
|
245
|
+
return require(current);
|
|
246
|
+
} catch {
|
|
247
|
+
return require(legacy);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
var { createSnapshotStore } = resolveSnapshotStore();
|
|
251
|
+
|
|
236
252
|
// src/client/bridge.ts
|
|
237
253
|
var EMPTY_STATS = {
|
|
238
254
|
date: "",
|
|
@@ -659,6 +675,7 @@ var css = `
|
|
|
659
675
|
.dshAcInput:focus-visible { border-color: var(--dsw-alias-brand-primary); outline: none; }
|
|
660
676
|
.dshAcInput:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
661
677
|
.dshAcInputInvalid { border-color: var(--dsw-alias-label-error); }
|
|
678
|
+
.dshAcTextArea { box-sizing: border-box; height: auto; min-height: 84px; padding: 8px 12px; resize: vertical; }
|
|
662
679
|
.dshAcSelect {
|
|
663
680
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
664
681
|
background: var(--dsw-alias-bg-layer-3);
|
|
@@ -734,6 +751,7 @@ var AutoContinueSettingsCardController = class {
|
|
|
734
751
|
numberField("freshMs", 0),
|
|
735
752
|
booleanField("verbose"),
|
|
736
753
|
booleanField("classify"),
|
|
754
|
+
textField("retryableErrorPatterns"),
|
|
737
755
|
numberField("backoffFactor", 1),
|
|
738
756
|
numberField("backoffMaxMs", 0),
|
|
739
757
|
booleanField("notify"),
|
|
@@ -745,7 +763,7 @@ var AutoContinueSettingsCardController = class {
|
|
|
745
763
|
numberField("loopToolRepeat", 2),
|
|
746
764
|
textField("loopText")
|
|
747
765
|
]);
|
|
748
|
-
this.store = this.form.bind(() => this.projection(),
|
|
766
|
+
this.store = this.form.bind(() => this.projection(), createSnapshotStore);
|
|
749
767
|
}
|
|
750
768
|
projection() {
|
|
751
769
|
return {
|
|
@@ -764,6 +782,7 @@ var AutoContinueSettingsCardController = class {
|
|
|
764
782
|
freshMs: this.form.field("freshMs"),
|
|
765
783
|
verbose: this.form.field("verbose"),
|
|
766
784
|
classify: this.form.field("classify"),
|
|
785
|
+
retryableErrorPatterns: this.form.field("retryableErrorPatterns"),
|
|
767
786
|
backoffFactor: this.form.field("backoffFactor"),
|
|
768
787
|
backoffMaxMs: this.form.field("backoffMaxMs"),
|
|
769
788
|
notify: this.form.field("notify"),
|
|
@@ -831,6 +850,7 @@ function SettingsCard(props) {
|
|
|
831
850
|
] });
|
|
832
851
|
}
|
|
833
852
|
function ValueField(props) {
|
|
853
|
+
const className = props.invalid ? "dshAcInput dshAcInputInvalid" : "dshAcInput";
|
|
834
854
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshAcField", children: [
|
|
835
855
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshAcHead", children: [
|
|
836
856
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dshAcLabel", htmlFor: props.id, children: props.label }),
|
|
@@ -839,11 +859,23 @@ function ValueField(props) {
|
|
|
839
859
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dshAcReset", disabled: props.disabled, onClick: props.onReset, children: props.t("chrome.reset") })
|
|
840
860
|
] }) : null
|
|
841
861
|
] }),
|
|
842
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
862
|
+
props.multiline === true ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
863
|
+
"textarea",
|
|
864
|
+
{
|
|
865
|
+
id: props.id,
|
|
866
|
+
className: `${className} dshAcTextArea`,
|
|
867
|
+
"aria-invalid": props.invalid || void 0,
|
|
868
|
+
value: props.text,
|
|
869
|
+
placeholder: props.placeholder ?? "",
|
|
870
|
+
disabled: props.disabled,
|
|
871
|
+
rows: 4,
|
|
872
|
+
onChange: (event) => props.onEdit(event.target.value)
|
|
873
|
+
}
|
|
874
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
843
875
|
"input",
|
|
844
876
|
{
|
|
845
877
|
id: props.id,
|
|
846
|
-
className
|
|
878
|
+
className,
|
|
847
879
|
type: "text",
|
|
848
880
|
inputMode: props.numeric === true ? "numeric" : void 0,
|
|
849
881
|
"aria-invalid": props.invalid || void 0,
|
|
@@ -1189,6 +1221,20 @@ function AutoContinueSettingsCard(props) {
|
|
|
1189
1221
|
onReset: () => props.resetField("classify")
|
|
1190
1222
|
}
|
|
1191
1223
|
),
|
|
1224
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1225
|
+
ValueField,
|
|
1226
|
+
{
|
|
1227
|
+
id: "auto-continue-retryable-error-patterns",
|
|
1228
|
+
label: t("field.retryableErrorPatterns"),
|
|
1229
|
+
hint: t("field.retryableErrorPatternsHint"),
|
|
1230
|
+
multiline: true,
|
|
1231
|
+
...shared,
|
|
1232
|
+
...state2.retryableErrorPatterns,
|
|
1233
|
+
onEdit: (text) => props.edit("retryableErrorPatterns", text),
|
|
1234
|
+
placeholder: "Upstream rejected the request as invalid",
|
|
1235
|
+
onReset: () => props.resetField("retryableErrorPatterns")
|
|
1236
|
+
}
|
|
1237
|
+
),
|
|
1192
1238
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1193
1239
|
ValueField,
|
|
1194
1240
|
{
|