agentfootprint 11.4.0 → 11.5.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 (116) hide show
  1. package/CAPABILITIES.md +2 -1
  2. package/dist/adapters/mcp/agentcoreRuntime/invoke.js +7 -6
  3. package/dist/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
  4. package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js +36 -2
  5. package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
  6. package/dist/adapters/mcp/agentcoreRuntime/transport.js +3 -1
  7. package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  8. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.d.ts +7 -6
  9. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js +7 -6
  10. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
  11. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +32 -1
  12. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -1
  13. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
  14. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +4 -2
  15. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  16. package/dist/esm/events/payloads.d.ts +128 -27
  17. package/dist/esm/mcp/callLifetime.d.ts +57 -14
  18. package/dist/esm/mcp/callLifetime.js +67 -19
  19. package/dist/esm/mcp/callLifetime.js.map +1 -1
  20. package/dist/esm/mcp/callMarker.d.ts +43 -0
  21. package/dist/esm/mcp/callMarker.js +69 -0
  22. package/dist/esm/mcp/callMarker.js.map +1 -0
  23. package/dist/esm/mcp/connectionRefusals.js +7 -0
  24. package/dist/esm/mcp/connectionRefusals.js.map +1 -1
  25. package/dist/esm/mcp/index.d.ts +2 -0
  26. package/dist/esm/mcp/index.js +3 -0
  27. package/dist/esm/mcp/index.js.map +1 -1
  28. package/dist/esm/mcp/mcpClient.js +93 -26
  29. package/dist/esm/mcp/mcpClient.js.map +1 -1
  30. package/dist/esm/mcp/replay.d.ts +283 -0
  31. package/dist/esm/mcp/replay.js +401 -0
  32. package/dist/esm/mcp/replay.js.map +1 -0
  33. package/dist/esm/mcp/replayUnavailable.d.ts +55 -0
  34. package/dist/esm/mcp/replayUnavailable.js +74 -0
  35. package/dist/esm/mcp/replayUnavailable.js.map +1 -0
  36. package/dist/esm/mcp/requestLedger.d.ts +115 -32
  37. package/dist/esm/mcp/requestLedger.js +193 -52
  38. package/dist/esm/mcp/requestLedger.js.map +1 -1
  39. package/dist/esm/mcp/seriesEvidence.d.ts +73 -15
  40. package/dist/esm/mcp/seriesEvidence.js +114 -21
  41. package/dist/esm/mcp/seriesEvidence.js.map +1 -1
  42. package/dist/esm/mcp/throttleRetry.d.ts +86 -42
  43. package/dist/esm/mcp/throttleRetry.js +158 -50
  44. package/dist/esm/mcp/throttleRetry.js.map +1 -1
  45. package/dist/esm/mcp/toolResult.d.ts +6 -0
  46. package/dist/esm/mcp/toolResult.js +15 -0
  47. package/dist/esm/mcp/toolResult.js.map +1 -1
  48. package/dist/esm/mcp/types.d.ts +70 -8
  49. package/dist/esm/redaction/knownStrings.js +13 -0
  50. package/dist/esm/redaction/knownStrings.js.map +1 -1
  51. package/dist/esm/tool-providers/index.d.ts +2 -2
  52. package/dist/esm/tool-providers/index.js +4 -0
  53. package/dist/esm/tool-providers/index.js.map +1 -1
  54. package/dist/esm/tools/requestEvidence.js +60 -0
  55. package/dist/esm/tools/requestEvidence.js.map +1 -1
  56. package/dist/mcp/callLifetime.js +66 -18
  57. package/dist/mcp/callLifetime.js.map +1 -1
  58. package/dist/mcp/callMarker.js +74 -0
  59. package/dist/mcp/callMarker.js.map +1 -0
  60. package/dist/mcp/connectionRefusals.js +7 -0
  61. package/dist/mcp/connectionRefusals.js.map +1 -1
  62. package/dist/mcp/index.js +5 -1
  63. package/dist/mcp/index.js.map +1 -1
  64. package/dist/mcp/mcpClient.js +91 -24
  65. package/dist/mcp/mcpClient.js.map +1 -1
  66. package/dist/mcp/replay.js +411 -0
  67. package/dist/mcp/replay.js.map +1 -0
  68. package/dist/mcp/replayUnavailable.js +79 -0
  69. package/dist/mcp/replayUnavailable.js.map +1 -0
  70. package/dist/mcp/requestLedger.js +193 -52
  71. package/dist/mcp/requestLedger.js.map +1 -1
  72. package/dist/mcp/seriesEvidence.js +116 -22
  73. package/dist/mcp/seriesEvidence.js.map +1 -1
  74. package/dist/mcp/throttleRetry.js +160 -51
  75. package/dist/mcp/throttleRetry.js.map +1 -1
  76. package/dist/mcp/toolResult.js +17 -1
  77. package/dist/mcp/toolResult.js.map +1 -1
  78. package/dist/redaction/knownStrings.js +13 -0
  79. package/dist/redaction/knownStrings.js.map +1 -1
  80. package/dist/tool-providers/index.js +5 -1
  81. package/dist/tool-providers/index.js.map +1 -1
  82. package/dist/tools/requestEvidence.js +60 -0
  83. package/dist/tools/requestEvidence.js.map +1 -1
  84. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts +7 -6
  85. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts.map +1 -1
  86. package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +32 -1
  87. package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts.map +1 -1
  88. package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
  89. package/dist/types/events/payloads.d.ts +128 -27
  90. package/dist/types/events/payloads.d.ts.map +1 -1
  91. package/dist/types/mcp/callLifetime.d.ts +57 -14
  92. package/dist/types/mcp/callLifetime.d.ts.map +1 -1
  93. package/dist/types/mcp/callMarker.d.ts +44 -0
  94. package/dist/types/mcp/callMarker.d.ts.map +1 -0
  95. package/dist/types/mcp/index.d.ts +2 -0
  96. package/dist/types/mcp/index.d.ts.map +1 -1
  97. package/dist/types/mcp/mcpClient.d.ts.map +1 -1
  98. package/dist/types/mcp/replay.d.ts +284 -0
  99. package/dist/types/mcp/replay.d.ts.map +1 -0
  100. package/dist/types/mcp/replayUnavailable.d.ts +56 -0
  101. package/dist/types/mcp/replayUnavailable.d.ts.map +1 -0
  102. package/dist/types/mcp/requestLedger.d.ts +115 -32
  103. package/dist/types/mcp/requestLedger.d.ts.map +1 -1
  104. package/dist/types/mcp/seriesEvidence.d.ts +73 -15
  105. package/dist/types/mcp/seriesEvidence.d.ts.map +1 -1
  106. package/dist/types/mcp/throttleRetry.d.ts +86 -42
  107. package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
  108. package/dist/types/mcp/toolResult.d.ts +6 -0
  109. package/dist/types/mcp/toolResult.d.ts.map +1 -1
  110. package/dist/types/mcp/types.d.ts +70 -8
  111. package/dist/types/mcp/types.d.ts.map +1 -1
  112. package/dist/types/redaction/knownStrings.d.ts.map +1 -1
  113. package/dist/types/tool-providers/index.d.ts +2 -2
  114. package/dist/types/tool-providers/index.d.ts.map +1 -1
  115. package/dist/types/tools/requestEvidence.d.ts.map +1 -1
  116. package/package.json +1 -1
@@ -2270,17 +2270,22 @@ export interface ToolsCodeRunPayload {
2270
2270
  /**
2271
2271
  * What one attempt of an MCP request came to.
2272
2272
  *
2273
- * - `'answered'` — a response arrived and no retry rule refused it. It is
2274
- * delivered as it came, WHATEVER its status: a 503 is `answered` with
2273
+ * - `'answered'` — an HTTP response arrived and no retry rule refused it. It
2274
+ * is delivered as it came, WHATEVER its status: a 503 is `answered` with
2275
2275
  * `status: 503`. A response received is not an operation that succeeded,
2276
- * so nothing here is called success; read `status`.
2276
+ * so nothing here is called success; read `status`. Nor is it the MCP
2277
+ * call's answer: it records the HTTP exchange. A `202 Accepted`, or a
2278
+ * stream that closes before the call's result, is `answered`, and the
2279
+ * JSON-RPC answer may still never come — the end's `mayHaveRun` says when
2280
+ * the call failed that way.
2277
2281
  * - `'refused'` — a response the retry policy recognises as a refusal
2278
- * (`kind`): HTTP 429, or AgentCore Runtime's session-busy reply. The only
2279
- * outcome that is ever retried. It records the response the server sent,
2280
- * not proof that nothing ran: a 429 says the request was rate-limited,
2281
- * nothing more (RFC 6585 §4).
2282
+ * (`kind`): HTTP 429, or AgentCore Runtime's session-busy reply. It records
2283
+ * the response the server sent, not proof that nothing ran: a 429 says the
2284
+ * request was rate-limited, nothing more (RFC 6585 §4). `effect` says what
2285
+ * is known; `permission` says what allowed asking again.
2282
2286
  * - `'failed'` — the request threw before an answer arrived (a dropped
2283
- * connection, a DNS failure). Never retried: the server may have run it.
2287
+ * connection, a DNS failure). The server may have run it, so it is asked
2288
+ * again only when the app declared that for this tool (`replay`).
2284
2289
  * - `'timed-out'` — the call's deadline (`maxElapsedMs`) cut the request
2285
2290
  * while it was in flight.
2286
2291
  * - `'cut'` — the call was cancelled while the request was in flight: the
@@ -2288,7 +2293,7 @@ export interface ToolsCodeRunPayload {
2288
2293
  * or the call settling first. The request WAS sent; the server may already
2289
2294
  * have received it and run it. A cut is never a "not run".
2290
2295
  *
2291
- * The last three never had an answer, so they carry `remote: 'unknown'`.
2296
+ * The last three never had a response, so they carry `remote: 'unknown'`.
2292
2297
  *
2293
2298
  * Every request the series sent is a row, or is counted in `omitted`: the rows
2294
2299
  * and `omitted` together are the requests the series sent. A request that was
@@ -2305,7 +2310,7 @@ export type ToolRequestAttemptOutcome = 'answered' | 'refused' | 'failed' | 'tim
2305
2310
  export type ToolRequestOutcome = ToolRequestAttemptOutcome | 'cancelled';
2306
2311
  /**
2307
2312
  * Whether the server received and ran a request, when that is not known: the
2308
- * attempt ended without an answer (`failed`, `timed-out`, `cut`), so the
2313
+ * attempt ended without a response (`failed`, `timed-out`, `cut`), so the
2309
2314
  * request may have reached the server and run there, or not. The library never
2310
2315
  * records such a request as not run.
2311
2316
  */
@@ -2313,24 +2318,81 @@ export type ToolRequestRemote = 'unknown';
2313
2318
  /**
2314
2319
  * Why the request went on after an attempt, or ended with it.
2315
2320
  *
2316
- * - `'retryable'` — refused; another attempt follows after `waitMs`. The
2317
- * only reason that is not an end.
2321
+ * - `'retryable'` — another attempt follows after `waitMs`; `permission`
2322
+ * says what allowed it. The only reason that is not an end.
2318
2323
  * - `'answered'` — answered; the response is delivered.
2319
- * - `'exhausted'` — refused, and the request has made every attempt it may.
2320
- * - `'wait-limit'` — refused, and the wait would pass the call's total
2321
- * waiting budget (`maxWaitMs`). A stated `Retry-After` is never shortened
2322
- * to fit: the refusal is delivered instead.
2323
- * - `'no-time-left'` — refused, and the wait would pass the call's deadline;
2324
- * or the deadline cut a request or a wait.
2325
- * - `'not-replayable'` — refused, but the request's body cannot be sent twice.
2326
- * - `'not-retryable'` — the attempt failed in a way this retry never
2327
- * repeats: only a response the retry policy recognises as a refusal (a 429,
2328
- * a session-busy reply) is retried.
2324
+ * - `'exhausted'` — the request has made every attempt it may.
2325
+ * - `'wait-limit'` — the wait would pass the call's total waiting budget
2326
+ * (`maxWaitMs`). A stated `Retry-After` is never shortened to fit: the
2327
+ * refusal is delivered instead.
2328
+ * - `'no-time-left'` — the wait would pass the call's deadline; or the
2329
+ * deadline cut a request or a wait.
2330
+ * - `'not-replayable'` — the request's body cannot be sent twice.
2331
+ * - `'not-retryable'` — the attempt failed without an answer, and nothing
2332
+ * permits asking again after that failure: the app declared no repeat of
2333
+ * this tool on it (`replay`), or it is not a kind the series repeats.
2334
+ * - `'not-permitted'` — refused with a 429, and the policy in force does not
2335
+ * permit asking this tool again on it. Under `'app-authorized'`, only the
2336
+ * app's declaration does: a generic 429 does not prove the server ran
2337
+ * nothing.
2338
+ * - `'changes-without-key'` — the app declared this tool as making changes,
2339
+ * and the call carries no key the server is declared to deduplicate by. It
2340
+ * is never asked again automatically, under any policy.
2329
2341
  * - `'cancelled'` — the caller's signal ended the request.
2330
2342
  */
2331
- export type ToolRequestReason = 'retryable' | 'answered' | 'exhausted' | 'wait-limit' | 'no-time-left' | 'not-replayable' | 'not-retryable' | 'cancelled';
2343
+ export type ToolRequestReason = 'retryable' | 'answered' | 'exhausted' | 'wait-limit' | 'no-time-left' | 'not-replayable' | 'not-retryable' | 'not-permitted' | 'changes-without-key' | 'cancelled';
2332
2344
  /** The refusals a retry rule recognises: HTTP 429, and AgentCore Runtime's session-busy reply. */
2333
2345
  export type ToolRequestRefusalKind = 'throttle' | 'session-busy';
2346
+ /**
2347
+ * What is known about whether an attempt took effect on the server —
2348
+ * footprintjs's effect law (`AttemptEffect`), read for an MCP request:
2349
+ *
2350
+ * - `'none'` — it ran nothing, and that is established: the request was
2351
+ * never sent (an `end` whose `attempts` is `0`). No reply establishes it.
2352
+ * A 429 says the request was rate-limited, and AgentCore's session-busy
2353
+ * body is matched by its code and words — a server behind the runtime can
2354
+ * answer with the same error after it ran the call.
2355
+ * - `'unknown'` — anything sent that did not complete: a 429, a session-busy
2356
+ * reply, a lost connection or a timeout after the request was sent, a cut,
2357
+ * a 5xx, a JSON-RPC error, a tool error (one can follow a write that
2358
+ * succeeded).
2359
+ * - `'applied'` — the call completed: the server answered it, and not with
2360
+ * an error. It is never proof that anything was written.
2361
+ */
2362
+ export type ToolRequestEffect = 'none' | 'unknown' | 'applied';
2363
+ /** The failure kinds the app may declare a tool repeatable on (`replay.tools[name].on`). */
2364
+ export type ToolRequestReplayKind = 'throttle' | 'network';
2365
+ /**
2366
+ * What allowed the next attempt — present exactly when `reason` is
2367
+ * `'retryable'`. Permission and effect are separate facts: neither is ever
2368
+ * read from the other.
2369
+ *
2370
+ * - `by: 'declaration'` — the app's entry for this tool names the failure
2371
+ * kind (`on`).
2372
+ * - `by: 'adapter'` — the adapter the app chose retries this refusal as its
2373
+ * own policy: AgentCore Runtime's session-busy reply, which AWS documents
2374
+ * as a retryable conflict while a session starts or stops. It says what
2375
+ * it assumes in doing so (`assumes`).
2376
+ * - `by: 'compatibility'` — the compatibility policy, today's default: it
2377
+ * repeats a 429 for every tool, and says what it assumes in doing so
2378
+ * (`assumes`).
2379
+ *
2380
+ * An assumption is a policy, never evidence: the row's `effect` stays
2381
+ * `'unknown'`.
2382
+ */
2383
+ export interface ToolRequestPermission {
2384
+ readonly by: 'declaration' | 'adapter' | 'compatibility';
2385
+ /** `declaration` only — the failure kind the app's entry names. */
2386
+ readonly on?: ToolRequestReplayKind;
2387
+ /** `adapter` and `compatibility` only — what the policy assumes of the refusal it repeats. */
2388
+ readonly assumes?: 'refused-before-execution';
2389
+ /**
2390
+ * The app declared this tool as making changes, so it was asked again only
2391
+ * because the server is declared to deduplicate by a key this call carries.
2392
+ * The key protects only if the server uses it.
2393
+ */
2394
+ readonly deduplicated?: true;
2395
+ }
2334
2396
  /**
2335
2397
  * footprintjs's failure categories (`AttemptCategory`) — the one fixed set
2336
2398
  * every attempt record uses, so a saved request groups with every other.
@@ -2373,13 +2435,22 @@ export interface ToolRequestAttempt {
2373
2435
  /** `failed` and `timed-out` only. */
2374
2436
  readonly error?: ToolRequestError;
2375
2437
  /**
2376
- * `'unknown'` when the attempt ended without an answer (`failed`,
2438
+ * `'unknown'` when the attempt ended without a response (`failed`,
2377
2439
  * `timed-out`, `cut`): it was sent, and whether the server received and ran
2378
2440
  * it is not known. Absent when a response arrived: the record keeps what the
2379
2441
  * server answered (`status`) — for a `refused` attempt that is the server's
2380
2442
  * own refusal, not proof that nothing ran.
2381
2443
  */
2382
2444
  readonly remote?: ToolRequestRemote;
2445
+ /**
2446
+ * What is known about whether this attempt took effect (see
2447
+ * {@link ToolRequestEffect}). On every attempt the library files.
2448
+ */
2449
+ readonly effect?: ToolRequestEffect;
2450
+ /** What allowed the next attempt — present exactly when `reason` is `'retryable'`. */
2451
+ readonly permission?: ToolRequestPermission;
2452
+ /** How long the attempt took, in ms, from sending to its end. On every attempt the library files. */
2453
+ readonly durationMs?: number;
2383
2454
  }
2384
2455
  /** How an MCP request ended. */
2385
2456
  export interface ToolRequestEnd {
@@ -2406,17 +2477,47 @@ export interface ToolRequestEnd {
2406
2477
  /** `failed` and `timed-out` only. */
2407
2478
  readonly error?: ToolRequestError;
2408
2479
  /**
2409
- * `'unknown'` when the request ended on an attempt with no answer (`failed`,
2410
- * `timed-out`, `cut`): whether the server ran that request is not known.
2480
+ * `'unknown'` when the request ended on an attempt with no response
2481
+ * (`failed`, `timed-out`, `cut`): whether the server ran that request is not
2482
+ * known.
2411
2483
  */
2412
2484
  readonly remote?: ToolRequestRemote;
2485
+ /**
2486
+ * How many requests the series sent: `attempts.length + omitted`. On every
2487
+ * end the library files.
2488
+ */
2489
+ readonly attempts?: number;
2490
+ /**
2491
+ * What is known about the series' effect as a whole. Uncertainty survives:
2492
+ * once any attempt's effect is `'unknown'`, so is the series' — a later
2493
+ * proven refusal does not resolve it, and a later success does not prove the
2494
+ * operation happened only once. `'applied'` only when the call completed and
2495
+ * no attempt's effect is unknown; `'none'` only when every attempt is proven
2496
+ * to have run nothing (or none was sent). On every end the library files.
2497
+ */
2498
+ readonly effect?: ToolRequestEffect;
2499
+ /**
2500
+ * The call failed as "may have run" (footprintjs's `UnknownEffectError`, its
2501
+ * final error — a later refusal's, or the timeout — as the `cause`): under
2502
+ * the `'app-authorized'` policy, the call genuinely lacked an answer
2503
+ * somewhere and did not complete. Lacking an answer is not an unknown
2504
+ * effect: it is an attempt that got no response at all (`remote:
2505
+ * 'unknown'`), or a request accepted over HTTP (an `answered` 2xx) whose
2506
+ * JSON-RPC answer never came. A received refusal or error IS an answer —
2507
+ * its effect unknown, delivered as the server sent it — so a call whose
2508
+ * every attempt received one has no `mayHaveRun`. Absent under
2509
+ * `'compatibility'`, where the caller sees the error it always did, and
2510
+ * for a call its caller cancelled, which ends with the caller's reason.
2511
+ */
2512
+ readonly mayHaveRun?: true;
2413
2513
  }
2414
2514
  /**
2415
2515
  * What one MCP request's retry series did: every attempt it made, why it went
2416
2516
  * on or stopped after each, how long it waited, and how the request ended.
2417
2517
  *
2418
2518
  * Filed by `mcpClient`'s retry series for a call the agent dispatched, ONCE per
2419
- * request, when the request ends — and at the moment of a cancellation, so a
2519
+ * request, when the call settles — so the last attempt's `effect` can say
2520
+ * whether the call completed — and at the moment of a cancellation, so a
2420
2521
  * run cancelled during a wait still carries it when it is saved. A call that
2421
2522
  * settles before its series (the MCP SDK's own timeout, say) cancels the series
2422
2523
  * there and then, inside the call, so it is never filed after its run, and no
@@ -15,35 +15,78 @@
15
15
  * the call. The link is undone when the call settles: the SDK keeps its
16
16
  * abort listener on the signal it was given, and a link that outlived the
17
17
  * call would cancel a request that had already finished.
18
- * 2. ITS TICKET is issued, when the call carries the run's evidence port and
19
- * the connection has a ledger, keyed by the request's params object.
18
+ * 2. ITS MARKER is issued when the connection has a ledger, with its ticket
19
+ * when the call carries the run's evidence port: a library-private string
20
+ * in the request's `_meta` (`callMarker.ts`), which ties the request to
21
+ * the call even when the SDK copies the params, and which the transport
22
+ * takes out before anything is sent.
20
23
  * 3. ITS SETTLING ENDS ITS SERIES. However `callTool` settles — answered,
21
24
  * failed, or given up by the SDK (its own timeout) — a retry series still
22
25
  * sending the request is cut there and then, inside the call: it files
23
26
  * `cancelled` before the call's result is read, and sends nothing more.
27
+ * 4. ITS VERDICT REACHES ITS RECORD. A series that has ended waits for the
28
+ * call to settle before it files, because only the call can say whether
29
+ * the answer completed — a 200 can carry a tool error. The call leaves its
30
+ * verdict in the request's shared replay facts (`replay.ts` ·
31
+ * `RequestReplay`) before it settles the series' wait.
32
+ * 5. "MAY HAVE RUN" IS RAISED HERE — footprintjs's law, under the
33
+ * `'app-authorized'` policy (`replay.ts` · `RequestReplay`, its
34
+ * `uncertain`). A received refusal or error is preserved: a 429, a 503,
35
+ * a JSON-RPC or tool error — or a chain in which every attempt received
36
+ * one — keeps the server's error as it came, its effect still `unknown`.
37
+ * A call that genuinely lacked an answer somewhere — an attempt with no
38
+ * response at all, or a request the HTTP layer accepted whose JSON-RPC
39
+ * answer never came — and then fails, fails with footprintjs's
40
+ * `UnknownEffectError`, its final error (a later refusal's, or the
41
+ * timeout) as the `cause`: that error alone would read as "failed, try
42
+ * again". A call that completes returns normally. Under `'compatibility'`
43
+ * the caller sees the error it always did; the record still says the
44
+ * effect is unknown. A cancellation never raises it: a call its caller
45
+ * cancelled ends with the caller's reason, as it always did.
24
46
  *
25
- * In dev mode, a ticket that no request was bound to by the time the call
26
- * settled, when a `tools/call` request DID go out, is named once per connection: the SDK copied
27
- * the params, so the call's attempts could not be matched to it. Its
28
- * cancellation still reached the request (1, and the ledger's own watch on
29
- * `notifications/cancelled`).
47
+ * In dev mode, a ticket that no request was matched to by the time the call
48
+ * settled, when a `tools/call` request DID go out, is named once per
49
+ * connection: the SDK rebuilt the params without the marker, so the call's
50
+ * attempts could not be matched to it. Its cancellation still reached the
51
+ * request (1, and the ledger's own watch on `notifications/cancelled`). Under
52
+ * `'app-authorized'` such a request is never sent at all
53
+ * (`replayUnavailable.ts`).
30
54
  */
55
+ import type { CallVerdict } from './replay.js';
31
56
  import type { RequestLedger } from './requestLedger.js';
32
- /** A call that has been opened: the signal its SDK request runs under, and how to close it. */
33
- export interface OpenCall {
57
+ /** A call that has been opened: the params to send, the signal its SDK request runs under, and how to close it. */
58
+ export interface OpenCall<P extends object = object> {
59
+ /**
60
+ * What to hand the SDK: the call's params, with its private marker in
61
+ * `_meta` when a ledger watches the connection (`callMarker.ts`). The
62
+ * transport takes the marker out again: the server never sees it.
63
+ */
64
+ readonly params: P;
34
65
  /** What cancels the SDK request — absent when nothing can. */
35
66
  readonly signal?: AbortSignal;
36
- /** The call settled: undo the link, cut a series still sending, check the ticket was bound. */
37
- close(): void;
67
+ /**
68
+ * The call failed with `error` (`verdict` says how far it got): the error it
69
+ * fails with — `error` itself, or, under `'app-authorized'`, footprintjs's
70
+ * `UnknownEffectError` with `error` as its `cause` when the call genuinely
71
+ * lacked an answer somewhere (see 5 above).
72
+ */
73
+ failed(error: unknown, verdict: CallVerdict): unknown;
74
+ /**
75
+ * The call settled with `verdict`: leave it for the record, settle it in the
76
+ * ledger (its answer is no longer awaited), undo the link, cut a series
77
+ * still sending, check its request was matched.
78
+ */
79
+ close(verdict: CallVerdict): void;
38
80
  }
39
81
  /**
40
82
  * Open one call.
41
83
  *
42
- * @param params the request's params object — the SDK hands this very object to
43
- * the transport, which is how the ledger knows the request is this call's.
84
+ * @param params the request's params. The call sends them marked
85
+ * (`OpenCall.params`): the marker, not the object, is how the ledger knows
86
+ * the request is this call's — it survives an SDK that copies the params.
44
87
  * @param ctx the call's context: its signal, and the run's evidence port if any.
45
88
  * @param clientSignal the client's own `signal` (`McpClientOptions.signal`).
46
89
  * @param ledger the connection's ledger, when the library built its transport.
47
90
  * @param server the client's name, for the dev-mode note.
48
91
  */
49
- export declare function openCall(params: object, ctx: unknown, clientSignal: AbortSignal | undefined, ledger: RequestLedger | undefined, server: string): OpenCall;
92
+ export declare function openCall<P extends object>(params: P, ctx: unknown, clientSignal: AbortSignal | undefined, ledger: RequestLedger | undefined, server: string): OpenCall<P>;
@@ -15,27 +15,53 @@
15
15
  * the call. The link is undone when the call settles: the SDK keeps its
16
16
  * abort listener on the signal it was given, and a link that outlived the
17
17
  * call would cancel a request that had already finished.
18
- * 2. ITS TICKET is issued, when the call carries the run's evidence port and
19
- * the connection has a ledger, keyed by the request's params object.
18
+ * 2. ITS MARKER is issued when the connection has a ledger, with its ticket
19
+ * when the call carries the run's evidence port: a library-private string
20
+ * in the request's `_meta` (`callMarker.ts`), which ties the request to
21
+ * the call even when the SDK copies the params, and which the transport
22
+ * takes out before anything is sent.
20
23
  * 3. ITS SETTLING ENDS ITS SERIES. However `callTool` settles — answered,
21
24
  * failed, or given up by the SDK (its own timeout) — a retry series still
22
25
  * sending the request is cut there and then, inside the call: it files
23
26
  * `cancelled` before the call's result is read, and sends nothing more.
27
+ * 4. ITS VERDICT REACHES ITS RECORD. A series that has ended waits for the
28
+ * call to settle before it files, because only the call can say whether
29
+ * the answer completed — a 200 can carry a tool error. The call leaves its
30
+ * verdict in the request's shared replay facts (`replay.ts` ·
31
+ * `RequestReplay`) before it settles the series' wait.
32
+ * 5. "MAY HAVE RUN" IS RAISED HERE — footprintjs's law, under the
33
+ * `'app-authorized'` policy (`replay.ts` · `RequestReplay`, its
34
+ * `uncertain`). A received refusal or error is preserved: a 429, a 503,
35
+ * a JSON-RPC or tool error — or a chain in which every attempt received
36
+ * one — keeps the server's error as it came, its effect still `unknown`.
37
+ * A call that genuinely lacked an answer somewhere — an attempt with no
38
+ * response at all, or a request the HTTP layer accepted whose JSON-RPC
39
+ * answer never came — and then fails, fails with footprintjs's
40
+ * `UnknownEffectError`, its final error (a later refusal's, or the
41
+ * timeout) as the `cause`: that error alone would read as "failed, try
42
+ * again". A call that completes returns normally. Under `'compatibility'`
43
+ * the caller sees the error it always did; the record still says the
44
+ * effect is unknown. A cancellation never raises it: a call its caller
45
+ * cancelled ends with the caller's reason, as it always did.
24
46
  *
25
- * In dev mode, a ticket that no request was bound to by the time the call
26
- * settled, when a `tools/call` request DID go out, is named once per connection: the SDK copied
27
- * the params, so the call's attempts could not be matched to it. Its
28
- * cancellation still reached the request (1, and the ledger's own watch on
29
- * `notifications/cancelled`).
47
+ * In dev mode, a ticket that no request was matched to by the time the call
48
+ * settled, when a `tools/call` request DID go out, is named once per
49
+ * connection: the SDK rebuilt the params without the marker, so the call's
50
+ * attempts could not be matched to it. Its cancellation still reached the
51
+ * request (1, and the ledger's own watch on `notifications/cancelled`). Under
52
+ * `'app-authorized'` such a request is never sent at all
53
+ * (`replayUnavailable.ts`).
30
54
  */
31
- import { isDevMode } from 'footprintjs';
55
+ import { isDevMode, UnknownEffectError } from 'footprintjs';
32
56
  import { eitherSignal, linkedController } from '../lib/abort.js';
33
57
  import { requestEvidenceOf } from '../tools/requestEvidence.js';
58
+ import { markParams } from './callMarker.js';
34
59
  /**
35
60
  * Open one call.
36
61
  *
37
- * @param params the request's params object — the SDK hands this very object to
38
- * the transport, which is how the ledger knows the request is this call's.
62
+ * @param params the request's params. The call sends them marked
63
+ * (`OpenCall.params`): the marker, not the object, is how the ledger knows
64
+ * the request is this call's — it survives an SDK that copies the params.
39
65
  * @param ctx the call's context: its signal, and the run's evidence port if any.
40
66
  * @param clientSignal the client's own `signal` (`McpClientOptions.signal`).
41
67
  * @param ledger the connection's ledger, when the library built its transport.
@@ -51,15 +77,37 @@ export function openCall(params, ctx, clientSignal, ledger, server) {
51
77
  ? undefined
52
78
  : { sink, signal: eitherSignal(signal, settled?.signal) };
53
79
  const sentBefore = ledger?.toolCallsSent ?? 0;
54
- if (ticket !== undefined)
55
- ledger?.issue(params, ticket);
80
+ // Every call on a watched connection gets a marker, ticket or not: under
81
+ // 'app-authorized' its request must be matched to it either way. The call
82
+ // holds its entry, so what it reads survives the connection closing.
83
+ const issued = ledger?.issue(ticket);
84
+ let mayHaveRun = false;
56
85
  return {
86
+ params: issued === undefined ? params : markParams(params, issued.marker),
57
87
  ...(signal !== undefined && { signal }),
58
- close: () => {
88
+ failed: (error, verdict) => {
89
+ // A call its caller cancelled ends with the caller's reason, whatever its
90
+ // series did; the record still says what is known of its effect.
91
+ if (verdict === 'completed' || signal?.aborted === true)
92
+ return error;
93
+ const replay = issued?.request?.replay;
94
+ if (replay === undefined || !replay.uncertain)
95
+ return error;
96
+ mayHaveRun = true;
97
+ // The key, when one was sent, is an argument VALUE: it stays with the
98
+ // caller, who sent it, and never rides an error a run may record.
99
+ return new UnknownEffectError({ system: server, attempts: replay.sent, cause: error });
100
+ },
101
+ close: (verdict) => {
102
+ // The verdict first: settling the series' wait files its record.
103
+ issued?.request?.replay.settle(verdict, mayHaveRun);
104
+ // By its own entry, never the params object: a copy cannot keep a watch alive.
105
+ if (issued !== undefined)
106
+ ledger?.settle(issued);
59
107
  settled?.abort(callSettled());
60
108
  link?.unlink();
61
109
  if (ticket !== undefined && ledger !== undefined) {
62
- noteUnbound(ledger, ticket, sentBefore, server);
110
+ noteUnbound(ledger, issued?.request !== undefined, sentBefore, server);
63
111
  }
64
112
  },
65
113
  };
@@ -85,15 +133,15 @@ function callSettled() {
85
133
  });
86
134
  }
87
135
  /** Dev mode, once per connection: a request went out for this call, but the ledger never matched it. */
88
- function noteUnbound(ledger, ticket, sentBefore, server) {
89
- if (!isDevMode() || ledger.wasBound(ticket) || ledger.toolCallsSent <= sentBefore)
136
+ function noteUnbound(ledger, matched, sentBefore, server) {
137
+ if (!isDevMode() || matched || ledger.toolCallsSent <= sentBefore)
90
138
  return;
91
139
  if (!ledger.firstUnbound())
92
140
  return;
93
141
  // eslint-disable-next-line no-console
94
142
  console.warn(`[agentfootprint] mcpClient[${server}]: a tool call's request could not be matched to ` +
95
- 'the call, so its retry attempts were not recorded in the run. The MCP SDK copied the ' +
96
- "request's params before sending them. The call itself is unaffected, and its " +
97
- 'cancellation still reached the request.');
143
+ 'the call, so its retry attempts were not recorded in the run. The MCP SDK sent the ' +
144
+ "request without the call's `_meta` marker: it rebuilt the request's params. The call " +
145
+ 'itself is unaffected, and its cancellation still reached the request.');
98
146
  }
99
147
  //# sourceMappingURL=callLifetime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"callLifetime.js","sourceRoot":"","sources":["../../../src/mcp/callLifetime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"callLifetime.js","sourceRoot":"","sources":["../../../src/mcp/callLifetime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * callMarker — a library-private marker that ties one MCP tool call to the one
3
+ * request the SDK sends for it, whatever the SDK copies on the way.
4
+ *
5
+ * Pattern: mark at the call, take at the transport.
6
+ * Role: The correlation hop of the request ledger (`requestLedger.ts`). The
7
+ * call puts its marker in the request's own `_meta` bag
8
+ * (`markParams`); the MCP SDK carries `_meta` through to the
9
+ * transport — the very object, or a copy of it (`structuredClone`, a
10
+ * spread) — and the ledger's watched `send` takes the marker out again
11
+ * (`takeMarker`) before the transport serializes the request. The
12
+ * server receives the bytes it always did: the marker never leaves the
13
+ * client.
14
+ *
15
+ * An object's identity does not survive a copy; a string inside it does. So an
16
+ * SDK that copies the params still delivers the marker, and the request is
17
+ * matched to its call by the call's own word, never by guessing from content
18
+ * (two calls with the same arguments are the case content matching gets wrong).
19
+ * An SDK that rebuilds the params and drops `_meta` delivers nothing: the
20
+ * request then matches no call, and the ledger says so (`requestLedger.ts`).
21
+ */
22
+ /** The `_meta` key the marker rides under. Never sent: `takeMarker` removes it. */
23
+ export declare const CALL_MARKER_KEY = "agentfootprint/call-ref";
24
+ /**
25
+ * `params` with `marker` added to its `_meta` bag: a new object, with every
26
+ * other key in its place; `params` itself is not changed. A `_meta` that was
27
+ * absent is added, after the other keys, so `takeMarker` can remove it whole.
28
+ */
29
+ export declare function markParams<P extends object>(params: P, marker: string): P;
30
+ /** What `takeMarker` found: the marker, when the message carried one, and the message to send. */
31
+ export interface TakenMarker {
32
+ readonly marker?: string;
33
+ /** The message without the marker, or the message itself when it carried none. */
34
+ readonly message: unknown;
35
+ }
36
+ /**
37
+ * The marker a JSON-RPC message carries in `params._meta`, and the message
38
+ * without it. The message is never changed in place: the SDK's object stays
39
+ * the SDK's. Without the marker, the params are what they were before
40
+ * `markParams`: a `_meta` the marker alone filled is removed, and any other
41
+ * entry, the SDK's own included, keeps its place.
42
+ */
43
+ export declare function takeMarker(message: unknown): TakenMarker;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * callMarker — a library-private marker that ties one MCP tool call to the one
3
+ * request the SDK sends for it, whatever the SDK copies on the way.
4
+ *
5
+ * Pattern: mark at the call, take at the transport.
6
+ * Role: The correlation hop of the request ledger (`requestLedger.ts`). The
7
+ * call puts its marker in the request's own `_meta` bag
8
+ * (`markParams`); the MCP SDK carries `_meta` through to the
9
+ * transport — the very object, or a copy of it (`structuredClone`, a
10
+ * spread) — and the ledger's watched `send` takes the marker out again
11
+ * (`takeMarker`) before the transport serializes the request. The
12
+ * server receives the bytes it always did: the marker never leaves the
13
+ * client.
14
+ *
15
+ * An object's identity does not survive a copy; a string inside it does. So an
16
+ * SDK that copies the params still delivers the marker, and the request is
17
+ * matched to its call by the call's own word, never by guessing from content
18
+ * (two calls with the same arguments are the case content matching gets wrong).
19
+ * An SDK that rebuilds the params and drops `_meta` delivers nothing: the
20
+ * request then matches no call, and the ledger says so (`requestLedger.ts`).
21
+ */
22
+ /** The `_meta` key the marker rides under. Never sent: `takeMarker` removes it. */
23
+ export const CALL_MARKER_KEY = 'agentfootprint/call-ref';
24
+ /**
25
+ * `params` with `marker` added to its `_meta` bag: a new object, with every
26
+ * other key in its place; `params` itself is not changed. A `_meta` that was
27
+ * absent is added, after the other keys, so `takeMarker` can remove it whole.
28
+ */
29
+ export function markParams(params, marker) {
30
+ const meta = recordOf(params._meta) ?? {};
31
+ return { ...params, _meta: { ...meta, [CALL_MARKER_KEY]: marker } };
32
+ }
33
+ /**
34
+ * The marker a JSON-RPC message carries in `params._meta`, and the message
35
+ * without it. The message is never changed in place: the SDK's object stays
36
+ * the SDK's. Without the marker, the params are what they were before
37
+ * `markParams`: a `_meta` the marker alone filled is removed, and any other
38
+ * entry, the SDK's own included, keeps its place.
39
+ */
40
+ export function takeMarker(message) {
41
+ const fields = recordOf(message);
42
+ const params = recordOf(fields?.['params']);
43
+ const meta = recordOf(params?.['_meta']);
44
+ if (fields === undefined ||
45
+ params === undefined ||
46
+ meta === undefined ||
47
+ !Object.prototype.hasOwnProperty.call(meta, CALL_MARKER_KEY)) {
48
+ return { message };
49
+ }
50
+ const { [CALL_MARKER_KEY]: marker, ...rest } = meta;
51
+ // A copy keeps every key in its place: `_meta` is replaced where it stood, or
52
+ // removed when the marker alone filled it.
53
+ const unmarked = { ...params };
54
+ if (Object.keys(rest).length > 0)
55
+ unmarked['_meta'] = rest;
56
+ else
57
+ delete unmarked['_meta'];
58
+ return {
59
+ ...(typeof marker === 'string' && { marker }),
60
+ message: { ...fields, params: unmarked },
61
+ };
62
+ }
63
+ /** A plain object's own fields, or `undefined` for anything else. */
64
+ function recordOf(value) {
65
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
66
+ ? value
67
+ : undefined;
68
+ }
69
+ //# sourceMappingURL=callMarker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callMarker.js","sourceRoot":"","sources":["../../../src/mcp/callMarker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAmB,MAAS,EAAE,MAAc;IACpE,MAAM,IAAI,GAAG,QAAQ,CAAE,MAAuC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC5E,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;AACtE,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,IACE,MAAM,KAAK,SAAS;QACpB,MAAM,KAAK,SAAS;QACpB,IAAI,KAAK,SAAS;QAClB,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,EAC5D,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC;IACD,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACpD,8EAA8E;IAC9E,2CAA2C;IAC3C,MAAM,QAAQ,GAA4B,EAAE,GAAG,MAAM,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;;QACtD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO;QACL,GAAG,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,CAAC;QAC7C,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE;KACzC,CAAC;AACJ,CAAC;AAED,qEAAqE;AACrE,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAA2C;QAC9C,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
@@ -65,6 +65,13 @@ const BUILT_BY_THE_TRANSPORT = [
65
65
  '`agentfootprint/providers`) and hand THAT to your transport — it is the same ' +
66
66
  'implementation, applied where you build it.',
67
67
  ],
68
+ [
69
+ 'replay',
70
+ 'The replay declaration is asked by the retry series inside that transport, and a ' +
71
+ 'connection you built has none of ours: `retryingFetch` keeps the compatibility ' +
72
+ 'policy (a 429 retried for every tool, the assumption recorded nowhere). Pass ' +
73
+ '`transport` instead to have tool calls sent again only as you declare.',
74
+ ],
68
75
  [
69
76
  'clientInfo',
70
77
  'Pass it to the SDK `Client` constructor instead: `new Client(clientInfo, { capabilities: {} })`.',
@@ -1 +1 @@
1
- {"version":3,"file":"connectionRefusals.js","sourceRoot":"","sources":["../../../src/mcp/connectionRefusals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,0EAA0E;AAC1E,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAA6C,EAC7C,IAAY;IAEZ,MAAM,EAAE,GAAG,aAAa,IAAI,GAAG,CAAC;IAChC,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,iCAAiC;IACjC,MAAM,KAAK,GAAG,IAAoD,CAAC;IAEnE,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,SAAS,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,kFAAkF;gBACrF,oDAAoD,CACvD,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,sDAAsD;IACtD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,sBAAsB,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,0BAA0B,MAAM,6BAA6B;gBAChE,oEAAoE,MAAM,QAAQ;gBAClF,6DAA6D,KAAK,EAAE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,sBAAsB,GAExB;IACF;QACE,iBAAiB;QACjB,gFAAgF;YAC9E,+EAA+E;YAC/E,6CAA6C;KAChD;IACD;QACE,YAAY;QACZ,kGAAkG;KACnG;IACD;QACE,WAAW;QACX,gGAAgG;KACjG;IACD;QACE,KAAK;QACL,+EAA+E;YAC7E,wCAAwC;KAC3C;IACD;QACE,SAAS;QACT,uFAAuF;KACxF;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,gBAAgB,CAAC,UAAmB,EAAE,EAAU;IACvD,MAAM,OAAO,GAAG,CAAC,UAAU,IAAI,EAAE,CAAsC,CAAC;IACxE,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACxC,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,6BAA6B,MAAM,yCAAyC;gBAC/E,oFAAoF;gBACpF,kFAAkF;gBAClF,uDAAuD,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"connectionRefusals.js","sourceRoot":"","sources":["../../../src/mcp/connectionRefusals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,0EAA0E;AAC1E,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAA6C,EAC7C,IAAY;IAEZ,MAAM,EAAE,GAAG,aAAa,IAAI,GAAG,CAAC;IAChC,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,iCAAiC;IACjC,MAAM,KAAK,GAAG,IAAoD,CAAC;IAEnE,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,SAAS,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,kFAAkF;gBACrF,oDAAoD,CACvD,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,sDAAsD;IACtD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,sBAAsB,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,0BAA0B,MAAM,6BAA6B;gBAChE,oEAAoE,MAAM,QAAQ;gBAClF,6DAA6D,KAAK,EAAE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,sBAAsB,GAExB;IACF;QACE,iBAAiB;QACjB,gFAAgF;YAC9E,+EAA+E;YAC/E,6CAA6C;KAChD;IACD;QACE,QAAQ;QACR,mFAAmF;YACjF,iFAAiF;YACjF,+EAA+E;YAC/E,wEAAwE;KAC3E;IACD;QACE,YAAY;QACZ,kGAAkG;KACnG;IACD;QACE,WAAW;QACX,gGAAgG;KACjG;IACD;QACE,KAAK;QACL,+EAA+E;YAC7E,wCAAwC;KAC3C;IACD;QACE,SAAS;QACT,uFAAuF;KACxF;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,gBAAgB,CAAC,UAAmB,EAAE,EAAU;IACvD,MAAM,OAAO,GAAG,CAAC,UAAU,IAAI,EAAE,CAAsC,CAAC;IACxE,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACxC,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,6BAA6B,MAAM,yCAAyC;gBAC/E,oFAAoF;gBACpF,kFAAkF;gBAClF,uDAAuD,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -11,4 +11,6 @@ export { mockMcpClient, type MockMcpClientOptions, type MockMcpTool } from './mo
11
11
  export { gatewayTransport, GatewayAuthorizationRequiredError, type GatewayTransportOptions, } from './gatewayTransport.js';
12
12
  export { retryingFetch } from './throttleRetry.js';
13
13
  export type { RetryOnThrottle, ThrottleFetch, ThrottleRetryInfo, ThrottleRetryOptions, } from './throttleRetry.js';
14
+ export type { McpReplayDeduplication, McpReplayKind, McpReplayOptions, McpReplayPolicy, McpReplayTool, } from './replay.js';
15
+ export { McpReplayUnavailableError, type McpReplayUnavailableReason } from './replayUnavailable.js';
14
16
  export type { McpCallToolResult, McpClient, McpClientOptions, McpConnection, McpConnectionOptions, McpSdk, McpGatewayTransport, McpHttpTransport, McpStdioTransport, McpTransport, McpSdkClient, McpServeOptions, McpServeHandle, McpServeTransport, McpHttpServeTransport, McpStdioServeTransport, McpSdkServer, } from './types.js';
@@ -15,4 +15,7 @@ export { gatewayTransport, GatewayAuthorizationRequiredError, } from './gatewayT
15
15
  // 9.81.0 so the `connection` arm, which builds no transport of its own, can
16
16
  // apply the SAME implementation instead of silently going without it.
17
17
  export { retryingFetch } from './throttleRetry.js';
18
+ // A call under 'app-authorized' that a connection cannot honour is refused
19
+ // before it is sent, with a typed reason.
20
+ export { McpReplayUnavailableError } from './replayUnavailable.js';
18
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC;AAC/B,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC;AAC/B,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAgBnD,2EAA2E;AAC3E,0CAA0C;AAC1C,OAAO,EAAE,yBAAyB,EAAmC,MAAM,wBAAwB,CAAC"}