@mantyx/sdk 0.8.0 → 0.9.1
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/CHANGELOG.md +15 -1
- package/dist/a2a-server.cjs +13 -1
- package/dist/a2a-server.cjs.map +1 -1
- package/dist/a2a-server.d.cts +1 -1
- package/dist/a2a-server.d.ts +1 -1
- package/dist/a2a-server.js +1 -1
- package/dist/{chunk-T5SXWC6E.js → chunk-AE7ZSLBH.js} +120 -3
- package/dist/chunk-AE7ZSLBH.js.map +1 -0
- package/dist/{client-B3NEFlIU.d.cts → client-BB6cjfsz.d.cts} +194 -2
- package/dist/{client-B3NEFlIU.d.ts → client-BB6cjfsz.d.ts} +194 -2
- package/dist/index.cjs +120 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -4
- package/dist/index.d.ts +49 -4
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/agent-runs-protocol.md +271 -12
- package/package.json +1 -1
- package/dist/chunk-T5SXWC6E.js.map +0 -1
|
@@ -406,6 +406,44 @@ interface AgentSpecBase {
|
|
|
406
406
|
* ajv schema). See `docs/wire-protocol.md` §7.
|
|
407
407
|
*/
|
|
408
408
|
outputSchema?: OutputSchema;
|
|
409
|
+
/**
|
|
410
|
+
* Loop-detection guard. Tracks an order-invariant `(toolName, args)`
|
|
411
|
+
* signature for every assistant turn that emits one or more tool calls;
|
|
412
|
+
* when the same signature repeats consecutively the pipeline first injects
|
|
413
|
+
* a steering nudge ("either deliver a final answer or change strategy")
|
|
414
|
+
* and eventually forces a tools-disabled finalise turn.
|
|
415
|
+
*
|
|
416
|
+
* Pass an object to override the default thresholds, or `false` to
|
|
417
|
+
* explicitly disable the guard for this run / session. When omitted, the
|
|
418
|
+
* MANTYX runtime defaults apply (`{ consecutiveThreshold: 3,
|
|
419
|
+
* hardCutoffThreshold: 6 }`). See `docs/agent-runs-protocol.md` §4.6.
|
|
420
|
+
*
|
|
421
|
+
* Each intervention emits an observability-only `loop_detected` SSE event
|
|
422
|
+
* the SDK surfaces on the run-event stream (`tools` lists the looping
|
|
423
|
+
* batch; `hardCutoff: false` is the soft nudge round, `true` is the
|
|
424
|
+
* forced finalise). The synthetic skip + nudge are emitted on the normal
|
|
425
|
+
* `tool_result` / `assistant_delta` channels — the SDK does not need to
|
|
426
|
+
* act on the event itself.
|
|
427
|
+
*/
|
|
428
|
+
loopDetection?: LoopDetection | false;
|
|
429
|
+
/**
|
|
430
|
+
* Per-tool call caps enforced over the **lifetime of the run** (across
|
|
431
|
+
* every LLM turn). Calls under the cap run normally; calls past the cap
|
|
432
|
+
* are intercepted before execution and returned to the model as a
|
|
433
|
+
* synthetic "budget exceeded — pivot or finalize" tool result.
|
|
434
|
+
*
|
|
435
|
+
* Keys are the model-facing tool names (the same string on
|
|
436
|
+
* `local_tool_call.name`); values are `{ maxCalls: number }`. `maxCalls:
|
|
437
|
+
* 0` disables the tool entirely (the first attempt returns the synthetic
|
|
438
|
+
* body). Budgets are **per-tool, not pooled**.
|
|
439
|
+
*
|
|
440
|
+
* Pass `{}` to start from a clean slate (no defaults applied on top —
|
|
441
|
+
* useful for runs that intentionally want unbounded research). Omit
|
|
442
|
+
* entirely to keep the runtime defaults. Each interception emits an
|
|
443
|
+
* observability-only `tool_budget_exceeded` SSE event. See
|
|
444
|
+
* `docs/agent-runs-protocol.md` §4.7.
|
|
445
|
+
*/
|
|
446
|
+
toolBudgets?: ToolBudgets;
|
|
409
447
|
/**
|
|
410
448
|
* Flat string→string KV carried alongside the run / session for
|
|
411
449
|
* observability. Use it to tag runs with your own application identifiers
|
|
@@ -444,6 +482,50 @@ interface OutputSchema {
|
|
|
444
482
|
/** Required. JSON Schema describing the final assistant text. Root must be a JSON object. */
|
|
445
483
|
schema: Record<string, unknown>;
|
|
446
484
|
}
|
|
485
|
+
/**
|
|
486
|
+
* Loop-detection thresholds. See {@link AgentSpecBase.loopDetection} for the
|
|
487
|
+
* full semantics. Pass `false` (instead of an object) to disable the guard.
|
|
488
|
+
*
|
|
489
|
+
* Both fields are optional; omitted ones inherit the MANTYX runtime
|
|
490
|
+
* defaults (`consecutiveThreshold: 3`, `hardCutoffThreshold: 6`).
|
|
491
|
+
*/
|
|
492
|
+
interface LoopDetection {
|
|
493
|
+
/**
|
|
494
|
+
* Number of identical consecutive tool-call batches that triggers the
|
|
495
|
+
* **soft nudge** — the pipeline injects a steering message ("either
|
|
496
|
+
* deliver a final answer or change strategy"). Default `3`. Must be
|
|
497
|
+
* `>= 2` (one identical batch is just a single tool call, not a loop).
|
|
498
|
+
* Server-side upper bound: `100`.
|
|
499
|
+
*/
|
|
500
|
+
consecutiveThreshold?: number;
|
|
501
|
+
/**
|
|
502
|
+
* Number of identical consecutive tool-call batches that triggers the
|
|
503
|
+
* **hard cutoff** — the pipeline forces a tools-disabled finalise turn.
|
|
504
|
+
* Default `6`. Must be strictly greater than `consecutiveThreshold` (so
|
|
505
|
+
* the soft nudge has a chance to land). Server-side upper bound: `100`.
|
|
506
|
+
*/
|
|
507
|
+
hardCutoffThreshold?: number;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Per-tool call cap. See {@link AgentSpecBase.toolBudgets} for the full
|
|
511
|
+
* semantics.
|
|
512
|
+
*/
|
|
513
|
+
interface ToolBudget {
|
|
514
|
+
/**
|
|
515
|
+
* Hard cap on executed calls per run. `0` disables the tool entirely
|
|
516
|
+
* (every attempt returns the synthetic "budget exceeded" body on the
|
|
517
|
+
* first try). Server-side upper bound: `1000` (functionally unlimited;
|
|
518
|
+
* the in-runtime `maxToolTurns: 100` fires first).
|
|
519
|
+
*/
|
|
520
|
+
maxCalls: number;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Map of model-facing tool name → cap. See
|
|
524
|
+
* {@link AgentSpecBase.toolBudgets}. Pass an empty object (`{}`) to start
|
|
525
|
+
* from a clean slate (no runtime defaults applied on top); omit the field
|
|
526
|
+
* entirely to keep the defaults.
|
|
527
|
+
*/
|
|
528
|
+
type ToolBudgets = Record<string, ToolBudget>;
|
|
447
529
|
interface RunResult {
|
|
448
530
|
runId: string;
|
|
449
531
|
text: string;
|
|
@@ -463,7 +545,34 @@ interface ThinkingDeltaEvent extends RunEventBase {
|
|
|
463
545
|
}
|
|
464
546
|
interface AssistantMessageEvent extends RunEventBase {
|
|
465
547
|
type: "assistant_message";
|
|
548
|
+
/**
|
|
549
|
+
* Full assistant text for this turn (concatenation of every preceding
|
|
550
|
+
* `assistant_delta` for the turn, plus any non-streaming snapshot the
|
|
551
|
+
* engine appended at close). May be empty when the turn was tool-only.
|
|
552
|
+
*/
|
|
466
553
|
text: string;
|
|
554
|
+
/**
|
|
555
|
+
* 0-based tool-turn index this assistant message closes. Useful for
|
|
556
|
+
* SDK clients pairing the message with the subsequent `tool_result`
|
|
557
|
+
* rows.
|
|
558
|
+
*/
|
|
559
|
+
turn?: number;
|
|
560
|
+
/**
|
|
561
|
+
* Canonical lowercase stop reason normalized across providers
|
|
562
|
+
* (`"end_turn"`, `"tool_use"`, `"max_tokens"`, `"refusal"`,
|
|
563
|
+
* `"malformed_function_call"`, …). `null` / omitted when the provider
|
|
564
|
+
* did not report one.
|
|
565
|
+
*/
|
|
566
|
+
finishReason?: string | null;
|
|
567
|
+
/**
|
|
568
|
+
* Tool calls the model emitted on this turn. Omitted when the model
|
|
569
|
+
* did not call any tools.
|
|
570
|
+
*/
|
|
571
|
+
toolCalls?: Array<{
|
|
572
|
+
id: string;
|
|
573
|
+
name: string;
|
|
574
|
+
input: Record<string, unknown>;
|
|
575
|
+
}>;
|
|
467
576
|
}
|
|
468
577
|
interface ServerToolResultEvent extends RunEventBase {
|
|
469
578
|
type: "tool_result";
|
|
@@ -527,6 +636,43 @@ interface LocalToolResultInEvent extends RunEventBase {
|
|
|
527
636
|
result?: string;
|
|
528
637
|
error?: string;
|
|
529
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Observability event fired when the loop-detection guard intervenes.
|
|
641
|
+
* The synthetic skip + steering nudge are emitted on the normal
|
|
642
|
+
* `tool_result` / `assistant_delta` channels; this event lets the SDK
|
|
643
|
+
* render a status note (`looping — nudged` / `looping — gave up`).
|
|
644
|
+
*
|
|
645
|
+
* `hardCutoff: false` is the soft nudge round; `true` is the forced
|
|
646
|
+
* finalise. The same run may emit one of each.
|
|
647
|
+
*/
|
|
648
|
+
interface LoopDetectedEvent extends RunEventBase {
|
|
649
|
+
type: "loop_detected";
|
|
650
|
+
/** Length of the identical-batch streak that just tripped the threshold. */
|
|
651
|
+
consecutiveCount: number;
|
|
652
|
+
/** `false` for the soft nudge round; `true` once the pipeline forces finalisation. */
|
|
653
|
+
hardCutoff: boolean;
|
|
654
|
+
/** Names of the tool calls in the looping batch (no args). */
|
|
655
|
+
tools: string[];
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Observability event fired when a tool-budget interception happens. The
|
|
659
|
+
* synthetic "budget exceeded — pivot or finalize" tool result lands on the
|
|
660
|
+
* normal `tool_result` channel before this event fires; the SDK uses this
|
|
661
|
+
* event to render UI banners (`memory budget exhausted` etc.) without
|
|
662
|
+
* re-parsing tool-result bodies.
|
|
663
|
+
*/
|
|
664
|
+
interface ToolBudgetExceededEvent extends RunEventBase {
|
|
665
|
+
type: "tool_budget_exceeded";
|
|
666
|
+
/** Logical tool name (matches the key in `spec.toolBudgets`). */
|
|
667
|
+
tool: string;
|
|
668
|
+
/** Configured cap. */
|
|
669
|
+
maxCalls: number;
|
|
670
|
+
/**
|
|
671
|
+
* 1-based count of attempts to call this tool over the run lifetime.
|
|
672
|
+
* Always strictly greater than `maxCalls`.
|
|
673
|
+
*/
|
|
674
|
+
callIndex: number;
|
|
675
|
+
}
|
|
530
676
|
interface ResultEvent extends RunEventBase {
|
|
531
677
|
type: "result";
|
|
532
678
|
subtype: string;
|
|
@@ -535,14 +681,47 @@ interface ResultEvent extends RunEventBase {
|
|
|
535
681
|
}
|
|
536
682
|
interface ErrorEvent extends RunEventBase {
|
|
537
683
|
type: "error";
|
|
684
|
+
/** Human-readable failure message. */
|
|
538
685
|
error: string;
|
|
686
|
+
/**
|
|
687
|
+
* Legacy alias for {@link errorClass}. Equals `errorClass` when present;
|
|
688
|
+
* otherwise a small lowercase token (`"error"`, `"invalid_spec"`,
|
|
689
|
+
* `"worker_error"`, …).
|
|
690
|
+
*/
|
|
539
691
|
code?: string;
|
|
692
|
+
/**
|
|
693
|
+
* Canonical failure category. One of `"rate_limit"`, `"overloaded"`,
|
|
694
|
+
* `"server"`, `"context_window"`, `"truncation"`, `"invalid_request"`,
|
|
695
|
+
* `"auth"`, `"timeout"`, `"local_timeout"`, `"upstream_deadline"`,
|
|
696
|
+
* `"unknown"`. New categories may land additively. See
|
|
697
|
+
* `docs/agent-runs-protocol.md` §7 for the full list.
|
|
698
|
+
*/
|
|
699
|
+
errorClass?: string;
|
|
700
|
+
/**
|
|
701
|
+
* Canonical lowercase stop reason normalized across providers
|
|
702
|
+
* (`"max_tokens"`, `"refusal"`, `"malformed_function_call"`, …). When
|
|
703
|
+
* present, mirrors the value on the last `assistant_message` event.
|
|
704
|
+
*/
|
|
705
|
+
finishReason?: string | null;
|
|
706
|
+
/**
|
|
707
|
+
* **Best-effort raw bytes** the model emitted before the failure. For
|
|
708
|
+
* `outputSchema` runs this is likely **incomplete JSON** that will
|
|
709
|
+
* fail `JSON.parse` — see the wire-protocol truncation contract. Also
|
|
710
|
+
* persisted on `EphemeralAgentRun.finalText` so SDKs can recover it
|
|
711
|
+
* via `GET /agent-runs/:runId` after the SSE stream closes.
|
|
712
|
+
*/
|
|
713
|
+
partialText?: string;
|
|
714
|
+
/**
|
|
715
|
+
* Coarse retry hint inherited from the pipeline's error classifier.
|
|
716
|
+
* Informational; the SDK still owns the actual retry decision.
|
|
717
|
+
*/
|
|
718
|
+
retryable?: boolean;
|
|
540
719
|
}
|
|
541
720
|
interface CancelledEvent extends RunEventBase {
|
|
542
721
|
type: "cancelled";
|
|
543
722
|
reason?: string;
|
|
544
723
|
}
|
|
545
|
-
type RunEvent = AssistantDeltaEvent | ThinkingDeltaEvent | AssistantMessageEvent | ServerToolResultEvent | LocalToolCallEvent | LocalToolResultInEvent | ResultEvent | ErrorEvent | CancelledEvent | (RunEventBase & {
|
|
724
|
+
type RunEvent = AssistantDeltaEvent | ThinkingDeltaEvent | AssistantMessageEvent | ServerToolResultEvent | LocalToolCallEvent | LocalToolResultInEvent | LoopDetectedEvent | ToolBudgetExceededEvent | ResultEvent | ErrorEvent | CancelledEvent | (RunEventBase & {
|
|
546
725
|
type: string;
|
|
547
726
|
[key: string]: unknown;
|
|
548
727
|
});
|
|
@@ -634,12 +813,25 @@ declare class AgentSession {
|
|
|
634
813
|
* and does not mutate the session's stored value.
|
|
635
814
|
*/
|
|
636
815
|
outputSchema?: OutputSchema;
|
|
816
|
+
/**
|
|
817
|
+
* Per-message override for `loopDetection`. Applies only to this run
|
|
818
|
+
* and does not mutate the session's stored value. Pass `false` to
|
|
819
|
+
* disable the guard for this single turn.
|
|
820
|
+
*/
|
|
821
|
+
loopDetection?: LoopDetection | false;
|
|
822
|
+
/**
|
|
823
|
+
* Per-message override for `toolBudgets`. Applies only to this run
|
|
824
|
+
* and does not mutate the session's stored value.
|
|
825
|
+
*/
|
|
826
|
+
toolBudgets?: ToolBudgets;
|
|
637
827
|
}): Promise<RunResult>;
|
|
638
828
|
stream(prompt: string, opts?: {
|
|
639
829
|
signal?: AbortSignal;
|
|
640
830
|
metadata?: Record<string, string>;
|
|
641
831
|
reasoningLevel?: ReasoningLevel;
|
|
642
832
|
outputSchema?: OutputSchema;
|
|
833
|
+
loopDetection?: LoopDetection | false;
|
|
834
|
+
toolBudgets?: ToolBudgets;
|
|
643
835
|
}): AsyncGenerator<RunEvent, void, void>;
|
|
644
836
|
private buildSessionMessageBody;
|
|
645
837
|
history(): Promise<Array<{
|
|
@@ -690,4 +882,4 @@ interface LocalHandlers {
|
|
|
690
882
|
*/
|
|
691
883
|
declare function parseRunOutput<T = unknown>(result: RunResult, validator?: (value: unknown) => T): T;
|
|
692
884
|
|
|
693
|
-
export { type A2AToolRef as A, type
|
|
885
|
+
export { mantyxMcp as $, type A2AToolRef as A, type RunEventBase as B, type CancelledEvent as C, DEFAULT_BASE_URL as D, type ErrorEvent as E, type RunResult as F, type RunSpec as G, type SessionInfo as H, type SessionSpec as I, type ThinkingDeltaEvent as J, type ToolBudget as K, type LocalA2ATool as L, MantyxClient as M, type ToolBudgetExceededEvent as N, type OutputSchema as O, type ToolBudgets as P, defineLocalA2A as Q, type ReasoningLevel as R, type ServerToolResultEvent as S, type ToolRef as T, defineLocalMcp as U, defineLocalTool as V, isLocalA2ATool as W, isLocalMcpServer as X, isLocalTool as Y, type ZodLikeObject as Z, mantyxA2A as _, AgentSession as a, mantyxPluginTool as a0, mantyxTool as a1, parseRunOutput as a2, type AgentSpecBase as b, type AssistantDeltaEvent as c, type AssistantMessageEvent as d, type DefineLocalA2AOptions as e, type DefineLocalMcpOptions as f, type DefineLocalToolOptions as g, type LocalHandlers as h, type LocalMcpHttpTransport as i, type LocalMcpServer as j, type LocalMcpStdioTransport as k, type LocalTool as l, type LocalToolCallEvent as m, type LocalToolResultInEvent as n, type LoopDetectedEvent as o, type LoopDetection as p, type MantyxA2AOptions as q, type MantyxClientOptions as r, type MantyxMcpOptions as s, type MantyxPluginToolRef as t, type MantyxToolRef as u, type McpToolRef as v, type ModelCatalog as w, type ModelInfo as x, type ResultEvent as y, type RunEvent as z };
|
|
@@ -406,6 +406,44 @@ interface AgentSpecBase {
|
|
|
406
406
|
* ajv schema). See `docs/wire-protocol.md` §7.
|
|
407
407
|
*/
|
|
408
408
|
outputSchema?: OutputSchema;
|
|
409
|
+
/**
|
|
410
|
+
* Loop-detection guard. Tracks an order-invariant `(toolName, args)`
|
|
411
|
+
* signature for every assistant turn that emits one or more tool calls;
|
|
412
|
+
* when the same signature repeats consecutively the pipeline first injects
|
|
413
|
+
* a steering nudge ("either deliver a final answer or change strategy")
|
|
414
|
+
* and eventually forces a tools-disabled finalise turn.
|
|
415
|
+
*
|
|
416
|
+
* Pass an object to override the default thresholds, or `false` to
|
|
417
|
+
* explicitly disable the guard for this run / session. When omitted, the
|
|
418
|
+
* MANTYX runtime defaults apply (`{ consecutiveThreshold: 3,
|
|
419
|
+
* hardCutoffThreshold: 6 }`). See `docs/agent-runs-protocol.md` §4.6.
|
|
420
|
+
*
|
|
421
|
+
* Each intervention emits an observability-only `loop_detected` SSE event
|
|
422
|
+
* the SDK surfaces on the run-event stream (`tools` lists the looping
|
|
423
|
+
* batch; `hardCutoff: false` is the soft nudge round, `true` is the
|
|
424
|
+
* forced finalise). The synthetic skip + nudge are emitted on the normal
|
|
425
|
+
* `tool_result` / `assistant_delta` channels — the SDK does not need to
|
|
426
|
+
* act on the event itself.
|
|
427
|
+
*/
|
|
428
|
+
loopDetection?: LoopDetection | false;
|
|
429
|
+
/**
|
|
430
|
+
* Per-tool call caps enforced over the **lifetime of the run** (across
|
|
431
|
+
* every LLM turn). Calls under the cap run normally; calls past the cap
|
|
432
|
+
* are intercepted before execution and returned to the model as a
|
|
433
|
+
* synthetic "budget exceeded — pivot or finalize" tool result.
|
|
434
|
+
*
|
|
435
|
+
* Keys are the model-facing tool names (the same string on
|
|
436
|
+
* `local_tool_call.name`); values are `{ maxCalls: number }`. `maxCalls:
|
|
437
|
+
* 0` disables the tool entirely (the first attempt returns the synthetic
|
|
438
|
+
* body). Budgets are **per-tool, not pooled**.
|
|
439
|
+
*
|
|
440
|
+
* Pass `{}` to start from a clean slate (no defaults applied on top —
|
|
441
|
+
* useful for runs that intentionally want unbounded research). Omit
|
|
442
|
+
* entirely to keep the runtime defaults. Each interception emits an
|
|
443
|
+
* observability-only `tool_budget_exceeded` SSE event. See
|
|
444
|
+
* `docs/agent-runs-protocol.md` §4.7.
|
|
445
|
+
*/
|
|
446
|
+
toolBudgets?: ToolBudgets;
|
|
409
447
|
/**
|
|
410
448
|
* Flat string→string KV carried alongside the run / session for
|
|
411
449
|
* observability. Use it to tag runs with your own application identifiers
|
|
@@ -444,6 +482,50 @@ interface OutputSchema {
|
|
|
444
482
|
/** Required. JSON Schema describing the final assistant text. Root must be a JSON object. */
|
|
445
483
|
schema: Record<string, unknown>;
|
|
446
484
|
}
|
|
485
|
+
/**
|
|
486
|
+
* Loop-detection thresholds. See {@link AgentSpecBase.loopDetection} for the
|
|
487
|
+
* full semantics. Pass `false` (instead of an object) to disable the guard.
|
|
488
|
+
*
|
|
489
|
+
* Both fields are optional; omitted ones inherit the MANTYX runtime
|
|
490
|
+
* defaults (`consecutiveThreshold: 3`, `hardCutoffThreshold: 6`).
|
|
491
|
+
*/
|
|
492
|
+
interface LoopDetection {
|
|
493
|
+
/**
|
|
494
|
+
* Number of identical consecutive tool-call batches that triggers the
|
|
495
|
+
* **soft nudge** — the pipeline injects a steering message ("either
|
|
496
|
+
* deliver a final answer or change strategy"). Default `3`. Must be
|
|
497
|
+
* `>= 2` (one identical batch is just a single tool call, not a loop).
|
|
498
|
+
* Server-side upper bound: `100`.
|
|
499
|
+
*/
|
|
500
|
+
consecutiveThreshold?: number;
|
|
501
|
+
/**
|
|
502
|
+
* Number of identical consecutive tool-call batches that triggers the
|
|
503
|
+
* **hard cutoff** — the pipeline forces a tools-disabled finalise turn.
|
|
504
|
+
* Default `6`. Must be strictly greater than `consecutiveThreshold` (so
|
|
505
|
+
* the soft nudge has a chance to land). Server-side upper bound: `100`.
|
|
506
|
+
*/
|
|
507
|
+
hardCutoffThreshold?: number;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Per-tool call cap. See {@link AgentSpecBase.toolBudgets} for the full
|
|
511
|
+
* semantics.
|
|
512
|
+
*/
|
|
513
|
+
interface ToolBudget {
|
|
514
|
+
/**
|
|
515
|
+
* Hard cap on executed calls per run. `0` disables the tool entirely
|
|
516
|
+
* (every attempt returns the synthetic "budget exceeded" body on the
|
|
517
|
+
* first try). Server-side upper bound: `1000` (functionally unlimited;
|
|
518
|
+
* the in-runtime `maxToolTurns: 100` fires first).
|
|
519
|
+
*/
|
|
520
|
+
maxCalls: number;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Map of model-facing tool name → cap. See
|
|
524
|
+
* {@link AgentSpecBase.toolBudgets}. Pass an empty object (`{}`) to start
|
|
525
|
+
* from a clean slate (no runtime defaults applied on top); omit the field
|
|
526
|
+
* entirely to keep the defaults.
|
|
527
|
+
*/
|
|
528
|
+
type ToolBudgets = Record<string, ToolBudget>;
|
|
447
529
|
interface RunResult {
|
|
448
530
|
runId: string;
|
|
449
531
|
text: string;
|
|
@@ -463,7 +545,34 @@ interface ThinkingDeltaEvent extends RunEventBase {
|
|
|
463
545
|
}
|
|
464
546
|
interface AssistantMessageEvent extends RunEventBase {
|
|
465
547
|
type: "assistant_message";
|
|
548
|
+
/**
|
|
549
|
+
* Full assistant text for this turn (concatenation of every preceding
|
|
550
|
+
* `assistant_delta` for the turn, plus any non-streaming snapshot the
|
|
551
|
+
* engine appended at close). May be empty when the turn was tool-only.
|
|
552
|
+
*/
|
|
466
553
|
text: string;
|
|
554
|
+
/**
|
|
555
|
+
* 0-based tool-turn index this assistant message closes. Useful for
|
|
556
|
+
* SDK clients pairing the message with the subsequent `tool_result`
|
|
557
|
+
* rows.
|
|
558
|
+
*/
|
|
559
|
+
turn?: number;
|
|
560
|
+
/**
|
|
561
|
+
* Canonical lowercase stop reason normalized across providers
|
|
562
|
+
* (`"end_turn"`, `"tool_use"`, `"max_tokens"`, `"refusal"`,
|
|
563
|
+
* `"malformed_function_call"`, …). `null` / omitted when the provider
|
|
564
|
+
* did not report one.
|
|
565
|
+
*/
|
|
566
|
+
finishReason?: string | null;
|
|
567
|
+
/**
|
|
568
|
+
* Tool calls the model emitted on this turn. Omitted when the model
|
|
569
|
+
* did not call any tools.
|
|
570
|
+
*/
|
|
571
|
+
toolCalls?: Array<{
|
|
572
|
+
id: string;
|
|
573
|
+
name: string;
|
|
574
|
+
input: Record<string, unknown>;
|
|
575
|
+
}>;
|
|
467
576
|
}
|
|
468
577
|
interface ServerToolResultEvent extends RunEventBase {
|
|
469
578
|
type: "tool_result";
|
|
@@ -527,6 +636,43 @@ interface LocalToolResultInEvent extends RunEventBase {
|
|
|
527
636
|
result?: string;
|
|
528
637
|
error?: string;
|
|
529
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Observability event fired when the loop-detection guard intervenes.
|
|
641
|
+
* The synthetic skip + steering nudge are emitted on the normal
|
|
642
|
+
* `tool_result` / `assistant_delta` channels; this event lets the SDK
|
|
643
|
+
* render a status note (`looping — nudged` / `looping — gave up`).
|
|
644
|
+
*
|
|
645
|
+
* `hardCutoff: false` is the soft nudge round; `true` is the forced
|
|
646
|
+
* finalise. The same run may emit one of each.
|
|
647
|
+
*/
|
|
648
|
+
interface LoopDetectedEvent extends RunEventBase {
|
|
649
|
+
type: "loop_detected";
|
|
650
|
+
/** Length of the identical-batch streak that just tripped the threshold. */
|
|
651
|
+
consecutiveCount: number;
|
|
652
|
+
/** `false` for the soft nudge round; `true` once the pipeline forces finalisation. */
|
|
653
|
+
hardCutoff: boolean;
|
|
654
|
+
/** Names of the tool calls in the looping batch (no args). */
|
|
655
|
+
tools: string[];
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Observability event fired when a tool-budget interception happens. The
|
|
659
|
+
* synthetic "budget exceeded — pivot or finalize" tool result lands on the
|
|
660
|
+
* normal `tool_result` channel before this event fires; the SDK uses this
|
|
661
|
+
* event to render UI banners (`memory budget exhausted` etc.) without
|
|
662
|
+
* re-parsing tool-result bodies.
|
|
663
|
+
*/
|
|
664
|
+
interface ToolBudgetExceededEvent extends RunEventBase {
|
|
665
|
+
type: "tool_budget_exceeded";
|
|
666
|
+
/** Logical tool name (matches the key in `spec.toolBudgets`). */
|
|
667
|
+
tool: string;
|
|
668
|
+
/** Configured cap. */
|
|
669
|
+
maxCalls: number;
|
|
670
|
+
/**
|
|
671
|
+
* 1-based count of attempts to call this tool over the run lifetime.
|
|
672
|
+
* Always strictly greater than `maxCalls`.
|
|
673
|
+
*/
|
|
674
|
+
callIndex: number;
|
|
675
|
+
}
|
|
530
676
|
interface ResultEvent extends RunEventBase {
|
|
531
677
|
type: "result";
|
|
532
678
|
subtype: string;
|
|
@@ -535,14 +681,47 @@ interface ResultEvent extends RunEventBase {
|
|
|
535
681
|
}
|
|
536
682
|
interface ErrorEvent extends RunEventBase {
|
|
537
683
|
type: "error";
|
|
684
|
+
/** Human-readable failure message. */
|
|
538
685
|
error: string;
|
|
686
|
+
/**
|
|
687
|
+
* Legacy alias for {@link errorClass}. Equals `errorClass` when present;
|
|
688
|
+
* otherwise a small lowercase token (`"error"`, `"invalid_spec"`,
|
|
689
|
+
* `"worker_error"`, …).
|
|
690
|
+
*/
|
|
539
691
|
code?: string;
|
|
692
|
+
/**
|
|
693
|
+
* Canonical failure category. One of `"rate_limit"`, `"overloaded"`,
|
|
694
|
+
* `"server"`, `"context_window"`, `"truncation"`, `"invalid_request"`,
|
|
695
|
+
* `"auth"`, `"timeout"`, `"local_timeout"`, `"upstream_deadline"`,
|
|
696
|
+
* `"unknown"`. New categories may land additively. See
|
|
697
|
+
* `docs/agent-runs-protocol.md` §7 for the full list.
|
|
698
|
+
*/
|
|
699
|
+
errorClass?: string;
|
|
700
|
+
/**
|
|
701
|
+
* Canonical lowercase stop reason normalized across providers
|
|
702
|
+
* (`"max_tokens"`, `"refusal"`, `"malformed_function_call"`, …). When
|
|
703
|
+
* present, mirrors the value on the last `assistant_message` event.
|
|
704
|
+
*/
|
|
705
|
+
finishReason?: string | null;
|
|
706
|
+
/**
|
|
707
|
+
* **Best-effort raw bytes** the model emitted before the failure. For
|
|
708
|
+
* `outputSchema` runs this is likely **incomplete JSON** that will
|
|
709
|
+
* fail `JSON.parse` — see the wire-protocol truncation contract. Also
|
|
710
|
+
* persisted on `EphemeralAgentRun.finalText` so SDKs can recover it
|
|
711
|
+
* via `GET /agent-runs/:runId` after the SSE stream closes.
|
|
712
|
+
*/
|
|
713
|
+
partialText?: string;
|
|
714
|
+
/**
|
|
715
|
+
* Coarse retry hint inherited from the pipeline's error classifier.
|
|
716
|
+
* Informational; the SDK still owns the actual retry decision.
|
|
717
|
+
*/
|
|
718
|
+
retryable?: boolean;
|
|
540
719
|
}
|
|
541
720
|
interface CancelledEvent extends RunEventBase {
|
|
542
721
|
type: "cancelled";
|
|
543
722
|
reason?: string;
|
|
544
723
|
}
|
|
545
|
-
type RunEvent = AssistantDeltaEvent | ThinkingDeltaEvent | AssistantMessageEvent | ServerToolResultEvent | LocalToolCallEvent | LocalToolResultInEvent | ResultEvent | ErrorEvent | CancelledEvent | (RunEventBase & {
|
|
724
|
+
type RunEvent = AssistantDeltaEvent | ThinkingDeltaEvent | AssistantMessageEvent | ServerToolResultEvent | LocalToolCallEvent | LocalToolResultInEvent | LoopDetectedEvent | ToolBudgetExceededEvent | ResultEvent | ErrorEvent | CancelledEvent | (RunEventBase & {
|
|
546
725
|
type: string;
|
|
547
726
|
[key: string]: unknown;
|
|
548
727
|
});
|
|
@@ -634,12 +813,25 @@ declare class AgentSession {
|
|
|
634
813
|
* and does not mutate the session's stored value.
|
|
635
814
|
*/
|
|
636
815
|
outputSchema?: OutputSchema;
|
|
816
|
+
/**
|
|
817
|
+
* Per-message override for `loopDetection`. Applies only to this run
|
|
818
|
+
* and does not mutate the session's stored value. Pass `false` to
|
|
819
|
+
* disable the guard for this single turn.
|
|
820
|
+
*/
|
|
821
|
+
loopDetection?: LoopDetection | false;
|
|
822
|
+
/**
|
|
823
|
+
* Per-message override for `toolBudgets`. Applies only to this run
|
|
824
|
+
* and does not mutate the session's stored value.
|
|
825
|
+
*/
|
|
826
|
+
toolBudgets?: ToolBudgets;
|
|
637
827
|
}): Promise<RunResult>;
|
|
638
828
|
stream(prompt: string, opts?: {
|
|
639
829
|
signal?: AbortSignal;
|
|
640
830
|
metadata?: Record<string, string>;
|
|
641
831
|
reasoningLevel?: ReasoningLevel;
|
|
642
832
|
outputSchema?: OutputSchema;
|
|
833
|
+
loopDetection?: LoopDetection | false;
|
|
834
|
+
toolBudgets?: ToolBudgets;
|
|
643
835
|
}): AsyncGenerator<RunEvent, void, void>;
|
|
644
836
|
private buildSessionMessageBody;
|
|
645
837
|
history(): Promise<Array<{
|
|
@@ -690,4 +882,4 @@ interface LocalHandlers {
|
|
|
690
882
|
*/
|
|
691
883
|
declare function parseRunOutput<T = unknown>(result: RunResult, validator?: (value: unknown) => T): T;
|
|
692
884
|
|
|
693
|
-
export { type A2AToolRef as A, type
|
|
885
|
+
export { mantyxMcp as $, type A2AToolRef as A, type RunEventBase as B, type CancelledEvent as C, DEFAULT_BASE_URL as D, type ErrorEvent as E, type RunResult as F, type RunSpec as G, type SessionInfo as H, type SessionSpec as I, type ThinkingDeltaEvent as J, type ToolBudget as K, type LocalA2ATool as L, MantyxClient as M, type ToolBudgetExceededEvent as N, type OutputSchema as O, type ToolBudgets as P, defineLocalA2A as Q, type ReasoningLevel as R, type ServerToolResultEvent as S, type ToolRef as T, defineLocalMcp as U, defineLocalTool as V, isLocalA2ATool as W, isLocalMcpServer as X, isLocalTool as Y, type ZodLikeObject as Z, mantyxA2A as _, AgentSession as a, mantyxPluginTool as a0, mantyxTool as a1, parseRunOutput as a2, type AgentSpecBase as b, type AssistantDeltaEvent as c, type AssistantMessageEvent as d, type DefineLocalA2AOptions as e, type DefineLocalMcpOptions as f, type DefineLocalToolOptions as g, type LocalHandlers as h, type LocalMcpHttpTransport as i, type LocalMcpServer as j, type LocalMcpStdioTransport as k, type LocalTool as l, type LocalToolCallEvent as m, type LocalToolResultInEvent as n, type LoopDetectedEvent as o, type LoopDetection as p, type MantyxA2AOptions as q, type MantyxClientOptions as r, type MantyxMcpOptions as s, type MantyxPluginToolRef as t, type MantyxToolRef as u, type McpToolRef as v, type ModelCatalog as w, type ModelInfo as x, type ResultEvent as y, type RunEvent as z };
|
package/dist/index.cjs
CHANGED
|
@@ -98,11 +98,23 @@ var MantyxToolError = class extends MantyxError {
|
|
|
98
98
|
var MantyxRunError = class extends MantyxError {
|
|
99
99
|
runId;
|
|
100
100
|
subtype;
|
|
101
|
-
|
|
101
|
+
/** See {@link MantyxRunErrorInit.errorClass}. */
|
|
102
|
+
errorClass;
|
|
103
|
+
/** See {@link MantyxRunErrorInit.finishReason}. */
|
|
104
|
+
finishReason;
|
|
105
|
+
/** See {@link MantyxRunErrorInit.partialText}. */
|
|
106
|
+
partialText;
|
|
107
|
+
/** See {@link MantyxRunErrorInit.retryable}. */
|
|
108
|
+
retryable;
|
|
109
|
+
constructor(runId, subtype, message, init = {}) {
|
|
102
110
|
super(message, { code: subtype });
|
|
103
111
|
this.name = "MantyxRunError";
|
|
104
112
|
this.runId = runId;
|
|
105
113
|
this.subtype = subtype;
|
|
114
|
+
this.errorClass = init.errorClass;
|
|
115
|
+
this.finishReason = init.finishReason;
|
|
116
|
+
this.partialText = init.partialText;
|
|
117
|
+
this.retryable = init.retryable;
|
|
106
118
|
}
|
|
107
119
|
};
|
|
108
120
|
var MantyxParseError = class extends MantyxError {
|
|
@@ -758,7 +770,13 @@ var MantyxClient = class {
|
|
|
758
770
|
}
|
|
759
771
|
} else if (ev.type === "error") {
|
|
760
772
|
const e = ev;
|
|
761
|
-
|
|
773
|
+
const subtype = e.errorClass ?? e.code ?? "error";
|
|
774
|
+
throw new MantyxRunError(runId, subtype, e.error, {
|
|
775
|
+
...e.errorClass !== void 0 ? { errorClass: e.errorClass } : {},
|
|
776
|
+
...e.finishReason !== void 0 ? { finishReason: e.finishReason } : {},
|
|
777
|
+
...typeof e.partialText === "string" ? { partialText: e.partialText } : {},
|
|
778
|
+
...typeof e.retryable === "boolean" ? { retryable: e.retryable } : {}
|
|
779
|
+
});
|
|
762
780
|
} else if (ev.type === "cancelled") {
|
|
763
781
|
throw new MantyxRunError(runId, "cancelled", "Run was cancelled");
|
|
764
782
|
}
|
|
@@ -978,6 +996,12 @@ var AgentSession = class {
|
|
|
978
996
|
if (opts.outputSchema !== void 0) {
|
|
979
997
|
body.outputSchema = normalizeOutputSchema(opts.outputSchema);
|
|
980
998
|
}
|
|
999
|
+
if (opts.loopDetection !== void 0) {
|
|
1000
|
+
body.loopDetection = normalizeLoopDetection(opts.loopDetection);
|
|
1001
|
+
}
|
|
1002
|
+
if (opts.toolBudgets !== void 0) {
|
|
1003
|
+
body.toolBudgets = normalizeToolBudgets(opts.toolBudgets);
|
|
1004
|
+
}
|
|
981
1005
|
return body;
|
|
982
1006
|
}
|
|
983
1007
|
async history() {
|
|
@@ -1012,6 +1036,12 @@ function serializeAgentSpec(spec, extra = {}) {
|
|
|
1012
1036
|
if (spec.outputSchema !== void 0) {
|
|
1013
1037
|
body.outputSchema = normalizeOutputSchema(spec.outputSchema);
|
|
1014
1038
|
}
|
|
1039
|
+
if (spec.loopDetection !== void 0) {
|
|
1040
|
+
body.loopDetection = normalizeLoopDetection(spec.loopDetection);
|
|
1041
|
+
}
|
|
1042
|
+
if (spec.toolBudgets !== void 0) {
|
|
1043
|
+
body.toolBudgets = normalizeToolBudgets(spec.toolBudgets);
|
|
1044
|
+
}
|
|
1015
1045
|
if (spec.budgets) body.budgets = spec.budgets;
|
|
1016
1046
|
if (spec.metadata && Object.keys(spec.metadata).length > 0) body.metadata = spec.metadata;
|
|
1017
1047
|
if (extra.prompt !== void 0) body.prompt = extra.prompt;
|
|
@@ -1169,6 +1199,93 @@ function normalizeOutputSchema(value) {
|
|
|
1169
1199
|
}
|
|
1170
1200
|
return out;
|
|
1171
1201
|
}
|
|
1202
|
+
var LOOP_DETECTION_THRESHOLD_MAX = 100;
|
|
1203
|
+
function normalizeLoopDetection(value) {
|
|
1204
|
+
if (value === false) return false;
|
|
1205
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1206
|
+
throw new MantyxError(
|
|
1207
|
+
`loopDetection must be an object or the literal \`false\`, got ${JSON.stringify(value)}`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
const out = {};
|
|
1211
|
+
if (value.consecutiveThreshold !== void 0) {
|
|
1212
|
+
out.consecutiveThreshold = assertThreshold(
|
|
1213
|
+
"loopDetection.consecutiveThreshold",
|
|
1214
|
+
value.consecutiveThreshold,
|
|
1215
|
+
2
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
if (value.hardCutoffThreshold !== void 0) {
|
|
1219
|
+
out.hardCutoffThreshold = assertThreshold(
|
|
1220
|
+
"loopDetection.hardCutoffThreshold",
|
|
1221
|
+
value.hardCutoffThreshold,
|
|
1222
|
+
3
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
if (typeof out.consecutiveThreshold === "number" && typeof out.hardCutoffThreshold === "number" && out.hardCutoffThreshold <= out.consecutiveThreshold) {
|
|
1226
|
+
throw new MantyxError(
|
|
1227
|
+
`loopDetection.hardCutoffThreshold (${out.hardCutoffThreshold}) must be strictly greater than loopDetection.consecutiveThreshold (${out.consecutiveThreshold})`
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
return out;
|
|
1231
|
+
}
|
|
1232
|
+
function assertThreshold(label, value, min) {
|
|
1233
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value)) {
|
|
1234
|
+
throw new MantyxError(`${label} must be an integer, got ${JSON.stringify(value)}`);
|
|
1235
|
+
}
|
|
1236
|
+
if (value < min) {
|
|
1237
|
+
throw new MantyxError(`${label} must be >= ${min}, got ${value}`);
|
|
1238
|
+
}
|
|
1239
|
+
if (value > LOOP_DETECTION_THRESHOLD_MAX) {
|
|
1240
|
+
throw new MantyxError(
|
|
1241
|
+
`${label} must be <= ${LOOP_DETECTION_THRESHOLD_MAX} (server-enforced), got ${value}`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
return value;
|
|
1245
|
+
}
|
|
1246
|
+
var TOOL_BUDGETS_MAX_ENTRIES = 32;
|
|
1247
|
+
var TOOL_BUDGET_MAX_NAME_LEN = 120;
|
|
1248
|
+
var TOOL_BUDGET_MAX_CALLS = 1e3;
|
|
1249
|
+
function normalizeToolBudgets(value) {
|
|
1250
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1251
|
+
throw new MantyxError(
|
|
1252
|
+
`toolBudgets must be an object of shape { [name]: { maxCalls } }, got ${JSON.stringify(value)}`
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
const keys = Object.keys(value);
|
|
1256
|
+
if (keys.length > TOOL_BUDGETS_MAX_ENTRIES) {
|
|
1257
|
+
throw new MantyxError(
|
|
1258
|
+
`toolBudgets has ${keys.length} entries; the server enforces a ${TOOL_BUDGETS_MAX_ENTRIES}-entry limit`
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
const out = {};
|
|
1262
|
+
for (const key of keys) {
|
|
1263
|
+
if (typeof key !== "string" || key.length < 1 || key.length > TOOL_BUDGET_MAX_NAME_LEN) {
|
|
1264
|
+
throw new MantyxError(
|
|
1265
|
+
`toolBudgets keys must be 1..${TOOL_BUDGET_MAX_NAME_LEN}-char strings, got ${JSON.stringify(key)}`
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
const entry = value[key];
|
|
1269
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
1270
|
+
throw new MantyxError(
|
|
1271
|
+
`toolBudgets[${JSON.stringify(key)}] must be an object { maxCalls }, got ${JSON.stringify(entry)}`
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
const maxCalls = entry.maxCalls;
|
|
1275
|
+
if (typeof maxCalls !== "number" || !Number.isFinite(maxCalls) || !Number.isInteger(maxCalls) || maxCalls < 0) {
|
|
1276
|
+
throw new MantyxError(
|
|
1277
|
+
`toolBudgets[${JSON.stringify(key)}].maxCalls must be a non-negative integer, got ${JSON.stringify(maxCalls)}`
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
if (maxCalls > TOOL_BUDGET_MAX_CALLS) {
|
|
1281
|
+
throw new MantyxError(
|
|
1282
|
+
`toolBudgets[${JSON.stringify(key)}].maxCalls must be <= ${TOOL_BUDGET_MAX_CALLS} (server-enforced), got ${maxCalls}`
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
out[key] = { maxCalls };
|
|
1286
|
+
}
|
|
1287
|
+
return out;
|
|
1288
|
+
}
|
|
1172
1289
|
function parseRunOutput(result, validator) {
|
|
1173
1290
|
let parsed;
|
|
1174
1291
|
try {
|
|
@@ -1198,7 +1315,7 @@ function sleep(ms) {
|
|
|
1198
1315
|
}
|
|
1199
1316
|
|
|
1200
1317
|
// src/version.ts
|
|
1201
|
-
var SDK_VERSION = "0.
|
|
1318
|
+
var SDK_VERSION = "0.9.1";
|
|
1202
1319
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1203
1320
|
0 && (module.exports = {
|
|
1204
1321
|
AgentSession,
|