dsh-client-auto-continue 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/README.zh.md +2 -6
- package/lib/client.js +14 -46
- package/lib/client.js.map +3 -3
- package/lib/index.js +5 -11
- package/lib/types/client/dsh-store-compat.d.ts +39 -0
- package/lib/types/client/engine.d.ts +5 -250
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/locales.d.ts +0 -4
- package/lib/types/client/settings-card.d.ts +1 -3
- package/lib/types/client/settings-form.d.ts +1 -1
- package/lib/types/host/engine.d.ts +1 -141
- package/lib/types/index.d.ts +0 -8
- package/lib/types/shared/core.d.ts +234 -0
- package/package.json +2 -4
- package/src/client/dsh-store-compat.ts +57 -0
- package/src/client/engine.ts +17 -1639
- package/src/client/index.ts +1 -1
- package/src/client/locales.ts +0 -8
- package/src/client/settings-card.tsx +1 -27
- package/src/client/settings-form.ts +1 -1
- package/src/host/engine.ts +24 -457
- package/src/index.ts +2 -5
- package/src/shared/core.ts +458 -0
package/lib/index.js
CHANGED
|
@@ -97,7 +97,7 @@ var alwaysPolicySchema = z.object({
|
|
|
97
97
|
var RetryPolicySchema = z.union([normalPolicySchema, alwaysPolicySchema]);
|
|
98
98
|
var { version } = createRequire(import.meta.url)("../package.json");
|
|
99
99
|
|
|
100
|
-
// src/
|
|
100
|
+
// src/shared/core.ts
|
|
101
101
|
var DEFAULT_CONFIG = {
|
|
102
102
|
continueText: "继续",
|
|
103
103
|
continueTextMaxTokens: "继续",
|
|
@@ -110,8 +110,6 @@ var DEFAULT_CONFIG = {
|
|
|
110
110
|
scanOnBoot: true,
|
|
111
111
|
scanLimit: 8,
|
|
112
112
|
freshMs: 15 * 60 * 1e3,
|
|
113
|
-
reconnectScanDelayMs: 5e3,
|
|
114
|
-
reconnectBackoffMs: 3e3,
|
|
115
113
|
verbose: true,
|
|
116
114
|
classify: true,
|
|
117
115
|
backoffFactor: 2,
|
|
@@ -150,8 +148,6 @@ function resolveConfig(section) {
|
|
|
150
148
|
scanOnBoot: booleanOr(value.scanOnBoot, DEFAULT_CONFIG.scanOnBoot),
|
|
151
149
|
scanLimit: Math.max(1, numberOr(value.scanLimit, DEFAULT_CONFIG.scanLimit)),
|
|
152
150
|
freshMs: numberOr(value.freshMs, DEFAULT_CONFIG.freshMs),
|
|
153
|
-
reconnectScanDelayMs: numberOr(value.reconnectScanDelayMs, DEFAULT_CONFIG.reconnectScanDelayMs),
|
|
154
|
-
reconnectBackoffMs: numberOr(value.reconnectBackoffMs, DEFAULT_CONFIG.reconnectBackoffMs),
|
|
155
151
|
verbose: booleanOr(value.verbose, DEFAULT_CONFIG.verbose),
|
|
156
152
|
classify: booleanOr(value.classify, DEFAULT_CONFIG.classify),
|
|
157
153
|
backoffFactor: Math.max(1, numberOr(value.backoffFactor, DEFAULT_CONFIG.backoffFactor)),
|
|
@@ -227,8 +223,6 @@ function todayKey() {
|
|
|
227
223
|
function emptyDayStats() {
|
|
228
224
|
return { date: todayKey(), sent: 0, skipped: 0, recovered: 0, failed: 0, gaveUp: 0, looped: 0, byCode: {} };
|
|
229
225
|
}
|
|
230
|
-
var RECOVERY_WINDOW_MS = 10 * 60 * 1e3;
|
|
231
|
-
var ECHO_WINDOW_MS = 10 * 60 * 1e3;
|
|
232
226
|
var freshState = () => ({
|
|
233
227
|
consecutive: 0,
|
|
234
228
|
lastAutoAt: 0,
|
|
@@ -253,6 +247,8 @@ var freshState = () => ({
|
|
|
253
247
|
loopCancelled: false,
|
|
254
248
|
loopRetryTimer: void 0
|
|
255
249
|
});
|
|
250
|
+
var RECOVERY_WINDOW_MS = 10 * 60 * 1e3;
|
|
251
|
+
var ECHO_WINDOW_MS = 10 * 60 * 1e3;
|
|
256
252
|
function isOurEcho(state, event) {
|
|
257
253
|
if (event.type !== "user/message") return false;
|
|
258
254
|
const message = event.data;
|
|
@@ -262,6 +258,8 @@ function isOurEcho(state, event) {
|
|
|
262
258
|
const text = message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
263
259
|
return text === state.lastSentText;
|
|
264
260
|
}
|
|
261
|
+
|
|
262
|
+
// src/host/engine.ts
|
|
265
263
|
var AutoContinueRunner = class {
|
|
266
264
|
/**
|
|
267
265
|
* @param ctx - host plugin context (agents registry, session events, settings).
|
|
@@ -911,10 +909,6 @@ var AutoContinueSchema = z2.object({
|
|
|
911
909
|
scanLimit: z2.natural().min(1).default(8),
|
|
912
910
|
/** Scan only considers interruptions inside this window (ms). */
|
|
913
911
|
freshMs: z2.natural().default(15 * 60 * 1e3),
|
|
914
|
-
/** Delay before scanning after a reconnect (ms). */
|
|
915
|
-
reconnectScanDelayMs: z2.natural().default(5e3),
|
|
916
|
-
/** SSE reconnect backoff (ms). */
|
|
917
|
-
reconnectBackoffMs: z2.natural().default(3e3),
|
|
918
912
|
/** Log `[auto-continue]` lines to the browser console. */
|
|
919
913
|
verbose: z2.boolean().default(true),
|
|
920
914
|
/** Classify failures: auto-continue transient errors only; permanent ones are skipped and notified. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot-store bridge across the two public DSH client module layouts.
|
|
3
|
+
*
|
|
4
|
+
* DSH 0.1.2 moved the store engine from the dynamic
|
|
5
|
+
* `@deepseek-ai/dsh-client-runtime/client` row into the shell-seeded
|
|
6
|
+
* `@deepseek-ai/dsh-client-store` platform module. Keep the probe dynamic so
|
|
7
|
+
* esbuild does not turn both candidates into eager top-level requires: the
|
|
8
|
+
* loader must only resolve the module that exists in the running DSH cohort.
|
|
9
|
+
*/
|
|
10
|
+
/** Writable observable snapshot used by the settings card. */
|
|
11
|
+
export interface SnapshotStore<T> {
|
|
12
|
+
getSnapshot(): T;
|
|
13
|
+
subscribe(listener: () => void): () => void;
|
|
14
|
+
update(mutator: (draft: T) => void): void;
|
|
15
|
+
set(next: T): void;
|
|
16
|
+
}
|
|
17
|
+
/** Settings state consumed by the staged form. */
|
|
18
|
+
export interface SettingsScopeSnapshot<T> {
|
|
19
|
+
status: 'loading' | 'ready' | 'unavailable';
|
|
20
|
+
value: T | undefined;
|
|
21
|
+
base: unknown;
|
|
22
|
+
user: unknown;
|
|
23
|
+
revision: number | undefined;
|
|
24
|
+
writable: boolean;
|
|
25
|
+
mode: 'host' | 'memory';
|
|
26
|
+
}
|
|
27
|
+
/** Stable subset shared by the legacy and DSH 0.1.2 settings scopes. */
|
|
28
|
+
export interface SettingsScope<T> {
|
|
29
|
+
getSnapshot(): SettingsScopeSnapshot<T>;
|
|
30
|
+
subscribe(listener: () => void): () => void;
|
|
31
|
+
set(field: string, value: unknown): Promise<void>;
|
|
32
|
+
unset(field: string): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export declare const createSnapshotStore: <T>(init: T, options?: {
|
|
35
|
+
flush?: "raf" | "sync";
|
|
36
|
+
persist?: {
|
|
37
|
+
name: string;
|
|
38
|
+
};
|
|
39
|
+
}) => SnapshotStore<T>;
|
|
@@ -1,253 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Browser half: re-exports the shared core (config, templates, guards).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* After a grace period it sends a queued prompt (default 「继续」) to that
|
|
8
|
-
* session — exactly equivalent to the user typing it manually.
|
|
9
|
-
*
|
|
10
|
-
* All behavior is driven by the `auto-continue` settings namespace (see the
|
|
11
|
-
* plugin's settings card); every knob below is user-configurable there.
|
|
12
|
-
*/
|
|
13
|
-
import type { IApiClient, SessionId } from '@deepseek-ai/dsh-client-connection/client';
|
|
14
|
-
/** The `auto-continue` settings section (all fields optional on the wire; the host schema carries defaults). */
|
|
15
|
-
export interface AutoContinueSettings {
|
|
16
|
-
/** Text automatically sent after an interruption. */
|
|
17
|
-
continueText?: string;
|
|
18
|
-
/** Text sent when the output token ceiling is reached (same placeholders as `continueText`). */
|
|
19
|
-
continueTextMaxTokens?: string;
|
|
20
|
-
/** Idempotency guard: inspect the last tool call before resuming and steer the model. */
|
|
21
|
-
guardTools?: boolean;
|
|
22
|
-
/** Guard text appended when the last tool call has no confirmed result (it may have partially executed). */
|
|
23
|
-
guardPendingText?: string;
|
|
24
|
-
/** Guard text appended when the last tool call completed successfully (don't rerun it). */
|
|
25
|
-
guardDoneText?: string;
|
|
26
|
-
/** Grace period after an interruption before auto-sending (ms). */
|
|
27
|
-
graceMs?: number;
|
|
28
|
-
/** Minimum interval between two auto-continues per session (ms). */
|
|
29
|
-
cooldownMs?: number;
|
|
30
|
-
/** Max consecutive auto-continues per session before stopping. */
|
|
31
|
-
maxConsecutive?: number;
|
|
32
|
-
/** Scan recently interrupted sessions on page load / reconnect. */
|
|
33
|
-
scanOnBoot?: boolean;
|
|
34
|
-
/** Max sessions the scan checks (most recently updated). */
|
|
35
|
-
scanLimit?: number;
|
|
36
|
-
/** Scan only considers interruptions inside this window (ms). */
|
|
37
|
-
freshMs?: number;
|
|
38
|
-
/** Delay before scanning after a reconnect (ms). */
|
|
39
|
-
reconnectScanDelayMs?: number;
|
|
40
|
-
/** SSE reconnect backoff (ms). */
|
|
41
|
-
reconnectBackoffMs?: number;
|
|
42
|
-
/** Log `[auto-continue]` lines to the browser console. */
|
|
43
|
-
verbose?: boolean;
|
|
44
|
-
/** Classify failures: auto-continue transient errors only; permanent ones (auth/balance/model) are skipped and notified. */
|
|
45
|
-
classify?: boolean;
|
|
46
|
-
/** Cooldown multiplier per consecutive failure (adaptive backoff). */
|
|
47
|
-
backoffFactor?: number;
|
|
48
|
-
/** Cap on the effective backoff interval (ms). */
|
|
49
|
-
backoffMaxMs?: number;
|
|
50
|
-
/** Show browser notifications for auto-continue events. */
|
|
51
|
-
notify?: boolean;
|
|
52
|
-
/** Globally pause auto-continue: no live or scan send, queued pending sends cancelled. */
|
|
53
|
-
paused?: boolean;
|
|
54
|
-
/** Loop guard: detect a running turn spinning in place (short talk without tools, or the same tool repeating) and restart it. */
|
|
55
|
-
loopGuard?: boolean;
|
|
56
|
-
/** A model message shorter than this many chars counts as a "short sentence" (loop signal). */
|
|
57
|
-
loopShortChars?: number;
|
|
58
|
-
/** Consecutive short sentences within this window (ms) with no tool call in between trip the loop guard. */
|
|
59
|
-
loopWindowMs?: number;
|
|
60
|
-
/** Consecutive short sentences trip the loop guard. */
|
|
61
|
-
loopShortCount?: number;
|
|
62
|
-
/** Consecutive identical tool calls with identical arguments AND identical results trip the loop guard. */
|
|
63
|
-
loopToolRepeat?: number;
|
|
64
|
-
/** Consecutive identical short sentences trip the loop guard (strongest spinning signal). */
|
|
65
|
-
loopRepeatText?: number;
|
|
66
|
-
/** Text sent after the loop guard cancels and restarts a turn (supports {tool}). */
|
|
67
|
-
loopText?: string;
|
|
68
|
-
}
|
|
69
|
-
/** Fully resolved configuration (built-in defaults + user overrides). */
|
|
70
|
-
export type AutoContinueConfig = Required<AutoContinueSettings>;
|
|
71
|
-
/** Built-in defaults — must match the host schema defaults in src/index.ts. */
|
|
72
|
-
export declare const DEFAULT_CONFIG: AutoContinueConfig;
|
|
73
|
-
/** Resolve a (possibly partial / not-yet-loaded) settings section to a full config. */
|
|
74
|
-
export declare function resolveConfig(section: AutoContinueSettings | undefined): AutoContinueConfig;
|
|
75
|
-
/** 一次回合失败的机器可读事实(turn/end error 的 LlmFailure 载荷)。 */
|
|
76
|
-
export interface FailureFacts {
|
|
77
|
-
/** 稳定机器路由码(如 UPSTREAM、RATE_LIMIT_EXCEEDED、INVALID_API_KEY)。 */
|
|
78
|
-
code: string;
|
|
79
|
-
/** 人类可读的失败描述。 */
|
|
80
|
-
message: string;
|
|
81
|
-
/** 供应商 HTTP 状态码(可用时)。 */
|
|
82
|
-
status?: number;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* 错误分类: 该失败是否值得自动继续。
|
|
86
|
-
* 永久性失败(认证/余额/模型不存在/上下文超限等)重试也不会成功, 应跳过并通知用户;
|
|
87
|
-
* 其余(网络、超时、5xx、429 等)视为临时性失败, 允许自动恢复。
|
|
88
|
-
*/
|
|
89
|
-
export declare function isTransientFailure(failure: FailureFacts): boolean;
|
|
90
|
-
/**
|
|
91
|
-
* host/agent-error 消息分类: 仅明确属于网络/传输类的临时错误才自动继续。
|
|
92
|
-
* 其余(序列化失败、配置/宿主内部错误等)视为永久性——重试无益, 且用户停止导致的
|
|
93
|
-
* 序列化失败(如 Windows 下 abort 的 DOMException reason)绝不能自动续跑。
|
|
4
|
+
* The engine itself moved into the host process in 0.8.0
|
|
5
|
+
* (see src/host/engine.ts); this module only serves the settings card and
|
|
6
|
+
* anything else the thin browser half still needs.
|
|
94
7
|
*/
|
|
95
|
-
export
|
|
96
|
-
/** 通知上的一个操作按钮(action 标识 + 显示文案)。 */
|
|
97
|
-
export interface NotifyAction {
|
|
98
|
-
/** 稳定动作标识, 点击时经 onAction 回调传出。 */
|
|
99
|
-
action: string;
|
|
100
|
-
/** 按钮显示文案。 */
|
|
101
|
-
title: string;
|
|
102
|
-
}
|
|
103
|
-
/** 通知的可选行为: 操作按钮列表与点击回调。 */
|
|
104
|
-
export interface NotifyOptions {
|
|
105
|
-
actions?: NotifyAction[];
|
|
106
|
-
onAction?: (action: string) => void;
|
|
107
|
-
}
|
|
108
|
-
/** 模板填充所需的上下文(全部可选, 缺失的占位符填为空串)。 */
|
|
109
|
-
export interface TemplateContext {
|
|
110
|
-
/** 失败事实(错误码/消息/HTTP 状态), 对应 {code}/{message}/{status}。 */
|
|
111
|
-
facts?: FailureFacts;
|
|
112
|
-
/** 失败前最后一次工具调用的名称, 对应 {tool}。 */
|
|
113
|
-
tool?: string;
|
|
114
|
-
/** 失败回合的编号, 对应 {turn}。 */
|
|
115
|
-
turn?: number;
|
|
116
|
-
/** 连续失败次数(含本次), 对应 {errorCount}。 */
|
|
117
|
-
errorCount?: number;
|
|
118
|
-
/** 会话标题(来自 session.list 投影, 可用时), 对应 {sessionTitle}。 */
|
|
119
|
-
sessionTitle?: string;
|
|
120
|
-
/** 自失败发生以来的毫秒数, 对应 {elapsed}。 */
|
|
121
|
-
elapsedMs?: number;
|
|
122
|
-
/** 上一步工具结果摘要(截断), 对应 {result}(护栏模板用)。 */
|
|
123
|
-
result?: string;
|
|
124
|
-
}
|
|
125
|
-
/** 用失败事实与回合信息填充 continueText 模板占位符({code}/{message}/{status}/{tool}/{turn}/{errorCount}/{sessionTitle}/{elapsed}/{result})。 */
|
|
126
|
-
export declare function fillTemplate(template: string, ctx: TemplateContext): string;
|
|
127
|
-
/** 上一步工具调用的判定结果: 是否已确认完成, 以及文本摘要。 */
|
|
128
|
-
export interface ToolResultFacts {
|
|
129
|
-
/** 工具是否成功完成(内部失败或 isError 视为未成功)。 */
|
|
130
|
-
ok: boolean;
|
|
131
|
-
/** 工具输出的文本摘要(截断)。 */
|
|
132
|
-
excerpt: string;
|
|
133
|
-
}
|
|
134
|
-
/** 自适应退避: 同一会话连续失败时的有效冷却间隔。 */
|
|
135
|
-
export declare function effectiveCooldown(consecutive: number, base: number, factor: number, max: number): number;
|
|
136
|
-
/** 暂停某会话: 到 `until` 之前, 引擎不会为该会话自动继续(通知按钮等调用)。 */
|
|
137
|
-
export declare function pauseSession(sessionId: SessionId, ms: number): void;
|
|
138
|
-
/** 解除某会话的暂停。 */
|
|
139
|
-
export declare function unpauseSession(sessionId: SessionId): void;
|
|
140
|
-
/** 会话暂停的截止时间戳; 0 表示未暂停。 */
|
|
141
|
-
export declare function sessionPauseUntil(sessionId: SessionId): number;
|
|
142
|
-
/** 当前生效(未过期)的暂停会话列表; 顺带清理过期条目。 */
|
|
143
|
-
export declare function pausedSessions(): {
|
|
144
|
-
sessionId: SessionId;
|
|
145
|
-
until: number;
|
|
146
|
-
}[];
|
|
147
|
-
/** 一天的自动继续统计。 */
|
|
148
|
-
export interface DayStats {
|
|
149
|
-
/** 本地日期 YYYY-MM-DD。 */
|
|
150
|
-
date: string;
|
|
151
|
-
/** 自动发送次数。 */
|
|
152
|
-
sent: number;
|
|
153
|
-
/** 因永久性错误跳过的次数。 */
|
|
154
|
-
skipped: number;
|
|
155
|
-
/** 发送后回合成功完成(恢复成功)的次数。 */
|
|
156
|
-
recovered: number;
|
|
157
|
-
/** 发送后再次失败的次数。 */
|
|
158
|
-
failed: number;
|
|
159
|
-
/** 达到连续上限而停止的次数(按停止事件计)。 */
|
|
160
|
-
gaveUp: number;
|
|
161
|
-
/** loop guard 打断并重启回合的次数。 */
|
|
162
|
-
looped: number;
|
|
163
|
-
/** 按错误码计数的失败分布。 */
|
|
164
|
-
byCode: Record<string, number>;
|
|
165
|
-
}
|
|
166
|
-
/** 今日统计(设置卡片展示用)。 */
|
|
167
|
-
export declare function readTodayStats(): DayStats;
|
|
168
|
-
/** 清零今日统计。 */
|
|
169
|
-
export declare function resetTodayStats(): void;
|
|
170
|
-
/** 插件主体: 一条 mux 流 + 一条 host 流 + 启动/重连扫描。 */
|
|
171
|
-
export declare class AutoContinueRunner {
|
|
172
|
-
private readonly api;
|
|
173
|
-
private readonly getConfig;
|
|
174
|
-
private readonly states;
|
|
175
|
-
private readonly muxAbort;
|
|
176
|
-
private readonly hostAbort;
|
|
177
|
-
private disposed;
|
|
178
|
-
private reconnectScans;
|
|
179
|
-
/**
|
|
180
|
-
* @param api - shared wire client (ctx.connection.api).
|
|
181
|
-
* @param getConfig - read the current resolved configuration (settings scope).
|
|
182
|
-
*/
|
|
183
|
-
constructor(api: IApiClient, getConfig: () => AutoContinueConfig);
|
|
184
|
-
private log;
|
|
185
|
-
dispose(): void;
|
|
186
|
-
private state;
|
|
187
|
-
private runMux;
|
|
188
|
-
private runHost;
|
|
189
|
-
private onMuxFrame;
|
|
190
|
-
/** 从 assistant/message 事件提取纯文本。 */
|
|
191
|
-
private assistantText;
|
|
192
|
-
/**
|
|
193
|
-
* loop guard 信号 1(空转): 时间窗内连续短句且期间无工具调用。
|
|
194
|
-
* 短句 = 模型消息文本短于 loopShortChars; 长句、工具调用、或短句间隔超过
|
|
195
|
-
* loopWindowMs(正常思考的短文本散布在长时间里)都会重置计数。
|
|
196
|
-
*/
|
|
197
|
-
private onAssistantMessage;
|
|
198
|
-
/** 两个循环信号的公共检查; 命中且本回合未打断过则打断。 */
|
|
199
|
-
private checkLoop;
|
|
200
|
-
/**
|
|
201
|
-
* 打断运行中的回合: cancel(带来源标记)+ 进冷却。
|
|
202
|
-
* 随后的 turn/end aborted 会因 loopCancelled 走「可恢复中断」路径,
|
|
203
|
-
* 用 loopText 重启回合——不会与用户手动停止混淆。
|
|
204
|
-
*/
|
|
205
|
-
private interruptLoop;
|
|
206
|
-
private onSessionEvent;
|
|
207
|
-
private onHostFrame;
|
|
208
|
-
/** 回合失败入口: 先做错误分类, 永久性失败跳过并通知, 临时性失败走正常调度。 */
|
|
209
|
-
private onTurnFailure;
|
|
210
|
-
/** 通知操作按钮与回调(「立即续跑」/「暂停该会话 1 小时」)。 */
|
|
211
|
-
private notifyOptions;
|
|
212
|
-
private onNotifyAction;
|
|
213
|
-
/** 恢复结果记账: 自动发送后窗口内的回合结束, 判定恢复成功或失败。 */
|
|
214
|
-
private noteRecovery;
|
|
215
|
-
/** 立即为该会话发送一次自动继续(无视冷却与连续上限; 由通知按钮触发)。 */
|
|
216
|
-
resumeNow(sessionId: SessionId): Promise<void>;
|
|
217
|
-
/** 本会话当前生效的冷却间隔(自适应退避)。 */
|
|
218
|
-
private cooldownFor;
|
|
219
|
-
private schedule;
|
|
220
|
-
private cancelPending;
|
|
221
|
-
private fire;
|
|
222
|
-
/**
|
|
223
|
-
* 组装本次续跑消息: 模板填充 + 幂等护栏。
|
|
224
|
-
* 护栏依据上一步工具调用的执行状态附加指引, 防止重跑副作用操作:
|
|
225
|
-
* - 结果未确认(可能已部分执行)→ 提示先确认状态、不要重复执行
|
|
226
|
-
* - 已确认成功 → 提示已完成、不要重复执行
|
|
227
|
-
* - 已失败 → 不加护栏(重试工具本来就是目的)
|
|
228
|
-
*/
|
|
229
|
-
private buildContinueText;
|
|
230
|
-
/** 上一步工具调用的护栏状态(实时路径, 由 mux 帧维护)。 */
|
|
231
|
-
private currentGuard;
|
|
232
|
-
/**
|
|
233
|
-
* 宿主权威兜底: 历史里最后一条事件是否就是同一文本的 user 消息。
|
|
234
|
-
* 是 = 它还在排队未被处理, 不应再叠加发送; 否(回合结束等其他事件)= 放行。
|
|
235
|
-
* 查询失败时返回 false(放行, 本地防线仍在)。
|
|
236
|
-
*/
|
|
237
|
-
private hostHasPendingSameText;
|
|
238
|
-
/** 会话标题缓存(来自 session.list 投影, {sessionTitle} 占位符用)。 */ private readonly titles;
|
|
239
|
-
/** 查一次 session.list, 顺带缓存该会话的标题。 */
|
|
240
|
-
private fetchSessionInfo;
|
|
241
|
-
private runningViaList;
|
|
242
|
-
private scheduleReconnectScan;
|
|
243
|
-
private bootScanLoop;
|
|
244
|
-
/** 反复尝试扫描, 直到成功(宿主就绪)或达到次数上限。 */
|
|
245
|
-
private scanLoop;
|
|
246
|
-
/**
|
|
247
|
-
* 扫描最近中断过的会话: 最后回合以非人为原因结束, 且其后没有新回合或用户消息。
|
|
248
|
-
* @returns 是否成功完成一次扫描(宿主就绪)。
|
|
249
|
-
*/
|
|
250
|
-
private scanInterrupted;
|
|
251
|
-
/** 从历史事件恢复上一步工具调用状态(扫描路径的幂等护栏)。 */
|
|
252
|
-
private applyGuardFromEvents;
|
|
253
|
-
}
|
|
8
|
+
export { DEFAULT_CONFIG, effectiveCooldown, fillTemplate, isTransientAgentError, isTransientFailure, resolveConfig, type AutoContinueConfig, type AutoContinueSettings, type DayStats, type FailureFacts, type TemplateContext, } from '../shared/core.ts';
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* action endpoint,
|
|
10
10
|
* - feeds the card's stats / paused-sessions panels from the bridge state.
|
|
11
11
|
*/
|
|
12
|
-
import type { ClientContext } from '@deepseek-ai/
|
|
12
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
13
13
|
import { type SettingsCardKey } from './locales.ts';
|
|
14
14
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
15
15
|
interface LocaleNamespaceMap {
|
|
@@ -31,10 +31,6 @@ export declare const zh: {
|
|
|
31
31
|
'field.scanLimitHint': string;
|
|
32
32
|
'field.freshMs': string;
|
|
33
33
|
'field.freshMsHint': string;
|
|
34
|
-
'field.reconnectScanDelayMs': string;
|
|
35
|
-
'field.reconnectScanDelayMsHint': string;
|
|
36
|
-
'field.reconnectBackoffMs': string;
|
|
37
|
-
'field.reconnectBackoffMsHint': string;
|
|
38
34
|
'field.verbose': string;
|
|
39
35
|
'field.verboseHint': string;
|
|
40
36
|
'field.classify': string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type SettingsScope, type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
1
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
2
|
import { type AutoContinueSettings } from './engine.ts';
|
|
3
|
+
import { type SettingsScope, type SnapshotStore } from './dsh-store-compat.ts';
|
|
4
4
|
import { type CardActions, type CardFieldState, type CardShell } from './settings-form.ts';
|
|
5
5
|
/** What the auto-continue card renders. */
|
|
6
6
|
export interface AutoContinueSettingsCardState extends CardShell {
|
|
@@ -16,8 +16,6 @@ export interface AutoContinueSettingsCardState extends CardShell {
|
|
|
16
16
|
scanOnBoot: CardFieldState;
|
|
17
17
|
scanLimit: CardFieldState;
|
|
18
18
|
freshMs: CardFieldState;
|
|
19
|
-
reconnectScanDelayMs: CardFieldState;
|
|
20
|
-
reconnectBackoffMs: CardFieldState;
|
|
21
19
|
verbose: CardFieldState;
|
|
22
20
|
classify: CardFieldState;
|
|
23
21
|
backoffFactor: CardFieldState;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* default — and whether the user layer carries it (presence, not value
|
|
11
11
|
* equality, marks an override).
|
|
12
12
|
*/
|
|
13
|
-
import type { SettingsScope, SnapshotStore } from '
|
|
13
|
+
import type { SettingsScope, SnapshotStore } from './dsh-store-compat.ts';
|
|
14
14
|
/** The write one field's staged text performs when the card is saved. */
|
|
15
15
|
export type FieldWrite = {
|
|
16
16
|
kind: 'set';
|
|
@@ -13,147 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
15
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
16
|
-
|
|
17
|
-
export interface AutoContinueSettings {
|
|
18
|
-
/** Text automatically sent after an interruption. */
|
|
19
|
-
continueText?: string;
|
|
20
|
-
/** Text sent when the output token ceiling is reached (same placeholders as `continueText`). */
|
|
21
|
-
continueTextMaxTokens?: string;
|
|
22
|
-
/** Idempotency guard: inspect the last tool call before resuming and steer the model. */
|
|
23
|
-
guardTools?: boolean;
|
|
24
|
-
/** Guard text appended when the last tool call has no confirmed result (it may have partially executed). */
|
|
25
|
-
guardPendingText?: string;
|
|
26
|
-
/** Guard text appended when the last tool call completed successfully (don't rerun it). */
|
|
27
|
-
guardDoneText?: string;
|
|
28
|
-
/** Grace period after an interruption before auto-sending (ms). */
|
|
29
|
-
graceMs?: number;
|
|
30
|
-
/** Minimum interval between two auto-continues per session (ms). */
|
|
31
|
-
cooldownMs?: number;
|
|
32
|
-
/** Max consecutive auto-continues per session before stopping. */
|
|
33
|
-
maxConsecutive?: number;
|
|
34
|
-
/** Scan recently interrupted sessions on page load / reconnect. */
|
|
35
|
-
scanOnBoot?: boolean;
|
|
36
|
-
/** Max sessions the scan checks (most recently updated). */
|
|
37
|
-
scanLimit?: number;
|
|
38
|
-
/** Scan only considers interruptions inside this window (ms). */
|
|
39
|
-
freshMs?: number;
|
|
40
|
-
/** Delay before scanning after a reconnect (ms). */
|
|
41
|
-
reconnectScanDelayMs?: number;
|
|
42
|
-
/** SSE reconnect backoff (ms). */
|
|
43
|
-
reconnectBackoffMs?: number;
|
|
44
|
-
/** Log `[auto-continue]` lines to the browser console. */
|
|
45
|
-
verbose?: boolean;
|
|
46
|
-
/** Classify failures: auto-continue transient errors only; permanent ones (auth/balance/model) are skipped and notified. */
|
|
47
|
-
classify?: boolean;
|
|
48
|
-
/** Cooldown multiplier per consecutive failure (adaptive backoff). */
|
|
49
|
-
backoffFactor?: number;
|
|
50
|
-
/** Cap on the effective backoff interval (ms). */
|
|
51
|
-
backoffMaxMs?: number;
|
|
52
|
-
/** Show browser notifications for auto-continue events. */
|
|
53
|
-
notify?: boolean;
|
|
54
|
-
/** Globally pause auto-continue: no live or scan send, queued pending sends cancelled. */
|
|
55
|
-
paused?: boolean;
|
|
56
|
-
/** Loop guard: detect a running turn spinning in place (short talk without tools, or the same tool repeating) and restart it. */
|
|
57
|
-
loopGuard?: boolean;
|
|
58
|
-
/** A model message shorter than this many chars counts as a "short sentence" (loop signal). */
|
|
59
|
-
loopShortChars?: number;
|
|
60
|
-
/** Consecutive short sentences within this window (ms) with no tool call in between trip the loop guard. */
|
|
61
|
-
loopWindowMs?: number;
|
|
62
|
-
/** Consecutive short sentences trip the loop guard. */
|
|
63
|
-
loopShortCount?: number;
|
|
64
|
-
/** Consecutive identical tool calls with identical arguments AND identical results trip the loop guard. */
|
|
65
|
-
loopToolRepeat?: number;
|
|
66
|
-
/** Consecutive identical short sentences trip the loop guard (strongest spinning signal). */
|
|
67
|
-
loopRepeatText?: number;
|
|
68
|
-
/** Text sent after the loop guard cancels and restarts a turn (supports {tool}). */
|
|
69
|
-
loopText?: string;
|
|
70
|
-
}
|
|
71
|
-
/** Fully resolved configuration (built-in defaults + user overrides). */
|
|
72
|
-
export type AutoContinueConfig = Required<AutoContinueSettings>;
|
|
73
|
-
/** Built-in defaults — must match the host schema defaults in src/index.ts. */
|
|
74
|
-
export declare const DEFAULT_CONFIG: AutoContinueConfig;
|
|
75
|
-
/** Resolve a (possibly partial / not-yet-loaded) settings section to a full config. */
|
|
76
|
-
export declare function resolveConfig(section: AutoContinueSettings | undefined): AutoContinueConfig;
|
|
77
|
-
/** 一次回合失败的机器可读事实(turn/end error 的 LlmFailure 载荷)。 */
|
|
78
|
-
export interface FailureFacts {
|
|
79
|
-
/** 稳定机器路由码(如 UPSTREAM、RATE_LIMIT_EXCEEDED、INVALID_API_KEY)。 */
|
|
80
|
-
code: string;
|
|
81
|
-
/** 人类可读的失败描述。 */
|
|
82
|
-
message: string;
|
|
83
|
-
/** 供应商 HTTP 状态码(可用时)。 */
|
|
84
|
-
status?: number;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* 错误分类: 该失败是否值得自动继续。
|
|
88
|
-
* 永久性失败(认证/余额/模型不存在/上下文超限等)重试也不会成功, 应跳过并通知用户;
|
|
89
|
-
* 其余(网络、超时、5xx、429 等)视为临时性失败, 允许自动恢复。
|
|
90
|
-
*/
|
|
91
|
-
export declare function isTransientFailure(failure: FailureFacts): boolean;
|
|
92
|
-
/**
|
|
93
|
-
* host/agent-error 消息分类: 仅明确属于网络/传输类的临时错误才自动继续。
|
|
94
|
-
* 其余(序列化失败、配置/宿主内部错误等)视为永久性——重试无益, 且用户停止导致的
|
|
95
|
-
* 序列化失败(如 Windows 下 abort 的 DOMException reason)绝不能自动续跑。
|
|
96
|
-
*/
|
|
97
|
-
export declare function isTransientAgentError(message: string): boolean;
|
|
98
|
-
/** 通知上的一个操作按钮(action 标识 + 显示文案)。 */
|
|
99
|
-
export interface NotifyAction {
|
|
100
|
-
/** 稳定动作标识, 点击时经 onAction 回调传出。 */
|
|
101
|
-
action: string;
|
|
102
|
-
/** 按钮显示文案。 */
|
|
103
|
-
title: string;
|
|
104
|
-
}
|
|
105
|
-
/** 通知的可选行为: 操作按钮列表与点击回调。 */
|
|
106
|
-
export interface NotifyOptions {
|
|
107
|
-
actions?: NotifyAction[];
|
|
108
|
-
onAction?: (action: string) => void;
|
|
109
|
-
}
|
|
110
|
-
/** 模板填充所需的上下文(全部可选, 缺失的占位符填为空串)。 */
|
|
111
|
-
export interface TemplateContext {
|
|
112
|
-
/** 失败事实(错误码/消息/HTTP 状态), 对应 {code}/{message}/{status}。 */
|
|
113
|
-
facts?: FailureFacts;
|
|
114
|
-
/** 失败前最后一次工具调用的名称, 对应 {tool}。 */
|
|
115
|
-
tool?: string;
|
|
116
|
-
/** 失败回合的编号, 对应 {turn}。 */
|
|
117
|
-
turn?: number;
|
|
118
|
-
/** 连续失败次数(含本次), 对应 {errorCount}。 */
|
|
119
|
-
errorCount?: number;
|
|
120
|
-
/** 会话标题(来自 session.list 投影, 可用时), 对应 {sessionTitle}。 */
|
|
121
|
-
sessionTitle?: string;
|
|
122
|
-
/** 自失败发生以来的毫秒数, 对应 {elapsed}。 */
|
|
123
|
-
elapsedMs?: number;
|
|
124
|
-
/** 上一步工具结果摘要(截断), 对应 {result}(护栏模板用)。 */
|
|
125
|
-
result?: string;
|
|
126
|
-
}
|
|
127
|
-
/** 用失败事实与回合信息填充 continueText 模板占位符({code}/{message}/{status}/{tool}/{turn}/{errorCount}/{sessionTitle}/{elapsed}/{result})。 */
|
|
128
|
-
export declare function fillTemplate(template: string, ctx: TemplateContext): string;
|
|
129
|
-
/** 上一步工具调用的判定结果: 是否已确认完成, 以及文本摘要。 */
|
|
130
|
-
export interface ToolResultFacts {
|
|
131
|
-
/** 工具是否成功完成(内部失败或 isError 视为未成功)。 */
|
|
132
|
-
ok: boolean;
|
|
133
|
-
/** 工具输出的文本摘要(截断)。 */
|
|
134
|
-
excerpt: string;
|
|
135
|
-
}
|
|
136
|
-
/** 自适应退避: 同一会话连续失败时的有效冷却间隔。 */
|
|
137
|
-
export declare function effectiveCooldown(consecutive: number, base: number, factor: number, max: number): number;
|
|
138
|
-
/** 一天的自动继续统计(host 单实例内存态)。 */
|
|
139
|
-
export interface DayStats {
|
|
140
|
-
/** 本地日期 YYYY-MM-DD。 */
|
|
141
|
-
date: string;
|
|
142
|
-
/** 自动发送次数。 */
|
|
143
|
-
sent: number;
|
|
144
|
-
/** 因永久性错误跳过的次数。 */
|
|
145
|
-
skipped: number;
|
|
146
|
-
/** 发送后回合成功完成(恢复成功)的次数。 */
|
|
147
|
-
recovered: number;
|
|
148
|
-
/** 发送后再次失败的次数。 */
|
|
149
|
-
failed: number;
|
|
150
|
-
/** 达到连续上限而停止的次数(按停止事件计)。 */
|
|
151
|
-
gaveUp: number;
|
|
152
|
-
/** loop guard 打断并重启回合的次数。 */
|
|
153
|
-
looped: number;
|
|
154
|
-
/** 按错误码计数的失败分布。 */
|
|
155
|
-
byCode: Record<string, number>;
|
|
156
|
-
}
|
|
16
|
+
import { type AutoContinueConfig, type DayStats, type NotifyAction } from '../shared/core.ts';
|
|
157
17
|
/** 通知桥事件: host 引擎产生, browser 侧订阅展示(Notification / 动作按钮)。 */
|
|
158
18
|
export interface HostNotice {
|
|
159
19
|
/** 稳定标识(供 browser 去重)。 */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -37,10 +37,6 @@ export declare const AutoContinueSchema: z<Schemastery.ObjectS<{
|
|
|
37
37
|
scanLimit: z<number, number>;
|
|
38
38
|
/** Scan only considers interruptions inside this window (ms). */
|
|
39
39
|
freshMs: z<number, number>;
|
|
40
|
-
/** Delay before scanning after a reconnect (ms). */
|
|
41
|
-
reconnectScanDelayMs: z<number, number>;
|
|
42
|
-
/** SSE reconnect backoff (ms). */
|
|
43
|
-
reconnectBackoffMs: z<number, number>;
|
|
44
40
|
/** Log `[auto-continue]` lines to the browser console. */
|
|
45
41
|
verbose: z<boolean, boolean>;
|
|
46
42
|
/** Classify failures: auto-continue transient errors only; permanent ones are skipped and notified. */
|
|
@@ -90,10 +86,6 @@ export declare const AutoContinueSchema: z<Schemastery.ObjectS<{
|
|
|
90
86
|
scanLimit: z<number, number>;
|
|
91
87
|
/** Scan only considers interruptions inside this window (ms). */
|
|
92
88
|
freshMs: z<number, number>;
|
|
93
|
-
/** Delay before scanning after a reconnect (ms). */
|
|
94
|
-
reconnectScanDelayMs: z<number, number>;
|
|
95
|
-
/** SSE reconnect backoff (ms). */
|
|
96
|
-
reconnectBackoffMs: z<number, number>;
|
|
97
89
|
/** Log `[auto-continue]` lines to the browser console. */
|
|
98
90
|
verbose: z<boolean, boolean>;
|
|
99
91
|
/** Classify failures: auto-continue transient errors only; permanent ones are skipped and notified. */
|