@ziggs-ai/api-client 0.9.10 → 0.9.12
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 +1 -1
- package/dist/ConnectionManager.d.ts +1 -1
- package/dist/ConnectionManager.js +1 -1
- package/dist/capabilities/agreements.d.ts +1 -1
- package/dist/capabilities/agreements.js +1 -1
- package/dist/capabilities/artifacts.d.ts +3 -3
- package/dist/capabilities/artifacts.js +6 -6
- package/dist/capabilities/chat.d.ts +4 -4
- package/dist/capabilities/chat.js +5 -5
- package/dist/capabilities/connections.js +13 -8
- package/dist/capabilities/context.d.ts +4 -4
- package/dist/capabilities/context.js +6 -6
- package/dist/capabilities/discovery.js +1 -1
- package/dist/capabilities/grants.d.ts +3 -3
- package/dist/capabilities/grants.js +3 -3
- package/dist/capabilities/links.js +1 -1
- package/dist/capabilities/marketplace.d.ts +1 -1
- package/dist/capabilities/marketplace.js +1 -1
- package/dist/capabilities/payments.js +1 -1
- package/dist/capabilities/proposeProviderId.d.ts +1 -1
- package/dist/capabilities/proposeProviderId.js +1 -1
- package/dist/capabilities/types.d.ts +3 -3
- package/dist/capabilities/types.js +2 -2
- package/dist/http/AgentSearchClient.d.ts +1 -2
- package/dist/http/AgreementClient.d.ts +11 -21
- package/dist/http/AgreementClient.js +29 -59
- package/dist/http/ArtifactsClient.d.ts +8 -8
- package/dist/http/ArtifactsClient.js +10 -10
- package/dist/http/ChatClient.d.ts +3 -3
- package/dist/http/ChatClient.js +6 -6
- package/dist/http/ConnectionsClient.d.ts +8 -8
- package/dist/http/ConnectionsClient.js +7 -7
- package/dist/http/ContextDiscoveryClient.d.ts +1 -1
- package/dist/http/ContextGrantsClient.d.ts +7 -7
- package/dist/http/ContextGrantsClient.js +3 -3
- package/dist/http/ContextReadClient.d.ts +3 -3
- package/dist/http/ContextReadClient.js +4 -4
- package/dist/http/GrantsClient.d.ts +4 -4
- package/dist/http/GrantsClient.js +1 -1
- package/dist/http/InboxClient.d.ts +9 -5
- package/dist/http/InboxClient.js +13 -6
- package/dist/http/MarketplaceClient.js +3 -3
- package/dist/http/OrgsClient.d.ts +10 -10
- package/dist/http/OrgsClient.js +11 -11
- package/dist/http/PaymentsClient.d.ts +4 -4
- package/dist/http/PaymentsClient.js +7 -7
- package/dist/http/TaskClient.d.ts +6 -6
- package/dist/http/TaskClient.js +3 -3
- package/dist/http/agreementFlows.d.ts +3 -3
- package/dist/http/agreementFlows.js +2 -2
- package/dist/http/grants.d.ts +1 -1
- package/dist/http/grants.js +1 -1
- package/dist/http/index.js +1 -1
- package/dist/http/operatorHeaders.d.ts +2 -2
- package/dist/http/operatorHeaders.js +2 -2
- package/dist/index.js +2 -2
- package/dist/shared/apiError.d.ts +1 -1
- package/dist/shared/apiError.js +1 -1
- package/dist/shared/rateLimit.d.ts +2 -2
- package/dist/shared/rateLimit.js +2 -2
- package/dist/types.d.ts +30 -28
- package/dist/types.js +4 -4
- package/package.json +2 -2
|
@@ -13,7 +13,7 @@ function buildHeaders(creds) {
|
|
|
13
13
|
'content-type': 'application/json',
|
|
14
14
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
15
15
|
'X-Agent-Id': creds.agentId,
|
|
16
|
-
//
|
|
16
|
+
// the wake's lane, so the backend can fence this call to the
|
|
17
17
|
// engagement it belongs to rather than the agent's whole authority.
|
|
18
18
|
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
19
19
|
};
|
|
@@ -29,10 +29,10 @@ function assertCreds(creds, op) {
|
|
|
29
29
|
*
|
|
30
30
|
* A link is reach, not commerce: it carries no money, no escrow, no execution
|
|
31
31
|
* state and no approvals ledger. Handing the raw document over anyway put Mongo
|
|
32
|
-
* bookkeeping in front of an LLM, which is what
|
|
32
|
+
* bookkeeping in front of an LLM, which is what forbade.
|
|
33
33
|
*
|
|
34
34
|
* Lives here rather than in `capabilities/links.ts` because this is where the
|
|
35
|
-
* rule is applied
|
|
35
|
+
* rule is applied; that module re-exports it so the public name is
|
|
36
36
|
* unchanged.
|
|
37
37
|
*/
|
|
38
38
|
export function linkSummary(a) {
|
|
@@ -62,7 +62,7 @@ export function linkSummary(a) {
|
|
|
62
62
|
* be written out at six call sites across the SDK runner, the MCP tools and the
|
|
63
63
|
* capability layer, and three verbs never got it: `agreement_counter`,
|
|
64
64
|
* `agreement_fulfill` and `agreement_subcontract` returned the raw document
|
|
65
|
-
|
|
65
|
+
*. Applying it at the parse boundary means a new verb inherits the
|
|
66
66
|
* rule instead of remembering to opt in, and no surface can word its own verdict.
|
|
67
67
|
*
|
|
68
68
|
* Typed as `Agreement` on the way out: every key the summary keeps IS an
|
|
@@ -80,7 +80,7 @@ export async function proposeAgreement(proposalData, creds) {
|
|
|
80
80
|
const headers = buildHeaders(creds);
|
|
81
81
|
if (idempotencyKey)
|
|
82
82
|
headers['Idempotency-Key'] = idempotencyKey;
|
|
83
|
-
//
|
|
83
|
+
// canonical REST path. Backend keeps /propose serving the
|
|
84
84
|
// identical handler with a Deprecation/Sunset header until PR-F removes it.
|
|
85
85
|
const res = await fetch(`${getAgreementBaseUrl()}/proposals`, {
|
|
86
86
|
method: 'POST',
|
|
@@ -121,11 +121,11 @@ export async function delegateAgreement(proposalData, creds) {
|
|
|
121
121
|
const headers = buildHeaders(creds);
|
|
122
122
|
if (idempotencyKey)
|
|
123
123
|
headers['Idempotency-Key'] = idempotencyKey;
|
|
124
|
-
//
|
|
124
|
+
// parentAgreementId in path AND body. Backend's
|
|
125
125
|
// `DelegateTaskDto` validates `parentAgreementId` as a required string;
|
|
126
126
|
// controller (`POST /agreements/:parentAgreementId/delegations`) merges
|
|
127
127
|
// path over body, so duplicating the value is harmless. Stripping it
|
|
128
|
-
// from the body (as
|
|
128
|
+
// from the body (as #72 did) trips DTO validation and returns
|
|
129
129
|
// `400: parentAgreementId must be a string` → infinite retry loop in
|
|
130
130
|
// `agreement_subcontract`. Send both, let the path win.
|
|
131
131
|
const bodyWithParent = { ...proposalData };
|
|
@@ -146,7 +146,7 @@ export async function delegateAgreement(proposalData, creds) {
|
|
|
146
146
|
return shapeAgreement(data['agreement']);
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
149
|
-
* Approve or reject a pending agreement (
|
|
149
|
+
* Approve or reject a pending agreement (canonical client path).
|
|
150
150
|
*
|
|
151
151
|
* Routes to `POST /claim` (open or org-scoped broadcast) or
|
|
152
152
|
* `PUT /approvals/:partyId` (ledger). Legacy rows without approvals[] are
|
|
@@ -170,14 +170,14 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
170
170
|
});
|
|
171
171
|
const proposedTo = agreement.parties?.proposedTo;
|
|
172
172
|
if (isBroadcastTarget(proposedTo) || isBroadcastTarget(agreement.parties?.payer)) {
|
|
173
|
-
//
|
|
173
|
+
// respond is approve/reject on DIRECT proposals only. An open
|
|
174
174
|
// broadcast (quest, standing offer, link invite) has no per-recipient
|
|
175
175
|
// approval slot — a BYSTANDER can neither approve nor reject it
|
|
176
|
-
|
|
176
|
+
//; their move is to claim it, which has its own verb.
|
|
177
177
|
//
|
|
178
|
-
//
|
|
178
|
+
// with one exception — the caller who HOLDS a pending approval
|
|
179
179
|
// on the row. A hand-off pins its provider and that provider's consent
|
|
180
|
-
// is a real ledger slot even on an open broadcast
|
|
180
|
+
// is a real ledger slot even on an open broadcast; this guard
|
|
181
181
|
// used to reject before looking, so the provider could not consent
|
|
182
182
|
// through ANY surface and the hand-off sat unclaimable forever. The
|
|
183
183
|
// backend keeps a consented broadcast OPEN for claims.
|
|
@@ -188,20 +188,9 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
188
188
|
else if (!partyId) {
|
|
189
189
|
throw new Error(`No pending approval entry for this operator on agreement ${agreementId}`);
|
|
190
190
|
}
|
|
191
|
-
// ZIG-1087 — the slot
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
// sending this would 403 with "You can only submit your own approval" —
|
|
195
|
-
// a condition, from which the caller cannot tell that no retry will ever
|
|
196
|
-
// work. Withholding consent from delegates is deliberate; being silent about
|
|
197
|
-
// it is not.
|
|
198
|
-
if (partyId && partyId !== creds.agentId) {
|
|
199
|
-
throw new Error(`Agreement ${agreementId} is waiting on ${partyId} — your principal, not you. ` +
|
|
200
|
-
`Consent is the human's to give: a delegate can submit its own approval slot and never its principal's, ` +
|
|
201
|
-
`so there is nothing to retry here and no tool that changes it. ` +
|
|
202
|
-
`Ask your human to approve or reject it${opts.appUrl ? ` at ${opts.appUrl}` : ' in the Ziggs app, under Agreements'}, ` +
|
|
203
|
-
`then read the agreement again to see the outcome.`);
|
|
204
|
-
}
|
|
191
|
+
// ZIG-1087 / ZIG-1316 — if the slot is the principal's, the server accepts
|
|
192
|
+
// only under a live approval-authority grant (else 403). Do not pre-refuse
|
|
193
|
+
// here; the PUT is the source of truth.
|
|
205
194
|
return approveAgreementAsParty(agreementId, partyId, action === 'approve' ? 'approved' : 'rejected', creds);
|
|
206
195
|
}
|
|
207
196
|
/**
|
|
@@ -215,7 +204,7 @@ export function resolvePendingApprovalPartyId(facts, candidateIds) {
|
|
|
215
204
|
return id;
|
|
216
205
|
}
|
|
217
206
|
// A row with no pending ledger entries is a legacy one (approvals[] predates
|
|
218
|
-
//
|
|
207
|
+
//), and there the named responder slot IS the decision.
|
|
219
208
|
const proposedTo = facts.proposedTo ?? null;
|
|
220
209
|
if (proposedTo &&
|
|
221
210
|
actorIds.includes(proposedTo) &&
|
|
@@ -225,7 +214,7 @@ export function resolvePendingApprovalPartyId(facts, candidateIds) {
|
|
|
225
214
|
return null;
|
|
226
215
|
}
|
|
227
216
|
/**
|
|
228
|
-
*
|
|
217
|
+
* resolve which approvals.partyId the current operator may submit.
|
|
229
218
|
* Checks pending ledger entries against impersonated agent id and owner principal.
|
|
230
219
|
*/
|
|
231
220
|
export function resolveMyPendingApprovalPartyId(agreement, opts) {
|
|
@@ -242,7 +231,7 @@ export function resolveMyPendingApprovalPartyId(agreement, opts) {
|
|
|
242
231
|
*
|
|
243
232
|
* `partyId` MUST match the authenticated actor (`actor.id === partyId`): owner
|
|
244
233
|
* principal when approving as human, delegate agent id when impersonating.
|
|
245
|
-
* Canonical for hire, service, link, and quest proposals
|
|
234
|
+
* Canonical for hire, service, link, and quest proposals.
|
|
246
235
|
*/
|
|
247
236
|
export async function approveAgreementAsParty(agreementId, partyId, status, creds) {
|
|
248
237
|
if (!agreementId)
|
|
@@ -309,10 +298,10 @@ export async function getAgreementStatus(agreementId, creds) {
|
|
|
309
298
|
}
|
|
310
299
|
export async function listAgreements(filters = {}, creds) {
|
|
311
300
|
assertCreds(creds, 'list agreements');
|
|
312
|
-
//
|
|
301
|
+
// return an empty array only for a genuine empty 200 result. Any
|
|
313
302
|
// failure (non-2xx / network) throws so the MCP tool reports a real error
|
|
314
|
-
// instead of "you have no agreements".
|
|
315
|
-
// Canonical query path: GET /agreements?scope=&status=&engagementKind
|
|
303
|
+
// instead of "you have no agreements". HTTP failures are ApiError.
|
|
304
|
+
// Canonical query path: GET /agreements?scope=&status=&engagementKind=&..
|
|
316
305
|
const url = new URL(getAgreementBaseUrl());
|
|
317
306
|
if (filters.status)
|
|
318
307
|
url.searchParams.set('status', filters.status);
|
|
@@ -342,7 +331,7 @@ export async function listAgreements(filters = {}, creds) {
|
|
|
342
331
|
}
|
|
343
332
|
export async function getMyAgreements(filters = {}, creds) {
|
|
344
333
|
assertCreds(creds, 'get my agreements');
|
|
345
|
-
//
|
|
334
|
+
// empty array only for a genuine empty 200; any failure throws so
|
|
346
335
|
// the tool surfaces a real error rather than "no agreements" (see listAgreements).
|
|
347
336
|
// Canonical query path: GET /agreements?scope=mine returns the enriched
|
|
348
337
|
// shape (tasks + originChatId + isYou flags).
|
|
@@ -386,10 +375,10 @@ async function getAgreementDocument(agreementId, creds) {
|
|
|
386
375
|
if (!agreementId)
|
|
387
376
|
return null;
|
|
388
377
|
assertCreds(creds, 'get agreement');
|
|
389
|
-
//
|
|
378
|
+
// a genuine 404 (and 200-with-no-agreement) is a real "not found"
|
|
390
379
|
// and returns null. Every other failure (403/5xx/network) must throw so the
|
|
391
380
|
// caller can tell "does not exist" from "could not fetch" instead of a 403
|
|
392
|
-
// masquerading as not-found.
|
|
381
|
+
// masquerading as not-found. thrown as ApiError for toolError.
|
|
393
382
|
let res;
|
|
394
383
|
try {
|
|
395
384
|
res = await fetch(`${getAgreementBaseUrl()}/${agreementId}`, {
|
|
@@ -441,7 +430,7 @@ export async function revokeAgreement(agreementId, creds) {
|
|
|
441
430
|
if (!agreementId)
|
|
442
431
|
throw new Error('agreementId is required for revocation');
|
|
443
432
|
assertCreds(creds, 'agreement revocation');
|
|
444
|
-
//
|
|
433
|
+
// canonical REST verb. Legacy POST /agreements/:id/revoke stays
|
|
445
434
|
// serving the identical handler (with Deprecation headers) until PR-F.
|
|
446
435
|
const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}`, {
|
|
447
436
|
method: 'DELETE',
|
|
@@ -458,7 +447,7 @@ export async function revokeAgreement(agreementId, creds) {
|
|
|
458
447
|
const envelope = data;
|
|
459
448
|
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
460
449
|
}
|
|
461
|
-
/**
|
|
450
|
+
/** mark an agreement fulfilled (complete). A provider closing its own
|
|
462
451
|
* delivered work — party-gated server-side. */
|
|
463
452
|
export async function fulfillAgreement(agreementId, creds) {
|
|
464
453
|
if (!agreementId)
|
|
@@ -480,7 +469,7 @@ export async function fulfillAgreement(agreementId, creds) {
|
|
|
480
469
|
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
481
470
|
}
|
|
482
471
|
/**
|
|
483
|
-
* Claim an open agreement
|
|
472
|
+
* Claim an open agreement. Three shapes are claimable:
|
|
484
473
|
* - open link invite (`engagementKind: link`, proposedTo everyone)
|
|
485
474
|
* - open broadcast quest (proposedTo 'everyone', status open)
|
|
486
475
|
* - org-scoped broadcast quest (proposedTo 'org') — the server rejects the
|
|
@@ -501,7 +490,7 @@ export async function claimAgreement(agreementId, creds) {
|
|
|
501
490
|
if (!data?.['agreement']) {
|
|
502
491
|
throw new Error('Invalid response: expected { ok, agreement } from POST /agreements/:id/claim');
|
|
503
492
|
}
|
|
504
|
-
//
|
|
493
|
+
// the route reports which kind of broadcast this turned out to be.
|
|
505
494
|
// A caller cannot work it out from the row — post-claim no sentinel is left,
|
|
506
495
|
// and a quest (you do the work) reads the same shape as a standing offer (you
|
|
507
496
|
// pay for it) unless you know which slot you landed in.
|
|
@@ -568,24 +557,6 @@ export async function getChatsForAgreement(agreementId, creds) {
|
|
|
568
557
|
return [];
|
|
569
558
|
}
|
|
570
559
|
}
|
|
571
|
-
export async function joinAgreement(agreementId, creds) {
|
|
572
|
-
if (!agreementId)
|
|
573
|
-
throw new Error('agreementId is required for join');
|
|
574
|
-
assertCreds(creds, 'join agreement');
|
|
575
|
-
const res = await fetch(`${getAgreementBaseUrl()}/${agreementId}/join`, {
|
|
576
|
-
method: 'POST',
|
|
577
|
-
headers: buildHeaders(creds),
|
|
578
|
-
});
|
|
579
|
-
if (!res.ok) {
|
|
580
|
-
const body = await res.text().catch(() => '');
|
|
581
|
-
throwApiError(res, body, `Join agreement failed: ${res.status} ${res.statusText}`);
|
|
582
|
-
}
|
|
583
|
-
const data = await res.json().catch(() => null);
|
|
584
|
-
if (!data?.['chatId']) {
|
|
585
|
-
throw new Error('Invalid response: expected { chatId } from /agreements/:id/join');
|
|
586
|
-
}
|
|
587
|
-
return data;
|
|
588
|
-
}
|
|
589
560
|
// ---------------------------------------------------------------------------
|
|
590
561
|
// Artifact links
|
|
591
562
|
// ---------------------------------------------------------------------------
|
|
@@ -696,7 +667,7 @@ export class AgreementClient {
|
|
|
696
667
|
constructor(operatorKey, agentId) {
|
|
697
668
|
if (!operatorKey)
|
|
698
669
|
throw new Error('AgreementClient: operatorKey is required');
|
|
699
|
-
// agentId may be absent for agent-scoped keys
|
|
670
|
+
// agentId may be absent for agent-scoped keys; the Creds-based
|
|
700
671
|
// standalone functions still assert it per call.
|
|
701
672
|
this.creds = { operatorKey, agentId };
|
|
702
673
|
}
|
|
@@ -717,7 +688,6 @@ export class AgreementClient {
|
|
|
717
688
|
claimAgreement(id) { return claimAgreement(id, this.creds); }
|
|
718
689
|
linkToChat(id, chatId, linkType) { return linkAgreementToChat(id, chatId, linkType ?? 'mention', this.creds); }
|
|
719
690
|
listChats(id) { return getChatsForAgreement(id, this.creds); }
|
|
720
|
-
join(id) { return joinAgreement(id, this.creds); }
|
|
721
691
|
linkArtifact(id, artifactId, linkType) { return linkArtifactToAgreement(id, artifactId, linkType ?? 'produced', this.creds); }
|
|
722
692
|
listArtifacts(id) { return getArtifactsForAgreement(id, this.creds); }
|
|
723
693
|
linkUser(id, userId, role) { return linkUserToAgreement(id, userId, role, this.creds); }
|
|
@@ -8,7 +8,7 @@ export interface ListArtifactsQuery {
|
|
|
8
8
|
agreementId?: string;
|
|
9
9
|
taskId?: string;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* `'me'` lists artifacts YOU authored, in any scope or none. The
|
|
12
12
|
* only listing that surfaces a free-standing artifact, since the others read
|
|
13
13
|
* attach tables. Mutually exclusive with the scope filters.
|
|
14
14
|
*/
|
|
@@ -80,14 +80,14 @@ export type ArtifactFileView = Record<string, unknown> & {
|
|
|
80
80
|
* they persist, are searchable, but are not visible to other chat parties.
|
|
81
81
|
*/
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
83
|
+
* turn a runtime lane id into a scope the backend can accept.
|
|
84
84
|
*
|
|
85
85
|
* A task with no origin chat runs on the lane `agrn-<agreementId>` (see
|
|
86
86
|
* AgentHost.laneSessionIdForTask). That is a routing key, not a chat: no such
|
|
87
87
|
* chat row exists, so passing it as `chatId` made every breadcrumb and recorded
|
|
88
88
|
* thought on an agreement lane come back 403 "not authorized for this scope" —
|
|
89
89
|
* a fictional scope reading like an auth failure. The work is agreement-scoped,
|
|
90
|
-
* so say so; same rule
|
|
90
|
+
* so say so; same rule settled for deliverables.
|
|
91
91
|
*/
|
|
92
92
|
export declare const AGREEMENT_LANE_PREFIX = "agrn-";
|
|
93
93
|
export declare function artifactScopeForSession(sessionId: string): {
|
|
@@ -102,7 +102,7 @@ export declare class ArtifactsClient {
|
|
|
102
102
|
/**
|
|
103
103
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
104
104
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
105
|
-
* @param laneId
|
|
105
|
+
* @param laneId the wake's lane (chat id, or `agrn-<agreementId>`),
|
|
106
106
|
* sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
|
|
107
107
|
* instead of returning every customer's deliverables in one call.
|
|
108
108
|
*/
|
|
@@ -122,7 +122,7 @@ export declare class ArtifactsClient {
|
|
|
122
122
|
}): Promise<void>;
|
|
123
123
|
write(input: WriteArtifactInput): Promise<void>;
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
125
|
+
* the deliberate-record variant: a deliverable the model chose to
|
|
126
126
|
* record must fail loudly and hand back the artifactId, unlike `write`'s
|
|
127
127
|
* soft-fail breadcrumb contract. This is the one wire call for both agent
|
|
128
128
|
* surfaces (the SDK artifact_record tool and ziggs-mcp's
|
|
@@ -132,7 +132,7 @@ export declare class ArtifactsClient {
|
|
|
132
132
|
artifactId?: string;
|
|
133
133
|
}>;
|
|
134
134
|
/**
|
|
135
|
-
*
|
|
135
|
+
* attach an existing artifact to a chat or a task.
|
|
136
136
|
*
|
|
137
137
|
* Attaching confers nothing on its own: it places the artifact inside the
|
|
138
138
|
* container, and that container's audience (chat members / task parties) can
|
|
@@ -160,12 +160,12 @@ export declare class ArtifactsClient {
|
|
|
160
160
|
filename: string;
|
|
161
161
|
}>;
|
|
162
162
|
reExtract(artifactId: string): Promise<ArtifactFileView>;
|
|
163
|
-
/**
|
|
163
|
+
/** shared fetch → text → throwApiError → JSON parse for file rail. */
|
|
164
164
|
private _post;
|
|
165
165
|
private _get;
|
|
166
166
|
private _attach;
|
|
167
167
|
/**
|
|
168
|
-
*
|
|
168
|
+
* at most one container, and none is fine.
|
|
169
169
|
*
|
|
170
170
|
* This used to demand exactly one, which is what made a deliverable fail at the
|
|
171
171
|
* last step when the model had not worked out which container it was standing
|
|
@@ -19,14 +19,14 @@ function resolveUploadBytes(input) {
|
|
|
19
19
|
* they persist, are searchable, but are not visible to other chat parties.
|
|
20
20
|
*/
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* turn a runtime lane id into a scope the backend can accept.
|
|
23
23
|
*
|
|
24
24
|
* A task with no origin chat runs on the lane `agrn-<agreementId>` (see
|
|
25
25
|
* AgentHost.laneSessionIdForTask). That is a routing key, not a chat: no such
|
|
26
26
|
* chat row exists, so passing it as `chatId` made every breadcrumb and recorded
|
|
27
27
|
* thought on an agreement lane come back 403 "not authorized for this scope" —
|
|
28
28
|
* a fictional scope reading like an auth failure. The work is agreement-scoped,
|
|
29
|
-
* so say so; same rule
|
|
29
|
+
* so say so; same rule settled for deliverables.
|
|
30
30
|
*/
|
|
31
31
|
export const AGREEMENT_LANE_PREFIX = 'agrn-';
|
|
32
32
|
export function artifactScopeForSession(sessionId) {
|
|
@@ -42,7 +42,7 @@ export class ArtifactsClient {
|
|
|
42
42
|
/**
|
|
43
43
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
44
44
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
45
|
-
* @param laneId
|
|
45
|
+
* @param laneId the wake's lane (chat id, or `agrn-<agreementId>`),
|
|
46
46
|
* sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
|
|
47
47
|
* instead of returning every customer's deliverables in one call.
|
|
48
48
|
*/
|
|
@@ -88,7 +88,7 @@ export class ArtifactsClient {
|
|
|
88
88
|
* locally (operators can still wire their own sink).
|
|
89
89
|
*/
|
|
90
90
|
async recordThought(sessionId, text, opts = {}) {
|
|
91
|
-
//
|
|
91
|
+
// `sessionId` may be an agreement lane, which is not a chat.
|
|
92
92
|
return this.write({
|
|
93
93
|
...artifactScopeForSession(sessionId),
|
|
94
94
|
text,
|
|
@@ -111,7 +111,7 @@ export class ArtifactsClient {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* the deliberate-record variant: a deliverable the model chose to
|
|
115
115
|
* record must fail loudly and hand back the artifactId, unlike `write`'s
|
|
116
116
|
* soft-fail breadcrumb contract. This is the one wire call for both agent
|
|
117
117
|
* surfaces (the SDK artifact_record tool and ziggs-mcp's
|
|
@@ -150,7 +150,7 @@ export class ArtifactsClient {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
|
-
*
|
|
153
|
+
* attach an existing artifact to a chat or a task.
|
|
154
154
|
*
|
|
155
155
|
* Attaching confers nothing on its own: it places the artifact inside the
|
|
156
156
|
* container, and that container's audience (chat members / task parties) can
|
|
@@ -252,7 +252,7 @@ export class ArtifactsClient {
|
|
|
252
252
|
throw new Error('ArtifactsClient.reExtract: artifactId is required');
|
|
253
253
|
return this._post(`/artifacts/${encodeURIComponent(artifactId)}/re-extract`, {}, 'artifact re-extract');
|
|
254
254
|
}
|
|
255
|
-
/**
|
|
255
|
+
/** shared fetch → text → throwApiError → JSON parse for file rail. */
|
|
256
256
|
async _post(path, body, label) {
|
|
257
257
|
const res = await fetch(`${getBackendUrl()}${path}`, {
|
|
258
258
|
method: 'POST',
|
|
@@ -280,7 +280,7 @@ export class ArtifactsClient {
|
|
|
280
280
|
return { success: true };
|
|
281
281
|
}
|
|
282
282
|
/**
|
|
283
|
-
*
|
|
283
|
+
* at most one container, and none is fine.
|
|
284
284
|
*
|
|
285
285
|
* This used to demand exactly one, which is what made a deliverable fail at the
|
|
286
286
|
* last step when the model had not worked out which container it was standing
|
|
@@ -290,12 +290,12 @@ export class ArtifactsClient {
|
|
|
290
290
|
*/
|
|
291
291
|
_assertScopeXor(input) {
|
|
292
292
|
if (input.chatId && input.agreementId) {
|
|
293
|
-
//
|
|
293
|
+
// the refusal names the recovery, because this is the one gate
|
|
294
294
|
// for every caller — the exposed tool surfaces inherit it rather than each
|
|
295
295
|
// wording their own, and a caller that used to have chatId silently
|
|
296
296
|
// dropped here now learns what to do instead. Wording matters: a bare
|
|
297
297
|
// "pick one" at the last step of a finished task is what the drop was
|
|
298
|
-
// added to avoid (
|
|
298
|
+
// added to avoid (dogfood).
|
|
299
299
|
throw new Error('pass at most one of chatId or agreementId — a deliverable under an ' +
|
|
300
300
|
'agreement wants agreementId alone (its parties see it); use chatId ' +
|
|
301
301
|
'only for a chat-scoped note. To put it in both places, record it ' +
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Creds } from '../types.js';
|
|
2
|
-
/** Shape of GET /chats/mine items — the backend's ChatReadDto
|
|
2
|
+
/** Shape of GET /chats/mine items — the backend's ChatReadDto. */
|
|
3
3
|
export interface ChatSummary {
|
|
4
4
|
chatId: string;
|
|
5
5
|
members: Array<{
|
|
@@ -18,7 +18,7 @@ export declare function openConversation(participantId: string, creds: Creds, {
|
|
|
18
18
|
export interface SendChatMessageInput {
|
|
19
19
|
chatId: string;
|
|
20
20
|
/**
|
|
21
|
-
* Recipient id. Optional
|
|
21
|
+
* Recipient id. Optional: when omitted, the backend infers the
|
|
22
22
|
* receiver if the chat has exactly one other member (one agent, or one
|
|
23
23
|
* other human). Provide it explicitly in chats with multiple members.
|
|
24
24
|
*/
|
|
@@ -62,7 +62,7 @@ export type AddChatMemberResult = {
|
|
|
62
62
|
chat?: unknown;
|
|
63
63
|
};
|
|
64
64
|
/**
|
|
65
|
-
* POST /chats/:chatId/members — add user/agent; agent-invite may return pending admission
|
|
65
|
+
* POST /chats/:chatId/members — add user/agent; agent-invite may return pending admission.
|
|
66
66
|
*/
|
|
67
67
|
export declare function addChatMember(input: AddChatMemberInput, creds: Creds): Promise<AddChatMemberResult>;
|
|
68
68
|
/**
|
package/dist/http/ChatClient.js
CHANGED
|
@@ -6,7 +6,7 @@ function buildHeaders(creds) {
|
|
|
6
6
|
'content-type': 'application/json',
|
|
7
7
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
8
8
|
'X-Agent-Id': creds.agentId,
|
|
9
|
-
//
|
|
9
|
+
// the wake's lane, so the backend can fence this call to the
|
|
10
10
|
// engagement it belongs to rather than the agent's whole authority.
|
|
11
11
|
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
12
12
|
};
|
|
@@ -36,7 +36,7 @@ export async function openConversation(participantId, creds, { newChat = false }
|
|
|
36
36
|
if (!data?.['chatId'] || typeof data['chatId'] !== 'string') {
|
|
37
37
|
throw new Error('Invalid response: expected { chatId } from POST /chats');
|
|
38
38
|
}
|
|
39
|
-
//
|
|
39
|
+
// left UNDEFINED rather than defaulted when the backend does not
|
|
40
40
|
// send it. A backend from before this field cannot be assumed to have created
|
|
41
41
|
// the room — defaulting to false would report a reused conversation as fresh,
|
|
42
42
|
// which is worse than saying nothing.
|
|
@@ -47,7 +47,7 @@ export async function openConversation(participantId, creds, { newChat = false }
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
* POST /chats/:chatId/members — add user/agent; agent-invite may return pending admission
|
|
50
|
+
* POST /chats/:chatId/members — add user/agent; agent-invite may return pending admission.
|
|
51
51
|
*/
|
|
52
52
|
export async function addChatMember(input, creds) {
|
|
53
53
|
assertCreds(creds, 'add chat member');
|
|
@@ -111,9 +111,9 @@ export async function sendChatMessage(input, creds) {
|
|
|
111
111
|
}
|
|
112
112
|
export async function listMyChats(creds) {
|
|
113
113
|
assertCreds(creds, 'list my chats');
|
|
114
|
-
//
|
|
114
|
+
// empty array only for a genuine empty 200; any failure (non-2xx /
|
|
115
115
|
// network) throws so ziggs_chat_list reports a real error instead of "you
|
|
116
|
-
// have no chats".
|
|
116
|
+
// have no chats". HTTP failures are ApiError (status/body/code).
|
|
117
117
|
let res;
|
|
118
118
|
try {
|
|
119
119
|
res = await fetch(`${getBackendUrl()}/chats/mine`, {
|
|
@@ -142,7 +142,7 @@ export class ChatClient {
|
|
|
142
142
|
constructor(operatorKey, agentId) {
|
|
143
143
|
if (!operatorKey)
|
|
144
144
|
throw new Error('ChatClient: operatorKey is required');
|
|
145
|
-
// agentId may be absent for agent-scoped keys
|
|
145
|
+
// agentId may be absent for agent-scoped keys; the Creds-based
|
|
146
146
|
// standalone functions still assert it per call.
|
|
147
147
|
this.creds = { operatorKey, agentId };
|
|
148
148
|
}
|
|
@@ -19,7 +19,7 @@ export interface ConnectionGrant {
|
|
|
19
19
|
[key: string]: unknown;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* every connection grant the acting agent holds, grouped by
|
|
23
23
|
* connection, via the unified GET /grants. `provider` comes from the grant's
|
|
24
24
|
* resolved scope label.
|
|
25
25
|
*/
|
|
@@ -34,17 +34,17 @@ export interface McpConnectionRequestParams {
|
|
|
34
34
|
reason?: string;
|
|
35
35
|
/**
|
|
36
36
|
* Working chat the consent card is opened into. Required by the backend for
|
|
37
|
-
* the agent-tool path; the per-user gateway trigger
|
|
37
|
+
* the agent-tool path; the per-user gateway trigger omits it.
|
|
38
38
|
*/
|
|
39
39
|
chatId?: string;
|
|
40
40
|
/**
|
|
41
41
|
* Target the consent request at this end-user (X-On-Behalf-Of-User) instead
|
|
42
|
-
* of the operator — the per-user MCP gateway's connect prompt
|
|
42
|
+
* of the operator — the per-user MCP gateway's connect prompt.
|
|
43
43
|
*/
|
|
44
44
|
onBehalfOfUserId?: string;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* the one connections client for every surface. Consolidates the
|
|
48
48
|
* former agent-sdk private ZiggsConnectClient and ziggs-mcp's inline fetches
|
|
49
49
|
* (proxy, requestMcpConnection). Proxied provider calls never
|
|
50
50
|
* return the raw OAuth token; every proxy response is additionally scanned by
|
|
@@ -72,7 +72,7 @@ export declare class ConnectionsClient {
|
|
|
72
72
|
connectionId: string;
|
|
73
73
|
}): Promise<ConnectionGrant[]>;
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
75
|
+
* cross-connection discovery over the unified GET /grants:
|
|
76
76
|
* every live connection grant this agent holds, grouped by connection, so a
|
|
77
77
|
* proxy caller's connectionId/grantId no longer has to arrive out of band.
|
|
78
78
|
* Moved here from ziggs-mcp's inline helper. The response is scanned
|
|
@@ -89,7 +89,7 @@ export declare class ConnectionsClient {
|
|
|
89
89
|
* Attenuate (re-delegate) a connection grant with tighter caveats. Narrowing
|
|
90
90
|
* a grant whose connection you own mints immediately; a grant whose owner is
|
|
91
91
|
* a different party returns `{ status: 'pending_approval', agreementId }`
|
|
92
|
-
* (
|
|
92
|
+
* (consent flow) and no grant is minted yet.
|
|
93
93
|
*/
|
|
94
94
|
attenuateGrant({ connectionId, grantId, holderId, caveats, }: {
|
|
95
95
|
connectionId: string;
|
|
@@ -103,9 +103,9 @@ export declare class ConnectionsClient {
|
|
|
103
103
|
grantId: string;
|
|
104
104
|
}): Promise<unknown>;
|
|
105
105
|
/**
|
|
106
|
-
*
|
|
106
|
+
* agent-initiated MCP connection request: ask the principal to
|
|
107
107
|
* connect a remote MCP server and grant this agent the listed tools. Opens a
|
|
108
|
-
* connection-consent agreement in the working chat
|
|
108
|
+
* connection-consent agreement in the working chat; on approval the
|
|
109
109
|
* server is connected (if needed) and the agent is granted the tools.
|
|
110
110
|
*/
|
|
111
111
|
requestMcpConnection({ serverUrl, tools, reason, chatId, onBehalfOfUserId, }: McpConnectionRequestParams): Promise<Record<string, unknown>>;
|
|
@@ -2,7 +2,7 @@ import { getBackendUrl } from '../utils/urlUtils.js';
|
|
|
2
2
|
import { throwApiError } from '../shared/apiError.js';
|
|
3
3
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
4
|
import { GrantsClient } from './GrantsClient.js';
|
|
5
|
-
//
|
|
5
|
+
// defense-in-depth mirror of the backend leak-guard
|
|
6
6
|
// (assertProxyResponseDoesNotLeakTokens). The backend strips the *specific*
|
|
7
7
|
// vault token from the response; clients never see that token, so this layer
|
|
8
8
|
// instead pattern-scans the proxied body for high-signal provider credential
|
|
@@ -22,7 +22,7 @@ export function assertNoLeakedConnectionSecret(serialized) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* the one connections client for every surface. Consolidates the
|
|
26
26
|
* former agent-sdk private ZiggsConnectClient and ziggs-mcp's inline fetches
|
|
27
27
|
* (proxy, requestMcpConnection). Proxied provider calls never
|
|
28
28
|
* return the raw OAuth token; every proxy response is additionally scanned by
|
|
@@ -84,7 +84,7 @@ export class ConnectionsClient {
|
|
|
84
84
|
return grants.filter((g) => g['holderId'] === this.agentId);
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* cross-connection discovery over the unified GET /grants:
|
|
88
88
|
* every live connection grant this agent holds, grouped by connection, so a
|
|
89
89
|
* proxy caller's connectionId/grantId no longer has to arrive out of band.
|
|
90
90
|
* Moved here from ziggs-mcp's inline helper. The response is scanned
|
|
@@ -126,7 +126,7 @@ export class ConnectionsClient {
|
|
|
126
126
|
* Attenuate (re-delegate) a connection grant with tighter caveats. Narrowing
|
|
127
127
|
* a grant whose connection you own mints immediately; a grant whose owner is
|
|
128
128
|
* a different party returns `{ status: 'pending_approval', agreementId }`
|
|
129
|
-
* (
|
|
129
|
+
* (consent flow) and no grant is minted yet.
|
|
130
130
|
*/
|
|
131
131
|
async attenuateGrant({ connectionId, grantId, holderId, caveats, }) {
|
|
132
132
|
if (!connectionId)
|
|
@@ -146,9 +146,9 @@ export class ConnectionsClient {
|
|
|
146
146
|
return this._request('DELETE', `/connections/${encodeURIComponent(connectionId)}/grants/${encodeURIComponent(grantId)}`, undefined);
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
149
|
-
*
|
|
149
|
+
* agent-initiated MCP connection request: ask the principal to
|
|
150
150
|
* connect a remote MCP server and grant this agent the listed tools. Opens a
|
|
151
|
-
* connection-consent agreement in the working chat
|
|
151
|
+
* connection-consent agreement in the working chat; on approval the
|
|
152
152
|
* server is connected (if needed) and the agent is granted the tools.
|
|
153
153
|
*/
|
|
154
154
|
async requestMcpConnection({ serverUrl, tools, reason, chatId, onBehalfOfUserId, }) {
|
|
@@ -169,7 +169,7 @@ export class ConnectionsClient {
|
|
|
169
169
|
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
170
170
|
const text = await response.text().catch(() => '');
|
|
171
171
|
if (!response.ok) {
|
|
172
|
-
//
|
|
172
|
+
// ApiError (status/body/code); ConnectionsError remains the
|
|
173
173
|
// documented duck type for callers that branch on `.status`.
|
|
174
174
|
throwApiError(response, text, `${method} ${path} failed: ${response.status}`);
|
|
175
175
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Labels-only pointer to context the agent could request access to (P4).
|
|
3
|
-
*
|
|
3
|
+
* covers chats, agreements, and connections; connection labels are the
|
|
4
4
|
* provider name only (never tokens/account labels).
|
|
5
5
|
*/
|
|
6
6
|
export interface DiscoverableItem {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { GrantView } from './grants.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* added `artifact` — the narrowest context scope: one specific artifact,
|
|
4
4
|
* shared without sharing any chat or agreement it sits in. Still the context
|
|
5
5
|
* rail, not a new grant primitive.
|
|
6
6
|
*/
|
|
@@ -11,7 +11,7 @@ export interface ContextGrantScope {
|
|
|
11
11
|
id: string;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* @deprecated
|
|
14
|
+
* @deprecated context grants now serialise to the canonical
|
|
15
15
|
* {@link GrantView} like every other rail. This alias previously declared a
|
|
16
16
|
* flat `scopeKind`/`scopeId` + `revoked` shape that the backend never actually
|
|
17
17
|
* emitted (it sends nested `scope: { kind, id }` and folds temporal/watermark
|
|
@@ -43,13 +43,13 @@ export type DelegateContextGrantResult = {
|
|
|
43
43
|
agreementId: string;
|
|
44
44
|
ownerId?: string;
|
|
45
45
|
};
|
|
46
|
-
/** A single readable entry inside a grant's scope — id + label only
|
|
46
|
+
/** A single readable entry inside a grant's scope — id + label only. */
|
|
47
47
|
export interface ReachEntry {
|
|
48
48
|
id: string;
|
|
49
49
|
label: string;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* reach expansion — the chat/agreement ids a grant you hold actually
|
|
53
53
|
* covers, so an org/agreement-scoped grant becomes a concrete list you can
|
|
54
54
|
* `context_read` through (via=chat:<id> / agreement:<id>). Ids + labels only,
|
|
55
55
|
* never content. Org scope is capped; `truncatedChats`/`truncatedAgreements`
|
|
@@ -64,7 +64,7 @@ export interface GrantReachResult {
|
|
|
64
64
|
truncatedAgreements: number;
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* context grant management — list / issue / delegate / revoke.
|
|
68
68
|
*/
|
|
69
69
|
export declare class ContextGrantsClient {
|
|
70
70
|
private readonly operatorKey;
|
|
@@ -78,14 +78,14 @@ export declare class ContextGrantsClient {
|
|
|
78
78
|
issueGrant(input: IssueContextGrantInput): Promise<GrantView>;
|
|
79
79
|
delegateGrant(parentGrantId: string, input: DelegateContextGrantInput): Promise<DelegateContextGrantResult>;
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
81
|
+
* expand a grant you hold into the chat/agreement ids inside its
|
|
82
82
|
* scope. A grant is a fence, not a listing: discovery says "you hold
|
|
83
83
|
* org:acme", this says which chats/agreements that covers. Holder-only,
|
|
84
84
|
* labels-only, grant-fenced server-side.
|
|
85
85
|
*/
|
|
86
86
|
getReach(grantId: string): Promise<GrantReachResult>;
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
88
|
+
* share an artifact THIS agent authored with another agent.
|
|
89
89
|
*
|
|
90
90
|
* Not a delegation: an agent holds no grant over its own output, so there is
|
|
91
91
|
* no parent to attenuate. Authorship is the authority, held by the agent's
|