@mcp-abap-adt/llm-agent 11.1.2 → 12.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.
Files changed (98) hide show
  1. package/README.md +9 -2
  2. package/dist/adapters/cline-client-adapter.d.ts +18 -0
  3. package/dist/adapters/cline-client-adapter.d.ts.map +1 -0
  4. package/dist/adapters/cline-client-adapter.js +21 -0
  5. package/dist/adapters/cline-client-adapter.js.map +1 -0
  6. package/dist/api-adapters/anthropic-adapter.d.ts +10 -0
  7. package/dist/api-adapters/anthropic-adapter.d.ts.map +1 -0
  8. package/dist/api-adapters/anthropic-adapter.js +356 -0
  9. package/dist/api-adapters/anthropic-adapter.js.map +1 -0
  10. package/dist/api-adapters/index.d.ts +3 -0
  11. package/dist/api-adapters/index.d.ts.map +1 -0
  12. package/dist/api-adapters/index.js +3 -0
  13. package/dist/api-adapters/index.js.map +1 -0
  14. package/dist/api-adapters/openai-adapter.d.ts +10 -0
  15. package/dist/api-adapters/openai-adapter.d.ts.map +1 -0
  16. package/dist/api-adapters/openai-adapter.js +277 -0
  17. package/dist/api-adapters/openai-adapter.js.map +1 -0
  18. package/dist/cache/index.d.ts +4 -0
  19. package/dist/cache/index.d.ts.map +1 -0
  20. package/dist/cache/index.js +3 -0
  21. package/dist/cache/index.js.map +1 -0
  22. package/dist/cache/noop-tool-cache.d.ts +7 -0
  23. package/dist/cache/noop-tool-cache.d.ts.map +1 -0
  24. package/dist/cache/noop-tool-cache.js +8 -0
  25. package/dist/cache/noop-tool-cache.js.map +1 -0
  26. package/dist/cache/tool-cache.d.ts +14 -0
  27. package/dist/cache/tool-cache.d.ts.map +1 -0
  28. package/dist/cache/tool-cache.js +32 -0
  29. package/dist/cache/tool-cache.js.map +1 -0
  30. package/dist/cache/types.d.ts +7 -0
  31. package/dist/cache/types.d.ts.map +1 -0
  32. package/dist/cache/types.js +2 -0
  33. package/dist/cache/types.js.map +1 -0
  34. package/dist/external-tools-normalizer.d.ts +15 -0
  35. package/dist/external-tools-normalizer.d.ts.map +1 -0
  36. package/dist/external-tools-normalizer.js +146 -0
  37. package/dist/external-tools-normalizer.js.map +1 -0
  38. package/dist/index.d.ts +18 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +21 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/interfaces/api-adapter.d.ts +36 -0
  43. package/dist/interfaces/api-adapter.d.ts.map +1 -0
  44. package/dist/interfaces/api-adapter.js +9 -0
  45. package/dist/interfaces/api-adapter.js.map +1 -0
  46. package/dist/interfaces/client-adapter.d.ts +23 -0
  47. package/dist/interfaces/client-adapter.d.ts.map +1 -0
  48. package/dist/interfaces/client-adapter.js +10 -0
  49. package/dist/interfaces/client-adapter.js.map +1 -0
  50. package/dist/logger/types.d.ts +63 -0
  51. package/dist/logger/types.d.ts.map +1 -0
  52. package/dist/logger/types.js +2 -0
  53. package/dist/logger/types.js.map +1 -0
  54. package/dist/policy/fallback-llm-call-strategy.d.ts +16 -0
  55. package/dist/policy/fallback-llm-call-strategy.d.ts.map +1 -0
  56. package/dist/policy/fallback-llm-call-strategy.js +66 -0
  57. package/dist/policy/fallback-llm-call-strategy.js.map +1 -0
  58. package/dist/policy/index.d.ts +4 -0
  59. package/dist/policy/index.d.ts.map +1 -0
  60. package/dist/policy/index.js +4 -0
  61. package/dist/policy/index.js.map +1 -0
  62. package/dist/policy/non-streaming-llm-call-strategy.d.ts +6 -0
  63. package/dist/policy/non-streaming-llm-call-strategy.d.ts.map +1 -0
  64. package/dist/policy/non-streaming-llm-call-strategy.js +21 -0
  65. package/dist/policy/non-streaming-llm-call-strategy.js.map +1 -0
  66. package/dist/policy/streaming-llm-call-strategy.d.ts +6 -0
  67. package/dist/policy/streaming-llm-call-strategy.d.ts.map +1 -0
  68. package/dist/policy/streaming-llm-call-strategy.js +7 -0
  69. package/dist/policy/streaming-llm-call-strategy.js.map +1 -0
  70. package/dist/resilience/circuit-breaker-embedder.d.ts +16 -0
  71. package/dist/resilience/circuit-breaker-embedder.d.ts.map +1 -0
  72. package/dist/resilience/circuit-breaker-embedder.js +47 -0
  73. package/dist/resilience/circuit-breaker-embedder.js.map +1 -0
  74. package/dist/resilience/circuit-breaker-llm.d.ts +19 -0
  75. package/dist/resilience/circuit-breaker-llm.d.ts.map +1 -0
  76. package/dist/resilience/circuit-breaker-llm.js +72 -0
  77. package/dist/resilience/circuit-breaker-llm.js.map +1 -0
  78. package/dist/resilience/circuit-breaker.d.ts +36 -0
  79. package/dist/resilience/circuit-breaker.d.ts.map +1 -0
  80. package/dist/resilience/circuit-breaker.js +60 -0
  81. package/dist/resilience/circuit-breaker.js.map +1 -0
  82. package/dist/resilience/fallback-rag.d.ts +21 -0
  83. package/dist/resilience/fallback-rag.d.ts.map +1 -0
  84. package/dist/resilience/fallback-rag.js +80 -0
  85. package/dist/resilience/fallback-rag.js.map +1 -0
  86. package/dist/resilience/index.d.ts +5 -0
  87. package/dist/resilience/index.d.ts.map +1 -0
  88. package/dist/resilience/index.js +5 -0
  89. package/dist/resilience/index.js.map +1 -0
  90. package/dist/testing/index.d.ts +5 -2
  91. package/dist/testing/index.d.ts.map +1 -1
  92. package/dist/testing/index.js +30 -0
  93. package/dist/testing/index.js.map +1 -1
  94. package/dist/tool-call-deltas.d.ts +4 -0
  95. package/dist/tool-call-deltas.d.ts.map +1 -0
  96. package/dist/tool-call-deltas.js +25 -0
  97. package/dist/tool-call-deltas.js.map +1 -0
  98. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ import type { AgentCallOptions, LlmStreamChunk, Message, OrchestratorError, Result, SmartAgentResponse } from '@mcp-abap-adt/llm-agent';
2
+ export interface ApiSseEvent {
3
+ /**
4
+ * Optional SSE event name.
5
+ * - Anthropic: "message_start", "content_block_delta", ...
6
+ * - OpenAI: omitted (OpenAI SSE has no `event:` field)
7
+ */
8
+ event?: string;
9
+ /**
10
+ * Already-serialized SSE data payload.
11
+ * Consumer writes verbatim after `data: `.
12
+ */
13
+ data: string;
14
+ }
15
+ export interface ApiRequestContext {
16
+ readonly adapterName: string;
17
+ readonly protocol: Record<string, unknown>;
18
+ }
19
+ export interface NormalizedRequest {
20
+ messages: Message[];
21
+ stream: boolean;
22
+ options?: AgentCallOptions;
23
+ context: ApiRequestContext;
24
+ }
25
+ export interface ILlmApiAdapter {
26
+ readonly name: string;
27
+ normalizeRequest(request: unknown): NormalizedRequest;
28
+ transformStream(source: AsyncIterable<Result<LlmStreamChunk, OrchestratorError>>, context: ApiRequestContext): AsyncIterable<ApiSseEvent>;
29
+ formatResult(response: SmartAgentResponse, context: ApiRequestContext): unknown;
30
+ formatError?(error: OrchestratorError, context: ApiRequestContext): unknown;
31
+ }
32
+ export declare class AdapterValidationError extends Error {
33
+ readonly statusCode: number;
34
+ constructor(message: string, statusCode?: number);
35
+ }
36
+ //# sourceMappingURL=api-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-adapter.d.ts","sourceRoot":"","sources":["../../src/interfaces/api-adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,iBAAiB,EACjB,MAAM,EACN,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAAC;IAEtD,eAAe,CACb,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC,EAChE,OAAO,EAAE,iBAAiB,GACzB,aAAa,CAAC,WAAW,CAAC,CAAC;IAE9B,YAAY,CACV,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC;IAEX,WAAW,CAAC,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC;CAC7E;AAED,qBAAa,sBAAuB,SAAQ,KAAK;aAG7B,UAAU,EAAE,MAAM;gBADlC,OAAO,EAAE,MAAM,EACC,UAAU,GAAE,MAAY;CAK3C"}
@@ -0,0 +1,9 @@
1
+ export class AdapterValidationError extends Error {
2
+ statusCode;
3
+ constructor(message, statusCode = 400) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.name = 'AdapterValidationError';
7
+ }
8
+ }
9
+ //# sourceMappingURL=api-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-adapter.js","sourceRoot":"","sources":["../../src/interfaces/api-adapter.ts"],"names":[],"mappings":"AAuDA,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAG7B;IAFlB,YACE,OAAe,EACC,aAAqB,GAAG;QAExC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,eAAU,GAAV,UAAU,CAAc;QAGxC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Contract for adapting agent responses to a specific client format.
3
+ *
4
+ * Some clients (e.g. Cline) are prompt-based agents that expect tool
5
+ * calls formatted as XML inside the assistant `content` field rather
6
+ * than as native OpenAI `tool_calls`. A client adapter detects such
7
+ * clients and wraps the final response accordingly.
8
+ */
9
+ export interface IClientAdapter {
10
+ /** Human-readable name used in logs. */
11
+ readonly name: string;
12
+ /**
13
+ * Return `true` if this adapter should handle the given request.
14
+ * Called once per request with the client's system prompt (if any).
15
+ */
16
+ detect(systemPrompt: string): boolean;
17
+ /**
18
+ * Wrap the final assistant content before it is sent to the client.
19
+ * Only called when {@link detect} returned `true` for this request.
20
+ */
21
+ wrapResponse(content: string): string;
22
+ }
23
+ //# sourceMappingURL=client-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-adapter.d.ts","sourceRoot":"","sources":["../../src/interfaces/client-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAEtC;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACvC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Contract for adapting agent responses to a specific client format.
3
+ *
4
+ * Some clients (e.g. Cline) are prompt-based agents that expect tool
5
+ * calls formatted as XML inside the assistant `content` field rather
6
+ * than as native OpenAI `tool_calls`. A client adapter detects such
7
+ * clients and wraps the final response accordingly.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=client-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-adapter.js","sourceRoot":"","sources":["../../src/interfaces/client-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,63 @@
1
+ export interface ILogger {
2
+ log(event: LogEvent): void;
3
+ }
4
+ export type LogEvent = {
5
+ type: 'classify';
6
+ traceId: string;
7
+ inputLength: number;
8
+ subpromptCount: number;
9
+ durationMs: number;
10
+ } | {
11
+ type: 'rag_upsert';
12
+ traceId: string;
13
+ store: string;
14
+ durationMs: number;
15
+ } | {
16
+ type: 'rag_query';
17
+ traceId: string;
18
+ store: string;
19
+ k: number;
20
+ resultCount: number;
21
+ durationMs: number;
22
+ } | {
23
+ type: 'llm_call';
24
+ traceId: string;
25
+ iteration: number;
26
+ finishReason: string;
27
+ durationMs: number;
28
+ } | {
29
+ type: 'tool_call';
30
+ traceId: string;
31
+ toolName: string;
32
+ isError: boolean;
33
+ durationMs: number;
34
+ } | {
35
+ type: 'pipeline_done';
36
+ traceId: string;
37
+ stopReason: string;
38
+ iterations: number;
39
+ toolCallCount: number;
40
+ durationMs: number;
41
+ } | {
42
+ type: 'pipeline_error';
43
+ traceId: string;
44
+ code: string;
45
+ message: string;
46
+ durationMs: number;
47
+ } | {
48
+ type: 'tools_selected';
49
+ traceId: string;
50
+ total: number;
51
+ selected: number;
52
+ names: string[];
53
+ } | {
54
+ type: 'rag_translate';
55
+ traceId: string;
56
+ original: string;
57
+ translated: string;
58
+ } | {
59
+ type: 'warning';
60
+ traceId: string;
61
+ message: string;
62
+ };
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/logger/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,QAAQ,GAChB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1E;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/logger/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import type { CallOptions, ILlm, ILlmCallStrategy, LlmError, LlmStreamChunk, LlmTool, Message, Result } from '@mcp-abap-adt/llm-agent';
2
+ import type { ILogger } from '../logger/types.js';
3
+ /**
4
+ * Starts with streaming. On error, logs the cause and retries the same call
5
+ * via non-streaming. All subsequent calls use non-streaming for this instance.
6
+ */
7
+ export declare class FallbackLlmCallStrategy implements ILlmCallStrategy {
8
+ private readonly logger?;
9
+ private streamingDisabled;
10
+ private readonly streaming;
11
+ private readonly nonStreaming;
12
+ constructor(logger?: ILogger | undefined);
13
+ call(llm: ILlm, messages: Message[], tools: LlmTool[], options?: CallOptions): AsyncIterable<Result<LlmStreamChunk, LlmError>>;
14
+ private logFallback;
15
+ }
16
+ //# sourceMappingURL=fallback-llm-call-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-llm-call-strategy.d.ts","sourceRoot":"","sources":["../../src/policy/fallback-llm-call-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,IAAI,EACJ,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,OAAO,EACP,OAAO,EACP,MAAM,EACP,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAIlD;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAKlD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAJpC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqC;gBAErC,MAAM,CAAC,EAAE,OAAO,YAAA;IAEtC,IAAI,CACT,GAAG,EAAE,IAAI,EACT,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,OAAO,EAAE,EAChB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IA4ClD,OAAO,CAAC,WAAW;CAcpB"}
@@ -0,0 +1,66 @@
1
+ import { NonStreamingLlmCallStrategy } from './non-streaming-llm-call-strategy.js';
2
+ import { StreamingLlmCallStrategy } from './streaming-llm-call-strategy.js';
3
+ /**
4
+ * Starts with streaming. On error, logs the cause and retries the same call
5
+ * via non-streaming. All subsequent calls use non-streaming for this instance.
6
+ */
7
+ export class FallbackLlmCallStrategy {
8
+ logger;
9
+ streamingDisabled = false;
10
+ streaming = new StreamingLlmCallStrategy();
11
+ nonStreaming = new NonStreamingLlmCallStrategy();
12
+ constructor(logger) {
13
+ this.logger = logger;
14
+ }
15
+ async *call(llm, messages, tools, options) {
16
+ if (this.streamingDisabled) {
17
+ yield* this.nonStreaming.call(llm, messages, tools, options);
18
+ return;
19
+ }
20
+ try {
21
+ const chunks = [];
22
+ let hadError = false;
23
+ for await (const chunk of this.streaming.call(llm, messages, tools, options)) {
24
+ if (!chunk.ok) {
25
+ // Streaming returned a Result error — treat as streaming failure
26
+ hadError = true;
27
+ this.logFallback(chunk.error.message, chunk.error);
28
+ break;
29
+ }
30
+ chunks.push(chunk);
31
+ yield chunk;
32
+ }
33
+ if (hadError) {
34
+ this.streamingDisabled = true;
35
+ // Retry the same call non-streaming — chunks already yielded are
36
+ // partial content that the consumer may have streamed to the client.
37
+ // We yield a reset signal so the consumer can discard partial state.
38
+ yield { ok: true, value: { content: '', reset: true } };
39
+ yield* this.nonStreaming.call(llm, messages, tools, options);
40
+ }
41
+ }
42
+ catch (err) {
43
+ // Streaming threw an exception (e.g. SSE disconnect, network error)
44
+ const errMsg = err instanceof Error ? err.message : String(err);
45
+ this.logFallback(errMsg, err);
46
+ this.streamingDisabled = true;
47
+ yield { ok: true, value: { content: '', reset: true } };
48
+ yield* this.nonStreaming.call(llm, messages, tools, options);
49
+ }
50
+ }
51
+ logFallback(message, err) {
52
+ // biome-ignore lint/suspicious/noExplicitAny: ErrorWithCause shape
53
+ const cause = err?.cause;
54
+ const causeDetail = cause?.message || cause;
55
+ const causeCode = cause?.code;
56
+ const detail = causeDetail
57
+ ? ` (cause: ${causeDetail}${causeCode ? `, code: ${causeCode}` : ''})`
58
+ : '';
59
+ this.logger?.log({
60
+ type: 'warning',
61
+ traceId: 'tool-loop',
62
+ message: `Streaming failed, falling back to non-streaming: ${message}${detail}`,
63
+ });
64
+ }
65
+ }
66
+ //# sourceMappingURL=fallback-llm-call-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-llm-call-strategy.js","sourceRoot":"","sources":["../../src/policy/fallback-llm-call-strategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { FallbackLlmCallStrategy } from './fallback-llm-call-strategy.js';
2
+ export { NonStreamingLlmCallStrategy } from './non-streaming-llm-call-strategy.js';
3
+ export { StreamingLlmCallStrategy } from './streaming-llm-call-strategy.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { FallbackLlmCallStrategy } from './fallback-llm-call-strategy.js';
2
+ export { NonStreamingLlmCallStrategy } from './non-streaming-llm-call-strategy.js';
3
+ export { StreamingLlmCallStrategy } from './streaming-llm-call-strategy.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { CallOptions, ILlm, ILlmCallStrategy, LlmError, LlmStreamChunk, LlmTool, Message, Result } from '@mcp-abap-adt/llm-agent';
2
+ /** Always uses chat(). Result is yielded as a single chunk. Errors propagate as-is. */
3
+ export declare class NonStreamingLlmCallStrategy implements ILlmCallStrategy {
4
+ call(llm: ILlm, messages: Message[], tools: LlmTool[], options?: CallOptions): AsyncIterable<Result<LlmStreamChunk, LlmError>>;
5
+ }
6
+ //# sourceMappingURL=non-streaming-llm-call-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"non-streaming-llm-call-strategy.d.ts","sourceRoot":"","sources":["../../src/policy/non-streaming-llm-call-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,IAAI,EACJ,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,OAAO,EACP,OAAO,EACP,MAAM,EACP,MAAM,yBAAyB,CAAC;AAEjC,uFAAuF;AACvF,qBAAa,2BAA4B,YAAW,gBAAgB;IAC3D,IAAI,CACT,GAAG,EAAE,IAAI,EACT,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,OAAO,EAAE,EAChB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;CAmBnD"}
@@ -0,0 +1,21 @@
1
+ /** Always uses chat(). Result is yielded as a single chunk. Errors propagate as-is. */
2
+ export class NonStreamingLlmCallStrategy {
3
+ async *call(llm, messages, tools, options) {
4
+ const result = await llm.chat(messages, tools, options);
5
+ if (!result.ok) {
6
+ yield result;
7
+ return;
8
+ }
9
+ const response = result.value;
10
+ yield {
11
+ ok: true,
12
+ value: {
13
+ content: response.content,
14
+ finishReason: response.finishReason,
15
+ toolCalls: response.toolCalls,
16
+ usage: response.usage,
17
+ },
18
+ };
19
+ }
20
+ }
21
+ //# sourceMappingURL=non-streaming-llm-call-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"non-streaming-llm-call-strategy.js","sourceRoot":"","sources":["../../src/policy/non-streaming-llm-call-strategy.ts"],"names":[],"mappings":"AAWA,uFAAuF;AACvF,MAAM,OAAO,2BAA2B;IACtC,KAAK,CAAC,CAAC,IAAI,CACT,GAAS,EACT,QAAmB,EACnB,KAAgB,EAChB,OAAqB;QAErB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAExD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,MAAM,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;QAC9B,MAAM;YACJ,EAAE,EAAE,IAAI;YACR,KAAK,EAAE;gBACL,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,YAAY,EAAE,QAAQ,CAAC,YAAY;gBACnC,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,KAAK,EAAE,QAAQ,CAAC,KAAK;aACtB;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import type { CallOptions, ILlm, ILlmCallStrategy, LlmError, LlmStreamChunk, LlmTool, Message, Result } from '@mcp-abap-adt/llm-agent';
2
+ /** Always uses streamChat(). Errors propagate as-is. */
3
+ export declare class StreamingLlmCallStrategy implements ILlmCallStrategy {
4
+ call(llm: ILlm, messages: Message[], tools: LlmTool[], options?: CallOptions): AsyncIterable<Result<LlmStreamChunk, LlmError>>;
5
+ }
6
+ //# sourceMappingURL=streaming-llm-call-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-llm-call-strategy.d.ts","sourceRoot":"","sources":["../../src/policy/streaming-llm-call-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,IAAI,EACJ,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,OAAO,EACP,OAAO,EACP,MAAM,EACP,MAAM,yBAAyB,CAAC;AAEjC,wDAAwD;AACxD,qBAAa,wBAAyB,YAAW,gBAAgB;IACxD,IAAI,CACT,GAAG,EAAE,IAAI,EACT,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,OAAO,EAAE,EAChB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;CAGnD"}
@@ -0,0 +1,7 @@
1
+ /** Always uses streamChat(). Errors propagate as-is. */
2
+ export class StreamingLlmCallStrategy {
3
+ async *call(llm, messages, tools, options) {
4
+ yield* llm.streamChat(messages, tools, options);
5
+ }
6
+ }
7
+ //# sourceMappingURL=streaming-llm-call-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-llm-call-strategy.js","sourceRoot":"","sources":["../../src/policy/streaming-llm-call-strategy.ts"],"names":[],"mappings":"AAWA,wDAAwD;AACxD,MAAM,OAAO,wBAAwB;IACnC,KAAK,CAAC,CAAC,IAAI,CACT,GAAS,EACT,QAAmB,EACnB,KAAgB,EAChB,OAAqB;QAErB,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CircuitBreakerEmbedder — IEmbedder decorator that wraps calls with a CircuitBreaker.
3
+ *
4
+ * When the circuit is open, calls throw an error rather than hitting the
5
+ * underlying embedding service.
6
+ */
7
+ import type { CallOptions, IEmbedder, IEmbedResult } from '@mcp-abap-adt/llm-agent';
8
+ import type { CircuitBreaker } from './circuit-breaker.js';
9
+ export declare class CircuitBreakerEmbedder implements IEmbedder {
10
+ private readonly inner;
11
+ readonly breaker: CircuitBreaker;
12
+ constructor(inner: IEmbedder, breaker: CircuitBreaker);
13
+ embed(text: string, options?: CallOptions): Promise<IEmbedResult>;
14
+ embedBatch(texts: string[], options?: CallOptions): Promise<IEmbedResult[]>;
15
+ }
16
+ //# sourceMappingURL=circuit-breaker-embedder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker-embedder.d.ts","sourceRoot":"","sources":["../../src/resilience/circuit-breaker-embedder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,YAAY,EACb,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,qBAAa,sBAAuB,YAAW,SAAS;IAEpD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc;gBADf,KAAK,EAAE,SAAS,EACxB,OAAO,EAAE,cAAc;IAG5B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAcjE,UAAU,CACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,YAAY,EAAE,CAAC;CAmB3B"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * CircuitBreakerEmbedder — IEmbedder decorator that wraps calls with a CircuitBreaker.
3
+ *
4
+ * When the circuit is open, calls throw an error rather than hitting the
5
+ * underlying embedding service.
6
+ */
7
+ import { isBatchEmbedder, RagError } from '@mcp-abap-adt/llm-agent';
8
+ export class CircuitBreakerEmbedder {
9
+ inner;
10
+ breaker;
11
+ constructor(inner, breaker) {
12
+ this.inner = inner;
13
+ this.breaker = breaker;
14
+ }
15
+ async embed(text, options) {
16
+ if (!this.breaker.isCallPermitted) {
17
+ throw new RagError('Embedder circuit breaker is open', 'CIRCUIT_OPEN');
18
+ }
19
+ try {
20
+ const result = await this.inner.embed(text, options);
21
+ this.breaker.recordSuccess();
22
+ return result;
23
+ }
24
+ catch (err) {
25
+ this.breaker.recordFailure();
26
+ throw err;
27
+ }
28
+ }
29
+ async embedBatch(texts, options) {
30
+ if (!this.breaker.isCallPermitted) {
31
+ throw new RagError('Embedder circuit breaker is open', 'CIRCUIT_OPEN');
32
+ }
33
+ if (!isBatchEmbedder(this.inner)) {
34
+ throw new RagError('Inner embedder does not support batch embedding', 'EMBED_ERROR');
35
+ }
36
+ try {
37
+ const result = await this.inner.embedBatch(texts, options);
38
+ this.breaker.recordSuccess();
39
+ return result;
40
+ }
41
+ catch (err) {
42
+ this.breaker.recordFailure();
43
+ throw err;
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=circuit-breaker-embedder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker-embedder.js","sourceRoot":"","sources":["../../src/resilience/circuit-breaker-embedder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGpE,MAAM,OAAO,sBAAsB;IAEd;IACR;IAFX,YACmB,KAAgB,EACxB,OAAuB;QADf,UAAK,GAAL,KAAK,CAAW;QACxB,YAAO,GAAP,OAAO,CAAgB;IAC/B,CAAC;IAEJ,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,OAAqB;QAC7C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAClC,MAAM,IAAI,QAAQ,CAAC,kCAAkC,EAAE,cAAc,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACrD,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7B,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CACd,KAAe,EACf,OAAqB;QAErB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAClC,MAAM,IAAI,QAAQ,CAAC,kCAAkC,EAAE,cAAc,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,QAAQ,CAChB,iDAAiD,EACjD,aAAa,CACd,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7B,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * CircuitBreakerLlm — ILlm decorator that wraps calls with a CircuitBreaker.
3
+ *
4
+ * When the circuit is open, calls return `Result { ok: false, error: LlmError('CIRCUIT_OPEN') }`
5
+ * without hitting the underlying LLM.
6
+ */
7
+ import type { ILlm, Message } from '@mcp-abap-adt/llm-agent';
8
+ import { type CallOptions, LlmError, type LlmResponse, type LlmStreamChunk, type LlmTool, type Result } from '@mcp-abap-adt/llm-agent';
9
+ import type { CircuitBreaker } from './circuit-breaker.js';
10
+ export declare class CircuitBreakerLlm implements ILlm {
11
+ private readonly inner;
12
+ readonly breaker: CircuitBreaker;
13
+ healthCheck?: ILlm['healthCheck'];
14
+ constructor(inner: ILlm, breaker: CircuitBreaker);
15
+ get model(): string | undefined;
16
+ chat(messages: Message[], tools?: LlmTool[], options?: CallOptions): Promise<Result<LlmResponse, LlmError>>;
17
+ streamChat(messages: Message[], tools?: LlmTool[], options?: CallOptions): AsyncIterable<Result<LlmStreamChunk, LlmError>>;
18
+ }
19
+ //# sourceMappingURL=circuit-breaker-llm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker-llm.d.ts","sourceRoot":"","sources":["../../src/resilience/circuit-breaker-llm.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,KAAK,WAAW,EAChB,QAAQ,EACR,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,MAAM,EACZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,qBAAa,iBAAkB,YAAW,IAAI;IAI1C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc;IAJlC,WAAW,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;gBAGf,KAAK,EAAE,IAAI,EACnB,OAAO,EAAE,cAAc;IAOlC,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAEK,IAAI,CACR,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,CAAC,EAAE,OAAO,EAAE,EACjB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAgBlC,UAAU,CACf,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,CAAC,EAAE,OAAO,EAAE,EACjB,OAAO,CAAC,EAAE,WAAW,GACpB,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;CAoCnD"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * CircuitBreakerLlm — ILlm decorator that wraps calls with a CircuitBreaker.
3
+ *
4
+ * When the circuit is open, calls return `Result { ok: false, error: LlmError('CIRCUIT_OPEN') }`
5
+ * without hitting the underlying LLM.
6
+ */
7
+ import { LlmError, } from '@mcp-abap-adt/llm-agent';
8
+ export class CircuitBreakerLlm {
9
+ inner;
10
+ breaker;
11
+ healthCheck;
12
+ constructor(inner, breaker) {
13
+ this.inner = inner;
14
+ this.breaker = breaker;
15
+ if (inner.healthCheck) {
16
+ this.healthCheck = inner.healthCheck.bind(inner);
17
+ }
18
+ }
19
+ get model() {
20
+ return this.inner.model;
21
+ }
22
+ async chat(messages, tools, options) {
23
+ if (!this.breaker.isCallPermitted) {
24
+ return {
25
+ ok: false,
26
+ error: new LlmError('Circuit breaker is open', 'CIRCUIT_OPEN'),
27
+ };
28
+ }
29
+ const result = await this.inner.chat(messages, tools, options);
30
+ if (result.ok) {
31
+ this.breaker.recordSuccess();
32
+ }
33
+ else {
34
+ this.breaker.recordFailure();
35
+ }
36
+ return result;
37
+ }
38
+ async *streamChat(messages, tools, options) {
39
+ if (!this.breaker.isCallPermitted) {
40
+ yield {
41
+ ok: false,
42
+ error: new LlmError('Circuit breaker is open', 'CIRCUIT_OPEN'),
43
+ };
44
+ return;
45
+ }
46
+ let hadError = false;
47
+ try {
48
+ for await (const chunk of this.inner.streamChat(messages, tools, options)) {
49
+ if (!chunk.ok) {
50
+ hadError = true;
51
+ this.breaker.recordFailure();
52
+ yield chunk;
53
+ return;
54
+ }
55
+ yield chunk;
56
+ }
57
+ }
58
+ catch (err) {
59
+ hadError = true;
60
+ this.breaker.recordFailure();
61
+ yield {
62
+ ok: false,
63
+ error: new LlmError(String(err), 'LLM_ERROR'),
64
+ };
65
+ return;
66
+ }
67
+ if (!hadError) {
68
+ this.breaker.recordSuccess();
69
+ }
70
+ }
71
+ }
72
+ //# sourceMappingURL=circuit-breaker-llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker-llm.js","sourceRoot":"","sources":["../../src/resilience/circuit-breaker-llm.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAEL,QAAQ,GAKT,MAAM,yBAAyB,CAAC;AAGjC,MAAM,OAAO,iBAAiB;IAIT;IACR;IAJX,WAAW,CAAuB;IAElC,YACmB,KAAW,EACnB,OAAuB;QADf,UAAK,GAAL,KAAK,CAAM;QACnB,YAAO,GAAP,OAAO,CAAgB;QAEhC,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI,CACR,QAAmB,EACnB,KAAiB,EACjB,OAAqB;QAErB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAClC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,IAAI,QAAQ,CAAC,yBAAyB,EAAE,cAAc,CAAC;aAC/D,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC/B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,CAAC,UAAU,CACf,QAAmB,EACnB,KAAiB,EACjB,OAAqB;QAErB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAClC,MAAM;gBACJ,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,IAAI,QAAQ,CAAC,yBAAyB,EAAE,cAAc,CAAC;aAC/D,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC;YACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAC7C,QAAQ,EACR,KAAK,EACL,OAAO,CACR,EAAE,CAAC;gBACF,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;oBACd,QAAQ,GAAG,IAAI,CAAC;oBAChB,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;oBAC7B,MAAM,KAAK,CAAC;oBACZ,OAAO;gBACT,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7B,MAAM;gBACJ,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC;aAC9C,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Generic Circuit Breaker — three-state pattern (closed → open → half-open).
3
+ *
4
+ * - **Closed**: requests pass through; failures are counted.
5
+ * - **Open**: requests are rejected immediately; after `recoveryWindowMs` the
6
+ * breaker transitions to half-open.
7
+ * - **Half-Open**: a single probe request is allowed. If it succeeds the
8
+ * breaker closes; if it fails it re-opens.
9
+ */
10
+ export type CircuitState = 'closed' | 'open' | 'half-open';
11
+ export interface CircuitBreakerConfig {
12
+ /** Number of consecutive failures before opening. Default: 5 */
13
+ failureThreshold?: number;
14
+ /** Time (ms) to wait before probing again. Default: 30 000 */
15
+ recoveryWindowMs?: number;
16
+ /** Optional callback on state changes. */
17
+ onStateChange?: (from: CircuitState, to: CircuitState) => void;
18
+ }
19
+ export declare class CircuitBreaker {
20
+ private _state;
21
+ private failureCount;
22
+ private openedAt;
23
+ readonly failureThreshold: number;
24
+ readonly recoveryWindowMs: number;
25
+ private readonly onStateChange?;
26
+ constructor(config?: CircuitBreakerConfig);
27
+ get state(): CircuitState;
28
+ /** Record a successful call — resets the breaker to closed. */
29
+ recordSuccess(): void;
30
+ /** Record a failed call — may trip the breaker open. */
31
+ recordFailure(): void;
32
+ /** Whether the circuit allows requests through. */
33
+ get isCallPermitted(): boolean;
34
+ private _transition;
35
+ }
36
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/resilience/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,CAAC;CAChE;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAK;IAErB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAGrB;gBAEE,MAAM,GAAE,oBAAyB;IAM7C,IAAI,KAAK,IAAI,YAAY,CAQxB;IAED,+DAA+D;IAC/D,aAAa,IAAI,IAAI;IAOrB,wDAAwD;IACxD,aAAa,IAAI,IAAI;IAWrB,mDAAmD;IACnD,IAAI,eAAe,IAAI,OAAO,CAG7B;IAED,OAAO,CAAC,WAAW;CAOpB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Generic Circuit Breaker — three-state pattern (closed → open → half-open).
3
+ *
4
+ * - **Closed**: requests pass through; failures are counted.
5
+ * - **Open**: requests are rejected immediately; after `recoveryWindowMs` the
6
+ * breaker transitions to half-open.
7
+ * - **Half-Open**: a single probe request is allowed. If it succeeds the
8
+ * breaker closes; if it fails it re-opens.
9
+ */
10
+ export class CircuitBreaker {
11
+ _state = 'closed';
12
+ failureCount = 0;
13
+ openedAt = 0;
14
+ failureThreshold;
15
+ recoveryWindowMs;
16
+ onStateChange;
17
+ constructor(config = {}) {
18
+ this.failureThreshold = config.failureThreshold ?? 5;
19
+ this.recoveryWindowMs = config.recoveryWindowMs ?? 30_000;
20
+ this.onStateChange = config.onStateChange;
21
+ }
22
+ get state() {
23
+ if (this._state === 'open' &&
24
+ Date.now() - this.openedAt >= this.recoveryWindowMs) {
25
+ this._transition('half-open');
26
+ }
27
+ return this._state;
28
+ }
29
+ /** Record a successful call — resets the breaker to closed. */
30
+ recordSuccess() {
31
+ if (this._state !== 'closed') {
32
+ this._transition('closed');
33
+ }
34
+ this.failureCount = 0;
35
+ }
36
+ /** Record a failed call — may trip the breaker open. */
37
+ recordFailure() {
38
+ this.failureCount++;
39
+ if (this._state === 'half-open' ||
40
+ this.failureCount >= this.failureThreshold) {
41
+ this._transition('open');
42
+ this.openedAt = Date.now();
43
+ }
44
+ }
45
+ /** Whether the circuit allows requests through. */
46
+ get isCallPermitted() {
47
+ const s = this.state; // triggers half-open check
48
+ return s === 'closed' || s === 'half-open';
49
+ }
50
+ _transition(to) {
51
+ if (this._state === to)
52
+ return;
53
+ const from = this._state;
54
+ this._state = to;
55
+ if (to === 'closed')
56
+ this.failureCount = 0;
57
+ this.onStateChange?.(from, to);
58
+ }
59
+ }
60
+ //# sourceMappingURL=circuit-breaker.js.map