@ziggs-ai/api-client 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/capabilities/artifacts.d.ts +3 -0
  2. package/dist/capabilities/artifacts.js +86 -0
  3. package/dist/capabilities/chat.d.ts +11 -0
  4. package/dist/capabilities/chat.js +38 -0
  5. package/dist/capabilities/connections.d.ts +4 -0
  6. package/dist/capabilities/connections.js +112 -0
  7. package/dist/capabilities/context.d.ts +23 -0
  8. package/dist/capabilities/context.js +220 -0
  9. package/dist/capabilities/discovery.d.ts +4 -0
  10. package/dist/capabilities/discovery.js +77 -0
  11. package/dist/capabilities/grants.d.ts +9 -0
  12. package/dist/capabilities/grants.js +77 -0
  13. package/dist/capabilities/index.d.ts +9 -0
  14. package/dist/capabilities/index.js +9 -0
  15. package/dist/capabilities/links.d.ts +17 -0
  16. package/dist/capabilities/links.js +193 -0
  17. package/dist/capabilities/payments.d.ts +11 -0
  18. package/dist/capabilities/payments.js +404 -0
  19. package/dist/capabilities/types.d.ts +88 -0
  20. package/dist/capabilities/types.js +24 -0
  21. package/dist/http/AgreementClient.d.ts +7 -0
  22. package/dist/http/AgreementClient.js +2 -0
  23. package/dist/http/ConnectionsClient.d.ts +27 -1
  24. package/dist/http/ConnectionsClient.js +29 -0
  25. package/dist/http/GrantsClient.d.ts +16 -0
  26. package/dist/http/GrantsClient.js +3 -0
  27. package/dist/http/OrgsClient.d.ts +36 -0
  28. package/dist/http/OrgsClient.js +61 -0
  29. package/dist/http/PaymentsClient.d.ts +75 -10
  30. package/dist/http/PaymentsClient.js +26 -14
  31. package/dist/http/index.d.ts +5 -5
  32. package/dist/http/index.js +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/package.json +1 -1
  36. package/dist/http/grantRails.d.ts +0 -20
  37. package/dist/http/grantRails.js +0 -50
@@ -0,0 +1,404 @@
1
+ import { PaymentsClient } from '../http/PaymentsClient.js';
2
+ import { rethrowWithContext, } from './types.js';
3
+ function client(env) {
4
+ const { operatorKey, agentId } = env.creds;
5
+ if (!operatorKey)
6
+ throw new Error('operatorKey missing from tool context');
7
+ return new PaymentsClient(operatorKey, agentId, env.baseUrl);
8
+ }
9
+ /** Lower the optional caveat args to the wire's caveat list (was written twice). */
10
+ function buildCaveats(args) {
11
+ const caveats = [];
12
+ if (args['maxAmount'] != null)
13
+ caveats.push({ type: 'max_amount', value: args['maxAmount'] });
14
+ if (args['dailyBudget'] != null)
15
+ caveats.push({ type: 'daily_budget', value: args['dailyBudget'] });
16
+ if (args['allowedRecipients'] != null)
17
+ caveats.push({ type: 'allowed_recipients', value: args['allowedRecipients'] });
18
+ if (args['expiresInSeconds'] != null)
19
+ caveats.push({ type: 'expires_at', value: Date.now() + args['expiresInSeconds'] * 1000 });
20
+ return caveats;
21
+ }
22
+ const grantCaveatParams = {
23
+ maxAmount: { type: 'number', description: 'Per-transfer ceiling in cents' },
24
+ dailyBudget: { type: 'number', description: 'Rolling daily budget in cents' },
25
+ allowedRecipients: {
26
+ type: 'array',
27
+ items: { type: 'string' },
28
+ description: 'Wallet ids the holder may pay',
29
+ },
30
+ expiresInSeconds: { type: 'number', description: 'Grant lifetime from now' },
31
+ };
32
+ export const paymentBalanceCapability = {
33
+ key: 'payment_balance',
34
+ names: { sdk: 'payment_balance', mcp: 'ziggs_payment_balance' },
35
+ descriptions: {
36
+ sdk: "Check the caller's current wallet balance and available balance (total minus active holds). Use before a transfer to confirm sufficient funds.",
37
+ mcp: "Check the caller's current wallet balance and available balance (total minus active holds). Use before a transfer to confirm sufficient funds.",
38
+ },
39
+ annotation: 'read-only',
40
+ params: {},
41
+ handler: async (_args, env) => {
42
+ try {
43
+ return await client(env).balance();
44
+ }
45
+ catch (e) {
46
+ rethrowWithContext(e, 'Failed to get balance');
47
+ }
48
+ },
49
+ };
50
+ export const paymentResolveWalletCapability = {
51
+ key: 'payment_resolve_wallet',
52
+ names: { sdk: 'payment_resolve_wallet', mcp: 'ziggs_payment_resolve_wallet' },
53
+ descriptions: {
54
+ sdk: 'Look up a walletId by userId or agentId. Use before a transfer when you only know the recipient by their platform ID.',
55
+ mcp: 'Look up a walletId by userId or agentId. Use before a transfer when you only know the recipient by their platform ID.',
56
+ },
57
+ annotation: 'read-only',
58
+ params: {
59
+ userId: { type: 'string', description: 'User to resolve' },
60
+ agentId: { type: 'string', description: 'Agent to resolve' },
61
+ },
62
+ handler: async (args, env) => {
63
+ if (!args['userId'] && !args['agentId'])
64
+ throw new Error('Provide userId or agentId to resolve a wallet');
65
+ try {
66
+ const wallet = await client(env).resolve({
67
+ userId: args['userId'],
68
+ agentId: args['agentId'],
69
+ });
70
+ return {
71
+ walletId: wallet?.walletId || null,
72
+ ownerId: wallet?.ownerId || null,
73
+ currency: wallet?.currency || 'pez',
74
+ status: wallet?.status || null,
75
+ };
76
+ }
77
+ catch (e) {
78
+ rethrowWithContext(e, 'Failed to resolve wallet');
79
+ }
80
+ },
81
+ };
82
+ export const paymentTransferCapability = {
83
+ key: 'payment_transfer',
84
+ names: { sdk: 'payment_transfer', mcp: 'ziggs_payment_transfer' },
85
+ descriptions: {
86
+ sdk: 'Transfer funds to another wallet. Amounts are integer cents. Transfers above the wallet owner\'s policy pause with status "approval_required" — wait inline with payment_wait_for_approval when you expect a quick decision.',
87
+ mcp: 'Transfer funds to another wallet. Amounts are integer cents. As a delegate you spend under a payment grant the wallet owner issued (paymentGrantId — find yours via ziggs_list_grants scopeKind=wallet). Transfers above the owner\'s policy pause with status "approval_required": the human approves on the wallet page (it also shows in ziggs_pending_decisions) — you can wait inline with ziggs_payment_wait_for_approval, and you must NEVER approve your own transfer.',
88
+ },
89
+ annotation: 'write',
90
+ params: {
91
+ toWalletId: {
92
+ type: 'string',
93
+ required: true,
94
+ description: 'Destination wal_... id — or a userId/agentId to auto-resolve',
95
+ },
96
+ amount: { type: 'number', required: true, description: 'Amount in integer cents, > 0' },
97
+ description: { type: 'string', description: 'Human-readable transfer memo' },
98
+ idempotencyKey: {
99
+ type: 'string',
100
+ description: 'Client-supplied key to make retries safe (auto-generated when omitted)',
101
+ },
102
+ paymentGrantId: {
103
+ type: 'string',
104
+ description: 'Payment grant to spend under (required for agent-impersonated transfers)',
105
+ },
106
+ },
107
+ handler: async (args, env) => {
108
+ if (!args['toWalletId'])
109
+ throw new Error('toWalletId is required');
110
+ const amount = args['amount'];
111
+ if (!amount || amount <= 0)
112
+ throw new Error('amount must be positive');
113
+ let result;
114
+ try {
115
+ result = await client(env).transfer({
116
+ to: args['toWalletId'],
117
+ amount: Math.round(amount),
118
+ description: args['description'] || 'Agent-initiated transfer',
119
+ idempotencyKey: args['idempotencyKey'],
120
+ paymentGrantId: args['paymentGrantId'],
121
+ });
122
+ }
123
+ catch (e) {
124
+ rethrowWithContext(e, 'Transfer failed');
125
+ }
126
+ if (result.status === 'approval_required') {
127
+ const base = {
128
+ status: 'approval_required',
129
+ approvalId: result.approvalId,
130
+ expiresAt: result.expiresAt,
131
+ reason: result.reason,
132
+ amount,
133
+ toWalletId: result.toWalletId,
134
+ };
135
+ // Same pause, surface-local guidance: the MCP delegate must surface the
136
+ // pending approval to the human (pull-only MCP has no push); the SDK
137
+ // runtime routes via structured next_actions.
138
+ if (env.surface === 'mcp') {
139
+ return {
140
+ ...base,
141
+ note: 'Transfer paused: the wallet owner must approve this amount on the wallet page (also listed by ziggs_pending_decisions). Tell the human now (pull-only MCP has no push). ' +
142
+ 'Wait inline with ziggs_payment_wait_for_approval when you expect a quick decision (≤2 min).',
143
+ };
144
+ }
145
+ return {
146
+ ...base,
147
+ message: 'Transfer paused: the wallet owner must approve this amount.',
148
+ next_actions: [
149
+ {
150
+ tool: 'payment_wait_for_approval',
151
+ when: 'You expect a quick decision (≤2 min) and can wait inline.',
152
+ args: { approvalId: result.approvalId, timeoutMs: 120000 },
153
+ },
154
+ {
155
+ tool: 'task_update_plan_step',
156
+ when: 'You want to abandon the transfer and route around it.',
157
+ },
158
+ ],
159
+ };
160
+ }
161
+ return {
162
+ status: 'transferred',
163
+ transactionId: result.transactionId,
164
+ amount,
165
+ toWalletId: result.toWalletId,
166
+ };
167
+ },
168
+ };
169
+ export const paymentWaitForApprovalCapability = {
170
+ key: 'payment_wait_for_approval',
171
+ names: { sdk: 'payment_wait_for_approval', mcp: 'ziggs_payment_wait_for_approval' },
172
+ descriptions: {
173
+ sdk: 'Poll a paused transfer (status "approval_required") until the human decides or the timeout passes. Returns executed | rejected | expired | timeout | gone.',
174
+ mcp: 'Poll a paused transfer (status "approval_required") until the human decides or the timeout passes. Returns executed | rejected | expired | timeout | gone. Use for quick decisions (≤2 min); for longer waits, stop and check again next session.',
175
+ },
176
+ annotation: 'read-only',
177
+ params: {
178
+ approvalId: {
179
+ type: 'string',
180
+ required: true,
181
+ description: 'Approval to wait on (from the paused transfer)',
182
+ },
183
+ timeoutMs: { type: 'number', description: 'Max wait, default 120000' },
184
+ pollMs: { type: 'number', description: 'Poll interval, default 3000 (min 500)' },
185
+ },
186
+ handler: async (args, env) => {
187
+ if (!args['approvalId'])
188
+ throw new Error('approvalId is required');
189
+ try {
190
+ const result = await client(env).waitForApproval(args['approvalId'], {
191
+ timeoutMs: args['timeoutMs'],
192
+ pollMs: args['pollMs'],
193
+ });
194
+ const loose = result;
195
+ return {
196
+ status: result.status,
197
+ approvalId: args['approvalId'],
198
+ transactionId: loose['transactionId'] || null,
199
+ approval: loose['approval'] || null,
200
+ };
201
+ }
202
+ catch (e) {
203
+ rethrowWithContext(e, 'wait_for_approval failed');
204
+ }
205
+ },
206
+ };
207
+ export const paymentHoldCapability = {
208
+ key: 'payment_hold',
209
+ names: { sdk: 'payment_hold', mcp: 'ziggs_payment_hold' },
210
+ descriptions: {
211
+ sdk: 'Pre-authorize (escrow) funds without moving them. Use to reserve payment at agreement formation; release with payment_release once work is complete or to refund if work is cancelled.',
212
+ mcp: "Pre-authorize (escrow) funds without moving them. Use to reserve payment at agreement formation; release with ziggs_payment_release once work is complete, or refund if it's cancelled.",
213
+ },
214
+ annotation: 'write',
215
+ params: {
216
+ amount: { type: 'number', required: true, description: 'Amount in integer cents, > 0' },
217
+ description: { type: 'string', description: 'Human-readable hold memo' },
218
+ idempotencyKey: { type: 'string', description: 'Client-supplied retry-safety key' },
219
+ },
220
+ handler: async (args, env) => {
221
+ const amount = args['amount'];
222
+ if (!amount || amount <= 0)
223
+ throw new Error('amount must be positive');
224
+ try {
225
+ const result = await client(env).hold({
226
+ amount: Math.round(amount),
227
+ description: args['description'] || 'Agent escrow hold',
228
+ idempotencyKey: args['idempotencyKey'],
229
+ });
230
+ return {
231
+ status: 'held',
232
+ transactionId: result.transaction?.transactionId || null,
233
+ amount,
234
+ };
235
+ }
236
+ catch (e) {
237
+ rethrowWithContext(e, 'Hold failed');
238
+ }
239
+ },
240
+ };
241
+ export const paymentReleaseCapability = {
242
+ key: 'payment_release',
243
+ names: { sdk: 'payment_release', mcp: 'ziggs_payment_release' },
244
+ descriptions: {
245
+ sdk: "Settle or refund an escrow hold. Use action='complete' to transfer held funds to toWalletId (work done), or action='refund' to return funds to the sender (work cancelled).",
246
+ mcp: "Settle or refund an escrow hold. action='complete' transfers held funds to toWalletId (work done); action='refund' returns funds to the sender (work cancelled).",
247
+ },
248
+ annotation: 'write',
249
+ params: {
250
+ holdId: {
251
+ type: 'string',
252
+ required: true,
253
+ description: 'Hold to settle (transactionId from the hold)',
254
+ },
255
+ action: {
256
+ type: 'string',
257
+ required: true,
258
+ enum: ['complete', 'refund'],
259
+ description: 'complete = pay out, refund = return',
260
+ },
261
+ toWalletId: { type: 'string', description: 'Destination wallet — required when action=complete' },
262
+ idempotencyKey: { type: 'string', description: 'Client-supplied retry-safety key' },
263
+ },
264
+ handler: async (args, env) => {
265
+ if (!args['holdId'])
266
+ throw new Error('holdId is required');
267
+ const action = args['action'];
268
+ if (action !== 'complete' && action !== 'refund')
269
+ throw new Error("action must be 'complete' or 'refund'");
270
+ if (action === 'complete' && !args['toWalletId'])
271
+ throw new Error('toWalletId is required when action=complete');
272
+ try {
273
+ const result = await client(env).release({
274
+ holdId: args['holdId'],
275
+ action,
276
+ toWalletId: args['toWalletId'],
277
+ idempotencyKey: args['idempotencyKey'],
278
+ });
279
+ return {
280
+ status: action === 'complete' ? 'settled' : 'refunded',
281
+ transactionId: result.transaction?.transactionId || null,
282
+ holdId: args['holdId'],
283
+ action,
284
+ };
285
+ }
286
+ catch (e) {
287
+ rethrowWithContext(e, 'Release failed');
288
+ }
289
+ },
290
+ };
291
+ export const paymentIssueGrantCapability = {
292
+ key: 'payment_issue_grant',
293
+ names: { sdk: 'payment_issue_grant', mcp: 'ziggs_payment_issue_grant' },
294
+ descriptions: {
295
+ sdk: "Issue a payment grant delegating bounded spend from the operator's wallet to an agent holder. Caveats bound what the holder can do (max_amount, daily_budget, allowed_recipients, expiry). The holder spends by passing the grantId as paymentGrantId on transfers.",
296
+ mcp: "Issue a payment grant delegating bounded spend from the operator's wallet to an agent holder. Caveats bound what the holder can do (max_amount, daily_budget, allowed_recipients, expiry). The holder spends by passing the grantId as paymentGrantId on transfers.",
297
+ },
298
+ annotation: 'write',
299
+ params: {
300
+ holderId: { type: 'string', required: true, description: 'Agent that will hold the grant' },
301
+ ...grantCaveatParams,
302
+ },
303
+ handler: async (args, env) => {
304
+ if (!args['holderId'])
305
+ throw new Error('holderId is required');
306
+ try {
307
+ const caveats = buildCaveats(args);
308
+ const { grant } = await client(env).issueGrant({
309
+ holderId: args['holderId'],
310
+ caveats,
311
+ });
312
+ return {
313
+ grantId: grant?.grantId || null,
314
+ holderId: grant?.holderId || args['holderId'],
315
+ caveats: grant?.caveats || caveats,
316
+ expiresAt: grant?.expiresAt || null,
317
+ };
318
+ }
319
+ catch (e) {
320
+ rethrowWithContext(e, 'Failed to issue payment grant');
321
+ }
322
+ },
323
+ };
324
+ export const paymentAttenuateGrantCapability = {
325
+ key: 'payment_attenuate_grant',
326
+ names: { sdk: 'payment_attenuate_grant', mcp: 'ziggs_payment_attenuate_grant' },
327
+ descriptions: {
328
+ sdk: 'Re-delegate a payment grant you hold to another agent with TIGHTER caveats (narrowing only — the child can never exceed the parent). Use to pass a bounded spend slice to a sub-agent.',
329
+ mcp: 'Re-delegate a payment grant you hold to another agent with TIGHTER caveats (narrowing only — the child can never exceed the parent). Use to pass a bounded spend slice to a sub-agent.',
330
+ },
331
+ annotation: 'write',
332
+ params: {
333
+ grantId: { type: 'string', required: true, description: 'Parent grant to attenuate' },
334
+ holderId: {
335
+ type: 'string',
336
+ required: true,
337
+ description: 'Agent that will hold the narrowed grant',
338
+ },
339
+ ...grantCaveatParams,
340
+ },
341
+ handler: async (args, env) => {
342
+ const grantId = args['grantId'];
343
+ if (!grantId)
344
+ throw new Error('grantId is required');
345
+ if (!args['holderId'])
346
+ throw new Error('holderId is required');
347
+ try {
348
+ const caveats = buildCaveats(args);
349
+ const { grant } = await client(env).attenuateGrant({
350
+ grantId,
351
+ holderId: args['holderId'],
352
+ caveats,
353
+ });
354
+ return {
355
+ grantId: grant?.grantId || null,
356
+ parentGrantId: grant?.parentGrantId || grantId,
357
+ holderId: grant?.holderId || args['holderId'],
358
+ caveats: grant?.caveats || caveats,
359
+ expiresAt: grant?.expiresAt || null,
360
+ };
361
+ }
362
+ catch (e) {
363
+ rethrowWithContext(e, 'Failed to attenuate payment grant');
364
+ }
365
+ },
366
+ };
367
+ export const paymentRevokeGrantCapability = {
368
+ key: 'payment_revoke_grant',
369
+ names: { sdk: 'payment_revoke_grant', mcp: 'ziggs_payment_revoke_grant' },
370
+ descriptions: {
371
+ sdk: 'Revoke a payment grant (and its attenuated children). The holder can no longer spend under it.',
372
+ mcp: 'Revoke a payment grant (and its attenuated children). The holder can no longer spend under it.',
373
+ },
374
+ annotation: 'destructive',
375
+ params: {
376
+ grantId: { type: 'string', required: true, description: 'Grant to revoke' },
377
+ },
378
+ handler: async (args, env) => {
379
+ const grantId = args['grantId'];
380
+ if (!grantId)
381
+ throw new Error('grantId is required');
382
+ try {
383
+ const result = await client(env).revokeGrant(grantId);
384
+ return { status: 'revoked', grantId, revoked: result?.revoked ?? null };
385
+ }
386
+ catch (e) {
387
+ rethrowWithContext(e, 'Failed to revoke payment grant');
388
+ }
389
+ },
390
+ };
391
+ // ZIG-893 — payment_list_grants stays retired; the wallet rail is part of the
392
+ // unified list_grants capability (scopeKind: 'wallet'). Grant *mutations* stay
393
+ // on the payment rail above.
394
+ export const PAYMENT_CAPABILITIES = [
395
+ paymentBalanceCapability,
396
+ paymentTransferCapability,
397
+ paymentWaitForApprovalCapability,
398
+ paymentHoldCapability,
399
+ paymentReleaseCapability,
400
+ paymentResolveWalletCapability,
401
+ paymentIssueGrantCapability,
402
+ paymentAttenuateGrantCapability,
403
+ paymentRevokeGrantCapability,
404
+ ];
@@ -0,0 +1,88 @@
1
+ /**
2
+ * ZIG-956 — one shared definition per SDK/MCP tool capability.
3
+ *
4
+ * The tool-surface-parity epic (ZIG-893→903) unified the HTTP layer here in
5
+ * api-client but left ~25 shared capabilities hand-written twice: once in the
6
+ * agent-sdk's defineTool DSL, once in ziggs-mcp zod. Each capability below is
7
+ * the single source for the tool's schema, validation, response shaping, and
8
+ * guidance; the two surfaces register it through thin adapters
9
+ * (agent-sdk `toolFromCapability`, ziggs-mcp `registerCapability`).
10
+ *
11
+ * Params are a neutral JSON-schema-flavoured DSL rather than zod because the
12
+ * two surfaces cannot share one zod: agent-sdk is on zod v4, ziggs-mcp on
13
+ * zod v3 (pinned by the MCP SDK's ZodRawShape), and api-client deliberately
14
+ * has no zod dependency. The SDK adapter feeds params straight into
15
+ * defineTool's converter; the MCP adapter lowers them to zod v3.
16
+ */
17
+ import type { Creds } from '../types.js';
18
+ export type CapabilitySurface = 'sdk' | 'mcp';
19
+ /** read-only / write / destructive — lowered to MCP tool annotations. */
20
+ export type CapabilityAnnotation = 'read-only' | 'write' | 'destructive';
21
+ export interface CapabilityParam {
22
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array';
23
+ required?: boolean;
24
+ description?: string;
25
+ /** Only for type 'string'. */
26
+ enum?: readonly string[];
27
+ /** Only for type 'array'. */
28
+ items?: {
29
+ type: 'string';
30
+ enum?: readonly string[];
31
+ } | {
32
+ type: 'object';
33
+ properties?: Record<string, unknown>;
34
+ required?: string[];
35
+ };
36
+ }
37
+ /**
38
+ * Runtime context a surface adapter hands the shared handler. `creds.agentId`
39
+ * may be absent for agent-scoped operator keys (payments rail); capabilities
40
+ * that impersonate set `needsAgentId` so the adapter fails early instead.
41
+ */
42
+ export interface CapabilityEnv {
43
+ creds: {
44
+ operatorKey: string;
45
+ agentId?: string;
46
+ };
47
+ /** SDK runner base-URL override (BACKEND_URL / ZIGGS_BACKEND_URL). */
48
+ baseUrl?: string;
49
+ /** Web-app origin for human-facing URLs (claim links etc.). */
50
+ webUrl?: string;
51
+ surface: CapabilitySurface;
52
+ }
53
+ export interface CapabilityDefinition {
54
+ /** Stable capability key (the parity-table base name), e.g. 'payment_transfer'. */
55
+ key: string;
56
+ /** Registered tool name per surface — the ZIG-903 parity table is the authority. */
57
+ names: {
58
+ sdk: string;
59
+ mcp: string;
60
+ };
61
+ /**
62
+ * Tool description per surface. Kept side by side deliberately: wording may
63
+ * reference surface-local tool names and MCP delegate-protocol guidance, but
64
+ * schema + handler can no longer drift.
65
+ */
66
+ descriptions: {
67
+ sdk: string;
68
+ mcp: string;
69
+ };
70
+ annotation: CapabilityAnnotation;
71
+ params: Record<string, CapabilityParam>;
72
+ /** True when the handler impersonates an agent (X-Agent-Id required). */
73
+ needsAgentId?: boolean;
74
+ handler: (args: Record<string, unknown>, env: CapabilityEnv) => Promise<unknown>;
75
+ /** Pass-through flags for the SDK's defineTool options. */
76
+ sdkOptions?: {
77
+ isAgreementCreation?: boolean;
78
+ isGenericFallback?: boolean;
79
+ };
80
+ }
81
+ /** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
82
+ export declare function fullCreds(env: CapabilityEnv): Creds;
83
+ /**
84
+ * Re-throw a client error with a capability-level prefix, preserving the HTTP
85
+ * status/body the clients attach (the SDK runtime and toolError classification
86
+ * both read them).
87
+ */
88
+ export declare function rethrowWithContext(error: unknown, prefix: string): never;
@@ -0,0 +1,24 @@
1
+ /** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
2
+ export function fullCreds(env) {
3
+ const { operatorKey, agentId } = env.creds;
4
+ if (!operatorKey)
5
+ throw new Error('operatorKey missing from tool context');
6
+ if (!agentId)
7
+ throw new Error('agentId missing from tool context');
8
+ return { operatorKey, agentId };
9
+ }
10
+ /**
11
+ * Re-throw a client error with a capability-level prefix, preserving the HTTP
12
+ * status/body the clients attach (the SDK runtime and toolError classification
13
+ * both read them).
14
+ */
15
+ export function rethrowWithContext(error, prefix) {
16
+ const e = error;
17
+ const wrapped = new Error(`${prefix}: ${e.message}`);
18
+ if (e.status !== undefined)
19
+ wrapped.status = e.status;
20
+ if (e.body !== undefined)
21
+ wrapped.body = e.body;
22
+ wrapped['cause'] = error;
23
+ throw wrapped;
24
+ }
@@ -128,6 +128,13 @@ export interface GetMyAgreementsFilters {
128
128
  engagementKind?: EngagementKind;
129
129
  proposalStatus?: string;
130
130
  hasTask?: boolean;
131
+ /**
132
+ * ZIG-942: `scope=mine` alone returns every agreement the caller's grant can
133
+ * read in the active org — including ones the caller is not a party to
134
+ * (`isYou` all-false). Pass `partyOnly: true` for a true "mine": only
135
+ * agreements where the caller (key owner or impersonated agent) is a party.
136
+ */
137
+ partyOnly?: boolean;
131
138
  }
132
139
  export declare function getMyAgreements(filters: GetMyAgreementsFilters | undefined, creds: Creds): Promise<Agreement[]>;
133
140
  export declare function getAgreement(agreementId: string, creds: Creds): Promise<Agreement | null>;
@@ -284,6 +284,8 @@ export async function getMyAgreements(filters = {}, creds) {
284
284
  url.searchParams.set('proposalStatus', filters.proposalStatus);
285
285
  if (filters.hasTask != null)
286
286
  url.searchParams.set('hasTask', String(filters.hasTask));
287
+ if (filters.partyOnly)
288
+ url.searchParams.set('partyOnly', 'true');
287
289
  let res;
288
290
  try {
289
291
  res = await fetch(url.toString(), { method: 'GET', headers: buildHeaders(creds) });
@@ -1,4 +1,5 @@
1
1
  import 'dotenv/config';
2
+ import type { GrantView } from './grants.js';
2
3
  export declare function assertNoLeakedConnectionSecret(serialized: string): void;
3
4
  /** Thrown by ConnectionsClient with the HTTP status and raw body attached. */
4
5
  export interface ConnectionsError extends Error {
@@ -11,6 +12,23 @@ export interface ConnectionProxyParams {
11
12
  action: string;
12
13
  payload?: unknown;
13
14
  }
15
+ /** A grant row from the per-connection lister (GET /connections/:id/grants). */
16
+ export interface ConnectionGrant {
17
+ grantId?: string;
18
+ holderId?: string;
19
+ caveats?: unknown[];
20
+ [key: string]: unknown;
21
+ }
22
+ /**
23
+ * ZIG-641 / ZIG-648 — every connection grant the acting agent holds, grouped by
24
+ * connection, via the unified GET /grants. `provider` comes from the grant's
25
+ * resolved scope label.
26
+ */
27
+ export interface ConnectionWithGrants {
28
+ connectionId: string;
29
+ provider: string | null;
30
+ grants: GrantView[];
31
+ }
14
32
  export interface McpConnectionRequestParams {
15
33
  serverUrl: string;
16
34
  tools: string[];
@@ -53,7 +71,15 @@ export declare class ConnectionsClient {
53
71
  */
54
72
  listGrants({ connectionId }: {
55
73
  connectionId: string;
56
- }): Promise<unknown[]>;
74
+ }): Promise<ConnectionGrant[]>;
75
+ /**
76
+ * ZIG-641 / ZIG-956 — cross-connection discovery over the unified GET /grants:
77
+ * every live connection grant this agent holds, grouped by connection, so a
78
+ * proxy caller's connectionId/grantId no longer has to arrive out of band.
79
+ * Moved here from ziggs-mcp's inline helper. The response is scanned
80
+ * defensively for leaked secrets, as `proxy` does.
81
+ */
82
+ listForHolder(): Promise<ConnectionWithGrants[]>;
57
83
  /** Issue a connection grant to an agent holder (connection owner side). */
58
84
  issueGrant({ connectionId, holderId, caveats, }: {
59
85
  connectionId: string;
@@ -1,6 +1,7 @@
1
1
  import 'dotenv/config';
2
2
  import { getBackendUrl } from '../utils/urlUtils.js';
3
3
  import { buildOperatorHeaders } from './operatorHeaders.js';
4
+ import { GrantsClient } from './GrantsClient.js';
4
5
  // ZIG-569 — defense-in-depth mirror of the backend leak-guard
5
6
  // (assertProxyResponseDoesNotLeakTokens). The backend strips the *specific*
6
7
  // vault token from the response; clients never see that token, so this layer
@@ -82,6 +83,34 @@ export class ConnectionsClient {
82
83
  const grants = res['grants'] || [];
83
84
  return grants.filter((g) => g['holderId'] === this.agentId);
84
85
  }
86
+ /**
87
+ * ZIG-641 / ZIG-956 — cross-connection discovery over the unified GET /grants:
88
+ * every live connection grant this agent holds, grouped by connection, so a
89
+ * proxy caller's connectionId/grantId no longer has to arrive out of band.
90
+ * Moved here from ziggs-mcp's inline helper. The response is scanned
91
+ * defensively for leaked secrets, as `proxy` does.
92
+ */
93
+ async listForHolder() {
94
+ const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl);
95
+ // All pages of the agent's live connection grants (not just the first page).
96
+ const items = await grantsClient.listAllGrants({
97
+ scopeKind: 'connection',
98
+ health: 'active',
99
+ });
100
+ const byConnection = new Map();
101
+ for (const g of items) {
102
+ const connectionId = g.scope.id;
103
+ let group = byConnection.get(connectionId);
104
+ if (!group) {
105
+ group = { connectionId, provider: g.scope.label ?? null, grants: [] };
106
+ byConnection.set(connectionId, group);
107
+ }
108
+ group.grants.push(g);
109
+ }
110
+ const result = [...byConnection.values()];
111
+ assertNoLeakedConnectionSecret(JSON.stringify(result));
112
+ return result;
113
+ }
85
114
  /** Issue a connection grant to an agent holder (connection owner side). */
86
115
  async issueGrant({ connectionId, holderId, caveats, }) {
87
116
  if (!connectionId)
@@ -16,10 +16,26 @@ export interface ListGrantsQuery {
16
16
  cursor?: string;
17
17
  limit?: number;
18
18
  }
19
+ /**
20
+ * ZIG-893 / ZIG-956 — a grant rail the caller's operator key cannot read, named
21
+ * by the backend on `GET /grants` (it silently drops those rails from `items`,
22
+ * so the unified list tool names what it isn't entitled to instead of
23
+ * presenting a short list as if it were complete). Replaces the client-side
24
+ * mirror of the backend scope table + JWT decode (the retired grantRails.ts).
25
+ */
26
+ export interface UnreadableRail {
27
+ rail: 'context' | 'connection' | 'wallet';
28
+ requiredScope: string;
29
+ }
19
30
  export interface ListGrantsResult {
20
31
  items: GrantView[];
21
32
  nextCursor: string | null;
22
33
  hasMore: boolean;
34
+ /**
35
+ * Rails the caller can't read, per the backend. Absent when the backend
36
+ * predates ZIG-956 or every requested rail was readable.
37
+ */
38
+ unreadableRails?: UnreadableRail[];
23
39
  }
24
40
  /**
25
41
  * ZIG-648 — unified grant listing across every rail. `GET /grants` returns the
@@ -48,6 +48,9 @@ export class GrantsClient {
48
48
  items: parsed.items ?? [],
49
49
  nextCursor: parsed.nextCursor ?? null,
50
50
  hasMore: parsed.hasMore ?? false,
51
+ ...(parsed.unreadableRails?.length
52
+ ? { unreadableRails: parsed.unreadableRails }
53
+ : {}),
51
54
  };
52
55
  }
53
56
  /**