llmist 1.6.1 → 1.7.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/dist/index.cjs CHANGED
@@ -1101,7 +1101,7 @@ function applyLineLimit(lines, limit) {
1101
1101
  }
1102
1102
  return lines;
1103
1103
  }
1104
- function createGadgetOutputViewer(store) {
1104
+ function createGadgetOutputViewer(store, maxOutputChars = DEFAULT_MAX_OUTPUT_CHARS) {
1105
1105
  return createGadget({
1106
1106
  name: "GadgetOutputViewer",
1107
1107
  description: "View stored output from gadgets that returned too much data. Use patterns to filter lines (like grep) and limit to control output size. Patterns are applied first in order, then the limit is applied to the result.",
@@ -1166,19 +1166,43 @@ function createGadgetOutputViewer(store) {
1166
1166
  if (limit) {
1167
1167
  lines = applyLineLimit(lines, limit);
1168
1168
  }
1169
+ let output = lines.join("\n");
1169
1170
  const totalLines = stored.lineCount;
1170
1171
  const returnedLines = lines.length;
1171
1172
  if (returnedLines === 0) {
1172
1173
  return `No lines matched the filters. Original output had ${totalLines} lines.`;
1173
1174
  }
1174
- const header = returnedLines < totalLines ? `[Showing ${returnedLines} of ${totalLines} lines]
1175
- ` : `[Showing all ${totalLines} lines]
1175
+ let truncatedBySize = false;
1176
+ let linesIncluded = returnedLines;
1177
+ if (output.length > maxOutputChars) {
1178
+ truncatedBySize = true;
1179
+ let truncatedOutput = "";
1180
+ linesIncluded = 0;
1181
+ for (const line of lines) {
1182
+ if (truncatedOutput.length + line.length + 1 > maxOutputChars) break;
1183
+ truncatedOutput += line + "\n";
1184
+ linesIncluded++;
1185
+ }
1186
+ output = truncatedOutput;
1187
+ }
1188
+ let header;
1189
+ if (truncatedBySize) {
1190
+ const remainingLines = returnedLines - linesIncluded;
1191
+ header = `[Showing ${linesIncluded} of ${totalLines} lines (truncated due to size limit)]
1192
+ [... ${remainingLines.toLocaleString()} more lines. Use limit parameter to paginate, e.g., limit: "${linesIncluded + 1}-${linesIncluded + 200}"]
1176
1193
  `;
1177
- return header + lines.join("\n");
1194
+ } else if (returnedLines < totalLines) {
1195
+ header = `[Showing ${returnedLines} of ${totalLines} lines]
1196
+ `;
1197
+ } else {
1198
+ header = `[Showing all ${totalLines} lines]
1199
+ `;
1200
+ }
1201
+ return header + output;
1178
1202
  }
1179
1203
  });
1180
1204
  }
1181
- var import_zod, patternSchema;
1205
+ var import_zod, patternSchema, DEFAULT_MAX_OUTPUT_CHARS;
1182
1206
  var init_output_viewer = __esm({
1183
1207
  "src/gadgets/output-viewer.ts"() {
1184
1208
  "use strict";
@@ -1190,6 +1214,7 @@ var init_output_viewer = __esm({
1190
1214
  before: import_zod.z.number().int().min(0).default(0).describe("Context lines before each match (like grep -B)"),
1191
1215
  after: import_zod.z.number().int().min(0).default(0).describe("Context lines after each match (like grep -A)")
1192
1216
  });
1217
+ DEFAULT_MAX_OUTPUT_CHARS = 76800;
1193
1218
  }
1194
1219
  });
1195
1220
 
@@ -3395,7 +3420,10 @@ var init_agent = __esm({
3395
3420
  const contextWindow = limits?.contextWindow ?? FALLBACK_CONTEXT_WINDOW;
3396
3421
  this.outputLimitCharLimit = Math.floor(contextWindow * (limitPercent / 100) * CHARS_PER_TOKEN);
3397
3422
  if (this.outputLimitEnabled) {
3398
- this.registry.register("GadgetOutputViewer", createGadgetOutputViewer(this.outputStore));
3423
+ this.registry.register(
3424
+ "GadgetOutputViewer",
3425
+ createGadgetOutputViewer(this.outputStore, this.outputLimitCharLimit)
3426
+ );
3399
3427
  }
3400
3428
  this.hooks = this.mergeOutputLimiterHook(options.hooks);
3401
3429
  const baseBuilder = new LLMMessageBuilder(options.promptConfig);
@@ -5881,6 +5909,7 @@ var init_builder = __esm({
5881
5909
  gadgetOutputLimitPercent;
5882
5910
  compactionConfig;
5883
5911
  signal;
5912
+ trailingMessage;
5884
5913
  constructor(client) {
5885
5914
  this.client = client;
5886
5915
  }
@@ -6356,6 +6385,31 @@ var init_builder = __esm({
6356
6385
  this.signal = signal;
6357
6386
  return this;
6358
6387
  }
6388
+ /**
6389
+ * Add an ephemeral trailing message that appears at the end of each LLM request.
6390
+ *
6391
+ * The message is NOT persisted to conversation history - it only appears in the
6392
+ * current LLM call. This is useful for injecting context-specific instructions
6393
+ * or reminders without polluting the conversation history.
6394
+ *
6395
+ * @param message - Static string or function that generates the message
6396
+ * @returns This builder for chaining
6397
+ *
6398
+ * @example
6399
+ * ```typescript
6400
+ * // Static message
6401
+ * .withTrailingMessage("Always respond in JSON format.")
6402
+ *
6403
+ * // Dynamic message based on iteration
6404
+ * .withTrailingMessage((ctx) =>
6405
+ * `[Iteration ${ctx.iteration}/${ctx.maxIterations}] Stay focused on the task.`
6406
+ * )
6407
+ * ```
6408
+ */
6409
+ withTrailingMessage(message) {
6410
+ this.trailingMessage = message;
6411
+ return this;
6412
+ }
6359
6413
  /**
6360
6414
  * Add a synthetic gadget call to the conversation history.
6361
6415
  *
@@ -6397,6 +6451,36 @@ ${endPrefix}`
6397
6451
  });
6398
6452
  return this;
6399
6453
  }
6454
+ /**
6455
+ * Compose the final hooks, including trailing message if configured.
6456
+ */
6457
+ composeHooks() {
6458
+ if (!this.trailingMessage) {
6459
+ return this.hooks;
6460
+ }
6461
+ const trailingMsg = this.trailingMessage;
6462
+ const existingBeforeLLMCall = this.hooks?.controllers?.beforeLLMCall;
6463
+ const trailingMessageController = async (ctx) => {
6464
+ const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
6465
+ if (result.action === "skip") {
6466
+ return result;
6467
+ }
6468
+ const messages = [...result.modifiedOptions?.messages || ctx.options.messages];
6469
+ const content = typeof trailingMsg === "function" ? trailingMsg({ iteration: ctx.iteration, maxIterations: ctx.maxIterations }) : trailingMsg;
6470
+ messages.push({ role: "user", content });
6471
+ return {
6472
+ action: "proceed",
6473
+ modifiedOptions: { ...result.modifiedOptions, messages }
6474
+ };
6475
+ };
6476
+ return {
6477
+ ...this.hooks,
6478
+ controllers: {
6479
+ ...this.hooks?.controllers,
6480
+ beforeLLMCall: trailingMessageController
6481
+ }
6482
+ };
6483
+ }
6400
6484
  /**
6401
6485
  * Format parameters as block format with JSON Pointer paths.
6402
6486
  */
@@ -6458,7 +6542,7 @@ ${endPrefix}`
6458
6542
  maxIterations: this.maxIterations,
6459
6543
  temperature: this.temperature,
6460
6544
  logger: this.logger,
6461
- hooks: this.hooks,
6545
+ hooks: this.composeHooks(),
6462
6546
  promptConfig: this.promptConfig,
6463
6547
  initialMessages: this.initialMessages,
6464
6548
  onHumanInputRequired: this.onHumanInputRequired,
@@ -6562,7 +6646,7 @@ ${endPrefix}`
6562
6646
  maxIterations: this.maxIterations,
6563
6647
  temperature: this.temperature,
6564
6648
  logger: this.logger,
6565
- hooks: this.hooks,
6649
+ hooks: this.composeHooks(),
6566
6650
  promptConfig: this.promptConfig,
6567
6651
  initialMessages: this.initialMessages,
6568
6652
  onHumanInputRequired: this.onHumanInputRequired,