llmist 18.1.0 → 18.3.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 +339 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +147 -2
- package/dist/index.d.ts +147 -2
- package/dist/index.js +333 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1926,7 +1926,8 @@ function resolveRetryConfig(config) {
|
|
|
1926
1926
|
onRetriesExhausted: config.onRetriesExhausted,
|
|
1927
1927
|
shouldRetry: config.shouldRetry,
|
|
1928
1928
|
respectRetryAfter: config.respectRetryAfter ?? DEFAULT_RETRY_CONFIG.respectRetryAfter,
|
|
1929
|
-
maxRetryAfterMs: config.maxRetryAfterMs ?? DEFAULT_RETRY_CONFIG.maxRetryAfterMs
|
|
1929
|
+
maxRetryAfterMs: config.maxRetryAfterMs ?? DEFAULT_RETRY_CONFIG.maxRetryAfterMs,
|
|
1930
|
+
retryOnEmpty: config.retryOnEmpty ?? DEFAULT_RETRY_CONFIG.retryOnEmpty
|
|
1930
1931
|
};
|
|
1931
1932
|
}
|
|
1932
1933
|
function getErrorStatusCode(error) {
|
|
@@ -2178,8 +2179,9 @@ var init_retry = __esm({
|
|
|
2178
2179
|
factor: 2,
|
|
2179
2180
|
randomize: true,
|
|
2180
2181
|
respectRetryAfter: true,
|
|
2181
|
-
maxRetryAfterMs: 12e4
|
|
2182
|
+
maxRetryAfterMs: 12e4,
|
|
2182
2183
|
// 2 minutes cap
|
|
2184
|
+
retryOnEmpty: true
|
|
2183
2185
|
};
|
|
2184
2186
|
}
|
|
2185
2187
|
});
|
|
@@ -3256,6 +3258,18 @@ async function runWithHandlers(agentGenerator, handlers) {
|
|
|
3256
3258
|
});
|
|
3257
3259
|
}
|
|
3258
3260
|
break;
|
|
3261
|
+
case "gadget_args_partial":
|
|
3262
|
+
if (handlers.onGadgetArgsPartial) {
|
|
3263
|
+
await handlers.onGadgetArgsPartial({
|
|
3264
|
+
gadgetName: event.gadgetName,
|
|
3265
|
+
invocationId: event.invocationId,
|
|
3266
|
+
fieldPath: event.fieldPath,
|
|
3267
|
+
value: event.value,
|
|
3268
|
+
delta: event.delta,
|
|
3269
|
+
isFieldComplete: event.isFieldComplete
|
|
3270
|
+
});
|
|
3271
|
+
}
|
|
3272
|
+
break;
|
|
3259
3273
|
case "gadget_result":
|
|
3260
3274
|
if (handlers.onGadgetResult) {
|
|
3261
3275
|
await handlers.onGadgetResult(event.result);
|
|
@@ -5188,11 +5202,51 @@ var init_output_limit_manager = __esm({
|
|
|
5188
5202
|
}
|
|
5189
5203
|
});
|
|
5190
5204
|
|
|
5205
|
+
// src/core/errors.ts
|
|
5206
|
+
function isAbortError(error) {
|
|
5207
|
+
if (!(error instanceof Error)) return false;
|
|
5208
|
+
if (error.name === "AbortError") return true;
|
|
5209
|
+
if (error.name === "APIConnectionAbortedError") return true;
|
|
5210
|
+
if (error.name === "APIUserAbortError") return true;
|
|
5211
|
+
const message = error.message.toLowerCase();
|
|
5212
|
+
if (message.includes("abort")) return true;
|
|
5213
|
+
if (message.includes("cancelled")) return true;
|
|
5214
|
+
if (message.includes("canceled")) return true;
|
|
5215
|
+
return false;
|
|
5216
|
+
}
|
|
5217
|
+
var EmptyCompletionError;
|
|
5218
|
+
var init_errors = __esm({
|
|
5219
|
+
"src/core/errors.ts"() {
|
|
5220
|
+
"use strict";
|
|
5221
|
+
EmptyCompletionError = class extends Error {
|
|
5222
|
+
/** Agent iteration on which the empty completion was observed. */
|
|
5223
|
+
iteration;
|
|
5224
|
+
/** Finish reason reported alongside the empty body (often null). */
|
|
5225
|
+
finishReason;
|
|
5226
|
+
constructor(params) {
|
|
5227
|
+
super(
|
|
5228
|
+
`LLM returned an empty completion (no text, tool calls, or reasoning) on iteration ${params.iteration}`
|
|
5229
|
+
);
|
|
5230
|
+
this.name = "EmptyCompletionError";
|
|
5231
|
+
this.iteration = params.iteration;
|
|
5232
|
+
this.finishReason = params.finishReason;
|
|
5233
|
+
}
|
|
5234
|
+
};
|
|
5235
|
+
}
|
|
5236
|
+
});
|
|
5237
|
+
|
|
5191
5238
|
// src/agent/retry-orchestrator.ts
|
|
5239
|
+
function isEmptyCompletion(meta) {
|
|
5240
|
+
if (meta.didExecuteGadgets) return false;
|
|
5241
|
+
if (meta.rawResponse?.trim() || meta.finalMessage?.trim()) return false;
|
|
5242
|
+
if (meta.thinkingContent?.trim()) return false;
|
|
5243
|
+
return true;
|
|
5244
|
+
}
|
|
5192
5245
|
var RetryOrchestrator;
|
|
5193
5246
|
var init_retry_orchestrator = __esm({
|
|
5194
5247
|
"src/agent/retry-orchestrator.ts"() {
|
|
5195
5248
|
"use strict";
|
|
5249
|
+
init_errors();
|
|
5196
5250
|
init_retry();
|
|
5197
5251
|
init_safe_observe();
|
|
5198
5252
|
init_tree_hook_bridge();
|
|
@@ -5258,6 +5312,7 @@ var init_retry_orchestrator = __esm({
|
|
|
5258
5312
|
let gadgetCallCount = 0;
|
|
5259
5313
|
const textOutputs = [];
|
|
5260
5314
|
const gadgetResults = [];
|
|
5315
|
+
let emptyFailure = null;
|
|
5261
5316
|
while (streamAttempt < maxStreamAttempts) {
|
|
5262
5317
|
streamAttempt++;
|
|
5263
5318
|
try {
|
|
@@ -5266,6 +5321,7 @@ var init_retry_orchestrator = __esm({
|
|
|
5266
5321
|
for await (const event of processor.process(stream2)) {
|
|
5267
5322
|
if (event.type === "stream_complete") {
|
|
5268
5323
|
streamMetadata = event;
|
|
5324
|
+
continue;
|
|
5269
5325
|
}
|
|
5270
5326
|
if (event.type === "llm_response_end") {
|
|
5271
5327
|
this.tree.endLLMResponse(llmNodeId, {
|
|
@@ -5287,43 +5343,44 @@ var init_retry_orchestrator = __esm({
|
|
|
5287
5343
|
for (const id of processor.getFailedInvocationIds()) {
|
|
5288
5344
|
this.failedInvocationIds.add(id);
|
|
5289
5345
|
}
|
|
5346
|
+
if (this.retryConfig.enabled && this.retryConfig.retryOnEmpty && streamMetadata !== null && !streamMetadata.finishReason && isEmptyCompletion(streamMetadata)) {
|
|
5347
|
+
const emptyError = new EmptyCompletionError({
|
|
5348
|
+
iteration,
|
|
5349
|
+
finishReason: streamMetadata.finishReason
|
|
5350
|
+
});
|
|
5351
|
+
if (streamAttempt < maxStreamAttempts) {
|
|
5352
|
+
await this.backoffBeforeRetry(
|
|
5353
|
+
emptyError,
|
|
5354
|
+
streamAttempt,
|
|
5355
|
+
maxStreamAttempts,
|
|
5356
|
+
iteration,
|
|
5357
|
+
llmNodeId
|
|
5358
|
+
);
|
|
5359
|
+
streamMetadata = null;
|
|
5360
|
+
gadgetCallCount = 0;
|
|
5361
|
+
textOutputs.length = 0;
|
|
5362
|
+
gadgetResults.length = 0;
|
|
5363
|
+
continue;
|
|
5364
|
+
}
|
|
5365
|
+
emptyFailure = emptyError;
|
|
5366
|
+
break;
|
|
5367
|
+
}
|
|
5368
|
+
if (streamMetadata !== null) {
|
|
5369
|
+
yield streamMetadata;
|
|
5370
|
+
}
|
|
5290
5371
|
break;
|
|
5291
5372
|
} catch (streamError) {
|
|
5292
5373
|
const error = streamError;
|
|
5293
5374
|
const canRetry = this.retryConfig.enabled && streamAttempt < maxStreamAttempts;
|
|
5294
5375
|
const shouldRetryError = this.retryConfig.shouldRetry ? this.retryConfig.shouldRetry(error) : isRetryableError(error);
|
|
5295
5376
|
if (canRetry && shouldRetryError) {
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
`Stream iteration failed (attempt ${streamAttempt}/${maxStreamAttempts}), retrying...`,
|
|
5303
|
-
{
|
|
5304
|
-
error: error.message,
|
|
5305
|
-
retriesLeft: maxStreamAttempts - streamAttempt,
|
|
5306
|
-
delayMs: Math.round(finalDelay),
|
|
5307
|
-
retryAfterMs
|
|
5308
|
-
}
|
|
5377
|
+
await this.backoffBeforeRetry(
|
|
5378
|
+
error,
|
|
5379
|
+
streamAttempt,
|
|
5380
|
+
maxStreamAttempts,
|
|
5381
|
+
iteration,
|
|
5382
|
+
llmNodeId
|
|
5309
5383
|
);
|
|
5310
|
-
this.retryConfig.onRetry?.(error, streamAttempt);
|
|
5311
|
-
await safeObserve(async () => {
|
|
5312
|
-
if (this.hooks.observers?.onRetryAttempt) {
|
|
5313
|
-
const subagentContext = getSubagentContextForNode(this.tree, llmNodeId);
|
|
5314
|
-
const hookContext = {
|
|
5315
|
-
iteration,
|
|
5316
|
-
attemptNumber: streamAttempt,
|
|
5317
|
-
retriesLeft: maxStreamAttempts - streamAttempt,
|
|
5318
|
-
error,
|
|
5319
|
-
retryAfterMs: retryAfterMs ?? void 0,
|
|
5320
|
-
logger: this.logger,
|
|
5321
|
-
subagentContext
|
|
5322
|
-
};
|
|
5323
|
-
await this.hooks.observers.onRetryAttempt(hookContext);
|
|
5324
|
-
}
|
|
5325
|
-
}, this.logger);
|
|
5326
|
-
await this.sleep(finalDelay);
|
|
5327
5384
|
streamMetadata = null;
|
|
5328
5385
|
gadgetCallCount = 0;
|
|
5329
5386
|
textOutputs.length = 0;
|
|
@@ -5340,8 +5397,55 @@ var init_retry_orchestrator = __esm({
|
|
|
5340
5397
|
throw error;
|
|
5341
5398
|
}
|
|
5342
5399
|
}
|
|
5400
|
+
if (emptyFailure !== null) {
|
|
5401
|
+
this.logger.error(`LLM returned empty completions on all ${streamAttempt} attempts`, {
|
|
5402
|
+
iteration
|
|
5403
|
+
});
|
|
5404
|
+
this.retryConfig.onRetriesExhausted?.(emptyFailure, streamAttempt);
|
|
5405
|
+
throw emptyFailure;
|
|
5406
|
+
}
|
|
5343
5407
|
return streamMetadata !== null ? { streamMetadata, textOutputs, gadgetResults, gadgetCallCount } : null;
|
|
5344
5408
|
}
|
|
5409
|
+
/**
|
|
5410
|
+
* Apply the configured backoff before a retry attempt: compute the delay
|
|
5411
|
+
* (Retry-After hint or exponential backoff, with optional jitter), emit the
|
|
5412
|
+
* retry log, fire the `onRetry` callback and `onRetryAttempt` observer, then
|
|
5413
|
+
* sleep. Shared by the error-retry and empty-completion-retry paths so both
|
|
5414
|
+
* honour identical backoff and observer semantics.
|
|
5415
|
+
*/
|
|
5416
|
+
async backoffBeforeRetry(error, streamAttempt, maxStreamAttempts, iteration, llmNodeId) {
|
|
5417
|
+
const retryAfterMs = this.retryConfig.respectRetryAfter ? extractRetryAfterMs(error) : null;
|
|
5418
|
+
const baseDelay = this.retryConfig.minTimeout * this.retryConfig.factor ** (streamAttempt - 1);
|
|
5419
|
+
const cappedBaseDelay = Math.min(baseDelay, this.retryConfig.maxTimeout);
|
|
5420
|
+
const delay = retryAfterMs !== null ? Math.min(retryAfterMs, this.retryConfig.maxRetryAfterMs) : cappedBaseDelay;
|
|
5421
|
+
const finalDelay = this.retryConfig.randomize ? delay * (0.5 + Math.random()) : delay;
|
|
5422
|
+
this.logger.warn(
|
|
5423
|
+
`Stream iteration failed (attempt ${streamAttempt}/${maxStreamAttempts}), retrying...`,
|
|
5424
|
+
{
|
|
5425
|
+
error: error.message,
|
|
5426
|
+
retriesLeft: maxStreamAttempts - streamAttempt,
|
|
5427
|
+
delayMs: Math.round(finalDelay),
|
|
5428
|
+
retryAfterMs
|
|
5429
|
+
}
|
|
5430
|
+
);
|
|
5431
|
+
this.retryConfig.onRetry?.(error, streamAttempt);
|
|
5432
|
+
await safeObserve(async () => {
|
|
5433
|
+
if (this.hooks.observers?.onRetryAttempt) {
|
|
5434
|
+
const subagentContext = getSubagentContextForNode(this.tree, llmNodeId);
|
|
5435
|
+
const hookContext = {
|
|
5436
|
+
iteration,
|
|
5437
|
+
attemptNumber: streamAttempt,
|
|
5438
|
+
retriesLeft: maxStreamAttempts - streamAttempt,
|
|
5439
|
+
error,
|
|
5440
|
+
retryAfterMs: retryAfterMs ?? void 0,
|
|
5441
|
+
logger: this.logger,
|
|
5442
|
+
subagentContext
|
|
5443
|
+
};
|
|
5444
|
+
await this.hooks.observers.onRetryAttempt(hookContext);
|
|
5445
|
+
}
|
|
5446
|
+
}, this.logger);
|
|
5447
|
+
await this.sleep(finalDelay);
|
|
5448
|
+
}
|
|
5345
5449
|
};
|
|
5346
5450
|
}
|
|
5347
5451
|
});
|
|
@@ -12483,7 +12587,7 @@ var init_model_registry = __esm({
|
|
|
12483
12587
|
* Register a provider and collect its model specifications
|
|
12484
12588
|
*/
|
|
12485
12589
|
registerProvider(provider) {
|
|
12486
|
-
const specs = provider.getModelSpecs?.() ?? [];
|
|
12590
|
+
const specs = [...provider.getModelSpecs?.() ?? []];
|
|
12487
12591
|
if (specs.length > 0) {
|
|
12488
12592
|
this.modelSpecs.push(...specs);
|
|
12489
12593
|
this.providerMap.set(provider.providerId, specs);
|
|
@@ -12577,7 +12681,7 @@ var init_model_registry = __esm({
|
|
|
12577
12681
|
if (!providerId) {
|
|
12578
12682
|
return [...this.modelSpecs];
|
|
12579
12683
|
}
|
|
12580
|
-
return this.providerMap.get(providerId) ?? [];
|
|
12684
|
+
return [...this.providerMap.get(providerId) ?? []];
|
|
12581
12685
|
}
|
|
12582
12686
|
/**
|
|
12583
12687
|
* Get context window and output limits for a model
|
|
@@ -14478,13 +14582,22 @@ var init_parser2 = __esm({
|
|
|
14478
14582
|
GadgetCallParser = class {
|
|
14479
14583
|
buffer = "";
|
|
14480
14584
|
lastEmittedTextOffset = 0;
|
|
14585
|
+
/** Non-null only while a single trailing gadget block is mid-stream. */
|
|
14586
|
+
currentPartial = null;
|
|
14481
14587
|
startPrefix;
|
|
14482
14588
|
endPrefix;
|
|
14483
14589
|
argPrefix;
|
|
14590
|
+
/** Length of the longest marker; `maxMarkerLength - 1` is the scan-resume overlap. */
|
|
14591
|
+
maxMarkerLength;
|
|
14484
14592
|
constructor(options = {}) {
|
|
14485
14593
|
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
14486
14594
|
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
14487
14595
|
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
14596
|
+
this.maxMarkerLength = Math.max(
|
|
14597
|
+
this.startPrefix.length,
|
|
14598
|
+
this.endPrefix.length,
|
|
14599
|
+
this.argPrefix.length
|
|
14600
|
+
);
|
|
14488
14601
|
}
|
|
14489
14602
|
/**
|
|
14490
14603
|
* Extract and consume text up to the given index.
|
|
@@ -14571,12 +14684,13 @@ var init_parser2 = __esm({
|
|
|
14571
14684
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
14572
14685
|
if (metadataEndIndex === -1) break;
|
|
14573
14686
|
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
14574
|
-
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
14687
|
+
const { gadgetName, invocationId, dependencies } = this.currentPartial ? this.currentPartial : this.parseInvocationMetadata(headerLine);
|
|
14575
14688
|
const contentStartIndex = metadataEndIndex + 1;
|
|
14576
14689
|
let partEndIndex;
|
|
14577
14690
|
let endMarkerLength = 0;
|
|
14578
|
-
const
|
|
14579
|
-
const
|
|
14691
|
+
const bodySearchStart = this.currentPartial ? contentStartIndex + Math.max(0, this.currentPartial.bodyScannedLen - (this.maxMarkerLength - 1)) : contentStartIndex;
|
|
14692
|
+
const nextStartPos = this.buffer.indexOf(this.startPrefix, bodySearchStart);
|
|
14693
|
+
const endPos = this.buffer.indexOf(this.endPrefix, bodySearchStart);
|
|
14580
14694
|
if (nextStartPos !== -1 && (endPos === -1 || nextStartPos < endPos)) {
|
|
14581
14695
|
partEndIndex = nextStartPos;
|
|
14582
14696
|
endMarkerLength = 0;
|
|
@@ -14584,9 +14698,22 @@ var init_parser2 = __esm({
|
|
|
14584
14698
|
partEndIndex = endPos;
|
|
14585
14699
|
endMarkerLength = this.endPrefix.length;
|
|
14586
14700
|
} else {
|
|
14701
|
+
if (!this.currentPartial) {
|
|
14702
|
+
this.currentPartial = this.newPartialState(gadgetName, invocationId, dependencies);
|
|
14703
|
+
}
|
|
14704
|
+
yield* this.emitArgPartials(
|
|
14705
|
+
this.currentPartial,
|
|
14706
|
+
contentStartIndex,
|
|
14707
|
+
this.buffer.length,
|
|
14708
|
+
false
|
|
14709
|
+
);
|
|
14587
14710
|
break;
|
|
14588
14711
|
}
|
|
14589
|
-
const
|
|
14712
|
+
const rawSlice = this.buffer.substring(contentStartIndex, partEndIndex);
|
|
14713
|
+
if (this.currentPartial) {
|
|
14714
|
+
yield* this.emitArgPartials(this.currentPartial, contentStartIndex, partEndIndex, true);
|
|
14715
|
+
}
|
|
14716
|
+
const parametersRaw = rawSlice.trim();
|
|
14590
14717
|
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
14591
14718
|
yield {
|
|
14592
14719
|
type: "gadget_call",
|
|
@@ -14599,6 +14726,7 @@ var init_parser2 = __esm({
|
|
|
14599
14726
|
dependencies
|
|
14600
14727
|
}
|
|
14601
14728
|
};
|
|
14729
|
+
this.currentPartial = null;
|
|
14602
14730
|
startIndex = partEndIndex + endMarkerLength;
|
|
14603
14731
|
this.lastEmittedTextOffset = startIndex;
|
|
14604
14732
|
}
|
|
@@ -14607,6 +14735,117 @@ var init_parser2 = __esm({
|
|
|
14607
14735
|
this.lastEmittedTextOffset = 0;
|
|
14608
14736
|
}
|
|
14609
14737
|
}
|
|
14738
|
+
/** Create fresh partial-tracking state for a newly-started streaming gadget. */
|
|
14739
|
+
newPartialState(gadgetName, invocationId, dependencies) {
|
|
14740
|
+
return {
|
|
14741
|
+
gadgetName,
|
|
14742
|
+
invocationId,
|
|
14743
|
+
dependencies,
|
|
14744
|
+
emittedFieldLengths: /* @__PURE__ */ new Map(),
|
|
14745
|
+
completedFields: /* @__PURE__ */ new Set(),
|
|
14746
|
+
bodyScannedLen: 0,
|
|
14747
|
+
lastArgRelOffset: -1
|
|
14748
|
+
};
|
|
14749
|
+
}
|
|
14750
|
+
/**
|
|
14751
|
+
* Emit per-field "growing value" partials for an in-progress (or, when
|
|
14752
|
+
* `allComplete`, a just-completed) gadget body delimited by [bodyStart, bodyEnd).
|
|
14753
|
+
*
|
|
14754
|
+
* Incremental by design: each call resumes the `!!!ARG:` scan near where the last
|
|
14755
|
+
* one stopped (backing off by one marker's worth so a marker split across a chunk
|
|
14756
|
+
* boundary is still found) and only re-touches the in-progress field, so a long
|
|
14757
|
+
* streamed body costs O(new bytes) per feed instead of O(body). Every field except
|
|
14758
|
+
* the in-progress (last) one is complete — a following `!!!ARG:` terminated it; the
|
|
14759
|
+
* last field is tentative unless `allComplete`. The tentative field holds back any
|
|
14760
|
+
* suffix that is a partial prefix of a gadget marker so it never leaks into a value.
|
|
14761
|
+
*
|
|
14762
|
+
* We deliberately do NOT run stripMarkdownFences here: an unbalanced opening fence
|
|
14763
|
+
* sits before the first `!!!ARG:` (never emitted) and the authoritative gadget_call
|
|
14764
|
+
* still strips fences from the full raw parameters.
|
|
14765
|
+
*/
|
|
14766
|
+
*emitArgPartials(state, bodyStart, bodyEnd, allComplete) {
|
|
14767
|
+
const argLen = this.argPrefix.length;
|
|
14768
|
+
let searchAbs = bodyStart + Math.max(0, state.bodyScannedLen - (this.maxMarkerLength - 1));
|
|
14769
|
+
if (state.lastArgRelOffset >= 0) {
|
|
14770
|
+
searchAbs = Math.max(searchAbs, bodyStart + state.lastArgRelOffset + argLen);
|
|
14771
|
+
}
|
|
14772
|
+
while (true) {
|
|
14773
|
+
const argAbs = this.buffer.indexOf(this.argPrefix, searchAbs);
|
|
14774
|
+
if (argAbs === -1 || argAbs >= bodyEnd) break;
|
|
14775
|
+
if (state.lastArgRelOffset >= 0) {
|
|
14776
|
+
yield* this.emitFieldRange(state, bodyStart + state.lastArgRelOffset, argAbs, true);
|
|
14777
|
+
}
|
|
14778
|
+
state.lastArgRelOffset = argAbs - bodyStart;
|
|
14779
|
+
searchAbs = argAbs + argLen;
|
|
14780
|
+
}
|
|
14781
|
+
if (state.lastArgRelOffset >= 0) {
|
|
14782
|
+
yield* this.emitFieldRange(state, bodyStart + state.lastArgRelOffset, bodyEnd, allComplete);
|
|
14783
|
+
}
|
|
14784
|
+
state.bodyScannedLen = bodyEnd - bodyStart;
|
|
14785
|
+
}
|
|
14786
|
+
/**
|
|
14787
|
+
* Emit a single field whose `!!!ARG:` marker starts at `markerAbs` and whose value
|
|
14788
|
+
* runs to `valueEndAbs` (the next marker, or the body end). Mirrors the per-field
|
|
14789
|
+
* semantics of the old split-based emitter: field-path line, hold-back for a
|
|
14790
|
+
* tentative value, single trailing-newline strip.
|
|
14791
|
+
*/
|
|
14792
|
+
*emitFieldRange(state, markerAbs, valueEndAbs, complete2) {
|
|
14793
|
+
const pathStart = markerAbs + this.argPrefix.length;
|
|
14794
|
+
const newlineAbs = this.buffer.indexOf("\n", pathStart);
|
|
14795
|
+
if (newlineAbs === -1 || newlineAbs >= valueEndAbs) {
|
|
14796
|
+
if (!complete2) return;
|
|
14797
|
+
const pointer = this.buffer.substring(pathStart, valueEndAbs).trim();
|
|
14798
|
+
if (pointer) yield* this.emitFieldDelta(state, pointer, "", true);
|
|
14799
|
+
return;
|
|
14800
|
+
}
|
|
14801
|
+
const fieldPath = this.buffer.substring(pathStart, newlineAbs).trim();
|
|
14802
|
+
if (!fieldPath) return;
|
|
14803
|
+
let value = this.buffer.substring(newlineAbs + 1, valueEndAbs);
|
|
14804
|
+
if (!complete2) {
|
|
14805
|
+
const hold = this.trailingPartialMarkerLength(value);
|
|
14806
|
+
if (hold > 0) value = value.slice(0, value.length - hold);
|
|
14807
|
+
}
|
|
14808
|
+
if (value.endsWith("\n")) value = value.slice(0, -1);
|
|
14809
|
+
yield* this.emitFieldDelta(state, fieldPath, value, complete2);
|
|
14810
|
+
}
|
|
14811
|
+
/**
|
|
14812
|
+
* Emit a single partial for a field, but only when its value grew or it newly
|
|
14813
|
+
* completed — keeping event volume proportional to field growth, not characters.
|
|
14814
|
+
*/
|
|
14815
|
+
*emitFieldDelta(state, fieldPath, value, fieldComplete) {
|
|
14816
|
+
const previousLength = state.emittedFieldLengths.get(fieldPath) ?? -1;
|
|
14817
|
+
const grew = value.length > previousLength;
|
|
14818
|
+
const newlyComplete = fieldComplete && !state.completedFields.has(fieldPath);
|
|
14819
|
+
if (!grew && !newlyComplete) return;
|
|
14820
|
+
const delta = previousLength < 0 ? value : value.slice(Math.min(previousLength, value.length));
|
|
14821
|
+
state.emittedFieldLengths.set(fieldPath, value.length);
|
|
14822
|
+
if (fieldComplete) state.completedFields.add(fieldPath);
|
|
14823
|
+
yield {
|
|
14824
|
+
type: "gadget_args_partial",
|
|
14825
|
+
invocationId: state.invocationId,
|
|
14826
|
+
gadgetName: state.gadgetName,
|
|
14827
|
+
fieldPath,
|
|
14828
|
+
value,
|
|
14829
|
+
delta,
|
|
14830
|
+
isFieldComplete: fieldComplete
|
|
14831
|
+
};
|
|
14832
|
+
}
|
|
14833
|
+
/**
|
|
14834
|
+
* Length of the longest suffix of `value` that is a proper prefix of any gadget
|
|
14835
|
+
* marker (start/end/arg). Used to hold back the beginning of an incoming marker
|
|
14836
|
+
* so it never appears inside a streamed value.
|
|
14837
|
+
*/
|
|
14838
|
+
trailingPartialMarkerLength(value) {
|
|
14839
|
+
const markers = [this.startPrefix, this.endPrefix, this.argPrefix];
|
|
14840
|
+
const limit = Math.min(this.maxMarkerLength - 1, value.length);
|
|
14841
|
+
for (let len = limit; len >= 1; len--) {
|
|
14842
|
+
const suffix = value.slice(value.length - len);
|
|
14843
|
+
for (const marker of markers) {
|
|
14844
|
+
if (suffix.length < marker.length && marker.startsWith(suffix)) return len;
|
|
14845
|
+
}
|
|
14846
|
+
}
|
|
14847
|
+
return 0;
|
|
14848
|
+
}
|
|
14610
14849
|
// Finalize parsing and return remaining text or incomplete gadgets
|
|
14611
14850
|
*finalize() {
|
|
14612
14851
|
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastEmittedTextOffset);
|
|
@@ -14619,9 +14858,18 @@ var init_parser2 = __esm({
|
|
|
14619
14858
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
14620
14859
|
if (metadataEndIndex !== -1) {
|
|
14621
14860
|
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
14622
|
-
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
14861
|
+
const { gadgetName, invocationId, dependencies } = this.currentPartial ? this.currentPartial : this.parseInvocationMetadata(headerLine);
|
|
14623
14862
|
const contentStartIndex = metadataEndIndex + 1;
|
|
14624
|
-
const
|
|
14863
|
+
const contentRaw = this.buffer.substring(contentStartIndex);
|
|
14864
|
+
if (this.currentPartial) {
|
|
14865
|
+
yield* this.emitArgPartials(
|
|
14866
|
+
this.currentPartial,
|
|
14867
|
+
contentStartIndex,
|
|
14868
|
+
this.buffer.length,
|
|
14869
|
+
true
|
|
14870
|
+
);
|
|
14871
|
+
}
|
|
14872
|
+
const parametersRaw = contentRaw.trim();
|
|
14625
14873
|
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
14626
14874
|
yield {
|
|
14627
14875
|
type: "gadget_call",
|
|
@@ -14634,6 +14882,7 @@ var init_parser2 = __esm({
|
|
|
14634
14882
|
dependencies
|
|
14635
14883
|
}
|
|
14636
14884
|
};
|
|
14885
|
+
this.currentPartial = null;
|
|
14637
14886
|
return;
|
|
14638
14887
|
}
|
|
14639
14888
|
}
|
|
@@ -14646,6 +14895,7 @@ var init_parser2 = __esm({
|
|
|
14646
14895
|
reset() {
|
|
14647
14896
|
this.buffer = "";
|
|
14648
14897
|
this.lastEmittedTextOffset = 0;
|
|
14898
|
+
this.currentPartial = null;
|
|
14649
14899
|
}
|
|
14650
14900
|
};
|
|
14651
14901
|
}
|
|
@@ -15571,6 +15821,29 @@ async function notifyGadgetStart(ctx) {
|
|
|
15571
15821
|
await safeObserve(() => hookFn(context), ctx.logger);
|
|
15572
15822
|
}
|
|
15573
15823
|
}
|
|
15824
|
+
async function notifyGadgetArgsPartial(ctx) {
|
|
15825
|
+
if (!ctx.hooks?.onGadgetArgsPartial && !ctx.parentObservers?.onGadgetArgsPartial) return;
|
|
15826
|
+
const subagentContext = ctx.tree && ctx.parentNodeId ? getSubagentContextForNode(ctx.tree, ctx.parentNodeId) : void 0;
|
|
15827
|
+
const context = {
|
|
15828
|
+
iteration: ctx.iteration,
|
|
15829
|
+
invocationId: ctx.event.invocationId,
|
|
15830
|
+
gadgetName: ctx.event.gadgetName,
|
|
15831
|
+
fieldPath: ctx.event.fieldPath,
|
|
15832
|
+
value: ctx.event.value,
|
|
15833
|
+
delta: ctx.event.delta,
|
|
15834
|
+
isFieldComplete: ctx.event.isFieldComplete,
|
|
15835
|
+
logger: ctx.logger,
|
|
15836
|
+
subagentContext
|
|
15837
|
+
};
|
|
15838
|
+
if (ctx.hooks?.onGadgetArgsPartial) {
|
|
15839
|
+
const hookFn = ctx.hooks.onGadgetArgsPartial;
|
|
15840
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
15841
|
+
}
|
|
15842
|
+
if (ctx.parentObservers?.onGadgetArgsPartial) {
|
|
15843
|
+
const hookFn = ctx.parentObservers.onGadgetArgsPartial;
|
|
15844
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
15845
|
+
}
|
|
15846
|
+
}
|
|
15574
15847
|
async function notifyGadgetComplete(ctx) {
|
|
15575
15848
|
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
15576
15849
|
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
@@ -16379,6 +16652,7 @@ var init_stream_processor = __esm({
|
|
|
16379
16652
|
init_gadget_dispatcher();
|
|
16380
16653
|
init_gadget_hook_lifecycle();
|
|
16381
16654
|
init_gadget_limit_guard();
|
|
16655
|
+
init_observer_notifier();
|
|
16382
16656
|
init_safe_observe();
|
|
16383
16657
|
StreamProcessor = class {
|
|
16384
16658
|
iteration;
|
|
@@ -16387,6 +16661,10 @@ var init_stream_processor = __esm({
|
|
|
16387
16661
|
parser;
|
|
16388
16662
|
// Execution Tree context
|
|
16389
16663
|
tree;
|
|
16664
|
+
/** LLM-call node these gadgets hang off; used to derive subagentContext for partials. */
|
|
16665
|
+
parentNodeId;
|
|
16666
|
+
/** Parent agent observers (subagent visibility) — also notified for arg partials. */
|
|
16667
|
+
parentObservers;
|
|
16390
16668
|
responseText = "";
|
|
16391
16669
|
// Dependency resolution is delegated to GadgetDependencyResolver
|
|
16392
16670
|
dependencyResolver;
|
|
@@ -16400,6 +16678,8 @@ var init_stream_processor = __esm({
|
|
|
16400
16678
|
this.hooks = options.hooks ?? {};
|
|
16401
16679
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
16402
16680
|
this.tree = options.tree;
|
|
16681
|
+
this.parentNodeId = options.parentNodeId;
|
|
16682
|
+
this.parentObservers = options.parentObservers;
|
|
16403
16683
|
this.dependencyResolver = new GadgetDependencyResolver({
|
|
16404
16684
|
priorCompletedInvocations: options.priorCompletedInvocations,
|
|
16405
16685
|
priorFailedInvocations: options.priorFailedInvocations
|
|
@@ -16601,6 +16881,17 @@ var init_stream_processor = __esm({
|
|
|
16601
16881
|
for await (const e of this.dispatcher.dispatch(event.call)) {
|
|
16602
16882
|
yield e;
|
|
16603
16883
|
}
|
|
16884
|
+
} else if (event.type === "gadget_args_partial") {
|
|
16885
|
+
await notifyGadgetArgsPartial({
|
|
16886
|
+
tree: this.tree,
|
|
16887
|
+
hooks: this.hooks.observers,
|
|
16888
|
+
parentObservers: this.parentObservers,
|
|
16889
|
+
logger: this.logger,
|
|
16890
|
+
iteration: this.iteration,
|
|
16891
|
+
parentNodeId: this.parentNodeId,
|
|
16892
|
+
event
|
|
16893
|
+
});
|
|
16894
|
+
yield event;
|
|
16604
16895
|
} else {
|
|
16605
16896
|
yield event;
|
|
16606
16897
|
}
|
|
@@ -16772,7 +17063,7 @@ var init_stream_processor_factory = __esm({
|
|
|
16772
17063
|
|
|
16773
17064
|
// src/mcp/errors.ts
|
|
16774
17065
|
var McpError, McpUntrustedCommandError, McpConnectError, McpToolCallError, McpTimeoutError, JsonSchemaConversionError;
|
|
16775
|
-
var
|
|
17066
|
+
var init_errors2 = __esm({
|
|
16776
17067
|
"src/mcp/errors.ts"() {
|
|
16777
17068
|
"use strict";
|
|
16778
17069
|
McpError = class extends Error {
|
|
@@ -16858,7 +17149,7 @@ var init_allowlist = __esm({
|
|
|
16858
17149
|
"src/mcp/allowlist.ts"() {
|
|
16859
17150
|
"use strict";
|
|
16860
17151
|
import_node_path6 = __toESM(require("path"), 1);
|
|
16861
|
-
|
|
17152
|
+
init_errors2();
|
|
16862
17153
|
DEFAULT_MCP_COMMAND_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
16863
17154
|
"npx",
|
|
16864
17155
|
"node",
|
|
@@ -16896,7 +17187,7 @@ var init_client2 = __esm({
|
|
|
16896
17187
|
"src/mcp/client.ts"() {
|
|
16897
17188
|
"use strict";
|
|
16898
17189
|
init_allowlist();
|
|
16899
|
-
|
|
17190
|
+
init_errors2();
|
|
16900
17191
|
cachedSdk = null;
|
|
16901
17192
|
DEFAULT_CLIENT_INFO = { name: "llmist", version: "0.0.0" };
|
|
16902
17193
|
McpClient = class {
|
|
@@ -17478,7 +17769,7 @@ var init_json_schema_to_zod = __esm({
|
|
|
17478
17769
|
"src/mcp/json-schema-to-zod.ts"() {
|
|
17479
17770
|
"use strict";
|
|
17480
17771
|
import_zod4 = require("zod");
|
|
17481
|
-
|
|
17772
|
+
init_errors2();
|
|
17482
17773
|
}
|
|
17483
17774
|
});
|
|
17484
17775
|
|
|
@@ -18485,6 +18776,7 @@ __export(index_exports, {
|
|
|
18485
18776
|
DEFAULT_RATE_LIMIT_CONFIG: () => DEFAULT_RATE_LIMIT_CONFIG,
|
|
18486
18777
|
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
18487
18778
|
DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
|
|
18779
|
+
EmptyCompletionError: () => EmptyCompletionError,
|
|
18488
18780
|
ExecutionTree: () => ExecutionTree,
|
|
18489
18781
|
FALLBACK_CHARS_PER_TOKEN: () => FALLBACK_CHARS_PER_TOKEN,
|
|
18490
18782
|
GADGET_ARG_PREFIX: () => GADGET_ARG_PREFIX,
|
|
@@ -18770,19 +19062,7 @@ init_stream_processor();
|
|
|
18770
19062
|
// src/index.ts
|
|
18771
19063
|
init_client();
|
|
18772
19064
|
init_constants();
|
|
18773
|
-
|
|
18774
|
-
// src/core/errors.ts
|
|
18775
|
-
function isAbortError(error) {
|
|
18776
|
-
if (!(error instanceof Error)) return false;
|
|
18777
|
-
if (error.name === "AbortError") return true;
|
|
18778
|
-
if (error.name === "APIConnectionAbortedError") return true;
|
|
18779
|
-
if (error.name === "APIUserAbortError") return true;
|
|
18780
|
-
const message = error.message.toLowerCase();
|
|
18781
|
-
if (message.includes("abort")) return true;
|
|
18782
|
-
if (message.includes("cancelled")) return true;
|
|
18783
|
-
if (message.includes("canceled")) return true;
|
|
18784
|
-
return false;
|
|
18785
|
-
}
|
|
19065
|
+
init_errors();
|
|
18786
19066
|
|
|
18787
19067
|
// src/core/execution-events.ts
|
|
18788
19068
|
function isLLMEvent(event) {
|
|
@@ -18842,7 +19122,7 @@ init_typed_gadget();
|
|
|
18842
19122
|
// src/mcp/index.ts
|
|
18843
19123
|
init_allowlist();
|
|
18844
19124
|
init_client2();
|
|
18845
|
-
|
|
19125
|
+
init_errors2();
|
|
18846
19126
|
|
|
18847
19127
|
// src/mcp/gadget-exporter.ts
|
|
18848
19128
|
init_schema_to_json();
|
|
@@ -19492,6 +19772,7 @@ function getHostExports2(ctx) {
|
|
|
19492
19772
|
DEFAULT_RATE_LIMIT_CONFIG,
|
|
19493
19773
|
DEFAULT_RETRY_CONFIG,
|
|
19494
19774
|
DEFAULT_SUMMARIZATION_PROMPT,
|
|
19775
|
+
EmptyCompletionError,
|
|
19495
19776
|
ExecutionTree,
|
|
19496
19777
|
FALLBACK_CHARS_PER_TOKEN,
|
|
19497
19778
|
GADGET_ARG_PREFIX,
|