dsh-nextsteps 1.3.3 → 1.4.0
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/client.js +10 -7
- package/index.js +51 -25
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -9,7 +9,7 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
const TOOL_NAME = 'suggest_followups';
|
|
10
10
|
const MAX_FOLLOWUPS = 6;
|
|
11
11
|
const PROMPT_TRUNCATE = 60;
|
|
12
|
-
const CLICKED_KEY = 'dsh
|
|
12
|
+
const CLICKED_KEY = 'dsh-nextsteps.clicked';
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
const CSS = `
|
|
@@ -226,9 +226,12 @@ window.__ModuleLoader__.load({
|
|
|
226
226
|
function captureForTurn(sessionId, turn) {
|
|
227
227
|
const entries = captures.get(sessionId);
|
|
228
228
|
if (entries === undefined) return null;
|
|
229
|
+
const external = typeof pluginSettings.model === 'string' && pluginSettings.model !== '';
|
|
229
230
|
let best = null;
|
|
230
|
-
for (const entry of entries.
|
|
231
|
-
if (entry.turn
|
|
231
|
+
for (const [callId, entry] of entries.entries()) {
|
|
232
|
+
if (entry.turn !== turn) continue;
|
|
233
|
+
if (external && !callId.startsWith('generated-')) continue;
|
|
234
|
+
best = entry;
|
|
232
235
|
}
|
|
233
236
|
return best;
|
|
234
237
|
}
|
|
@@ -363,9 +366,9 @@ window.__ModuleLoader__.load({
|
|
|
363
366
|
const SETTINGS_ROUTE = '/api/dsh-nextsteps.settings';
|
|
364
367
|
const UPDATE_ROUTE = '/api/dsh-nextsteps.update';
|
|
365
368
|
const DICTIONARIES = {
|
|
366
|
-
en: {"header":"Suggested followups","showEarlier":"Show earlier suggestions","hideEarlier":"Hide earlier suggestions","srFollowupSent":"Followup sent","sendFailed":"Could not send: ","intro":"Suggested next-step prompts under every finished agent turn — one click sends the followup.","setLanguage":"Language","setLanguageTip":"Followups are generated in this language. Auto follows the conversation.","optAuto":"Auto (follow the conversation)","optEn":"English","optPt":"Português (Brasil)","optZh":"简体中文","setModel":"Model","setModelTip":"The model that generates the suggestions. Auto keeps them inside the main turn.","optModelAuto":"Auto (same model as the chat)","setMax":"Followups per turn","setMaxTip":"How many suggested prompts the model should produce (1-6).","setHeader":"Show header","setHeaderTip":"The small “Suggested followups” label above the cards.","updates":"Updates","current":"Version","checking":"Checking…","upToDate":"up to date","available":"{v} is available","manualCopy":"Development or renamed copy — update it manually.","installing":"Installing {v}…","restartMsg":"Version {v} was installed. Restart the dsh to use it.","noReleases":"No releases published yet","checkFailed":"Update check failed ({e})","updateNow":"Update now","check":"Check for updates","openRelease":"Open release page","releaseNotes":"Release notes","repo":"GitHub repository","issues":"Report an issue"},
|
|
367
|
-
'pt-BR': {"header":"Sugestões de acompanhamento","showEarlier":"Mostrar sugestões anteriores","hideEarlier":"Ocultar sugestões anteriores","srFollowupSent":"Follow-up enviado","sendFailed":"Não consegui enviar: ","intro":"Sugestões de próximos passos sob cada turno concluído do agente — um clique envia o followup.","setLanguage":"Idioma","setLanguageTip":"Os followups são gerados neste idioma. Automático segue a conversa.","optAuto":"Automático (segue a conversa)","optEn":"English","optPt":"Português (Brasil)","optZh":"简体中文","setModel":"Modelo","setModelTip":"O modelo que gera as sugestões. Automático as mantém no turno principal.","optModelAuto":"Automático (mesmo modelo da conversa)","setMax":"Followups por turno","setMaxTip":"Quantas sugestões o modelo deve produzir (1-6).","setHeader":"Mostrar cabeçalho","setHeaderTip":"O rótulo pequeno “Sugestões de acompanhamento” acima dos cartões.","updates":"Atualizações","current":"Versão","checking":"Verificando…","upToDate":"em dia","available":"{v} disponível","manualCopy":"Cópia de desenvolvimento ou renomeada — atualize manualmente.","installing":"Instalando {v}…","restartMsg":"Versão {v} instalada. Reinicie o dsh para usá-la.","noReleases":"Nenhum release publicado ainda","checkFailed":"Falha ao verificar atualizações ({e})","updateNow":"Atualizar agora","check":"Verificar atualizações","openRelease":"Abrir página do release","releaseNotes":"Notas do release","repo":"Repositório no GitHub","issues":"Reportar um problema"},
|
|
368
|
-
'zh-CN': {"header":"建议的后续操作","showEarlier":"显示较早的建议","hideEarlier":"隐藏较早的建议","srFollowupSent":"后续操作已发送","sendFailed":"发送失败:","intro":"在每个智能体回合完成后的下方推荐下一步提示——单击即可发送。","setLanguage":"语言","setLanguageTip":"后续操作提示将以此语言生成。“自动”会跟随对话语言。","optAuto":"自动(跟随对话)","optEn":"English","optPt":"Português (Brasil)","optZh":"简体中文","setModel":"模型","setModelTip":"生成建议的模型。自动会跟随对话所用的模型。","optModelAuto":"自动(与对话相同的模型)","setMax":"每回合提示数","setMaxTip":"模型应生成的建议提示数量(1-6)。","setHeader":"显示标题","setHeaderTip":"卡片上方的小标题“建议的后续操作”。","updates":"更新","current":"版本","checking":"检查中…","upToDate":"已是最新","available":"{v} 可用","manualCopy":"开发或重命名的副本——请手动更新。","installing":"正在安装 {v}…","restartMsg":"版本 {v} 已安装。请重启 dsh 以使用。","noReleases":"尚未发布任何版本","checkFailed":"检查更新失败({e})","updateNow":"立即更新","check":"检查更新","openRelease":"打开发布页","releaseNotes":"发布说明","repo":"GitHub 仓库","issues":"反馈问题"}
|
|
369
|
+
en: {"header":"Suggested followups","showEarlier":"Show earlier suggestions","hideEarlier":"Hide earlier suggestions","srFollowupSent":"Followup sent","sendFailed":"Could not send: ","intro":"Suggested next-step prompts under every finished agent turn — one click sends the followup.","setLanguage":"Language","setLanguageTip":"Followups are generated in this language. Auto follows the conversation.","optAuto":"Auto (follow the conversation)","optEn":"English","optPt":"Português (Brasil)","optZh":"简体中文","setModel":"Model","setModelTip":"The model that generates the suggestions. Auto keeps them inside the main turn.","optModelAuto":"Auto (same model as the chat)","modelUnavailable":"No model list available in this install","setMax":"Followups per turn","setMaxTip":"How many suggested prompts the model should produce (1-6).","setHeader":"Show header","setHeaderTip":"The small “Suggested followups” label above the cards.","updates":"Updates","current":"Version","checking":"Checking…","upToDate":"up to date","available":"{v} is available","manualCopy":"Development or renamed copy — update it manually.","installing":"Installing {v}…","restartMsg":"Version {v} was installed. Restart the dsh to use it.","noReleases":"No releases published yet","checkFailed":"Update check failed ({e})","updateNow":"Update now","check":"Check for updates","openRelease":"Open release page","releaseNotes":"Release notes","repo":"GitHub repository","issues":"Report an issue"},
|
|
370
|
+
'pt-BR': {"header":"Sugestões de acompanhamento","showEarlier":"Mostrar sugestões anteriores","hideEarlier":"Ocultar sugestões anteriores","srFollowupSent":"Follow-up enviado","sendFailed":"Não consegui enviar: ","intro":"Sugestões de próximos passos sob cada turno concluído do agente — um clique envia o followup.","setLanguage":"Idioma","setLanguageTip":"Os followups são gerados neste idioma. Automático segue a conversa.","optAuto":"Automático (segue a conversa)","optEn":"English","optPt":"Português (Brasil)","optZh":"简体中文","setModel":"Modelo","setModelTip":"O modelo que gera as sugestões. Automático as mantém no turno principal.","optModelAuto":"Automático (mesmo modelo da conversa)","modelUnavailable":"Nenhuma lista de modelos disponível nesta instalação","setMax":"Followups por turno","setMaxTip":"Quantas sugestões o modelo deve produzir (1-6).","setHeader":"Mostrar cabeçalho","setHeaderTip":"O rótulo pequeno “Sugestões de acompanhamento” acima dos cartões.","updates":"Atualizações","current":"Versão","checking":"Verificando…","upToDate":"em dia","available":"{v} disponível","manualCopy":"Cópia de desenvolvimento ou renomeada — atualize manualmente.","installing":"Instalando {v}…","restartMsg":"Versão {v} instalada. Reinicie o dsh para usá-la.","noReleases":"Nenhum release publicado ainda","checkFailed":"Falha ao verificar atualizações ({e})","updateNow":"Atualizar agora","check":"Verificar atualizações","openRelease":"Abrir página do release","releaseNotes":"Notas do release","repo":"Repositório no GitHub","issues":"Reportar um problema"},
|
|
371
|
+
'zh-CN': {"header":"建议的后续操作","showEarlier":"显示较早的建议","hideEarlier":"隐藏较早的建议","srFollowupSent":"后续操作已发送","sendFailed":"发送失败:","intro":"在每个智能体回合完成后的下方推荐下一步提示——单击即可发送。","setLanguage":"语言","setLanguageTip":"后续操作提示将以此语言生成。“自动”会跟随对话语言。","optAuto":"自动(跟随对话)","optEn":"English","optPt":"Português (Brasil)","optZh":"简体中文","setModel":"模型","setModelTip":"生成建议的模型。自动会跟随对话所用的模型。","optModelAuto":"自动(与对话相同的模型)","modelUnavailable":"此安装没有可用的模型列表","setMax":"每回合提示数","setMaxTip":"模型应生成的建议提示数量(1-6)。","setHeader":"显示标题","setHeaderTip":"卡片上方的小标题“建议的后续操作”。","updates":"更新","current":"版本","checking":"检查中…","upToDate":"已是最新","available":"{v} 可用","manualCopy":"开发或重命名的副本——请手动更新。","installing":"正在安装 {v}…","restartMsg":"版本 {v} 已安装。请重启 dsh 以使用。","noReleases":"尚未发布任何版本","checkFailed":"检查更新失败({e})","updateNow":"立即更新","check":"检查更新","openRelease":"打开发布页","releaseNotes":"发布说明","repo":"GitHub 仓库","issues":"反馈问题"}
|
|
369
372
|
};
|
|
370
373
|
let pluginSettings = { lang: 'auto', maxFollowups: 3, showHeader: true };
|
|
371
374
|
let pluginVersion;
|
|
@@ -611,7 +614,7 @@ window.__ModuleLoader__.load({
|
|
|
611
614
|
row(t('setLanguage'), t('setLanguageTip'), h('select', { value: settings.lang, onChange: (event) => save({ lang: event.target.value }), style: controlStyle, 'aria-label': t('setLanguage') },
|
|
612
615
|
h('option', { value: 'auto' }, t('optAuto')),
|
|
613
616
|
...LANGUAGE_OPTIONS.map((option) => h('option', { key: option.value, value: option.value }, option.label))), 'lang'),
|
|
614
|
-
row(t('setModel'), t('setModelTip'),
|
|
617
|
+
row(t('setModel'), t('setModelTip'), models === undefined ? h('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } }, t('checking')) : (models.groups ?? []).length === 0 ? h('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } }, t('modelUnavailable')) : h('select', { value: settings.model, onChange: (event) => save({ model: event.target.value }), style: controlStyle, 'aria-label': t('setModel') },
|
|
615
618
|
h('option', { value: '' }, t('optModelAuto')),
|
|
616
619
|
...(models.groups ?? []).map((group) => h('optgroup', { key: group.id, label: group.name },
|
|
617
620
|
group.models.map((entry) => h('option', { key: group.id + '/' + entry.id, value: group.id + '/' + entry.id }, entry.name === entry.id ? entry.name : entry.name + ' (' + entry.id + ')'))))), 'model'),
|
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
|
|
7
7
|
const TOOL_NAME = 'suggest_followups';
|
|
8
8
|
const name = 'dsh-nextsteps';
|
|
9
|
-
const inject = ['tools', 'connection'
|
|
9
|
+
const inject = ['tools', 'connection'];
|
|
10
10
|
const NPM_NAME = 'dsh-nextsteps';
|
|
11
11
|
const RELEASES_URL = 'https://api.github.com/repos/klaus-2/dsh-nextsteps/releases/latest';
|
|
12
12
|
const SETTINGS_FILE = path.join(homedir(), '.dsh', 'dsh-nextsteps', 'settings.json');
|
|
@@ -124,13 +124,19 @@ const PARAMETER_SCHEMA = {
|
|
|
124
124
|
required: ['followups']
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
-
function toolDescription() {
|
|
127
|
+
function toolDescription(external = false) {
|
|
128
|
+
if (external) {
|
|
129
|
+
return [
|
|
130
|
+
'This tool is not used any more: the host generates the followup suggestions after your turn with a separate model.',
|
|
131
|
+
'Do not call it. Just finish your answer as usual.'
|
|
132
|
+
].join('\n');
|
|
133
|
+
}
|
|
128
134
|
return [
|
|
129
135
|
'MANDATORY in every turn: once all real work and other tool calls are done, call this tool BEFORE writing your final answer — the answer must come after this tool returns, so the turn ends cleanly right after it. Never write your final answer before calling it.',
|
|
130
|
-
'Suggest exactly the number of followup prompts stated in the nextsteps section of the system prompt.',
|
|
136
|
+
'Suggest exactly the number of followup prompts stated in the nextsteps section of the system prompt; when there is no such section, suggest three.',
|
|
131
137
|
'Good suggestions include alternatives to the latest implementation, related features, cleanup opportunities, tests to add, or verification steps you could run yourself; if plan steps remain, one suggestion may be continuing with the next step.',
|
|
132
138
|
`Each prompt must be short (usually one sentence), goal-oriented — name the outcome, not the steps — stand alone without conversation context, and be phrased in the user's first-person voice.`,
|
|
133
|
-
'Write every prompt and label in the language the nextsteps section of the system prompt states.',
|
|
139
|
+
'Write every prompt and label in the language the nextsteps section of the system prompt states; when there is no such section, use the language of the conversation.',
|
|
134
140
|
'Never suggest actions the user would carry out themselves outside this session (e.g. "commit these changes"); judge by the tools you actually have.',
|
|
135
141
|
'This never waits for an answer: its result only confirms the suggestions were recorded.',
|
|
136
142
|
'Example input:',
|
|
@@ -140,6 +146,11 @@ function toolDescription() {
|
|
|
140
146
|
|
|
141
147
|
function nextstepsSectionText() {
|
|
142
148
|
const settings = settingsLoad();
|
|
149
|
+
if (typeof settings.model === 'string' && settings.model !== '') {
|
|
150
|
+
return [
|
|
151
|
+
'nextsteps (the followup suggestion cards): the host generates them after your turn with a separate model, so you must not call any followup tool. Just finish your answer as usual.'
|
|
152
|
+
].join('\n');
|
|
153
|
+
}
|
|
143
154
|
const language = LANGUAGE_NAMES[settings.lang] ?? LANGUAGE_NAMES.auto;
|
|
144
155
|
const languageRule = settings.lang === 'auto'
|
|
145
156
|
? `Write every prompt and label in the same language the conversation has been using.`
|
|
@@ -153,10 +164,11 @@ function nextstepsSectionText() {
|
|
|
153
164
|
|
|
154
165
|
const PLUGIN_REV = 'sf-2';
|
|
155
166
|
|
|
156
|
-
function toolSpec() {
|
|
167
|
+
function toolSpec(settings = { lang: 'auto', maxFollowups: 3, model: '' }) {
|
|
168
|
+
const external = typeof settings.model === 'string' && settings.model !== '';
|
|
157
169
|
return {
|
|
158
170
|
name: TOOL_NAME,
|
|
159
|
-
description: toolDescription(),
|
|
171
|
+
description: toolDescription(external),
|
|
160
172
|
parameters: PARAMETER_SCHEMA,
|
|
161
173
|
output: {
|
|
162
174
|
schema: { type: 'string' },
|
|
@@ -166,6 +178,7 @@ function toolSpec() {
|
|
|
166
178
|
]
|
|
167
179
|
},
|
|
168
180
|
async execute(args) {
|
|
181
|
+
if (external) return 'The host generates the followup suggestions after this turn. Do not call this tool again.';
|
|
169
182
|
const followups = coerceFollowups(args.followups);
|
|
170
183
|
const problem = validationProblem(followups);
|
|
171
184
|
if (problem !== null) return problem;
|
|
@@ -202,15 +215,33 @@ async function modelCatalog(ctx) {
|
|
|
202
215
|
return out;
|
|
203
216
|
}
|
|
204
217
|
|
|
218
|
+
function textOf(content) {
|
|
219
|
+
if (typeof content === 'string') return content;
|
|
220
|
+
if (!Array.isArray(content)) return '';
|
|
221
|
+
const parts = [];
|
|
222
|
+
for (const part of content) {
|
|
223
|
+
if (part === null || typeof part !== 'object') continue;
|
|
224
|
+
if (part.type === 'text' && typeof part.text === 'string') parts.push(part.text);
|
|
225
|
+
else if (part.type === 'reasoning' && typeof part.text === 'string') parts.push(part.text);
|
|
226
|
+
}
|
|
227
|
+
return parts.join('\n');
|
|
228
|
+
}
|
|
229
|
+
|
|
205
230
|
function turnTranscript(events, afterSeq) {
|
|
206
231
|
const lines = [];
|
|
207
232
|
for (const entry of events) {
|
|
208
233
|
const event = entry?.event ?? entry;
|
|
209
234
|
if (event === null || typeof event !== 'object') continue;
|
|
210
235
|
if (Number.isSafeInteger(afterSeq) && Number.isSafeInteger(event.seq) && event.seq > afterSeq) break;
|
|
211
|
-
if (event.type === 'user/message'
|
|
212
|
-
|
|
213
|
-
|
|
236
|
+
if (event.type === 'user/message') {
|
|
237
|
+
const text = textOf(event.data?.content);
|
|
238
|
+
if (text !== '' && event.data?.source?.kind === 'user') lines.push(`User: ${text.slice(0, 2000)}`);
|
|
239
|
+
} else if (event.type === 'assistant/message') {
|
|
240
|
+
const text = textOf(event.data?.message?.content);
|
|
241
|
+
if (text !== '') lines.push(`Assistant: ${text.slice(0, 2000)}`);
|
|
242
|
+
} else if (event.type === 'tool/call' && typeof event.data?.name === 'string') {
|
|
243
|
+
lines.push(`Tool call: ${event.data.name}`);
|
|
244
|
+
}
|
|
214
245
|
}
|
|
215
246
|
return lines.join('\n\n');
|
|
216
247
|
}
|
|
@@ -321,30 +352,25 @@ async function apply(ctx) {
|
|
|
321
352
|
settings = next;
|
|
322
353
|
try {
|
|
323
354
|
try { ctx.tools.unregister(TOOL_NAME); } catch { /* not registered yet */ }
|
|
324
|
-
|
|
355
|
+
ctx.tools.register(toolSpec(next));
|
|
325
356
|
} catch (error) {
|
|
326
357
|
console.error('dsh-nextsteps: tool re-registration failed:', error);
|
|
327
358
|
}
|
|
328
359
|
};
|
|
329
360
|
try {
|
|
330
|
-
|
|
361
|
+
ctx.tools.register(toolSpec(settings));
|
|
331
362
|
} catch (error) {
|
|
332
363
|
console.error('dsh-nextsteps: tool registration failed:', error);
|
|
333
364
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
sub.systemPrompt.section({ name: 'dsh-nextsteps', order: 640, interpolate: false, text: () => nextstepsSectionText() });
|
|
344
|
-
} catch (error) {
|
|
345
|
-
console.error('dsh-nextsteps: system prompt section rejected:', error);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
365
|
+
const systemPrompt = get(ctx, 'systemPrompt');
|
|
366
|
+
if (systemPrompt !== undefined && typeof systemPrompt.section === 'function') {
|
|
367
|
+
try {
|
|
368
|
+
systemPrompt.section({ name: 'dsh-nextsteps', order: 640, interpolate: false, text: () => nextstepsSectionText() });
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('dsh-nextsteps: system prompt section rejected:', error);
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
console.warn('dsh-nextsteps: the system prompt service is unavailable; the suggestions follow the conversation language');
|
|
348
374
|
}
|
|
349
375
|
ctx.connection?.fetch?.register({
|
|
350
376
|
path: '/api/dsh-nextsteps.settings',
|