@haven_ai/sdk 0.0.0-dev.202609191954.6adcc6b → 0.0.0-dev.202609192153.41dae78
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/README.md +12 -0
- package/dist/index.cjs +51 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +51 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -579,6 +579,18 @@ original `tools/call`, setting BOTH `PAYMENT-SIGNATURE` (x402 v2) and `X-PAYMENT
|
|
|
579
579
|
user intent so fresh merchant quotes or sessions do not become duplicate Haven
|
|
580
580
|
payments.
|
|
581
581
|
|
|
582
|
+
If the merchant's session is gone by the time the paid retry arrives (it
|
|
583
|
+
expired, or the merchant restarted), a merchant that answers HTTP 404 with
|
|
584
|
+
JSON-RPC `-32001` **and** `error.data = { settled: false, next_action:
|
|
585
|
+
'reinitialize_then_retry_same_payment_header' }` is stating its own guarantee
|
|
586
|
+
that nothing was settled; since #3171 `fetch()`, `payX402Quote()`,
|
|
587
|
+
`resumeX402Payment()` and `completeX402MerchantCall()` act on exactly that
|
|
588
|
+
shape — re-initialize once and resend the SAME payment header on the new
|
|
589
|
+
session. A bare `-32001` without the data, any other 404, a failed
|
|
590
|
+
re-initialize, or a second 404 is returned to you unchanged and, after
|
|
591
|
+
funding, recorded as a rejection as before: the SDK never resends on an
|
|
592
|
+
inference, only on the merchant's stated guarantee.
|
|
593
|
+
|
|
582
594
|
See [`examples/mcp-x402-sse.ts`](./examples/mcp-x402-sse.ts) for a complete
|
|
583
595
|
MCP flow with initialize, `mcp-session-id`, JSON-RPC `tools/call`, quote
|
|
584
596
|
inspection, saved resume state, and final retry.
|
package/dist/index.cjs
CHANGED
|
@@ -1494,6 +1494,7 @@ var MCP_CLIENT_INFO = { name: "haven-sdk", version: "1" };
|
|
|
1494
1494
|
var MCP_X402_PAYMENT_META_KEY = "x402/payment";
|
|
1495
1495
|
var MCP_X402_PAYMENT_RESPONSE_META_KEY = "x402/payment-response";
|
|
1496
1496
|
var X402_RETRY_REJECTED_STATUS = 402;
|
|
1497
|
+
var SESSION_NOT_FOUND_NEXT_ACTION = "reinitialize_then_retry_same_payment_header";
|
|
1497
1498
|
var McpMerchantTransport = class {
|
|
1498
1499
|
merchantTimeout;
|
|
1499
1500
|
fetchImpl;
|
|
@@ -1654,6 +1655,29 @@ var McpMerchantTransport = class {
|
|
|
1654
1655
|
}
|
|
1655
1656
|
return this.fetch(url, withMcpPaymentMeta({ ...init, headers }, paymentHeader));
|
|
1656
1657
|
}
|
|
1658
|
+
/**
|
|
1659
|
+
* #3171: deliver the payment; if the merchant answers its unknown-session
|
|
1660
|
+
* 404 WITH the settled-nothing recovery data, re-initialize once and resend
|
|
1661
|
+
* the SAME header on the new session. The merchant's own guarantee (#1578:
|
|
1662
|
+
* the session guard runs before its payment gate) is what makes the resend
|
|
1663
|
+
* safe — a bare -32001, or any other 404, is returned to the caller
|
|
1664
|
+
* untouched (body re-wrapped, since reading it consumed the stream). One
|
|
1665
|
+
* recovery only: a second 404 is the answer.
|
|
1666
|
+
*/
|
|
1667
|
+
async deliverPaymentRecoveringSession(url, init, paymentHeader, reinitialize) {
|
|
1668
|
+
const first = await this.deliverPayment(url, init, paymentHeader);
|
|
1669
|
+
if (first.status !== 404) return first;
|
|
1670
|
+
let text;
|
|
1671
|
+
try {
|
|
1672
|
+
text = await first.clone().text();
|
|
1673
|
+
} catch {
|
|
1674
|
+
return first;
|
|
1675
|
+
}
|
|
1676
|
+
if (!sessionNotFoundRecovery(text)) return first;
|
|
1677
|
+
const sessionId = await reinitialize();
|
|
1678
|
+
if (!sessionId) return first;
|
|
1679
|
+
return this.deliverPayment(url, this.withSessionHeaders(init, sessionId), paymentHeader);
|
|
1680
|
+
}
|
|
1657
1681
|
/**
|
|
1658
1682
|
* #3118: read a native MCP payment-required tool result from a response
|
|
1659
1683
|
* WITHOUT consuming it — JSON or SSE framed. `undefined` for anything that
|
|
@@ -1780,6 +1804,24 @@ function parseJsonRpcToolsCall(body) {
|
|
|
1780
1804
|
function isJsonRpcToolsCallBody(body) {
|
|
1781
1805
|
return parseJsonRpcToolsCall(body) !== void 0;
|
|
1782
1806
|
}
|
|
1807
|
+
function sessionNotFoundRecovery(body) {
|
|
1808
|
+
let parsed;
|
|
1809
|
+
try {
|
|
1810
|
+
parsed = JSON.parse(body);
|
|
1811
|
+
} catch {
|
|
1812
|
+
return void 0;
|
|
1813
|
+
}
|
|
1814
|
+
if (!isRecord(parsed) || !isRecord(parsed.error)) return void 0;
|
|
1815
|
+
if (parsed.error.code !== -32001) return void 0;
|
|
1816
|
+
const data = parsed.error.data;
|
|
1817
|
+
if (!isRecord(data)) return void 0;
|
|
1818
|
+
if (data.settled !== false || data.next_action !== SESSION_NOT_FOUND_NEXT_ACTION) return void 0;
|
|
1819
|
+
return {
|
|
1820
|
+
reason: typeof data.reason === "string" ? data.reason : "session_expired",
|
|
1821
|
+
settled: false,
|
|
1822
|
+
next_action: SESSION_NOT_FOUND_NEXT_ACTION
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1783
1825
|
function isRecord(value) {
|
|
1784
1826
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1785
1827
|
}
|
|
@@ -2931,10 +2973,11 @@ var MerchantCompletion = class {
|
|
|
2931
2973
|
if (!receipt.paymentHeader) {
|
|
2932
2974
|
throw new HavenApiError("No x402 payment header was returned for payment retry", 500);
|
|
2933
2975
|
}
|
|
2934
|
-
const retryResponse = await this.merchantTransport.
|
|
2976
|
+
const retryResponse = await this.merchantTransport.deliverPaymentRecoveringSession(
|
|
2935
2977
|
url,
|
|
2936
2978
|
initialInit,
|
|
2937
|
-
receipt.paymentHeader
|
|
2979
|
+
receipt.paymentHeader,
|
|
2980
|
+
() => this.merchantTransport.initialize(url, initialInit)
|
|
2938
2981
|
);
|
|
2939
2982
|
const toolResult = retryResponse.ok ? await this.merchantTransport.readToolResult(retryResponse) : void 0;
|
|
2940
2983
|
const inBandChallenge = toolResult ? extractMcpPaymentRequired(toolResult) : void 0;
|
|
@@ -4265,7 +4308,12 @@ var HavenClient = class {
|
|
|
4265
4308
|
}
|
|
4266
4309
|
let requestInit = withX402Wallet(input.init, x402Wallet) ?? {};
|
|
4267
4310
|
if (mcpSessionId) requestInit = this.merchantTransport.withSessionHeaders(requestInit, mcpSessionId);
|
|
4268
|
-
const response = await this.merchantTransport.
|
|
4311
|
+
const response = await this.merchantTransport.deliverPaymentRecoveringSession(
|
|
4312
|
+
input.url,
|
|
4313
|
+
requestInit,
|
|
4314
|
+
input.paymentHeader,
|
|
4315
|
+
() => this.merchantTransport.initialize(input.url, input.init, x402Wallet)
|
|
4316
|
+
);
|
|
4269
4317
|
const surfaced = await this.merchantTransport.surfaceResult(response);
|
|
4270
4318
|
const text = await surfaced.text();
|
|
4271
4319
|
let body;
|