@goodandready/dsh-goal 0.2.11 → 0.2.12
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/client.js +142 -3
- package/lib/engine-store.js +4 -3
- package/lib/goal-engine.js +10 -3
- package/lib/index.js +32 -22
- package/lib/routes.js +31 -1
- package/lib/tools.js +2 -2
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1609,7 +1609,7 @@ window.__ModuleLoader__.load({
|
|
|
1609
1609
|
if (!AudioCtx) return;
|
|
1610
1610
|
const actx = new AudioCtx();
|
|
1611
1611
|
if (actx.state === 'suspended') {
|
|
1612
|
-
actx.resume().catch(() => {});
|
|
1612
|
+
actx.resume().catch((err) => { console.warn('[dsh-goal] Fetch templates error:', err); });
|
|
1613
1613
|
}
|
|
1614
1614
|
const now = actx.currentTime;
|
|
1615
1615
|
if (scheme === 'retro') {
|
|
@@ -2590,9 +2590,51 @@ window.__ModuleLoader__.load({
|
|
|
2590
2590
|
function QuickLaunchModal({ onClose, onStart, t }) {
|
|
2591
2591
|
|
|
2592
2592
|
const [title, setTitle] = useState('');
|
|
2593
|
+
const [showTemplates, setShowTemplates] = useState(false);
|
|
2594
|
+
const [backendTemplates, setBackendTemplates] = useState([]);
|
|
2595
|
+
const [selectedTpl, setSelectedTpl] = useState(null);
|
|
2596
|
+
const [tplInputs, setTplInputs] = useState({});
|
|
2597
|
+
const [loadingTpls, setLoadingTpls] = useState(false);
|
|
2593
2598
|
|
|
2594
2599
|
const inputRef = useRef(null);
|
|
2595
2600
|
|
|
2601
|
+
useEffect(() => {
|
|
2602
|
+
if (showTemplates && backendTemplates.length === 0) {
|
|
2603
|
+
setLoadingTpls(true);
|
|
2604
|
+
fetch('/dsh-goal/templates')
|
|
2605
|
+
.then((r) => r.json())
|
|
2606
|
+
.then((d) => {
|
|
2607
|
+
if (d && Array.isArray(d.templates)) {
|
|
2608
|
+
setBackendTemplates(d.templates);
|
|
2609
|
+
}
|
|
2610
|
+
})
|
|
2611
|
+
.catch((err) => { console.warn('[dsh-goal] Fetch templates error:', err); })
|
|
2612
|
+
.finally(() => setLoadingTpls(false));
|
|
2613
|
+
}
|
|
2614
|
+
}, [showTemplates, backendTemplates.length]);
|
|
2615
|
+
|
|
2616
|
+
const handleApplyTemplate = async () => {
|
|
2617
|
+
if (!selectedTpl) return;
|
|
2618
|
+
try {
|
|
2619
|
+
const res = await fetch('/dsh-goal/action', {
|
|
2620
|
+
method: 'POST',
|
|
2621
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2622
|
+
body: JSON.stringify({
|
|
2623
|
+
action: 'instantiate_template',
|
|
2624
|
+
templateId: selectedTpl.id,
|
|
2625
|
+
inputs: tplInputs,
|
|
2626
|
+
}),
|
|
2627
|
+
});
|
|
2628
|
+
if (res.ok) {
|
|
2629
|
+
const data = await res.json();
|
|
2630
|
+
if (data.ok && data.template) {
|
|
2631
|
+
setTitle(data.template.title);
|
|
2632
|
+
setShowTemplates(false);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
} catch (err) { console.warn('[dsh-goal] Apply template error:', err); }
|
|
2636
|
+
};
|
|
2637
|
+
|
|
2596
2638
|
useEffect(() => {
|
|
2597
2639
|
|
|
2598
2640
|
if (inputRef.current) inputRef.current.focus();
|
|
@@ -2782,8 +2824,94 @@ window.__ModuleLoader__.load({
|
|
|
2782
2824
|
},
|
|
2783
2825
|
tpl.label
|
|
2784
2826
|
)
|
|
2827
|
+
),
|
|
2828
|
+
React.createElement(
|
|
2829
|
+
'button',
|
|
2830
|
+
{
|
|
2831
|
+
type: 'button',
|
|
2832
|
+
className: 'dsh-goal-chip-btn',
|
|
2833
|
+
style: { fontWeight: 600, borderColor: 'var(--dsw-alias-border-brand, var(--dsw-alias-border-l2))' },
|
|
2834
|
+
onClick: () => setShowTemplates(!showTemplates),
|
|
2835
|
+
},
|
|
2836
|
+
(showTemplates ? '▲ ' : '▼ ') + '📋 ' + (t('templatesTitle') || 'Engineering Templates')
|
|
2785
2837
|
)
|
|
2786
2838
|
),
|
|
2839
|
+
showTemplates
|
|
2840
|
+
? React.createElement(
|
|
2841
|
+
'div',
|
|
2842
|
+
{
|
|
2843
|
+
style: {
|
|
2844
|
+
marginTop: 10,
|
|
2845
|
+
padding: '10px 12px',
|
|
2846
|
+
borderRadius: 6,
|
|
2847
|
+
border: '1px solid var(--dsw-alias-border-l1)',
|
|
2848
|
+
background: 'var(--dsw-alias-bg-hover)',
|
|
2849
|
+
},
|
|
2850
|
+
},
|
|
2851
|
+
loadingTpls
|
|
2852
|
+
? React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'Loading templates…')
|
|
2853
|
+
: React.createElement(
|
|
2854
|
+
'div',
|
|
2855
|
+
null,
|
|
2856
|
+
React.createElement(
|
|
2857
|
+
'div',
|
|
2858
|
+
{ style: { display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 } },
|
|
2859
|
+
backendTemplates.map((bt) =>
|
|
2860
|
+
React.createElement(
|
|
2861
|
+
'button',
|
|
2862
|
+
{
|
|
2863
|
+
key: bt.id,
|
|
2864
|
+
type: 'button',
|
|
2865
|
+
className: `dsh-goal-chip-btn ${selectedTpl?.id === bt.id ? 'active' : ''}`,
|
|
2866
|
+
style: selectedTpl?.id === bt.id ? { fontWeight: 600, border: '1px solid var(--dsw-alias-accent-primary)' } : {},
|
|
2867
|
+
onClick: () => {
|
|
2868
|
+
setSelectedTpl(bt);
|
|
2869
|
+
const initial = {};
|
|
2870
|
+
(bt.inputs || []).forEach((inp) => {
|
|
2871
|
+
initial[inp.key] = inp.defaultValue || '';
|
|
2872
|
+
});
|
|
2873
|
+
setTplInputs(initial);
|
|
2874
|
+
},
|
|
2875
|
+
},
|
|
2876
|
+
bt.title
|
|
2877
|
+
)
|
|
2878
|
+
)
|
|
2879
|
+
),
|
|
2880
|
+
selectedTpl
|
|
2881
|
+
? React.createElement(
|
|
2882
|
+
'div',
|
|
2883
|
+
{ style: { borderTop: '1px solid var(--dsw-alias-border-l1)', paddingTop: 8 } },
|
|
2884
|
+
React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginBottom: 6 } }, selectedTpl.description),
|
|
2885
|
+
(selectedTpl.inputs || []).map((inp) =>
|
|
2886
|
+
React.createElement(
|
|
2887
|
+
'div',
|
|
2888
|
+
{ key: inp.key, style: { marginBottom: 6 } },
|
|
2889
|
+
React.createElement('label', { style: { fontSize: 11, display: 'block', marginBottom: 2 } }, inp.label),
|
|
2890
|
+
React.createElement('input', {
|
|
2891
|
+
type: 'text',
|
|
2892
|
+
className: 'dsh-goal-card-input',
|
|
2893
|
+
style: { width: '100%', boxSizing: 'border-box', fontSize: 12, padding: '4px 8px' },
|
|
2894
|
+
placeholder: inp.placeholder || '',
|
|
2895
|
+
value: tplInputs[inp.key] || '',
|
|
2896
|
+
onChange: (e) => setTplInputs({ ...tplInputs, [inp.key]: e.target.value }),
|
|
2897
|
+
})
|
|
2898
|
+
)
|
|
2899
|
+
),
|
|
2900
|
+
React.createElement(
|
|
2901
|
+
'button',
|
|
2902
|
+
{
|
|
2903
|
+
type: 'button',
|
|
2904
|
+
className: 'dsh-goal-btn dsh-goal-btn-primary',
|
|
2905
|
+
style: { marginTop: 4, padding: '4px 10px', fontSize: 12 },
|
|
2906
|
+
onClick: handleApplyTemplate,
|
|
2907
|
+
},
|
|
2908
|
+
'Apply Template'
|
|
2909
|
+
)
|
|
2910
|
+
)
|
|
2911
|
+
: null
|
|
2912
|
+
)
|
|
2913
|
+
)
|
|
2914
|
+
: null
|
|
2787
2915
|
),
|
|
2788
2916
|
|
|
2789
2917
|
React.createElement(
|
|
@@ -3398,6 +3526,17 @@ window.__ModuleLoader__.load({
|
|
|
3398
3526
|
|
|
3399
3527
|
const isCompleted = state.state === 'COMPLETED';
|
|
3400
3528
|
|
|
3529
|
+
const formatElapsed = (totalSeconds) => {
|
|
3530
|
+
const s = Math.max(0, Math.floor(Number(totalSeconds) || 0));
|
|
3531
|
+
if (s < 60) return `${s}s`;
|
|
3532
|
+
const m = Math.floor(s / 60);
|
|
3533
|
+
const remS = s % 60;
|
|
3534
|
+
if (m < 60) return remS > 0 ? `${m}m ${remS}s` : `${m}m`;
|
|
3535
|
+
const h = Math.floor(m / 60);
|
|
3536
|
+
const remM = m % 60;
|
|
3537
|
+
return remM > 0 ? `${h}h ${remM}m` : `${h}h`;
|
|
3538
|
+
};
|
|
3539
|
+
|
|
3401
3540
|
const formatLiveElapsed = () => {
|
|
3402
3541
|
|
|
3403
3542
|
if (!state) return '0s';
|
|
@@ -3821,7 +3960,7 @@ window.__ModuleLoader__.load({
|
|
|
3821
3960
|
const [updateState, setUpdateState] = useState({
|
|
3822
3961
|
checking: false,
|
|
3823
3962
|
updating: false,
|
|
3824
|
-
currentVersion: '
|
|
3963
|
+
currentVersion: '',
|
|
3825
3964
|
latestVersion: '',
|
|
3826
3965
|
updateAvailable: false,
|
|
3827
3966
|
notice: '',
|
|
@@ -4159,7 +4298,7 @@ window.__ModuleLoader__.load({
|
|
|
4159
4298
|
'div',
|
|
4160
4299
|
{ style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
4161
4300
|
React.createElement('span', { style: { fontSize: 13, fontWeight: 500, color: 'var(--dsw-alias-label-primary)' } }, t('version') || 'Version'),
|
|
4162
|
-
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'v' +
|
|
4301
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, updateState.currentVersion ? 'v' + updateState.currentVersion : 'v0.2.12'),
|
|
4163
4302
|
updateState.updateAvailable
|
|
4164
4303
|
? React.createElement('span', { className: 'dsh-goal-badge dsh-goal-badge-warn', style: { fontSize: 11 } }, 'v' + updateState.latestVersion + ' ' + (t('updateAvailable') || 'available'))
|
|
4165
4304
|
: React.createElement('span', { className: 'dsh-goal-badge dsh-goal-badge-ok', style: { fontSize: 11 } }, '✓ ' + (t('upToDate') || 'Up to date')),
|
package/lib/engine-store.js
CHANGED
|
@@ -6,9 +6,10 @@ import { GoalState } from './goal-engine-constants.js';
|
|
|
6
6
|
* Storage manager for goal engine disk state persistence
|
|
7
7
|
*/
|
|
8
8
|
export class EngineStore {
|
|
9
|
-
constructor(engine, storagePath) {
|
|
9
|
+
constructor(engine, storagePath, logger) {
|
|
10
10
|
this.engine = engine;
|
|
11
11
|
this.storagePath = storagePath || null;
|
|
12
|
+
this.logger = logger || engine?.logger || console;
|
|
12
13
|
this.saveTimer = null;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -77,7 +78,7 @@ export class EngineStore {
|
|
|
77
78
|
}
|
|
78
79
|
} catch (err) {
|
|
79
80
|
// best-effort load failure logged defensively
|
|
80
|
-
|
|
81
|
+
this.logger?.warn?.('[GoalEngine] Failed to load state from disk:', err?.message || err);
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -128,7 +129,7 @@ export class EngineStore {
|
|
|
128
129
|
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf8');
|
|
129
130
|
fs.renameSync(tmp, this.storagePath);
|
|
130
131
|
} catch (err) {
|
|
131
|
-
|
|
132
|
+
this.logger?.warn?.('[GoalEngine] Failed to write state to disk:', err?.message || err);
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
|
package/lib/goal-engine.js
CHANGED
|
@@ -5,12 +5,12 @@ import { buildStatePromptInjection } from './engine-prompt.js';
|
|
|
5
5
|
import { matchMilestone, parseMilestoneItems, applyMilestoneUpdate, toggleMilestoneChecklistItem } from './engine-milestones.js';
|
|
6
6
|
import { applyIntervention, recordActivity, processTokenBudget } from './engine-intervention.js';
|
|
7
7
|
import { createGoalBranch, sanitizeBranchSlug, getCurrentGitBranch, mergeGoalBranch, discardGoalBranch } from './engine-git-branch.js';
|
|
8
|
-
import { parseIssueRef, syncMilestoneWithIssue } from './engine-issue-sync.js';
|
|
8
|
+
import { parseIssueRef, parseIssueChecklist, syncMilestoneWithIssue } from './engine-issue-sync.js';
|
|
9
9
|
import { generateRetrospectiveData } from './engine-retrospective.js';
|
|
10
10
|
import { generatePreplanDraft } from './engine-templates.js';
|
|
11
11
|
import { pruneInactiveSessions, addNudgeToGoal, consumeGoalNudge, extendGoalBudget } from './engine-sessions.js';
|
|
12
12
|
|
|
13
|
-
export { GoalState, MilestoneStatus, formatElapsed, formatETA, detectLanguage, sessionIdOf, matchMilestone };
|
|
13
|
+
export { GoalState, MilestoneStatus, formatElapsed, formatETA, detectLanguage, sessionIdOf, matchMilestone, parseIssueChecklist };
|
|
14
14
|
export { getGitCurrentCommit, exportReportMarkdown, exportReportGitHubPR, createMilestoneCheckpoint, rollbackToCheckpoint, saveGoalArtifact };
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -73,7 +73,7 @@ export class GoalEngine {
|
|
|
73
73
|
try {
|
|
74
74
|
listener(snapshot, sid);
|
|
75
75
|
} catch (err) {
|
|
76
|
-
|
|
76
|
+
this.logger?.error?.('[dsh-goal] Error in GoalEngine listener:', err);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -234,6 +234,13 @@ export class GoalEngine {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
if (options.issueBody && (!options.milestones || options.milestones.length === 0)) {
|
|
238
|
+
const items = parseIssueChecklist(options.issueBody);
|
|
239
|
+
if (items.length > 0) {
|
|
240
|
+
options.milestones = items.map((it) => ({ title: it.text, status: it.done ? MilestoneStatus.COMPLETED : MilestoneStatus.PENDING }));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
237
244
|
if (this.enablePreplanning && options.preplan !== false && (!options.milestones || options.milestones.length === 0)) {
|
|
238
245
|
goal.preplan = { active: true, draftMilestones: generatePreplanDraft(cleanTitle) };
|
|
239
246
|
}
|
package/lib/index.js
CHANGED
|
@@ -65,9 +65,12 @@ export function apply(ctx, config = {}) {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
const defaultStorageDir = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');
|
|
68
|
-
const
|
|
69
|
-
? currentSettings.storagePath
|
|
70
|
-
:
|
|
68
|
+
const customStorage = typeof currentSettings.storagePath === 'string' && currentSettings.storagePath.trim()
|
|
69
|
+
? currentSettings.storagePath.trim()
|
|
70
|
+
: null;
|
|
71
|
+
const isTest = process.env.NODE_ENV === 'test' || Boolean(process.env.NODE_TEST_CONTEXT);
|
|
72
|
+
const storagePath = customStorage || (isTest ? null : path.join(defaultStorageDir, 'dsh-goal-state.json'));
|
|
73
|
+
const logger = ctx.logger ? ctx.logger('goal') : console;
|
|
71
74
|
|
|
72
75
|
const engine = new GoalEngine({
|
|
73
76
|
defaultMaxIterations: currentSettings.maxIterations,
|
|
@@ -88,6 +91,7 @@ export function apply(ctx, config = {}) {
|
|
|
88
91
|
soundScheme: currentSettings.soundScheme,
|
|
89
92
|
enableVoiceAnnouncements: currentSettings.enableVoiceAnnouncements,
|
|
90
93
|
storagePath,
|
|
94
|
+
logger,
|
|
91
95
|
});
|
|
92
96
|
|
|
93
97
|
// Track running agents through global DSH core event
|
|
@@ -122,7 +126,7 @@ export function apply(ctx, config = {}) {
|
|
|
122
126
|
try {
|
|
123
127
|
specificAgent.cancel({ kind: 'user' });
|
|
124
128
|
} catch (err) {
|
|
125
|
-
|
|
129
|
+
logger.warn('Failed to cancel specific agent for session:', err);
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
|
|
@@ -135,7 +139,7 @@ export function apply(ctx, config = {}) {
|
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
} catch (err) {
|
|
138
|
-
|
|
142
|
+
logger.warn('Failed to cancel running agent:', err);
|
|
139
143
|
}
|
|
140
144
|
}
|
|
141
145
|
|
|
@@ -145,7 +149,7 @@ export function apply(ctx, config = {}) {
|
|
|
145
149
|
try {
|
|
146
150
|
lastActiveAgent.cancel({ kind: 'user' });
|
|
147
151
|
} catch (err) {
|
|
148
|
-
|
|
152
|
+
logger.warn('Failed to cancel lastActiveAgent:', err);
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
155
|
}
|
|
@@ -193,7 +197,7 @@ export function apply(ctx, config = {}) {
|
|
|
193
197
|
sessionAgents.set(sid, target);
|
|
194
198
|
return true;
|
|
195
199
|
} catch (err) {
|
|
196
|
-
|
|
200
|
+
logger.warn('Failed to resume agent:', err);
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
203
|
return false;
|
|
@@ -263,7 +267,7 @@ export function apply(ctx, config = {}) {
|
|
|
263
267
|
}
|
|
264
268
|
}
|
|
265
269
|
} catch (err) {
|
|
266
|
-
|
|
270
|
+
logger.warn('Settings register skipped:', err.message);
|
|
267
271
|
}
|
|
268
272
|
});
|
|
269
273
|
|
|
@@ -291,7 +295,7 @@ export function apply(ctx, config = {}) {
|
|
|
291
295
|
cctx.effect(() => () => unregister(), 'dsh-goal: /goal command unregister');
|
|
292
296
|
}
|
|
293
297
|
} catch (err) {
|
|
294
|
-
|
|
298
|
+
logger.warn('Commands register skipped:', err.message);
|
|
295
299
|
}
|
|
296
300
|
});
|
|
297
301
|
|
|
@@ -329,24 +333,30 @@ export function apply(ctx, config = {}) {
|
|
|
329
333
|
}
|
|
330
334
|
}
|
|
331
335
|
} catch (err) {
|
|
332
|
-
|
|
336
|
+
logger.warn('SystemPrompt section register skipped:', err.message);
|
|
333
337
|
}
|
|
334
338
|
});
|
|
335
339
|
|
|
336
340
|
// 4. Tools registration
|
|
337
|
-
registerTools(ctx, { engine, resumeActiveAgent });
|
|
341
|
+
registerTools(ctx, { engine, resumeActiveAgent, logger });
|
|
338
342
|
|
|
339
|
-
// 5. One-click auto-updater registration
|
|
343
|
+
// 5. One-click auto-updater registration with Cordis effect cleanup (Issue #99)
|
|
340
344
|
if (ctx.webServer?.register) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
345
|
+
ctx.effect(() => {
|
|
346
|
+
let unreg = null;
|
|
347
|
+
try {
|
|
348
|
+
unreg = registerPluginUpdater(ctx, {
|
|
349
|
+
packageName: '@goodandready/dsh-goal',
|
|
350
|
+
endpoint: '/api/dsh-goal/update',
|
|
351
|
+
manifestUrl: new URL('../package.json', import.meta.url),
|
|
352
|
+
});
|
|
353
|
+
} catch (err) {
|
|
354
|
+
logger.warn('Failed to register plugin updater:', err.message);
|
|
355
|
+
}
|
|
356
|
+
return () => {
|
|
357
|
+
if (typeof unreg === 'function') unreg();
|
|
358
|
+
};
|
|
359
|
+
}, 'dsh-goal: plugin auto-updater');
|
|
350
360
|
}
|
|
351
361
|
|
|
352
362
|
// 6. WebServer HTTP & SSE routes
|
|
@@ -457,7 +467,7 @@ export function apply(ctx, config = {}) {
|
|
|
457
467
|
target.followup(promptMsg);
|
|
458
468
|
sessionAgents.set(sid, target);
|
|
459
469
|
} catch (err) {
|
|
460
|
-
|
|
470
|
+
logger.warn('Failed auto-drive followup:', err);
|
|
461
471
|
}
|
|
462
472
|
}
|
|
463
473
|
}
|
package/lib/routes.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { MilestoneStatus, detectLanguage, sessionIdOf } from './goal-engine-constants.js';
|
|
4
4
|
import { saveGoalArtifact, rollbackToCheckpoint } from './engine-reports.js';
|
|
5
|
-
import { getTemplatesCatalogue } from './engine-templates.js';
|
|
5
|
+
import { getTemplatesCatalogue, instantiateTemplate } from './engine-templates.js';
|
|
6
6
|
|
|
7
7
|
export function isLoopback(value) {
|
|
8
8
|
if (!value) return false;
|
|
@@ -297,6 +297,7 @@ export function registerRoutes(ctx, {
|
|
|
297
297
|
maxIterations: conf.maxIterations || 25,
|
|
298
298
|
lang: detectedLang,
|
|
299
299
|
cwd: safeStartCwd,
|
|
300
|
+
issueBody: data.issueBody,
|
|
300
301
|
}, sid);
|
|
301
302
|
|
|
302
303
|
const startPrompt = detectedLang === 'zh'
|
|
@@ -511,6 +512,21 @@ export function registerRoutes(ctx, {
|
|
|
511
512
|
res.statusCode = 200;
|
|
512
513
|
return res.end(JSON.stringify({ ok: true, templates: getTemplatesCatalogue() }));
|
|
513
514
|
|
|
515
|
+
case 'instantiate_template': {
|
|
516
|
+
const tplId = data.templateId || data.id;
|
|
517
|
+
if (!tplId) {
|
|
518
|
+
res.statusCode = 400;
|
|
519
|
+
return res.end(JSON.stringify({ error: 'templateId is required' }));
|
|
520
|
+
}
|
|
521
|
+
const instance = instantiateTemplate(tplId, data.inputs || {});
|
|
522
|
+
if (!instance) {
|
|
523
|
+
res.statusCode = 404;
|
|
524
|
+
return res.end(JSON.stringify({ error: `Template "${tplId}" not found` }));
|
|
525
|
+
}
|
|
526
|
+
res.statusCode = 200;
|
|
527
|
+
return res.end(JSON.stringify({ ok: true, template: instance }));
|
|
528
|
+
}
|
|
529
|
+
|
|
514
530
|
default:
|
|
515
531
|
res.statusCode = 400;
|
|
516
532
|
return res.end(JSON.stringify({ error: `Unknown action: ${action}` }));
|
|
@@ -526,6 +542,20 @@ export function registerRoutes(ctx, {
|
|
|
526
542
|
return;
|
|
527
543
|
}
|
|
528
544
|
|
|
545
|
+
const KNOWN_ROUTES = {
|
|
546
|
+
'/dsh-goal/events': ['GET'],
|
|
547
|
+
'/dsh-goal/templates': ['GET'],
|
|
548
|
+
'/dsh-goal/state': ['GET'],
|
|
549
|
+
'/dsh-goal/action': ['POST'],
|
|
550
|
+
};
|
|
551
|
+
const cleanPath = pathname.replace(/\/+$/, '') || '/';
|
|
552
|
+
const allowedMethods = KNOWN_ROUTES[cleanPath];
|
|
553
|
+
if (allowedMethods && !allowedMethods.includes(req.method)) {
|
|
554
|
+
res.statusCode = 405;
|
|
555
|
+
res.setHeader('Allow', allowedMethods.join(', '));
|
|
556
|
+
return res.end(JSON.stringify({ error: `Method Not Allowed: use ${allowedMethods.join(', ')}` }));
|
|
557
|
+
}
|
|
558
|
+
|
|
529
559
|
res.statusCode = 404;
|
|
530
560
|
res.end(JSON.stringify({ error: 'Endpoint not found' }));
|
|
531
561
|
},
|
package/lib/tools.js
CHANGED
|
@@ -42,7 +42,7 @@ export function formatGoalValue(snap) {
|
|
|
42
42
|
* @param {any} options.engine
|
|
43
43
|
* @param {Function} [options.resumeActiveAgent]
|
|
44
44
|
*/
|
|
45
|
-
export function registerTools(ctx, { engine, resumeActiveAgent }) {
|
|
45
|
+
export function registerTools(ctx, { engine, resumeActiveAgent, logger = console }) {
|
|
46
46
|
ctx.inject(['tools'], (tctx) => {
|
|
47
47
|
if (!tctx.tools?.register) return;
|
|
48
48
|
|
|
@@ -65,7 +65,7 @@ export function registerTools(ctx, { engine, resumeActiveAgent }) {
|
|
|
65
65
|
}, `dsh-goal: tool ${name}`);
|
|
66
66
|
}
|
|
67
67
|
} catch (err) {
|
|
68
|
-
|
|
68
|
+
logger.warn(`[dsh-goal] Tool ${definition.name} registration skipped:`, err.message);
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
71
|
|
package/package.json
CHANGED