dsh-client-auto-continue 0.4.4 → 0.5.1
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 +18 -5
- package/README.zh.md +18 -5
- package/docs/screenshots/05-plugins-view.png +0 -0
- package/docs/screenshots/06-card-fields.png +0 -0
- package/docs/screenshots/07-card-panels.png +0 -0
- package/lib/client.js +455 -22
- package/lib/client.js.map +2 -2
- package/lib/index.js +5 -1
- package/lib/types/client/engine.d.ts +77 -3
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/locales.d.ts +18 -0
- package/lib/types/client/settings-card.d.ts +3 -1
- package/lib/types/index.d.ts +8 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -27,13 +27,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
27
27
|
var index_exports = {};
|
|
28
28
|
__export(index_exports, {
|
|
29
29
|
apply: () => apply,
|
|
30
|
-
|
|
30
|
+
fillTemplate: () => fillTemplate,
|
|
31
|
+
inject: () => inject,
|
|
32
|
+
pauseSession: () => pauseSession,
|
|
33
|
+
pausedSessions: () => pausedSessions,
|
|
34
|
+
readTodayStats: () => readTodayStats,
|
|
35
|
+
resetTodayStats: () => resetTodayStats,
|
|
36
|
+
sessionPauseUntil: () => sessionPauseUntil,
|
|
37
|
+
unpauseSession: () => unpauseSession
|
|
31
38
|
});
|
|
32
39
|
module.exports = __toCommonJS(index_exports);
|
|
33
40
|
|
|
34
41
|
// src/client/engine.ts
|
|
35
42
|
var DEFAULT_CONFIG = {
|
|
36
43
|
continueText: "继续",
|
|
44
|
+
continueTextMaxTokens: "继续",
|
|
37
45
|
graceMs: 3e3,
|
|
38
46
|
cooldownMs: 2e4,
|
|
39
47
|
maxConsecutive: 3,
|
|
@@ -46,7 +54,8 @@ var DEFAULT_CONFIG = {
|
|
|
46
54
|
classify: true,
|
|
47
55
|
backoffFactor: 2,
|
|
48
56
|
backoffMaxMs: 3e5,
|
|
49
|
-
notify: false
|
|
57
|
+
notify: false,
|
|
58
|
+
paused: false
|
|
50
59
|
};
|
|
51
60
|
function numberOr(value, fallback) {
|
|
52
61
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
@@ -57,8 +66,10 @@ function booleanOr(value, fallback) {
|
|
|
57
66
|
function resolveConfig(section) {
|
|
58
67
|
const value = section ?? {};
|
|
59
68
|
const text = typeof value.continueText === "string" && value.continueText.trim() !== "" ? value.continueText : DEFAULT_CONFIG.continueText;
|
|
69
|
+
const maxTokensText = typeof value.continueTextMaxTokens === "string" && value.continueTextMaxTokens.trim() !== "" ? value.continueTextMaxTokens : DEFAULT_CONFIG.continueTextMaxTokens;
|
|
60
70
|
return {
|
|
61
71
|
continueText: text,
|
|
72
|
+
continueTextMaxTokens: maxTokensText,
|
|
62
73
|
graceMs: numberOr(value.graceMs, DEFAULT_CONFIG.graceMs),
|
|
63
74
|
cooldownMs: numberOr(value.cooldownMs, DEFAULT_CONFIG.cooldownMs),
|
|
64
75
|
maxConsecutive: Math.max(1, numberOr(value.maxConsecutive, DEFAULT_CONFIG.maxConsecutive)),
|
|
@@ -71,7 +82,8 @@ function resolveConfig(section) {
|
|
|
71
82
|
classify: booleanOr(value.classify, DEFAULT_CONFIG.classify),
|
|
72
83
|
backoffFactor: Math.max(1, numberOr(value.backoffFactor, DEFAULT_CONFIG.backoffFactor)),
|
|
73
84
|
backoffMaxMs: numberOr(value.backoffMaxMs, DEFAULT_CONFIG.backoffMaxMs),
|
|
74
|
-
notify: booleanOr(value.notify, DEFAULT_CONFIG.notify)
|
|
85
|
+
notify: booleanOr(value.notify, DEFAULT_CONFIG.notify),
|
|
86
|
+
paused: booleanOr(value.paused, DEFAULT_CONFIG.paused)
|
|
75
87
|
};
|
|
76
88
|
}
|
|
77
89
|
function isNonHumanReason(kind) {
|
|
@@ -87,24 +99,47 @@ function isTransientFailure(failure) {
|
|
|
87
99
|
function isTransientAgentError(message) {
|
|
88
100
|
return /network|timeout|timed ?out|econn|etimedout|socket|5\d\d|\b429\b|upstream|temporar/i.test(message);
|
|
89
101
|
}
|
|
90
|
-
function notify(title, body) {
|
|
102
|
+
function notify(title, body, options) {
|
|
91
103
|
try {
|
|
92
104
|
const N = globalThis.Notification;
|
|
93
105
|
if (typeof N === "undefined") return;
|
|
94
106
|
const permission = N.permission;
|
|
107
|
+
const create = () => {
|
|
108
|
+
const instance = new N(title, {
|
|
109
|
+
body,
|
|
110
|
+
...options?.actions !== void 0 && options.actions.length > 0 ? { actions: options.actions } : {}
|
|
111
|
+
});
|
|
112
|
+
const target = instance;
|
|
113
|
+
target.onclick = () => {
|
|
114
|
+
try {
|
|
115
|
+
globalThis.focus?.();
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
if (options?.onAction !== void 0) {
|
|
120
|
+
target.onaction = (event) => options.onAction?.(event.action);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
95
123
|
if (permission === "granted") {
|
|
96
|
-
|
|
124
|
+
create();
|
|
97
125
|
} else if (permission === "default") {
|
|
98
126
|
void N.requestPermission?.().then((result) => {
|
|
99
|
-
if (result === "granted")
|
|
127
|
+
if (result === "granted") create();
|
|
100
128
|
}).catch(() => {
|
|
101
129
|
});
|
|
102
130
|
}
|
|
103
131
|
} catch {
|
|
104
132
|
}
|
|
105
133
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
134
|
+
function formatElapsed(ms) {
|
|
135
|
+
if (ms === void 0 || !Number.isFinite(ms) || ms < 0) return "";
|
|
136
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
137
|
+
const s = Math.round(ms / 1e3);
|
|
138
|
+
if (s < 60) return `${s}s`;
|
|
139
|
+
return `${Math.floor(s / 60)}m${s % 60 > 0 ? `${s % 60}s` : ""}`;
|
|
140
|
+
}
|
|
141
|
+
function fillTemplate(template, ctx) {
|
|
142
|
+
return template.replace(/\{code\}/g, ctx.facts?.code ?? "").replace(/\{message\}/g, ctx.facts?.message ?? "").replace(/\{status\}/g, ctx.facts?.status !== void 0 ? String(ctx.facts.status) : "").replace(/\{tool\}/g, ctx.tool ?? "").replace(/\{turn\}/g, ctx.turn !== void 0 ? String(ctx.turn) : "").replace(/\{errorCount\}/g, ctx.errorCount !== void 0 ? String(ctx.errorCount) : "").replace(/\{sessionTitle\}/g, ctx.sessionTitle ?? "").replace(/\{elapsed\}/g, formatElapsed(ctx.elapsedMs));
|
|
108
143
|
}
|
|
109
144
|
function effectiveCooldown(consecutive, base, factor, max) {
|
|
110
145
|
const multiplier = Math.pow(factor, consecutive);
|
|
@@ -151,6 +186,92 @@ function writeLastSend(sessionId, at) {
|
|
|
151
186
|
} catch {
|
|
152
187
|
}
|
|
153
188
|
}
|
|
189
|
+
var pauseKey = (sessionId) => `${lockPrefix}pause:${sessionId}`;
|
|
190
|
+
function pauseSession(sessionId, ms) {
|
|
191
|
+
try {
|
|
192
|
+
localStorage.setItem(pauseKey(sessionId), String(Date.now() + ms));
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function unpauseSession(sessionId) {
|
|
197
|
+
try {
|
|
198
|
+
localStorage.removeItem(pauseKey(sessionId));
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function sessionPauseUntil(sessionId) {
|
|
203
|
+
try {
|
|
204
|
+
return Number(localStorage.getItem(pauseKey(sessionId)) ?? 0) || 0;
|
|
205
|
+
} catch {
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function pausedSessions() {
|
|
210
|
+
const out = [];
|
|
211
|
+
const now = Date.now();
|
|
212
|
+
try {
|
|
213
|
+
for (let i = 0; i < localStorage.length; i += 1) {
|
|
214
|
+
const key = localStorage.key(i);
|
|
215
|
+
if (key === null || !key.startsWith(`${lockPrefix}pause:`)) continue;
|
|
216
|
+
const sessionId = key.slice(lockPrefix.length + "pause:".length);
|
|
217
|
+
const until = Number(localStorage.getItem(key) ?? 0) || 0;
|
|
218
|
+
if (until > now) out.push({ sessionId, until });
|
|
219
|
+
else localStorage.removeItem(key);
|
|
220
|
+
}
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
var statsKey = `${lockPrefix}stats`;
|
|
226
|
+
var STATS_MAX_DAYS = 90;
|
|
227
|
+
function todayKey() {
|
|
228
|
+
const d = /* @__PURE__ */ new Date();
|
|
229
|
+
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
230
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
231
|
+
return `${d.getFullYear()}-${mm}-${dd}`;
|
|
232
|
+
}
|
|
233
|
+
function readStats() {
|
|
234
|
+
try {
|
|
235
|
+
const raw = localStorage.getItem(statsKey);
|
|
236
|
+
if (raw === null) return [];
|
|
237
|
+
const parsed = JSON.parse(raw);
|
|
238
|
+
if (!Array.isArray(parsed)) return [];
|
|
239
|
+
return parsed.filter(
|
|
240
|
+
(item) => typeof item === "object" && item !== null && typeof item.date === "string"
|
|
241
|
+
);
|
|
242
|
+
} catch {
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function writeStats(list) {
|
|
247
|
+
try {
|
|
248
|
+
localStorage.setItem(statsKey, JSON.stringify(list));
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function bumpStat(delta) {
|
|
253
|
+
const list = readStats();
|
|
254
|
+
let day = list.find((item) => item.date === todayKey());
|
|
255
|
+
if (day === void 0) {
|
|
256
|
+
day = { date: todayKey(), sent: 0, skipped: 0, recovered: 0, failed: 0, gaveUp: 0, byCode: {} };
|
|
257
|
+
list.unshift(day);
|
|
258
|
+
}
|
|
259
|
+
if (delta.sent !== void 0) day.sent += delta.sent;
|
|
260
|
+
if (delta.skipped !== void 0) day.skipped += delta.skipped;
|
|
261
|
+
if (delta.recovered !== void 0) day.recovered += delta.recovered;
|
|
262
|
+
if (delta.failed !== void 0) day.failed += delta.failed;
|
|
263
|
+
if (delta.gaveUp !== void 0) day.gaveUp += delta.gaveUp;
|
|
264
|
+
if (delta.code !== void 0) day.byCode[delta.code] = (day.byCode[delta.code] ?? 0) + 1;
|
|
265
|
+
writeStats(list.slice(0, STATS_MAX_DAYS));
|
|
266
|
+
}
|
|
267
|
+
function readTodayStats() {
|
|
268
|
+
const today = todayKey();
|
|
269
|
+
const found = readStats().find((item) => item.date === today);
|
|
270
|
+
return found ?? { date: today, sent: 0, skipped: 0, recovered: 0, failed: 0, gaveUp: 0, byCode: {} };
|
|
271
|
+
}
|
|
272
|
+
function resetTodayStats() {
|
|
273
|
+
writeStats(readStats().filter((item) => item.date !== todayKey()));
|
|
274
|
+
}
|
|
154
275
|
var freshState = () => ({
|
|
155
276
|
consecutive: 0,
|
|
156
277
|
lastAutoAt: 0,
|
|
@@ -161,9 +282,12 @@ var freshState = () => ({
|
|
|
161
282
|
queued: 0,
|
|
162
283
|
subagent: false,
|
|
163
284
|
lastFailure: void 0,
|
|
285
|
+
lastFailureAt: 0,
|
|
164
286
|
lastTool: void 0,
|
|
165
|
-
lastTurn: void 0
|
|
287
|
+
lastTurn: void 0,
|
|
288
|
+
pendingRecoveryAt: 0
|
|
166
289
|
});
|
|
290
|
+
var RECOVERY_WINDOW_MS = 10 * 60 * 1e3;
|
|
167
291
|
function isOurEcho(state, event) {
|
|
168
292
|
if (event.type !== "user/message") return false;
|
|
169
293
|
const message = event.data;
|
|
@@ -210,6 +334,8 @@ var AutoContinueRunner = class {
|
|
|
210
334
|
this.hostAbort = new AbortController();
|
|
211
335
|
this.disposed = false;
|
|
212
336
|
this.reconnectScans = 0;
|
|
337
|
+
/** 会话标题缓存(来自 session.list 投影, {sessionTitle} 占位符用)。 */
|
|
338
|
+
this.titles = /* @__PURE__ */ new Map();
|
|
213
339
|
const config = this.getConfig();
|
|
214
340
|
void this.runMux();
|
|
215
341
|
void this.runHost();
|
|
@@ -295,11 +421,14 @@ var AutoContinueRunner = class {
|
|
|
295
421
|
if (reason.kind === "completed") {
|
|
296
422
|
state.consecutive = 0;
|
|
297
423
|
state.lastFailure = void 0;
|
|
424
|
+
this.noteRecovery(sessionId, "completed");
|
|
298
425
|
} else if (reason.kind === "aborted") {
|
|
299
426
|
state.consecutive = 0;
|
|
427
|
+
state.pendingRecoveryAt = 0;
|
|
300
428
|
} else if (reason.kind === "blocked") {
|
|
301
429
|
} else if (reason.kind === "interrupted") {
|
|
302
430
|
state.consecutive = 0;
|
|
431
|
+
state.pendingRecoveryAt = 0;
|
|
303
432
|
} else if (reason.kind === "error") {
|
|
304
433
|
const error = reason.error;
|
|
305
434
|
state.lastFailure = {
|
|
@@ -308,8 +437,12 @@ var AutoContinueRunner = class {
|
|
|
308
437
|
...typeof error.status === "number" ? { status: error.status } : {}
|
|
309
438
|
};
|
|
310
439
|
state.lastTurn = event.data.turn;
|
|
440
|
+
state.lastFailureAt = Date.now();
|
|
441
|
+
this.noteRecovery(sessionId, "error");
|
|
311
442
|
this.onTurnFailure(sessionId, "turn/end:error", state.lastFailure);
|
|
312
443
|
} else if (reason.kind === "max-tokens") {
|
|
444
|
+
state.lastFailureAt = Date.now();
|
|
445
|
+
this.noteRecovery(sessionId, "error");
|
|
313
446
|
this.schedule(sessionId, "turn/end:max-tokens");
|
|
314
447
|
}
|
|
315
448
|
break;
|
|
@@ -340,8 +473,13 @@ var AutoContinueRunner = class {
|
|
|
340
473
|
this.log(`host/agent-error(${frame.sessionId}): ${frame.message}`);
|
|
341
474
|
if (this.getConfig().classify && !isTransientAgentError(frame.message)) {
|
|
342
475
|
this.log(`跳过 ${frame.sessionId}: 永久性 agent 错误 — ${frame.message}`);
|
|
476
|
+
bumpStat({ skipped: 1 });
|
|
343
477
|
if (this.getConfig().notify) {
|
|
344
|
-
notify(
|
|
478
|
+
notify(
|
|
479
|
+
"dsh-auto-continue: 未自动继续",
|
|
480
|
+
`${frame.sessionId}: 永久性 agent 错误 ${frame.message.slice(0, 120)}`,
|
|
481
|
+
this.notifyOptions(frame.sessionId)
|
|
482
|
+
);
|
|
345
483
|
}
|
|
346
484
|
break;
|
|
347
485
|
}
|
|
@@ -362,13 +500,61 @@ var AutoContinueRunner = class {
|
|
|
362
500
|
if (config.classify && !isTransientFailure(failure)) {
|
|
363
501
|
const summary = `${failure.code}${failure.status !== void 0 ? ` (HTTP ${failure.status})` : ""}`;
|
|
364
502
|
this.log(`跳过 ${sessionId}(${reason}): 永久性失败 ${summary} — ${failure.message}`);
|
|
503
|
+
bumpStat({ skipped: 1, code: failure.code });
|
|
365
504
|
if (config.notify) {
|
|
366
|
-
notify(
|
|
505
|
+
notify(
|
|
506
|
+
"dsh-auto-continue: 未自动继续",
|
|
507
|
+
`${sessionId}: 永久性错误 ${summary},需要人工处理`,
|
|
508
|
+
this.notifyOptions(sessionId)
|
|
509
|
+
);
|
|
367
510
|
}
|
|
368
511
|
return;
|
|
369
512
|
}
|
|
370
513
|
this.schedule(sessionId, reason);
|
|
371
514
|
}
|
|
515
|
+
/** 通知操作按钮与回调(「立即续跑」/「暂停该会话 1 小时」)。 */
|
|
516
|
+
notifyOptions(sessionId) {
|
|
517
|
+
return {
|
|
518
|
+
actions: [
|
|
519
|
+
{ action: "resume", title: "立即续跑" },
|
|
520
|
+
{ action: "pause1h", title: "暂停该会话 1 小时" }
|
|
521
|
+
],
|
|
522
|
+
onAction: (action) => this.onNotifyAction(sessionId, action)
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
onNotifyAction(sessionId, action) {
|
|
526
|
+
if (action === "resume") {
|
|
527
|
+
this.log(`通知按钮: 立即续跑 ${sessionId}`);
|
|
528
|
+
void this.resumeNow(sessionId);
|
|
529
|
+
} else if (action === "pause1h") {
|
|
530
|
+
this.log(`通知按钮: 暂停 ${sessionId} 1 小时`);
|
|
531
|
+
pauseSession(sessionId, 60 * 60 * 1e3);
|
|
532
|
+
this.cancelPending(sessionId, "通知按钮暂停该会话");
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/** 恢复结果记账: 自动发送后窗口内的回合结束, 判定恢复成功或失败。 */
|
|
536
|
+
noteRecovery(sessionId, outcome) {
|
|
537
|
+
const state = this.state(sessionId);
|
|
538
|
+
if (state.pendingRecoveryAt === 0) return;
|
|
539
|
+
if (Date.now() - state.pendingRecoveryAt > RECOVERY_WINDOW_MS) {
|
|
540
|
+
state.pendingRecoveryAt = 0;
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
state.pendingRecoveryAt = 0;
|
|
544
|
+
bumpStat(outcome === "completed" ? { recovered: 1 } : { failed: 1 });
|
|
545
|
+
this.log(`恢复结果(${sessionId}): ${outcome === "completed" ? "成功" : "失败"}`);
|
|
546
|
+
}
|
|
547
|
+
/** 立即为该会话发送一次自动继续(无视冷却与连续上限; 由通知按钮触发)。 */
|
|
548
|
+
async resumeNow(sessionId) {
|
|
549
|
+
if (this.disposed) return;
|
|
550
|
+
const state = this.state(sessionId);
|
|
551
|
+
if (state.subagent) return;
|
|
552
|
+
if (state.pendingTimer !== void 0) {
|
|
553
|
+
clearTimeout(state.pendingTimer);
|
|
554
|
+
state.pendingTimer = void 0;
|
|
555
|
+
}
|
|
556
|
+
await this.fire(sessionId, "manual:notification", true);
|
|
557
|
+
}
|
|
372
558
|
/** 本会话当前生效的冷却间隔(自适应退避)。 */
|
|
373
559
|
cooldownFor(state) {
|
|
374
560
|
const config = this.getConfig();
|
|
@@ -383,6 +569,14 @@ var AutoContinueRunner = class {
|
|
|
383
569
|
const state = this.state(sessionId);
|
|
384
570
|
const config = this.getConfig();
|
|
385
571
|
if (state.subagent) return;
|
|
572
|
+
if (config.paused) {
|
|
573
|
+
this.log(`跳过 ${sessionId}(${reason}): 全局暂停中`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (Date.now() < sessionPauseUntil(sessionId)) {
|
|
577
|
+
this.log(`跳过 ${sessionId}(${reason}): 会话暂停中`);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
386
580
|
if (state.pendingTimer !== void 0) return;
|
|
387
581
|
if (Date.now() - state.lastAttemptAt < this.cooldownFor(state)) return;
|
|
388
582
|
if (state.consecutive >= config.maxConsecutive) {
|
|
@@ -398,8 +592,9 @@ var AutoContinueRunner = class {
|
|
|
398
592
|
void this.fire(sessionId, reason);
|
|
399
593
|
}, config.graceMs);
|
|
400
594
|
state.pendingTimer = timer;
|
|
595
|
+
const template = reason.includes("max-tokens") ? config.continueTextMaxTokens : config.continueText;
|
|
401
596
|
this.log(
|
|
402
|
-
`检测到非人为中断 ${sessionId}(${reason}), ${config.graceMs}ms 后自动发送「${
|
|
597
|
+
`检测到非人为中断 ${sessionId}(${reason}), ${config.graceMs}ms 后自动发送「${template}」`
|
|
403
598
|
);
|
|
404
599
|
}
|
|
405
600
|
cancelPending(sessionId, why) {
|
|
@@ -409,7 +604,7 @@ var AutoContinueRunner = class {
|
|
|
409
604
|
state.pendingTimer = void 0;
|
|
410
605
|
this.log(`取消 ${sessionId} 的自动继续(${why})`);
|
|
411
606
|
}
|
|
412
|
-
async fire(sessionId, reason) {
|
|
607
|
+
async fire(sessionId, reason, force = false) {
|
|
413
608
|
if (this.disposed) return;
|
|
414
609
|
const state = this.state(sessionId);
|
|
415
610
|
const config = this.getConfig();
|
|
@@ -427,7 +622,7 @@ var AutoContinueRunner = class {
|
|
|
427
622
|
this.log(`跳过 ${sessionId}: 已有排队消息`);
|
|
428
623
|
return;
|
|
429
624
|
}
|
|
430
|
-
if (Date.now() - readLastSend(sessionId) < this.cooldownFor(state)) {
|
|
625
|
+
if (!force && Date.now() - readLastSend(sessionId) < this.cooldownFor(state)) {
|
|
431
626
|
this.log(`跳过 ${sessionId}: 其他标签页刚发送过`);
|
|
432
627
|
return;
|
|
433
628
|
}
|
|
@@ -435,7 +630,23 @@ var AutoContinueRunner = class {
|
|
|
435
630
|
this.log(`跳过 ${sessionId}: 其他标签页正在发送`);
|
|
436
631
|
return;
|
|
437
632
|
}
|
|
438
|
-
const
|
|
633
|
+
const template = reason.includes("max-tokens") ? config.continueTextMaxTokens : config.continueText;
|
|
634
|
+
let sessionTitle;
|
|
635
|
+
if (template.includes("{sessionTitle}")) {
|
|
636
|
+
sessionTitle = this.titles.get(sessionId);
|
|
637
|
+
if (sessionTitle === void 0) {
|
|
638
|
+
const info = await this.fetchSessionInfo(sessionId);
|
|
639
|
+
sessionTitle = info?.title;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const text = fillTemplate(template, {
|
|
643
|
+
facts: state.lastFailure,
|
|
644
|
+
tool: state.lastTool,
|
|
645
|
+
turn: state.lastTurn,
|
|
646
|
+
errorCount: state.consecutive + 1,
|
|
647
|
+
sessionTitle,
|
|
648
|
+
elapsedMs: state.lastFailureAt > 0 ? Date.now() - state.lastFailureAt : void 0
|
|
649
|
+
});
|
|
439
650
|
const zone = clientTimeZone();
|
|
440
651
|
state.lastAttemptAt = Date.now();
|
|
441
652
|
try {
|
|
@@ -450,15 +661,26 @@ var AutoContinueRunner = class {
|
|
|
450
661
|
state.consecutive += 1;
|
|
451
662
|
state.lastAutoAt = now;
|
|
452
663
|
state.lastSentText = text;
|
|
664
|
+
state.pendingRecoveryAt = now;
|
|
453
665
|
writeLastSend(sessionId, now);
|
|
666
|
+
bumpStat({ sent: 1, ...state.lastFailure !== void 0 ? { code: state.lastFailure.code } : {} });
|
|
454
667
|
this.log(`已自动发送「${text}」到 ${sessionId}(${reason}), 第 ${state.consecutive} 次连续`);
|
|
455
668
|
if (config.notify) {
|
|
456
|
-
notify(
|
|
669
|
+
notify(
|
|
670
|
+
"dsh-auto-continue: 已自动继续",
|
|
671
|
+
`${sessionId}: 已发送「${text}」(第 ${state.consecutive} 次连续)`,
|
|
672
|
+
this.notifyOptions(sessionId)
|
|
673
|
+
);
|
|
457
674
|
}
|
|
458
675
|
if (state.consecutive >= config.maxConsecutive) {
|
|
676
|
+
bumpStat({ gaveUp: 1 });
|
|
459
677
|
this.log(`达到连续上限 ${config.maxConsecutive} 次, 停止自动继续 ${sessionId}`);
|
|
460
678
|
if (config.notify) {
|
|
461
|
-
notify(
|
|
679
|
+
notify(
|
|
680
|
+
"dsh-auto-continue: 已停止自动继续",
|
|
681
|
+
`${sessionId}: 连续失败 ${state.consecutive} 次, 需要人工介入`,
|
|
682
|
+
this.notifyOptions(sessionId)
|
|
683
|
+
);
|
|
462
684
|
}
|
|
463
685
|
}
|
|
464
686
|
} else {
|
|
@@ -472,18 +694,26 @@ var AutoContinueRunner = class {
|
|
|
472
694
|
releaseSend(sessionId);
|
|
473
695
|
}
|
|
474
696
|
}
|
|
475
|
-
|
|
697
|
+
/** 查一次 session.list, 顺带缓存该会话的标题。 */
|
|
698
|
+
async fetchSessionInfo(sessionId) {
|
|
476
699
|
try {
|
|
477
700
|
const response = await this.api.sessions.list({});
|
|
478
701
|
if (!response.result.ok) return void 0;
|
|
479
702
|
const item = response.result.value.items.find(
|
|
480
703
|
(summary) => summary.sessionId === sessionId
|
|
481
704
|
);
|
|
482
|
-
|
|
705
|
+
if (item === void 0) return void 0;
|
|
706
|
+
const title = item.projections?.values?.title;
|
|
707
|
+
if (typeof title === "string" && title !== "") this.titles.set(sessionId, title);
|
|
708
|
+
return { running: item.running, title: typeof title === "string" ? title : void 0 };
|
|
483
709
|
} catch {
|
|
484
710
|
return void 0;
|
|
485
711
|
}
|
|
486
712
|
}
|
|
713
|
+
async runningViaList(sessionId) {
|
|
714
|
+
const info = await this.fetchSessionInfo(sessionId);
|
|
715
|
+
return info?.running;
|
|
716
|
+
}
|
|
487
717
|
// ---------- 启动/重连扫描 ----------
|
|
488
718
|
scheduleReconnectScan() {
|
|
489
719
|
this.reconnectScans += 1;
|
|
@@ -518,9 +748,14 @@ var AutoContinueRunner = class {
|
|
|
518
748
|
*/
|
|
519
749
|
async scanInterrupted() {
|
|
520
750
|
const config = this.getConfig();
|
|
751
|
+
if (config.paused) return true;
|
|
521
752
|
const response = await this.api.sessions.list({});
|
|
522
753
|
if (!response.result.ok) return false;
|
|
523
754
|
const items = response.result.value.items;
|
|
755
|
+
for (const summary of items) {
|
|
756
|
+
const title = summary.projections?.values?.title;
|
|
757
|
+
if (typeof title === "string" && title !== "") this.titles.set(summary.sessionId, title);
|
|
758
|
+
}
|
|
524
759
|
const candidates = items.filter((summary) => !summary.running && summary.parentSessionId === void 0).slice(0, config.scanLimit);
|
|
525
760
|
const now = Date.now();
|
|
526
761
|
for (const summary of candidates) {
|
|
@@ -529,6 +764,7 @@ var AutoContinueRunner = class {
|
|
|
529
764
|
if (state.pendingTimer !== void 0) continue;
|
|
530
765
|
if (state.consecutive >= config.maxConsecutive) continue;
|
|
531
766
|
if (now - state.lastAttemptAt < this.cooldownFor(state)) continue;
|
|
767
|
+
if (now < sessionPauseUntil(summary.sessionId)) continue;
|
|
532
768
|
let events;
|
|
533
769
|
try {
|
|
534
770
|
const page = await this.api.sessions.history({
|
|
@@ -572,8 +808,12 @@ var AutoContinueRunner = class {
|
|
|
572
808
|
var zh = {
|
|
573
809
|
"card.title": "自动继续",
|
|
574
810
|
"card.description": "请求因网络等原因(非人为)中断后, 自动发送「继续」续跑。",
|
|
811
|
+
"field.paused": "暂停自动继续",
|
|
812
|
+
"field.pausedHint": "全局暂停: 实时与扫描都不会再自动发送, 已排队的待发送也会取消。",
|
|
575
813
|
"field.continueText": "继续文本",
|
|
576
814
|
"field.continueTextHint": "中断后自动发送的消息内容。",
|
|
815
|
+
"field.continueTextMaxTokens": "超限时的继续文本",
|
|
816
|
+
"field.continueTextMaxTokensHint": "达到输出 token 上限时自动发送的文本, 支持与继续文本相同的占位符。",
|
|
577
817
|
"field.graceMs": "宽限期 (ms)",
|
|
578
818
|
"field.graceMsHint": "检测到中断后等待的时长; 期间宿主自行恢复则取消。",
|
|
579
819
|
"field.cooldownMs": "冷却时间 (ms)",
|
|
@@ -599,7 +839,21 @@ var zh = {
|
|
|
599
839
|
"field.backoffMaxMs": "最大退避间隔 (ms)",
|
|
600
840
|
"field.backoffMaxMsHint": "自适应退避的上限, 防止等待过久。",
|
|
601
841
|
"field.notify": "浏览器通知",
|
|
602
|
-
"field.notifyHint": "
|
|
842
|
+
"field.notifyHint": "自动继续成功/放弃/遇到永久性错误时弹出浏览器通知, 通知带「立即续跑」与「暂停该会话 1 小时」按钮。",
|
|
843
|
+
"stats.title": "今日统计",
|
|
844
|
+
"stats.sent": "自动继续",
|
|
845
|
+
"stats.skipped": "跳过(永久错误)",
|
|
846
|
+
"stats.recovered": "恢复成功",
|
|
847
|
+
"stats.failed": "继续后仍失败",
|
|
848
|
+
"stats.gaveUp": "停止(达上限)",
|
|
849
|
+
"stats.byCode": "错误码分布",
|
|
850
|
+
"stats.empty": "今天还没有自动继续记录。",
|
|
851
|
+
"stats.reset": "清零",
|
|
852
|
+
"pause.title": "已暂停会话",
|
|
853
|
+
"pause.none": "没有暂停中的会话。",
|
|
854
|
+
"pause.clearAll": "全部解除",
|
|
855
|
+
"pause.unpause": "解除",
|
|
856
|
+
"pause.minutes": "分钟",
|
|
603
857
|
"chrome.collapse": "收起设置",
|
|
604
858
|
"chrome.expand": "展开设置",
|
|
605
859
|
"chrome.unsaved": "未保存",
|
|
@@ -618,8 +872,12 @@ var zh = {
|
|
|
618
872
|
var en = {
|
|
619
873
|
"card.title": "Auto continue",
|
|
620
874
|
"card.description": "When a request is interrupted by a non-human cause, automatically send 「继续」 to resume.",
|
|
875
|
+
"field.paused": "Pause auto-continue",
|
|
876
|
+
"field.pausedHint": "Globally pause: no live or scan auto-send fires, and queued pending sends are cancelled.",
|
|
621
877
|
"field.continueText": "Continue text",
|
|
622
878
|
"field.continueTextHint": "Message automatically sent after an interruption.",
|
|
879
|
+
"field.continueTextMaxTokens": "Continue text (max tokens)",
|
|
880
|
+
"field.continueTextMaxTokensHint": "Text sent when the output token ceiling is reached; same placeholders as the continue text.",
|
|
623
881
|
"field.graceMs": "Grace period (ms)",
|
|
624
882
|
"field.graceMsHint": "Wait after an interruption; cancelled if the host recovers on its own.",
|
|
625
883
|
"field.cooldownMs": "Cooldown (ms)",
|
|
@@ -645,7 +903,21 @@ var en = {
|
|
|
645
903
|
"field.backoffMaxMs": "Max backoff (ms)",
|
|
646
904
|
"field.backoffMaxMsHint": "Cap on the adaptive backoff interval.",
|
|
647
905
|
"field.notify": "Browser notifications",
|
|
648
|
-
"field.notifyHint":
|
|
906
|
+
"field.notifyHint": 'Notify when auto-continue fires, gives up, or hits a permanent error; notifications carry "Resume now" and "Pause this session 1h" buttons.',
|
|
907
|
+
"stats.title": "Today's stats",
|
|
908
|
+
"stats.sent": "Auto-continued",
|
|
909
|
+
"stats.skipped": "Skipped (permanent)",
|
|
910
|
+
"stats.recovered": "Recovered",
|
|
911
|
+
"stats.failed": "Failed after",
|
|
912
|
+
"stats.gaveUp": "Gave up (cap)",
|
|
913
|
+
"stats.byCode": "By error code",
|
|
914
|
+
"stats.empty": "No auto-continue activity today.",
|
|
915
|
+
"stats.reset": "Reset",
|
|
916
|
+
"pause.title": "Paused sessions",
|
|
917
|
+
"pause.none": "No sessions paused.",
|
|
918
|
+
"pause.clearAll": "Clear all",
|
|
919
|
+
"pause.unpause": "Resume",
|
|
920
|
+
"pause.minutes": "min",
|
|
649
921
|
"chrome.collapse": "Hide settings",
|
|
650
922
|
"chrome.expand": "Show settings",
|
|
651
923
|
"chrome.unsaved": "Unsaved",
|
|
@@ -978,6 +1250,32 @@ var css = `
|
|
|
978
1250
|
.dshAcSelect:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
979
1251
|
.dshAcInvalid { color: var(--dsw-alias-label-error); margin: 0; font-size: 12px; line-height: 1.5; }
|
|
980
1252
|
.dshAcHint { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 1.5; }
|
|
1253
|
+
.dshAcPanel { border-top: 1px solid var(--dsw-alias-border-l2); flex-direction: column; gap: 8px; padding: 12px 0; display: flex; }
|
|
1254
|
+
.dshAcPanelHead { align-items: center; gap: 8px; display: flex; }
|
|
1255
|
+
.dshAcPanelTitle { color: var(--dsw-alias-label-primary); flex: 1; font-size: 13px; font-weight: 600; line-height: 1.5; }
|
|
1256
|
+
.dshAcStats { gap: 4px 16px; margin: 0; grid-template-columns: repeat(2, minmax(0, 1fr)); display: grid; }
|
|
1257
|
+
.dshAcStats > div { justify-content: space-between; gap: 8px; display: flex; }
|
|
1258
|
+
.dshAcStats dt { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 1.5; }
|
|
1259
|
+
.dshAcStats dd { color: var(--dsw-alias-label-primary); margin: 0; font-size: 12px; font-weight: 600; line-height: 1.5; }
|
|
1260
|
+
.dshAcCodes { flex-wrap: wrap; align-items: center; gap: 6px; display: flex; }
|
|
1261
|
+
.dshAcCode {
|
|
1262
|
+
white-space: nowrap;
|
|
1263
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
1264
|
+
color: var(--dsw-alias-label-secondary);
|
|
1265
|
+
border-radius: 999px;
|
|
1266
|
+
padding: 1px 8px;
|
|
1267
|
+
font-size: 11px;
|
|
1268
|
+
font-weight: 500;
|
|
1269
|
+
line-height: 17px;
|
|
1270
|
+
}
|
|
1271
|
+
.dshAcPauseList { flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; display: flex; }
|
|
1272
|
+
.dshAcPauseList li { align-items: center; gap: 8px; display: flex; }
|
|
1273
|
+
.dshAcPauseId {
|
|
1274
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1275
|
+
color: var(--dsw-alias-label-primary);
|
|
1276
|
+
font-size: 12px;
|
|
1277
|
+
line-height: 1.5;
|
|
1278
|
+
}
|
|
981
1279
|
`;
|
|
982
1280
|
function injectStyles() {
|
|
983
1281
|
if (typeof document === "undefined") return;
|
|
@@ -998,7 +1296,9 @@ var AutoContinueSettingsCardController = class {
|
|
|
998
1296
|
*/
|
|
999
1297
|
constructor(scope) {
|
|
1000
1298
|
this.form = new CardForm(scope, [
|
|
1299
|
+
booleanField("paused"),
|
|
1001
1300
|
textField("continueText"),
|
|
1301
|
+
textField("continueTextMaxTokens"),
|
|
1002
1302
|
numberField("graceMs", 0),
|
|
1003
1303
|
numberField("cooldownMs", 0),
|
|
1004
1304
|
numberField("maxConsecutive", 1),
|
|
@@ -1018,7 +1318,9 @@ var AutoContinueSettingsCardController = class {
|
|
|
1018
1318
|
projection() {
|
|
1019
1319
|
return {
|
|
1020
1320
|
...this.form.shell(),
|
|
1321
|
+
paused: this.form.field("paused"),
|
|
1021
1322
|
continueText: this.form.field("continueText"),
|
|
1323
|
+
continueTextMaxTokens: this.form.field("continueTextMaxTokens"),
|
|
1022
1324
|
graceMs: this.form.field("graceMs"),
|
|
1023
1325
|
cooldownMs: this.form.field("cooldownMs"),
|
|
1024
1326
|
maxConsecutive: this.form.field("maxConsecutive"),
|
|
@@ -1141,6 +1443,112 @@ function BooleanField(props) {
|
|
|
1141
1443
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dshAcHint", children: props.hint })
|
|
1142
1444
|
] });
|
|
1143
1445
|
}
|
|
1446
|
+
function LivePanels(props) {
|
|
1447
|
+
const { t } = props;
|
|
1448
|
+
const [, refresh] = (0, import_react.useState)(0);
|
|
1449
|
+
(0, import_react.useEffect)(() => {
|
|
1450
|
+
const timer = setInterval(() => refresh((value) => value + 1), 5e3);
|
|
1451
|
+
return () => clearInterval(timer);
|
|
1452
|
+
}, []);
|
|
1453
|
+
const stats = readTodayStats();
|
|
1454
|
+
const hasStats = stats.sent + stats.skipped + stats.recovered + stats.failed + stats.gaveUp > 0;
|
|
1455
|
+
const codes = Object.entries(stats.byCode).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
1456
|
+
const paused = pausedSessions();
|
|
1457
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "dshAcPanel", children: [
|
|
1459
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshAcPanelHead", children: [
|
|
1460
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dshAcPanelTitle", children: t("stats.title") }),
|
|
1461
|
+
hasStats ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1462
|
+
"button",
|
|
1463
|
+
{
|
|
1464
|
+
type: "button",
|
|
1465
|
+
className: "dshAcReset",
|
|
1466
|
+
onClick: () => {
|
|
1467
|
+
resetTodayStats();
|
|
1468
|
+
refresh((value) => value + 1);
|
|
1469
|
+
},
|
|
1470
|
+
children: t("stats.reset")
|
|
1471
|
+
}
|
|
1472
|
+
) : null
|
|
1473
|
+
] }),
|
|
1474
|
+
!hasStats ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dshAcHint", children: t("stats.empty") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1475
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "dshAcStats", children: [
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: t("stats.sent") }),
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: stats.sent })
|
|
1479
|
+
] }),
|
|
1480
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1481
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: t("stats.recovered") }),
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: stats.recovered })
|
|
1483
|
+
] }),
|
|
1484
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1485
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: t("stats.failed") }),
|
|
1486
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: stats.failed })
|
|
1487
|
+
] }),
|
|
1488
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1489
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: t("stats.skipped") }),
|
|
1490
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: stats.skipped })
|
|
1491
|
+
] }),
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: t("stats.gaveUp") }),
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: stats.gaveUp })
|
|
1495
|
+
] })
|
|
1496
|
+
] }),
|
|
1497
|
+
codes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshAcCodes", children: [
|
|
1498
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dshAcHint", children: [
|
|
1499
|
+
t("stats.byCode"),
|
|
1500
|
+
":"
|
|
1501
|
+
] }),
|
|
1502
|
+
codes.map(([code, count]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dshAcCode", children: [
|
|
1503
|
+
code,
|
|
1504
|
+
" ×",
|
|
1505
|
+
count
|
|
1506
|
+
] }, code))
|
|
1507
|
+
] }) : null
|
|
1508
|
+
] })
|
|
1509
|
+
] }),
|
|
1510
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "dshAcPanel", children: [
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshAcPanelHead", children: [
|
|
1512
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dshAcPanelTitle", children: t("pause.title") }),
|
|
1513
|
+
paused.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1514
|
+
"button",
|
|
1515
|
+
{
|
|
1516
|
+
type: "button",
|
|
1517
|
+
className: "dshAcReset",
|
|
1518
|
+
onClick: () => {
|
|
1519
|
+
for (const item of paused) unpauseSession(item.sessionId);
|
|
1520
|
+
refresh((value) => value + 1);
|
|
1521
|
+
},
|
|
1522
|
+
children: t("pause.clearAll")
|
|
1523
|
+
}
|
|
1524
|
+
) : null
|
|
1525
|
+
] }),
|
|
1526
|
+
paused.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dshAcHint", children: t("pause.none") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "dshAcPauseList", children: paused.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dshAcPauseId", children: [
|
|
1528
|
+
item.sessionId.slice(0, 8),
|
|
1529
|
+
"…"
|
|
1530
|
+
] }),
|
|
1531
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dshAcHint", children: [
|
|
1532
|
+
Math.max(1, Math.ceil((item.until - Date.now()) / 6e4)),
|
|
1533
|
+
" ",
|
|
1534
|
+
t("pause.minutes")
|
|
1535
|
+
] }),
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1537
|
+
"button",
|
|
1538
|
+
{
|
|
1539
|
+
type: "button",
|
|
1540
|
+
className: "dshAcReset",
|
|
1541
|
+
onClick: () => {
|
|
1542
|
+
unpauseSession(item.sessionId);
|
|
1543
|
+
refresh((value) => value + 1);
|
|
1544
|
+
},
|
|
1545
|
+
children: t("pause.unpause")
|
|
1546
|
+
}
|
|
1547
|
+
)
|
|
1548
|
+
] }, item.sessionId)) })
|
|
1549
|
+
] })
|
|
1550
|
+
] });
|
|
1551
|
+
}
|
|
1144
1552
|
function AutoContinueSettingsCard(props) {
|
|
1145
1553
|
const { t } = props;
|
|
1146
1554
|
const state = props.useAutoContinueSettingsCard((snapshot) => snapshot);
|
|
@@ -1156,6 +1564,18 @@ function AutoContinueSettingsCard(props) {
|
|
|
1156
1564
|
onSave: props.save,
|
|
1157
1565
|
onDiscard: props.discard,
|
|
1158
1566
|
children: [
|
|
1567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1568
|
+
BooleanField,
|
|
1569
|
+
{
|
|
1570
|
+
id: "auto-continue-paused",
|
|
1571
|
+
label: t("field.paused"),
|
|
1572
|
+
hint: t("field.pausedHint"),
|
|
1573
|
+
...shared,
|
|
1574
|
+
...state.paused,
|
|
1575
|
+
onEdit: (text) => props.edit("paused", text),
|
|
1576
|
+
onReset: () => props.resetField("paused")
|
|
1577
|
+
}
|
|
1578
|
+
),
|
|
1159
1579
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1160
1580
|
ValueField,
|
|
1161
1581
|
{
|
|
@@ -1168,6 +1588,18 @@ function AutoContinueSettingsCard(props) {
|
|
|
1168
1588
|
onReset: () => props.resetField("continueText")
|
|
1169
1589
|
}
|
|
1170
1590
|
),
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1592
|
+
ValueField,
|
|
1593
|
+
{
|
|
1594
|
+
id: "auto-continue-continue-text-max-tokens",
|
|
1595
|
+
label: t("field.continueTextMaxTokens"),
|
|
1596
|
+
hint: t("field.continueTextMaxTokensHint"),
|
|
1597
|
+
...shared,
|
|
1598
|
+
...state.continueTextMaxTokens,
|
|
1599
|
+
onEdit: (text) => props.edit("continueTextMaxTokens", text),
|
|
1600
|
+
onReset: () => props.resetField("continueTextMaxTokens")
|
|
1601
|
+
}
|
|
1602
|
+
),
|
|
1171
1603
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1172
1604
|
ValueField,
|
|
1173
1605
|
{
|
|
@@ -1332,7 +1764,8 @@ function AutoContinueSettingsCard(props) {
|
|
|
1332
1764
|
onEdit: (text) => props.edit("notify", text),
|
|
1333
1765
|
onReset: () => props.resetField("notify")
|
|
1334
1766
|
}
|
|
1335
|
-
)
|
|
1767
|
+
),
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LivePanels, { t })
|
|
1336
1769
|
]
|
|
1337
1770
|
}
|
|
1338
1771
|
);
|