@goodandready/dsh-goal 0.2.21 → 0.2.23
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/lib/engine-prompt.js +27 -0
- package/package.json +4 -12
- package/lib/card-form-state.js +0 -147
- package/lib/command-handler.js +0 -242
package/lib/engine-prompt.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { detectLanguage } from "./goal-engine.js";
|
|
1
2
|
import { isMilestoneBlocked } from './engine-milestones.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -100,3 +101,29 @@ export function buildStatePromptInjection(snapshot, pendingNudge) {
|
|
|
100
101
|
`3. When all milestones are completed, call tool goal_finish with a detailed summary of achieved results.`
|
|
101
102
|
);
|
|
102
103
|
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Format goal start prompt with mandatory milestone contract
|
|
108
|
+
* @param {string} goalText
|
|
109
|
+
* @param {string} [explicitLang]
|
|
110
|
+
* @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
export function formatGoalStartPrompt(goalText, explicitLang) {
|
|
113
|
+
const lang = explicitLang || detectLanguage(goalText);
|
|
114
|
+
if (lang === "zh") {
|
|
115
|
+
return `🎯 目标模式已激活 (Goal Mode): "${goalText}"\n\n` +
|
|
116
|
+
`自主执行契约:\n` +
|
|
117
|
+
`1. 第一步必选动作:在开展任何工作或回复之前,你的第一个动作必须是调用 goal_set_milestones 工具,提交包含 3-7 个具体连续步骤的工作计划。用户将在界面中实时查看此计划。\n` +
|
|
118
|
+
`2. 第二步:计划确立后开始执行任务。在开始每个步骤前,通过 goal_update_progress(milestone_id, "in_progress") 标记其为进行中。\n` +
|
|
119
|
+
`3. 第三步:完成某一步骤后,通过 goal_update_progress(milestone_id, "completed", "阶段简要成果") 标记为已完成。\n` +
|
|
120
|
+
`4. 第四步:当所有里程碑全部完成后,调用 goal_finish(summary) 工具并附带详尽的最终成果总结。`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return `🎯 Goal Mode activated: "${goalText}"\n\n` +
|
|
124
|
+
`STRICT AUTONOMOUS CONTRACT:\n` +
|
|
125
|
+
`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` +
|
|
126
|
+
`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` +
|
|
127
|
+
`3. STEP 3: Upon completing a milestone, mark it as completed via goal_update_progress(milestone_id, "completed", "brief milestone outcome").\n` +
|
|
128
|
+
`4. STEP 4: When all milestones are completed, call tool goal_finish(summary) with a comprehensive summary of achieved results.`;
|
|
129
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-goal",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.23",
|
|
4
4
|
"description": "Core Goal Addon (Token Budget, Milestones, Git Snapshots & Retrospective Reports) for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"
|
|
22
|
+
"build:client": "node scripts/build-client.mjs",
|
|
23
|
+
"test": "node scripts/build-client.mjs --check && NODE_ENV=test node --test test/*.test.mjs"
|
|
23
24
|
},
|
|
24
25
|
"keywords": [
|
|
25
26
|
"dsh",
|
|
@@ -55,18 +56,9 @@
|
|
|
55
56
|
"peerDependencies": {
|
|
56
57
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
57
58
|
"@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
58
|
-
"@deepseek-ai/schemastery": "^3.18.4",
|
|
59
|
-
"@deepseek-ai/dsh-goal": "^0.1.7-rc.2 || ^0.2.0-rc.1"
|
|
60
|
-
},
|
|
61
|
-
"devDependencies": {
|
|
62
59
|
"@deepseek-ai/schemastery": "^3.18.4"
|
|
63
60
|
},
|
|
64
|
-
"
|
|
61
|
+
"devDependencies": {
|
|
65
62
|
"@deepseek-ai/schemastery": "^3.18.4"
|
|
66
|
-
},
|
|
67
|
-
"peerDependenciesMeta": {
|
|
68
|
-
"@deepseek-ai/dsh-goal": {
|
|
69
|
-
"optional": true
|
|
70
|
-
}
|
|
71
63
|
}
|
|
72
64
|
}
|
package/lib/card-form-state.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure state and validation logic for dsh-goal settings form.
|
|
3
|
-
* Independent from DOM/React, 100% testable via node --test.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const DEFAULT_SETTINGS = {
|
|
7
|
-
maxIterations: 25,
|
|
8
|
-
autoDrive: true,
|
|
9
|
-
enableSound: true,
|
|
10
|
-
showQuickLaunchButton: true,
|
|
11
|
-
consecutiveToolFailureLimit: 3,
|
|
12
|
-
enableBrowserNotifications: true,
|
|
13
|
-
maxTokenBudget: 0,
|
|
14
|
-
budgetWarningThreshold: 80,
|
|
15
|
-
autoCheckpointOnMilestone: false,
|
|
16
|
-
storagePath: '',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const NUMERIC_FIELDS = {
|
|
20
|
-
maxIterations: 1,
|
|
21
|
-
consecutiveToolFailureLimit: 0,
|
|
22
|
-
maxTokenBudget: 0,
|
|
23
|
-
budgetWarningThreshold: 0,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Parse numeric settings field:
|
|
28
|
-
* - empty string / null / undefined -> undefined (reset override)
|
|
29
|
-
* - valid integer >= min -> number
|
|
30
|
-
* - otherwise -> NaN (invalid)
|
|
31
|
-
* @param {any} raw
|
|
32
|
-
* @param {number} [min=1]
|
|
33
|
-
* @returns {number|undefined}
|
|
34
|
-
*/
|
|
35
|
-
export function parseNumberField(raw, min = 1) {
|
|
36
|
-
if (raw === '' || raw === null || raw === undefined) {
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
const n = Number(raw);
|
|
40
|
-
if (!Number.isFinite(n) || n < min || !Number.isInteger(n)) {
|
|
41
|
-
return NaN;
|
|
42
|
-
}
|
|
43
|
-
return n;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Compute status for a specific setting field
|
|
48
|
-
* @param {string} field
|
|
49
|
-
* @param {any} draftValue
|
|
50
|
-
* @param {Object} snap - { value, base, user }
|
|
51
|
-
*/
|
|
52
|
-
export function getFieldStatus(field, draftValue, snap) {
|
|
53
|
-
const baseVal = snap?.base?.[field] ?? DEFAULT_SETTINGS[field];
|
|
54
|
-
const userVal = snap?.user?.[field];
|
|
55
|
-
const effectiveVal = snap?.value?.[field] ?? baseVal;
|
|
56
|
-
|
|
57
|
-
const isOverridden = userVal !== undefined && userVal !== null;
|
|
58
|
-
const currentVal = draftValue !== undefined ? draftValue : effectiveVal;
|
|
59
|
-
|
|
60
|
-
let invalid = false;
|
|
61
|
-
let isDirty = false;
|
|
62
|
-
|
|
63
|
-
if (field in NUMERIC_FIELDS) {
|
|
64
|
-
if (draftValue !== undefined) {
|
|
65
|
-
if (draftValue === '') {
|
|
66
|
-
invalid = false;
|
|
67
|
-
isDirty = userVal !== undefined;
|
|
68
|
-
} else {
|
|
69
|
-
const min = NUMERIC_FIELDS[field];
|
|
70
|
-
const parsed = parseNumberField(draftValue, min);
|
|
71
|
-
if (Number.isNaN(parsed)) {
|
|
72
|
-
invalid = true;
|
|
73
|
-
isDirty = true;
|
|
74
|
-
} else {
|
|
75
|
-
isDirty = parsed !== (userVal !== undefined ? userVal : baseVal);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} else if (field === 'storagePath') {
|
|
80
|
-
if (draftValue !== undefined) {
|
|
81
|
-
const cleanVal = typeof draftValue === 'string' ? draftValue.trim() : '';
|
|
82
|
-
isDirty = cleanVal !== (userVal !== undefined ? userVal : (baseVal || ''));
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
// boolean fields (autoDrive, enableSound, showQuickLaunchButton, enableBrowserNotifications, autoCheckpointOnMilestone)
|
|
86
|
-
if (draftValue !== undefined) {
|
|
87
|
-
isDirty = Boolean(draftValue) !== (userVal !== undefined ? Boolean(userVal) : Boolean(baseVal));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
value: currentVal,
|
|
93
|
-
baseValue: baseVal,
|
|
94
|
-
userValue: userVal,
|
|
95
|
-
isOverridden,
|
|
96
|
-
isDirty,
|
|
97
|
-
invalid,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Compute list of key-value writes for scope.set
|
|
103
|
-
* @param {Object|null} draft
|
|
104
|
-
* @param {Object} snap - { value, base, user }
|
|
105
|
-
* @returns {Array<[string, any]>}
|
|
106
|
-
*/
|
|
107
|
-
export function computeSavePlan(draft, snap) {
|
|
108
|
-
if (!draft) return [];
|
|
109
|
-
|
|
110
|
-
const writes = [];
|
|
111
|
-
const fields = [
|
|
112
|
-
'maxIterations',
|
|
113
|
-
'autoDrive',
|
|
114
|
-
'enableSound',
|
|
115
|
-
'showQuickLaunchButton',
|
|
116
|
-
'consecutiveToolFailureLimit',
|
|
117
|
-
'enableBrowserNotifications',
|
|
118
|
-
'maxTokenBudget',
|
|
119
|
-
'budgetWarningThreshold',
|
|
120
|
-
'autoCheckpointOnMilestone',
|
|
121
|
-
'storagePath',
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
for (const f of fields) {
|
|
125
|
-
if (draft[f] === undefined) continue;
|
|
126
|
-
|
|
127
|
-
const status = getFieldStatus(f, draft[f], snap);
|
|
128
|
-
if (status.invalid) continue;
|
|
129
|
-
|
|
130
|
-
if (status.isDirty) {
|
|
131
|
-
if (f in NUMERIC_FIELDS) {
|
|
132
|
-
if (draft[f] === '') {
|
|
133
|
-
writes.push([f, undefined]);
|
|
134
|
-
} else {
|
|
135
|
-
writes.push([f, parseNumberField(draft[f], NUMERIC_FIELDS[f])]);
|
|
136
|
-
}
|
|
137
|
-
} else if (f === 'storagePath') {
|
|
138
|
-
const clean = typeof draft[f] === 'string' ? draft[f].trim() : '';
|
|
139
|
-
writes.push([f, clean ? clean : undefined]);
|
|
140
|
-
} else {
|
|
141
|
-
writes.push([f, Boolean(draft[f])]);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return writes;
|
|
147
|
-
}
|
package/lib/command-handler.js
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { GoalState, detectLanguage } from './goal-engine.js';
|
|
3
|
-
|
|
4
|
-
export const USAGE_EN = 'Usage: /goal [<goal>|clear|pause|resume]';
|
|
5
|
-
export const USAGE_ZH = '用法: /goal [<目标描述>|clear|pause|resume]';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Parse input string for /goal command
|
|
9
|
-
* @param {string} rawInput
|
|
10
|
-
* @returns {{ action: string, text?: string }}
|
|
11
|
-
*/
|
|
12
|
-
export function parseGoalInput(rawInput = '') {
|
|
13
|
-
const input = String(rawInput || '').trim();
|
|
14
|
-
if (!input) {
|
|
15
|
-
return { action: 'show' };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const lower = input.toLowerCase();
|
|
19
|
-
if (lower === 'clear') return { action: 'clear' };
|
|
20
|
-
if (lower === 'pause') return { action: 'pause' };
|
|
21
|
-
if (lower === 'resume') return { action: 'resume' };
|
|
22
|
-
|
|
23
|
-
return { action: 'start', text: input };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Create a valid identified user message for agent followup
|
|
28
|
-
* @param {string} text
|
|
29
|
-
* @returns {Object}
|
|
30
|
-
*/
|
|
31
|
-
export function createGoalUserMessage(text) {
|
|
32
|
-
return {
|
|
33
|
-
id: randomUUID(),
|
|
34
|
-
role: 'user',
|
|
35
|
-
content: [{ type: 'text', text }],
|
|
36
|
-
source: { kind: 'user' },
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Format goal start prompt with mandatory milestone contract
|
|
42
|
-
* @param {string} goalText
|
|
43
|
-
* @param {string} [explicitLang]
|
|
44
|
-
* @returns {string}
|
|
45
|
-
*/
|
|
46
|
-
export function formatGoalStartPrompt(goalText, explicitLang) {
|
|
47
|
-
const lang = explicitLang || detectLanguage(goalText);
|
|
48
|
-
if (lang === 'zh') {
|
|
49
|
-
return `🎯 目标模式已激活 (Goal Mode): "${goalText}"\n\n` +
|
|
50
|
-
`自主执行契约:\n` +
|
|
51
|
-
`1. 第一步必选动作:在开展任何工作或回复之前,你的第一个动作必须是调用 goal_set_milestones 工具,提交包含 3-7 个具体连续步骤的工作计划。用户将在界面中实时查看此计划。\n` +
|
|
52
|
-
`2. 第二步:计划确立后开始执行任务。在开始每个步骤前,通过 goal_update_progress(milestone_id, "in_progress") 标记其为进行中。\n` +
|
|
53
|
-
`3. 第三步:完成某一步骤后,通过 goal_update_progress(milestone_id, "completed", "阶段简要成果") 标记为已完成。\n` +
|
|
54
|
-
`4. 第四步:当所有里程碑全部完成后,调用 goal_finish(summary) 工具并附带详尽的最终成果总结。`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return `🎯 Goal Mode activated: "${goalText}"\n\n` +
|
|
58
|
-
`STRICT AUTONOMOUS CONTRACT:\n` +
|
|
59
|
-
`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` +
|
|
60
|
-
`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` +
|
|
61
|
-
`3. STEP 3: Upon completing a milestone, mark it as completed via goal_update_progress(milestone_id, "completed", "brief milestone outcome").\n` +
|
|
62
|
-
`4. STEP 4: When all milestones are completed, call tool goal_finish(summary) with a comprehensive summary of achieved results.`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Format response for UI and manage agent task execution
|
|
67
|
-
* @param {import('./goal-engine.js').GoalEngine} engine
|
|
68
|
-
* @param {{ action: string, text?: string }} parsed
|
|
69
|
-
* @param {Object} [config]
|
|
70
|
-
* @param {Object} [agent]
|
|
71
|
-
* @param {string} [sessionId='default']
|
|
72
|
-
* @returns {{ kind: string, text: string }}
|
|
73
|
-
*/
|
|
74
|
-
export function executeGoalSlashCommand(engine, parsed, config = {}, agent = null, sessionId = 'default') {
|
|
75
|
-
const sid = sessionId || 'default';
|
|
76
|
-
const snap = engine.getGoalSnapshot(sid);
|
|
77
|
-
const lang = snap.lang || (parsed.text ? detectLanguage(parsed.text) : 'en');
|
|
78
|
-
const isZh = lang === 'zh';
|
|
79
|
-
const usage = isZh ? USAGE_ZH : USAGE_EN;
|
|
80
|
-
|
|
81
|
-
switch (parsed.action) {
|
|
82
|
-
case 'show': {
|
|
83
|
-
if (!snap.hasActiveGoal) {
|
|
84
|
-
return {
|
|
85
|
-
kind: 'success',
|
|
86
|
-
text: isZh ? `目标模式: 当前未设置活动目标。\n${usage}` : `Goal Mode: no active goal set.\n${usage}`,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const milestonesInfo = snap.milestones && snap.milestones.length > 0
|
|
91
|
-
? `\n\n${isZh ? '工作计划' : 'Work Plan'}:\n` +
|
|
92
|
-
snap.milestones
|
|
93
|
-
.map((m, i) => ` ${i + 1}. [${m.status.toUpperCase()}] ${m.title}${m.notes ? ` (${m.notes})` : ''}`)
|
|
94
|
-
.join('\n')
|
|
95
|
-
: '';
|
|
96
|
-
|
|
97
|
-
const etaText = snap.formattedETA ? ` (ETA: ${snap.formattedETA})` : '';
|
|
98
|
-
const tokensInfo = (snap.tokensUsage && snap.tokensUsage.totalTokens > 0)
|
|
99
|
-
? (isZh
|
|
100
|
-
? `\nToken: ${snap.tokensUsage.totalTokens.toLocaleString()} (输入: ${snap.tokensUsage.promptTokens.toLocaleString()}, 输出: ${snap.tokensUsage.completionTokens.toLocaleString()})`
|
|
101
|
-
: `\nTokens: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`)
|
|
102
|
-
: '';
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
kind: 'success',
|
|
106
|
-
text: isZh
|
|
107
|
-
? `🎯 目标: "${snap.title}"\n` +
|
|
108
|
-
`状态: ${snap.state}\n` +
|
|
109
|
-
`耗时: ${snap.formattedElapsed}${etaText}\n` +
|
|
110
|
-
`迭代轮次: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
111
|
-
tokensInfo +
|
|
112
|
-
milestonesInfo +
|
|
113
|
-
`\n\n快捷指令: /goal pause, /goal resume, /goal clear, /goal <新目标>`
|
|
114
|
-
: `🎯 Goal: "${snap.title}"\n` +
|
|
115
|
-
`Status: ${snap.state}\n` +
|
|
116
|
-
`Duration: ${snap.formattedElapsed}${etaText}\n` +
|
|
117
|
-
`Iterations: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
118
|
-
tokensInfo +
|
|
119
|
-
milestonesInfo +
|
|
120
|
-
`\n\nCommands: /goal pause, /goal resume, /goal clear, /goal <new goal>`,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
case 'clear': {
|
|
125
|
-
if (!snap.hasActiveGoal) {
|
|
126
|
-
return {
|
|
127
|
-
kind: 'success',
|
|
128
|
-
text: isZh ? '当前未设置目标。' : 'No active goal set.',
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
engine.clear(sid);
|
|
132
|
-
if (agent && typeof agent.cancel === 'function') {
|
|
133
|
-
try {
|
|
134
|
-
agent.cancel({ kind: 'user', reason: 'Goal cleared by user' });
|
|
135
|
-
} catch (err) {
|
|
136
|
-
// agent cancellation error handled defensively
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return {
|
|
140
|
-
kind: 'success',
|
|
141
|
-
text: isZh ? '🎯 目标已清除。' : '🎯 Goal cleared.',
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
case 'pause': {
|
|
146
|
-
if (!snap.hasActiveGoal) {
|
|
147
|
-
return {
|
|
148
|
-
kind: 'error',
|
|
149
|
-
text: isZh
|
|
150
|
-
? `无法暂停:未设置活动目标。\n${usage}`
|
|
151
|
-
: `Cannot pause: no active goal.\n${usage}`,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
if (snap.state === GoalState.PAUSED) {
|
|
155
|
-
return {
|
|
156
|
-
kind: 'success',
|
|
157
|
-
text: isZh ? '⏸ 目标当前已处于暂停状态。' : '⏸ Goal is already paused.',
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
engine.pause(isZh ? '用户命令暂停 (/goal pause)' : 'Paused by user command (/goal pause)', sid);
|
|
161
|
-
if (agent && typeof agent.cancel === 'function') {
|
|
162
|
-
try {
|
|
163
|
-
agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
|
|
164
|
-
} catch (err) {
|
|
165
|
-
// agent cancellation error handled defensively
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return {
|
|
169
|
-
kind: 'success',
|
|
170
|
-
text: isZh
|
|
171
|
-
? `⏸ 目标已暂停: "${snap.title}"。智能体已停止,使用 /goal resume 或 ▶️ 按钮继续。`
|
|
172
|
-
: `⏸ Goal paused: "${snap.title}". Agent stopped. Use /goal resume or ▶️ button to continue.`,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
case 'resume': {
|
|
177
|
-
if (!snap.hasActiveGoal) {
|
|
178
|
-
return {
|
|
179
|
-
kind: 'error',
|
|
180
|
-
text: isZh
|
|
181
|
-
? `无法恢复:未设置活动目标。\n${usage}`
|
|
182
|
-
: `Cannot resume: no active goal.\n${usage}`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
if (snap.state === GoalState.RUNNING) {
|
|
186
|
-
return {
|
|
187
|
-
kind: 'success',
|
|
188
|
-
text: isZh ? '▶️ 目标已在运行中。' : '▶️ Goal is already running.',
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
engine.resume(sid);
|
|
192
|
-
if (agent && typeof agent.followup === 'function') {
|
|
193
|
-
try {
|
|
194
|
-
const resumePrompt = isZh
|
|
195
|
-
? `▶️ 用户已恢复目标运行。请继续推进工作计划中尚未完成的步骤。通过 goal_update_progress 实时更新进度。`
|
|
196
|
-
: `▶️ Goal resumed by user. Continue executing the work plan from where you stopped. Update steps via goal_update_progress.`;
|
|
197
|
-
agent.followup(createGoalUserMessage(resumePrompt));
|
|
198
|
-
} catch (err) {
|
|
199
|
-
// agent followup error handled defensively
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
kind: 'success',
|
|
204
|
-
text: isZh
|
|
205
|
-
? `▶️ 目标已恢复: "${snap.title}"。智能体继续工作中。`
|
|
206
|
-
: `▶️ Goal resumed: "${snap.title}". Agent continues working.`,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
case 'start': {
|
|
211
|
-
const goalLang = detectLanguage(parsed.text);
|
|
212
|
-
const isZhStart = goalLang === 'zh';
|
|
213
|
-
const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
|
|
214
|
-
const cwd = config.cwd;
|
|
215
|
-
const newSnap = engine.startGoal(parsed.text, { maxIterations, lang: goalLang, cwd }, sid);
|
|
216
|
-
|
|
217
|
-
if (agent && typeof agent.followup === 'function') {
|
|
218
|
-
try {
|
|
219
|
-
const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text, goalLang));
|
|
220
|
-
agent.followup(userMsg);
|
|
221
|
-
} catch (err) {
|
|
222
|
-
// agent dispatch error handled defensively
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
kind: 'success',
|
|
228
|
-
text: isZhStart
|
|
229
|
-
? `🎯 目标已激活: "${newSnap.title}" (上限 ${newSnap.maxIterations} 轮迭代)。\n` +
|
|
230
|
-
`暂停: /goal pause | 清除: /goal clear`
|
|
231
|
-
: `🎯 Goal activated: "${newSnap.title}" (max ${newSnap.maxIterations} iterations).\n` +
|
|
232
|
-
`To pause: /goal pause | To clear: /goal clear`,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
default:
|
|
237
|
-
return {
|
|
238
|
-
kind: 'error',
|
|
239
|
-
text: isZh ? `未知指令。${usage}` : `Unknown command. ${usage}`,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
}
|