llmist 0.6.2 → 0.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/cli.cjs CHANGED
@@ -31,6 +31,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  ));
32
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
33
 
34
+ // src/core/constants.ts
35
+ var GADGET_START_PREFIX, GADGET_END_PREFIX, DEFAULT_GADGET_OUTPUT_LIMIT, DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT, CHARS_PER_TOKEN, FALLBACK_CONTEXT_WINDOW;
36
+ var init_constants = __esm({
37
+ "src/core/constants.ts"() {
38
+ "use strict";
39
+ GADGET_START_PREFIX = "!!!GADGET_START:";
40
+ GADGET_END_PREFIX = "!!!GADGET_END";
41
+ DEFAULT_GADGET_OUTPUT_LIMIT = true;
42
+ DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT = 15;
43
+ CHARS_PER_TOKEN = 4;
44
+ FALLBACK_CONTEXT_WINDOW = 128e3;
45
+ }
46
+ });
47
+
34
48
  // src/core/model-shortcuts.ts
35
49
  function isKnownModelPattern(model) {
36
50
  const normalized = model.toLowerCase();
@@ -328,20 +342,6 @@ var init_registry = __esm({
328
342
  }
329
343
  });
330
344
 
331
- // src/core/constants.ts
332
- var GADGET_START_PREFIX, GADGET_END_PREFIX, DEFAULT_GADGET_OUTPUT_LIMIT, DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT, CHARS_PER_TOKEN, FALLBACK_CONTEXT_WINDOW;
333
- var init_constants = __esm({
334
- "src/core/constants.ts"() {
335
- "use strict";
336
- GADGET_START_PREFIX = "!!!GADGET_START:";
337
- GADGET_END_PREFIX = "!!!GADGET_END";
338
- DEFAULT_GADGET_OUTPUT_LIMIT = true;
339
- DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT = 15;
340
- CHARS_PER_TOKEN = 4;
341
- FALLBACK_CONTEXT_WINDOW = 128e3;
342
- }
343
- });
344
-
345
345
  // src/core/prompt-config.ts
346
346
  function resolvePromptTemplate(template, defaultValue, context) {
347
347
  const resolved = template ?? defaultValue;
@@ -923,6 +923,10 @@ function formatParamsAsYaml(params) {
923
923
  }
924
924
  return lines.join("\n");
925
925
  }
926
+ function formatTomlInlineTable(obj) {
927
+ const entries = Object.entries(obj).map(([k, v]) => `${k} = ${formatTomlValue(v)}`);
928
+ return `{ ${entries.join(", ")} }`;
929
+ }
926
930
  function formatTomlValue(value) {
927
931
  if (typeof value === "string") {
928
932
  if (value.includes("\n")) {
@@ -940,10 +944,17 @@ ${delimiter}`;
940
944
  return '""';
941
945
  }
942
946
  if (Array.isArray(value)) {
943
- return JSON.stringify(value);
947
+ if (value.length === 0) return "[]";
948
+ const items = value.map((item) => {
949
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
950
+ return formatTomlInlineTable(item);
951
+ }
952
+ return formatTomlValue(item);
953
+ });
954
+ return `[${items.join(", ")}]`;
944
955
  }
945
956
  if (typeof value === "object") {
946
- return JSON.stringify(value);
957
+ return formatTomlInlineTable(value);
947
958
  }
948
959
  return JSON.stringify(value);
949
960
  }
@@ -1959,6 +1970,14 @@ function preprocessTomlHeredoc(tomlStr) {
1959
1970
  }
1960
1971
  return result.join("\n");
1961
1972
  }
1973
+ function stripMarkdownFences(content) {
1974
+ let cleaned = content.trim();
1975
+ const openingFence = /^```(?:toml|yaml|json)?\s*\n/i;
1976
+ const closingFence = /\n?```\s*$/;
1977
+ cleaned = cleaned.replace(openingFence, "");
1978
+ cleaned = cleaned.replace(closingFence, "");
1979
+ return cleaned.trim();
1980
+ }
1962
1981
  var yaml2, import_js_toml, globalInvocationCounter, StreamParser;
1963
1982
  var init_parser = __esm({
1964
1983
  "src/gadgets/parser.ts"() {
@@ -2014,35 +2033,36 @@ var init_parser = __esm({
2014
2033
  * Parse parameter string according to configured format
2015
2034
  */
2016
2035
  parseParameters(raw) {
2036
+ const cleaned = stripMarkdownFences(raw);
2017
2037
  if (this.parameterFormat === "json") {
2018
2038
  try {
2019
- return { parameters: JSON.parse(raw) };
2039
+ return { parameters: JSON.parse(cleaned) };
2020
2040
  } catch (error) {
2021
2041
  return { parseError: this.truncateParseError(error, "JSON") };
2022
2042
  }
2023
2043
  }
2024
2044
  if (this.parameterFormat === "yaml") {
2025
2045
  try {
2026
- return { parameters: yaml2.load(preprocessYaml(raw)) };
2046
+ return { parameters: yaml2.load(preprocessYaml(cleaned)) };
2027
2047
  } catch (error) {
2028
2048
  return { parseError: this.truncateParseError(error, "YAML") };
2029
2049
  }
2030
2050
  }
2031
2051
  if (this.parameterFormat === "toml") {
2032
2052
  try {
2033
- return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(raw)) };
2053
+ return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(cleaned)) };
2034
2054
  } catch (error) {
2035
2055
  return { parseError: this.truncateParseError(error, "TOML") };
2036
2056
  }
2037
2057
  }
2038
2058
  try {
2039
- return { parameters: JSON.parse(raw) };
2059
+ return { parameters: JSON.parse(cleaned) };
2040
2060
  } catch {
2041
2061
  try {
2042
- return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(raw)) };
2062
+ return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(cleaned)) };
2043
2063
  } catch {
2044
2064
  try {
2045
- return { parameters: yaml2.load(preprocessYaml(raw)) };
2065
+ return { parameters: yaml2.load(preprocessYaml(cleaned)) };
2046
2066
  } catch (error) {
2047
2067
  return { parseError: this.truncateParseError(error, "auto") };
2048
2068
  }
@@ -2588,6 +2608,7 @@ var init_agent = __esm({
2588
2608
  gadgetEndPrefix;
2589
2609
  onHumanInputRequired;
2590
2610
  textOnlyHandler;
2611
+ textWithGadgetsHandler;
2591
2612
  stopOnGadgetError;
2592
2613
  shouldContinueAfterError;
2593
2614
  defaultGadgetTimeoutMs;
@@ -2618,6 +2639,7 @@ var init_agent = __esm({
2618
2639
  this.gadgetEndPrefix = options.gadgetEndPrefix;
2619
2640
  this.onHumanInputRequired = options.onHumanInputRequired;
2620
2641
  this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
2642
+ this.textWithGadgetsHandler = options.textWithGadgetsHandler;
2621
2643
  this.stopOnGadgetError = options.stopOnGadgetError ?? true;
2622
2644
  this.shouldContinueAfterError = options.shouldContinueAfterError;
2623
2645
  this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
@@ -2805,6 +2827,17 @@ var init_agent = __esm({
2805
2827
  }
2806
2828
  }
2807
2829
  if (result.didExecuteGadgets) {
2830
+ if (this.textWithGadgetsHandler) {
2831
+ const textContent = result.outputs.filter((output) => output.type === "text").map((output) => output.content).join("");
2832
+ if (textContent.trim()) {
2833
+ const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
2834
+ this.conversation.addGadgetCall(
2835
+ gadgetName,
2836
+ parameterMapping(textContent),
2837
+ resultMapping ? resultMapping(textContent) : textContent
2838
+ );
2839
+ }
2840
+ }
2808
2841
  for (const output of result.outputs) {
2809
2842
  if (output.type === "gadget_result") {
2810
2843
  const gadgetResult = output.result;
@@ -2816,7 +2849,13 @@ var init_agent = __esm({
2816
2849
  }
2817
2850
  }
2818
2851
  } else {
2819
- this.conversation.addAssistantMessage(finalMessage);
2852
+ if (finalMessage.trim()) {
2853
+ this.conversation.addGadgetCall(
2854
+ "TellUser",
2855
+ { message: finalMessage, done: false, type: "info" },
2856
+ `\u2139\uFE0F ${finalMessage}`
2857
+ );
2858
+ }
2820
2859
  const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
2821
2860
  if (shouldBreak) {
2822
2861
  break;
@@ -4690,6 +4729,7 @@ var AgentBuilder;
4690
4729
  var init_builder = __esm({
4691
4730
  "src/agent/builder.ts"() {
4692
4731
  "use strict";
4732
+ init_constants();
4693
4733
  init_model_shortcuts();
4694
4734
  init_registry();
4695
4735
  init_agent();
@@ -4711,6 +4751,7 @@ var init_builder = __esm({
4711
4751
  gadgetStartPrefix;
4712
4752
  gadgetEndPrefix;
4713
4753
  textOnlyHandler;
4754
+ textWithGadgetsHandler;
4714
4755
  stopOnGadgetError;
4715
4756
  shouldContinueAfterError;
4716
4757
  defaultGadgetTimeoutMs;
@@ -4973,6 +5014,30 @@ var init_builder = __esm({
4973
5014
  this.textOnlyHandler = handler;
4974
5015
  return this;
4975
5016
  }
5017
+ /**
5018
+ * Set the handler for text content that appears alongside gadget calls.
5019
+ *
5020
+ * When set, text accompanying gadget responses will be wrapped as a
5021
+ * synthetic gadget call before the actual gadget results in the
5022
+ * conversation history.
5023
+ *
5024
+ * @param handler - Configuration for wrapping text
5025
+ * @returns This builder for chaining
5026
+ *
5027
+ * @example
5028
+ * ```typescript
5029
+ * // Wrap text as TellUser gadget
5030
+ * .withTextWithGadgetsHandler({
5031
+ * gadgetName: "TellUser",
5032
+ * parameterMapping: (text) => ({ message: text, done: false, type: "info" }),
5033
+ * resultMapping: (text) => `ℹ️ ${text}`,
5034
+ * })
5035
+ * ```
5036
+ */
5037
+ withTextWithGadgetsHandler(handler) {
5038
+ this.textWithGadgetsHandler = handler;
5039
+ return this;
5040
+ }
4976
5041
  /**
4977
5042
  * Set whether to stop gadget execution on first error.
4978
5043
  *
@@ -5087,6 +5152,69 @@ var init_builder = __esm({
5087
5152
  this.gadgetOutputLimitPercent = percent;
5088
5153
  return this;
5089
5154
  }
5155
+ /**
5156
+ * Add a synthetic gadget call to the conversation history.
5157
+ *
5158
+ * This is useful for in-context learning - showing the LLM what "past self"
5159
+ * did correctly so it mimics the pattern. The call is formatted with proper
5160
+ * markers and parameter format.
5161
+ *
5162
+ * @param gadgetName - Name of the gadget
5163
+ * @param parameters - Parameters passed to the gadget
5164
+ * @param result - Result returned by the gadget
5165
+ * @returns This builder for chaining
5166
+ *
5167
+ * @example
5168
+ * ```typescript
5169
+ * .withSyntheticGadgetCall(
5170
+ * 'TellUser',
5171
+ * {
5172
+ * message: '👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
5173
+ * done: false,
5174
+ * type: 'info'
5175
+ * },
5176
+ * 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands'
5177
+ * )
5178
+ * ```
5179
+ */
5180
+ withSyntheticGadgetCall(gadgetName, parameters, result) {
5181
+ const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
5182
+ const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
5183
+ const format = this.parameterFormat ?? "yaml";
5184
+ const paramStr = this.formatSyntheticParameters(parameters, format);
5185
+ this.initialMessages.push({
5186
+ role: "assistant",
5187
+ content: `${startPrefix}${gadgetName}
5188
+ ${paramStr}
5189
+ ${endPrefix}`
5190
+ });
5191
+ this.initialMessages.push({
5192
+ role: "user",
5193
+ content: `Result: ${result}`
5194
+ });
5195
+ return this;
5196
+ }
5197
+ /**
5198
+ * Format parameters for synthetic gadget calls.
5199
+ * Uses heredoc for multiline string values.
5200
+ */
5201
+ formatSyntheticParameters(parameters, format) {
5202
+ if (format === "json" || format === "auto") {
5203
+ return JSON.stringify(parameters);
5204
+ }
5205
+ return Object.entries(parameters).map(([key, value]) => {
5206
+ if (typeof value === "string" && value.includes("\n")) {
5207
+ const separator = format === "yaml" ? ":" : " =";
5208
+ return `${key}${separator} <<<EOF
5209
+ ${value}
5210
+ EOF`;
5211
+ }
5212
+ if (format === "yaml") {
5213
+ return typeof value === "string" ? `${key}: ${value}` : `${key}: ${JSON.stringify(value)}`;
5214
+ }
5215
+ return `${key} = ${JSON.stringify(value)}`;
5216
+ }).join("\n");
5217
+ }
5090
5218
  /**
5091
5219
  * Build and create the agent with the given user prompt.
5092
5220
  * Returns the Agent instance ready to run.
@@ -5129,6 +5257,7 @@ var init_builder = __esm({
5129
5257
  gadgetStartPrefix: this.gadgetStartPrefix,
5130
5258
  gadgetEndPrefix: this.gadgetEndPrefix,
5131
5259
  textOnlyHandler: this.textOnlyHandler,
5260
+ textWithGadgetsHandler: this.textWithGadgetsHandler,
5132
5261
  stopOnGadgetError: this.stopOnGadgetError,
5133
5262
  shouldContinueAfterError: this.shouldContinueAfterError,
5134
5263
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
@@ -5230,6 +5359,7 @@ var init_builder = __esm({
5230
5359
  gadgetStartPrefix: this.gadgetStartPrefix,
5231
5360
  gadgetEndPrefix: this.gadgetEndPrefix,
5232
5361
  textOnlyHandler: this.textOnlyHandler,
5362
+ textWithGadgetsHandler: this.textWithGadgetsHandler,
5233
5363
  stopOnGadgetError: this.stopOnGadgetError,
5234
5364
  shouldContinueAfterError: this.shouldContinueAfterError,
5235
5365
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
@@ -5289,7 +5419,7 @@ var import_commander3 = require("commander");
5289
5419
  // package.json
5290
5420
  var package_default = {
5291
5421
  name: "llmist",
5292
- version: "0.6.1",
5422
+ version: "0.6.2",
5293
5423
  description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
5294
5424
  type: "module",
5295
5425
  main: "dist/index.cjs",
@@ -5437,7 +5567,7 @@ var tellUser = createGadget({
5437
5567
  name: "TellUser",
5438
5568
  description: "Tell the user something important. Set done=true when your work is complete and you want to end the conversation.",
5439
5569
  schema: import_zod2.z.object({
5440
- message: import_zod2.z.string().describe("The message to display to the user in Markdown"),
5570
+ message: import_zod2.z.string().optional().describe("The message to display to the user in Markdown"),
5441
5571
  done: import_zod2.z.boolean().default(false).describe("Set to true to end the conversation, false to continue"),
5442
5572
  type: import_zod2.z.enum(["info", "success", "warning", "error"]).default("info").describe("Message type: info, success, warning, or error")
5443
5573
  }),
@@ -5457,9 +5587,20 @@ var tellUser = createGadget({
5457
5587
  done: false,
5458
5588
  type: "warning"
5459
5589
  }
5590
+ },
5591
+ {
5592
+ comment: "Share detailed analysis with bullet points (use heredoc for multiline)",
5593
+ params: {
5594
+ message: "Here's what I found in the codebase:\n\n1. **Main entry point**: `src/index.ts` exports all public APIs\n2. **Core logic**: Located in `src/core/` with 5 modules\n3. **Tests**: Good coverage in `src/__tests__/`\n\nI'll continue exploring the core modules.",
5595
+ done: false,
5596
+ type: "info"
5597
+ }
5460
5598
  }
5461
5599
  ],
5462
5600
  execute: ({ message, done, type }) => {
5601
+ if (!message || message.trim() === "") {
5602
+ return "\u26A0\uFE0F TellUser was called without a message. Please provide content in the 'message' field.";
5603
+ }
5463
5604
  const prefixes = {
5464
5605
  info: "\u2139\uFE0F ",
5465
5606
  success: "\u2705 ",
@@ -5741,53 +5882,6 @@ ${rendered}`;
5741
5882
  }
5742
5883
 
5743
5884
  // src/cli/utils.ts
5744
- var RARE_EMOJI = [
5745
- "\u{1F531}",
5746
- "\u2697\uFE0F",
5747
- "\u{1F9FF}",
5748
- "\u{1F530}",
5749
- "\u269B\uFE0F",
5750
- "\u{1F3FA}",
5751
- "\u{1F9EB}",
5752
- "\u{1F52C}",
5753
- "\u2695\uFE0F",
5754
- "\u{1F5DD}\uFE0F",
5755
- "\u2696\uFE0F",
5756
- "\u{1F52E}",
5757
- "\u{1FAAC}",
5758
- "\u{1F9EC}",
5759
- "\u2699\uFE0F",
5760
- "\u{1F529}",
5761
- "\u{1FA9B}",
5762
- "\u26CF\uFE0F",
5763
- "\u{1FA83}",
5764
- "\u{1F3F9}",
5765
- "\u{1F6E1}\uFE0F",
5766
- "\u2694\uFE0F",
5767
- "\u{1F5E1}\uFE0F",
5768
- "\u{1FA93}",
5769
- "\u{1F5C3}\uFE0F",
5770
- "\u{1F4DC}",
5771
- "\u{1F4EF}",
5772
- "\u{1F3B4}",
5773
- "\u{1F004}",
5774
- "\u{1F3B2}"
5775
- ];
5776
- function generateMarkers() {
5777
- const pick = (count) => {
5778
- const result = [];
5779
- const pool = [...RARE_EMOJI];
5780
- for (let i = 0; i < count && pool.length > 0; i++) {
5781
- const idx = Math.floor(Math.random() * pool.length);
5782
- result.push(pool.splice(idx, 1)[0]);
5783
- }
5784
- return result.join("");
5785
- };
5786
- return {
5787
- startPrefix: pick(5),
5788
- endPrefix: pick(5)
5789
- };
5790
- }
5791
5885
  function createNumericParser({
5792
5886
  label,
5793
5887
  integer = false,
@@ -6213,6 +6307,10 @@ function configToAgentOptions(config) {
6213
6307
  if (config.builtins !== void 0) result.builtins = config.builtins;
6214
6308
  if (config["builtin-interaction"] !== void 0)
6215
6309
  result.builtinInteraction = config["builtin-interaction"];
6310
+ if (config["gadget-start-prefix"] !== void 0)
6311
+ result.gadgetStartPrefix = config["gadget-start-prefix"];
6312
+ if (config["gadget-end-prefix"] !== void 0)
6313
+ result.gadgetEndPrefix = config["gadget-end-prefix"];
6216
6314
  return result;
6217
6315
  }
6218
6316
 
@@ -6427,9 +6525,27 @@ Command rejected by user with message: "${response}"`
6427
6525
  builder.withGadgets(...gadgets);
6428
6526
  }
6429
6527
  builder.withParameterFormat(options.parameterFormat);
6430
- const markers = generateMarkers();
6431
- builder.withGadgetStartPrefix(markers.startPrefix);
6432
- builder.withGadgetEndPrefix(markers.endPrefix);
6528
+ if (options.gadgetStartPrefix) {
6529
+ builder.withGadgetStartPrefix(options.gadgetStartPrefix);
6530
+ }
6531
+ if (options.gadgetEndPrefix) {
6532
+ builder.withGadgetEndPrefix(options.gadgetEndPrefix);
6533
+ }
6534
+ builder.withSyntheticGadgetCall(
6535
+ "TellUser",
6536
+ {
6537
+ message: "\u{1F44B} Hello! I'm ready to help.\n\nHere's what I can do:\n- Analyze your codebase\n- Execute commands\n- Answer questions\n\nWhat would you like me to work on?",
6538
+ done: false,
6539
+ type: "info"
6540
+ },
6541
+ "\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\nHere's what I can do:\n- Analyze your codebase\n- Execute commands\n- Answer questions\n\nWhat would you like me to work on?"
6542
+ );
6543
+ builder.withTextOnlyHandler("acknowledge");
6544
+ builder.withTextWithGadgetsHandler({
6545
+ gadgetName: "TellUser",
6546
+ parameterMapping: (text) => ({ message: text, done: false, type: "info" }),
6547
+ resultMapping: (text) => `\u2139\uFE0F ${text}`
6548
+ });
6433
6549
  const agent = builder.ask(prompt);
6434
6550
  for await (const event of agent.run()) {
6435
6551
  if (event.type === "text") {
@@ -6551,7 +6667,9 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
6551
6667
  "gadget",
6552
6668
  "parameter-format",
6553
6669
  "builtins",
6554
- "builtin-interaction"
6670
+ "builtin-interaction",
6671
+ "gadget-start-prefix",
6672
+ "gadget-end-prefix"
6555
6673
  ]);
6556
6674
  var CUSTOM_CONFIG_KEYS = /* @__PURE__ */ new Set([
6557
6675
  ...COMPLETE_CONFIG_KEYS,
@@ -6713,6 +6831,20 @@ function validateAgentConfig(raw, section) {
6713
6831
  section
6714
6832
  );
6715
6833
  }
6834
+ if ("gadget-start-prefix" in rawObj) {
6835
+ result["gadget-start-prefix"] = validateString(
6836
+ rawObj["gadget-start-prefix"],
6837
+ "gadget-start-prefix",
6838
+ section
6839
+ );
6840
+ }
6841
+ if ("gadget-end-prefix" in rawObj) {
6842
+ result["gadget-end-prefix"] = validateString(
6843
+ rawObj["gadget-end-prefix"],
6844
+ "gadget-end-prefix",
6845
+ section
6846
+ );
6847
+ }
6716
6848
  return result;
6717
6849
  }
6718
6850
  function validateCustomConfig(raw, section) {
@@ -6768,6 +6900,20 @@ function validateCustomConfig(raw, section) {
6768
6900
  section
6769
6901
  );
6770
6902
  }
6903
+ if ("gadget-start-prefix" in rawObj) {
6904
+ result["gadget-start-prefix"] = validateString(
6905
+ rawObj["gadget-start-prefix"],
6906
+ "gadget-start-prefix",
6907
+ section
6908
+ );
6909
+ }
6910
+ if ("gadget-end-prefix" in rawObj) {
6911
+ result["gadget-end-prefix"] = validateString(
6912
+ rawObj["gadget-end-prefix"],
6913
+ "gadget-end-prefix",
6914
+ section
6915
+ );
6916
+ }
6771
6917
  if ("max-tokens" in rawObj) {
6772
6918
  result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
6773
6919
  integer: true,