@goodandready/dsh-goal 0.1.9 → 0.1.10
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 +3 -0
- package/{docs/README.ru.md → README.ru.md} +12 -1
- package/README.zh.md +2 -0
- package/docs/design/DESIGN.md +23 -0
- package/lib/index.js +316 -121
- package/package.json +8 -7
- package/docs/README.zh.md +0 -143
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
|
+
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
21
22
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
23
|
</p>
|
|
23
24
|
|
|
@@ -50,6 +51,7 @@ Without an autonomous tracking framework, agents can lose context across turns,
|
|
|
50
51
|
* 🔘 **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
52
|
* 📊 **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
53
|
* 🤖 **Autonomous Agent Contract**: Provides `goal_set_milestones`, `goal_update_progress`, and `goal_finish` tools directly to the agent.
|
|
54
|
+
* 🔄 **Core DSH Goal Tools Interception (*Added in v0.1.10*)**: Seamless drop-in compatibility for models calling built-in DSH goal tools (`update_goal`, `get_goal`, `create_goal`). Intercepts actions (`complete`, `pause`, `resume`, `edit`, `blocked`), bypasses rigid authority restrictions that caused crashes (`complete and blocked require a direct human turn`), and routes all state updates directly to GoalEngine.
|
|
53
55
|
* 🛡️ **Safety Guardrails**: Configurable `maxIterations` safety limit and Smart Progress Guard to catch and pause idle turns without progress.
|
|
54
56
|
* 🔔 **Web Audio Chimes**: Pleasant synthesized audio feedback on goal completion or failure via Web Audio API.
|
|
55
57
|
|
|
@@ -120,6 +122,7 @@ Handles `/goal` commands and subcommands:
|
|
|
120
122
|
* Subscribes to `turn/end` for zero-latency turn-to-turn auto-drive using `setImmediate`.
|
|
121
123
|
* Listens to `approval/asked` to automatically pause goal when operator confirmation is needed.
|
|
122
124
|
* Registers agent tools: `goal_set_milestones`, `goal_update_progress`, `goal_finish`.
|
|
125
|
+
* Seamlessly shadows core DSH goal tools: `update_goal`, `get_goal`, `create_goal` with zero collision and full `GOAL_OUTPUT` schema compliance.
|
|
123
126
|
|
|
124
127
|
### 4. `lib/client.js` — Frontend Web UI
|
|
125
128
|
* **Sticky Top Banner**: Mounts via slot `conversation.input.dock` with live timer, status badge, pause/resume, and details button.
|
|
@@ -17,11 +17,21 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<a href="
|
|
20
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
21
|
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
22
22
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
|
+
<table align="center">
|
|
26
|
+
<tr>
|
|
27
|
+
<td align="center">
|
|
28
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему звезду на GitHub</strong> — это покажет мне, что плагин вам полезен, и будет мотивировать меня развивать его дальше.
|
|
29
|
+
<br><br>
|
|
30
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новый функционал</strong>, создайте issue на GitHub на любом языке — я рассмотрю ваше предложение и реализую полезные идеи в одной из следующих версий плагина.
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
25
35
|
</div>
|
|
26
36
|
|
|
27
37
|
---
|
|
@@ -35,6 +45,7 @@
|
|
|
35
45
|
* ⏸️ **Управление циклом (Play / Pause / Cancel)**: мгновенная приостановка автономного цикла агента или его возобновление в любой момент.
|
|
36
46
|
* 📋 **Интерактивная панель майлстоунов**: модальное окно со списком подзадач, прогресс-баром и журналом выполнения.
|
|
37
47
|
* 🤖 **Инструменты агента**: `goal_set_milestones`, `goal_update_progress` и `goal_finish` для самостоятельной работы ИИ.
|
|
48
|
+
* 🔄 **Перехват и замещение инструментов ядра DSH (*Добавлено в v0.1.10*)**: Бесшовная совместимость для моделей, вызывающих стандартные инструменты DSH (`update_goal`, `get_goal`, `create_goal`). Перехватывает действия (`complete`, `pause`, `resume`, `edit`, `blocked`), устраняет падения ядра (`complete and blocked require a direct human turn`) и синхронизирует состояние напрямую с GoalEngine.
|
|
38
49
|
* 🛡️ **Защитные лимиты**: настраиваемое ограничение `maxIterations` для предотвращения бесконечных циклов.
|
|
39
50
|
|
|
40
51
|
---
|
package/README.zh.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
21
|
+
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
21
22
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
23
|
</p>
|
|
23
24
|
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
* 🔘 **快速启动按钮(v0.1.8 新增)**:常驻于输入框上方的启动按钮,支持弹窗一键制定目标,可在设置中自由开关。
|
|
51
52
|
* 📊 **Token 消耗统计与 Markdown 导出(v0.1.7 新增)**:实时累计提示词、生成词及总 Token 消耗,支持一键复制完整 Markdown 报告。
|
|
52
53
|
* 🤖 **智能体自主协作工具**:向智能体直接提供 `goal_set_milestones`、`goal_update_progress` 和 `goal_finish` 工具。
|
|
54
|
+
* 🔄 **DSH 核心目标工具无缝拦截与兼容 (*v0.1.10 新增*)**: 为使用原生 DSH 目标控制指令的模型提供完美向下兼容,透明接管 `update_goal`、`get_goal`、`create_goal`。拦截并处理 `complete`、`pause`、`resume`、`edit`、`blocked` 等操作,彻底解决原生工具抛出的 `complete and blocked require a direct human turn` 权限崩溃错误,所有状态直接同步至 GoalEngine。
|
|
53
55
|
* 🛡️ **安全防护机制**:支持自定义最大迭代次数(`maxIterations`)以及智能卡顿检测(Smart Progress Guard)。
|
|
54
56
|
* 🔔 **Web Audio 提示音**:任务完成或失败时,通过 Web Audio API 播放舒缓的合成音效。
|
|
55
57
|
|
package/docs/design/DESIGN.md
CHANGED
|
@@ -116,3 +116,26 @@
|
|
|
116
116
|
4. **Экспорт отчета в Markdown (One-click Markdown Export):**
|
|
117
117
|
- Для выполненной цели модальное окно предоставляет кнопку «📋 Скопировать отчёт в Markdown».
|
|
118
118
|
- Генерирует отчет с заголовком, статусом, длительностью, итерациями, расходом токенов, резюме результатов и таблицей вех.
|
|
119
|
+
|
|
120
|
+
### Решение 10: Бесшовная совместимость и перехват базовых инструментов целей DSH (update_goal, get_goal, create_goal) (v0.1.10)
|
|
121
|
+
|
|
122
|
+
**Контекст:**
|
|
123
|
+
В GitHub Issue #2 пользователи столкнулись с ошибкой:
|
|
124
|
+
`complete and blocked require a direct human turn or the current goal round`
|
|
125
|
+
при попытке модели завершить или обновить цель вызовом встроенного инструмента `update_goal`.
|
|
126
|
+
Причина: встроенный плагин ядра DSH (`@deepseek-ai/dsh-tool-goal`) накладывает жёсткие проверки происхождения хода (authority checks), которые дают сбой в автономных циклах и при вызовах вне строго размеченного раунда цели.
|
|
127
|
+
|
|
128
|
+
**Принятые решения:**
|
|
129
|
+
1. **Динамическое замещение инструментов ядра в `ToolRuntime`:**
|
|
130
|
+
- При инициализации `ctx.inject(['tools'], (tctx) => { ... })` плагин проверяет наличие `update_goal`, `get_goal`, `create_goal` в глобальном слое (`tctx.tools.layers.global.tools.data`), удаляет конфликтующие строгие определения ядра и регистрирует собственные полнофункциональные версии.
|
|
131
|
+
2. **Маршрутизация действий в `GoalEngine`:**
|
|
132
|
+
- `update_goal` перехватывает все действия модели:
|
|
133
|
+
- `action: 'complete'` — завершает цель в `engine.completeGoal()`, внедряет заключительный контекст `<goal_complete>` и возвращает статус `complete`;
|
|
134
|
+
- `action: 'pause'` — переводит цель в паузу через `engine.pause()`;
|
|
135
|
+
- `action: 'resume'` — возобновляет цель (`engine.resume()`) и перезапускает автодрайв агента;
|
|
136
|
+
- `action: 'edit'` — обновляет цель (`title`) и лимит раундов (`maxIterations`);
|
|
137
|
+
- `action: 'blocked'` — фиксирует блокер в `engine.pause()` с возвратом фазы `blocked` и кода причины.
|
|
138
|
+
3. **Строгое соответствие схеме `GOAL_OUTPUT`:**
|
|
139
|
+
- Все перехваченные инструменты возвращают структуры, валидные по спецификации `GOAL_VALUE_SCHEMA` ядра (`goal: null` при отсутствии активной цели, либо объект цели с `id`, `revision`, `objective`, `phase`, `roundsStarted`, `maxGoalRounds`, `activation`).
|
|
140
|
+
4. **Нейтрализация устаревшего системного промпта ядра:**
|
|
141
|
+
- Удаляется секция `tool:goal` ядра с ошибочными директивами и заменяется актуальной динамической инструкцией `tool:dsh-goal`.
|
package/lib/index.js
CHANGED
|
@@ -305,127 +305,322 @@ export function apply(ctx, config = {}) {
|
|
|
305
305
|
}
|
|
306
306
|
});
|
|
307
307
|
|
|
308
|
-
// 3. Инжекция контекста цели в системный промпт (systemPrompt)
|
|
309
|
-
ctx.inject(['systemPrompt'], (pctx) => {
|
|
310
|
-
try {
|
|
311
|
-
if (typeof pctx.systemPrompt?.section === 'function') {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
308
|
+
// 3. Инжекция контекста цели в системный промпт (systemPrompt)
|
|
309
|
+
ctx.inject(['systemPrompt'], (pctx) => {
|
|
310
|
+
try {
|
|
311
|
+
if (typeof pctx.systemPrompt?.section === 'function') {
|
|
312
|
+
// Clear conflicting core tool:goal prompt section if present
|
|
313
|
+
const globalSections = pctx.systemPrompt.layers?.global?.sections;
|
|
314
|
+
if (globalSections?.data instanceof Map && globalSections.data.has('tool:goal')) {
|
|
315
|
+
globalSections.data.delete('tool:goal');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const order = typeof pctx.systemPrompt.getSectionOrder === 'function'
|
|
319
|
+
? (pctx.systemPrompt.getSectionOrder('TOOL_GOAL') || 2400)
|
|
320
|
+
: 2400;
|
|
321
|
+
|
|
322
|
+
const unregisterSection = pctx.systemPrompt.section({
|
|
323
|
+
name: 'tool:dsh-goal',
|
|
324
|
+
order,
|
|
325
|
+
text: (sessionCtx) => {
|
|
326
|
+
const sid = sessionIdOf(sessionCtx, 'default');
|
|
327
|
+
return engine.getStatePromptInjection(sid);
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
if (typeof unregisterSection === 'function') {
|
|
332
|
+
pctx.effect(() => () => unregisterSection(), 'dsh-goal: system prompt section');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.warn('[dsh-goal] SystemPrompt section register skipped:', err.message);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// 4. Регистрация инструментов для модели (tools)
|
|
341
|
+
ctx.inject(['tools'], (tctx) => {
|
|
342
|
+
if (!tctx.tools?.register) return;
|
|
343
|
+
|
|
344
|
+
// Helper: Safely replace or register tool in ToolRuntime
|
|
345
|
+
const safeRegister = (definition) => {
|
|
346
|
+
try {
|
|
347
|
+
const name = definition.name;
|
|
348
|
+
const globalTools = tctx.tools.layers?.global?.tools;
|
|
349
|
+
if (globalTools?.data instanceof Map && globalTools.data.has(name)) {
|
|
350
|
+
globalTools.data.delete(name);
|
|
351
|
+
}
|
|
352
|
+
const unregister = tctx.tools.register(definition);
|
|
353
|
+
if (typeof unregister === 'function') {
|
|
354
|
+
tctx.effect(() => () => unregister(), `dsh-goal: tool ${name}`);
|
|
355
|
+
}
|
|
356
|
+
} catch (err) {
|
|
357
|
+
console.warn(`[dsh-goal] Tool ${definition.name} registration skipped:`, err.message);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const JSON_OUTPUT = {
|
|
362
|
+
schema: { type: 'object', additionalProperties: true },
|
|
363
|
+
render: (_args, val) => [{ type: 'text', text: JSON.stringify(val) }],
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
function formatGoalValue(snap) {
|
|
367
|
+
if (!snap || !snap.hasActiveGoal) {
|
|
368
|
+
return { goal: null };
|
|
369
|
+
}
|
|
370
|
+
let phase = 'active';
|
|
371
|
+
if (snap.state === GoalState.PAUSED) phase = 'paused';
|
|
372
|
+
else if (snap.state === GoalState.COMPLETED) phase = 'complete';
|
|
373
|
+
|
|
374
|
+
const roundsStarted = Number.isInteger(snap.iterations) ? snap.iterations : 0;
|
|
375
|
+
const maxGoalRounds = Number.isInteger(snap.maxIterations) ? snap.maxIterations : 25;
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
goal: {
|
|
379
|
+
id: snap.id || 'goal-active',
|
|
380
|
+
revision: 1,
|
|
381
|
+
objective: snap.title || 'Goal',
|
|
382
|
+
phase,
|
|
383
|
+
roundsStarted,
|
|
384
|
+
maxGoalRounds,
|
|
385
|
+
},
|
|
386
|
+
activation: snap.state === GoalState.RUNNING ? 'armed' : 'disarmed',
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Совместимый инструмент 1: get_goal
|
|
391
|
+
const handleGetGoal = async (_args, toolCtx) => {
|
|
392
|
+
const sid = sessionIdOf(toolCtx, 'default');
|
|
393
|
+
const snap = engine.getSnapshot(sid);
|
|
394
|
+
return formatGoalValue(snap);
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
safeRegister({
|
|
398
|
+
name: 'get_goal',
|
|
399
|
+
description: 'Read the current same-session goal, including objective, phase, and round limits.',
|
|
400
|
+
parameters: { type: 'object', properties: {} },
|
|
401
|
+
output: JSON_OUTPUT,
|
|
402
|
+
execute: handleGetGoal,
|
|
403
|
+
handler: handleGetGoal,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Совместимый инструмент 2: create_goal
|
|
407
|
+
const handleCreateGoal = async (args, toolCtx) => {
|
|
408
|
+
const sid = sessionIdOf(toolCtx, 'default');
|
|
409
|
+
const maxIterations = Number(args.max_goal_rounds) || 25;
|
|
410
|
+
const snap = engine.startGoal(args.objective, { maxIterations }, sid);
|
|
411
|
+
return formatGoalValue(snap);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
safeRegister({
|
|
415
|
+
name: 'create_goal',
|
|
416
|
+
description: 'Create one persisted same-session completion goal for long-running autonomous work.',
|
|
417
|
+
parameters: {
|
|
418
|
+
type: 'object',
|
|
419
|
+
properties: {
|
|
420
|
+
objective: {
|
|
421
|
+
type: 'string',
|
|
422
|
+
description: 'The concrete completion objective.',
|
|
423
|
+
},
|
|
424
|
+
max_goal_rounds: {
|
|
425
|
+
type: 'number',
|
|
426
|
+
description: 'Optional positive integer limit on automatic continuation rounds.',
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
required: ['objective'],
|
|
430
|
+
},
|
|
431
|
+
output: JSON_OUTPUT,
|
|
432
|
+
execute: handleCreateGoal,
|
|
433
|
+
handler: handleCreateGoal,
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Совместимый инструмент 3: update_goal (перехватчик authority checks и роутер в GoalEngine)
|
|
437
|
+
const handleUpdateGoal = async (args, toolCtx) => {
|
|
438
|
+
const sid = sessionIdOf(toolCtx, 'default');
|
|
439
|
+
const action = args.action;
|
|
440
|
+
|
|
441
|
+
if (action === 'complete') {
|
|
442
|
+
const summary = args.blocked_reason || args.objective || 'Goal marked complete';
|
|
443
|
+
const snap = engine.completeGoal(summary, sid);
|
|
444
|
+
if (toolCtx?.deferContext) {
|
|
445
|
+
try {
|
|
446
|
+
toolCtx.deferContext({
|
|
447
|
+
type: 'text',
|
|
448
|
+
text: `<goal_complete>\nObjective: ${JSON.stringify(snap.title || summary)}\nThe goal is marked complete. Summarize what was accomplished for the user.\n</goal_complete>`,
|
|
449
|
+
});
|
|
450
|
+
} catch (_) {}
|
|
451
|
+
}
|
|
452
|
+
return formatGoalValue(snap);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (action === 'pause') {
|
|
456
|
+
const reason = args.blocked_reason || 'Paused by model';
|
|
457
|
+
const snap = engine.pause(reason, sid);
|
|
458
|
+
return formatGoalValue(snap);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (action === 'resume') {
|
|
462
|
+
const snap = engine.resume(sid);
|
|
463
|
+
resumeActiveAgent(undefined, sid);
|
|
464
|
+
return formatGoalValue(snap);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (action === 'edit') {
|
|
468
|
+
const snap = engine.getSnapshot(sid);
|
|
469
|
+
if (args.objective) snap.title = args.objective;
|
|
470
|
+
if (args.max_goal_rounds) snap.maxIterations = Number(args.max_goal_rounds);
|
|
471
|
+
engine.emit(sid, true);
|
|
472
|
+
return formatGoalValue(snap);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (action === 'blocked') {
|
|
476
|
+
const reason = args.blocked_reason || 'Goal blocked';
|
|
477
|
+
const snap = engine.pause('Blocked: ' + reason, sid);
|
|
478
|
+
if (toolCtx?.deferContext) {
|
|
479
|
+
try {
|
|
480
|
+
toolCtx.deferContext({
|
|
481
|
+
type: 'text',
|
|
482
|
+
text: `<goal_blocked>\nObjective: ${JSON.stringify(snap.title || 'Goal')}\nBlocked: ${JSON.stringify(reason)}\nExplain to the user what blocked progress.\n</goal_blocked>`,
|
|
483
|
+
});
|
|
484
|
+
} catch (_) {}
|
|
485
|
+
}
|
|
486
|
+
const res = formatGoalValue(snap);
|
|
487
|
+
if (res.goal) {
|
|
488
|
+
res.goal.phase = 'blocked';
|
|
489
|
+
res.goal.blockedReason = { code: 'model-reported', message: reason };
|
|
490
|
+
}
|
|
491
|
+
return res;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return formatGoalValue(engine.getSnapshot(sid));
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
safeRegister({
|
|
498
|
+
name: 'update_goal',
|
|
499
|
+
description: 'Update the active goal: complete, pause, resume, edit, or report blocked.',
|
|
500
|
+
parameters: {
|
|
501
|
+
type: 'object',
|
|
502
|
+
properties: {
|
|
503
|
+
goal_id: { type: 'string', description: 'Exact id returned by get_goal.' },
|
|
504
|
+
revision: { type: 'number', description: 'Exact positive revision returned by get_goal.' },
|
|
505
|
+
action: {
|
|
506
|
+
type: 'string',
|
|
507
|
+
enum: ['edit', 'pause', 'resume', 'complete', 'blocked'],
|
|
508
|
+
description: 'edit | pause | resume | complete | blocked',
|
|
509
|
+
},
|
|
510
|
+
objective: { type: 'string', description: 'Replacement objective; valid with action edit.' },
|
|
511
|
+
max_goal_rounds: { type: 'number', description: 'Replacement cap; valid with action edit.' },
|
|
512
|
+
blocked_reason: { type: 'string', description: 'Concrete blocking condition; required with action blocked.' },
|
|
513
|
+
},
|
|
514
|
+
required: ['action'],
|
|
515
|
+
},
|
|
516
|
+
output: JSON_OUTPUT,
|
|
517
|
+
execute: handleUpdateGoal,
|
|
518
|
+
handler: handleUpdateGoal,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// Инструмент 4: Декомпозиция цели на вехи
|
|
522
|
+
const handleSetMilestones = async ({ milestones }, toolCtx) => {
|
|
523
|
+
const sid = sessionIdOf(toolCtx, 'default');
|
|
524
|
+
const snap = engine.getSnapshot(sid);
|
|
525
|
+
if (!snap.hasActiveGoal) {
|
|
526
|
+
return { error: 'No active goal currently set. Start a goal first.' };
|
|
527
|
+
}
|
|
528
|
+
engine.addMilestones(milestones, true, sid);
|
|
529
|
+
return {
|
|
530
|
+
success: true,
|
|
531
|
+
milestones: engine.getSnapshot(sid).milestones,
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
safeRegister({
|
|
536
|
+
name: 'goal_set_milestones',
|
|
537
|
+
description: 'Break down the current active goal into a sequence of concrete milestones/sub-tasks.',
|
|
538
|
+
parameters: {
|
|
539
|
+
type: 'object',
|
|
540
|
+
properties: {
|
|
541
|
+
milestones: {
|
|
542
|
+
type: 'array',
|
|
543
|
+
items: { type: 'string' },
|
|
544
|
+
description: 'List of milestone titles to accomplish.',
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
required: ['milestones'],
|
|
548
|
+
},
|
|
549
|
+
output: JSON_OUTPUT,
|
|
550
|
+
execute: handleSetMilestones,
|
|
551
|
+
handler: handleSetMilestones,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Инструмент 5: Обновление статуса вехи
|
|
555
|
+
const handleUpdateProgress = async ({ milestone_id, status, notes }, toolCtx) => {
|
|
556
|
+
const sid = sessionIdOf(toolCtx, 'default');
|
|
557
|
+
const ok = engine.updateMilestone(milestone_id, status, notes, sid);
|
|
558
|
+
if (!ok) {
|
|
559
|
+
return { error: `Milestone ${milestone_id} not found or no active goal.` };
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
success: true,
|
|
563
|
+
snapshot: engine.getSnapshot(sid),
|
|
564
|
+
};
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
safeRegister({
|
|
568
|
+
name: 'goal_update_progress',
|
|
569
|
+
description: 'Update the status of a specific goal milestone and optionally log progress notes.',
|
|
570
|
+
parameters: {
|
|
571
|
+
type: 'object',
|
|
572
|
+
properties: {
|
|
573
|
+
milestone_id: {
|
|
574
|
+
type: 'string',
|
|
575
|
+
description: 'The ID of the milestone (e.g. "m-1", "m-2").',
|
|
576
|
+
},
|
|
577
|
+
status: {
|
|
578
|
+
type: 'string',
|
|
579
|
+
enum: ['pending', 'in_progress', 'completed', 'failed'],
|
|
580
|
+
description: 'New status for this milestone.',
|
|
581
|
+
},
|
|
582
|
+
notes: {
|
|
583
|
+
type: 'string',
|
|
584
|
+
description: 'Brief summary of what was accomplished or why it failed.',
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
required: ['milestone_id', 'status'],
|
|
588
|
+
},
|
|
589
|
+
output: JSON_OUTPUT,
|
|
590
|
+
execute: handleUpdateProgress,
|
|
591
|
+
handler: handleUpdateProgress,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Инструмент 6: Успешное завершение цели
|
|
595
|
+
const handleGoalFinish = async ({ summary }, toolCtx) => {
|
|
596
|
+
const sid = sessionIdOf(toolCtx, 'default');
|
|
597
|
+
const snap = engine.completeGoal(summary, sid);
|
|
598
|
+
return {
|
|
599
|
+
success: true,
|
|
600
|
+
completed: true,
|
|
601
|
+
summary,
|
|
602
|
+
};
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
safeRegister({
|
|
606
|
+
name: 'goal_finish',
|
|
607
|
+
description: 'Conclude the active goal successfully with a final summary and achievements.',
|
|
608
|
+
parameters: {
|
|
609
|
+
type: 'object',
|
|
610
|
+
properties: {
|
|
611
|
+
summary: {
|
|
612
|
+
type: 'string',
|
|
613
|
+
description: 'Final summary of the goal outcome and deliverables.',
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
required: ['summary'],
|
|
617
|
+
},
|
|
618
|
+
output: JSON_OUTPUT,
|
|
619
|
+
execute: handleGoalFinish,
|
|
620
|
+
handler: handleGoalFinish,
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
429
624
|
// 5. Регистрация HTTP REST API маршрутов и SSE событий
|
|
430
625
|
ctx.effect(() => {
|
|
431
626
|
if (!ctx.webServer?.register) return () => {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-goal",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
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",
|
|
@@ -14,10 +14,14 @@
|
|
|
14
14
|
"lib/",
|
|
15
15
|
"cordis.patch.yml",
|
|
16
16
|
"README.md",
|
|
17
|
-
"
|
|
17
|
+
"README.ru.md",
|
|
18
|
+
"README.zh.md",
|
|
18
19
|
"LICENSE",
|
|
19
|
-
"
|
|
20
|
+
"docs/design/DESIGN.md"
|
|
20
21
|
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "node --test test/*.test.mjs"
|
|
24
|
+
},
|
|
21
25
|
"keywords": [
|
|
22
26
|
"dsh",
|
|
23
27
|
"dsh-plugin",
|
|
@@ -55,8 +59,5 @@
|
|
|
55
59
|
},
|
|
56
60
|
"dependencies": {
|
|
57
61
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
58
|
-
},
|
|
59
|
-
"scripts": {
|
|
60
|
-
"test": "node --test test/*.test.mjs"
|
|
61
62
|
}
|
|
62
|
-
}
|
|
63
|
+
}
|
package/docs/README.zh.md
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
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.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
22
|
-
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
23
|
-
</p>
|
|
24
|
-
|
|
25
|
-
</div>
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## ⚡ 概述与核心解决痛点
|
|
30
|
-
|
|
31
|
-
复杂的软件工程任务往往需要多步骤的自主循环:将宏观目标分解为里程碑子任务、多轮自动连续推进而无需用户反复手动下发指令,以及对执行过程保持清晰的可视化监控。
|
|
32
|
-
|
|
33
|
-
**`@goodandready/dsh-goal`** 通过 `/goal` 指令为 DeepSeek Harness 带来原生的目标自主执行系统:
|
|
34
|
-
* 🎯 **顶部常驻状态条**:固定在聊天窗口顶部的状态横幅,显示实时运行计时器(`• 2s`, `• 1m 45s`)、当前目标标题与控制按钮。
|
|
35
|
-
* ⏸️ **自主循环控制 (Play / Pause / Cancel)**:可随时暂停智能体自主多轮循环,或根据需要一键恢复。
|
|
36
|
-
* 📋 **里程碑任务抽屉**:交互式子任务清单,展示完成百分比进度条与执行日志。
|
|
37
|
-
* 🤖 **智能体原生工具**:提供 `goal_set_milestones`, `goal_update_progress`, `goal_finish` 工具。
|
|
38
|
-
* 🛡️ **安全防护机制**:支持配置 `maxIterations` 最大循环轮次,杜绝死循环消耗。
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## 🏛️ 架构设计
|
|
43
|
-
|
|
44
|
-
```mermaid
|
|
45
|
-
graph TD
|
|
46
|
-
subgraph Input ["用户交互与输入"]
|
|
47
|
-
Cmd["聊天斜杠指令: /goal <目标描述>"]
|
|
48
|
-
API["REST API 路由: POST /dsh-goal/action"]
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
subgraph GoalEngine ["目标生命周期引擎 (lib/index.js)"]
|
|
52
|
-
State["状态机 (IDLE, RUNNING, PAUSED, COMPLETED)"]
|
|
53
|
-
Milestones["里程碑分解与进度追踪器"]
|
|
54
|
-
Disk["状态持久化存储 (~/.dsh/goal-state.json)"]
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
subgraph AgentLoop ["智能体自主执行驱动"]
|
|
58
|
-
ToolSet["goal_set_milestones"]
|
|
59
|
-
ToolProgress["goal_update_progress"]
|
|
60
|
-
ToolFinish["goal_finish"]
|
|
61
|
-
LimitGuard{"maxIterations 熔断防护"}
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
subgraph UI ["DSH Web 前端界面"]
|
|
65
|
-
Banner["顶部常驻状态横幅"]
|
|
66
|
-
Timer["实时执行计时器"]
|
|
67
|
-
Drawer["里程碑清单详情模态框"]
|
|
68
|
-
Settings["设置面板卡片 (Schemastery)"]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
Cmd --> GoalEngine
|
|
72
|
-
API --> GoalEngine
|
|
73
|
-
GoalEngine --> State
|
|
74
|
-
State --> Disk
|
|
75
|
-
State --> Banner
|
|
76
|
-
State --> Drawer
|
|
77
|
-
GoalEngine --> AgentLoop
|
|
78
|
-
AgentLoop --> LimitGuard
|
|
79
|
-
ToolSet --> GoalEngine
|
|
80
|
-
ToolProgress --> GoalEngine
|
|
81
|
-
ToolFinish --> GoalEngine
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## 📦 快速安装
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
dsh plugin --profile web add @goodandready/dsh-goal
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
重启 DeepSeek Harness 实例并刷新浏览器页面。
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## 💬 快速上手
|
|
97
|
-
|
|
98
|
-
在聊天框中直接启动自主目标:
|
|
99
|
-
|
|
100
|
-
```text
|
|
101
|
-
/goal 重构身份验证中间件并编写端到端集成测试
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
或通过 REST 接口调用:
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
curl -X POST http://localhost:3080/dsh-goal/action \
|
|
108
|
-
-H "Content-Type: application/json" \
|
|
109
|
-
-d '{"action":"start","title":"优化数据库查询性能"}'
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## ⚙️ 配置参数参考 (`settings.yaml`)
|
|
115
|
-
|
|
116
|
-
```yaml
|
|
117
|
-
dsh-goal:
|
|
118
|
-
maxIterations: 25
|
|
119
|
-
autoDrive: true
|
|
120
|
-
enableSound: true
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
| 参数项 | 类型 | 默认值 | 说明 |
|
|
124
|
-
|:---|:---|:---|:---|
|
|
125
|
-
| `maxIterations` | `number` | `25` | 安全熔断阈值:单目标允许的最大自主轮次 |
|
|
126
|
-
| `autoDrive` | `boolean` | `true` | 是否在轮次结束后自动驱动智能体继续推进 |
|
|
127
|
-
| `enableSound` | `boolean` | `true` | 目标完成时是否播放提示音效 |
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## 🧪 自动化测试
|
|
132
|
-
|
|
133
|
-
运行自动化测试套件:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
npm test
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## 📄 许可证
|
|
142
|
-
|
|
143
|
-
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|