@atcn/sdk 1.4.1 → 1.5.1

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.1";
12
+ export declare const SDK_VERSION = "1.5.1";
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.1";
19
+ export const SDK_VERSION = "1.5.1";
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,26 @@
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
+ * preset reads a LiteLLM, OpenRouter or Stripe export (CSV, JSON or JSONL) without a column map.
13
+ */
14
+ type CsvImportOptions = {
15
+ preset?: "litellm" | "openrouter" | "stripe";
16
+ kind?: "charge" | "invoice" | "estimate" | "hold";
17
+ source?: string;
18
+ key_columns?: string;
19
+ currency?: string;
20
+ issued_by?: ExpectationIssuer;
21
+ map?: Partial<Record<ImportField, string>>;
22
+ minor_digits?: number;
23
+ };
9
24
  /** Caller-supplied references address records before their server IDs are known: ext("job-42"). */
10
25
  export declare const ext: (externalRef: string) => string;
11
26
  /** Idempotency key derived from the caller's stable references; long references are hashed to fit 8-200 chars. */
@@ -43,10 +58,24 @@ export declare class SubledgerClient {
43
58
  match_id: string | null;
44
59
  exception_ids: string[];
45
60
  }>;
46
- importCsv(csv: string, opts?: Opts): Promise<{
61
+ recordRailAttestation(input: {
62
+ attestation: RailAttestation;
63
+ source: string;
64
+ match?: Record<string, string>;
65
+ event_date?: string;
66
+ }, opts?: Opts): Promise<{
67
+ financial_event: Json;
68
+ deduplicated: boolean;
69
+ attributed?: boolean;
70
+ attribution: Json | null;
71
+ match_id: string | null;
72
+ exception_ids: string[];
73
+ }>;
74
+ importCsv(csv: string, opts?: Opts & CsvImportOptions): Promise<{
47
75
  imported: number;
48
76
  deduplicated: number;
49
77
  rejected: number;
78
+ skipped?: number;
50
79
  rows: Json[];
51
80
  }>;
52
81
  listFinancialEvents(query?: {
@@ -80,7 +109,7 @@ export declare class SubledgerClient {
80
109
  }, opts?: Opts): Promise<Json>;
81
110
  reportCaptureGap(taskId: string, gap: {
82
111
  delegation_id?: string | null;
83
- kind: "capture_failed" | "queue_overflow" | "provider_undisclosed" | "manual_gap";
112
+ kind: CaptureGapInput["kind"];
84
113
  detail: string;
85
114
  }, opts?: Opts): Promise<Json>;
86
115
  closeTask(taskId: string, opts?: Opts): Promise<{
@@ -90,7 +119,7 @@ export declare class SubledgerClient {
90
119
  getClosure(taskId: string, version?: number): Promise<{
91
120
  payload: {
92
121
  document_type: "atcn.subledger.closure";
93
- schema_version: "1.4" | "1.2" | "1.3";
122
+ schema_version: "1.2" | "1.3" | "1.4" | "1.5";
94
123
  closure_id: string;
95
124
  version: number;
96
125
  previous_closure_id: string | null;
@@ -112,6 +141,7 @@ export declare class SubledgerClient {
112
141
  scope_ref: string | null;
113
142
  retrospective: boolean;
114
143
  created_at: string;
144
+ estimate_tolerance_bps?: number | undefined;
115
145
  };
116
146
  delegations: {
117
147
  delegation_id: string;
@@ -145,6 +175,11 @@ export declare class SubledgerClient {
145
175
  name: string;
146
176
  version: string;
147
177
  } | undefined;
178
+ additional_models?: {
179
+ provider: string;
180
+ name: string;
181
+ version: string;
182
+ }[] | undefined;
148
183
  config_digest?: string | undefined;
149
184
  };
150
185
  protocol?: {
@@ -158,13 +193,40 @@ export declare class SubledgerClient {
158
193
  agent_card_url?: string | undefined;
159
194
  } | undefined;
160
195
  } | undefined;
196
+ pricing?: {
197
+ rates: {
198
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
199
+ price_numerator: number;
200
+ price_denominator: number;
201
+ model?: {
202
+ provider: string;
203
+ name: string;
204
+ } | undefined;
205
+ tool_name?: string | undefined;
206
+ }[];
207
+ tolerance_bps: number;
208
+ fixed_minor?: number | undefined;
209
+ } | undefined;
210
+ refund_terms?: {
211
+ on_failure: "dispute" | "refund";
212
+ on_timeout: "dispute" | "refund";
213
+ after_settlement: {
214
+ cap_minor: number;
215
+ window_seconds: number;
216
+ };
217
+ } | undefined;
218
+ witness_policy?: {
219
+ min_independent_witnesses: number;
220
+ independence: "distinct_verified_domain";
221
+ witness_provider_ids?: string[] | undefined;
222
+ } | undefined;
161
223
  }[];
162
224
  delivery_claims: {
163
225
  event_id: string;
164
226
  delegation_id: string;
165
227
  type: "acceptance" | "completion" | "partial_completion" | "cancellation" | "provider_failure" | "terms_update" | "correction";
166
228
  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")[];
229
+ 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
230
  note: string | null;
169
231
  evidence: {
170
232
  uri: string;
@@ -176,11 +238,35 @@ export declare class SubledgerClient {
176
238
  retrospective: boolean;
177
239
  occurred_at: string;
178
240
  recorded_at: string;
241
+ usage?: {
242
+ trace_digest: string;
243
+ summary: {
244
+ models: {
245
+ provider: string;
246
+ name: string;
247
+ calls: number;
248
+ input_tokens: number;
249
+ output_tokens: number;
250
+ cache_read_input_tokens: number;
251
+ }[];
252
+ tools: {
253
+ name: string;
254
+ calls: number;
255
+ }[];
256
+ a2a_calls: number;
257
+ };
258
+ } | undefined;
259
+ signer?: {
260
+ provider_id: string;
261
+ binding_id: string;
262
+ key_id: string;
263
+ value: string;
264
+ } | undefined;
179
265
  }[];
180
266
  financial_events: {
181
267
  record: {
182
268
  financial_event_id: string;
183
- type: "quote" | "invoice" | "charge" | "payment_reported" | "refund" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
269
+ type: "refund" | "charge" | "invoice" | "estimate" | "hold" | "quote" | "payment_reported" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
184
270
  source: string;
185
271
  source_event_id: string;
186
272
  provider_id: string | null;
@@ -190,7 +276,7 @@ export declare class SubledgerClient {
190
276
  event_date: string;
191
277
  imported_at: string;
192
278
  provider_status: string | null;
193
- normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "failed" | "refunded" | "reversed" | "void";
279
+ normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "pending_finality" | "failed" | "refunded" | "reversed" | "void";
194
280
  evidence: {
195
281
  uri: string;
196
282
  digest: string | null;
@@ -210,6 +296,29 @@ export declare class SubledgerClient {
210
296
  rate_denominator: number;
211
297
  } | null;
212
298
  reason: string | null;
299
+ skill?: {
300
+ namespace: string;
301
+ skill_id: string;
302
+ agent_card_url?: string | undefined;
303
+ } | undefined;
304
+ expectation?: {
305
+ issued_by: "agent" | "gateway" | "operator";
306
+ source_ref: string | null;
307
+ basis: string | null;
308
+ expires_at: string | null;
309
+ supersedes: string | null;
310
+ hold_status?: "expired" | "open" | "captured" | "released" | undefined;
311
+ signer?: {
312
+ provider_id: string;
313
+ binding_id: string;
314
+ key_id: string;
315
+ value: string;
316
+ } | undefined;
317
+ } | undefined;
318
+ rail_attestation?: {
319
+ scheme: string;
320
+ record: Record<string, unknown>;
321
+ } | undefined;
213
322
  };
214
323
  event_digest: string;
215
324
  attributed_to: string;
@@ -348,7 +457,7 @@ export declare class SubledgerClient {
348
457
  receipt_revision: number;
349
458
  issuer_operator_id: string;
350
459
  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")[];
460
+ fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage")[];
352
461
  note: string | null;
353
462
  evidence: {
354
463
  uri: string;
@@ -356,7 +465,7 @@ export declare class SubledgerClient {
356
465
  evidence_type: string;
357
466
  }[];
358
467
  corrections: {
359
- field: "delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status";
468
+ field: "delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage";
360
469
  proposed_value: string;
361
470
  reason: string;
362
471
  }[];
@@ -371,6 +480,7 @@ export declare class SubledgerClient {
371
480
  attestation_digest: string;
372
481
  reason: string;
373
482
  }[] | undefined;
483
+ role?: "witness" | undefined;
374
484
  };
375
485
  statement_digest: string;
376
486
  provider_signature: {
@@ -378,7 +488,7 @@ export declare class SubledgerClient {
378
488
  binding_id: string;
379
489
  value: string;
380
490
  } | null;
381
- assurance: ("expired" | "contested" | "issuer_signed" | "buyer_recorded" | "network_recorded" | "recipient_viewed" | "link_authenticated_response" | "provider_identity_bound" | "provider_key_signed" | "superseded" | "revoked")[];
491
+ 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
492
  decision: {
383
493
  status: "accepted" | "rejected";
384
494
  reason: string;
@@ -422,6 +532,81 @@ export declare class SubledgerClient {
422
532
  decision_id: string | null;
423
533
  decision_digest: string | null;
424
534
  }[] | undefined;
535
+ usage_checks?: {
536
+ delegation_id: string;
537
+ currency: string;
538
+ expected_minor: number | null;
539
+ lines: {
540
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
541
+ units: number;
542
+ cost_minor: number;
543
+ model?: {
544
+ provider: string;
545
+ name: string;
546
+ } | undefined;
547
+ tool_name?: string | undefined;
548
+ }[];
549
+ billed_minor: number;
550
+ difference_minor: number | null;
551
+ allowed_difference_minor: number | null;
552
+ within_tolerance: boolean | null;
553
+ unpriced: string[];
554
+ trace_digests: string[];
555
+ 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")[];
556
+ }[] | undefined;
557
+ expectation_report?: {
558
+ currency: string;
559
+ task: {
560
+ estimated_minor: number | null;
561
+ held_minor: number;
562
+ actual_minor: number;
563
+ variance_vs_estimate_minor: number | null;
564
+ variance_vs_estimate_bps: number | null;
565
+ variance_vs_hold_minor: number | null;
566
+ variance_vs_hold_bps: number | null;
567
+ unestimated_minor: number;
568
+ };
569
+ nodes: {
570
+ estimated_minor: number | null;
571
+ held_minor: number;
572
+ actual_minor: number;
573
+ variance_vs_estimate_minor: number | null;
574
+ variance_vs_estimate_bps: number | null;
575
+ variance_vs_hold_minor: number | null;
576
+ variance_vs_hold_bps: number | null;
577
+ node_id: string;
578
+ estimate_event_id: string | null;
579
+ }[];
580
+ records: {
581
+ financial_event_id: string;
582
+ node_id: string;
583
+ type: "estimate" | "hold";
584
+ issued_by: "agent" | "gateway" | "operator";
585
+ status: "superseded" | "current" | "not_latest" | "after_charge" | "other_currency";
586
+ hold_status: "expired" | "open" | "captured" | "released" | null;
587
+ 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")[];
588
+ }[];
589
+ } | undefined;
590
+ rail_attestations?: {
591
+ financial_event_id: string;
592
+ scheme: string;
593
+ rail: string;
594
+ rail_ref: string;
595
+ anchor: string;
596
+ assurance: ["rail_attested"];
597
+ }[] | undefined;
598
+ resolved_exceptions?: {
599
+ exception_id: string;
600
+ kind: string;
601
+ status: "open" | "dismissed" | "resolved";
602
+ delegation_id: string | null;
603
+ financial_event_id: string | null;
604
+ detail: string;
605
+ created_at: string;
606
+ resolved_by: string;
607
+ resolved_at: string;
608
+ resolution: string | null;
609
+ }[] | undefined;
425
610
  };
426
611
  signature: {
427
612
  key_id: string;
@@ -443,7 +628,7 @@ export declare class SubledgerClient {
443
628
  getReceipt(receiptId: string): Promise<{
444
629
  payload: {
445
630
  document_type: "atcn.subledger.receipt";
446
- schema_version: "1.4" | "1.2" | "1.3";
631
+ schema_version: "1.2" | "1.3" | "1.4" | "1.5";
447
632
  receipt_id: string;
448
633
  revision: number;
449
634
  previous_receipt_id: string | null;
@@ -480,6 +665,11 @@ export declare class SubledgerClient {
480
665
  name: string;
481
666
  version: string;
482
667
  } | undefined;
668
+ additional_models?: {
669
+ provider: string;
670
+ name: string;
671
+ version: string;
672
+ }[] | undefined;
483
673
  config_digest?: string | undefined;
484
674
  };
485
675
  protocol?: {
@@ -493,6 +683,33 @@ export declare class SubledgerClient {
493
683
  agent_card_url?: string | undefined;
494
684
  } | undefined;
495
685
  } | undefined;
686
+ pricing?: {
687
+ rates: {
688
+ meter: "input_tokens" | "output_tokens" | "cache_read_input_tokens" | "model_call" | "tool_call" | "a2a_call";
689
+ price_numerator: number;
690
+ price_denominator: number;
691
+ model?: {
692
+ provider: string;
693
+ name: string;
694
+ } | undefined;
695
+ tool_name?: string | undefined;
696
+ }[];
697
+ tolerance_bps: number;
698
+ fixed_minor?: number | undefined;
699
+ } | undefined;
700
+ refund_terms?: {
701
+ on_failure: "dispute" | "refund";
702
+ on_timeout: "dispute" | "refund";
703
+ after_settlement: {
704
+ cap_minor: number;
705
+ window_seconds: number;
706
+ };
707
+ } | undefined;
708
+ witness_policy?: {
709
+ min_independent_witnesses: number;
710
+ independence: "distinct_verified_domain";
711
+ witness_provider_ids?: string[] | undefined;
712
+ } | undefined;
496
713
  };
497
714
  provider: {
498
715
  provider_id: string | null;
@@ -514,14 +731,38 @@ export declare class SubledgerClient {
514
731
  supersedes_event_id: string | null;
515
732
  reason: string | null;
516
733
  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")[];
734
+ 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
735
  recorded_at: string;
736
+ signer?: {
737
+ provider_id: string;
738
+ binding_id: string;
739
+ key_id: string;
740
+ value: string;
741
+ } | undefined;
742
+ usage?: {
743
+ trace_digest: string;
744
+ summary: {
745
+ models: {
746
+ provider: string;
747
+ name: string;
748
+ calls: number;
749
+ input_tokens: number;
750
+ output_tokens: number;
751
+ cache_read_input_tokens: number;
752
+ }[];
753
+ tools: {
754
+ name: string;
755
+ calls: number;
756
+ }[];
757
+ a2a_calls: number;
758
+ };
759
+ } | undefined;
519
760
  }[];
520
761
  financial_events: {
521
- type: "quote" | "invoice" | "charge" | "payment_reported" | "refund" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
762
+ type: "refund" | "charge" | "invoice" | "estimate" | "hold" | "quote" | "payment_reported" | "reversal" | "fee" | "credit" | "adjustment" | "fx_rate";
763
+ provider_id: string | null;
522
764
  currency: string;
523
765
  retrospective: boolean;
524
- provider_id: string | null;
525
766
  evidence: {
526
767
  uri: string;
527
768
  digest: string | null;
@@ -534,7 +775,7 @@ export declare class SubledgerClient {
534
775
  source_event_id: string;
535
776
  provider_reference: string | null;
536
777
  provider_status: string | null;
537
- normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "failed" | "refunded" | "reversed" | "void";
778
+ normalized_status: "unknown" | "quoted" | "issued" | "pending" | "reported_paid" | "pending_finality" | "failed" | "refunded" | "reversed" | "void";
538
779
  payer: "provider" | "buyer" | "other";
539
780
  included_in_event_id: string | null;
540
781
  reverses_event_id: string | null;
@@ -542,6 +783,29 @@ export declare class SubledgerClient {
542
783
  imported_at: string;
543
784
  financial_event_id: string;
544
785
  allocation_version: number;
786
+ skill?: {
787
+ namespace: string;
788
+ skill_id: string;
789
+ agent_card_url?: string | undefined;
790
+ } | undefined;
791
+ expectation?: {
792
+ issued_by: "agent" | "gateway" | "operator";
793
+ source_ref: string | null;
794
+ basis: string | null;
795
+ expires_at: string | null;
796
+ supersedes: string | null;
797
+ hold_status?: "expired" | "open" | "captured" | "released" | undefined;
798
+ signer?: {
799
+ provider_id: string;
800
+ binding_id: string;
801
+ key_id: string;
802
+ value: string;
803
+ } | undefined;
804
+ } | undefined;
805
+ rail_attestation?: {
806
+ scheme: string;
807
+ record: Record<string, unknown>;
808
+ } | undefined;
545
809
  }[];
546
810
  totals: Record<string, {
547
811
  quoted: number;
@@ -557,23 +821,34 @@ export declare class SubledgerClient {
557
821
  unresolved: number;
558
822
  downstream_reported: number;
559
823
  }>;
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")[];
824
+ field_status: Partial<Record<"delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage", "contested" | "missing" | "imported" | "buyer_asserted" | "provider_reported">>;
825
+ unverified_fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage")[];
562
826
  corrections: {
563
827
  response_id: string;
564
828
  receipt_revision: number;
565
- fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status")[];
829
+ fields: ("delivery.status" | "delivery.evidence" | "scope.terms_digest" | "financial.amounts" | "financial.status" | "delivery.usage")[];
566
830
  decision: "accepted" | "rejected" | "open";
567
831
  }[];
568
832
  lineage: {
569
833
  complete: boolean;
570
834
  capture_gaps: {
835
+ kind: string;
571
836
  detail: string;
572
837
  reported_at: string;
573
838
  gap_id: string;
574
- kind: string;
575
839
  }[];
576
840
  };
841
+ key_bindings?: {
842
+ binding_id: string;
843
+ provider_id: string;
844
+ key_id: string;
845
+ public_key: string;
846
+ method: "operator_configured" | "domain_challenge";
847
+ created_by: string;
848
+ created_at: string;
849
+ revoked_at: string | null;
850
+ domain?: string | null | undefined;
851
+ }[] | undefined;
577
852
  };
578
853
  signature: {
579
854
  key_id: string;
@@ -596,6 +871,15 @@ export declare class SubledgerClient {
596
871
  expires_at: string;
597
872
  allowed_actions: string[];
598
873
  }>;
874
+ /** A witness link: lets another provider (not the delegation's own) sign that it observed the run. It allows only viewing and witnessing. */
875
+ createWitnessShare(receiptId: string, witnessProviderId: string, ttlHours?: number, opts?: Opts): Promise<{
876
+ share_id: string;
877
+ token: string;
878
+ url: string;
879
+ expires_at: string;
880
+ allowed_actions: string[];
881
+ witness_provider_id: string;
882
+ }>;
599
883
  revokeReceiptShare(shareId: string, opts?: Opts): Promise<Json>;
600
884
  listResponses(receiptId: string): Promise<{
601
885
  items: Json[];
@@ -664,6 +948,8 @@ export declare class ReceiptLinkClient {
664
948
  issued_at?: string;
665
949
  expires_at?: string;
666
950
  refs?: AttestationRef[];
951
+ /** Schema 1.5: a witness statement, sent through a witness link. */
952
+ role?: "witness";
667
953
  }, signing?: {
668
954
  bindingId: string;
669
955
  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.1",
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.1",
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.1",
40
- "@atcn/schema": "1.4.1",
41
- "@atcn/subledger": "1.4.1",
42
- "@atcn/usage": "1.4.1",
39
+ "@atcn/core": "1.5.1",
40
+ "@atcn/schema": "1.5.1",
41
+ "@atcn/subledger": "1.5.1",
42
+ "@atcn/usage": "1.5.1",
43
43
  "zod": "^4.1.0"
44
44
  }
45
45
  }