dsh-client-auto-continue 0.11.0 → 0.11.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 +10 -4
- package/README.zh.md +10 -4
- package/docs/auto-continue-workflow-icons.LICENSE.txt +40 -0
- package/docs/auto-continue-workflow.archify.json +155 -0
- package/docs/auto-continue-workflow.en.archify.json +155 -0
- package/docs/auto-continue-workflow.en.html +14844 -0
- package/docs/auto-continue-workflow.en.svg +530 -0
- package/docs/auto-continue-workflow.html +14844 -0
- package/docs/auto-continue-workflow.svg +530 -0
- package/docs/screenshots/02-settings-card.png +0 -0
- package/lib/client.js +0 -12
- package/lib/client.js.map +2 -2
- package/lib/index.js +3 -3
- package/lib/types/client/locales.d.ts +0 -1
- package/package.json +2 -2
- package/scripts/apply-workflow-icons.mjs +165 -0
- package/src/client/locales.ts +0 -2
- package/src/client/settings-card.tsx +0 -1
- package/src/client/styles.ts +0 -9
- package/src/index.ts +5 -5
package/lib/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import z2 from "@deepseek-ai/schemastery";
|
|
3
|
-
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
3
|
|
|
5
4
|
// node_modules/@deepseek-ai/dsh-llm/lib/index.js
|
|
6
5
|
import { createRequire } from "node:module";
|
|
@@ -949,6 +948,7 @@ ${event.data.arguments}`;
|
|
|
949
948
|
|
|
950
949
|
// src/index.ts
|
|
951
950
|
var AUTO_CONTINUE_NS = "auto-continue";
|
|
951
|
+
var SETTINGS_NS = AUTO_CONTINUE_NS;
|
|
952
952
|
var AutoContinueSchema = z2.object({
|
|
953
953
|
/** Active browser/UI locale mirrored by the client. */
|
|
954
954
|
locale: z2.string().default("zh"),
|
|
@@ -1005,7 +1005,7 @@ var AutoContinueSchema = z2.object({
|
|
|
1005
1005
|
});
|
|
1006
1006
|
function apply(ctx) {
|
|
1007
1007
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
1008
|
-
settingsCtx.settings.register(
|
|
1008
|
+
settingsCtx.settings.register(SETTINGS_NS, AutoContinueSchema, {
|
|
1009
1009
|
applies: "live"
|
|
1010
1010
|
});
|
|
1011
1011
|
});
|
|
@@ -1014,7 +1014,7 @@ function apply(ctx) {
|
|
|
1014
1014
|
if (runnerRef !== void 0) runnerRef.dispose();
|
|
1015
1015
|
const runner = new AutoContinueRunner(
|
|
1016
1016
|
engineCtx,
|
|
1017
|
-
() => resolveConfig(engineCtx.settings.get(
|
|
1017
|
+
() => resolveConfig(engineCtx.settings.get(SETTINGS_NS))
|
|
1018
1018
|
);
|
|
1019
1019
|
runnerRef = runner;
|
|
1020
1020
|
const sseClients = /* @__PURE__ */ new Set();
|
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 a localized continue prompt when a request is interrupted by network errors or other non-human causes",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"build": "node build.mjs && tsc -p tsconfig.build.json",
|
|
47
47
|
"watch": "node build.mjs --watch",
|
|
48
48
|
"typecheck": "tsc --noEmit",
|
|
49
|
-
"test": "node tests/simulate-host.mjs && node tests/simulate-client-loader.mjs && node tests/settings-card-ui.mjs && node tests/prepare-github-package.mjs",
|
|
49
|
+
"test": "node tests/simulate-host.mjs && node tests/settings-host-compat.mjs && node tests/simulate-client-loader.mjs && node tests/settings-card-ui.mjs && node tests/prepare-github-package.mjs",
|
|
50
50
|
"prepack": "npm run build"
|
|
51
51
|
},
|
|
52
52
|
"keywords": [
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const artifactPath = process.argv[2]
|
|
7
|
+
? path.resolve(process.cwd(), process.argv[2])
|
|
8
|
+
: path.resolve(scriptDir, '../docs/auto-continue-workflow.html');
|
|
9
|
+
|
|
10
|
+
// Lucide icon geometry uses viewBox="0 0 24 24". The surrounding Archify
|
|
11
|
+
// semantic class continues to own color, stroke width, theme, and interaction.
|
|
12
|
+
// Source and license: https://lucide.dev/ and
|
|
13
|
+
// docs/auto-continue-workflow-icons.LICENSE.txt.
|
|
14
|
+
const icons = {
|
|
15
|
+
turn_end: {
|
|
16
|
+
name: 'radio',
|
|
17
|
+
elements: [
|
|
18
|
+
'<path d="M16.247 7.761a6 6 0 0 1 0 8.478"/>',
|
|
19
|
+
'<path d="M19.075 4.933a10 10 0 0 1 0 14.134"/>',
|
|
20
|
+
'<path d="M4.925 19.067a10 10 0 0 1 0-14.134"/>',
|
|
21
|
+
'<path d="M7.753 16.239a6 6 0 0 1 0-8.478"/>',
|
|
22
|
+
'<circle cx="12" cy="12" r="2"/>',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
classify_failure: {
|
|
26
|
+
name: 'scan-search',
|
|
27
|
+
elements: [
|
|
28
|
+
'<path d="M3 7V5a2 2 0 0 1 2-2h2"/>',
|
|
29
|
+
'<path d="M17 3h2a2 2 0 0 1 2 2v2"/>',
|
|
30
|
+
'<path d="M21 17v2a2 2 0 0 1-2 2h-2"/>',
|
|
31
|
+
'<path d="M7 21H5a2 2 0 0 1-2-2v-2"/>',
|
|
32
|
+
'<circle cx="12" cy="12" r="3"/>',
|
|
33
|
+
'<path d="m16 16-1.9-1.9"/>',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
safety_gate: {
|
|
37
|
+
name: 'shield-check',
|
|
38
|
+
elements: [
|
|
39
|
+
'<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/>',
|
|
40
|
+
'<path d="m9 12 2 2 4-4"/>',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
grace_wait: {
|
|
44
|
+
name: 'timer',
|
|
45
|
+
elements: [
|
|
46
|
+
'<line x1="10" x2="14" y1="2" y2="2"/>',
|
|
47
|
+
'<line x1="12" x2="15" y1="14" y2="11"/>',
|
|
48
|
+
'<circle cx="12" cy="14" r="8"/>',
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
compose_prompt: {
|
|
52
|
+
name: 'message-square-text',
|
|
53
|
+
elements: [
|
|
54
|
+
'<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/>',
|
|
55
|
+
'<path d="M7 11h10"/>',
|
|
56
|
+
'<path d="M7 15h6"/>',
|
|
57
|
+
'<path d="M7 7h8"/>',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
agent_followup: {
|
|
61
|
+
name: 'send',
|
|
62
|
+
elements: [
|
|
63
|
+
'<path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/>',
|
|
64
|
+
'<path d="m21.854 2.147-10.94 10.939"/>',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
recovery_feedback: {
|
|
68
|
+
name: 'circle-check-big',
|
|
69
|
+
elements: [
|
|
70
|
+
'<path d="M21.801 10A10 10 0 1 1 17 3.335"/>',
|
|
71
|
+
'<path d="m9 11 3 3L22 4"/>',
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
agent_cancel: {
|
|
75
|
+
name: 'circle-stop',
|
|
76
|
+
elements: [
|
|
77
|
+
'<circle cx="12" cy="12" r="10"/>',
|
|
78
|
+
'<rect x="9" y="9" width="6" height="6" rx="1"/>',
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
manual_stop: {
|
|
82
|
+
name: 'hand',
|
|
83
|
+
elements: [
|
|
84
|
+
'<path d="M18 11V6a2 2 0 0 0-2-2 2 2 0 0 0-2 2"/>',
|
|
85
|
+
'<path d="M14 10V4a2 2 0 0 0-2-2 2 2 0 0 0-2 2v2"/>',
|
|
86
|
+
'<path d="M10 10.5V6a2 2 0 0 0-2-2 2 2 0 0 0-2 2v8"/>',
|
|
87
|
+
'<path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/>',
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const ICON_SCALE = 0.4583333333;
|
|
93
|
+
const ICON_RENDERED_SIZE = 24 * ICON_SCALE;
|
|
94
|
+
|
|
95
|
+
function compactNumber(value) {
|
|
96
|
+
return Number(value.toFixed(4)).toString();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function replaceTextY(nodeHtml, selectorPattern, y) {
|
|
100
|
+
const pattern = new RegExp(`(<text ${selectorPattern}[^>]*\\sy=")[^"]+("[^>]*>)`);
|
|
101
|
+
if (!pattern.test(nodeHtml)) return nodeHtml;
|
|
102
|
+
return nodeHtml.replace(pattern, `$1${compactNumber(y)}$2`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let html = fs.readFileSync(artifactPath, 'utf8');
|
|
106
|
+
let applied = 0;
|
|
107
|
+
|
|
108
|
+
for (const [nodeId, icon] of Object.entries(icons)) {
|
|
109
|
+
const nodeStart = html.indexOf(`<g id="node-${nodeId}"`);
|
|
110
|
+
if (nodeStart === -1) throw new Error(`Missing workflow node: ${nodeId}`);
|
|
111
|
+
|
|
112
|
+
const nextNode = html.indexOf('<g id="node-', nodeStart + 1);
|
|
113
|
+
const edgeLabels = html.indexOf('<!-- Edge labels -->', nodeStart);
|
|
114
|
+
const nodeEnd = nextNode === -1 ? edgeLabels : Math.min(nextNode, edgeLabels);
|
|
115
|
+
if (nodeEnd === -1) throw new Error(`Missing workflow node boundary: ${nodeId}`);
|
|
116
|
+
|
|
117
|
+
let nodeHtml = html.slice(nodeStart, nodeEnd);
|
|
118
|
+
const rectMatch = nodeHtml.match(/<rect x="([^"]+)" y="([^"]+)" width="([^"]+)" height="([^"]+)"/);
|
|
119
|
+
if (!rectMatch) throw new Error(`Missing workflow node bounds: ${nodeId}`);
|
|
120
|
+
|
|
121
|
+
const [, rawX, rawY, rawWidth, rawHeight] = rectMatch;
|
|
122
|
+
const x = Number(rawX);
|
|
123
|
+
const y = Number(rawY);
|
|
124
|
+
const width = Number(rawWidth);
|
|
125
|
+
const height = Number(rawHeight);
|
|
126
|
+
const iconX = x + (width - ICON_RENDERED_SIZE) / 2;
|
|
127
|
+
const iconY = y + 5;
|
|
128
|
+
|
|
129
|
+
const sigilStart = nodeHtml.indexOf('<g aria-hidden="true" data-semantic-sigil=');
|
|
130
|
+
const nodeLabel = nodeHtml.indexOf('<text data-node-label=');
|
|
131
|
+
if (sigilStart === -1 || nodeLabel === -1 || sigilStart > nodeLabel) {
|
|
132
|
+
throw new Error(`Missing semantic sigil for node: ${nodeId}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const openEnd = nodeHtml.indexOf('>', sigilStart);
|
|
136
|
+
const closeStart = nodeHtml.indexOf('</g>', openEnd);
|
|
137
|
+
if (openEnd === -1 || closeStart === -1 || closeStart > nodeLabel) {
|
|
138
|
+
throw new Error(`Malformed semantic sigil for node: ${nodeId}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let openTag = nodeHtml.slice(sigilStart, openEnd + 1)
|
|
142
|
+
.replace(/\sdata-lucide-icon="[^"]*"/, '')
|
|
143
|
+
.replace(/\sdata-icon-layout="[^"]*"/, '')
|
|
144
|
+
.replace(/transform="[^"]*"/, `transform="translate(${compactNumber(iconX)} ${compactNumber(iconY)}) scale(${ICON_SCALE})"`)
|
|
145
|
+
.replace(/>$/, ` data-lucide-icon="${icon.name}" data-icon-layout="stacked">`);
|
|
146
|
+
const body = icon.elements.map((element) => ` ${element}`).join('\n');
|
|
147
|
+
nodeHtml = `${nodeHtml.slice(0, sigilStart)}${openTag}\n${body}\n ${nodeHtml.slice(closeStart)}`;
|
|
148
|
+
|
|
149
|
+
// Stack icon, title, detail, and optional tag on distinct baselines.
|
|
150
|
+
// Standard nodes are 52px high; tagged nodes use the extra 16px row.
|
|
151
|
+
nodeHtml = replaceTextY(nodeHtml, 'data-node-label="[^"]*"', y + 29);
|
|
152
|
+
nodeHtml = replaceTextY(nodeHtml, 'data-detail="context"', y + (height > 52 ? 46 : 44));
|
|
153
|
+
nodeHtml = replaceTextY(nodeHtml, 'data-detail="fine"', y + 61);
|
|
154
|
+
|
|
155
|
+
html = `${html.slice(0, nodeStart)}${nodeHtml}${html.slice(nodeEnd)}`;
|
|
156
|
+
applied += 1;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const attribution = '<!-- Node glyphs: Lucide Icons; see auto-continue-workflow-icons.LICENSE.txt -->';
|
|
160
|
+
if (!html.includes(attribution)) {
|
|
161
|
+
html = html.replace(' <!-- Nodes -->', ` <!-- Nodes -->\n ${attribution}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
fs.writeFileSync(artifactPath, html);
|
|
165
|
+
console.log(`Applied ${applied} Lucide workflow icons to ${artifactPath}`);
|
package/src/client/locales.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { LOCALIZED_TEXT_DEFAULTS } from '../shared/core.ts';
|
|
|
8
8
|
/** 简体中文词典(键集的事实来源)。 */
|
|
9
9
|
export const zh = {
|
|
10
10
|
'card.title': '自动继续',
|
|
11
|
-
'card.eyebrow': '中断接力器',
|
|
12
11
|
'card.description': '接住意外中断的请求,等现场稳定下来,再把对话交还给 Agent。',
|
|
13
12
|
'flow.interrupted': '检测中断',
|
|
14
13
|
'flow.grace': '安全等待',
|
|
@@ -118,7 +117,6 @@ export type SettingsCardKey = keyof typeof zh;
|
|
|
118
117
|
/** English dictionary, checked complete against the zh key set. */
|
|
119
118
|
export const en: Record<SettingsCardKey, string> = {
|
|
120
119
|
'card.title': 'Auto continue',
|
|
121
|
-
'card.eyebrow': 'Continuity relay',
|
|
122
120
|
'card.description': 'Catches an interrupted request, waits for things to settle, then hands the thread back to the agent.',
|
|
123
121
|
'flow.interrupted': 'Interrupted',
|
|
124
122
|
'flow.grace': 'Safe pause',
|
|
@@ -243,7 +243,6 @@ function SettingsCard(props: {
|
|
|
243
243
|
>
|
|
244
244
|
<span className="dshAcRelayMark"><RelayMark /></span>
|
|
245
245
|
<span className="dshAcHeadText">
|
|
246
|
-
<span className="dshAcEyebrow">{props.t('card.eyebrow')}</span>
|
|
247
246
|
<span className="dshAcName">{title}</span>
|
|
248
247
|
<span className="dshAcDescription">{props.t(props.descriptionKey)}</span>
|
|
249
248
|
<RelayJourney t={props.t} />
|
package/src/client/styles.ts
CHANGED
|
@@ -99,15 +99,6 @@ const css = `
|
|
|
99
99
|
animation: dshAcRelayTravel 1.8s cubic-bezier(.4, 0, .2, 1) infinite;
|
|
100
100
|
}
|
|
101
101
|
.dshAcHeadText { flex-direction: column; flex: 1; gap: 3px; min-width: 0; display: flex; }
|
|
102
|
-
.dshAcEyebrow {
|
|
103
|
-
color: color-mix(in srgb, var(--dsh-ac-violet) 76%, var(--dsw-alias-label-secondary));
|
|
104
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
105
|
-
font-size: 10px;
|
|
106
|
-
font-weight: 700;
|
|
107
|
-
line-height: 1.4;
|
|
108
|
-
letter-spacing: .12em;
|
|
109
|
-
text-transform: uppercase;
|
|
110
|
-
}
|
|
111
102
|
.dshAcName {
|
|
112
103
|
color: var(--dsw-alias-label-primary);
|
|
113
104
|
font-family: ui-rounded, "SF Pro Rounded", "Segoe UI", sans-serif;
|
package/src/index.ts
CHANGED
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
13
13
|
import z from '@deepseek-ai/schemastery';
|
|
14
|
-
import {
|
|
14
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
15
15
|
import { AutoContinueRunner } from './host/engine.ts';
|
|
16
16
|
import { resolveConfig, type AutoContinueSettings } from './shared/core.ts';
|
|
17
|
-
//
|
|
18
|
-
import type {} from '@deepseek-ai/dsh-settings';
|
|
17
|
+
// The type-only settings import also pulls in the `ctx.settings` Context augmentation.
|
|
19
18
|
// Type-only: pulls the `ctx.webServer` Context augmentation.
|
|
20
19
|
import type {} from '@deepseek-ai/dsh-host-webserver';
|
|
21
20
|
import type {} from '@deepseek-ai/dsh-agent';
|
|
@@ -23,6 +22,7 @@ import type {} from '@deepseek-ai/dsh-session';
|
|
|
23
22
|
|
|
24
23
|
/** Settings namespace of the auto-continue plugin (lowercase kebab-case). */
|
|
25
24
|
export const AUTO_CONTINUE_NS = 'auto-continue';
|
|
25
|
+
const SETTINGS_NS = AUTO_CONTINUE_NS as SettingsNamespace;
|
|
26
26
|
|
|
27
27
|
/** Wire schema; blank localized text fields tell resolveConfig() to select the active locale's defaults. */
|
|
28
28
|
export const AutoContinueSchema = z.object({
|
|
@@ -93,7 +93,7 @@ export const AutoContinueSchema = z.object({
|
|
|
93
93
|
*/
|
|
94
94
|
export function apply(ctx: Context): void {
|
|
95
95
|
ctx.inject(['settings'], (settingsCtx) => {
|
|
96
|
-
settingsCtx.settings.register(
|
|
96
|
+
settingsCtx.settings.register(SETTINGS_NS, AutoContinueSchema, {
|
|
97
97
|
applies: 'live',
|
|
98
98
|
});
|
|
99
99
|
});
|
|
@@ -108,7 +108,7 @@ export function apply(ctx: Context): void {
|
|
|
108
108
|
// 回调重入时先清理旧引擎, 避免定时器与监听器叠加。
|
|
109
109
|
if (runnerRef !== undefined) runnerRef.dispose();
|
|
110
110
|
const runner = new AutoContinueRunner(engineCtx, () =>
|
|
111
|
-
resolveConfig(engineCtx.settings.get(
|
|
111
|
+
resolveConfig(engineCtx.settings.get(SETTINGS_NS) as AutoContinueSettings | undefined),
|
|
112
112
|
);
|
|
113
113
|
runnerRef = runner;
|
|
114
114
|
|