llmist 17.5.1 → 18.0.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.d.cts CHANGED
@@ -1010,6 +1010,18 @@ interface CreateGadgetConfig<TSchema extends ZodType> {
1010
1010
  * - `N > 1` = At most N concurrent
1011
1011
  */
1012
1012
  maxConcurrent?: number;
1013
+ /**
1014
+ * If true, this gadget's results are marked sticky and survive compaction.
1015
+ * See `AbstractGadget.stickyResult` for the full contract.
1016
+ */
1017
+ stickyResult?: boolean;
1018
+ /**
1019
+ * If true, the consuming agent loop should treat this gadget as a per-
1020
+ * iteration barrier — when it appears in a tool batch, no sibling gadgets
1021
+ * in the same batch execute. See `AbstractGadget.iterationBarrier` for the
1022
+ * full contract (enforcement is consumer-side; this flag is declarative).
1023
+ */
1024
+ iterationBarrier?: boolean;
1013
1025
  }
1014
1026
  /**
1015
1027
  * Creates a gadget from a function (simpler than class-based approach).
@@ -1229,6 +1241,8 @@ declare function Gadget<TSchema extends ZodType>(config: GadgetConfig<TSchema>):
1229
1241
  * ```
1230
1242
  */
1231
1243
  execute(params: Record<string, unknown>, ctx?: ExecutionContext): GadgetExecuteReturn | Promise<GadgetExecuteReturn>;
1244
+ stickyResult?: boolean;
1245
+ iterationBarrier?: boolean;
1232
1246
  throwIfAborted(ctx?: ExecutionContext): void;
1233
1247
  onAbort(ctx: ExecutionContext | undefined, cleanup: () => void | Promise<void>): void;
1234
1248
  createLinkedAbortController(ctx?: ExecutionContext): AbortController;
@@ -3858,6 +3872,15 @@ interface GadgetExecutionResult {
3858
3872
  mediaIds?: string[];
3859
3873
  /** Stored media with paths (for CLI display) */
3860
3874
  storedMedia?: StoredMedia[];
3875
+ /**
3876
+ * If true, the message persisting this gadget result is marked sticky
3877
+ * (`metadata.sticky = true`) so compaction strategies preserve it
3878
+ * indefinitely. Use for gadgets whose output the agent needs to remember
3879
+ * for the rest of the conversation (e.g. `LoadSkill` whose body is the
3880
+ * canonical reference the agent will keep consulting). Copied from
3881
+ * `AbstractGadget.stickyResult` by the executor.
3882
+ */
3883
+ stickyResult?: boolean;
3861
3884
  }
3862
3885
  /**
3863
3886
  * Result returned by gadget execute() method.
@@ -4131,6 +4154,43 @@ declare abstract class AbstractGadget {
4131
4154
  * This is a safety floor: external config cannot weaken it.
4132
4155
  */
4133
4156
  exclusive?: boolean;
4157
+ /**
4158
+ * If true, results produced by this gadget are marked sticky on the
4159
+ * conversation (`message.metadata.sticky === true`). Compaction strategies
4160
+ * preserve sticky messages past the truncation point, so the agent retains
4161
+ * the gadget's output for the rest of the conversation rather than having
4162
+ * it dropped on the next compaction pass.
4163
+ *
4164
+ * Use for gadgets whose output is *reference material* the agent will keep
4165
+ * consulting — `LoadSkill` is the canonical example: a multi-KB skill body
4166
+ * the agent needs to remember across iterations. Don't use for routine
4167
+ * gadget outputs (file reads, computation results) — those should churn
4168
+ * normally with the conversation.
4169
+ *
4170
+ * Has no effect on agents that don't enable compaction.
4171
+ */
4172
+ stickyResult?: boolean;
4173
+ /**
4174
+ * Hints to the consuming agent loop that when this gadget appears in an
4175
+ * LLM iteration's tool batch, no other gadget in the same batch should
4176
+ * execute. Sibling tool calls in the same iteration are expected to be
4177
+ * skipped (not executed) with a synthetic result; the next LLM iteration
4178
+ * gets only this gadget's output back, and must re-plan from there.
4179
+ *
4180
+ * llmist exposes this as declarative metadata only — enforcement is the
4181
+ * consuming agent loop's responsibility (the loop already owns the
4182
+ * stream-event consumption and the `beforeGadgetExecution` controller, so
4183
+ * it can buffer per-iteration calls, decide barrier-status at
4184
+ * `llm_response_end`, and skip non-barrier siblings via the standard
4185
+ * skip-with-synthetic-result mechanism). See `LoadSkill` for the canonical
4186
+ * use case: the agent loop should freeze sibling tool execution so the
4187
+ * LLM sees only the loaded skill body before issuing dependent work.
4188
+ *
4189
+ * Orthogonal to `stickyResult` (which affects compaction) and `exclusive`
4190
+ * (which queues the marked gadget alone, AFTER others — opposite of this
4191
+ * flag's "freeze the others" semantic).
4192
+ */
4193
+ iterationBarrier?: boolean;
4134
4194
  /**
4135
4195
  * Execute the gadget with the given parameters.
4136
4196
  * Can be synchronous or asynchronous.
@@ -4554,7 +4614,7 @@ declare class LLMMessageBuilder {
4554
4614
  * @param mediaIds - Optional IDs for the media outputs
4555
4615
  * @param storedMedia - Optional stored media info including file paths
4556
4616
  */
4557
- addGadgetCallResult(gadget: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): this;
4617
+ addGadgetCallResult(gadget: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[], metadata?: Record<string, unknown>): this;
4558
4618
  /**
4559
4619
  * Format parameters as Block format with JSON Pointer paths.
4560
4620
  * Uses the configured argPrefix for consistency with system prompt.
@@ -5914,7 +5974,7 @@ interface IConversationManager {
5914
5974
  * Optionally includes media outputs (images, audio, etc.) for multimodal results.
5915
5975
  * If storedMedia is provided, file paths will be included in the result message.
5916
5976
  */
5917
- addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): void;
5977
+ addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[], metadata?: Record<string, unknown>): void;
5918
5978
  /**
5919
5979
  * Gets the complete conversation history including base messages (system prompts, gadget instructions).
5920
5980
  */
@@ -7643,7 +7703,7 @@ declare class ConversationManager implements IConversationManager {
7643
7703
  constructor(baseMessages: LLMMessage[], initialMessages: LLMMessage[], options?: ConversationManagerOptions);
7644
7704
  addUserMessage(content: MessageContent): void;
7645
7705
  addAssistantMessage(content: string): void;
7646
- addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): void;
7706
+ addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[], metadata?: Record<string, unknown>): void;
7647
7707
  getMessages(): LLMMessage[];
7648
7708
  getHistoryMessages(): LLMMessage[];
7649
7709
  getBaseMessages(): LLMMessage[];
@@ -10519,8 +10579,8 @@ declare function resolveInstructions(instructions: string, options?: {
10519
10579
  * LoadSkill meta-gadget — bridges the skill system into the gadget execution pipeline.
10520
10580
  *
10521
10581
  * When skills are registered with an agent, this gadget is auto-created and added
10522
- * to the gadget registry. The LLM can invoke it like any other gadget, and the
10523
- * skill's instructions are returned as the gadget result.
10582
+ * to the gadget registry. The LLM invokes it with an array of skill names; each
10583
+ * skill's resolved instructions are composed into a single multi-section result.
10524
10584
  *
10525
10585
  * This approach requires zero changes to the stream processor or agent loop.
10526
10586
  *
@@ -10532,8 +10592,16 @@ declare const LOAD_SKILL_GADGET_NAME = "LoadSkill";
10532
10592
  /**
10533
10593
  * Create the LoadSkill meta-gadget from a skill registry.
10534
10594
  *
10535
- * The gadget description includes a summary of all available skills,
10536
- * so the LLM knows what skills exist and when to load them.
10595
+ * The gadget's tool description includes a summary of all available skills, so
10596
+ * the LLM knows what skills exist and when to load them. Setting
10597
+ * `iterationBarrier: true` and `stickyResult: true` are the two declarative
10598
+ * flags every LoadSkill should carry:
10599
+ *
10600
+ * - `iterationBarrier`: tells the consuming agent loop to skip every sibling
10601
+ * tool call in the same iteration's batch. The next LLM iteration sees
10602
+ * only the loaded skill bodies and re-plans from there.
10603
+ * - `stickyResult`: tells the compaction layer to preserve the result past
10604
+ * truncation, so the agent doesn't re-load the same skill ten turns later.
10537
10605
  */
10538
10606
  declare function createLoadSkillGadget(registry: SkillRegistry): AbstractGadget;
10539
10607
 
package/dist/index.d.ts CHANGED
@@ -1010,6 +1010,18 @@ interface CreateGadgetConfig<TSchema extends ZodType> {
1010
1010
  * - `N > 1` = At most N concurrent
1011
1011
  */
1012
1012
  maxConcurrent?: number;
1013
+ /**
1014
+ * If true, this gadget's results are marked sticky and survive compaction.
1015
+ * See `AbstractGadget.stickyResult` for the full contract.
1016
+ */
1017
+ stickyResult?: boolean;
1018
+ /**
1019
+ * If true, the consuming agent loop should treat this gadget as a per-
1020
+ * iteration barrier — when it appears in a tool batch, no sibling gadgets
1021
+ * in the same batch execute. See `AbstractGadget.iterationBarrier` for the
1022
+ * full contract (enforcement is consumer-side; this flag is declarative).
1023
+ */
1024
+ iterationBarrier?: boolean;
1013
1025
  }
1014
1026
  /**
1015
1027
  * Creates a gadget from a function (simpler than class-based approach).
@@ -1229,6 +1241,8 @@ declare function Gadget<TSchema extends ZodType>(config: GadgetConfig<TSchema>):
1229
1241
  * ```
1230
1242
  */
1231
1243
  execute(params: Record<string, unknown>, ctx?: ExecutionContext): GadgetExecuteReturn | Promise<GadgetExecuteReturn>;
1244
+ stickyResult?: boolean;
1245
+ iterationBarrier?: boolean;
1232
1246
  throwIfAborted(ctx?: ExecutionContext): void;
1233
1247
  onAbort(ctx: ExecutionContext | undefined, cleanup: () => void | Promise<void>): void;
1234
1248
  createLinkedAbortController(ctx?: ExecutionContext): AbortController;
@@ -3858,6 +3872,15 @@ interface GadgetExecutionResult {
3858
3872
  mediaIds?: string[];
3859
3873
  /** Stored media with paths (for CLI display) */
3860
3874
  storedMedia?: StoredMedia[];
3875
+ /**
3876
+ * If true, the message persisting this gadget result is marked sticky
3877
+ * (`metadata.sticky = true`) so compaction strategies preserve it
3878
+ * indefinitely. Use for gadgets whose output the agent needs to remember
3879
+ * for the rest of the conversation (e.g. `LoadSkill` whose body is the
3880
+ * canonical reference the agent will keep consulting). Copied from
3881
+ * `AbstractGadget.stickyResult` by the executor.
3882
+ */
3883
+ stickyResult?: boolean;
3861
3884
  }
3862
3885
  /**
3863
3886
  * Result returned by gadget execute() method.
@@ -4131,6 +4154,43 @@ declare abstract class AbstractGadget {
4131
4154
  * This is a safety floor: external config cannot weaken it.
4132
4155
  */
4133
4156
  exclusive?: boolean;
4157
+ /**
4158
+ * If true, results produced by this gadget are marked sticky on the
4159
+ * conversation (`message.metadata.sticky === true`). Compaction strategies
4160
+ * preserve sticky messages past the truncation point, so the agent retains
4161
+ * the gadget's output for the rest of the conversation rather than having
4162
+ * it dropped on the next compaction pass.
4163
+ *
4164
+ * Use for gadgets whose output is *reference material* the agent will keep
4165
+ * consulting — `LoadSkill` is the canonical example: a multi-KB skill body
4166
+ * the agent needs to remember across iterations. Don't use for routine
4167
+ * gadget outputs (file reads, computation results) — those should churn
4168
+ * normally with the conversation.
4169
+ *
4170
+ * Has no effect on agents that don't enable compaction.
4171
+ */
4172
+ stickyResult?: boolean;
4173
+ /**
4174
+ * Hints to the consuming agent loop that when this gadget appears in an
4175
+ * LLM iteration's tool batch, no other gadget in the same batch should
4176
+ * execute. Sibling tool calls in the same iteration are expected to be
4177
+ * skipped (not executed) with a synthetic result; the next LLM iteration
4178
+ * gets only this gadget's output back, and must re-plan from there.
4179
+ *
4180
+ * llmist exposes this as declarative metadata only — enforcement is the
4181
+ * consuming agent loop's responsibility (the loop already owns the
4182
+ * stream-event consumption and the `beforeGadgetExecution` controller, so
4183
+ * it can buffer per-iteration calls, decide barrier-status at
4184
+ * `llm_response_end`, and skip non-barrier siblings via the standard
4185
+ * skip-with-synthetic-result mechanism). See `LoadSkill` for the canonical
4186
+ * use case: the agent loop should freeze sibling tool execution so the
4187
+ * LLM sees only the loaded skill body before issuing dependent work.
4188
+ *
4189
+ * Orthogonal to `stickyResult` (which affects compaction) and `exclusive`
4190
+ * (which queues the marked gadget alone, AFTER others — opposite of this
4191
+ * flag's "freeze the others" semantic).
4192
+ */
4193
+ iterationBarrier?: boolean;
4134
4194
  /**
4135
4195
  * Execute the gadget with the given parameters.
4136
4196
  * Can be synchronous or asynchronous.
@@ -4554,7 +4614,7 @@ declare class LLMMessageBuilder {
4554
4614
  * @param mediaIds - Optional IDs for the media outputs
4555
4615
  * @param storedMedia - Optional stored media info including file paths
4556
4616
  */
4557
- addGadgetCallResult(gadget: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): this;
4617
+ addGadgetCallResult(gadget: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[], metadata?: Record<string, unknown>): this;
4558
4618
  /**
4559
4619
  * Format parameters as Block format with JSON Pointer paths.
4560
4620
  * Uses the configured argPrefix for consistency with system prompt.
@@ -5914,7 +5974,7 @@ interface IConversationManager {
5914
5974
  * Optionally includes media outputs (images, audio, etc.) for multimodal results.
5915
5975
  * If storedMedia is provided, file paths will be included in the result message.
5916
5976
  */
5917
- addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): void;
5977
+ addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[], metadata?: Record<string, unknown>): void;
5918
5978
  /**
5919
5979
  * Gets the complete conversation history including base messages (system prompts, gadget instructions).
5920
5980
  */
@@ -7643,7 +7703,7 @@ declare class ConversationManager implements IConversationManager {
7643
7703
  constructor(baseMessages: LLMMessage[], initialMessages: LLMMessage[], options?: ConversationManagerOptions);
7644
7704
  addUserMessage(content: MessageContent): void;
7645
7705
  addAssistantMessage(content: string): void;
7646
- addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): void;
7706
+ addGadgetCallResult(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[], metadata?: Record<string, unknown>): void;
7647
7707
  getMessages(): LLMMessage[];
7648
7708
  getHistoryMessages(): LLMMessage[];
7649
7709
  getBaseMessages(): LLMMessage[];
@@ -10519,8 +10579,8 @@ declare function resolveInstructions(instructions: string, options?: {
10519
10579
  * LoadSkill meta-gadget — bridges the skill system into the gadget execution pipeline.
10520
10580
  *
10521
10581
  * When skills are registered with an agent, this gadget is auto-created and added
10522
- * to the gadget registry. The LLM can invoke it like any other gadget, and the
10523
- * skill's instructions are returned as the gadget result.
10582
+ * to the gadget registry. The LLM invokes it with an array of skill names; each
10583
+ * skill's resolved instructions are composed into a single multi-section result.
10524
10584
  *
10525
10585
  * This approach requires zero changes to the stream processor or agent loop.
10526
10586
  *
@@ -10532,8 +10592,16 @@ declare const LOAD_SKILL_GADGET_NAME = "LoadSkill";
10532
10592
  /**
10533
10593
  * Create the LoadSkill meta-gadget from a skill registry.
10534
10594
  *
10535
- * The gadget description includes a summary of all available skills,
10536
- * so the LLM knows what skills exist and when to load them.
10595
+ * The gadget's tool description includes a summary of all available skills, so
10596
+ * the LLM knows what skills exist and when to load them. Setting
10597
+ * `iterationBarrier: true` and `stickyResult: true` are the two declarative
10598
+ * flags every LoadSkill should carry:
10599
+ *
10600
+ * - `iterationBarrier`: tells the consuming agent loop to skip every sibling
10601
+ * tool call in the same iteration's batch. The next LLM iteration sees
10602
+ * only the loaded skill bodies and re-plans from there.
10603
+ * - `stickyResult`: tells the compaction layer to preserve the result past
10604
+ * truncation, so the agent doesn't re-load the same skill ten turns later.
10537
10605
  */
10538
10606
  declare function createLoadSkillGadget(registry: SkillRegistry): AbstractGadget;
10539
10607
 
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ import {
81
81
  toBase64,
82
82
  validateGadgetSchema,
83
83
  withErrorHandling
84
- } from "./chunk-HM7PUGPA.js";
84
+ } from "./chunk-DNB4DPM4.js";
85
85
 
86
86
  // src/core/execution-tree-aggregator.ts
87
87
  var ExecutionTreeAggregator;
@@ -1884,11 +1884,19 @@ var init_sliding_window = __esm({
1884
1884
  }
1885
1885
  const turnsToKeep = turns.slice(-preserveCount);
1886
1886
  const turnsRemoved = turns.length - preserveCount;
1887
+ const keptMessageRefs = new Set(turnsToKeep.flatMap((turn) => turn.messages));
1888
+ const stickyToPreserve = messages.filter(
1889
+ (msg) => msg.metadata?.sticky === true && !keptMessageRefs.has(msg)
1890
+ );
1887
1891
  const truncationMarker = {
1888
1892
  role: "user",
1889
1893
  content: TRUNCATION_MARKER_TEMPLATE.replace("{count}", turnsRemoved.toString())
1890
1894
  };
1891
- const compactedMessages = [truncationMarker, ...flattenTurns(turnsToKeep)];
1895
+ const compactedMessages = [
1896
+ truncationMarker,
1897
+ ...stickyToPreserve,
1898
+ ...flattenTurns(turnsToKeep)
1899
+ ];
1892
1900
  const tokensAfter = Math.ceil(
1893
1901
  compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
1894
1902
  );
@@ -1932,7 +1940,14 @@ var init_summarization = __esm({
1932
1940
  }
1933
1941
  const turnsToSummarize = turns.slice(0, -preserveCount);
1934
1942
  const turnsToKeep = turns.slice(-preserveCount);
1935
- const conversationToSummarize = this.formatTurnsForSummary(flattenTurns(turnsToSummarize));
1943
+ const keptMessageRefs = new Set(turnsToKeep.flatMap((turn) => turn.messages));
1944
+ const stickyToPreserve = messages.filter(
1945
+ (msg) => msg.metadata?.sticky === true && !keptMessageRefs.has(msg)
1946
+ );
1947
+ const turnsToSummarizeMessages = flattenTurns(turnsToSummarize).filter(
1948
+ (msg) => msg.metadata?.sticky !== true
1949
+ );
1950
+ const conversationToSummarize = this.formatTurnsForSummary(turnsToSummarizeMessages);
1936
1951
  const summary = await this.generateSummary(conversationToSummarize, config, context);
1937
1952
  const summaryMessage = {
1938
1953
  role: "user",
@@ -1940,7 +1955,11 @@ var init_summarization = __esm({
1940
1955
  ${summary}
1941
1956
  [End of summary - conversation continues below]`
1942
1957
  };
1943
- const compactedMessages = [summaryMessage, ...flattenTurns(turnsToKeep)];
1958
+ const compactedMessages = [
1959
+ summaryMessage,
1960
+ ...stickyToPreserve,
1961
+ ...flattenTurns(turnsToKeep)
1962
+ ];
1944
1963
  const tokensAfter = Math.ceil(
1945
1964
  compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
1946
1965
  );
@@ -2261,7 +2280,7 @@ var init_conversation_manager = __esm({
2261
2280
  addAssistantMessage(content) {
2262
2281
  this.historyBuilder.addAssistant(content);
2263
2282
  }
2264
- addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds, storedMedia) {
2283
+ addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds, storedMedia, metadata) {
2265
2284
  this.historyBuilder.addGadgetCallResult(
2266
2285
  gadgetName,
2267
2286
  parameters,
@@ -2269,7 +2288,8 @@ var init_conversation_manager = __esm({
2269
2288
  invocationId,
2270
2289
  media,
2271
2290
  mediaIds,
2272
- storedMedia
2291
+ storedMedia,
2292
+ metadata
2273
2293
  );
2274
2294
  }
2275
2295
  getMessages() {
@@ -2401,6 +2421,7 @@ var init_conversation_updater = __esm({
2401
2421
  for (const output of gadgetResults) {
2402
2422
  if (output.type === "gadget_result") {
2403
2423
  const gadgetResult = output.result;
2424
+ const metadata = gadgetResult.stickyResult === true && gadgetResult.error === void 0 ? { sticky: true } : void 0;
2404
2425
  this.conversation.addGadgetCallResult(
2405
2426
  gadgetResult.gadgetName,
2406
2427
  gadgetResult.parameters,
@@ -2408,7 +2429,8 @@ var init_conversation_updater = __esm({
2408
2429
  gadgetResult.invocationId,
2409
2430
  gadgetResult.media,
2410
2431
  gadgetResult.mediaIds,
2411
- gadgetResult.storedMedia
2432
+ gadgetResult.storedMedia,
2433
+ metadata
2412
2434
  );
2413
2435
  }
2414
2436
  }
@@ -4103,11 +4125,20 @@ var init_activation = __esm({
4103
4125
 
4104
4126
  // src/skills/load-skill-gadget.ts
4105
4127
  import { z as z2 } from "zod";
4128
+ function composeSkillSections(sections) {
4129
+ return sections.map(({ name, body }) => `==== ${name} ====
4130
+ ${body}`).join("\n\n");
4131
+ }
4106
4132
  function createLoadSkillGadget(registry) {
4107
4133
  const summaries = registry.getMetadataSummaries();
4108
4134
  const skillNames = registry.getModelInvocable().map((s) => s.name);
4109
4135
  const description = [
4110
- "Load a skill's specialized instructions into context for a task.",
4136
+ "Load one or more skill bodies into context. Pass an array even for a",
4137
+ 'single skill: `{skills: ["some-skill"]}`. **This gadget is an iteration',
4138
+ "barrier \u2014 no other gadgets in the same tool batch will execute, so load",
4139
+ "every skill you know you'll need in one shot.** The loaded bodies are",
4140
+ "sticky and survive context compaction.",
4141
+ "",
4111
4142
  "Available skills:",
4112
4143
  summaries
4113
4144
  ].join("\n");
@@ -4115,19 +4146,33 @@ function createLoadSkillGadget(registry) {
4115
4146
  name: LOAD_SKILL_GADGET_NAME,
4116
4147
  description,
4117
4148
  schema: z2.object({
4118
- skill: z2.enum(skillNames).describe("Name of the skill to load"),
4119
- arguments: z2.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
4149
+ skills: z2.array(z2.enum(skillNames)).min(1).describe(
4150
+ "One or more skill names to load in this single call. Prefer loading everything you know you'll need at once \u2014 this gadget is an iteration barrier, so sibling tool calls in the same batch will not execute."
4151
+ ),
4152
+ arguments: z2.string().optional().describe(
4153
+ "Optional argument string substituted into each skill's $ARGUMENTS placeholders. Applies to every skill in the batch. To pass different arguments to different skills, issue separate LoadSkill calls."
4154
+ )
4120
4155
  }),
4121
- execute: async ({ skill: skillName, arguments: args }) => {
4122
- const skill = registry.get(skillName);
4123
- if (!skill) {
4124
- return `Unknown skill: "${skillName}". Available skills: ${skillNames.join(", ")}`;
4125
- }
4126
- const activation = await skill.activate({
4127
- arguments: args,
4128
- cwd: process.cwd()
4129
- });
4130
- return activation.resolvedInstructions;
4156
+ stickyResult: true,
4157
+ iterationBarrier: true,
4158
+ execute: async ({ skills: skillNamesArg, arguments: args }) => {
4159
+ const sections = [];
4160
+ for (const skillName of skillNamesArg) {
4161
+ const skill = registry.get(skillName);
4162
+ if (!skill) {
4163
+ sections.push({
4164
+ name: skillName,
4165
+ body: `Unknown skill: "${skillName}". Available skills: ${skillNames.join(", ")}`
4166
+ });
4167
+ continue;
4168
+ }
4169
+ const activation = await skill.activate({
4170
+ arguments: args,
4171
+ cwd: process.cwd()
4172
+ });
4173
+ sections.push({ name: skillName, body: activation.resolvedInstructions });
4174
+ }
4175
+ return composeSkillSections(sections);
4131
4176
  }
4132
4177
  });
4133
4178
  }
@@ -13499,7 +13544,13 @@ var init_executor = __esm({
13499
13544
  cost: totalCost,
13500
13545
  media,
13501
13546
  mediaIds,
13502
- storedMedia
13547
+ storedMedia,
13548
+ // Copy the gadget's sticky flag into the result envelope so the
13549
+ // conversation-updater can mark the persisted message sticky for
13550
+ // the compaction layer. Only set on success — errors and other
13551
+ // unsuccessful exits skip this so failed loads don't pin themselves
13552
+ // in context.
13553
+ stickyResult: gadget.stickyResult
13503
13554
  };
13504
13555
  } catch (error) {
13505
13556
  const isTaskCompletionSignal = error instanceof Error && error.name === "TaskCompletionSignal";
@@ -15688,7 +15739,7 @@ var init_agent = __esm({
15688
15739
  }
15689
15740
  const unsubscribeBridge = bridgeTreeToHooks(this.tree, this.hooks, this.logger);
15690
15741
  if (this.mcpSpecs.length > 0) {
15691
- const { setupMcpServers } = await import("./runtime-GKQ6QIQP.js");
15742
+ const { setupMcpServers } = await import("./runtime-KB7US2FQ.js");
15692
15743
  this.mcpLifecycle = await setupMcpServers({
15693
15744
  specs: this.mcpSpecs,
15694
15745
  registry: this.registry,