settld 0.2.4 → 0.2.6

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 (128) hide show
  1. package/Dockerfile +2 -2
  2. package/docs/CONFIG.md +12 -0
  3. package/docs/README.md +3 -0
  4. package/docs/ops/HOSTED_BASELINE_R2.md +4 -2
  5. package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +19 -7
  6. package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +8 -3
  7. package/package.json +4 -1
  8. package/packages/api-sdk/README.md +71 -0
  9. package/packages/api-sdk/src/client.js +1021 -0
  10. package/packages/api-sdk/src/express-middleware.js +163 -0
  11. package/packages/api-sdk/src/index.d.ts +1662 -0
  12. package/packages/api-sdk/src/index.js +10 -0
  13. package/packages/api-sdk/src/webhook-signature.js +182 -0
  14. package/packages/api-sdk/src/x402-autopay.js +210 -0
  15. package/scripts/ci/cli-pack-smoke.mjs +2 -0
  16. package/scripts/ci/run-public-onboarding-gate.mjs +136 -0
  17. package/scripts/setup/login.mjs +73 -2
  18. package/scripts/setup/onboard.mjs +173 -28
  19. package/scripts/setup/onboarding-failure-taxonomy.mjs +107 -0
  20. package/scripts/setup/onboarding-state-machine.mjs +102 -0
  21. package/services/magic-link/README.md +352 -0
  22. package/services/magic-link/assets/samples/closepack/known-bad/acceptance/acceptance_criteria.json +1 -0
  23. package/services/magic-link/assets/samples/closepack/known-bad/acceptance/acceptance_evaluation.json +1 -0
  24. package/services/magic-link/assets/samples/closepack/known-bad/attestation/bundle_head_attestation.json +1 -0
  25. package/services/magic-link/assets/samples/closepack/known-bad/evidence/evidence_index.json +1 -0
  26. package/services/magic-link/assets/samples/closepack/known-bad/governance/policy.json +1 -0
  27. package/services/magic-link/assets/samples/closepack/known-bad/governance/revocations.json +1 -0
  28. package/services/magic-link/assets/samples/closepack/known-bad/manifest.json +1 -0
  29. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/attestation/bundle_head_attestation.json +1 -0
  30. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/governance/policy.json +1 -0
  31. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/governance/revocations.json +1 -0
  32. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/invoice/invoice_claim.json +1 -0
  33. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/manifest.json +1 -0
  34. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/metering/metering_report.json +1 -0
  35. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/attestation/bundle_head_attestation.json +1 -0
  36. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/events/events.jsonl +1 -0
  37. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/events/payload_material.jsonl +1 -0
  38. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/events.jsonl +1 -0
  39. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/payload_material.jsonl +1 -0
  40. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/snapshot.json +1 -0
  41. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/policy.json +1 -0
  42. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/revocations.json +1 -0
  43. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/events.jsonl +0 -0
  44. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/payload_material.jsonl +0 -0
  45. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/snapshot.json +1 -0
  46. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/job/snapshot.json +1 -0
  47. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/keys/public_keys.json +1 -0
  48. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/manifest.json +1 -0
  49. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/verify/report.json +1 -0
  50. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/verify/verification_report.json +1 -0
  51. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/pricing/pricing_matrix.json +1 -0
  52. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/pricing/pricing_matrix_signatures.json +1 -0
  53. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/settld.json +1 -0
  54. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/verify/verification_report.json +1 -0
  55. package/services/magic-link/assets/samples/closepack/known-bad/settld.json +1 -0
  56. package/services/magic-link/assets/samples/closepack/known-bad/sla/sla_definition.json +1 -0
  57. package/services/magic-link/assets/samples/closepack/known-bad/sla/sla_evaluation.json +1 -0
  58. package/services/magic-link/assets/samples/closepack/known-bad/verify/verification_report.json +1 -0
  59. package/services/magic-link/assets/samples/closepack/known-good/acceptance/acceptance_criteria.json +1 -0
  60. package/services/magic-link/assets/samples/closepack/known-good/acceptance/acceptance_evaluation.json +1 -0
  61. package/services/magic-link/assets/samples/closepack/known-good/attestation/bundle_head_attestation.json +1 -0
  62. package/services/magic-link/assets/samples/closepack/known-good/evidence/evidence_index.json +1 -0
  63. package/services/magic-link/assets/samples/closepack/known-good/governance/policy.json +1 -0
  64. package/services/magic-link/assets/samples/closepack/known-good/governance/revocations.json +1 -0
  65. package/services/magic-link/assets/samples/closepack/known-good/manifest.json +1 -0
  66. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/attestation/bundle_head_attestation.json +1 -0
  67. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/governance/policy.json +1 -0
  68. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/governance/revocations.json +1 -0
  69. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/invoice/invoice_claim.json +1 -0
  70. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/manifest.json +1 -0
  71. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/metering/metering_report.json +1 -0
  72. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/attestation/bundle_head_attestation.json +1 -0
  73. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/events/events.jsonl +1 -0
  74. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/events/payload_material.jsonl +1 -0
  75. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/events.jsonl +1 -0
  76. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/payload_material.jsonl +1 -0
  77. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/snapshot.json +1 -0
  78. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/policy.json +1 -0
  79. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/revocations.json +1 -0
  80. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/events.jsonl +0 -0
  81. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/payload_material.jsonl +0 -0
  82. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/snapshot.json +1 -0
  83. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/job/snapshot.json +1 -0
  84. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/keys/public_keys.json +1 -0
  85. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/manifest.json +1 -0
  86. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/verify/report.json +1 -0
  87. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/verify/verification_report.json +1 -0
  88. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/pricing/pricing_matrix.json +1 -0
  89. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/pricing/pricing_matrix_signatures.json +1 -0
  90. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/settld.json +1 -0
  91. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/verify/verification_report.json +1 -0
  92. package/services/magic-link/assets/samples/closepack/known-good/settld.json +1 -0
  93. package/services/magic-link/assets/samples/closepack/known-good/sla/sla_definition.json +1 -0
  94. package/services/magic-link/assets/samples/closepack/known-good/sla/sla_evaluation.json +1 -0
  95. package/services/magic-link/assets/samples/closepack/known-good/verify/verification_report.json +1 -0
  96. package/services/magic-link/assets/samples/trust.json +11 -0
  97. package/services/magic-link/src/audit-log.js +24 -0
  98. package/services/magic-link/src/buyer-auth.js +251 -0
  99. package/services/magic-link/src/buyer-notifications.js +402 -0
  100. package/services/magic-link/src/buyer-users.js +129 -0
  101. package/services/magic-link/src/decision-otp.js +187 -0
  102. package/services/magic-link/src/decisions.js +92 -0
  103. package/services/magic-link/src/email-resend.js +89 -0
  104. package/services/magic-link/src/ingest-keys.js +137 -0
  105. package/services/magic-link/src/maintenance.js +95 -0
  106. package/services/magic-link/src/onboarding-email-sequence.js +331 -0
  107. package/services/magic-link/src/payment-triggers.js +733 -0
  108. package/services/magic-link/src/pdf.js +149 -0
  109. package/services/magic-link/src/policy.js +69 -0
  110. package/services/magic-link/src/redaction.js +6 -0
  111. package/services/magic-link/src/render-model.js +70 -0
  112. package/services/magic-link/src/retention-gc.js +158 -0
  113. package/services/magic-link/src/run-records.js +496 -0
  114. package/services/magic-link/src/s3.js +171 -0
  115. package/services/magic-link/src/server.js +15849 -0
  116. package/services/magic-link/src/settlement-decisions.js +84 -0
  117. package/services/magic-link/src/smtp.js +217 -0
  118. package/services/magic-link/src/storage-cli.js +88 -0
  119. package/services/magic-link/src/storage-format.js +59 -0
  120. package/services/magic-link/src/tenant-billing.js +115 -0
  121. package/services/magic-link/src/tenant-onboarding.js +467 -0
  122. package/services/magic-link/src/tenant-settings.js +1140 -0
  123. package/services/magic-link/src/usage.js +80 -0
  124. package/services/magic-link/src/verify-queue.js +179 -0
  125. package/services/magic-link/src/verify-worker.js +157 -0
  126. package/services/magic-link/src/webhook-retries.js +542 -0
  127. package/services/magic-link/src/webhooks.js +218 -0
  128. package/src/api/app.js +135 -1
@@ -0,0 +1,1021 @@
1
+ import { createHash, sign as nodeSign } from "node:crypto";
2
+
3
+ function assertNonEmptyString(value, name) {
4
+ if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty string`);
5
+ }
6
+
7
+ function assertSha256Hex(value, name) {
8
+ const raw = typeof value === "string" ? value.trim().toLowerCase() : "";
9
+ if (!/^[0-9a-f]{64}$/.test(raw)) throw new TypeError(`${name} must be sha256 hex`);
10
+ return raw;
11
+ }
12
+
13
+ function randomRequestId() {
14
+ try {
15
+ if (globalThis.crypto?.randomUUID) return String(globalThis.crypto.randomUUID());
16
+ } catch {
17
+ // ignore
18
+ }
19
+ return `req_${Math.random().toString(16).slice(2)}${Date.now().toString(16)}`;
20
+ }
21
+
22
+ function normalizePrefix(value, fallback) {
23
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
24
+ return fallback;
25
+ }
26
+
27
+ function canonicalize(value) {
28
+ if (value === null) return null;
29
+ const valueType = typeof value;
30
+ if (valueType === "string" || valueType === "boolean") return value;
31
+ if (valueType === "number") {
32
+ if (!Number.isFinite(value)) throw new TypeError("Unsupported number for canonical JSON: non-finite");
33
+ if (Object.is(value, -0)) throw new TypeError("Unsupported number for canonical JSON: -0");
34
+ return value;
35
+ }
36
+ if (valueType === "undefined") throw new TypeError("Unsupported value for canonical JSON: undefined");
37
+ if (valueType === "bigint" || valueType === "function" || valueType === "symbol") {
38
+ throw new TypeError(`Unsupported type for canonical JSON: ${valueType}`);
39
+ }
40
+ if (Array.isArray(value)) return value.map((item) => canonicalize(item));
41
+ if (valueType === "object") {
42
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
43
+ throw new TypeError("Unsupported object for canonical JSON: non-plain object");
44
+ }
45
+ const out = {};
46
+ for (const key of Object.keys(value).sort()) {
47
+ const normalized = canonicalize(value[key]);
48
+ if (normalized !== undefined) out[key] = normalized;
49
+ }
50
+ return out;
51
+ }
52
+ throw new TypeError(`Unsupported value for canonical JSON: ${String(value)}`);
53
+ }
54
+
55
+ function canonicalJsonStringify(value) {
56
+ return JSON.stringify(canonicalize(value));
57
+ }
58
+
59
+ function sha256HexUtf8(text) {
60
+ return createHash("sha256").update(String(text), "utf8").digest("hex");
61
+ }
62
+
63
+ function normalizeIsoDate(value, { fallbackNow = false, name = "timestamp" } = {}) {
64
+ const raw =
65
+ typeof value === "string" && value.trim() !== ""
66
+ ? value.trim()
67
+ : fallbackNow
68
+ ? new Date().toISOString()
69
+ : null;
70
+ if (!raw) throw new TypeError(`${name} is required`);
71
+ const parsed = Date.parse(raw);
72
+ if (!Number.isFinite(parsed)) throw new TypeError(`${name} must be an ISO date string`);
73
+ return new Date(parsed).toISOString();
74
+ }
75
+
76
+ function asNonEmptyStringOrNull(value) {
77
+ if (typeof value !== "string") return null;
78
+ const trimmed = value.trim();
79
+ return trimmed ? trimmed : null;
80
+ }
81
+
82
+ function assertReasonCode(value, name) {
83
+ const raw = asNonEmptyStringOrNull(value);
84
+ if (!raw) throw new TypeError(`${name} is required`);
85
+ const normalized = String(raw).toUpperCase();
86
+ if (!/^[A-Z0-9_]{2,64}$/.test(normalized)) throw new TypeError(`${name} must match ^[A-Z0-9_]{2,64}$`);
87
+ return normalized;
88
+ }
89
+
90
+ async function readJson(res) {
91
+ const text = await res.text();
92
+ if (!text) return null;
93
+ try {
94
+ return JSON.parse(text);
95
+ } catch {
96
+ return { raw: text };
97
+ }
98
+ }
99
+
100
+ function headersToRecord(headers) {
101
+ const out = {};
102
+ for (const [k, v] of headers.entries()) out[String(k).toLowerCase()] = String(v);
103
+ return out;
104
+ }
105
+
106
+ export class SettldClient {
107
+ constructor(opts) {
108
+ assertNonEmptyString(opts?.baseUrl, "baseUrl");
109
+ assertNonEmptyString(opts?.tenantId, "tenantId");
110
+ this.baseUrl = String(opts.baseUrl).replace(/\/+$/, "");
111
+ this.tenantId = String(opts.tenantId);
112
+ this.protocol = opts?.protocol ? String(opts.protocol) : "1.0";
113
+ this.apiKey = opts?.apiKey ? String(opts.apiKey) : null;
114
+ this.xApiKey = opts?.xApiKey ? String(opts.xApiKey) : null;
115
+ this.opsToken = opts?.opsToken ? String(opts.opsToken) : null;
116
+ this.fetchImpl = opts?.fetch ?? fetch;
117
+ this.userAgent = opts?.userAgent ? String(opts.userAgent) : null;
118
+ }
119
+
120
+ async request(method, pathname, { body, requestId, idempotencyKey, expectedPrevChainHash, signal } = {}) {
121
+ const url = new URL(pathname, this.baseUrl);
122
+ const rid = requestId ?? randomRequestId();
123
+
124
+ const headers = {
125
+ "content-type": "application/json",
126
+ "x-proxy-tenant-id": this.tenantId,
127
+ "x-settld-protocol": this.protocol,
128
+ "x-request-id": rid
129
+ };
130
+ if (this.userAgent) headers["user-agent"] = this.userAgent;
131
+ if (idempotencyKey) headers["x-idempotency-key"] = String(idempotencyKey);
132
+ if (expectedPrevChainHash) headers["x-proxy-expected-prev-chain-hash"] = String(expectedPrevChainHash);
133
+ if (this.apiKey) headers["authorization"] = `Bearer ${this.apiKey}`;
134
+ if (this.xApiKey) headers["x-api-key"] = this.xApiKey;
135
+ if (this.opsToken) headers["x-proxy-ops-token"] = this.opsToken;
136
+
137
+ const res = await this.fetchImpl(url.toString(), {
138
+ method,
139
+ headers,
140
+ body: body === undefined ? undefined : JSON.stringify(body),
141
+ signal
142
+ });
143
+
144
+ const outHeaders = headersToRecord(res.headers);
145
+ const responseRequestId = outHeaders["x-request-id"] ?? null;
146
+ const parsed = await readJson(res);
147
+ if (!res.ok) {
148
+ const errBody = parsed && typeof parsed === "object" ? parsed : {};
149
+ const e = {
150
+ status: res.status,
151
+ code: errBody?.code ?? null,
152
+ message: errBody?.error ?? `request failed (${res.status})`,
153
+ details: errBody?.details,
154
+ requestId: responseRequestId
155
+ };
156
+ const thrown = new Error(e.message);
157
+ thrown.settld = e;
158
+ throw thrown;
159
+ }
160
+
161
+ return { ok: true, status: res.status, requestId: responseRequestId, body: parsed, headers: outHeaders };
162
+ }
163
+
164
+ capabilities(opts) {
165
+ return this.request("GET", "/capabilities", opts);
166
+ }
167
+
168
+ openApi(opts) {
169
+ return this.request("GET", "/openapi.json", opts);
170
+ }
171
+
172
+ x402GateAuthorizePayment(body, opts) {
173
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
174
+ assertNonEmptyString(body?.gateId, "body.gateId");
175
+ return this.request("POST", "/x402/gate/authorize-payment", { ...opts, body });
176
+ }
177
+
178
+ createJob(body, opts) {
179
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
180
+ return this.request("POST", "/jobs", { ...opts, body });
181
+ }
182
+
183
+ getJob(jobId, opts) {
184
+ assertNonEmptyString(jobId, "jobId");
185
+ return this.request("GET", `/jobs/${encodeURIComponent(jobId)}`, opts);
186
+ }
187
+
188
+ registerAgent(body, opts) {
189
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
190
+ assertNonEmptyString(body?.publicKeyPem, "body.publicKeyPem");
191
+ return this.request("POST", "/agents/register", { ...opts, body });
192
+ }
193
+
194
+ listAgents(params = {}, opts) {
195
+ const qs = new URLSearchParams();
196
+ if (params.status) qs.set("status", String(params.status));
197
+ if (params.capability) qs.set("capability", String(params.capability));
198
+ if (params.minTrustScore !== undefined && params.minTrustScore !== null) qs.set("minTrustScore", String(params.minTrustScore));
199
+ if (params.includeReputation !== undefined && params.includeReputation !== null) qs.set("includeReputation", String(Boolean(params.includeReputation)));
200
+ if (params.reputationVersion) qs.set("reputationVersion", String(params.reputationVersion));
201
+ if (params.reputationWindow) qs.set("reputationWindow", String(params.reputationWindow));
202
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
203
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
204
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
205
+ return this.request("GET", `/agents${suffix}`, opts);
206
+ }
207
+
208
+ getAgent(agentId, opts) {
209
+ assertNonEmptyString(agentId, "agentId");
210
+ return this.request("GET", `/agents/${encodeURIComponent(agentId)}`, opts);
211
+ }
212
+
213
+ getAgentReputation(agentId, opts = {}) {
214
+ assertNonEmptyString(agentId, "agentId");
215
+ const qs = new URLSearchParams();
216
+ if (opts.reputationVersion) qs.set("reputationVersion", String(opts.reputationVersion));
217
+ if (opts.reputationWindow) qs.set("reputationWindow", String(opts.reputationWindow));
218
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
219
+ const { reputationVersion: _rVersion, reputationWindow: _rWindow, ...requestOpts } = opts ?? {};
220
+ return this.request("GET", `/agents/${encodeURIComponent(agentId)}/reputation${suffix}`, requestOpts);
221
+ }
222
+
223
+ searchMarketplaceAgents(params = {}, opts) {
224
+ const qs = new URLSearchParams();
225
+ if (params.status) qs.set("status", String(params.status));
226
+ if (params.capability) qs.set("capability", String(params.capability));
227
+ if (params.minTrustScore !== undefined && params.minTrustScore !== null) qs.set("minTrustScore", String(params.minTrustScore));
228
+ if (params.riskTier) qs.set("riskTier", String(params.riskTier));
229
+ if (params.includeReputation !== undefined && params.includeReputation !== null) qs.set("includeReputation", String(Boolean(params.includeReputation)));
230
+ if (params.reputationVersion) qs.set("reputationVersion", String(params.reputationVersion));
231
+ if (params.reputationWindow) qs.set("reputationWindow", String(params.reputationWindow));
232
+ if (params.scoreStrategy) qs.set("scoreStrategy", String(params.scoreStrategy));
233
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
234
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
235
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
236
+ return this.request("GET", `/marketplace/agents/search${suffix}`, opts);
237
+ }
238
+
239
+ upsertMarketplaceSettlementPolicy(body, opts) {
240
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
241
+ return this.request("POST", "/marketplace/settlement-policies", { ...opts, body });
242
+ }
243
+
244
+ listMarketplaceSettlementPolicies(params = {}, opts) {
245
+ const qs = new URLSearchParams();
246
+ if (params.policyId) qs.set("policyId", String(params.policyId));
247
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
248
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
249
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
250
+ return this.request("GET", `/marketplace/settlement-policies${suffix}`, opts);
251
+ }
252
+
253
+ getMarketplaceSettlementPolicy(policyId, policyVersion, opts) {
254
+ assertNonEmptyString(policyId, "policyId");
255
+ if (!Number.isSafeInteger(Number(policyVersion)) || Number(policyVersion) <= 0) {
256
+ throw new TypeError("policyVersion must be a positive safe integer");
257
+ }
258
+ return this.request(
259
+ "GET",
260
+ `/marketplace/settlement-policies/${encodeURIComponent(policyId)}/${encodeURIComponent(String(policyVersion))}`,
261
+ opts
262
+ );
263
+ }
264
+
265
+ createMarketplaceRfq(body, opts) {
266
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
267
+ return this.request("POST", "/marketplace/rfqs", { ...opts, body });
268
+ }
269
+
270
+ listMarketplaceRfqs(params = {}, opts) {
271
+ const qs = new URLSearchParams();
272
+ if (params.status) qs.set("status", String(params.status));
273
+ if (params.capability) qs.set("capability", String(params.capability));
274
+ if (params.posterAgentId) qs.set("posterAgentId", String(params.posterAgentId));
275
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
276
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
277
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
278
+ return this.request("GET", `/marketplace/rfqs${suffix}`, opts);
279
+ }
280
+
281
+ submitMarketplaceBid(rfqId, body, opts) {
282
+ assertNonEmptyString(rfqId, "rfqId");
283
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
284
+ return this.request("POST", `/marketplace/rfqs/${encodeURIComponent(rfqId)}/bids`, { ...opts, body });
285
+ }
286
+
287
+ listMarketplaceBids(rfqId, params = {}, opts) {
288
+ assertNonEmptyString(rfqId, "rfqId");
289
+ const qs = new URLSearchParams();
290
+ if (params.status) qs.set("status", String(params.status));
291
+ if (params.bidderAgentId) qs.set("bidderAgentId", String(params.bidderAgentId));
292
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
293
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
294
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
295
+ return this.request("GET", `/marketplace/rfqs/${encodeURIComponent(rfqId)}/bids${suffix}`, opts);
296
+ }
297
+
298
+ applyMarketplaceBidCounterOffer(rfqId, bidId, body, opts) {
299
+ assertNonEmptyString(rfqId, "rfqId");
300
+ assertNonEmptyString(bidId, "bidId");
301
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
302
+ return this.request("POST", `/marketplace/rfqs/${encodeURIComponent(rfqId)}/bids/${encodeURIComponent(bidId)}/counter-offer`, {
303
+ ...opts,
304
+ body
305
+ });
306
+ }
307
+
308
+ acceptMarketplaceBid(rfqId, body, opts) {
309
+ assertNonEmptyString(rfqId, "rfqId");
310
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
311
+ return this.request("POST", `/marketplace/rfqs/${encodeURIComponent(rfqId)}/accept`, { ...opts, body });
312
+ }
313
+
314
+ getAgentWallet(agentId, opts) {
315
+ assertNonEmptyString(agentId, "agentId");
316
+ return this.request("GET", `/agents/${encodeURIComponent(agentId)}/wallet`, opts);
317
+ }
318
+
319
+ creditAgentWallet(agentId, body, opts) {
320
+ assertNonEmptyString(agentId, "agentId");
321
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
322
+ return this.request("POST", `/agents/${encodeURIComponent(agentId)}/wallet/credit`, { ...opts, body });
323
+ }
324
+
325
+ createAgentRun(agentId, body = {}, opts) {
326
+ assertNonEmptyString(agentId, "agentId");
327
+ if (!body || typeof body !== "object") throw new TypeError("body must be an object");
328
+ return this.request("POST", `/agents/${encodeURIComponent(agentId)}/runs`, { ...opts, body });
329
+ }
330
+
331
+ listAgentRuns(agentId, params = {}, opts) {
332
+ assertNonEmptyString(agentId, "agentId");
333
+ const qs = new URLSearchParams();
334
+ if (params.status) qs.set("status", String(params.status));
335
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
336
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
337
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
338
+ return this.request("GET", `/agents/${encodeURIComponent(agentId)}/runs${suffix}`, opts);
339
+ }
340
+
341
+ getAgentRun(agentId, runId, opts) {
342
+ assertNonEmptyString(agentId, "agentId");
343
+ assertNonEmptyString(runId, "runId");
344
+ return this.request("GET", `/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}`, opts);
345
+ }
346
+
347
+ listAgentRunEvents(agentId, runId, opts) {
348
+ assertNonEmptyString(agentId, "agentId");
349
+ assertNonEmptyString(runId, "runId");
350
+ return this.request("GET", `/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/events`, opts);
351
+ }
352
+
353
+ appendAgentRunEvent(agentId, runId, body, opts) {
354
+ assertNonEmptyString(agentId, "agentId");
355
+ assertNonEmptyString(runId, "runId");
356
+ if (!opts?.expectedPrevChainHash) throw new TypeError("expectedPrevChainHash is required for appendAgentRunEvent");
357
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
358
+ assertNonEmptyString(body?.type, "body.type");
359
+ return this.request("POST", `/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/events`, { ...opts, body });
360
+ }
361
+
362
+ getRunVerification(runId, opts) {
363
+ assertNonEmptyString(runId, "runId");
364
+ return this.request("GET", `/runs/${encodeURIComponent(runId)}/verification`, opts);
365
+ }
366
+
367
+ getRunSettlement(runId, opts) {
368
+ assertNonEmptyString(runId, "runId");
369
+ return this.request("GET", `/runs/${encodeURIComponent(runId)}/settlement`, opts);
370
+ }
371
+
372
+ getRunAgreement(runId, opts) {
373
+ assertNonEmptyString(runId, "runId");
374
+ return this.request("GET", `/runs/${encodeURIComponent(runId)}/agreement`, opts);
375
+ }
376
+
377
+ applyRunAgreementChangeOrder(runId, body, opts) {
378
+ assertNonEmptyString(runId, "runId");
379
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
380
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/agreement/change-order`, { ...opts, body });
381
+ }
382
+
383
+ cancelRunAgreement(runId, body, opts) {
384
+ assertNonEmptyString(runId, "runId");
385
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
386
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/agreement/cancel`, { ...opts, body });
387
+ }
388
+
389
+ getRunSettlementPolicyReplay(runId, opts) {
390
+ assertNonEmptyString(runId, "runId");
391
+ return this.request("GET", `/runs/${encodeURIComponent(runId)}/settlement/policy-replay`, opts);
392
+ }
393
+
394
+ resolveRunSettlement(runId, body, opts) {
395
+ assertNonEmptyString(runId, "runId");
396
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
397
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/settlement/resolve`, { ...opts, body });
398
+ }
399
+
400
+ opsLockToolCallHold(body, opts) {
401
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
402
+ return this.request("POST", "/ops/tool-calls/holds/lock", { ...opts, body });
403
+ }
404
+
405
+ opsListToolCallHolds(params = {}, opts) {
406
+ const qs = new URLSearchParams();
407
+ if (params.agreementHash) qs.set("agreementHash", String(params.agreementHash));
408
+ if (params.status) qs.set("status", String(params.status));
409
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
410
+ if (params.offset !== undefined && params.offset !== null) qs.set("offset", String(params.offset));
411
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
412
+ return this.request("GET", `/ops/tool-calls/holds${suffix}`, opts);
413
+ }
414
+
415
+ opsGetToolCallReplayEvaluate(agreementHash, opts) {
416
+ assertSha256Hex(agreementHash, "agreementHash");
417
+ return this.request("GET", `/ops/tool-calls/replay-evaluate?agreementHash=${encodeURIComponent(String(agreementHash).toLowerCase())}`, opts);
418
+ }
419
+
420
+ opsGetToolCallHold(holdHash, opts) {
421
+ assertNonEmptyString(holdHash, "holdHash");
422
+ return this.request("GET", `/ops/tool-calls/holds/${encodeURIComponent(holdHash)}`, opts);
423
+ }
424
+
425
+ opsGetReputationFacts(params = {}, opts) {
426
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
427
+ const agentId = asNonEmptyStringOrNull(params.agentId);
428
+ if (!agentId) throw new TypeError("agentId is required");
429
+ const qs = new URLSearchParams();
430
+ qs.set("agentId", agentId);
431
+ const toolId = asNonEmptyStringOrNull(params.toolId);
432
+ if (toolId) qs.set("toolId", toolId);
433
+ if (params.window !== undefined && params.window !== null) qs.set("window", String(params.window));
434
+ const asOf = asNonEmptyStringOrNull(params.asOf);
435
+ if (asOf) qs.set("asOf", asOf);
436
+ if (params.includeEvents !== undefined && params.includeEvents !== null) qs.set("includeEvents", params.includeEvents ? "1" : "0");
437
+ return this.request("GET", `/ops/reputation/facts?${qs.toString()}`, opts);
438
+ }
439
+
440
+ opsRunToolCallHoldbackMaintenance(body = {}, opts) {
441
+ if (!body || typeof body !== "object") throw new TypeError("body must be an object");
442
+ return this.request("POST", "/ops/maintenance/tool-call-holdback/run", { ...opts, body });
443
+ }
444
+
445
+ toolCallListArbitrationCases(params = {}, opts) {
446
+ const qs = new URLSearchParams();
447
+ if (params.agreementHash) qs.set("agreementHash", String(params.agreementHash));
448
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
449
+ return this.request("GET", `/tool-calls/arbitration/cases${suffix}`, opts);
450
+ }
451
+
452
+ toolCallGetArbitrationCase(caseId, opts) {
453
+ assertNonEmptyString(caseId, "caseId");
454
+ return this.request("GET", `/tool-calls/arbitration/cases/${encodeURIComponent(caseId)}`, opts);
455
+ }
456
+
457
+ toolCallOpenArbitration(body, opts) {
458
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
459
+ return this.request("POST", "/tool-calls/arbitration/open", { ...opts, body });
460
+ }
461
+
462
+ toolCallSubmitArbitrationVerdict(body, opts) {
463
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
464
+ return this.request("POST", "/tool-calls/arbitration/verdict", { ...opts, body });
465
+ }
466
+
467
+ opsGetSettlementAdjustment(adjustmentId, opts) {
468
+ assertNonEmptyString(adjustmentId, "adjustmentId");
469
+ return this.request("GET", `/ops/settlement-adjustments/${encodeURIComponent(adjustmentId)}`, opts);
470
+ }
471
+
472
+ getArtifact(artifactId, opts) {
473
+ assertNonEmptyString(artifactId, "artifactId");
474
+ return this.request("GET", `/artifacts/${encodeURIComponent(artifactId)}`, opts);
475
+ }
476
+
477
+ async getArtifacts(params = {}, opts) {
478
+ const artifactIdsRaw = Array.isArray(params) ? params : params?.artifactIds;
479
+ if (!Array.isArray(artifactIdsRaw) || artifactIdsRaw.length === 0) {
480
+ throw new TypeError("artifactIds[] is required");
481
+ }
482
+ const artifactIds = artifactIdsRaw.map((id, idx) => {
483
+ const raw = asNonEmptyStringOrNull(id);
484
+ if (!raw) throw new TypeError(`artifactIds[${idx}] must be a non-empty string`);
485
+ return raw;
486
+ });
487
+ const rows = await Promise.all(
488
+ artifactIds.map(async (artifactId) => {
489
+ const response = await this.getArtifact(artifactId, opts);
490
+ return { artifactId, response };
491
+ })
492
+ );
493
+ return {
494
+ artifacts: rows.map((row) => ({ artifactId: row.artifactId, artifact: row.response?.body?.artifact ?? null })),
495
+ responses: rows.map((row) => row.response)
496
+ };
497
+ }
498
+
499
+ createAgreement(params = {}) {
500
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
501
+ assertNonEmptyString(params.toolId, "params.toolId");
502
+ assertNonEmptyString(params.callId, "params.callId");
503
+ const manifestHash = assertSha256Hex(params.manifestHash, "params.manifestHash");
504
+ const createdAt = normalizeIsoDate(params.createdAt, { fallbackNow: true, name: "params.createdAt" });
505
+
506
+ const inputCanonical = canonicalJsonStringify(params.input ?? {});
507
+ const inputHash = sha256HexUtf8(inputCanonical);
508
+
509
+ const agreementCore = canonicalize({
510
+ schemaVersion: "ToolCallAgreement.v1",
511
+ toolId: String(params.toolId),
512
+ manifestHash,
513
+ callId: String(params.callId),
514
+ inputHash,
515
+ acceptanceCriteria: params.acceptanceCriteria ?? null,
516
+ settlementTerms: params.settlementTerms ?? null,
517
+ payerAgentId: asNonEmptyStringOrNull(params.payerAgentId),
518
+ payeeAgentId: asNonEmptyStringOrNull(params.payeeAgentId),
519
+ createdAt
520
+ });
521
+ const agreementCanonical = canonicalJsonStringify(agreementCore);
522
+ const agreementHash = sha256HexUtf8(agreementCanonical);
523
+ const agreement = canonicalize({ ...agreementCore, agreementHash });
524
+ return { agreement, agreementHash, inputHash, canonicalJson: canonicalJsonStringify(agreement) };
525
+ }
526
+
527
+ signEvidence(params = {}) {
528
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
529
+ const agreement = params.agreement && typeof params.agreement === "object" && !Array.isArray(params.agreement) ? params.agreement : null;
530
+ const agreementHash = assertSha256Hex(params.agreementHash ?? agreement?.agreementHash, "agreementHash");
531
+ const callId = asNonEmptyStringOrNull(params.callId ?? agreement?.callId);
532
+ const inputHash = assertSha256Hex(params.inputHash ?? agreement?.inputHash, "inputHash");
533
+ if (!callId) throw new TypeError("callId is required");
534
+
535
+ const startedAt = normalizeIsoDate(params.startedAt, { fallbackNow: true, name: "startedAt" });
536
+ const completedAt = normalizeIsoDate(params.completedAt ?? startedAt, { fallbackNow: true, name: "completedAt" });
537
+ const outputCanonical = canonicalJsonStringify(params.output ?? {});
538
+ const outputHash = sha256HexUtf8(outputCanonical);
539
+
540
+ const evidenceCore = canonicalize({
541
+ schemaVersion: "ToolCallEvidence.v1",
542
+ agreementHash,
543
+ callId,
544
+ inputHash,
545
+ outputHash,
546
+ outputRef: asNonEmptyStringOrNull(params.outputRef),
547
+ metrics: params.metrics ?? null,
548
+ startedAt,
549
+ completedAt,
550
+ createdAt: normalizeIsoDate(params.createdAt ?? completedAt, { fallbackNow: true, name: "createdAt" })
551
+ });
552
+ const evidenceHash = sha256HexUtf8(canonicalJsonStringify(evidenceCore));
553
+
554
+ const signerPrivateKeyPem = asNonEmptyStringOrNull(params.signerPrivateKeyPem);
555
+ const signerKeyId = asNonEmptyStringOrNull(params.signerKeyId);
556
+ let signature = null;
557
+ if (signerPrivateKeyPem) {
558
+ if (!signerKeyId) throw new TypeError("signerKeyId is required when signerPrivateKeyPem is provided");
559
+ const signatureBase64 = nodeSign(null, Buffer.from(evidenceHash, "hex"), signerPrivateKeyPem).toString("base64");
560
+ signature = {
561
+ algorithm: "ed25519",
562
+ signerKeyId,
563
+ evidenceHash,
564
+ signature: signatureBase64
565
+ };
566
+ }
567
+
568
+ const evidence = canonicalize({
569
+ ...evidenceCore,
570
+ evidenceHash,
571
+ ...(signature ? { signature } : {})
572
+ });
573
+ return { evidence, evidenceHash, outputHash, canonicalJson: canonicalJsonStringify(evidence) };
574
+ }
575
+
576
+ createHold(params = {}, opts) {
577
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
578
+ const agreement = params.agreement && typeof params.agreement === "object" && !Array.isArray(params.agreement) ? params.agreement : null;
579
+ const agreementHash = assertSha256Hex(params.agreementHash ?? agreement?.agreementHash, "agreementHash");
580
+ const receiptHash = assertSha256Hex(params.receiptHash, "receiptHash");
581
+ const payerAgentId = asNonEmptyStringOrNull(params.payerAgentId);
582
+ const payeeAgentId = asNonEmptyStringOrNull(params.payeeAgentId);
583
+ if (!payerAgentId) throw new TypeError("payerAgentId is required");
584
+ if (!payeeAgentId) throw new TypeError("payeeAgentId is required");
585
+ if (payerAgentId === payeeAgentId) throw new TypeError("payerAgentId and payeeAgentId must differ");
586
+
587
+ const amountCents = Number(params.amountCents);
588
+ if (!Number.isSafeInteger(amountCents) || amountCents <= 0) throw new TypeError("amountCents must be a positive safe integer");
589
+ const holdbackBps = params.holdbackBps === undefined ? 0 : Number(params.holdbackBps);
590
+ if (!Number.isSafeInteger(holdbackBps) || holdbackBps < 0 || holdbackBps > 10_000) {
591
+ throw new TypeError("holdbackBps must be an integer within 0..10000");
592
+ }
593
+ const challengeWindowMs = params.challengeWindowMs === undefined ? 0 : Number(params.challengeWindowMs);
594
+ if (!Number.isSafeInteger(challengeWindowMs) || challengeWindowMs < 0) {
595
+ throw new TypeError("challengeWindowMs must be a non-negative safe integer");
596
+ }
597
+
598
+ return this.opsLockToolCallHold(
599
+ {
600
+ agreementHash,
601
+ receiptHash,
602
+ payerAgentId,
603
+ payeeAgentId,
604
+ amountCents,
605
+ currency: asNonEmptyStringOrNull(params.currency) ?? "USD",
606
+ holdbackBps,
607
+ challengeWindowMs
608
+ },
609
+ opts
610
+ );
611
+ }
612
+
613
+ async settle(params = {}, opts) {
614
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
615
+ const agreement = params.agreement && typeof params.agreement === "object" && !Array.isArray(params.agreement) ? params.agreement : null;
616
+ const evidence = params.evidence && typeof params.evidence === "object" && !Array.isArray(params.evidence) ? params.evidence : null;
617
+ const agreementHash = assertSha256Hex(params.agreementHash ?? agreement?.agreementHash, "agreementHash");
618
+ const evidenceHashRaw = params.evidenceHash ?? evidence?.evidenceHash ?? null;
619
+ const evidenceHash = evidenceHashRaw ? assertSha256Hex(evidenceHashRaw, "evidenceHash") : null;
620
+
621
+ const amountCents = Number(params.amountCents);
622
+ if (!Number.isSafeInteger(amountCents) || amountCents <= 0) throw new TypeError("amountCents must be a positive safe integer");
623
+ const currency = asNonEmptyStringOrNull(params.currency) ?? "USD";
624
+ const settledAt = normalizeIsoDate(params.settledAt, { fallbackNow: true, name: "settledAt" });
625
+ const receiptRef = canonicalize({
626
+ schemaVersion: "ToolCallSettlementReceiptRef.v1",
627
+ agreementHash,
628
+ evidenceHash,
629
+ amountCents,
630
+ currency,
631
+ settledAt
632
+ });
633
+ const receiptHash = params.receiptHash
634
+ ? assertSha256Hex(params.receiptHash, "receiptHash")
635
+ : sha256HexUtf8(canonicalJsonStringify(receiptRef));
636
+
637
+ const holdResponse = await this.createHold(
638
+ {
639
+ agreementHash,
640
+ receiptHash,
641
+ payerAgentId: params.payerAgentId,
642
+ payeeAgentId: params.payeeAgentId,
643
+ amountCents,
644
+ currency,
645
+ holdbackBps: params.holdbackBps,
646
+ challengeWindowMs: params.challengeWindowMs
647
+ },
648
+ opts
649
+ );
650
+
651
+ return {
652
+ agreementHash,
653
+ receiptHash,
654
+ receiptRef,
655
+ hold: holdResponse?.body?.hold ?? null,
656
+ holdResponse
657
+ };
658
+ }
659
+
660
+ openDispute(params = {}, opts) {
661
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
662
+ const agreementHash = assertSha256Hex(params.agreementHash, "agreementHash");
663
+ const receiptHash = assertSha256Hex(params.receiptHash, "receiptHash");
664
+ const holdHash = assertSha256Hex(params.holdHash, "holdHash");
665
+ const arbiterAgentId = asNonEmptyStringOrNull(params.arbiterAgentId);
666
+ const summary = asNonEmptyStringOrNull(params.summary);
667
+ if (!arbiterAgentId) throw new TypeError("arbiterAgentId is required");
668
+ if (!summary) throw new TypeError("summary is required");
669
+
670
+ const evidenceRefs = Array.isArray(params.evidenceRefs) ? params.evidenceRefs.map((item) => String(item)) : [];
671
+ const adminOverride = params.adminOverride && typeof params.adminOverride === "object" && !Array.isArray(params.adminOverride)
672
+ ? params.adminOverride
673
+ : null;
674
+ const overrideEnabled = adminOverride?.enabled === true;
675
+
676
+ const providedEnvelope =
677
+ params.disputeOpenEnvelope && typeof params.disputeOpenEnvelope === "object" && !Array.isArray(params.disputeOpenEnvelope)
678
+ ? params.disputeOpenEnvelope
679
+ : null;
680
+ const openedByAgentId =
681
+ asNonEmptyStringOrNull(params.openedByAgentId) ?? asNonEmptyStringOrNull(providedEnvelope?.openedByAgentId);
682
+
683
+ let disputeOpenEnvelope = providedEnvelope;
684
+ if (!disputeOpenEnvelope && !overrideEnabled) {
685
+ if (!openedByAgentId) throw new TypeError("openedByAgentId is required when disputeOpenEnvelope is not provided");
686
+ disputeOpenEnvelope = this.buildDisputeOpenEnvelope({
687
+ agreementHash,
688
+ receiptHash,
689
+ holdHash,
690
+ openedByAgentId,
691
+ signerKeyId: params.signerKeyId,
692
+ signerPrivateKeyPem: params.signerPrivateKeyPem,
693
+ signature: params.signature,
694
+ caseId: params.caseId,
695
+ envelopeId: params.envelopeId,
696
+ reasonCode: params.reasonCode,
697
+ nonce: params.nonce,
698
+ openedAt: params.openedAt,
699
+ tenantId: params.tenantId
700
+ }).disputeOpenEnvelope;
701
+ }
702
+
703
+ return this.toolCallOpenArbitration(
704
+ {
705
+ agreementHash,
706
+ receiptHash,
707
+ holdHash,
708
+ ...(openedByAgentId ? { openedByAgentId } : {}),
709
+ ...(disputeOpenEnvelope ? { disputeOpenEnvelope } : {}),
710
+ arbiterAgentId,
711
+ summary,
712
+ evidenceRefs,
713
+ ...(adminOverride ? { adminOverride } : {})
714
+ },
715
+ opts
716
+ );
717
+ }
718
+
719
+ buildDisputeOpenEnvelope(params = {}) {
720
+ if (!params || typeof params !== "object" || Array.isArray(params)) throw new TypeError("params must be an object");
721
+ const agreementHash = assertSha256Hex(params.agreementHash, "agreementHash");
722
+ const receiptHash = assertSha256Hex(params.receiptHash, "receiptHash");
723
+ const holdHash = assertSha256Hex(params.holdHash, "holdHash");
724
+ const openedByAgentId = asNonEmptyStringOrNull(params.openedByAgentId);
725
+ if (!openedByAgentId) throw new TypeError("openedByAgentId is required");
726
+
727
+ const caseId = asNonEmptyStringOrNull(params.caseId) ?? `arb_case_tc_${agreementHash}`;
728
+ const envelopeId = asNonEmptyStringOrNull(params.envelopeId) ?? `dopen_tc_${agreementHash}`;
729
+ const signerKeyId = asNonEmptyStringOrNull(params.signerKeyId);
730
+ if (!signerKeyId) throw new TypeError("signerKeyId is required");
731
+ const tenantId = asNonEmptyStringOrNull(params.tenantId) ?? this.tenantId;
732
+ const openedAt = normalizeIsoDate(params.openedAt, { fallbackNow: true, name: "openedAt" });
733
+ const reasonCode = assertReasonCode(params.reasonCode ?? "TOOL_CALL_DISPUTE", "reasonCode");
734
+ const nonce = asNonEmptyStringOrNull(params.nonce) ?? `nonce_${Date.now().toString(36)}_${Math.random().toString(16).slice(2, 8)}`;
735
+
736
+ const core = canonicalize({
737
+ schemaVersion: "DisputeOpenEnvelope.v1",
738
+ artifactType: "DisputeOpenEnvelope.v1",
739
+ artifactId: envelopeId,
740
+ envelopeId,
741
+ caseId,
742
+ tenantId,
743
+ agreementHash,
744
+ receiptHash,
745
+ holdHash,
746
+ openedByAgentId,
747
+ openedAt,
748
+ reasonCode,
749
+ nonce,
750
+ signerKeyId
751
+ });
752
+ const envelopeHash = sha256HexUtf8(canonicalJsonStringify(core));
753
+ let signature = asNonEmptyStringOrNull(params.signature);
754
+ if (!signature) {
755
+ const signerPrivateKeyPem = asNonEmptyStringOrNull(params.signerPrivateKeyPem);
756
+ if (!signerPrivateKeyPem) throw new TypeError("signature or signerPrivateKeyPem is required");
757
+ signature = nodeSign(null, Buffer.from(envelopeHash, "hex"), signerPrivateKeyPem).toString("base64");
758
+ }
759
+ const disputeOpenEnvelope = canonicalize({ ...core, envelopeHash, signature });
760
+ return {
761
+ disputeOpenEnvelope,
762
+ envelopeHash,
763
+ canonicalJson: canonicalJsonStringify(disputeOpenEnvelope)
764
+ };
765
+ }
766
+
767
+ openRunDispute(runId, body = {}, opts) {
768
+ assertNonEmptyString(runId, "runId");
769
+ if (!body || typeof body !== "object" || Array.isArray(body)) throw new TypeError("body must be an object");
770
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/dispute/open`, { ...opts, body });
771
+ }
772
+
773
+ closeRunDispute(runId, body = {}, opts) {
774
+ assertNonEmptyString(runId, "runId");
775
+ if (!body || typeof body !== "object" || Array.isArray(body)) throw new TypeError("body must be an object");
776
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/dispute/close`, { ...opts, body });
777
+ }
778
+
779
+ submitRunDisputeEvidence(runId, body, opts) {
780
+ assertNonEmptyString(runId, "runId");
781
+ if (!body || typeof body !== "object" || Array.isArray(body)) throw new TypeError("body is required");
782
+ assertNonEmptyString(body?.evidenceRef, "body.evidenceRef");
783
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/dispute/evidence`, { ...opts, body });
784
+ }
785
+
786
+ escalateRunDispute(runId, body, opts) {
787
+ assertNonEmptyString(runId, "runId");
788
+ if (!body || typeof body !== "object" || Array.isArray(body)) throw new TypeError("body is required");
789
+ assertNonEmptyString(body?.escalationLevel, "body.escalationLevel");
790
+ return this.request("POST", `/runs/${encodeURIComponent(runId)}/dispute/escalate`, { ...opts, body });
791
+ }
792
+
793
+ async firstVerifiedRun(params = {}, opts = {}) {
794
+ if (!params || typeof params !== "object") throw new TypeError("params must be an object");
795
+ if (!params?.payeeAgent || typeof params.payeeAgent !== "object") throw new TypeError("params.payeeAgent is required");
796
+ assertNonEmptyString(params.payeeAgent?.publicKeyPem, "params.payeeAgent.publicKeyPem");
797
+
798
+ const stepPrefix = normalizePrefix(
799
+ opts.idempotencyPrefix,
800
+ `sdk_first_verified_run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`
801
+ );
802
+ const requestPrefix = normalizePrefix(opts.requestIdPrefix, randomRequestId());
803
+ const makeStepOpts = (step, extra = {}) => ({
804
+ signal: opts.signal,
805
+ requestId: `${requestPrefix}_${step}`,
806
+ idempotencyKey: `${stepPrefix}_${step}`,
807
+ ...extra
808
+ });
809
+
810
+ const payeeRegistration = await this.registerAgent(params.payeeAgent, makeStepOpts("register_payee"));
811
+ const payeeAgentId = payeeRegistration?.body?.agentIdentity?.agentId;
812
+ assertNonEmptyString(payeeAgentId, "payeeAgentId");
813
+
814
+ let payerRegistration = null;
815
+ let payerCredit = null;
816
+ let payerAgentId = null;
817
+ if (params.payerAgent) {
818
+ if (typeof params.payerAgent !== "object") throw new TypeError("params.payerAgent must be an object");
819
+ assertNonEmptyString(params.payerAgent?.publicKeyPem, "params.payerAgent.publicKeyPem");
820
+ payerRegistration = await this.registerAgent(params.payerAgent, makeStepOpts("register_payer"));
821
+ payerAgentId = payerRegistration?.body?.agentIdentity?.agentId ?? null;
822
+ assertNonEmptyString(payerAgentId, "payerAgentId");
823
+ }
824
+
825
+ const settlementAmountCents = params?.settlement?.amountCents;
826
+ const settlementCurrency = params?.settlement?.currency ?? "USD";
827
+ if ((settlementAmountCents !== undefined || params?.settlement?.payerAgentId) && !payerAgentId && !params?.settlement?.payerAgentId) {
828
+ throw new TypeError("params.payerAgent or params.settlement.payerAgentId is required when settlement is requested");
829
+ }
830
+
831
+ if (params.payerCredit && typeof params.payerCredit !== "object") throw new TypeError("params.payerCredit must be an object");
832
+ const payerCreditAmountCents = params?.payerCredit?.amountCents;
833
+ if (payerCreditAmountCents !== undefined && payerCreditAmountCents !== null) {
834
+ if (!Number.isFinite(payerCreditAmountCents) || payerCreditAmountCents <= 0) {
835
+ throw new TypeError("params.payerCredit.amountCents must be a positive number");
836
+ }
837
+ if (!payerAgentId) throw new TypeError("params.payerAgent is required when params.payerCredit is provided");
838
+ payerCredit = await this.creditAgentWallet(
839
+ payerAgentId,
840
+ {
841
+ amountCents: Number(payerCreditAmountCents),
842
+ currency: params?.payerCredit?.currency ?? settlementCurrency
843
+ },
844
+ makeStepOpts("credit_payer_wallet")
845
+ );
846
+ }
847
+
848
+ const runBody = { ...(params.run ?? {}) };
849
+ const settlementPayerAgentId = params?.settlement?.payerAgentId ?? payerAgentId;
850
+ if (settlementAmountCents !== undefined && settlementAmountCents !== null) {
851
+ if (!Number.isFinite(settlementAmountCents) || settlementAmountCents <= 0) {
852
+ throw new TypeError("params.settlement.amountCents must be a positive number");
853
+ }
854
+ if (!settlementPayerAgentId) {
855
+ throw new TypeError("params.settlement.payerAgentId or params.payerAgent is required when params.settlement.amountCents is set");
856
+ }
857
+ runBody.settlement = {
858
+ payerAgentId: settlementPayerAgentId,
859
+ amountCents: Number(settlementAmountCents),
860
+ currency: settlementCurrency,
861
+ ...(params?.settlement?.disputeWindowDays !== undefined ? { disputeWindowDays: params.settlement.disputeWindowDays } : {})
862
+ };
863
+ }
864
+
865
+ const runCreated = await this.createAgentRun(payeeAgentId, runBody, makeStepOpts("create_run"));
866
+ const runId = runCreated?.body?.run?.runId;
867
+ assertNonEmptyString(runId, "runId");
868
+ let prevChainHash = runCreated?.body?.run?.lastChainHash;
869
+ assertNonEmptyString(prevChainHash, "runCreated.body.run.lastChainHash");
870
+
871
+ const actor = params.actor ?? { type: "agent", id: payeeAgentId };
872
+ const runStarted = await this.appendAgentRunEvent(
873
+ payeeAgentId,
874
+ runId,
875
+ { type: "RUN_STARTED", actor, payload: params.startedPayload ?? { startedBy: "sdk.firstVerifiedRun" } },
876
+ makeStepOpts("run_started", { expectedPrevChainHash: prevChainHash })
877
+ );
878
+ prevChainHash = runStarted?.body?.run?.lastChainHash;
879
+ assertNonEmptyString(prevChainHash, "runStarted.body.run.lastChainHash");
880
+
881
+ const evidenceRef = typeof params.evidenceRef === "string" && params.evidenceRef.trim() !== ""
882
+ ? params.evidenceRef.trim()
883
+ : `evidence://${runId}/output.json`;
884
+ const runEvidenceAdded = await this.appendAgentRunEvent(
885
+ payeeAgentId,
886
+ runId,
887
+ { type: "EVIDENCE_ADDED", actor, payload: params.evidencePayload ?? { evidenceRef } },
888
+ makeStepOpts("evidence_added", { expectedPrevChainHash: prevChainHash })
889
+ );
890
+ prevChainHash = runEvidenceAdded?.body?.run?.lastChainHash;
891
+ assertNonEmptyString(prevChainHash, "runEvidenceAdded.body.run.lastChainHash");
892
+
893
+ const completedPayload = {
894
+ outputRef: typeof params.outputRef === "string" && params.outputRef.trim() !== "" ? params.outputRef.trim() : evidenceRef,
895
+ ...(params.completedPayload ?? {})
896
+ };
897
+ if (params.completedMetrics && typeof params.completedMetrics === "object") completedPayload.metrics = params.completedMetrics;
898
+ const runCompleted = await this.appendAgentRunEvent(
899
+ payeeAgentId,
900
+ runId,
901
+ { type: "RUN_COMPLETED", actor, payload: completedPayload },
902
+ makeStepOpts("run_completed", { expectedPrevChainHash: prevChainHash })
903
+ );
904
+
905
+ const run = await this.getAgentRun(payeeAgentId, runId, makeStepOpts("get_run"));
906
+ const verification = await this.getRunVerification(runId, makeStepOpts("get_verification"));
907
+
908
+ let settlement = null;
909
+ if (runBody.settlement || runCreated?.body?.settlement || runCompleted?.body?.settlement) {
910
+ settlement = await this.getRunSettlement(runId, makeStepOpts("get_settlement"));
911
+ }
912
+
913
+ return {
914
+ ids: { runId, payeeAgentId, payerAgentId },
915
+ payeeRegistration,
916
+ payerRegistration,
917
+ payerCredit,
918
+ runCreated,
919
+ runStarted,
920
+ runEvidenceAdded,
921
+ runCompleted,
922
+ run,
923
+ verification,
924
+ settlement
925
+ };
926
+ }
927
+
928
+ quoteJob(jobId, body, opts) {
929
+ assertNonEmptyString(jobId, "jobId");
930
+ if (!opts?.expectedPrevChainHash) throw new TypeError("expectedPrevChainHash is required for quoteJob");
931
+ return this.request("POST", `/jobs/${encodeURIComponent(jobId)}/quote`, { ...opts, body });
932
+ }
933
+
934
+ bookJob(jobId, body, opts) {
935
+ assertNonEmptyString(jobId, "jobId");
936
+ if (!opts?.expectedPrevChainHash) throw new TypeError("expectedPrevChainHash is required for bookJob");
937
+ return this.request("POST", `/jobs/${encodeURIComponent(jobId)}/book`, { ...opts, body });
938
+ }
939
+
940
+ appendJobEvent(jobId, body, opts) {
941
+ assertNonEmptyString(jobId, "jobId");
942
+ return this.request("POST", `/jobs/${encodeURIComponent(jobId)}/events`, { ...opts, body });
943
+ }
944
+
945
+ opsStatus(opts) {
946
+ return this.request("GET", "/ops/status", opts);
947
+ }
948
+
949
+ listPartyStatements(params, opts) {
950
+ assertNonEmptyString(params?.period, "period");
951
+ const qs = new URLSearchParams({ period: String(params.period) });
952
+ if (params.partyId) qs.set("partyId", String(params.partyId));
953
+ if (params.status) qs.set("status", String(params.status));
954
+ return this.request("GET", `/ops/party-statements?${qs.toString()}`, opts);
955
+ }
956
+
957
+ getPartyStatement(partyId, period, opts) {
958
+ assertNonEmptyString(partyId, "partyId");
959
+ assertNonEmptyString(period, "period");
960
+ return this.request("GET", `/ops/party-statements/${encodeURIComponent(partyId)}/${encodeURIComponent(period)}`, opts);
961
+ }
962
+
963
+ enqueuePayout(partyId, period, opts) {
964
+ assertNonEmptyString(partyId, "partyId");
965
+ assertNonEmptyString(period, "period");
966
+ return this.request("POST", `/ops/payouts/${encodeURIComponent(partyId)}/${encodeURIComponent(period)}/enqueue`, opts);
967
+ }
968
+
969
+ requestMonthClose(body, opts) {
970
+ if (!body || typeof body !== "object") throw new TypeError("body is required");
971
+ assertNonEmptyString(body?.month, "month");
972
+ return this.request("POST", "/ops/month-close", { ...opts, body });
973
+ }
974
+
975
+ getTenantAnalytics(tenantId, params = {}, opts) {
976
+ assertNonEmptyString(tenantId, "tenantId");
977
+ const qs = new URLSearchParams();
978
+ if (params.month) qs.set("month", String(params.month));
979
+ if (params.bucket) qs.set("bucket", String(params.bucket));
980
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
981
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
982
+ return this.request("GET", `/v1/tenants/${encodeURIComponent(tenantId)}/analytics${suffix}`, opts);
983
+ }
984
+
985
+ getTenantTrustGraph(tenantId, params = {}, opts) {
986
+ assertNonEmptyString(tenantId, "tenantId");
987
+ const qs = new URLSearchParams();
988
+ if (params.month) qs.set("month", String(params.month));
989
+ if (params.minRuns !== undefined && params.minRuns !== null) qs.set("minRuns", String(params.minRuns));
990
+ if (params.maxEdges !== undefined && params.maxEdges !== null) qs.set("maxEdges", String(params.maxEdges));
991
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
992
+ return this.request("GET", `/v1/tenants/${encodeURIComponent(tenantId)}/trust-graph${suffix}`, opts);
993
+ }
994
+
995
+ listTenantTrustGraphSnapshots(tenantId, params = {}, opts) {
996
+ assertNonEmptyString(tenantId, "tenantId");
997
+ const qs = new URLSearchParams();
998
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
999
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
1000
+ return this.request("GET", `/v1/tenants/${encodeURIComponent(tenantId)}/trust-graph/snapshots${suffix}`, opts);
1001
+ }
1002
+
1003
+ createTenantTrustGraphSnapshot(tenantId, body = {}, opts) {
1004
+ assertNonEmptyString(tenantId, "tenantId");
1005
+ if (body === null || typeof body !== "object" || Array.isArray(body)) throw new TypeError("body must be an object");
1006
+ return this.request("POST", `/v1/tenants/${encodeURIComponent(tenantId)}/trust-graph/snapshots`, { ...opts, body });
1007
+ }
1008
+
1009
+ diffTenantTrustGraph(tenantId, params = {}, opts) {
1010
+ assertNonEmptyString(tenantId, "tenantId");
1011
+ const qs = new URLSearchParams();
1012
+ if (params.baseMonth) qs.set("baseMonth", String(params.baseMonth));
1013
+ if (params.compareMonth) qs.set("compareMonth", String(params.compareMonth));
1014
+ if (params.limit !== undefined && params.limit !== null) qs.set("limit", String(params.limit));
1015
+ if (params.minRuns !== undefined && params.minRuns !== null) qs.set("minRuns", String(params.minRuns));
1016
+ if (params.maxEdges !== undefined && params.maxEdges !== null) qs.set("maxEdges", String(params.maxEdges));
1017
+ if (params.includeUnchanged !== undefined && params.includeUnchanged !== null) qs.set("includeUnchanged", String(Boolean(params.includeUnchanged)));
1018
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
1019
+ return this.request("GET", `/v1/tenants/${encodeURIComponent(tenantId)}/trust-graph/diff${suffix}`, opts);
1020
+ }
1021
+ }