@ziggs-ai/api-client 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/ConnectionManager.d.ts +23 -59
  2. package/dist/ConnectionManager.js +37 -166
  3. package/dist/capabilities/agreements.js +2 -2
  4. package/dist/capabilities/chat.js +13 -3
  5. package/dist/capabilities/connections.js +1 -1
  6. package/dist/capabilities/index.d.ts +1 -1
  7. package/dist/capabilities/index.js +1 -1
  8. package/dist/capabilities/links.d.ts +0 -8
  9. package/dist/capabilities/links.js +3 -25
  10. package/dist/capabilities/payments.js +5 -0
  11. package/dist/capabilities/types.js +3 -0
  12. package/dist/config.d.ts +33 -0
  13. package/dist/config.js +42 -0
  14. package/dist/http/AgentSearchClient.d.ts +0 -1
  15. package/dist/http/AgentSearchClient.js +0 -1
  16. package/dist/http/AgreementClient.d.ts +32 -1
  17. package/dist/http/AgreementClient.js +96 -23
  18. package/dist/http/ArtifactsClient.d.ts +0 -1
  19. package/dist/http/ArtifactsClient.js +0 -1
  20. package/dist/http/ChatClient.d.ts +6 -3
  21. package/dist/http/ChatClient.js +7 -6
  22. package/dist/http/ConnectionsClient.d.ts +0 -1
  23. package/dist/http/ConnectionsClient.js +4 -5
  24. package/dist/http/ContextDiscoveryClient.d.ts +0 -1
  25. package/dist/http/ContextDiscoveryClient.js +2 -2
  26. package/dist/http/ContextGrantsClient.d.ts +0 -1
  27. package/dist/http/ContextGrantsClient.js +0 -1
  28. package/dist/http/ContextReadClient.d.ts +0 -1
  29. package/dist/http/ContextReadClient.js +5 -17
  30. package/dist/http/GrantsClient.d.ts +0 -1
  31. package/dist/http/GrantsClient.js +2 -2
  32. package/dist/http/InboxClient.d.ts +1 -177
  33. package/dist/http/InboxClient.js +0 -40
  34. package/dist/http/MarketplaceClient.d.ts +0 -1
  35. package/dist/http/MarketplaceClient.js +5 -4
  36. package/dist/http/MessagesClient.d.ts +0 -1
  37. package/dist/http/MessagesClient.js +3 -6
  38. package/dist/http/OrgsClient.d.ts +0 -1
  39. package/dist/http/OrgsClient.js +3 -3
  40. package/dist/http/PaymentsClient.d.ts +4 -2
  41. package/dist/http/PaymentsClient.js +27 -7
  42. package/dist/http/TaskClient.d.ts +0 -1
  43. package/dist/http/TaskClient.js +0 -1
  44. package/dist/http/TelemetryClient.d.ts +0 -1
  45. package/dist/http/TelemetryClient.js +0 -1
  46. package/dist/http/agreementFlows.d.ts +13 -5
  47. package/dist/http/agreementFlows.js +18 -24
  48. package/dist/http/index.d.ts +0 -1
  49. package/dist/index.d.ts +5 -3
  50. package/dist/index.js +5 -2
  51. package/dist/shared/apiError.d.ts +22 -1
  52. package/dist/shared/apiError.js +60 -3
  53. package/dist/shared/rateLimit.d.ts +12 -22
  54. package/dist/shared/rateLimit.js +18 -51
  55. package/dist/shared/runtimeLog.d.ts +0 -6
  56. package/dist/shared/runtimeLog.js +10 -7
  57. package/dist/types.d.ts +167 -1
  58. package/dist/types.js +20 -1
  59. package/dist/utils/urlUtils.js +3 -2
  60. package/package.json +1 -2
@@ -1,172 +1,4 @@
1
- import 'dotenv/config';
2
- /**
3
- * What a delivery can be about. A closed union, not a comment: a consumer that
4
- * dispatches on `kind` (the MCP read plan) is only safe if the compiler can tell
5
- * it a case is missing. The task-only deliverable that was acked unread got
6
- * through precisely because this was `string`.
7
- *
8
- * A type and not a value list, unlike the backend's `RESOURCE_KINDS` — nothing
9
- * on this side validates a delivery kind at runtime (the server does that on the
10
- * way in), and exhaustiveness checking is purely type-level.
11
- */
12
- export type InboxDeliveryKind = 'message' | 'artifact' | 'task-state' | 'agreement';
13
- /**
14
- * One thing addressed to this agent. A reference, never content — following it
15
- * (a chat read, a task read) is where this agent's grants are enforced.
16
- */
17
- export interface InboxDeliveryRef {
18
- kind: InboxDeliveryKind;
19
- resourceId: string;
20
- chatId: string | null;
21
- agreementId: string | null;
22
- taskId: string | null;
23
- /** Who wrote it. Never this agent — you are not woken by your own writes. */
24
- actorId: string | null;
25
- ts: string;
26
- }
27
- /** Message/artifact deliveries folded by chat, so you can open chats directly. */
28
- export interface InboxChatNews {
29
- chatId: string;
30
- count: number;
31
- latestAt: string;
32
- }
33
- export interface InboxProposalRef {
34
- agreementId: string;
35
- title: string;
36
- proposedAt: string | null;
37
- /**
38
- * ZIG-1087 — party ids still owing a decision, and the named responder slot.
39
- * The inbox lists proposals awaiting the agent OR its human, and only the
40
- * agent's own slot is one it can submit; these say which is which.
41
- *
42
- * Optional because a backend deployed before ZIG-1087 omits them, and this
43
- * client is installed independently of the server it talks to. Absent reads
44
- * as "no slot of mine", which routes the decision to the human — the safe
45
- * direction: it withholds a call, it never invents authority.
46
- */
47
- pendingApprovalPartyIds?: string[];
48
- proposedTo?: string | null;
49
- }
50
- export interface InboxConnectionRequestRef {
51
- requestId: string;
52
- /**
53
- * Non-addressable persona reference for the requester (`psn_*`).
54
- * Never use as an account id for lookup / wake / pay (ZIG-1137).
55
- */
56
- requesterRef: string;
57
- /** ZIG-1039 — human-readable name for consent cards. */
58
- requesterDisplayName?: string | null;
59
- /** ZIG-1039 — org label for consent cards. */
60
- requesterOrgName?: string | null;
61
- message: string | null;
62
- requestedAt: string | null;
63
- /** ZIG-1087 — see InboxProposalRef; a link request uses the same gate. */
64
- pendingApprovalPartyIds?: string[];
65
- proposedTo?: string | null;
66
- }
67
- /** Pull-only MCP: prompt the human when proposals need a decision (ZIG-482 / ZIG-481). */
68
- export interface InboxHumanAttention {
69
- required: true;
70
- reason: 'proposals_awaiting_me' | 'connection_requests_awaiting_me' | 'multiple';
71
- proposalCount: number;
72
- truncatedProposals: number;
73
- connectionRequestCount: number;
74
- truncatedConnectionRequests: number;
75
- promptUser: string;
76
- }
77
- /**
78
- * An open task assigned to this agent (ZIG-973). References only — read the
79
- * task for its description/plan/inputs.
80
- *
81
- * Tasks ride their own channel because assignment IS their delivery: before
82
- * this, a task wake was a synthetic chat row, so work under a chat-less
83
- * agreement (or self-assigned) reached nobody.
84
- */
85
- export interface InboxTaskRef {
86
- taskId: string;
87
- agreementId: string | null;
88
- title: string;
89
- state: string;
90
- updatedAt: string | null;
91
- }
92
- export interface InboxEnvelope {
93
- asOf: string;
94
- /**
95
- * Unacked deliveries addressed to this agent, newest first. This IS the
96
- * inbox — read straight out of the delivery log, not derived from grants.
97
- */
98
- deliveries: InboxDeliveryRef[];
99
- /** True when there was more than one envelope's worth; the rest stay unacked. */
100
- deliveriesCapped: boolean;
101
- /** The chat-bearing deliveries above, folded by chat. */
102
- chats: InboxChatNews[];
103
- /**
104
- * Pass to `ack()` after acting. Null when there is nothing to ack. Ack after
105
- * acting, not after reading: a crash in between redelivers.
106
- */
107
- ackTo: string | null;
108
- /** Open tasks assigned to this agent — the work channel (ZIG-973). */
109
- tasksAwaitingMe: InboxTaskRef[];
110
- truncatedTasks: number;
111
- proposalsAwaitingMe: InboxProposalRef[];
112
- truncatedProposals: number;
113
- connectionRequestsAwaitingMe: InboxConnectionRequestRef[];
114
- truncatedConnectionRequests: number;
115
- humanAttention?: InboxHumanAttention;
116
- }
117
- export interface InboxAckResult {
118
- /** Where the watermark now sits. Monotonic — a rewind is a no-op, not an error. */
119
- ackedUpTo: string | null;
120
- }
121
- /**
122
- * Long-poll option shared by the inbox reads. The server holds the request up
123
- * to this many seconds (server-clamped, ~25s ceiling) and returns as soon as
124
- * anything actionable exists. Omit for an immediate snapshot — the response
125
- * shape is identical either way, so `wait` only changes how long an EMPTY
126
- * answer is withheld.
127
- */
128
- export interface InboxReadOptions {
129
- waitSeconds?: number;
130
- /**
131
- * Operator read only (ZIG-965): restrict the sweep to this roster. The
132
- * server intersects it with the agents the key owner runs — it narrows,
133
- * never widens. A launcher hosting a subset should always pass the agents
134
- * it actually registered, or it pays for a sweep of the owner's whole
135
- * seeded fleet.
136
- */
137
- agents?: string[];
138
- }
139
- /**
140
- * One agent's line in the operator sweep: enough to decide whether to start a
141
- * host, and nothing more.
142
- *
143
- * Deliberately NOT that agent's envelope. The launcher only chooses who to
144
- * run; the host it starts reads its own inbox on its own credentials a moment
145
- * later, so building N envelopes here was work thrown away.
146
- */
147
- export interface InboxOperatorAgentEntry {
148
- agentId: string;
149
- /** Newest delivery addressed to this agent, or null if it never had one. */
150
- deliveredUpTo: string | null;
151
- /** How far it has acked. Mail exists when `deliveredUpTo > ackedUpTo`. */
152
- ackedUpTo: string | null;
153
- /**
154
- * True when this agent holds open assigned work. Independent of the
155
- * watermark: a host that died mid-task already acked past the task's
156
- * delivery, and the open task is the only durable trace it needs restarting.
157
- */
158
- hasOpenTasks: boolean;
159
- }
160
- /** GET /inbox/operator: which of the key owner's agents have mail or open work. */
161
- export interface InboxOperatorEnvelope {
162
- asOf: string;
163
- /** Agents with mail or open work. */
164
- agents: InboxOperatorAgentEntry[];
165
- /** Agents examined that had neither. */
166
- idleAgents: number;
167
- /** Owned agents beyond the server's per-pass cap — not examined. */
168
- truncatedAgents: number;
169
- }
1
+ import type { InboxAckResult, InboxEnvelope, InboxReadOptions } from '../types.js';
170
2
  /**
171
3
  * The doorbell, not the door (ZIG-434): references addressed to this agent
172
4
  * since its last ack — never content. Flow: inbox → read → act → ack
@@ -184,14 +16,6 @@ export declare class InboxClient {
184
16
  private headers;
185
17
  private inboxUrl;
186
18
  getInbox(opts?: InboxReadOptions): Promise<InboxEnvelope>;
187
- /**
188
- * Operator-level multiplexed read: which of this key owner's agents have
189
- * mail or open work (an agent-scoped key collapses to its one agent).
190
- *
191
- * Returns who to start, never what they were sent — the host you start
192
- * reads its own inbox on its own identity. Ack stays per agent.
193
- */
194
- getOperatorInbox(opts?: InboxReadOptions): Promise<InboxOperatorEnvelope>;
195
19
  /**
196
20
  * Advance this agent's watermark — pass the envelope's `ackTo`. Monotonic
197
21
  * server-side: an older value is a no-op, so a replayed ack can never
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import { getBackendUrl } from '../utils/urlUtils.js';
3
2
  import { pollSurfaceError } from '../shared/rateLimit.js';
4
3
  /**
@@ -35,9 +34,6 @@ export class InboxClient {
35
34
  if (opts.waitSeconds != null && opts.waitSeconds > 0) {
36
35
  url.searchParams.set('wait', String(opts.waitSeconds));
37
36
  }
38
- if (opts.agents?.length) {
39
- url.searchParams.set('agents', opts.agents.join(','));
40
- }
41
37
  return url.toString();
42
38
  }
43
39
  async getInbox(opts = {}) {
@@ -50,42 +46,6 @@ export class InboxClient {
50
46
  }
51
47
  return JSON.parse(body);
52
48
  }
53
- /**
54
- * Operator-level multiplexed read: which of this key owner's agents have
55
- * mail or open work (an agent-scoped key collapses to its one agent).
56
- *
57
- * Returns who to start, never what they were sent — the host you start
58
- * reads its own inbox on its own identity. Ack stays per agent.
59
- */
60
- async getOperatorInbox(opts = {}) {
61
- // Bounded wait (ZIG-965): the server holds at most `waitSeconds` (+ sweep
62
- // time); a poll that outlives that by a wide margin is a dead sweep, and
63
- // without a timeout it blocked the launcher's whole poll loop — lazy wake
64
- // simply stopped. Abort and let the caller's retry loop take over.
65
- const timeoutMs = ((opts.waitSeconds ?? 0) + 30) * 1000;
66
- const ac = new AbortController();
67
- const timer = setTimeout(() => ac.abort(), timeoutMs);
68
- let res;
69
- try {
70
- res = await fetch(this.inboxUrl('/inbox/operator', opts), {
71
- headers: this.headers(),
72
- signal: ac.signal,
73
- });
74
- }
75
- catch (err) {
76
- throw ac.signal.aborted
77
- ? new Error(`InboxClient.getOperatorInbox timed out after ${timeoutMs}ms`)
78
- : err;
79
- }
80
- finally {
81
- clearTimeout(timer);
82
- }
83
- const body = await res.text().catch(() => '');
84
- if (!res.ok) {
85
- throw pollSurfaceError('InboxClient.getOperatorInbox', res, body);
86
- }
87
- return JSON.parse(body);
88
- }
89
49
  /**
90
50
  * Advance this agent's watermark — pass the envelope's `ackTo`. Monotonic
91
51
  * server-side: an older value is a no-op, so a replayed ack can never
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import { type Creds, type Agreement, type BroadcastAudience } from '../types.js';
3
2
  export interface PublishOfferPayload {
4
3
  description: string;
@@ -1,4 +1,5 @@
1
- import 'dotenv/config';
1
+ // ZIG-1111: one shaping rule for every agreement this package parses.
2
+ import { shapeAgreement } from './AgreementClient.js';
2
3
  import { getBackendUrl } from '../utils/urlUtils.js';
3
4
  import { throwApiError } from '../shared/apiError.js';
4
5
  function getMarketplaceBaseUrl() { return `${getBackendUrl()}/marketplace`; }
@@ -30,7 +31,7 @@ export async function publishOffer(payload, creds) {
30
31
  throwApiError(res, body, `Marketplace offer publish failed: ${res.status}`);
31
32
  }
32
33
  const data = await res.json().catch(() => null);
33
- return (data?.['offer'] ?? data);
34
+ return shapeAgreement((data?.['offer'] ?? data));
34
35
  }
35
36
  export async function pullOffers(options, creds) {
36
37
  assertCreds(creds, 'marketplace offers pull');
@@ -67,7 +68,7 @@ export async function claimOffer(agreementId, creds) {
67
68
  const data = await res.json().catch(() => null);
68
69
  if (!data?.['ok'])
69
70
  throw new Error(data?.['error'] || 'Claim failed');
70
- return data['offer'];
71
+ return shapeAgreement(data['offer']);
71
72
  }
72
73
  export async function publishQuest(payload, creds) {
73
74
  assertCreds(creds, 'quest publish');
@@ -83,7 +84,7 @@ export async function publishQuest(payload, creds) {
83
84
  const data = await res.json().catch(() => null);
84
85
  if (!data?.['agreement'])
85
86
  throw new Error('Quest publish returned no agreement');
86
- return data['agreement'];
87
+ return shapeAgreement(data['agreement']);
87
88
  }
88
89
  export async function pullQuests(options, creds) {
89
90
  assertCreds(creds, 'quest pull');
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  export interface ListMessagesOptions {
3
2
  /** ISO timestamp; only entries with `timestamp > after` are returned. */
4
3
  after?: string;
@@ -1,5 +1,5 @@
1
- import 'dotenv/config';
2
1
  import { getBackendUrl } from '../utils/urlUtils.js';
2
+ import { throwApiError } from '../shared/apiError.js';
3
3
  /**
4
4
  * Read-side client for forward-delta message reads.
5
5
  *
@@ -35,11 +35,8 @@ export class MessagesClient {
35
35
  const res = await fetch(url.toString(), { headers: this._headers() });
36
36
  if (!res.ok) {
37
37
  const body = await res.text().catch(() => '');
38
- const err = new Error(`MessagesClient.list ${res.status} ${res.statusText} ${body.slice(0, 200)}`);
39
- // Callers branch on the HTTP status (404 = chat deleted/not visible)
40
- // without parsing the message string.
41
- err.status = res.status;
42
- throw err;
38
+ // Callers branch on ApiError.status (404 = chat deleted/not visible).
39
+ throwApiError(res, body, `MessagesClient.list failed: ${res.status}`);
43
40
  }
44
41
  return (await res.json());
45
42
  }
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import type { Creds } from '../types.js';
3
2
  export interface MyOrg {
4
3
  orgId: string;
@@ -1,5 +1,5 @@
1
- import 'dotenv/config';
2
1
  import { getBackendUrl } from '../utils/urlUtils.js';
2
+ import { throwApiError } from '../shared/apiError.js';
3
3
  import { buildOperatorHeaders } from './operatorHeaders.js';
4
4
  /**
5
5
  * ZIG-739 / ZIG-956 — the operator's full org membership (not just granted
@@ -15,7 +15,7 @@ export async function fetchMyOrgs(creds, baseUrl) {
15
15
  });
16
16
  const body = await res.text().catch(() => '');
17
17
  if (!res.ok) {
18
- throw new Error(`GET /orgs/me ${res.status} ${body.slice(0, 200)}`);
18
+ throwApiError(res, body, `GET /orgs/me failed: ${res.status}`);
19
19
  }
20
20
  const parsed = body ? JSON.parse(body) : {};
21
21
  return (parsed.orgs ?? []).map((o) => ({
@@ -55,7 +55,7 @@ export async function fetchDelegateAccess(creds, baseUrl) {
55
55
  });
56
56
  const body = await res.text().catch(() => '');
57
57
  if (!res.ok) {
58
- throw new Error(`GET /agents/claude-delegate/access ${res.status} ${body.slice(0, 200)}`);
58
+ throwApiError(res, body, `GET /agents/claude-delegate/access failed: ${res.status}`);
59
59
  }
60
60
  return body ? JSON.parse(body) : {};
61
61
  }
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import type { GrantView } from './grants.js';
3
2
  /** Thrown by PaymentsClient with the HTTP status and raw body attached. */
4
3
  export interface PaymentsError extends Error {
@@ -101,11 +100,14 @@ export declare class PaymentsClient {
101
100
  description?: string;
102
101
  paymentGrantId?: string;
103
102
  }): Promise<TransferResult>;
104
- hold({ amount, idempotencyKey, description, }: {
103
+ hold({ amount, idempotencyKey, description, paymentGrantId, }: {
105
104
  amount: number;
106
105
  idempotencyKey?: string;
107
106
  description?: string;
107
+ paymentGrantId?: string;
108
108
  }): Promise<HoldResult>;
109
+ /** Prefer a single active wallet grant the agent already holds (ZIG-1182). */
110
+ private resolveAgentPaymentGrantId;
109
111
  release({ holdId, action, toWalletId, idempotencyKey, }: {
110
112
  holdId: string;
111
113
  action?: string;
@@ -1,8 +1,7 @@
1
- import 'dotenv/config';
2
1
  import { getBackendUrl } from '../utils/urlUtils.js';
3
2
  import { buildOperatorHeaders } from './operatorHeaders.js';
4
3
  import { GrantsClient } from './GrantsClient.js';
5
- import { parseErrorMessage } from '../shared/apiError.js';
4
+ import { throwApiError } from '../shared/apiError.js';
6
5
  function randomIdempotencyKey(prefix = 'op') {
7
6
  return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
8
7
  }
@@ -51,6 +50,11 @@ export class PaymentsClient {
51
50
  throw new Error('transfer: `to` is required');
52
51
  if (!(Number.isInteger(amount) && amount > 0))
53
52
  throw new Error('transfer: `amount` must be a positive integer (cents)');
53
+ // ZIG-1182 — resolve a standing/active grant when the caller omitted one
54
+ // (Claude/MCP ensureForUser mints it). Explicit id still wins.
55
+ if (this.agentId && !paymentGrantId) {
56
+ paymentGrantId = (await this.resolveAgentPaymentGrantId()) ?? undefined;
57
+ }
54
58
  if (this.agentId && !paymentGrantId)
55
59
  throw new Error('transfer: paymentGrantId is required for agent-impersonated transfers. The wallet owner must have issued a payment grant to this agentId.');
56
60
  let toWalletId = to;
@@ -86,15 +90,33 @@ export class PaymentsClient {
86
90
  amount,
87
91
  };
88
92
  }
89
- async hold({ amount, idempotencyKey, description, }) {
93
+ async hold({ amount, idempotencyKey, description, paymentGrantId, }) {
90
94
  if (!(Number.isInteger(amount) && amount > 0))
91
95
  throw new Error('hold: `amount` must be a positive integer (cents)');
96
+ // ZIG-1182 — agent holds reserve the owner's wallet; require a grant.
97
+ if (this.agentId && !paymentGrantId) {
98
+ paymentGrantId = (await this.resolveAgentPaymentGrantId()) ?? undefined;
99
+ }
100
+ if (this.agentId && !paymentGrantId)
101
+ throw new Error('hold: paymentGrantId is required for agent-impersonated holds. The wallet owner must have issued a payment grant to this agentId.');
92
102
  return (await this._post('/payments/hold', {
93
103
  amount,
94
104
  idempotencyKey: idempotencyKey || randomIdempotencyKey('hold'),
95
105
  description,
106
+ paymentGrantId,
96
107
  }));
97
108
  }
109
+ /** Prefer a single active wallet grant the agent already holds (ZIG-1182). */
110
+ async resolveAgentPaymentGrantId() {
111
+ try {
112
+ const grants = await this.listGrants();
113
+ const id = grants[0]?.grantId;
114
+ return typeof id === 'string' && id.length > 0 ? id : null;
115
+ }
116
+ catch {
117
+ return null;
118
+ }
119
+ }
98
120
  async release({ holdId, action = 'complete', toWalletId, idempotencyKey, }) {
99
121
  return (await this._post(`/payments/release/${holdId}`, {
100
122
  idempotencyKey: idempotencyKey || randomIdempotencyKey('rel'),
@@ -217,10 +239,8 @@ export class PaymentsClient {
217
239
  const response = await fetch(`${this.baseUrl}${path}`, init);
218
240
  const text = await response.text();
219
241
  if (!response.ok) {
220
- const err = new Error(parseErrorMessage(text, `HTTP ${response.status}`));
221
- err.status = response.status;
222
- err.body = text;
223
- throw err;
242
+ // ZIG-1124 — ApiError; PaymentsError remains the duck type for `.status`.
243
+ throwApiError(response, text, `HTTP ${response.status}`);
224
244
  }
225
245
  return text ? JSON.parse(text) : null;
226
246
  }
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import { type Creds, type Task, type TaskState } from '../types.js';
3
2
  /**
4
3
  * When the buyer reviews a task's plan. Task-rail only — an agreement has no
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import { getBackendUrl } from '../utils/urlUtils.js';
3
2
  import { throwApiError } from '../shared/apiError.js';
4
3
  function getTaskBaseUrl() { return `${getBackendUrl()}/tasks`; }
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  export declare class TelemetryClient {
3
2
  private readonly operatorKey;
4
3
  private readonly agentId?;
@@ -1,4 +1,3 @@
1
- import 'dotenv/config';
2
1
  import { runtimeLog } from '../shared/runtimeLog.js';
3
2
  import { getBackendUrl } from '../utils/urlUtils.js';
4
3
  import { buildOperatorHeaders } from './operatorHeaders.js';
@@ -1,4 +1,4 @@
1
- import { type ProposeTerms } from './AgreementClient.js';
1
+ import { type ClaimedKind, type ProposeTerms } from './AgreementClient.js';
2
2
  import { type Agreement, type Creds, type EngagementKind } from '../types.js';
3
3
  /**
4
4
  * ZIG-1022 — one propose grammar. Direct, broadcast (quest and standing
@@ -22,11 +22,19 @@ export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds)
22
22
  agreement: Agreement;
23
23
  shape: ProposeShape;
24
24
  }>;
25
- export type ClaimedKind = 'link' | 'offer' | 'quest' | 'hand-off';
26
25
  /**
27
- * ZIG-1021 — one claim verb for any open broadcast. Fetches the agreement to
28
- * route: link invites and quests claim through POST /agreements/:id/claim;
29
- * standing offers (open payer side) through POST /marketplace/offers/claim.
26
+ * ZIG-1021 — one claim verb for any open broadcast: link invite, quest,
27
+ * hand-off, or standing offer.
28
+ *
29
+ * One request. This used to read the agreement first to decide which endpoint to
30
+ * post to, and `GET /agreements/:id` is party-scoped — a claimer is by definition
31
+ * not yet a party to the broadcast it is claiming, so the routing read 404'd and
32
+ * every standing offer in the store failed with "Agreement not found" before
33
+ * either claim endpoint was called (ZIG-1155). The backend routes it now, where
34
+ * the row is readable without being a party to it.
35
+ *
36
+ * `kind` arrives on the claim response — the route that did the routing reports
37
+ * which broadcast kind this turned out to be.
30
38
  */
31
39
  export declare function claimOpenAgreement(agreementId: string, creds: Creds): Promise<{
32
40
  agreement: Agreement;
@@ -1,5 +1,5 @@
1
- import { createAgreement, claimAgreement, getAgreement, proposeBroadcast, proposeDirectTo, } from './AgreementClient.js';
2
- import { claimOffer, publishOffer } from './MarketplaceClient.js';
1
+ import { createAgreement, claimAgreement, proposeBroadcast, proposeDirectTo, } from './AgreementClient.js';
2
+ import { publishOffer } from './MarketplaceClient.js';
3
3
  import { isBroadcastTarget, } from '../types.js';
4
4
  export async function proposeUnified(input, creds) {
5
5
  const { proposedTo, chatId, engagementKind, providerId, ...terms } = input;
@@ -56,30 +56,24 @@ export async function proposeUnified(input, creds) {
56
56
  return { agreement, shape: 'direct' };
57
57
  }
58
58
  /**
59
- * ZIG-1021 — one claim verb for any open broadcast. Fetches the agreement to
60
- * route: link invites and quests claim through POST /agreements/:id/claim;
61
- * standing offers (open payer side) through POST /marketplace/offers/claim.
59
+ * ZIG-1021 — one claim verb for any open broadcast: link invite, quest,
60
+ * hand-off, or standing offer.
61
+ *
62
+ * One request. This used to read the agreement first to decide which endpoint to
63
+ * post to, and `GET /agreements/:id` is party-scoped — a claimer is by definition
64
+ * not yet a party to the broadcast it is claiming, so the routing read 404'd and
65
+ * every standing offer in the store failed with "Agreement not found" before
66
+ * either claim endpoint was called (ZIG-1155). The backend routes it now, where
67
+ * the row is readable without being a party to it.
68
+ *
69
+ * `kind` arrives on the claim response — the route that did the routing reports
70
+ * which broadcast kind this turned out to be.
62
71
  */
63
72
  export async function claimOpenAgreement(agreementId, creds) {
64
73
  if (!agreementId)
65
74
  throw new Error('agreementId is required');
66
- const existing = await getAgreement(agreementId, creds);
67
- if (!existing)
68
- throw new Error(`Agreement not found: ${agreementId}`);
69
- if (existing.engagementKind === 'link') {
70
- const { agreement } = await claimAgreement(agreementId, creds);
71
- return { agreement, kind: 'link' };
72
- }
73
- if (isBroadcastTarget(existing.parties?.payer)) {
74
- // Seller-broadcast standing offer: the open side is the payer — you buy.
75
- const agreement = await claimOffer(agreementId, creds);
76
- return { agreement, kind: 'offer' };
77
- }
78
- const { agreement } = await claimAgreement(agreementId, creds);
79
- // ZIG-1059: a pinned provider inverts the quest reading — the publisher's
80
- // hired agent does the work and the claimer is who it is done FOR.
81
- return {
82
- agreement,
83
- kind: existing.providerPinned === true ? 'hand-off' : 'quest',
84
- };
75
+ const { agreement, kind } = await claimAgreement(agreementId, creds);
76
+ // A server that has not shipped the `kind` field yet still claims correctly;
77
+ // 'quest' is the shape the route has always handled.
78
+ return { agreement, kind: kind ?? 'quest' };
85
79
  }
@@ -26,4 +26,3 @@ export type { MyOrg, OrgResolution } from './OrgsClient.js';
26
26
  export { AgentSearchClient } from './AgentSearchClient.js';
27
27
  export { TelemetryClient } from './TelemetryClient.js';
28
28
  export { InboxClient } from './InboxClient.js';
29
- export type { InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxOperatorAgentEntry, InboxOperatorEnvelope, } from './InboxClient.js';
package/dist/index.d.ts CHANGED
@@ -2,12 +2,14 @@ export * from './http/index.js';
2
2
  export * from './capabilities/index.js';
3
3
  export * from './relay/provisionRelayWorkers.js';
4
4
  export { ConnectionManager } from './ConnectionManager.js';
5
- export type { StartAgentOptions } from './ConnectionManager.js';
6
5
  export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, } from './types.js';
7
6
  export type { PrincipalPresentation } from './types.js';
8
7
  export { getBackendUrl, getWebSocketUrl } from './utils/urlUtils.js';
8
+ export { configureApiClient, apiClientConfig } from './config.js';
9
+ export type { ApiClientConfig, ApiClientLogLevel } from './config.js';
9
10
  export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
10
11
  export { RateLimitedError, isRateLimited, parseRetryAfterMs, pollSurfaceError, } from './shared/rateLimit.js';
11
- export { parseErrorMessage, throwApiError } from './shared/apiError.js';
12
- export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, ApiError, } from './types.js';
12
+ export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiError.js';
13
+ export { ApiError } from './types.js';
14
+ export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxQuestRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxReadOptions, } from './types.js';
13
15
  export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, } from './http/AgreementClient.js';
package/dist/index.js CHANGED
@@ -4,10 +4,13 @@ export * from './relay/provisionRelayWorkers.js';
4
4
  export { ConnectionManager } from './ConnectionManager.js';
5
5
  export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, } from './types.js';
6
6
  export { getBackendUrl, getWebSocketUrl } from './utils/urlUtils.js';
7
+ // ZIG-652: the host injects the environment; this package never reads it.
8
+ export { configureApiClient, apiClientConfig } from './config.js';
7
9
  export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
8
- // ZIG-1019: retry loops need the server's own wait, not a guess.
10
+ // ZIG-1019 / ZIG-1124: one ApiError shape; 429s carry the server's wait.
9
11
  export { RateLimitedError, isRateLimited, parseRetryAfterMs, pollSurfaceError, } from './shared/rateLimit.js';
10
12
  // One reader for the one error shape the API answers in — exported so nothing has
11
13
  // to re-implement the field precedence (it was copy-pasted into six clients, and
12
14
  // half of them had drifted).
13
- export { parseErrorMessage, throwApiError } from './shared/apiError.js';
15
+ export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiError.js';
16
+ export { ApiError } from './types.js';
@@ -16,7 +16,28 @@
16
16
  * so the same failure read differently depending on which client you called.
17
17
  */
18
18
  export declare function parseErrorMessage(responseBody: string, defaultMessage: string): string;
19
- /** Throw the parsed failure as an {@link ApiError}, preserving status and body. */
19
+ /** Machine code from `{ error, code }` when the filter (or thrower) supplied one. */
20
+ export declare function parseErrorCode(responseBody: string): string | null;
21
+ /**
22
+ * Read the wait out of a 429 response. `Retry-After` wins (it is the explicit
23
+ * one) and is accepted in both forms — delta-seconds or an HTTP date;
24
+ * `RateLimit-Reset` is the standard-headers fallback, in seconds. Capped so a
25
+ * bad header cannot park a loop for an hour, floored at a second so a `0` does
26
+ * not reproduce the hot-retry it is meant to stop.
27
+ *
28
+ * Lives next to {@link throwApiError} so every client path (not only the poll
29
+ * surface) can honour the server's number (ZIG-1124).
30
+ */
31
+ export declare function parseRetryAfterMs(headers: {
32
+ get(name: string): string | null;
33
+ }): number | null;
34
+ /**
35
+ * Throw the parsed failure as an {@link ApiError} (or {@link RateLimitedError}
36
+ * for 429), preserving status, body, machine code, and Retry-After.
37
+ */
20
38
  export declare function throwApiError(response: {
21
39
  status: number;
40
+ headers?: {
41
+ get(name: string): string | null;
42
+ };
22
43
  }, responseBody: string, defaultMessage: string): never;