@goodandready/dsh-goal 0.1.8 → 0.1.9
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 +118 -44
- package/README.zh.md +217 -0
- package/lib/client.js +146 -61
- package/lib/command-handler.js +76 -34
- package/lib/goal-engine.js +49 -5
- package/lib/index.js +28 -10
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -11,78 +11,124 @@
|
|
|
11
11
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
|
-
<!--
|
|
14
|
+
<!-- Обязательная кнопка перехода на витрину всех проектов -->
|
|
15
15
|
<p align="center">
|
|
16
|
-
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge
|
|
16
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/Все_проекты_автора-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="Все проекты автора"></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
|
-
<a href="
|
|
22
|
-
<a href="docs/README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
21
|
+
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
23
22
|
</p>
|
|
24
23
|
|
|
24
|
+
<!-- Обязательный блок поддержки проекта: локализуй текст под язык README -->
|
|
25
|
+
<table align="center">
|
|
26
|
+
<tr>
|
|
27
|
+
<td align="center">
|
|
28
|
+
⭐ <strong>If you like this plugin, please star it on GitHub</strong> — it shows me that the plugin is useful to you and motivates me to keep developing it.
|
|
29
|
+
<br><br>
|
|
30
|
+
🐛 <strong>If you find a bug or would like to request a feature</strong>, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
25
35
|
</div>
|
|
26
36
|
|
|
27
37
|
---
|
|
28
38
|
|
|
29
39
|
## ⚡ Overview & The Problem
|
|
30
40
|
|
|
31
|
-
Complex engineering tasks require multi-step autonomy: decomposing high-level objectives into milestones, executing
|
|
41
|
+
Complex engineering tasks require multi-step autonomy: decomposing high-level objectives into sequential milestones, executing iterations without requiring manual user re-prompting, and maintaining clear visibility into task progress.
|
|
42
|
+
|
|
43
|
+
Without an autonomous tracking framework, agents can lose context across turns, stall in passive loops, or fail to notify users when complex workflows stall.
|
|
32
44
|
|
|
33
|
-
**`@goodandready/dsh-goal`**
|
|
34
|
-
* 🎯 **Sticky Top Goal Banner**: Pinned
|
|
35
|
-
* ⏸️ **Play / Pause / Cancel**: Instantly pause the autonomous loop or resume execution on demand.
|
|
36
|
-
* 📋 **Milestone Breakdown
|
|
37
|
-
*
|
|
38
|
-
*
|
|
45
|
+
**`@goodandready/dsh-goal`** introduces Goal Mode to DeepSeek Harness:
|
|
46
|
+
* 🎯 **Sticky Top Goal Banner**: Pinned header over composer dock with live elapsed timer (`• 2s`, `• 1m 45s`), real-time status badge (`RUNNING`, `PAUSED`, `COMPLETED`), active goal title, and control actions.
|
|
47
|
+
* ⏸️ **Play / Pause / Resume / Cancel**: Instantly pause the autonomous loop or resume execution on demand via buttons or `/goal` command.
|
|
48
|
+
* 📋 **Milestone Breakdown & ETA**: Interactive checklist showing sub-tasks, completion status (`pending`, `in_progress`, `completed`, `failed`), progress bar, and dynamic completion ETA.
|
|
49
|
+
* 🌐 **Multi-Language Auto-Detection (*Added in v0.1.9*)**: Prompt injection, autonomous follow-ups, and UI badges automatically follow the user's input language (English by default, Chinese, or Russian when entered in Cyrillic).
|
|
50
|
+
* 🔘 **Quick Launch Button (*Added in v0.1.8*)**: Fast goal launcher docked above the message input with instant objective prompt modal. Toggleable in settings.
|
|
51
|
+
* 📊 **Token Usage Tracking & Markdown Export (*Added in v0.1.7*)**: Accumulated prompt, completion, and total tokens tracked per session with one-click Markdown summary export.
|
|
52
|
+
* 🤖 **Autonomous Agent Contract**: Provides `goal_set_milestones`, `goal_update_progress`, and `goal_finish` tools directly to the agent.
|
|
53
|
+
* 🛡️ **Safety Guardrails**: Configurable `maxIterations` safety limit and Smart Progress Guard to catch and pause idle turns without progress.
|
|
54
|
+
* 🔔 **Web Audio Chimes**: Pleasant synthesized audio feedback on goal completion or failure via Web Audio API.
|
|
39
55
|
|
|
40
56
|
---
|
|
41
57
|
|
|
42
58
|
## 🏛️ Architecture
|
|
43
59
|
|
|
44
60
|
```mermaid
|
|
45
|
-
graph
|
|
46
|
-
subgraph
|
|
61
|
+
graph LR
|
|
62
|
+
subgraph UserInterface ["User Interface & Interaction"]
|
|
47
63
|
Cmd["Slash Command: /goal <objective>"]
|
|
48
|
-
|
|
64
|
+
Dock["Sticky Goal Banner (conversation.input.dock)"]
|
|
65
|
+
Modal["Goal Details & Milestone Modal"]
|
|
66
|
+
QuickLaunch["Quick Launch Goal Button"]
|
|
49
67
|
end
|
|
50
68
|
|
|
51
|
-
subgraph
|
|
69
|
+
subgraph GoalEngineCore ["Goal Engine Core (lib/goal-engine.js)"]
|
|
52
70
|
State["State Manager (IDLE, RUNNING, PAUSED, COMPLETED)"]
|
|
53
|
-
Milestones["Milestone Tracker &
|
|
54
|
-
|
|
71
|
+
Milestones["Milestone Tracker & ETA Calculation"]
|
|
72
|
+
DiskStore["Persistence Store (goal-state.json)"]
|
|
73
|
+
LangDetector["detectLanguage (en, zh, ru)"]
|
|
55
74
|
end
|
|
56
75
|
|
|
57
|
-
subgraph
|
|
76
|
+
subgraph AutonomousLoop ["Autonomous Drive & Turn Coordinator (lib/index.js)"]
|
|
77
|
+
TurnHook["ctx.on('turn/end') AutoDrive"]
|
|
78
|
+
Guard["Smart Progress Guard & Iteration Limiter"]
|
|
79
|
+
PromptInject["getStatePromptInjection"]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
subgraph AgentTools ["Cordis Registered Tools"]
|
|
58
83
|
ToolSet["goal_set_milestones"]
|
|
59
84
|
ToolProgress["goal_update_progress"]
|
|
60
85
|
ToolFinish["goal_finish"]
|
|
61
|
-
LimitGuard{"maxIterations Guard"}
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
subgraph UI ["DSH Web Interface"]
|
|
65
|
-
Banner["Sticky Top Goal Banner"]
|
|
66
|
-
Timer["Live Elapsed Timer"]
|
|
67
|
-
Drawer["Milestone Checklist Modal"]
|
|
68
|
-
Settings["Settings Card (Schemastery)"]
|
|
69
86
|
end
|
|
70
87
|
|
|
71
|
-
Cmd -->
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ToolProgress --> GoalEngine
|
|
81
|
-
ToolFinish --> GoalEngine
|
|
88
|
+
Cmd --> GoalEngineCore
|
|
89
|
+
Dock --> GoalEngineCore
|
|
90
|
+
QuickLaunch --> GoalEngineCore
|
|
91
|
+
GoalEngineCore --> DiskStore
|
|
92
|
+
GoalEngineCore --> AutonomousLoop
|
|
93
|
+
AutonomousLoop --> PromptInject
|
|
94
|
+
GoalEngineCore --> Modal
|
|
95
|
+
AgentTools --> GoalEngineCore
|
|
96
|
+
AutonomousLoop --> Guard
|
|
82
97
|
```
|
|
83
98
|
|
|
84
99
|
---
|
|
85
100
|
|
|
101
|
+
## ✨ Features & Module Breakdown
|
|
102
|
+
|
|
103
|
+
### 1. `lib/goal-engine.js` — State Engine
|
|
104
|
+
Zero external dependency core managing session goals, milestone states, elapsed time calculations, ETA forecasts, token accumulators, and crash recovery hydration.
|
|
105
|
+
* **Auto Language Detection**: Automatically analyzes goal title and parameters (`detectLanguage`) to select English (`en`), Chinese (`zh`), or Russian (`ru`).
|
|
106
|
+
* **ETA Estimator**: Predicts remaining time based on average milestone velocity:
|
|
107
|
+
$$\text{ETA} = \frac{\text{elapsed}}{\text{completedMilestones}} \times \text{remainingMilestones}$$
|
|
108
|
+
* **Low-Latency State Serialization**: Synchronous debounced atomic file persistence to prevent data loss on crashes.
|
|
109
|
+
|
|
110
|
+
### 2. `lib/command-handler.js` — Slash Commands
|
|
111
|
+
Handles `/goal` commands and subcommands:
|
|
112
|
+
* `/goal <objective>`: Starts a new autonomous goal.
|
|
113
|
+
* `/goal pause`: Pauses current goal and halts agent turn.
|
|
114
|
+
* `/goal resume`: Resumes execution and triggers agent continuation.
|
|
115
|
+
* `/goal clear`: Resets session goal to IDLE.
|
|
116
|
+
* `/goal`: Shows status, elapsed time, ETA, iterations, tokens, and active milestones.
|
|
117
|
+
|
|
118
|
+
### 3. `lib/index.js` — DSH Cordis Lifecycle Coordinator
|
|
119
|
+
* Registers REST API endpoints (`GET /dsh-goal/state`, `POST /dsh-goal/action`, `GET /dsh-goal/events` SSE stream).
|
|
120
|
+
* Subscribes to `turn/end` for zero-latency turn-to-turn auto-drive using `setImmediate`.
|
|
121
|
+
* Listens to `approval/asked` to automatically pause goal when operator confirmation is needed.
|
|
122
|
+
* Registers agent tools: `goal_set_milestones`, `goal_update_progress`, `goal_finish`.
|
|
123
|
+
|
|
124
|
+
### 4. `lib/client.js` — Frontend Web UI
|
|
125
|
+
* **Sticky Top Banner**: Mounts via slot `conversation.input.dock` with live timer, status badge, pause/resume, and details button.
|
|
126
|
+
* **Goal Details Modal**: Full milestone list, progress bar, token statistics, and 📋 Markdown Report Copy.
|
|
127
|
+
* **Quick Launch Button**: Floating launcher for rapid goal formulation without typing slash commands.
|
|
128
|
+
* **Plugin Settings Card**: Schemastery-backed settings UI registered via `settings.plugin.item`.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
86
132
|
## 📦 Installation
|
|
87
133
|
|
|
88
134
|
```bash
|
|
@@ -95,42 +141,70 @@ Restart your DeepSeek Harness instance and refresh the browser.
|
|
|
95
141
|
|
|
96
142
|
## 💬 Usage & Quick Start
|
|
97
143
|
|
|
98
|
-
Start a
|
|
144
|
+
### 1. Start a Goal via Chat
|
|
145
|
+
Simply enter the `/goal` slash command:
|
|
99
146
|
|
|
100
147
|
```text
|
|
101
|
-
/goal Refactor
|
|
148
|
+
/goal Refactor authentication middleware and cover with unit tests
|
|
102
149
|
```
|
|
103
150
|
|
|
104
|
-
|
|
151
|
+
The agent will immediately:
|
|
152
|
+
1. Establish a structured milestone plan via `goal_set_milestones`.
|
|
153
|
+
2. Advance through milestones, marking each `in_progress` and `completed` via `goal_update_progress`.
|
|
154
|
+
3. Conclude with a full summary via `goal_finish`.
|
|
155
|
+
|
|
156
|
+
### 2. Quick Launch Button
|
|
157
|
+
Click the **Start Goal** button directly above the message input box, type your objective, and click **Start Goal**.
|
|
158
|
+
|
|
159
|
+
### 3. REST API Control
|
|
160
|
+
Control goals programmatically via HTTP:
|
|
105
161
|
|
|
106
162
|
```bash
|
|
163
|
+
# Start a goal
|
|
107
164
|
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
108
165
|
-H "Content-Type: application/json" \
|
|
109
|
-
-d '{"action":"start","title":"
|
|
166
|
+
-d '{"action":"start","title":"Implement automated backup pipeline"}'
|
|
167
|
+
|
|
168
|
+
# Pause
|
|
169
|
+
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
170
|
+
-H "Content-Type: application/json" \
|
|
171
|
+
-d '{"action":"pause"}'
|
|
172
|
+
|
|
173
|
+
# Resume
|
|
174
|
+
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
175
|
+
-H "Content-Type: application/json" \
|
|
176
|
+
-d '{"action":"resume"}'
|
|
177
|
+
|
|
178
|
+
# Inspect live state
|
|
179
|
+
curl http://localhost:3080/dsh-goal/state
|
|
110
180
|
```
|
|
111
181
|
|
|
112
182
|
---
|
|
113
183
|
|
|
114
184
|
## ⚙️ Configuration Reference (`settings.yaml`)
|
|
115
185
|
|
|
186
|
+
Configure settings in `settings.yaml` or through the **Settings → Plugins → Goal Mode** UI card:
|
|
187
|
+
|
|
116
188
|
```yaml
|
|
117
189
|
dsh-goal:
|
|
118
190
|
maxIterations: 25
|
|
119
191
|
autoDrive: true
|
|
120
192
|
enableSound: true
|
|
193
|
+
showQuickLaunchButton: true
|
|
121
194
|
```
|
|
122
195
|
|
|
123
196
|
| Parameter | Type | Default | Description |
|
|
124
197
|
|:---|:---|:---|:---|
|
|
125
|
-
| `maxIterations` | `number` | `25` | Safety limit: maximum autonomous
|
|
198
|
+
| `maxIterations` | `number` | `25` | Safety limit: maximum autonomous turns per goal |
|
|
126
199
|
| `autoDrive` | `boolean` | `true` | Keep the autonomous agent loop running between turns |
|
|
127
|
-
| `enableSound` | `boolean` | `true` | Play
|
|
200
|
+
| `enableSound` | `boolean` | `true` | Play audio chime when a goal completes or fails |
|
|
201
|
+
| `showQuickLaunchButton` | `boolean` | `true` | Show the quick launch goal button above the composer dock |
|
|
128
202
|
|
|
129
203
|
---
|
|
130
204
|
|
|
131
205
|
## 🧪 Testing
|
|
132
206
|
|
|
133
|
-
Run
|
|
207
|
+
Run unit and integration tests:
|
|
134
208
|
|
|
135
209
|
```bash
|
|
136
210
|
npm test
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# 📦 @goodandready/dsh-goal
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<h3>DeepSeek Harness 自主目标执行与多轮任务跟踪引擎</h3>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-goal"><img src="https://img.shields.io/npm/v/@goodandready/dsh-goal.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-goal.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
|
|
10
|
+
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
|
|
11
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<!-- Обязательная кнопка перехода на витрину всех проектов -->
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/Все_проекты_автора-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="Все проекты автора"></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
|
+
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<!-- Обязательный блок поддержки проекта: локализуй текст под язык README -->
|
|
25
|
+
<table align="center">
|
|
26
|
+
<tr>
|
|
27
|
+
<td align="center">
|
|
28
|
+
⭐ <strong>如果您喜欢这个插件,请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
|
|
29
|
+
<br><br>
|
|
30
|
+
🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ⚡ 概述与解决的问题
|
|
40
|
+
|
|
41
|
+
复杂的工程开发任务需要多步骤的自主推进:将高层目标分解为有序的里程碑、在各轮交互间无需用户重复提示即可持续执行,并保持清晰直观的进度可视化。
|
|
42
|
+
|
|
43
|
+
如果缺乏目标跟踪框架,智能体容易在多轮对话中丢失上下文、陷入被动等待,或在遇到卡点时无法及时提醒用户。
|
|
44
|
+
|
|
45
|
+
**`@goodandready/dsh-goal`** 为 DeepSeek Harness 带来了完整的目标模式(Goal Mode):
|
|
46
|
+
* 🎯 **输入框上方常驻目标横幅**:顶部常驻状态条,配备实时计时器(`• 2s`、`• 1m 45s`)、状态徽章(`RUNNING`、`PAUSED`、`COMPLETED`)、当前目标名称与控制操作。
|
|
47
|
+
* ⏸️ **播放 / 暂停 / 恢复 / 取消**:可通过按钮或 `/goal` 命令随时暂停自主循环,或在需要时恢复执行。
|
|
48
|
+
* 📋 **里程碑拆解与 ETA 预测**:交互式清单抽屉,展示子任务状态(`pending`、`in_progress`、`completed`、`failed`)、进度百分比与动态预估剩余时间。
|
|
49
|
+
* 🌐 **多语言智能识别(v0.1.9 新增)**:提示词注入、自主轮次追问和 UI 状态徽章自动匹配用户输入语言(默认英语,支持中文和俄语)。
|
|
50
|
+
* 🔘 **快速启动按钮(v0.1.8 新增)**:常驻于输入框上方的启动按钮,支持弹窗一键制定目标,可在设置中自由开关。
|
|
51
|
+
* 📊 **Token 消耗统计与 Markdown 导出(v0.1.7 新增)**:实时累计提示词、生成词及总 Token 消耗,支持一键复制完整 Markdown 报告。
|
|
52
|
+
* 🤖 **智能体自主协作工具**:向智能体直接提供 `goal_set_milestones`、`goal_update_progress` 和 `goal_finish` 工具。
|
|
53
|
+
* 🛡️ **安全防护机制**:支持自定义最大迭代次数(`maxIterations`)以及智能卡顿检测(Smart Progress Guard)。
|
|
54
|
+
* 🔔 **Web Audio 提示音**:任务完成或失败时,通过 Web Audio API 播放舒缓的合成音效。
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 🏛️ 架构图
|
|
59
|
+
|
|
60
|
+
```mermaid
|
|
61
|
+
graph LR
|
|
62
|
+
subgraph UserInterface ["用户界面与交互"]
|
|
63
|
+
Cmd["斜杠命令: /goal <目标>"]
|
|
64
|
+
Dock["常驻目标横幅 (conversation.input.dock)"]
|
|
65
|
+
Modal["目标详情与里程碑弹窗"]
|
|
66
|
+
QuickLaunch["快速启动目标按钮"]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
subgraph GoalEngineCore ["目标引擎核心 (lib/goal-engine.js)"]
|
|
70
|
+
State["状态管理 (IDLE, RUNNING, PAUSED, COMPLETED)"]
|
|
71
|
+
Milestones["里程碑跟踪与 ETA 计算"]
|
|
72
|
+
DiskStore["状态持久化存储 (goal-state.json)"]
|
|
73
|
+
LangDetector["语言检测器 (en, zh, ru)"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
subgraph AutonomousLoop ["自主循环与轮次协调器 (lib/index.js)"]
|
|
77
|
+
TurnHook["ctx.on('turn/end') 自动推进"]
|
|
78
|
+
Guard["卡顿防守与最大迭代限制"]
|
|
79
|
+
PromptInject["getStatePromptInjection 提示词注入"]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
subgraph AgentTools ["Cordis 注册工具"]
|
|
83
|
+
ToolSet["goal_set_milestones"]
|
|
84
|
+
ToolProgress["goal_update_progress"]
|
|
85
|
+
ToolFinish["goal_finish"]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
Cmd --> GoalEngineCore
|
|
89
|
+
Dock --> GoalEngineCore
|
|
90
|
+
QuickLaunch --> GoalEngineCore
|
|
91
|
+
GoalEngineCore --> DiskStore
|
|
92
|
+
GoalEngineCore --> AutonomousLoop
|
|
93
|
+
AutonomousLoop --> PromptInject
|
|
94
|
+
GoalEngineCore --> Modal
|
|
95
|
+
AgentTools --> GoalEngineCore
|
|
96
|
+
AutonomousLoop --> Guard
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ✨ 核心模块详解
|
|
102
|
+
|
|
103
|
+
### 1. `lib/goal-engine.js` — 状态引擎
|
|
104
|
+
纯 JavaScript 实现,零外部依赖,完整管理会话目标、里程碑状态、运行计时、ETA 预测、Token 累计与崩溃恢复水合。
|
|
105
|
+
* **语言自动识别**:根据目标文本自动选择英语 (`en`)、中文 (`zh`) 或俄语 (`ru`)。
|
|
106
|
+
* **ETA 预估计算**:基于已完成里程碑的平均耗时进行动态预估:
|
|
107
|
+
$$\text{ETA} = \frac{\text{已运行时间}}{\text{已完成里程碑数}} \times \text{剩余里程碑数}$$
|
|
108
|
+
* **原子防抖持久化**:采用安全的临时文件写入与重命名机制,避免进程中断导致数据损坏。
|
|
109
|
+
|
|
110
|
+
### 2. `lib/command-handler.js` — 命令处理
|
|
111
|
+
处理 `/goal` 斜杠命令及其子命令:
|
|
112
|
+
* `/goal <目标内容>`:启动新目标。
|
|
113
|
+
* `/goal pause`:暂停当前目标并中止智能体当前轮次。
|
|
114
|
+
* `/goal resume`:恢复执行并自动提示智能体继续。
|
|
115
|
+
* `/goal clear`:重置当前目标为 IDLE。
|
|
116
|
+
* `/goal`:展示状态、耗时、预估时间、迭代进度、Token 统计与里程碑列表。
|
|
117
|
+
|
|
118
|
+
### 3. `lib/index.js` — DSH Cordis 生命周期管理
|
|
119
|
+
* 注册 HTTP REST API(`GET /dsh-goal/state`、`POST /dsh-goal/action` 及 `GET /dsh-goal/events` SSE 实时流)。
|
|
120
|
+
* 监听 `turn/end` 事件,通过 `setImmediate` 实现轮次间的极低延迟自动驱动。
|
|
121
|
+
* 监听 `approval/asked` 事件,在需要操作员审批时自动暂停。
|
|
122
|
+
* 为智能体注册专属工具:`goal_set_milestones`、`goal_update_progress`、`goal_finish`。
|
|
123
|
+
|
|
124
|
+
### 4. `lib/client.js` — Web 前端界面
|
|
125
|
+
* **常驻目标横幅**:注入 `conversation.input.dock` 插槽,包含实时计时、状态徽章、暂停/恢复与详情按钮。
|
|
126
|
+
* **目标详情弹窗**:展示完整里程碑清单、进度条、Token 统计,并支持 📋 一键复制 Markdown 报告。
|
|
127
|
+
* **快速启动按钮**:输入框上方的便捷入口,免去手动输入命令的繁琐。
|
|
128
|
+
* **设置卡片**:基于 Schemastery 注册至 `settings.plugin.item` 的可视化设置面板。
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 📦 安装说明
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
dsh plugin --profile web add @goodandready/dsh-goal
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
安装完成后重启 DeepSeek Harness 实例并刷新浏览器即可。
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 💬 使用指南
|
|
143
|
+
|
|
144
|
+
### 1. 通过聊天输入启动目标
|
|
145
|
+
在聊天输入框中直接输入 `/goal` 命令:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
/goal 重构鉴权中间件并补充完整的单元测试
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
智能体将立即:
|
|
152
|
+
1. 通过 `goal_set_milestones` 制定清晰的步骤规划;
|
|
153
|
+
2. 逐项执行,并通过 `goal_update_progress` 标记 `in_progress` 与 `completed`;
|
|
154
|
+
3. 全部完成后调用 `goal_finish` 输出最终总结。
|
|
155
|
+
|
|
156
|
+
### 2. 快速启动按钮
|
|
157
|
+
点击输入框上方的 **启动目标** 按钮,在弹出的窗口中输入任务描述并点击确认。
|
|
158
|
+
|
|
159
|
+
### 3. 通过 REST API 控制
|
|
160
|
+
也可以通过 HTTP 请求远程控制目标:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# 启动目标
|
|
164
|
+
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
165
|
+
-H "Content-Type: application/json" \
|
|
166
|
+
-d '{"action":"start","title":"实现自动化备份流水线"}'
|
|
167
|
+
|
|
168
|
+
# 暂停目标
|
|
169
|
+
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
170
|
+
-H "Content-Type: application/json" \
|
|
171
|
+
-d '{"action":"pause"}'
|
|
172
|
+
|
|
173
|
+
# 恢复目标
|
|
174
|
+
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
175
|
+
-H "Content-Type: application/json" \
|
|
176
|
+
-d '{"action":"resume"}'
|
|
177
|
+
|
|
178
|
+
# 查看实时状态
|
|
179
|
+
curl http://localhost:3080/dsh-goal/state
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## ⚙️ 配置说明 (`settings.yaml`)
|
|
185
|
+
|
|
186
|
+
可在 `settings.yaml` 中配置,或在 **设置 → 插件 → 目标模式** 界面中调整:
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
dsh-goal:
|
|
190
|
+
maxIterations: 25
|
|
191
|
+
autoDrive: true
|
|
192
|
+
enableSound: true
|
|
193
|
+
showQuickLaunchButton: true
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
| 参数项 | 类型 | 默认值 | 说明 |
|
|
197
|
+
|:---|:---|:---|:---|
|
|
198
|
+
| `maxIterations` | `number` | `25` | 安全限制:每个目标允许执行的最大自主轮次 |
|
|
199
|
+
| `autoDrive` | `boolean` | `true` | 是否在轮次之间自动保持循环执行 |
|
|
200
|
+
| `enableSound` | `boolean` | `true` | 目标完成或失败时是否播放提示音效 |
|
|
201
|
+
| `showQuickLaunchButton` | `boolean` | `true` | 是否在输入框上方常驻快速启动按钮 |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 🧪 自动化测试
|
|
206
|
+
|
|
207
|
+
运行单元与集成测试套件:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm test
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 📄 开源许可
|
|
216
|
+
|
|
217
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
package/lib/client.js
CHANGED
|
@@ -789,14 +789,14 @@ window.__ModuleLoader__.load({
|
|
|
789
789
|
isCompleted
|
|
790
790
|
? React.createElement(IconCheck, { size: 18, className: 'cb-badge-ok' })
|
|
791
791
|
: React.createElement(IconTarget, { size: 18 }),
|
|
792
|
-
isCompleted ? (t('modalTitleCompleted') || '
|
|
792
|
+
isCompleted ? (t('modalTitleCompleted') || 'Goal completed: plan and results') : (t('modalTitle') || 'Goal plan and status'),
|
|
793
793
|
),
|
|
794
794
|
React.createElement(
|
|
795
795
|
'button',
|
|
796
796
|
{
|
|
797
797
|
className: 'dsh-goal-btn icon-only close',
|
|
798
798
|
onClick: onClose,
|
|
799
|
-
title: t('close') || '
|
|
799
|
+
title: t('close') || 'Close',
|
|
800
800
|
},
|
|
801
801
|
'✕',
|
|
802
802
|
),
|
|
@@ -817,39 +817,39 @@ window.__ModuleLoader__.load({
|
|
|
817
817
|
'div',
|
|
818
818
|
{ className: 'dsh-goal-stat-box' },
|
|
819
819
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedElapsed || '0s'),
|
|
820
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || '
|
|
820
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || 'Running time'),
|
|
821
821
|
),
|
|
822
822
|
React.createElement(
|
|
823
823
|
'div',
|
|
824
824
|
{ className: 'dsh-goal-stat-box' },
|
|
825
|
-
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? '
|
|
826
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || '
|
|
825
|
+
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? (t('done') || 'Done') : '—')),
|
|
826
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || 'ETA'),
|
|
827
827
|
),
|
|
828
828
|
React.createElement(
|
|
829
829
|
'div',
|
|
830
830
|
{ className: 'dsh-goal-stat-box', title: tokensTooltip },
|
|
831
831
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, tokensFormatted),
|
|
832
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || '
|
|
832
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || 'Tokens'),
|
|
833
833
|
),
|
|
834
834
|
React.createElement(
|
|
835
835
|
'div',
|
|
836
836
|
{ className: 'dsh-goal-stat-box' },
|
|
837
837
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.iterationsCount || 0}/${state.maxIterations || 25}`),
|
|
838
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, '
|
|
838
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('iterations') || 'Iterations'),
|
|
839
839
|
),
|
|
840
840
|
React.createElement(
|
|
841
841
|
'div',
|
|
842
842
|
{ className: 'dsh-goal-stat-box' },
|
|
843
843
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.progressPercent || 0}%`),
|
|
844
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, '
|
|
844
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('progress') || 'Progress'),
|
|
845
845
|
),
|
|
846
846
|
),
|
|
847
847
|
React.createElement(
|
|
848
848
|
'div',
|
|
849
849
|
{ style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 } },
|
|
850
|
-
React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || '
|
|
850
|
+
React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || 'Plan of work:'),
|
|
851
851
|
milestones.length === 0
|
|
852
|
-
? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || '
|
|
852
|
+
? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || 'Agent is preparing the plan of work...')
|
|
853
853
|
: milestones.map((m, i) =>
|
|
854
854
|
React.createElement(
|
|
855
855
|
'div',
|
|
@@ -1036,13 +1036,33 @@ window.__ModuleLoader__.load({
|
|
|
1036
1036
|
stateRef.current = state;
|
|
1037
1037
|
const lastStateRef = useRef(null);
|
|
1038
1038
|
|
|
1039
|
-
const
|
|
1039
|
+
const resolveLocale = () => {
|
|
1040
|
+
let active = 'en';
|
|
1040
1041
|
if (ctx?.locale?.getSnapshot) {
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
|
|
1042
|
+
const snapLoc = ctx.locale.getSnapshot();
|
|
1043
|
+
if (snapLoc?.active) {
|
|
1044
|
+
const code = snapLoc.active.toLowerCase();
|
|
1045
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1046
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1047
|
+
else active = 'en';
|
|
1048
|
+
}
|
|
1049
|
+
} else if (typeof navigator !== 'undefined' && navigator.language) {
|
|
1050
|
+
const code = navigator.language.toLowerCase();
|
|
1051
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1052
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1053
|
+
else active = 'en';
|
|
1044
1054
|
}
|
|
1045
|
-
|
|
1055
|
+
if (state?.lang) {
|
|
1056
|
+
if (state.lang === 'zh') active = 'zh';
|
|
1057
|
+
else if (state.lang === 'ru') active = 'ru';
|
|
1058
|
+
else if (state.lang === 'en') active = 'en';
|
|
1059
|
+
}
|
|
1060
|
+
return active;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
const t = (key) => {
|
|
1064
|
+
const active = resolveLocale();
|
|
1065
|
+
return LOCALES[active]?.[key] || LOCALES.en?.[key] || LOCALES.ru?.[key] || key;
|
|
1046
1066
|
};
|
|
1047
1067
|
|
|
1048
1068
|
useEffect(() => {
|
|
@@ -1310,7 +1330,7 @@ window.__ModuleLoader__.load({
|
|
|
1310
1330
|
{
|
|
1311
1331
|
className: `dsh-goal-badge ${isCompleted ? 'dsh-goal-badge-ok' : isPaused ? 'dsh-goal-badge-warn' : ''}`,
|
|
1312
1332
|
},
|
|
1313
|
-
isCompleted ? (t('goalCompleted') || '
|
|
1333
|
+
isCompleted ? (t('goalCompleted') || 'Goal completed') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Current goal'),
|
|
1314
1334
|
),
|
|
1315
1335
|
React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
|
|
1316
1336
|
),
|
|
@@ -1327,7 +1347,7 @@ window.__ModuleLoader__.load({
|
|
|
1327
1347
|
'button',
|
|
1328
1348
|
{
|
|
1329
1349
|
className: 'dsh-goal-btn icon-only',
|
|
1330
|
-
title: isPaused ? (t('resume') || '
|
|
1350
|
+
title: isPaused ? (t('resume') || 'Resume') : (t('pause') || 'Pause'),
|
|
1331
1351
|
onClick: () => handleAction(isPaused ? 'resume' : 'pause'),
|
|
1332
1352
|
},
|
|
1333
1353
|
isPaused ? React.createElement(IconPlay, { size: 14 }) : React.createElement(IconPause, { size: 14 }),
|
|
@@ -1337,7 +1357,7 @@ window.__ModuleLoader__.load({
|
|
|
1337
1357
|
'button',
|
|
1338
1358
|
{
|
|
1339
1359
|
className: 'dsh-goal-btn icon-only',
|
|
1340
|
-
title: t('details') || '
|
|
1360
|
+
title: t('details') || 'Expand goal details',
|
|
1341
1361
|
onClick: () => setIsModalOpen(true),
|
|
1342
1362
|
},
|
|
1343
1363
|
React.createElement(IconExpand, { size: 14 }),
|
|
@@ -1347,7 +1367,7 @@ window.__ModuleLoader__.load({
|
|
|
1347
1367
|
'button',
|
|
1348
1368
|
{
|
|
1349
1369
|
className: 'dsh-goal-btn icon-only close',
|
|
1350
|
-
title: t('closeBanner') || '
|
|
1370
|
+
title: t('closeBanner') || 'Close goal banner',
|
|
1351
1371
|
onClick: () => handleAction('clear'),
|
|
1352
1372
|
},
|
|
1353
1373
|
'✕',
|
|
@@ -1475,13 +1495,28 @@ window.__ModuleLoader__.load({
|
|
|
1475
1495
|
});
|
|
1476
1496
|
}, [scope]);
|
|
1477
1497
|
|
|
1478
|
-
const
|
|
1498
|
+
const resolveLocale = () => {
|
|
1499
|
+
let active = 'en';
|
|
1479
1500
|
if (ctx?.locale?.getSnapshot) {
|
|
1480
1501
|
const snapLoc = ctx.locale.getSnapshot();
|
|
1481
|
-
|
|
1482
|
-
|
|
1502
|
+
if (snapLoc?.active) {
|
|
1503
|
+
const code = snapLoc.active.toLowerCase();
|
|
1504
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1505
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1506
|
+
else active = 'en';
|
|
1507
|
+
}
|
|
1508
|
+
} else if (typeof navigator !== 'undefined' && navigator.language) {
|
|
1509
|
+
const code = navigator.language.toLowerCase();
|
|
1510
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1511
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1512
|
+
else active = 'en';
|
|
1483
1513
|
}
|
|
1484
|
-
return
|
|
1514
|
+
return active;
|
|
1515
|
+
};
|
|
1516
|
+
|
|
1517
|
+
const t = (key) => {
|
|
1518
|
+
const active = resolveLocale();
|
|
1519
|
+
return LOCALES[active]?.[key] || LOCALES.en[key] || LOCALES.ru?.[key] || key;
|
|
1485
1520
|
};
|
|
1486
1521
|
|
|
1487
1522
|
const status = (snap && snap.status) || 'ready';
|
|
@@ -1733,50 +1768,13 @@ window.__ModuleLoader__.load({
|
|
|
1733
1768
|
|
|
1734
1769
|
// --- СЛОВАРИ ЛОКАЛИЗАЦИИ ---
|
|
1735
1770
|
const LOCALES = {
|
|
1736
|
-
ru: {
|
|
1737
|
-
goalLabel: 'Текущая цель',
|
|
1738
|
-
goalCompleted: 'Цель выполнена',
|
|
1739
|
-
clearGoal: 'Очистить цель',
|
|
1740
|
-
closeBanner: 'Закрыть плашку цели',
|
|
1741
|
-
pause: 'Приостановить',
|
|
1742
|
-
resume: 'Возобновить',
|
|
1743
|
-
details: 'Развернуть детали цели',
|
|
1744
|
-
modalTitle: 'План и статус цели',
|
|
1745
|
-
modalTitleCompleted: 'Цель выполнена: план и результаты',
|
|
1746
|
-
time: 'Время работы',
|
|
1747
|
-
eta: 'Оценка (ETA)',
|
|
1748
|
-
tokens: 'Токены',
|
|
1749
|
-
milestones: 'План работ:',
|
|
1750
|
-
noMilestones: 'Агент формирует план работ...',
|
|
1751
|
-
close: 'Закрыть',
|
|
1752
|
-
pluginTitle: 'Цели и автономный режим (Goal Mode)',
|
|
1753
|
-
pluginDesc: 'Панель цели над полем ввода, декомпозиция вех и авто-драйв',
|
|
1754
|
-
maxIterLabel: 'Максимум итераций (Safety Limit):',
|
|
1755
|
-
autoDriveLabel: 'Авто-драйв: продолжать цикл автоматически',
|
|
1756
|
-
soundLabel: 'Звук по завершении цели',
|
|
1757
|
-
quickLaunchLabel: 'Кнопка быстрого запуска цели над полем ввода',
|
|
1758
|
-
quickLaunch: 'Запустить цель',
|
|
1759
|
-
quickLaunchTitle: 'Быстрый запуск цели',
|
|
1760
|
-
quickLaunchDesc: 'Сформулируйте задачу для агента в автономном режиме:',
|
|
1761
|
-
quickLaunchPlaceholder: 'Например: Реализовать экспорт отчетов и покрыть тестами...',
|
|
1762
|
-
startGoal: 'Запустить цель',
|
|
1763
|
-
copyReport: '📋 Скопировать отчёт в Markdown',
|
|
1764
|
-
reportCopied: '✅ Скопировано!',
|
|
1765
|
-
save: 'Сохранить',
|
|
1766
|
-
saving: 'Сохранение…',
|
|
1767
|
-
discard: 'Отменить правки',
|
|
1768
|
-
resetField: 'По умолчанию',
|
|
1769
|
-
overridden: 'изменено',
|
|
1770
|
-
saveFailed: 'Не сохранено — исправьте и повторите',
|
|
1771
|
-
loading: 'Загрузка настроек…',
|
|
1772
|
-
unavailable: 'Настройки недоступны',
|
|
1773
|
-
},
|
|
1774
1771
|
en: {
|
|
1775
1772
|
goalLabel: 'Current goal',
|
|
1776
1773
|
goalCompleted: 'Goal completed',
|
|
1777
1774
|
clearGoal: 'Clear goal',
|
|
1778
1775
|
closeBanner: 'Close goal banner',
|
|
1779
1776
|
pause: 'Pause',
|
|
1777
|
+
paused: 'Paused',
|
|
1780
1778
|
resume: 'Resume',
|
|
1781
1779
|
details: 'Expand Goal Details',
|
|
1782
1780
|
modalTitle: 'Goal Plan & Status',
|
|
@@ -1784,6 +1782,9 @@ window.__ModuleLoader__.load({
|
|
|
1784
1782
|
time: 'Running time',
|
|
1785
1783
|
eta: 'ETA',
|
|
1786
1784
|
tokens: 'Tokens',
|
|
1785
|
+
iterations: 'Iterations',
|
|
1786
|
+
progress: 'Progress',
|
|
1787
|
+
done: 'Done',
|
|
1787
1788
|
milestones: 'Plan of work:',
|
|
1788
1789
|
noMilestones: 'Agent is preparing the plan of work...',
|
|
1789
1790
|
close: 'Close',
|
|
@@ -1809,6 +1810,90 @@ window.__ModuleLoader__.load({
|
|
|
1809
1810
|
loading: 'Loading settings…',
|
|
1810
1811
|
unavailable: 'Settings unavailable',
|
|
1811
1812
|
},
|
|
1813
|
+
zh: {
|
|
1814
|
+
goalLabel: '当前目标',
|
|
1815
|
+
goalCompleted: '目标已完成',
|
|
1816
|
+
clearGoal: '清除目标',
|
|
1817
|
+
closeBanner: '关闭目标横幅',
|
|
1818
|
+
pause: '暂停',
|
|
1819
|
+
paused: '已暂停',
|
|
1820
|
+
resume: '恢复',
|
|
1821
|
+
details: '展开目标详情',
|
|
1822
|
+
modalTitle: '目标计划与状态',
|
|
1823
|
+
modalTitleCompleted: '目标已完成:计划与成果',
|
|
1824
|
+
time: '运行时间',
|
|
1825
|
+
eta: '预估剩余 (ETA)',
|
|
1826
|
+
tokens: 'Token 消耗',
|
|
1827
|
+
iterations: '迭代次数',
|
|
1828
|
+
progress: '进度',
|
|
1829
|
+
done: '已完成',
|
|
1830
|
+
milestones: '工作计划:',
|
|
1831
|
+
noMilestones: '智能体正在制定工作计划...',
|
|
1832
|
+
close: '关闭',
|
|
1833
|
+
pluginTitle: '目标模式与自主循环 (Goal Mode)',
|
|
1834
|
+
pluginDesc: '输入框上方常驻目标横幅、里程碑拆解与自主循环',
|
|
1835
|
+
maxIterLabel: '最大迭代次数 (Safety Limit):',
|
|
1836
|
+
autoDriveLabel: '自动执行:在各轮次间自动保持循环',
|
|
1837
|
+
soundLabel: '目标完成时播放提示音',
|
|
1838
|
+
quickLaunchLabel: '在输入框上方显示快速启动目标按钮',
|
|
1839
|
+
quickLaunch: '启动目标',
|
|
1840
|
+
quickLaunchTitle: '快速启动目标',
|
|
1841
|
+
quickLaunchDesc: '为自主模式下的智能体设定任务目标:',
|
|
1842
|
+
quickLaunchPlaceholder: '例如:实现报表导出功能并编写单元测试...',
|
|
1843
|
+
startGoal: '启动目标',
|
|
1844
|
+
copyReport: '📋 复制 Markdown 报告',
|
|
1845
|
+
reportCopied: '✅ 已复制!',
|
|
1846
|
+
save: '保存',
|
|
1847
|
+
saving: '保存中…',
|
|
1848
|
+
discard: '放弃更改',
|
|
1849
|
+
resetField: '恢复默认',
|
|
1850
|
+
overridden: '已修改',
|
|
1851
|
+
saveFailed: '保存失败 — 请检查并重试',
|
|
1852
|
+
loading: '正在加载设置…',
|
|
1853
|
+
unavailable: '设置不可用',
|
|
1854
|
+
},
|
|
1855
|
+
ru: {
|
|
1856
|
+
goalLabel: 'Текущая цель',
|
|
1857
|
+
goalCompleted: 'Цель выполнена',
|
|
1858
|
+
clearGoal: 'Очистить цель',
|
|
1859
|
+
closeBanner: 'Закрыть плашку цели',
|
|
1860
|
+
pause: 'Приостановить',
|
|
1861
|
+
paused: 'На паузе',
|
|
1862
|
+
resume: 'Возобновить',
|
|
1863
|
+
details: 'Развернуть детали цели',
|
|
1864
|
+
modalTitle: 'План и статус цели',
|
|
1865
|
+
modalTitleCompleted: 'Цель выполнена: план и результаты',
|
|
1866
|
+
time: 'Время работы',
|
|
1867
|
+
eta: 'Оценка (ETA)',
|
|
1868
|
+
tokens: 'Токены',
|
|
1869
|
+
iterations: 'Итерации',
|
|
1870
|
+
progress: 'Прогресс',
|
|
1871
|
+
done: 'Готово',
|
|
1872
|
+
milestones: 'План работ:',
|
|
1873
|
+
noMilestones: 'Агент формирует план работ...',
|
|
1874
|
+
close: 'Закрыть',
|
|
1875
|
+
pluginTitle: 'Цели и автономный режим (Goal Mode)',
|
|
1876
|
+
pluginDesc: 'Панель цели над полем ввода, декомпозиция вех и авто-драйв',
|
|
1877
|
+
maxIterLabel: 'Максимум итераций (Safety Limit):',
|
|
1878
|
+
autoDriveLabel: 'Авто-драйв: продолжать цикл автоматически',
|
|
1879
|
+
soundLabel: 'Звук по завершении цели',
|
|
1880
|
+
quickLaunchLabel: 'Кнопка быстрого запуска цели над полем ввода',
|
|
1881
|
+
quickLaunch: 'Запустить цель',
|
|
1882
|
+
quickLaunchTitle: 'Быстрый запуск цели',
|
|
1883
|
+
quickLaunchDesc: 'Сформулируйте задачу для агента в автономном режиме:',
|
|
1884
|
+
quickLaunchPlaceholder: 'Например: Реализовать экспорт отчетов и покрыть тестами...',
|
|
1885
|
+
startGoal: 'Запустить цель',
|
|
1886
|
+
copyReport: '📋 Скопировать отчёт в Markdown',
|
|
1887
|
+
reportCopied: '✅ Скопировано!',
|
|
1888
|
+
save: 'Сохранить',
|
|
1889
|
+
saving: 'Сохранение…',
|
|
1890
|
+
discard: 'Отменить правки',
|
|
1891
|
+
resetField: 'По умолчанию',
|
|
1892
|
+
overridden: 'изменено',
|
|
1893
|
+
saveFailed: 'Не сохранено — исправьте и повторите',
|
|
1894
|
+
loading: 'Загрузка настроек…',
|
|
1895
|
+
unavailable: 'Настройки недоступны',
|
|
1896
|
+
},
|
|
1812
1897
|
};
|
|
1813
1898
|
|
|
1814
1899
|
module.exports.inject = ['slots', 'locale', 'settingsScope'];
|
package/lib/command-handler.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { GoalState } from './goal-engine.js';
|
|
2
|
+
import { GoalState, detectLanguage } from './goal-engine.js';
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const USAGE_RU = 'Использование: /goal [<цель>|clear|pause|resume]';
|
|
5
|
+
export const USAGE_EN = 'Usage: /goal [<goal>|clear|pause|resume]';
|
|
6
|
+
export const USAGE = USAGE_RU;
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Парсинг строки ввода для команды /goal
|
|
@@ -39,15 +41,26 @@ export function createGoalUserMessage(text) {
|
|
|
39
41
|
/**
|
|
40
42
|
* Формирование стартового промпта с жестким требованием составить план работ
|
|
41
43
|
* @param {string} goalText
|
|
44
|
+
* @param {string} [explicitLang]
|
|
42
45
|
* @returns {string}
|
|
43
46
|
*/
|
|
44
|
-
export function formatGoalStartPrompt(goalText) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
export function formatGoalStartPrompt(goalText, explicitLang) {
|
|
48
|
+
const lang = explicitLang || detectLanguage(goalText);
|
|
49
|
+
if (lang === 'ru') {
|
|
50
|
+
return `🎯 Активирован режим цели (Goal Mode): "${goalText}"\n\n` +
|
|
51
|
+
`СТРОГИЙ КОНТРАКТ АВТОНОМНОГО РЕЖИМА:\n` +
|
|
52
|
+
`1. ОБЯЗАТЕЛЬНЫЙ ШАГ №1: Твоим ПЕРВЫМ действием ДО выполнения любой другой работы или ответа должен быть вызов инструмента goal_set_milestones с массивом из 3-7 конкретных, последовательных пунктов плана работ. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
|
|
53
|
+
`2. ШАГ №2: После установки плана переходи к выполнению шагов. Перед началом работы над каждым шагом отметь его статус как in_progress через goal_update_progress(milestone_id, "in_progress").\n` +
|
|
54
|
+
`3. ШАГ №3: После успешного завершения шага отметь его статус как completed через goal_update_progress(milestone_id, "completed", "краткие итоги шага").\n` +
|
|
55
|
+
`4. ШАГ №4: Когда все пункты плана выполнены, вызови инструмент goal_finish(summary) с подробным резюме достигнутых результатов.`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `🎯 Goal Mode activated: "${goalText}"\n\n` +
|
|
59
|
+
`STRICT AUTONOMOUS CONTRACT:\n` +
|
|
60
|
+
`1. MANDATORY STEP 1: Your FIRST action BEFORE doing any other work or reply MUST be calling tool goal_set_milestones with an array of 3-7 concrete, sequential milestones. The user sees this plan in real time.\n` +
|
|
61
|
+
`2. STEP 2: After establishing the plan, proceed to execute milestones. Before starting each milestone, mark it as in_progress via goal_update_progress(milestone_id, "in_progress").\n` +
|
|
62
|
+
`3. STEP 3: Upon completing a milestone, mark it as completed via goal_update_progress(milestone_id, "completed", "brief milestone outcome").\n` +
|
|
63
|
+
`4. STEP 4: When all milestones are completed, call tool goal_finish(summary) with a comprehensive summary of achieved results.`;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
/**
|
|
@@ -63,33 +76,49 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
63
76
|
const sid = sessionId || 'default';
|
|
64
77
|
const snap = engine.getSnapshot(sid);
|
|
65
78
|
|
|
79
|
+
const lang = (parsed.text ? detectLanguage(parsed.text) : (snap.lang || (snap.title ? detectLanguage(snap.title) : 'en'))) || 'en';
|
|
80
|
+
const isRu = lang === 'ru';
|
|
81
|
+
const usage = isRu ? USAGE_RU : USAGE_EN;
|
|
82
|
+
|
|
66
83
|
switch (parsed.action) {
|
|
67
84
|
case 'show': {
|
|
68
85
|
if (!snap.hasActiveGoal) {
|
|
69
86
|
return {
|
|
70
87
|
kind: 'success',
|
|
71
|
-
text:
|
|
88
|
+
text: isRu
|
|
89
|
+
? `🎯 Режим цели (Goal Mode): цель не установлена.\n${usage}`
|
|
90
|
+
: `🎯 Goal Mode: no active goal set.\n${usage}`,
|
|
72
91
|
};
|
|
73
92
|
}
|
|
74
93
|
|
|
75
94
|
const milestonesInfo = snap.milestones.length > 0
|
|
76
|
-
? `\nПлан работ:\n` + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
|
|
95
|
+
? (isRu ? `\nПлан работ:\n` : `\nWork plan:\n`) + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
|
|
77
96
|
: '';
|
|
78
97
|
|
|
79
98
|
const etaText = snap.formattedETA ? ` (ETA: ${snap.formattedETA})` : '';
|
|
80
99
|
const tokensInfo = (snap.tokensUsage && snap.tokensUsage.totalTokens > 0)
|
|
81
|
-
?
|
|
100
|
+
? (isRu
|
|
101
|
+
? `\nТокены: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`
|
|
102
|
+
: `\nTokens: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`)
|
|
82
103
|
: '';
|
|
83
104
|
|
|
84
105
|
return {
|
|
85
106
|
kind: 'success',
|
|
86
|
-
text:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
text: isRu
|
|
108
|
+
? `🎯 Цель: «${snap.title}»\n` +
|
|
109
|
+
`Статус: ${snap.state}\n` +
|
|
110
|
+
`Время: ${snap.formattedElapsed}${etaText}\n` +
|
|
111
|
+
`Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
112
|
+
tokensInfo +
|
|
113
|
+
milestonesInfo +
|
|
114
|
+
`\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`
|
|
115
|
+
: `🎯 Goal: "${snap.title}"\n` +
|
|
116
|
+
`Status: ${snap.state}\n` +
|
|
117
|
+
`Duration: ${snap.formattedElapsed}${etaText}\n` +
|
|
118
|
+
`Iterations: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
119
|
+
tokensInfo +
|
|
120
|
+
milestonesInfo +
|
|
121
|
+
`\n\nCommands: /goal pause, /goal resume, /goal clear, /goal <new goal>`,
|
|
93
122
|
};
|
|
94
123
|
}
|
|
95
124
|
|
|
@@ -97,7 +126,7 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
97
126
|
if (!snap.hasActiveGoal) {
|
|
98
127
|
return {
|
|
99
128
|
kind: 'success',
|
|
100
|
-
text: 'Цель не была установлена.',
|
|
129
|
+
text: isRu ? 'Цель не была установлена.' : 'No active goal set.',
|
|
101
130
|
};
|
|
102
131
|
}
|
|
103
132
|
engine.clear(sid);
|
|
@@ -110,7 +139,7 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
110
139
|
}
|
|
111
140
|
return {
|
|
112
141
|
kind: 'success',
|
|
113
|
-
text: '🎯 Цель сброшена.',
|
|
142
|
+
text: isRu ? '🎯 Цель сброшена.' : '🎯 Goal cleared.',
|
|
114
143
|
};
|
|
115
144
|
}
|
|
116
145
|
|
|
@@ -118,16 +147,18 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
118
147
|
if (!snap.hasActiveGoal) {
|
|
119
148
|
return {
|
|
120
149
|
kind: 'error',
|
|
121
|
-
text:
|
|
150
|
+
text: isRu
|
|
151
|
+
? `Нельзя приостановить: активная цель отсутствует.\n${usage}`
|
|
152
|
+
: `Cannot pause: no active goal.\n${usage}`,
|
|
122
153
|
};
|
|
123
154
|
}
|
|
124
155
|
if (snap.state === GoalState.PAUSED) {
|
|
125
156
|
return {
|
|
126
157
|
kind: 'success',
|
|
127
|
-
text: '⏸ Цель уже на паузе.',
|
|
158
|
+
text: isRu ? '⏸ Цель уже на паузе.' : '⏸ Goal is already paused.',
|
|
128
159
|
};
|
|
129
160
|
}
|
|
130
|
-
engine.pause('Пауза по команде пользователя (/goal pause)', sid);
|
|
161
|
+
engine.pause(isRu ? 'Пауза по команде пользователя (/goal pause)' : 'Paused by user command (/goal pause)', sid);
|
|
131
162
|
if (agent && typeof agent.cancel === 'function') {
|
|
132
163
|
try {
|
|
133
164
|
agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
|
|
@@ -137,7 +168,9 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
137
168
|
}
|
|
138
169
|
return {
|
|
139
170
|
kind: 'success',
|
|
140
|
-
text:
|
|
171
|
+
text: isRu
|
|
172
|
+
? `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`
|
|
173
|
+
: `⏸ Goal paused: "${snap.title}". Agent stopped. Use /goal resume or ▶️ button to continue.`,
|
|
141
174
|
};
|
|
142
175
|
}
|
|
143
176
|
|
|
@@ -145,19 +178,23 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
145
178
|
if (!snap.hasActiveGoal) {
|
|
146
179
|
return {
|
|
147
180
|
kind: 'error',
|
|
148
|
-
text:
|
|
181
|
+
text: isRu
|
|
182
|
+
? `Нельзя возобновить: активная цель отсутствует.\n${usage}`
|
|
183
|
+
: `Cannot resume: no active goal.\n${usage}`,
|
|
149
184
|
};
|
|
150
185
|
}
|
|
151
186
|
if (snap.state === GoalState.RUNNING) {
|
|
152
187
|
return {
|
|
153
188
|
kind: 'success',
|
|
154
|
-
text: '▶️ Цель уже выполняется.',
|
|
189
|
+
text: isRu ? '▶️ Цель уже выполняется.' : '▶️ Goal is already running.',
|
|
155
190
|
};
|
|
156
191
|
}
|
|
157
192
|
engine.resume(sid);
|
|
158
193
|
if (agent && typeof agent.followup === 'function') {
|
|
159
194
|
try {
|
|
160
|
-
const resumePrompt =
|
|
195
|
+
const resumePrompt = isRu
|
|
196
|
+
? `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`
|
|
197
|
+
: `▶️ Goal resumed by user. Continue executing the work plan from where you stopped. Update steps via goal_update_progress.`;
|
|
161
198
|
agent.followup(createGoalUserMessage(resumePrompt));
|
|
162
199
|
} catch (err) {
|
|
163
200
|
console.warn('[dsh-goal] Failed to followup agent on resume:', err);
|
|
@@ -165,18 +202,20 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
165
202
|
}
|
|
166
203
|
return {
|
|
167
204
|
kind: 'success',
|
|
168
|
-
text:
|
|
205
|
+
text: isRu
|
|
206
|
+
? `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`
|
|
207
|
+
: `▶️ Goal resumed: "${snap.title}". Agent continues working.`,
|
|
169
208
|
};
|
|
170
209
|
}
|
|
171
210
|
|
|
172
211
|
case 'start': {
|
|
173
212
|
const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
|
|
174
|
-
const newSnap = engine.startGoal(parsed.text, { maxIterations }, sid);
|
|
213
|
+
const newSnap = engine.startGoal(parsed.text, { maxIterations, lang }, sid);
|
|
175
214
|
|
|
176
215
|
// Если команда вызвана в контексте агента, отправляем цель в очередь модели
|
|
177
216
|
if (agent && typeof agent.followup === 'function') {
|
|
178
217
|
try {
|
|
179
|
-
const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text));
|
|
218
|
+
const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text, lang));
|
|
180
219
|
agent.followup(userMsg);
|
|
181
220
|
} catch (err) {
|
|
182
221
|
console.warn('[dsh-goal] Failed to dispatch goal to agent followup:', err);
|
|
@@ -185,15 +224,18 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
185
224
|
|
|
186
225
|
return {
|
|
187
226
|
kind: 'success',
|
|
188
|
-
text:
|
|
189
|
-
|
|
227
|
+
text: isRu
|
|
228
|
+
? `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
|
|
229
|
+
`Для паузы: /goal pause | Для сброса: /goal clear`
|
|
230
|
+
: `🎯 Goal activated: "${newSnap.title}" (max ${newSnap.maxIterations} iterations).\n` +
|
|
231
|
+
`To pause: /goal pause | To clear: /goal clear`,
|
|
190
232
|
};
|
|
191
233
|
}
|
|
192
234
|
|
|
193
235
|
default:
|
|
194
236
|
return {
|
|
195
237
|
kind: 'error',
|
|
196
|
-
text: `Неизвестная команда. ${
|
|
238
|
+
text: isRu ? `Неизвестная команда. ${usage}` : `Unknown command. ${usage}`,
|
|
197
239
|
};
|
|
198
240
|
}
|
|
199
241
|
}
|
package/lib/goal-engine.js
CHANGED
|
@@ -58,6 +58,23 @@ export function formatETA(seconds) {
|
|
|
58
58
|
return remainingMins > 0 ? `~${hours}h ${remainingMins}m` : `~${hours}h`;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Автоматическое определение языка текста (кириллица -> ru, иероглифы -> zh, иначе en)
|
|
63
|
+
* @param {string} text
|
|
64
|
+
* @param {string} [fallback='en']
|
|
65
|
+
* @returns {'ru' | 'en' | 'zh'}
|
|
66
|
+
*/
|
|
67
|
+
export function detectLanguage(text, fallback = 'en') {
|
|
68
|
+
if (!text || typeof text !== 'string') return fallback;
|
|
69
|
+
if (/[а-яёА-ЯЁ]/i.test(text)) {
|
|
70
|
+
return 'ru';
|
|
71
|
+
}
|
|
72
|
+
if (/[\u4e00-\u9fa5]/.test(text)) {
|
|
73
|
+
return 'zh';
|
|
74
|
+
}
|
|
75
|
+
return 'en';
|
|
76
|
+
}
|
|
77
|
+
|
|
61
78
|
export class GoalEngine {
|
|
62
79
|
constructor(options = {}) {
|
|
63
80
|
this.defaultMaxIterations = options.defaultMaxIterations ?? 25;
|
|
@@ -116,6 +133,9 @@ export class GoalEngine {
|
|
|
116
133
|
if (!goal.tokensUsage) {
|
|
117
134
|
goal.tokensUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
118
135
|
}
|
|
136
|
+
if (!goal.lang) {
|
|
137
|
+
goal.lang = detectLanguage(goal.title);
|
|
138
|
+
}
|
|
119
139
|
this.goals.set(sid, goal);
|
|
120
140
|
}
|
|
121
141
|
}
|
|
@@ -135,6 +155,9 @@ export class GoalEngine {
|
|
|
135
155
|
if (!data.tokensUsage) {
|
|
136
156
|
data.tokensUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
137
157
|
}
|
|
158
|
+
if (!data.lang) {
|
|
159
|
+
data.lang = detectLanguage(data.title);
|
|
160
|
+
}
|
|
138
161
|
this.goals.set('default', data);
|
|
139
162
|
}
|
|
140
163
|
if (dirty) {
|
|
@@ -330,6 +353,7 @@ export class GoalEngine {
|
|
|
330
353
|
sessionId: sid,
|
|
331
354
|
title: cleanTitle,
|
|
332
355
|
description: options.description?.trim() || '',
|
|
356
|
+
lang: options.lang || detectLanguage(cleanTitle),
|
|
333
357
|
state: GoalState.RUNNING,
|
|
334
358
|
startedAt: now,
|
|
335
359
|
pausedAt: null,
|
|
@@ -659,6 +683,7 @@ export class GoalEngine {
|
|
|
659
683
|
formattedElapsed: '0s',
|
|
660
684
|
estimatedRemainingSeconds: null,
|
|
661
685
|
formattedETA: null,
|
|
686
|
+
lang: 'en',
|
|
662
687
|
tokensUsage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
663
688
|
milestones: [],
|
|
664
689
|
progressPercent: 0,
|
|
@@ -683,6 +708,7 @@ export class GoalEngine {
|
|
|
683
708
|
state: goal.state,
|
|
684
709
|
title: goal.title,
|
|
685
710
|
description: goal.description,
|
|
711
|
+
lang: goal.lang || detectLanguage(goal.title),
|
|
686
712
|
startedAt: goal.startedAt,
|
|
687
713
|
pausedAt: goal.pausedAt,
|
|
688
714
|
totalPausedDurationMs: goal.totalPausedDurationMs,
|
|
@@ -715,14 +741,16 @@ export class GoalEngine {
|
|
|
715
741
|
}
|
|
716
742
|
|
|
717
743
|
const snapshot = this.getSnapshot(sid);
|
|
744
|
+
const lang = goal.lang || detectLanguage(snapshot.title);
|
|
718
745
|
const hasMilestones = snapshot.milestones.length > 0;
|
|
719
|
-
const milestonesText = hasMilestones
|
|
720
|
-
? snapshot.milestones.map((m, i) => ` ${i + 1}. [${m.status.toUpperCase()}] ${m.title}${m.notes ? ` (${m.notes})` : ''}`).join('\n')
|
|
721
|
-
: ' (План работ ещё не сформирован — немедленно вызови goal_set_milestones со списком шагов!)';
|
|
722
|
-
|
|
723
746
|
const etaText = snapshot.formattedETA ? ` (ETA: ${snapshot.formattedETA})` : '';
|
|
724
747
|
|
|
725
|
-
|
|
748
|
+
if (lang === 'ru') {
|
|
749
|
+
const milestonesText = hasMilestones
|
|
750
|
+
? snapshot.milestones.map((m, i) => ` ${i + 1}. [${m.status.toUpperCase()}] ${m.title}${m.notes ? ` (${m.notes})` : ''}`).join('\n')
|
|
751
|
+
: ' (План работ ещё не сформирован — немедленно вызови goal_set_milestones со списком шагов!)';
|
|
752
|
+
|
|
753
|
+
return `\n\n[DSH GOAL MODE ACTIVE]
|
|
726
754
|
Цель: "${snapshot.title}"
|
|
727
755
|
Время работы: ${snapshot.formattedElapsed}${etaText} | Итерация: ${snapshot.iterationsCount}/${snapshot.maxIterations}
|
|
728
756
|
План работ:
|
|
@@ -732,5 +760,21 @@ ${milestonesText}
|
|
|
732
760
|
1. ${hasMilestones ? 'Выполняй текущий активный пункт плана работ.' : 'ТВОЙ ПЕРВЫЙ ШАГ: Немедленно вызови инструмент goal_set_milestones со списком пунктов плана работ (3-7 конкретных шагов). Запрещено выполнять работу или завершать turn без вызова goal_set_milestones!'}
|
|
733
761
|
2. По мере выполнения каждого шага обязательно отмечай его статус через инструмент goal_update_progress (status: "in_progress" перед началом шага, status: "completed" по его завершении с кратким notes).
|
|
734
762
|
3. Когда все пункты плана будут выполнены, вызови инструмент goal_finish с подробным итоговым резюме достигнутых результатов.`;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const milestonesText = hasMilestones
|
|
766
|
+
? snapshot.milestones.map((m, i) => ` ${i + 1}. [${m.status.toUpperCase()}] ${m.title}${m.notes ? ` (${m.notes})` : ''}`).join('\n')
|
|
767
|
+
: ' (Work plan is not yet established — call goal_set_milestones immediately with initial steps!)';
|
|
768
|
+
|
|
769
|
+
return `\n\n[DSH GOAL MODE ACTIVE]
|
|
770
|
+
Goal: "${snapshot.title}"
|
|
771
|
+
Elapsed Time: ${snapshot.formattedElapsed}${etaText} | Iteration: ${snapshot.iterationsCount}/${snapshot.maxIterations}
|
|
772
|
+
Work Plan:
|
|
773
|
+
${milestonesText}
|
|
774
|
+
|
|
775
|
+
Goal Mode Instructions (MANDATORY TO FOLLOW):
|
|
776
|
+
1. ${hasMilestones ? 'Execute the current active milestone from the work plan.' : 'YOUR FIRST STEP: Immediately call tool goal_set_milestones with the list of milestones (3-7 concrete steps). You must not execute work or finish turn without calling goal_set_milestones!'}
|
|
777
|
+
2. As each milestone progresses, update its status via tool goal_update_progress (status: "in_progress" before starting, status: "completed" upon completion with brief notes).
|
|
778
|
+
3. When all milestones are completed, call tool goal_finish with a detailed summary of achieved results.`;
|
|
735
779
|
}
|
|
736
780
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import z from '@deepseek-ai/schemastery';
|
|
4
|
-
import { GoalEngine, GoalState, MilestoneStatus } from './goal-engine.js';
|
|
4
|
+
import { GoalEngine, GoalState, MilestoneStatus, detectLanguage } from './goal-engine.js';
|
|
5
5
|
import { parseGoalInput, executeGoalSlashCommand, createGoalUserMessage } from './command-handler.js';
|
|
6
6
|
|
|
7
7
|
export const name = '@goodandready/dsh-goal';
|
|
@@ -166,9 +166,12 @@ export function apply(ctx, config = {}) {
|
|
|
166
166
|
|
|
167
167
|
const resumeActiveAgent = (promptText, sessionId = 'default') => {
|
|
168
168
|
const sid = sessionId || 'default';
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
const snap = engine.getSnapshot(sid);
|
|
170
|
+
const lang = snap.lang || (snap.title ? detectLanguage(snap.title) : 'en');
|
|
171
|
+
const defaultResumePrompt = lang === 'ru'
|
|
172
|
+
? '▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.'
|
|
173
|
+
: '▶️ Goal resumed by user. Continue executing the work plan from where you stopped. Update steps via goal_update_progress.';
|
|
174
|
+
const resumeMsg = createGoalUserMessage(promptText || defaultResumePrompt);
|
|
172
175
|
let target = sessionAgents.get(sid);
|
|
173
176
|
if (!target && lastActiveAgent && typeof lastActiveAgent.followup === 'function') {
|
|
174
177
|
const lastSid = sessionIdOf(lastActiveAgent, null);
|
|
@@ -538,11 +541,16 @@ export function apply(ctx, config = {}) {
|
|
|
538
541
|
res.statusCode = 400;
|
|
539
542
|
return res.end(JSON.stringify({ error: 'Goal title cannot be empty' }));
|
|
540
543
|
}
|
|
544
|
+
const detectedLang = data.lang || detectLanguage(cleanTitle);
|
|
541
545
|
result = engine.startGoal(cleanTitle, {
|
|
542
546
|
description: typeof description === 'string' ? description.trim() : '',
|
|
543
547
|
maxIterations: getConfig().maxIterations,
|
|
548
|
+
lang: detectedLang,
|
|
544
549
|
}, sid);
|
|
545
|
-
|
|
550
|
+
const startPrompt = detectedLang === 'ru'
|
|
551
|
+
? `🎯 Цель установлена: "${cleanTitle}". Немедленно сформируй план работ (3-7 конкретных шагов) через инструмент goal_set_milestones и начни его выполнение.`
|
|
552
|
+
: `🎯 Goal established: "${cleanTitle}". Immediately formulate a work plan (3-7 concrete steps) via tool goal_set_milestones and start executing it.`;
|
|
553
|
+
resumeActiveAgent(startPrompt, sid);
|
|
546
554
|
break;
|
|
547
555
|
}
|
|
548
556
|
case 'pause':
|
|
@@ -637,9 +645,13 @@ export function apply(ctx, config = {}) {
|
|
|
637
645
|
}
|
|
638
646
|
|
|
639
647
|
// Item 3: Smart Progress Guard — detect idle loops without progress
|
|
648
|
+
const lang = snap.lang || (snap.title ? detectLanguage(snap.title) : 'en');
|
|
640
649
|
const stallCount = engine.incrementStallCount(sid);
|
|
641
650
|
if (stallCount >= 2) {
|
|
642
|
-
|
|
651
|
+
const stallReason = lang === 'ru'
|
|
652
|
+
? 'Агент не продвинулся по плану за последние 2 итерации — требуется внимание оператора'
|
|
653
|
+
: 'Agent made no progress on work plan in the last 2 iterations — operator attention required';
|
|
654
|
+
engine.pause(stallReason, sid);
|
|
643
655
|
return;
|
|
644
656
|
}
|
|
645
657
|
|
|
@@ -650,9 +662,11 @@ export function apply(ctx, config = {}) {
|
|
|
650
662
|
const triggerNextTurn = () => {
|
|
651
663
|
const currentSnap = engine.getSnapshot(sid);
|
|
652
664
|
if (currentSnap.hasActiveGoal && currentSnap.state === GoalState.RUNNING) {
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
665
|
+
const currentLang = currentSnap.lang || (currentSnap.title ? detectLanguage(currentSnap.title) : 'en');
|
|
666
|
+
const promptText = currentLang === 'ru'
|
|
667
|
+
? 'Продолжай автономное выполнение цели согласно плану работ. Отмечай каждый выполненный шаг через goal_update_progress, а по завершении всех задач вызови goal_finish.'
|
|
668
|
+
: 'Continue autonomous goal execution according to the work plan. Mark each completed step via goal_update_progress, and when all tasks are complete, call goal_finish.';
|
|
669
|
+
const promptMsg = createGoalUserMessage(promptText);
|
|
656
670
|
let target = sessionAgents.get(sid);
|
|
657
671
|
if (!target && lastActiveAgent && typeof lastActiveAgent.followup === 'function') {
|
|
658
672
|
const lastSid = sessionIdOf(lastActiveAgent, null);
|
|
@@ -698,7 +712,11 @@ export function apply(ctx, config = {}) {
|
|
|
698
712
|
const sid = sessionIdOf(event, 'default');
|
|
699
713
|
const snap = engine.getSnapshot(sid);
|
|
700
714
|
if (snap.hasActiveGoal && snap.state === GoalState.RUNNING) {
|
|
701
|
-
|
|
715
|
+
const lang = snap.lang || (snap.title ? detectLanguage(snap.title) : 'en');
|
|
716
|
+
const pauseReason = lang === 'ru'
|
|
717
|
+
? 'Ожидание подтверждения действия оператором'
|
|
718
|
+
: 'Waiting for operator confirmation/approval';
|
|
719
|
+
engine.pause(pauseReason, sid);
|
|
702
720
|
}
|
|
703
721
|
};
|
|
704
722
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-goal",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Goal
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "Autonomous Goal Execution & Multi-Turn Task Tracking Engine with Sticky Header for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -15,11 +15,9 @@
|
|
|
15
15
|
"cordis.patch.yml",
|
|
16
16
|
"README.md",
|
|
17
17
|
"docs/",
|
|
18
|
-
"LICENSE"
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.zh.md"
|
|
19
20
|
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"test": "node --test test/*.test.mjs"
|
|
22
|
-
},
|
|
23
21
|
"keywords": [
|
|
24
22
|
"dsh",
|
|
25
23
|
"dsh-plugin",
|
|
@@ -57,5 +55,8 @@
|
|
|
57
55
|
},
|
|
58
56
|
"dependencies": {
|
|
59
57
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"test": "node --test test/*.test.mjs"
|
|
60
61
|
}
|
|
61
|
-
}
|
|
62
|
+
}
|