dsh-nextsteps 1.3.3 → 1.3.4

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.
Files changed (3) hide show
  1. package/client.js +6 -3
  2. package/index.js +40 -9
  3. 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.suggest-followups.clicked';
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.values()) {
231
- if (entry.turn === turn) best = entry;
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
  }
package/index.js CHANGED
@@ -124,7 +124,13 @@ 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
136
  'Suggest exactly the number of followup prompts stated in the nextsteps section of the system prompt.',
@@ -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' && typeof event.data?.text === 'string') lines.push(`User: ${event.data.text.slice(0, 2000)}`);
212
- else if (event.type === 'agent/message' && typeof event.data?.text === 'string') lines.push(`Assistant: ${event.data.text.slice(0, 2000)}`);
213
- else if (event.type === 'tool/call' && typeof event.data?.name === 'string') lines.push(`Tool call: ${event.data.name}`);
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,13 +352,13 @@ async function apply(ctx) {
321
352
  settings = next;
322
353
  try {
323
354
  try { ctx.tools.unregister(TOOL_NAME); } catch { /* not registered yet */ }
324
- if (!isExternal(next)) ctx.tools.register(toolSpec());
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
- if (!isExternal(settings)) ctx.tools.register(toolSpec());
361
+ ctx.tools.register(toolSpec(settings));
331
362
  } catch (error) {
332
363
  console.error('dsh-nextsteps: tool registration failed:', error);
333
364
  }
@@ -335,7 +366,7 @@ async function apply(ctx) {
335
366
  ctx.inject(['tools', 'systemPrompt'], (sub) => {
336
367
  try {
337
368
  try { sub.tools.unregister(TOOL_NAME); } catch { /* not registered yet */ }
338
- if (!isExternal(settings)) sub.tools.register(toolSpec());
369
+ sub.tools.register(toolSpec(settings));
339
370
  } catch (error) {
340
371
  console.error('dsh-nextsteps: tool registration via inject failed:', error);
341
372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-nextsteps",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",