@ziggs-ai/api-client 0.1.22 → 0.1.24

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.
@@ -126,8 +126,15 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
126
126
  }
127
127
  const proposedTo = agreement.parties?.proposedTo;
128
128
  if (isBroadcastTarget(proposedTo)) {
129
- if (action === 'reject')
130
- return agreement;
129
+ if (action === 'reject') {
130
+ // ZIG-719: an open/broadcast proposal is offered to many via a sentinel
131
+ // `proposedTo` ('everyone' | 'org'), so there is no per-recipient party
132
+ // slot for one recipient to reject and nothing a single reject could
133
+ // change server-side. Returning the agreement here reported a fake
134
+ // success while the item stayed claimable. Fail honestly instead — the
135
+ // caller either claims it (action=approve → POST /claim) or ignores it.
136
+ throw new Error(`Agreement ${agreementId} is an open/broadcast proposal (proposedTo="${String(proposedTo)}") and cannot be rejected by a single recipient — it has no personal approval slot. Ignore it to pass on it, or use action=approve to claim it.`);
137
+ }
131
138
  const { agreement: updated } = await claimAgreement(agreementId, creds);
132
139
  return updated;
133
140
  }
@@ -233,57 +240,65 @@ export async function getAgreementStatus(agreementId, creds) {
233
240
  }
234
241
  export async function listAgreements(filters = {}, creds) {
235
242
  assertCreds(creds, 'list agreements');
243
+ // ZIG-699 — return an empty array only for a genuine empty 200 result. Any
244
+ // failure (non-2xx / network) throws so the MCP tool reports a real error
245
+ // instead of "you have no agreements". The status stays whitespace-delimited
246
+ // so the tool's toolError/classifyToolError maps it to a stable code.
247
+ // Canonical query path: GET /agreements?scope=&status=&engagementKind=&...
248
+ const url = new URL(getAgreementBaseUrl());
249
+ if (filters.status)
250
+ url.searchParams.set('status', filters.status);
251
+ if (filters.engagementKind)
252
+ url.searchParams.set('engagementKind', filters.engagementKind);
253
+ if (filters.proposalStatus)
254
+ url.searchParams.set('proposalStatus', filters.proposalStatus);
255
+ if (filters.hasTask != null)
256
+ url.searchParams.set('hasTask', String(filters.hasTask));
257
+ let res;
236
258
  try {
237
- // Canonical query path: GET /agreements?scope=&status=&engagementKind=&...
238
- const url = new URL(getAgreementBaseUrl());
239
- if (filters.status)
240
- url.searchParams.set('status', filters.status);
241
- if (filters.engagementKind)
242
- url.searchParams.set('engagementKind', filters.engagementKind);
243
- if (filters.proposalStatus)
244
- url.searchParams.set('proposalStatus', filters.proposalStatus);
245
- if (filters.hasTask != null)
246
- url.searchParams.set('hasTask', String(filters.hasTask));
247
- const res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
248
- if (!res.ok) {
249
- const body = await res.text().catch(() => '');
250
- runtimeLog.warn('AgreementClient', `⚠️ List agreements failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
251
- return [];
252
- }
253
- const data = await res.json().catch(() => null);
254
- return Array.isArray(data?.['agreements']) ? data['agreements'] : [];
259
+ res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
255
260
  }
256
261
  catch (e) {
257
262
  runtimeLog.warn('AgreementClient', `⚠️ List agreements failed: ${e.message}`);
258
- return [];
263
+ throw new Error(`GET /agreements network error: ${e.message}`);
264
+ }
265
+ if (!res.ok) {
266
+ const body = await res.text().catch(() => '');
267
+ runtimeLog.warn('AgreementClient', `⚠️ List agreements failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
268
+ throw new Error(`GET /agreements ${res.status} ${body?.slice(0, 200)}`);
259
269
  }
270
+ const data = await res.json().catch(() => null);
271
+ return Array.isArray(data?.['agreements']) ? data['agreements'] : [];
260
272
  }
261
273
  export async function getMyAgreements(filters = {}, creds) {
262
274
  assertCreds(creds, 'get my agreements');
275
+ // ZIG-699 — empty array only for a genuine empty 200; any failure throws so
276
+ // the tool surfaces a real error rather than "no agreements" (see listAgreements).
277
+ // Canonical query path: GET /agreements?scope=mine returns the enriched
278
+ // shape (tasks + originChatId + isYou flags).
279
+ const url = new URL(getAgreementBaseUrl());
280
+ url.searchParams.set('scope', 'mine');
281
+ if (filters.engagementKind)
282
+ url.searchParams.set('engagementKind', filters.engagementKind);
283
+ if (filters.proposalStatus)
284
+ url.searchParams.set('proposalStatus', filters.proposalStatus);
285
+ if (filters.hasTask != null)
286
+ url.searchParams.set('hasTask', String(filters.hasTask));
287
+ let res;
263
288
  try {
264
- // Canonical query path: GET /agreements?scope=mine returns the enriched
265
- // shape (tasks + originChatId + isYou flags).
266
- const url = new URL(getAgreementBaseUrl());
267
- url.searchParams.set('scope', 'mine');
268
- if (filters.engagementKind)
269
- url.searchParams.set('engagementKind', filters.engagementKind);
270
- if (filters.proposalStatus)
271
- url.searchParams.set('proposalStatus', filters.proposalStatus);
272
- if (filters.hasTask != null)
273
- url.searchParams.set('hasTask', String(filters.hasTask));
274
- const res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
275
- if (!res.ok) {
276
- const body = await res.text().catch(() => '');
277
- runtimeLog.warn('AgreementClient', `⚠️ Get my agreements failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
278
- return [];
279
- }
280
- const data = await res.json().catch(() => null);
281
- return Array.isArray(data?.['agreements']) ? data['agreements'] : [];
289
+ res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
282
290
  }
283
291
  catch (e) {
284
292
  runtimeLog.warn('AgreementClient', `⚠️ Get my agreements failed: ${e.message}`);
285
- return [];
293
+ throw new Error(`GET /agreements?scope=mine network error: ${e.message}`);
286
294
  }
295
+ if (!res.ok) {
296
+ const body = await res.text().catch(() => '');
297
+ runtimeLog.warn('AgreementClient', `⚠️ Get my agreements failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
298
+ throw new Error(`GET /agreements?scope=mine ${res.status} ${body?.slice(0, 200)}`);
299
+ }
300
+ const data = await res.json().catch(() => null);
301
+ return Array.isArray(data?.['agreements']) ? data['agreements'] : [];
287
302
  }
288
303
  export async function getAgreement(agreementId, creds) {
289
304
  if (!agreementId)
@@ -112,23 +112,27 @@ export async function sendChatMessage(input, creds) {
112
112
  }
113
113
  export async function listMyChats(creds) {
114
114
  assertCreds(creds, 'list my chats');
115
+ // ZIG-699 — empty array only for a genuine empty 200; any failure (non-2xx /
116
+ // network) throws so ziggs_list_chats reports a real error instead of "you
117
+ // have no chats". Status stays whitespace-delimited for toolError classification.
118
+ let res;
115
119
  try {
116
- const res = await fetch(`${getBackendUrl()}/chats/mine`, {
120
+ res = await fetch(`${getBackendUrl()}/chats/mine`, {
117
121
  method: 'GET',
118
122
  headers: buildHeaders(creds),
119
123
  });
120
- if (!res.ok) {
121
- const body = await res.text().catch(() => '');
122
- runtimeLog.warn('ChatClient', `⚠️ listMyChats failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
123
- return [];
124
- }
125
- const data = await res.json().catch(() => null);
126
- return Array.isArray(data?.['chats']) ? data['chats'] : [];
127
124
  }
128
125
  catch (e) {
129
126
  runtimeLog.warn('ChatClient', `⚠️ listMyChats failed: ${e.message}`);
130
- return [];
127
+ throw new Error(`GET /chats/mine network error: ${e.message}`);
131
128
  }
129
+ if (!res.ok) {
130
+ const body = await res.text().catch(() => '');
131
+ runtimeLog.warn('ChatClient', `⚠️ listMyChats failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
132
+ throw new Error(`GET /chats/mine ${res.status} ${body?.slice(0, 200)}`);
133
+ }
134
+ const data = await res.json().catch(() => null);
135
+ return Array.isArray(data?.['chats']) ? data['chats'] : [];
132
136
  }
133
137
  export class ChatClient {
134
138
  creds;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",