@atcn/sdk 1.4.0 → 1.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.
package/README.md CHANGED
@@ -1,25 +1,41 @@
1
1
  # @atcn/sdk
2
2
 
3
- The ATCN TypeScript SDK. It includes:
3
+ The TypeScript SDK for [ATCN](https://github.com/fadnisnikhil/atcn), a cost record for AI agent jobs that anyone can verify. Use it to:
4
4
 
5
- - **Signing.** `EventSigner` signs obligation events. `buildTerms`, `termsData`, `acceptanceData` and `buildEvidenceEnvelope` build their contents.
6
- - **API clients.** `AtcnClient` is for the clearing network. `SubledgerClient` and its `CaptureQueue` record tasks, delegations and charges. `ReceiptLinkClient` is for providers.
7
- - **Offline verifiers.** `verifySubledgerDocument` checks task closures and receipts, and `verifyClosurePackage` checks obligation closure packages.
8
- - **Webhooks and response statements.** `verifyWebhook` and `signWebhook` handle webhook signatures. `buildResponseStatement`, `signStatement` and `verifyStatementSignature` handle provider response statements.
5
+ - **check signed documents offline:** task closures, provider receipts and closure packages, with no network calls;
6
+ - **record jobs** (tasks, delegations, charges, estimates, holds) through a hosted ATCN API;
7
+ - **sign** obligation events, provider response statements and webhooks.
8
+
9
+ The same package is also published as [`atcn-sdk`](https://www.npmjs.com/package/atcn-sdk).
9
10
 
10
11
  ```bash
11
12
  npm install @atcn/sdk
12
13
  ```
13
14
 
15
+ ## Verify a closure someone sent you
16
+
14
17
  ```ts
18
+ import { readFileSync } from "node:fs";
15
19
  import { verifySubledgerDocument } from "@atcn/sdk";
16
20
 
21
+ const closure = JSON.parse(readFileSync("task-closure.json", "utf8"));
22
+ const trustedKeys = JSON.parse(readFileSync("keys.json", "utf8")).items;
23
+
17
24
  const report = verifySubledgerDocument(closure, { trustedKeys });
18
25
  console.log(report.valid ? "VALID" : report.checks.filter((c) => !c.ok));
19
26
  ```
20
27
 
21
- The API clients need an ATCN API at `baseUrl`. The hosted API is not open for signup yet. Everything else works offline.
28
+ `report.checks` lists every check by name with its details, so a failure says exactly what doesn't add up. To try it without writing a closure first, run `npx @atcn/local-runner demo` and point this at the files it prints.
29
+
30
+ ## What's inside
31
+
32
+ - **Offline verifiers.** `verifySubledgerDocument` checks task closures and receipts, and `verifyClosurePackage` checks obligation closure packages. Clearing verdicts are checked by `verifyClearingVerdict` in [`@atcn/core`](https://www.npmjs.com/package/@atcn/core).
33
+ - **Signed estimates and outcomes.** `buildExpectationStatement` and `signExpectation` let a provider sign its estimate or hold; `buildOutcomeStatement` and `signOutcomeStatement` let it sign how a task ended.
34
+ - **API clients.** `SubledgerClient` and its `CaptureQueue` record tasks, delegations and charges. `AtcnClient` is for obligations, and `ReceiptLinkClient` is for providers. They need an ATCN API at `baseUrl`; the hosted API is not open for signup yet.
35
+ - **Signing.** `EventSigner` signs obligation events. `buildTerms`, `termsData`, `acceptanceData` and `buildEvidenceEnvelope` build their contents.
36
+ - **Webhooks and response statements.** `verifyWebhook` and `signWebhook` handle webhook signatures. `buildResponseStatement`, `signStatement` and `verifyStatementSignature` handle provider response statements.
37
+ - **Agent traces.** `traceFromOtelSpans` turns an OpenTelemetry GenAI span export into an ATCN trace.
22
38
 
23
- `npx atcn init` records whether this machine shares anonymous usage metrics. Reporting is off by default and needs `ATCN_USAGE_URL`; see [docs/USAGE_DATA.md](../../docs/USAGE_DATA.md).
39
+ `npx atcn init` records whether this machine shares anonymous usage metrics. Reporting is off by default and needs `ATCN_USAGE_URL`; see [usage data](https://github.com/fadnisnikhil/atcn/blob/main/docs/USAGE_DATA.md).
24
40
 
25
- Part of [ATCN](../../README.md). Apache-2.0.
41
+ Source, docs and examples: [github.com/fadnisnikhil/atcn](https://github.com/fadnisnikhil/atcn). Apache-2.0.
@@ -1,4 +1,4 @@
1
- import { type Deliverable, type EvidenceEnvelope, type ObligationTerms, type PolicyTemplate, type SkillRef } from "@atcn/schema";
1
+ import { type Deliverable, type EvidenceEnvelope, type ObligationTerms, type PolicyTemplate, type Pricing, type RefundTerms, type SkillRef, type WitnessPolicy } from "@atcn/schema";
2
2
  export interface TermsInput {
3
3
  parentObligationId?: string | null;
4
4
  principalId: string;
@@ -24,6 +24,12 @@ export interface TermsInput {
24
24
  verifierAgentIds?: string[];
25
25
  /** The skill the counterparty performs (for A2A, the AgentSkill id). Produces schema_version 1.1 terms. */
26
26
  skill?: SkillRef;
27
+ /** Usage prices for the usage_cost verifier. Produces schema_version 1.2 terms. */
28
+ pricing?: Pricing;
29
+ /** What happens on failure or timeout, and the post-settlement refund budget. Produces schema_version 1.2 terms. */
30
+ refundTerms?: RefundTerms;
31
+ /** Independent witnesses the witness_quorum verifier requires. Produces schema_version 1.2 terms. */
32
+ witnessPolicy?: WitnessPolicy;
27
33
  }
28
34
  /** Builds version-1 obligation terms with sensible defaults (7-day deadline, 1-day offer window). */
29
35
  export declare function buildTerms(input: TermsInput): ObligationTerms;
package/dist/builders.js CHANGED
@@ -4,7 +4,7 @@ const DAY_MS = 24 * 3600 * 1000;
4
4
  export function buildTerms(input) {
5
5
  const now = Date.now();
6
6
  return {
7
- schema_version: input.skill ? "1.1" : "1.0",
7
+ schema_version: input.pricing || input.refundTerms || input.witnessPolicy ? "1.2" : input.skill ? "1.1" : "1.0",
8
8
  obligation_id: newId("obligation"),
9
9
  terms_version: 1,
10
10
  parent_obligation_id: input.parentObligationId ?? null,
@@ -28,6 +28,9 @@ export function buildTerms(input) {
28
28
  verifier_agent_ids: input.verifierAgentIds ?? [],
29
29
  issued_at: new Date(now).toISOString(),
30
30
  ...(input.skill ? { skill: input.skill } : {}),
31
+ ...(input.pricing ? { pricing: input.pricing } : {}),
32
+ ...(input.refundTerms ? { refund_terms: input.refundTerms } : {}),
33
+ ...(input.witnessPolicy ? { witness_policy: input.witnessPolicy } : {}),
31
34
  };
32
35
  }
33
36
  /** Data for obligation.created / obligation.offered / obligation.amended events. */
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ClearingDecision, EventPayload, PublicKeyRecord, Signed } from "@atcn/schema";
1
+ import type { ClearingDecision, ClearingVerdict, ClosurePackage, EventPayload, PublicKeyRecord, Signed } from "@atcn/schema";
2
2
  export declare class AtcnApiError extends Error {
3
3
  readonly status: number;
4
4
  readonly code: string;
@@ -9,7 +9,7 @@ export declare class AtcnApiError extends Error {
9
9
  constructor(status: number, code: string, message: string, reason: string | undefined, retryable: boolean, correlationId: string | undefined, details: unknown);
10
10
  }
11
11
  /** Must equal this package's version in package.json (checked by a test). */
12
- export declare const SDK_VERSION = "1.4.0";
12
+ export declare const SDK_VERSION = "1.5.0";
13
13
  /** Names the SDK and its version on every request, so the API operator can count SDK versions in use. Nothing else is sent. */
14
14
  export declare const SDK_HEADER = "atcn-sdk";
15
15
  export interface ClientOptions {
@@ -105,6 +105,14 @@ export declare class AtcnClient {
105
105
  listDecisions(obligationId: string): Promise<{
106
106
  items: ClearingDecision[];
107
107
  }>;
108
+ /** The decision in effect as a signed, record-only verdict, with the closure package it was read from (verifyClearingVerdict). */
109
+ clearingVerdict(obligationId: string, escrow?: {
110
+ rail: string;
111
+ escrow_ref: string;
112
+ }): Promise<{
113
+ verdict: ClearingVerdict;
114
+ closure_package: ClosurePackage;
115
+ }>;
108
116
  finalize(decisionId: string, idempotencyKey?: string): Promise<Json>;
109
117
  openDispute(decisionId: string, signed: SignedEvent): Promise<Json>;
110
118
  reviewDispute(disputeId: string, signed: SignedEvent): Promise<Json>;
@@ -131,7 +139,7 @@ export declare class AtcnClient {
131
139
  }>;
132
140
  exportClosurePackage(obligationId: string): Promise<{
133
141
  payload: {
134
- package_version: "1.0";
142
+ package_version: "1.0" | "1.1";
135
143
  generated_at: string;
136
144
  root_obligation_id: string;
137
145
  requested_obligation_id: string;
@@ -140,7 +148,7 @@ export declare class AtcnClient {
140
148
  parent_obligation_id: string | null;
141
149
  redacted: boolean;
142
150
  effective_terms: {
143
- schema_version: "1.0" | "1.1";
151
+ schema_version: "1.0" | "1.1" | "1.2";
144
152
  obligation_id: string;
145
153
  terms_version: number;
146
154
  parent_obligation_id: string | null;
@@ -186,6 +194,33 @@ export declare class AtcnClient {
186
194
  skill_id: string;
187
195
  agent_card_url?: string | undefined;
188
196
  } | undefined;
197
+ pricing?: {
198
+ rates: {
199
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
200
+ price_numerator: number;
201
+ price_denominator: number;
202
+ model?: {
203
+ provider: string;
204
+ name: string;
205
+ } | undefined;
206
+ tool_name?: string | undefined;
207
+ }[];
208
+ tolerance_bps: number;
209
+ fixed_minor?: number | undefined;
210
+ } | undefined;
211
+ refund_terms?: {
212
+ on_failure: "dispute" | "refund";
213
+ on_timeout: "dispute" | "refund";
214
+ after_settlement: {
215
+ cap_minor: number;
216
+ window_seconds: number;
217
+ };
218
+ } | undefined;
219
+ witness_policy?: {
220
+ min_independent_witnesses: number;
221
+ independence: "distinct_verified_domain";
222
+ witness_agent_ids?: string[] | undefined;
223
+ } | undefined;
189
224
  } | null;
190
225
  effective_terms_digest: string | null;
191
226
  state: string;
@@ -228,7 +263,7 @@ export declare class AtcnClient {
228
263
  task_type: string;
229
264
  description: string;
230
265
  evidence_admissibility: {
231
- allowed_producers: ("issuer" | "counterparty" | "verifier")[];
266
+ allowed_producers: ("issuer" | "counterparty" | "verifier" | "witness")[];
232
267
  require_digest_match: boolean;
233
268
  };
234
269
  required_evidence: string[];
@@ -313,7 +348,7 @@ export declare class AtcnClient {
313
348
  amount_minor: number;
314
349
  outcome: "insufficient_evidence" | "disputed" | "accepted" | "rejected";
315
350
  reasons: {
316
- code: "invalid_evidence" | "missing_evidence" | "failed_criteria" | "verifier_unavailable" | "probabilistic_review_required" | "partial_not_permitted" | "passed" | "dispute_amended" | "dispute_upheld";
351
+ code: "invalid_evidence" | "missing_evidence" | "failed_criteria" | "verifier_unavailable" | "probabilistic_review_required" | "partial_not_permitted" | "passed" | "dispute_amended" | "dispute_upheld" | "failure_terms_dispute" | "conflicting_attestations" | "witness_quorum_not_met";
317
352
  check_id?: string | undefined;
318
353
  evidence_type?: string | undefined;
319
354
  detail?: string | undefined;
@@ -366,7 +401,7 @@ export declare class AtcnClient {
366
401
  adapter: "manual" | "sandbox" | "stripe";
367
402
  idempotency_key: string;
368
403
  expires_at: string;
369
- status: "unknown" | "failed" | "refunded" | "settled" | "returned" | "submitted" | "processing";
404
+ status: "unknown" | "pending_finality" | "failed" | "refunded" | "settled" | "returned" | "submitted" | "processing";
370
405
  created_at: string;
371
406
  }[];
372
407
  settlement_events: {
@@ -376,12 +411,22 @@ export declare class AtcnClient {
376
411
  provider_event_id: string;
377
412
  provider_reference: string;
378
413
  provider_status: string;
379
- normalized_status: "unknown" | "failed" | "refunded" | "settled" | "returned" | "submitted" | "processing";
414
+ normalized_status: "unknown" | "pending_finality" | "failed" | "refunded" | "settled" | "returned" | "submitted" | "processing";
380
415
  currency: string;
381
416
  amount_minor: number;
382
417
  raw_json: string;
383
418
  reported_at: string;
384
419
  }[];
420
+ attestations?: {
421
+ evidence_id: string;
422
+ content: string;
423
+ }[] | undefined;
424
+ attestation_conflicts?: {
425
+ kind: "disputed" | "equivocation" | "disagreement" | "execution_mismatch";
426
+ subject: string;
427
+ attestation_digests: string[];
428
+ signers: string[];
429
+ }[] | undefined;
385
430
  };
386
431
  signature: {
387
432
  key_id: string;
package/dist/client.js CHANGED
@@ -16,7 +16,7 @@ export class AtcnApiError extends Error {
16
16
  }
17
17
  }
18
18
  /** Must equal this package's version in package.json (checked by a test). */
19
- export const SDK_VERSION = "1.4.0";
19
+ export const SDK_VERSION = "1.5.0";
20
20
  /** Names the SDK and its version on every request, so the API operator can count SDK versions in use. Nothing else is sent. */
21
21
  export const SDK_HEADER = "atcn-sdk";
22
22
  /** Thin HTTP client for the ATCN API. Every mutating call carries an Idempotency-Key. */
@@ -126,6 +126,12 @@ export class AtcnClient {
126
126
  listDecisions(obligationId) {
127
127
  return this.request("GET", `/v1/obligations/${obligationId}/decisions`);
128
128
  }
129
+ /** The decision in effect as a signed, record-only verdict, with the closure package it was read from (verifyClearingVerdict). */
130
+ clearingVerdict(obligationId, escrow) {
131
+ return this.request("GET", `/v1/obligations/${obligationId}/verdict`, undefined, {
132
+ query: { rail: escrow?.rail, escrow_ref: escrow?.escrow_ref },
133
+ });
134
+ }
129
135
  finalize(decisionId, idempotencyKey) {
130
136
  return this.request("POST", `/v1/decisions/${decisionId}/finalize`, undefined, { idempotencyKey });
131
137
  }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  export { EventSigner, type SignerIdentity } from "./signer.js";
2
2
  export { buildTerms, termsData, acceptanceData, buildEvidenceEnvelope, type TermsInput, type EnvelopeInput } from "./builders.js";
3
3
  export { AtcnClient, AtcnApiError, SDK_HEADER, SDK_VERSION, type ClientOptions, type RequestOptions } from "./client.js";
4
- export { verifyClosurePackage, REFERENCE_POLICIES, CODE_CHANGE_POLICY_V1, CODE_CHANGE_POLICY_V1_1, CODE_CHANGE_SUBTASK_POLICY_V1 } from "@atcn/core";
4
+ export { verifyClosurePackage, REFERENCE_POLICIES, CODE_CHANGE_POLICY_V1, CODE_CHANGE_POLICY_V1_1, CODE_CHANGE_SUBTASK_POLICY_V1, AGENT_USAGE_POLICY_V1 } from "@atcn/core";
5
5
  export { SubledgerClient, ReceiptLinkClient, CaptureQueue, ext, stableKey, type CaptureOperation, type CaptureQueueOptions, type FlushResult } from "./subledger.js";
6
6
  export { verifySubledgerDocument, buildResponseStatement, signStatement, verifyStatementSignature } from "@atcn/subledger";
7
+ export { buildExpectationStatement, signExpectation, verifyExpectationSignature, type ExpectationStatement, type ExpectationStatementInput } from "@atcn/subledger";
8
+ export { buildOutcomeStatement, signOutcomeStatement, verifyOutcomeSignature, type EvidenceRef, type OutcomeStatement, type SignedClaimType } from "@atcn/subledger";
7
9
  export { verifyWebhook, signWebhook, WEBHOOK_SIGNATURE_HEADER, generateKeyPair, digestOf, sha256Digest, canonicalize, executionBinding } from "@atcn/schema";
10
+ export { traceDigest, summarizeTrace, traceProblems, checkTrace, expectedCostFromUsage, allowedDifference, usageCostDetails, type AgentTrace, type UsageSummary, type Pricing, type ExecutionDescriptor } from "@atcn/schema";
11
+ export { traceFromOtelSpans, OTEL_GENAI_CONVENTIONS, A2A_TASK_ID_ATTRIBUTE, type OtlpTraceExport, type OtelImportResult, type SkippedSpan } from "./otel.js";
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  export { EventSigner } from "./signer.js";
2
2
  export { buildTerms, termsData, acceptanceData, buildEvidenceEnvelope } from "./builders.js";
3
3
  export { AtcnClient, AtcnApiError, SDK_HEADER, SDK_VERSION } from "./client.js";
4
- export { verifyClosurePackage, REFERENCE_POLICIES, CODE_CHANGE_POLICY_V1, CODE_CHANGE_POLICY_V1_1, CODE_CHANGE_SUBTASK_POLICY_V1 } from "@atcn/core";
4
+ export { verifyClosurePackage, REFERENCE_POLICIES, CODE_CHANGE_POLICY_V1, CODE_CHANGE_POLICY_V1_1, CODE_CHANGE_SUBTASK_POLICY_V1, AGENT_USAGE_POLICY_V1 } from "@atcn/core";
5
5
  export { SubledgerClient, ReceiptLinkClient, CaptureQueue, ext, stableKey } from "./subledger.js";
6
6
  export { verifySubledgerDocument, buildResponseStatement, signStatement, verifyStatementSignature } from "@atcn/subledger";
7
+ export { buildExpectationStatement, signExpectation, verifyExpectationSignature } from "@atcn/subledger";
8
+ export { buildOutcomeStatement, signOutcomeStatement, verifyOutcomeSignature } from "@atcn/subledger";
7
9
  export { verifyWebhook, signWebhook, WEBHOOK_SIGNATURE_HEADER, generateKeyPair, digestOf, sha256Digest, canonicalize, executionBinding } from "@atcn/schema";
10
+ export { traceDigest, summarizeTrace, traceProblems, checkTrace, expectedCostFromUsage, allowedDifference, usageCostDetails } from "@atcn/schema";
11
+ export { traceFromOtelSpans, OTEL_GENAI_CONVENTIONS, A2A_TASK_ID_ATTRIBUTE } from "./otel.js";
package/dist/otel.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { type AgentTrace, type ExecutionBinding } from "@atcn/schema";
2
+ /**
3
+ * The OpenTelemetry GenAI conventions this mapping was written against. They are at "Development" status, so names may
4
+ * change; every attribute name used below is in this file only.
5
+ */
6
+ export declare const OTEL_GENAI_CONVENTIONS = "OpenTelemetry semantic conventions 1.41.0, GenAI spans";
7
+ /** Not an OpenTelemetry attribute: set it on invoke_agent spans that call another agent over A2A, with the A2A task id. */
8
+ export declare const A2A_TASK_ID_ATTRIBUTE = "atcn.a2a.task_id";
9
+ interface OtlpAnyValue {
10
+ stringValue?: string;
11
+ intValue?: string | number;
12
+ doubleValue?: number;
13
+ boolValue?: boolean;
14
+ }
15
+ interface OtlpSpan {
16
+ spanId?: string;
17
+ name?: string;
18
+ startTimeUnixNano?: string | number;
19
+ endTimeUnixNano?: string | number;
20
+ attributes?: {
21
+ key: string;
22
+ value?: OtlpAnyValue;
23
+ }[];
24
+ }
25
+ /** An OTLP JSON trace export: resourceSpans[].scopeSpans[].spans[]. */
26
+ export interface OtlpTraceExport {
27
+ resourceSpans?: {
28
+ scopeSpans?: {
29
+ spans?: OtlpSpan[];
30
+ }[];
31
+ }[];
32
+ }
33
+ export interface SkippedSpan {
34
+ span_id: string;
35
+ name: string;
36
+ reason: string;
37
+ }
38
+ export interface OtelImportResult {
39
+ trace: AgentTrace;
40
+ /** Spans that did not become steps, with the reason, so nothing is dropped silently. */
41
+ skipped: SkippedSpan[];
42
+ }
43
+ /**
44
+ * Builds an AgentTrace for one run from an OTLP JSON export of GenAI spans. Steps are ordered by start time, then span
45
+ * id. Spans that are not model calls, tool calls or A2A calls are returned in `skipped` with the reason.
46
+ */
47
+ export declare function traceFromOtelSpans(otlp: OtlpTraceExport, execution: ExecutionBinding): OtelImportResult;
48
+ export {};
package/dist/otel.js ADDED
@@ -0,0 +1,116 @@
1
+ import { AgentTraceSchema, TRACE_VERSION } from "@atcn/schema";
2
+ /**
3
+ * The OpenTelemetry GenAI conventions this mapping was written against. They are at "Development" status, so names may
4
+ * change; every attribute name used below is in this file only.
5
+ */
6
+ export const OTEL_GENAI_CONVENTIONS = "OpenTelemetry semantic conventions 1.41.0, GenAI spans";
7
+ const MODEL_OPERATIONS = ["chat", "generate_content", "text_completion", "embeddings"];
8
+ /** Not an OpenTelemetry attribute: set it on invoke_agent spans that call another agent over A2A, with the A2A task id. */
9
+ export const A2A_TASK_ID_ATTRIBUTE = "atcn.a2a.task_id";
10
+ function attributes(span) {
11
+ const map = new Map();
12
+ for (const a of span.attributes ?? []) {
13
+ const v = a.value ?? {};
14
+ if (v.stringValue !== undefined)
15
+ map.set(a.key, v.stringValue);
16
+ else if (v.intValue !== undefined)
17
+ map.set(a.key, Number(v.intValue));
18
+ else if (v.doubleValue !== undefined)
19
+ map.set(a.key, v.doubleValue);
20
+ else if (v.boolValue !== undefined)
21
+ map.set(a.key, v.boolValue);
22
+ }
23
+ return map;
24
+ }
25
+ function text(attrs, key) {
26
+ const value = attrs.get(key);
27
+ return typeof value === "string" && value.length > 0 ? value : undefined;
28
+ }
29
+ function count(attrs, key) {
30
+ const value = attrs.get(key);
31
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
32
+ }
33
+ function isoFromNanos(nanos) {
34
+ if (nanos === undefined)
35
+ return undefined;
36
+ return new Date(Number(BigInt(nanos) / 1000000n)).toISOString();
37
+ }
38
+ function nanosOf(span) {
39
+ return span.startTimeUnixNano === undefined ? 0n : BigInt(span.startTimeUnixNano);
40
+ }
41
+ function stepFromSpan(span) {
42
+ const attrs = attributes(span);
43
+ const operation = text(attrs, "gen_ai.operation.name");
44
+ if (operation === undefined)
45
+ return "no gen_ai.operation.name";
46
+ const started_at = isoFromNanos(span.startTimeUnixNano);
47
+ const ended_at = isoFromNanos(span.endTimeUnixNano);
48
+ if (started_at === undefined || ended_at === undefined)
49
+ return "missing start or end time";
50
+ if (MODEL_OPERATIONS.includes(operation)) {
51
+ const provider = text(attrs, "gen_ai.provider.name");
52
+ const name = text(attrs, "gen_ai.response.model") ?? text(attrs, "gen_ai.request.model");
53
+ if (provider === undefined)
54
+ return "model call without gen_ai.provider.name";
55
+ if (name === undefined)
56
+ return "model call without gen_ai.response.model or gen_ai.request.model";
57
+ const input = count(attrs, "gen_ai.usage.input_tokens");
58
+ const output = count(attrs, "gen_ai.usage.output_tokens");
59
+ const cached = count(attrs, "gen_ai.usage.cache_read.input_tokens");
60
+ const upstream = text(attrs, "gen_ai.response.id");
61
+ return {
62
+ kind: "model_call",
63
+ started_at,
64
+ ended_at,
65
+ model: { provider, name },
66
+ ...(input !== undefined || output !== undefined
67
+ ? { usage: { input_tokens: input ?? 0, output_tokens: output ?? 0, ...(cached !== undefined ? { cache_read_input_tokens: cached } : {}) } }
68
+ : {}),
69
+ ...(upstream !== undefined ? { upstream_ref: upstream } : {}),
70
+ };
71
+ }
72
+ if (operation === "execute_tool") {
73
+ const name = text(attrs, "gen_ai.tool.name");
74
+ if (name === undefined)
75
+ return "tool call without gen_ai.tool.name";
76
+ const upstream = text(attrs, "gen_ai.tool.call.id");
77
+ return { kind: "tool_call", started_at, ended_at, tool: { name }, ...(upstream !== undefined ? { upstream_ref: upstream } : {}) };
78
+ }
79
+ if (operation === "invoke_agent") {
80
+ const taskId = text(attrs, A2A_TASK_ID_ATTRIBUTE);
81
+ if (taskId === undefined)
82
+ return `invoke_agent without ${A2A_TASK_ID_ATTRIBUTE}`;
83
+ const agentId = text(attrs, "gen_ai.agent.id");
84
+ return { kind: "a2a_call", started_at, ended_at, remote: { ...(agentId !== undefined ? { agent_id: agentId } : {}), task_id: taskId } };
85
+ }
86
+ return `operation ${operation} is not mapped`;
87
+ }
88
+ /**
89
+ * Builds an AgentTrace for one run from an OTLP JSON export of GenAI spans. Steps are ordered by start time, then span
90
+ * id. Spans that are not model calls, tool calls or A2A calls are returned in `skipped` with the reason.
91
+ */
92
+ export function traceFromOtelSpans(otlp, execution) {
93
+ const spans = (otlp.resourceSpans ?? []).flatMap((r) => (r.scopeSpans ?? []).flatMap((s) => s.spans ?? []));
94
+ const ordered = [...spans].sort((a, b) => {
95
+ const at = nanosOf(a);
96
+ const bt = nanosOf(b);
97
+ if (at !== bt)
98
+ return at < bt ? -1 : 1;
99
+ const aid = a.spanId ?? "";
100
+ const bid = b.spanId ?? "";
101
+ return aid < bid ? -1 : aid > bid ? 1 : 0;
102
+ });
103
+ const steps = [];
104
+ const skipped = [];
105
+ for (const span of ordered) {
106
+ const step = stepFromSpan(span);
107
+ if (typeof step === "string")
108
+ skipped.push({ span_id: span.spanId ?? "", name: span.name ?? "", reason: step });
109
+ else
110
+ steps.push({ seq: steps.length, ...step });
111
+ }
112
+ if (steps.length === 0)
113
+ throw new Error("the export has no GenAI model, tool or A2A spans");
114
+ const trace = AgentTraceSchema.parse({ trace_version: TRACE_VERSION, execution, steps });
115
+ return { trace, skipped };
116
+ }
@@ -1,11 +1,24 @@
1
1
  import type { z } from "zod";
2
- import { type AllocationInputSchema, type AllocationRuleInputSchema, type AttestableField, type Correction, type DelegationEventInputSchema, type DelegationInputSchema, type EvidenceRef, type FinancialEventInput, type OperatorKeyRecord, type ProviderInputSchema, type ResponseType, type SignedClosure, type SignedReceipt, type TaskInputSchema } from "@atcn/subledger";
2
+ import { type AllocationInputSchema, type AllocationRuleInputSchema, type AttestableField, type CaptureGapInput, type Correction, type DelegationEventInputSchema, type DelegationInputSchema, type EvidenceRef, type ExpectationIssuer, type FinancialEventInput, type ImportField, type OperatorKeyRecord, type ProviderInputSchema, type RailAttestation, type ResponseType, type SignedClosure, type SignedReceipt, type TaskInputSchema } from "@atcn/subledger";
3
3
  import { type AttestationRef, type ExecutionBinding } from "@atcn/schema";
4
4
  import { AtcnClient, type ClientOptions } from "./client.js";
5
5
  type Json = Record<string, unknown>;
6
6
  type Opts = {
7
7
  idempotencyKey?: string;
8
8
  };
9
+ /**
10
+ * How the hosted importer reads a CSV export: key_columns is a comma-separated list of columns that identify a row,
11
+ * map names the export's column for an import field, and minor_digits is the decimal places of amount_major (default 2).
12
+ */
13
+ type CsvImportOptions = {
14
+ kind?: "charge" | "invoice" | "estimate" | "hold";
15
+ source?: string;
16
+ key_columns?: string;
17
+ currency?: string;
18
+ issued_by?: ExpectationIssuer;
19
+ map?: Partial<Record<ImportField, string>>;
20
+ minor_digits?: number;
21
+ };
9
22
  /** Caller-supplied references address records before their server IDs are known: ext("job-42"). */
10
23
  export declare const ext: (externalRef: string) => string;
11
24
  /** Idempotency key derived from the caller's stable references; long references are hashed to fit 8-200 chars. */
@@ -43,7 +56,20 @@ export declare class SubledgerClient {
43
56
  match_id: string | null;
44
57
  exception_ids: string[];
45
58
  }>;
46
- importCsv(csv: string, opts?: Opts): Promise<{
59
+ recordRailAttestation(input: {
60
+ attestation: RailAttestation;
61
+ source: string;
62
+ match?: Record<string, string>;
63
+ event_date?: string;
64
+ }, opts?: Opts): Promise<{
65
+ financial_event: Json;
66
+ deduplicated: boolean;
67
+ attributed?: boolean;
68
+ attribution: Json | null;
69
+ match_id: string | null;
70
+ exception_ids: string[];
71
+ }>;
72
+ importCsv(csv: string, opts?: Opts & CsvImportOptions): Promise<{
47
73
  imported: number;
48
74
  deduplicated: number;
49
75
  rejected: number;
@@ -80,7 +106,7 @@ export declare class SubledgerClient {
80
106
  }, opts?: Opts): Promise<Json>;
81
107
  reportCaptureGap(taskId: string, gap: {
82
108
  delegation_id?: string | null;
83
- kind: "capture_failed" | "queue_overflow" | "provider_undisclosed" | "manual_gap";
109
+ kind: CaptureGapInput["kind"];
84
110
  detail: string;
85
111
  }, opts?: Opts): Promise<Json>;
86
112
  closeTask(taskId: string, opts?: Opts): Promise<{
@@ -90,7 +116,7 @@ export declare class SubledgerClient {
90
116
  getClosure(taskId: string, version?: number): Promise<{
91
117
  payload: {
92
118
  document_type: "atcn.subledger.closure";
93
- schema_version: "1.4" | "1.2" | "1.3";
119
+ schema_version: "1.2" | "1.3" | "1.4" | "1.5";
94
120
  closure_id: string;
95
121
  version: number;
96
122
  previous_closure_id: string | null;
@@ -112,6 +138,7 @@ export declare class SubledgerClient {
112
138
  scope_ref: string | null;
113
139
  retrospective: boolean;
114
140
  created_at: string;
141
+ estimate_tolerance_bps?: number | undefined;
115
142
  };
116
143
  delegations: {
117
144
  delegation_id: string;
@@ -145,6 +172,11 @@ export declare class SubledgerClient {
145
172
  name: string;
146
173
  version: string;
147
174
  } | undefined;
175
+ additional_models?: {
176
+ provider: string;
177
+ name: string;
178
+ version: string;
179
+ }[] | undefined;
148
180
  config_digest?: string | undefined;
149
181
  };
150
182
  protocol?: {
@@ -158,13 +190,40 @@ export declare class SubledgerClient {
158
190
  agent_card_url?: string | undefined;
159
191
  } | undefined;
160
192
  } | undefined;
193
+ pricing?: {
194
+ rates: {
195
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
196
+ price_numerator: number;
197
+ price_denominator: number;
198
+ model?: {
199
+ provider: string;
200
+ name: string;
201
+ } | undefined;
202
+ tool_name?: string | undefined;
203
+ }[];
204
+ tolerance_bps: number;
205
+ fixed_minor?: number | undefined;
206
+ } | undefined;
207
+ refund_terms?: {
208
+ on_failure: "dispute" | "refund";
209
+ on_timeout: "dispute" | "refund";
210
+ after_settlement: {
211
+ cap_minor: number;
212
+ window_seconds: number;
213
+ };
214
+ } | undefined;
215
+ witness_policy?: {
216
+ min_independent_witnesses: number;
217
+ independence: "distinct_verified_domain";
218
+ witness_provider_ids?: string[] | undefined;
219
+ } | undefined;
161
220
  }[];
162
221
  delivery_claims: {
163
222
  event_id: string;
164
223
  delegation_id: string;
165
224
  type: "acceptance" | "completion" | "partial_completion" | "cancellation" | "provider_failure" | "terms_update" | "correction";
166
225
  asserted_by: "provider" | "buyer" | "clearing_policy" | "dispute_reviewer" | "clearing_network";
167
- assurance: ("expired" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked")[];
226
+ assurance: ("expired" | "rail_attested" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked" | "gateway_signed")[];
168
227
  note: string | null;
169
228
  evidence: {
170
229
  uri: string;
@@ -176,11 +235,35 @@ export declare class SubledgerClient {
176
235
  retrospective: boolean;
177
236
  occurred_at: string;
178
237
  recorded_at: string;
238
+ usage?: {
239
+ trace_digest: string;
240
+ summary: {
241
+ models: {
242
+ provider: string;
243
+ name: string;
244
+ calls: number;
245
+ input_tokens: number;
246
+ output_tokens: number;
247
+ cache_read_input_tokens: number;
248
+ }[];
249
+ tools: {
250
+ name: string;
251
+ calls: number;
252
+ }[];
253
+ a2a_calls: number;
254
+ };
255
+ } | undefined;
256
+ signer?: {
257
+ provider_id: string;
258
+ binding_id: string;
259
+ key_id: string;
260
+ value: string;
261
+ } | undefined;
179
262
  }[];
180
263
  financial_events: {
181
264
  record: {
182
265
  financial_event_id: string;
183
- type: "quote" | "invoice" | "charge" | "payment_reported" | "refund" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
266
+ type: "refund" | "charge" | "invoice" | "estimate" | "hold" | "quote" | "payment_reported" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
184
267
  source: string;
185
268
  source_event_id: string;
186
269
  provider_id: string | null;
@@ -190,7 +273,7 @@ export declare class SubledgerClient {
190
273
  event_date: string;
191
274
  imported_at: string;
192
275
  provider_status: string | null;
193
- normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "failed" | "refunded" | "reversed" | "void";
276
+ normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "pending_finality" | "failed" | "refunded" | "reversed" | "void";
194
277
  evidence: {
195
278
  uri: string;
196
279
  digest: string | null;
@@ -210,6 +293,29 @@ export declare class SubledgerClient {
210
293
  rate_denominator: number;
211
294
  } | null;
212
295
  reason: string | null;
296
+ skill?: {
297
+ namespace: string;
298
+ skill_id: string;
299
+ agent_card_url?: string | undefined;
300
+ } | undefined;
301
+ expectation?: {
302
+ issued_by: "agent" | "gateway" | "operator";
303
+ source_ref: string | null;
304
+ basis: string | null;
305
+ expires_at: string | null;
306
+ supersedes: string | null;
307
+ hold_status?: "expired" | "open" | "captured" | "released" | undefined;
308
+ signer?: {
309
+ provider_id: string;
310
+ binding_id: string;
311
+ key_id: string;
312
+ value: string;
313
+ } | undefined;
314
+ } | undefined;
315
+ rail_attestation?: {
316
+ scheme: string;
317
+ record: Record<string, unknown>;
318
+ } | undefined;
213
319
  };
214
320
  event_digest: string;
215
321
  attributed_to: string;
@@ -348,7 +454,7 @@ export declare class SubledgerClient {
348
454
  receipt_revision: number;
349
455
  issuer_operator_id: string;
350
456
  response_type: "acknowledge_view" | "acknowledge_delivery" | "submit_evidence" | "propose_correction" | "signed_attestation";
351
- fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status")[];
457
+ fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage")[];
352
458
  note: string | null;
353
459
  evidence: {
354
460
  uri: string;
@@ -356,7 +462,7 @@ export declare class SubledgerClient {
356
462
  evidence_type: string;
357
463
  }[];
358
464
  corrections: {
359
- field: "delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status";
465
+ field: "delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage";
360
466
  proposed_value: string;
361
467
  reason: string;
362
468
  }[];
@@ -371,6 +477,7 @@ export declare class SubledgerClient {
371
477
  attestation_digest: string;
372
478
  reason: string;
373
479
  }[] | undefined;
480
+ role?: "witness" | undefined;
374
481
  };
375
482
  statement_digest: string;
376
483
  provider_signature: {
@@ -378,7 +485,7 @@ export declare class SubledgerClient {
378
485
  binding_id: string;
379
486
  value: string;
380
487
  } | null;
381
- assurance: ("expired" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked")[];
488
+ assurance: ("expired" | "rail_attested" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked" | "gateway_signed")[];
382
489
  decision: {
383
490
  status: "accepted" | "rejected";
384
491
  reason: string;
@@ -422,6 +529,81 @@ export declare class SubledgerClient {
422
529
  decision_id: string | null;
423
530
  decision_digest: string | null;
424
531
  }[] | undefined;
532
+ usage_checks?: {
533
+ delegation_id: string;
534
+ currency: string;
535
+ expected_minor: number | null;
536
+ lines: {
537
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
538
+ units: number;
539
+ cost_minor: number;
540
+ model?: {
541
+ provider: string;
542
+ name: string;
543
+ } | undefined;
544
+ tool_name?: string | undefined;
545
+ }[];
546
+ billed_minor: number;
547
+ difference_minor: number | null;
548
+ allowed_difference_minor: number | null;
549
+ within_tolerance: boolean | null;
550
+ unpriced: string[];
551
+ trace_digests: string[];
552
+ assurance: ("expired" | "rail_attested" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked" | "gateway_signed")[];
553
+ }[] | undefined;
554
+ expectation_report?: {
555
+ currency: string;
556
+ task: {
557
+ estimated_minor: number | null;
558
+ held_minor: number;
559
+ actual_minor: number;
560
+ variance_vs_estimate_minor: number | null;
561
+ variance_vs_estimate_bps: number | null;
562
+ variance_vs_hold_minor: number | null;
563
+ variance_vs_hold_bps: number | null;
564
+ unestimated_minor: number;
565
+ };
566
+ nodes: {
567
+ estimated_minor: number | null;
568
+ held_minor: number;
569
+ actual_minor: number;
570
+ variance_vs_estimate_minor: number | null;
571
+ variance_vs_estimate_bps: number | null;
572
+ variance_vs_hold_minor: number | null;
573
+ variance_vs_hold_bps: number | null;
574
+ node_id: string;
575
+ estimate_event_id: string | null;
576
+ }[];
577
+ records: {
578
+ financial_event_id: string;
579
+ node_id: string;
580
+ type: "estimate" | "hold";
581
+ issued_by: "agent" | "gateway" | "operator";
582
+ status: "superseded" | "current" | "not_latest" | "after_charge" | "other_currency";
583
+ hold_status: "expired" | "open" | "captured" | "released" | null;
584
+ assurance: ("expired" | "rail_attested" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked" | "gateway_signed")[];
585
+ }[];
586
+ } | undefined;
587
+ rail_attestations?: {
588
+ financial_event_id: string;
589
+ scheme: string;
590
+ rail: string;
591
+ rail_ref: string;
592
+ anchor: string;
593
+ assurance: ["rail_attested"];
594
+ }[] | undefined;
595
+ resolved_exceptions?: {
596
+ exception_id: string;
597
+ kind: string;
598
+ status: "open" | "dismissed" | "resolved";
599
+ delegation_id: string | null;
600
+ financial_event_id: string | null;
601
+ detail: string;
602
+ created_at: string;
603
+ resolved_by: string;
604
+ resolved_at: string;
605
+ resolution: string | null;
606
+ }[] | undefined;
425
607
  };
426
608
  signature: {
427
609
  key_id: string;
@@ -443,7 +625,7 @@ export declare class SubledgerClient {
443
625
  getReceipt(receiptId: string): Promise<{
444
626
  payload: {
445
627
  document_type: "atcn.subledger.receipt";
446
- schema_version: "1.4" | "1.2" | "1.3";
628
+ schema_version: "1.2" | "1.3" | "1.4" | "1.5";
447
629
  receipt_id: string;
448
630
  revision: number;
449
631
  previous_receipt_id: string | null;
@@ -480,6 +662,11 @@ export declare class SubledgerClient {
480
662
  name: string;
481
663
  version: string;
482
664
  } | undefined;
665
+ additional_models?: {
666
+ provider: string;
667
+ name: string;
668
+ version: string;
669
+ }[] | undefined;
483
670
  config_digest?: string | undefined;
484
671
  };
485
672
  protocol?: {
@@ -493,6 +680,33 @@ export declare class SubledgerClient {
493
680
  agent_card_url?: string | undefined;
494
681
  } | undefined;
495
682
  } | undefined;
683
+ pricing?: {
684
+ rates: {
685
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
686
+ price_numerator: number;
687
+ price_denominator: number;
688
+ model?: {
689
+ provider: string;
690
+ name: string;
691
+ } | undefined;
692
+ tool_name?: string | undefined;
693
+ }[];
694
+ tolerance_bps: number;
695
+ fixed_minor?: number | undefined;
696
+ } | undefined;
697
+ refund_terms?: {
698
+ on_failure: "dispute" | "refund";
699
+ on_timeout: "dispute" | "refund";
700
+ after_settlement: {
701
+ cap_minor: number;
702
+ window_seconds: number;
703
+ };
704
+ } | undefined;
705
+ witness_policy?: {
706
+ min_independent_witnesses: number;
707
+ independence: "distinct_verified_domain";
708
+ witness_provider_ids?: string[] | undefined;
709
+ } | undefined;
496
710
  };
497
711
  provider: {
498
712
  provider_id: string | null;
@@ -514,14 +728,38 @@ export declare class SubledgerClient {
514
728
  supersedes_event_id: string | null;
515
729
  reason: string | null;
516
730
  event_id: string;
517
- assurance: ("expired" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked")[];
731
+ assurance: ("expired" | "rail_attested" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked" | "gateway_signed")[];
518
732
  recorded_at: string;
733
+ signer?: {
734
+ provider_id: string;
735
+ binding_id: string;
736
+ key_id: string;
737
+ value: string;
738
+ } | undefined;
739
+ usage?: {
740
+ trace_digest: string;
741
+ summary: {
742
+ models: {
743
+ provider: string;
744
+ name: string;
745
+ calls: number;
746
+ input_tokens: number;
747
+ output_tokens: number;
748
+ cache_read_input_tokens: number;
749
+ }[];
750
+ tools: {
751
+ name: string;
752
+ calls: number;
753
+ }[];
754
+ a2a_calls: number;
755
+ };
756
+ } | undefined;
519
757
  }[];
520
758
  financial_events: {
521
- type: "quote" | "invoice" | "charge" | "payment_reported" | "refund" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
759
+ type: "refund" | "charge" | "invoice" | "estimate" | "hold" | "quote" | "payment_reported" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
760
+ provider_id: string | null;
522
761
  currency: string;
523
762
  retrospective: boolean;
524
- provider_id: string | null;
525
763
  evidence: {
526
764
  uri: string;
527
765
  digest: string | null;
@@ -534,7 +772,7 @@ export declare class SubledgerClient {
534
772
  source_event_id: string;
535
773
  provider_reference: string | null;
536
774
  provider_status: string | null;
537
- normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "failed" | "refunded" | "reversed" | "void";
775
+ normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "pending_finality" | "failed" | "refunded" | "reversed" | "void";
538
776
  payer: "provider" | "buyer" | "other";
539
777
  included_in_event_id: string | null;
540
778
  reverses_event_id: string | null;
@@ -542,6 +780,29 @@ export declare class SubledgerClient {
542
780
  imported_at: string;
543
781
  financial_event_id: string;
544
782
  allocation_version: number;
783
+ skill?: {
784
+ namespace: string;
785
+ skill_id: string;
786
+ agent_card_url?: string | undefined;
787
+ } | undefined;
788
+ expectation?: {
789
+ issued_by: "agent" | "gateway" | "operator";
790
+ source_ref: string | null;
791
+ basis: string | null;
792
+ expires_at: string | null;
793
+ supersedes: string | null;
794
+ hold_status?: "expired" | "open" | "captured" | "released" | undefined;
795
+ signer?: {
796
+ provider_id: string;
797
+ binding_id: string;
798
+ key_id: string;
799
+ value: string;
800
+ } | undefined;
801
+ } | undefined;
802
+ rail_attestation?: {
803
+ scheme: string;
804
+ record: Record<string, unknown>;
805
+ } | undefined;
545
806
  }[];
546
807
  totals: Record<string, {
547
808
  quoted: number;
@@ -557,23 +818,34 @@ export declare class SubledgerClient {
557
818
  unresolved: number;
558
819
  downstream_reported: number;
559
820
  }>;
560
- field_status: Record<"delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status", "contested" | "missing" | "imported" | "buyer_asserted" | "provider_reported">;
561
- unverified_fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status")[];
821
+ field_status: Partial<Record<"delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage", "contested" | "missing" | "imported" | "buyer_asserted" | "provider_reported">>;
822
+ unverified_fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage")[];
562
823
  corrections: {
563
824
  response_id: string;
564
825
  receipt_revision: number;
565
- fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status")[];
826
+ fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage")[];
566
827
  decision: "accepted" | "rejected" | "open";
567
828
  }[];
568
829
  lineage: {
569
830
  complete: boolean;
570
831
  capture_gaps: {
832
+ kind: string;
571
833
  detail: string;
572
834
  reported_at: string;
573
835
  gap_id: string;
574
- kind: string;
575
836
  }[];
576
837
  };
838
+ key_bindings?: {
839
+ binding_id: string;
840
+ provider_id: string;
841
+ key_id: string;
842
+ public_key: string;
843
+ method: "operator_configured" | "domain_challenge";
844
+ created_by: string;
845
+ created_at: string;
846
+ revoked_at: string | null;
847
+ domain?: string | null | undefined;
848
+ }[] | undefined;
577
849
  };
578
850
  signature: {
579
851
  key_id: string;
@@ -596,6 +868,15 @@ export declare class SubledgerClient {
596
868
  expires_at: string;
597
869
  allowed_actions: string[];
598
870
  }>;
871
+ /** A witness link: lets another provider (not the delegation's own) sign that it observed the run. It allows only viewing and witnessing. */
872
+ createWitnessShare(receiptId: string, witnessProviderId: string, ttlHours?: number, opts?: Opts): Promise<{
873
+ share_id: string;
874
+ token: string;
875
+ url: string;
876
+ expires_at: string;
877
+ allowed_actions: string[];
878
+ witness_provider_id: string;
879
+ }>;
599
880
  revokeReceiptShare(shareId: string, opts?: Opts): Promise<Json>;
600
881
  listResponses(receiptId: string): Promise<{
601
882
  items: Json[];
@@ -664,6 +945,8 @@ export declare class ReceiptLinkClient {
664
945
  issued_at?: string;
665
946
  expires_at?: string;
666
947
  refs?: AttestationRef[];
948
+ /** Schema 1.5: a witness statement, sent through a witness link. */
949
+ role?: "witness";
667
950
  }, signing?: {
668
951
  bindingId: string;
669
952
  keyId: string;
package/dist/subledger.js CHANGED
@@ -46,9 +46,14 @@ export class SubledgerClient {
46
46
  recordFinancialEvent(input, opts = {}) {
47
47
  return this.http.request("POST", "/v1/financial-events", input, { idempotencyKey: opts.idempotencyKey ?? stableKey("financial", input.source, input.source_event_id) });
48
48
  }
49
+ recordRailAttestation(input, opts = {}) {
50
+ return this.http.request("POST", "/v1/financial-events/rail-attestations", input, opts);
51
+ }
49
52
  importCsv(csv, opts = {}) {
53
+ const { idempotencyKey, map, ...query } = opts;
50
54
  return this.http.request("POST", "/v1/financial-events/import", undefined, {
51
- idempotencyKey: opts.idempotencyKey,
55
+ idempotencyKey,
56
+ query: { ...query, map: map === undefined ? undefined : JSON.stringify(map) },
52
57
  textBody: { contentType: "text/csv", text: csv },
53
58
  });
54
59
  }
@@ -97,6 +102,10 @@ export class SubledgerClient {
97
102
  createReceiptShare(receiptId, allowedActions = ["view"], ttlHours, opts = {}) {
98
103
  return this.http.request("POST", "/v1/receipt-shares", { receipt_id: receiptId, allowed_actions: allowedActions, ttl_hours: ttlHours }, opts);
99
104
  }
105
+ /** A witness link: lets another provider (not the delegation's own) sign that it observed the run. It allows only viewing and witnessing. */
106
+ createWitnessShare(receiptId, witnessProviderId, ttlHours, opts = {}) {
107
+ return this.http.request("POST", "/v1/receipt-shares", { receipt_id: receiptId, allowed_actions: ["view", "witness_attestation"], ttl_hours: ttlHours, witness_provider_id: witnessProviderId }, opts);
108
+ }
100
109
  revokeReceiptShare(shareId, opts = {}) {
101
110
  return this.http.request("DELETE", `/v1/receipt-shares/${shareId}`, undefined, opts);
102
111
  }
@@ -179,6 +188,7 @@ export class ReceiptLinkClient {
179
188
  issued_at: response.issued_at,
180
189
  expires_at: response.expires_at,
181
190
  refs: response.refs,
191
+ role: response.role,
182
192
  });
183
193
  provider_signature = { binding_id: signing.bindingId, key_id: signing.keyId, value: signStatement(statement, signing.privateKey) };
184
194
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atcn/sdk",
3
- "version": "1.4.0",
4
- "description": "TypeScript SDK for ATCN: sign events, build terms and evidence envelopes, call the API, verify webhooks, closures, and closure packages",
3
+ "version": "1.5.0",
4
+ "description": "TypeScript SDK for ATCN, a cost record for AI agent jobs that anyone can verify: record tasks and charges, sign events, verify closures offline",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -36,10 +36,10 @@
36
36
  "build": "tsc -p tsconfig.build.json"
37
37
  },
38
38
  "dependencies": {
39
- "@atcn/core": "1.4.0",
40
- "@atcn/schema": "1.4.0",
41
- "@atcn/subledger": "1.4.0",
42
- "@atcn/usage": "1.4.0",
39
+ "@atcn/core": "1.5.0",
40
+ "@atcn/schema": "1.5.0",
41
+ "@atcn/subledger": "1.5.0",
42
+ "@atcn/usage": "1.5.0",
43
43
  "zod": "^4.1.0"
44
44
  }
45
45
  }