@ziggs-ai/api-client 0.10.3 → 0.11.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.
- package/dist/capabilities/agreementVerbs.d.ts +2 -2
- package/dist/capabilities/agreementVerbs.js +19 -19
- package/dist/capabilities/agreements.d.ts +1 -1
- package/dist/capabilities/agreements.js +5 -5
- package/dist/capabilities/artifacts.d.ts +4 -2
- package/dist/capabilities/artifacts.js +169 -34
- package/dist/capabilities/chat.d.ts +2 -3
- package/dist/capabilities/chat.js +5 -6
- package/dist/capabilities/connections.js +1 -1
- package/dist/capabilities/context.js +3 -0
- package/dist/capabilities/grants.d.ts +7 -6
- package/dist/capabilities/grants.js +9 -8
- package/dist/capabilities/index.d.ts +2 -2
- package/dist/capabilities/index.js +2 -2
- package/dist/capabilities/links.d.ts +1 -1
- package/dist/capabilities/links.js +8 -11
- package/dist/capabilities/marketplace.js +63 -13
- package/dist/capabilities/payments.d.ts +24 -8
- package/dist/capabilities/payments.js +28 -392
- package/dist/capabilities/proposeProviderId.d.ts +1 -1
- package/dist/capabilities/proposeProviderId.js +1 -1
- package/dist/http/AgreementClient.d.ts +28 -22
- package/dist/http/AgreementClient.js +26 -17
- package/dist/http/ArtifactsClient.d.ts +3 -3
- package/dist/http/ArtifactsClient.js +6 -8
- package/dist/http/ChatClient.d.ts +1 -0
- package/dist/http/ChatClient.js +4 -1
- package/dist/http/ConnectionsClient.js +12 -1
- package/dist/http/ContextGrantsClient.d.ts +15 -1
- package/dist/http/ContextGrantsClient.js +2 -0
- package/dist/http/ContextReadClient.d.ts +18 -8
- package/dist/http/ContextReadClient.js +4 -3
- package/dist/http/GrantsClient.d.ts +14 -0
- package/dist/http/GrantsClient.js +18 -2
- package/dist/http/InboxClient.d.ts +1 -1
- package/dist/http/InboxClient.js +1 -1
- package/dist/http/MarketplaceClient.d.ts +6 -6
- package/dist/http/MarketplaceClient.js +11 -11
- package/dist/http/TaskClient.d.ts +13 -2
- package/dist/http/TaskClient.js +4 -2
- package/dist/http/agreementFlows.d.ts +4 -4
- package/dist/http/agreementFlows.js +9 -10
- package/dist/http/grants.d.ts +28 -0
- package/dist/http/index.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +59 -22
- package/dist/types.js +6 -6
- package/package.json +1 -1
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* tools. Stated on the schema so a fresh agent does not burn a turn learning
|
|
4
4
|
* the rule from the validation error.
|
|
5
5
|
*/
|
|
6
|
-
export declare const AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION = "REQUIRED on a direct proposal: name who does the work \u2014 your own id (you are offering to work) or the proposedTo id (you are commissioning the recipient). Do not omit it when proposedTo is a person/agent id. Broadcast (proposedTo everyone/org): omit for a
|
|
6
|
+
export declare const AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION = "REQUIRED on a direct proposal: name who does the work \u2014 your own id (you are offering to work) or the proposedTo id (you are commissioning the recipient). Do not omit it when proposedTo is a person/agent id. Broadcast (proposedTo everyone/org): omit for a request (claimer works), or your own id for a standing offer (you work). A third-party id brokers and needs a matching published offer. Payer is always the non-providing side.";
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* tools. Stated on the schema so a fresh agent does not burn a turn learning
|
|
4
4
|
* the rule from the validation error.
|
|
5
5
|
*/
|
|
6
|
-
export const AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION = 'REQUIRED on a direct proposal: name who does the work — your own id (you are offering to work) or the proposedTo id (you are commissioning the recipient). Do not omit it when proposedTo is a person/agent id. Broadcast (proposedTo everyone/org): omit for a
|
|
6
|
+
export const AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION = 'REQUIRED on a direct proposal: name who does the work — your own id (you are offering to work) or the proposedTo id (you are commissioning the recipient). Do not omit it when proposedTo is a person/agent id. Broadcast (proposedTo everyone/org): omit for a request (claimer works), or your own id for a standing offer (you work). A third-party id brokers and needs a matching published offer. Payer is always the non-providing side.';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Creds, type Agreement, type EngagementKind, type BroadcastAudience } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Shared proposal terms. When `engagementKind` is omitted the server defaults to `service`.
|
|
4
|
-
* For an open buyer-broadcast
|
|
4
|
+
* For an open buyer-broadcast request, set `proposedTo` to a broadcast sentinel —
|
|
5
5
|
* `OPEN_AGREEMENT_TARGET` (`"everyone"`, fully public) or `ORG_AGREEMENT_TARGET`
|
|
6
6
|
* (`"org"`, scoped to your org). Prefer `proposeBroadcast({ audience })` over
|
|
7
7
|
* setting `proposedTo` by hand.
|
|
@@ -49,8 +49,9 @@ export type ProposeBroadcastInput = Omit<ProposeDirectInput, 'proposedTo'> & {
|
|
|
49
49
|
* A trust link's agreement, as every caller sees it.
|
|
50
50
|
*
|
|
51
51
|
* A link is reach, not commerce: it carries no money, no escrow, no execution
|
|
52
|
-
* state and no approvals ledger. Handing the raw document over anyway
|
|
53
|
-
* bookkeeping in front of an LLM, which is what
|
|
52
|
+
* state and no approvals ledger. Handing the raw document over anyway puts
|
|
53
|
+
* storage bookkeeping in front of an LLM, which is what this summary exists to
|
|
54
|
+
* prevent.
|
|
54
55
|
*
|
|
55
56
|
* Lives here rather than in `capabilities/links.ts` because this is where the
|
|
56
57
|
* rule is applied; that module re-exports it so the public name is
|
|
@@ -78,7 +79,7 @@ export declare function proposeAgreement(proposalData: ProposeDirectInput, creds
|
|
|
78
79
|
/** Propose a contract to one party (user or agent). Server defaults `engagementKind` to `service`. */
|
|
79
80
|
export declare function proposeDirectTo(input: ProposeDirectInput, creds: Creds): Promise<Agreement>;
|
|
80
81
|
/**
|
|
81
|
-
* Propose an open
|
|
82
|
+
* Propose an open request. `audience` ('everyone' default | 'org') picks the
|
|
82
83
|
* broadcast sentinel written to `proposedTo`. Server defaults `engagementKind`
|
|
83
84
|
* to `service`.
|
|
84
85
|
*/
|
|
@@ -145,7 +146,7 @@ export declare function resolveMyPendingApprovalPartyId(agreement: Agreement, op
|
|
|
145
146
|
*
|
|
146
147
|
* `partyId` MUST match the authenticated actor (`actor.id === partyId`): owner
|
|
147
148
|
* principal when approving as human, delegate agent id when impersonating.
|
|
148
|
-
* Canonical for hire, service, link, and
|
|
149
|
+
* Canonical for hire, service, link, and request proposals.
|
|
149
150
|
*/
|
|
150
151
|
export declare function approveAgreementAsParty(agreementId: string, partyId: string, status: 'approved' | 'rejected', creds: Creds): Promise<Agreement>;
|
|
151
152
|
export interface CounterAgreementData {
|
|
@@ -180,21 +181,26 @@ export interface GetMyAgreementsFilters {
|
|
|
180
181
|
export declare function getMyAgreements(filters: GetMyAgreementsFilters | undefined, creds: Creds): Promise<Agreement[]>;
|
|
181
182
|
/** One agreement, shaped: a link comes back as its summary. */
|
|
182
183
|
export declare function getAgreement(agreementId: string, creds: Creds): Promise<Agreement | null>;
|
|
183
|
-
export interface
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
price?: number;
|
|
188
|
-
lifecycle?: string;
|
|
189
|
-
expiresAt?: string;
|
|
190
|
-
maxExecutions?: number;
|
|
184
|
+
export interface CreateLinkBody {
|
|
185
|
+
/** Target agent for a direct link. Omit for an open, claimable invite. */
|
|
186
|
+
targetAgentId?: string;
|
|
187
|
+
/** Message shown to whoever is asked to approve or claim it. */
|
|
191
188
|
description?: string;
|
|
192
|
-
|
|
193
|
-
/** Link invites only: how many people may claim this one link. Default 1. */
|
|
189
|
+
/** Open invites only: how many people may claim this one link. Default 1. */
|
|
194
190
|
maxClaims?: number;
|
|
195
|
-
|
|
191
|
+
/** Which of the caller's own agents stands on their side of the link. */
|
|
192
|
+
asAgentId?: string;
|
|
196
193
|
}
|
|
197
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Create a trust link — a direct proposal to one agent, or an open invite.
|
|
196
|
+
*
|
|
197
|
+
* This posts to `POST /agreements/links`. It used to post to `POST /agreements`,
|
|
198
|
+
* which also created work agreements inline and skipped the gates the proposal
|
|
199
|
+
* and marketplace paths run. Work agreements go through `proposeAgreement`
|
|
200
|
+
* (`POST /agreements/proposals`) and the marketplace claim routes; this rail
|
|
201
|
+
* carries links and nothing else.
|
|
202
|
+
*/
|
|
203
|
+
export declare function createLink(body: CreateLinkBody, creds: Creds): Promise<{
|
|
198
204
|
ok: boolean;
|
|
199
205
|
agreement: Agreement;
|
|
200
206
|
}>;
|
|
@@ -208,13 +214,13 @@ export declare function fulfillAgreement(agreementId: string, creds: Creds): Pro
|
|
|
208
214
|
ok: boolean;
|
|
209
215
|
agreement: Agreement;
|
|
210
216
|
}>;
|
|
211
|
-
/** What an open-broadcast claim
|
|
212
|
-
export type ClaimedKind = 'link' | 'offer' | '
|
|
217
|
+
/** What an open-broadcast claim resolved to. ⚠️ Mirrors the server; it is authoritative. */
|
|
218
|
+
export type ClaimedKind = 'link' | 'offer' | 'request' | 'hand-off';
|
|
213
219
|
/**
|
|
214
220
|
* Claim an open agreement. Three shapes are claimable:
|
|
215
221
|
* - open link invite (`engagementKind: link`, proposedTo everyone)
|
|
216
|
-
* - open broadcast
|
|
217
|
-
* - org-scoped broadcast
|
|
222
|
+
* - open broadcast request (proposedTo 'everyone', status open)
|
|
223
|
+
* - org-scoped broadcast request (proposedTo 'org') — the server rejects the
|
|
218
224
|
* claim with 403 if the caller is not a member of the agreement's orgId
|
|
219
225
|
*
|
|
220
226
|
* POST /agreements/:id/claim — canonical; no partyId in body.
|
|
@@ -261,7 +267,7 @@ export declare class AgreementClient {
|
|
|
261
267
|
list(filters?: ListAgreementsFilters): Promise<Agreement[]>;
|
|
262
268
|
listMine(filters?: GetMyAgreementsFilters): Promise<Agreement[]>;
|
|
263
269
|
get(id: string): Promise<Agreement | null>;
|
|
264
|
-
|
|
270
|
+
createLink(data: CreateLinkBody): Promise<{
|
|
265
271
|
ok: boolean;
|
|
266
272
|
agreement: Agreement;
|
|
267
273
|
}>;
|
|
@@ -28,8 +28,9 @@ function assertCreds(creds, op) {
|
|
|
28
28
|
* A trust link's agreement, as every caller sees it.
|
|
29
29
|
*
|
|
30
30
|
* A link is reach, not commerce: it carries no money, no escrow, no execution
|
|
31
|
-
* state and no approvals ledger. Handing the raw document over anyway
|
|
32
|
-
* bookkeeping in front of an LLM, which is what
|
|
31
|
+
* state and no approvals ledger. Handing the raw document over anyway puts
|
|
32
|
+
* storage bookkeeping in front of an LLM, which is what this summary exists to
|
|
33
|
+
* prevent.
|
|
33
34
|
*
|
|
34
35
|
* Lives here rather than in `capabilities/links.ts` because this is where the
|
|
35
36
|
* rule is applied; that module re-exports it so the public name is
|
|
@@ -102,7 +103,7 @@ export async function proposeDirectTo(input, creds) {
|
|
|
102
103
|
return proposeAgreement(input, creds);
|
|
103
104
|
}
|
|
104
105
|
/**
|
|
105
|
-
* Propose an open
|
|
106
|
+
* Propose an open request. `audience` ('everyone' default | 'org') picks the
|
|
106
107
|
* broadcast sentinel written to `proposedTo`. Server defaults `engagementKind`
|
|
107
108
|
* to `service`.
|
|
108
109
|
*/
|
|
@@ -171,7 +172,7 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
171
172
|
const proposedTo = agreement.parties?.proposedTo;
|
|
172
173
|
if (isBroadcastTarget(proposedTo) || isBroadcastTarget(agreement.parties?.payer)) {
|
|
173
174
|
// respond is approve/reject on DIRECT proposals only. An open
|
|
174
|
-
// broadcast (
|
|
175
|
+
// broadcast (request, standing offer, link invite) has no per-recipient
|
|
175
176
|
// approval slot — a BYSTANDER can neither approve nor reject it
|
|
176
177
|
//; their move is to claim it, which has its own verb.
|
|
177
178
|
//
|
|
@@ -188,7 +189,7 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
188
189
|
else if (!partyId) {
|
|
189
190
|
throw new Error(`No pending approval entry for this operator on agreement ${agreementId}`);
|
|
190
191
|
}
|
|
191
|
-
//
|
|
192
|
+
// If the slot is the principal's, the server accepts
|
|
192
193
|
// only under a live approval-authority grant (else 403). Do not pre-refuse
|
|
193
194
|
// here; the PUT is the source of truth.
|
|
194
195
|
return approveAgreementAsParty(agreementId, partyId, action === 'approve' ? 'approved' : 'rejected', creds);
|
|
@@ -231,7 +232,7 @@ export function resolveMyPendingApprovalPartyId(agreement, opts) {
|
|
|
231
232
|
*
|
|
232
233
|
* `partyId` MUST match the authenticated actor (`actor.id === partyId`): owner
|
|
233
234
|
* principal when approving as human, delegate agent id when impersonating.
|
|
234
|
-
* Canonical for hire, service, link, and
|
|
235
|
+
* Canonical for hire, service, link, and request proposals.
|
|
235
236
|
*/
|
|
236
237
|
export async function approveAgreementAsParty(agreementId, partyId, status, creds) {
|
|
237
238
|
if (!agreementId)
|
|
@@ -405,23 +406,31 @@ export async function getAgreement(agreementId, creds) {
|
|
|
405
406
|
const agreement = await getAgreementDocument(agreementId, creds);
|
|
406
407
|
return agreement ? shapeAgreement(agreement) : null;
|
|
407
408
|
}
|
|
408
|
-
|
|
409
|
+
/**
|
|
410
|
+
* Create a trust link — a direct proposal to one agent, or an open invite.
|
|
411
|
+
*
|
|
412
|
+
* This posts to `POST /agreements/links`. It used to post to `POST /agreements`,
|
|
413
|
+
* which also created work agreements inline and skipped the gates the proposal
|
|
414
|
+
* and marketplace paths run. Work agreements go through `proposeAgreement`
|
|
415
|
+
* (`POST /agreements/proposals`) and the marketplace claim routes; this rail
|
|
416
|
+
* carries links and nothing else.
|
|
417
|
+
*/
|
|
418
|
+
export async function createLink(body, creds) {
|
|
409
419
|
if (!body)
|
|
410
|
-
throw new Error('Body is required for
|
|
411
|
-
assertCreds(creds, '
|
|
412
|
-
|
|
413
|
-
const res = await fetch(getAgreementBaseUrl(), {
|
|
420
|
+
throw new Error('Body is required for link creation');
|
|
421
|
+
assertCreds(creds, 'link creation');
|
|
422
|
+
const res = await fetch(`${getAgreementBaseUrl()}/links`, {
|
|
414
423
|
method: 'POST',
|
|
415
424
|
headers: buildHeaders(creds),
|
|
416
425
|
body: JSON.stringify(body),
|
|
417
426
|
});
|
|
418
427
|
if (!res.ok) {
|
|
419
428
|
const responseBody = await res.text().catch(() => '');
|
|
420
|
-
throwApiError(res, responseBody, `
|
|
429
|
+
throwApiError(res, responseBody, `Link creation failed: ${res.status} ${res.statusText}`);
|
|
421
430
|
}
|
|
422
431
|
const data = await res.json().catch(() => null);
|
|
423
432
|
if (!data?.['agreement']) {
|
|
424
|
-
throw new Error('Invalid response: expected { ok, agreement } from POST /agreements');
|
|
433
|
+
throw new Error('Invalid response: expected { ok, agreement } from POST /agreements/links');
|
|
425
434
|
}
|
|
426
435
|
const envelope = data;
|
|
427
436
|
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
@@ -471,8 +480,8 @@ export async function fulfillAgreement(agreementId, creds) {
|
|
|
471
480
|
/**
|
|
472
481
|
* Claim an open agreement. Three shapes are claimable:
|
|
473
482
|
* - open link invite (`engagementKind: link`, proposedTo everyone)
|
|
474
|
-
* - open broadcast
|
|
475
|
-
* - org-scoped broadcast
|
|
483
|
+
* - open broadcast request (proposedTo 'everyone', status open)
|
|
484
|
+
* - org-scoped broadcast request (proposedTo 'org') — the server rejects the
|
|
476
485
|
* claim with 403 if the caller is not a member of the agreement's orgId
|
|
477
486
|
*
|
|
478
487
|
* POST /agreements/:id/claim — canonical; no partyId in body.
|
|
@@ -492,7 +501,7 @@ export async function claimAgreement(agreementId, creds) {
|
|
|
492
501
|
}
|
|
493
502
|
// the route reports which kind of broadcast this turned out to be.
|
|
494
503
|
// A caller cannot work it out from the row — post-claim no sentinel is left,
|
|
495
|
-
// and a
|
|
504
|
+
// and a request (you do the work) reads the same shape as a standing offer (you
|
|
496
505
|
// pay for it) unless you know which slot you landed in.
|
|
497
506
|
const envelope = data;
|
|
498
507
|
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
@@ -682,7 +691,7 @@ export class AgreementClient {
|
|
|
682
691
|
list(filters) { return listAgreements(filters, this.creds); }
|
|
683
692
|
listMine(filters) { return getMyAgreements(filters, this.creds); }
|
|
684
693
|
get(id) { return getAgreement(id, this.creds); }
|
|
685
|
-
|
|
694
|
+
createLink(data) { return createLink(data, this.creds); }
|
|
686
695
|
revoke(id) { return revokeAgreement(id, this.creds); }
|
|
687
696
|
fulfill(id) { return fulfillAgreement(id, this.creds); }
|
|
688
697
|
claimAgreement(id) { return claimAgreement(id, this.creds); }
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Inline `POST /artifacts` body cap (matches backend
|
|
3
3
|
* `ARTIFACT_PUBLIC_TEXT_MAX_CHARS`). Over this → file rail or multi-part; the
|
|
4
|
-
* server does not auto-split
|
|
4
|
+
* server does not auto-split.
|
|
5
5
|
*/
|
|
6
6
|
export declare const ARTIFACT_INLINE_TEXT_MAX_CHARS = 50000;
|
|
7
|
-
/**
|
|
7
|
+
/** Named escape hatch for agents that hit the inline cap. */
|
|
8
8
|
export declare const ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT: string;
|
|
9
9
|
export type ArtifactVisibility = 'chat' | 'agent-private';
|
|
10
10
|
export interface ListArtifactsOptions {
|
|
@@ -112,7 +112,7 @@ export declare class ArtifactsClient {
|
|
|
112
112
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
113
113
|
* @param laneId the wake's lane (chat id, or `agrn-<agreementId>`),
|
|
114
114
|
* sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
|
|
115
|
-
*
|
|
115
|
+
* rather than spanning every engagement the agent has authored in.
|
|
116
116
|
*/
|
|
117
117
|
constructor(operatorKey: string, agentId?: string, laneId?: string);
|
|
118
118
|
list(q: ListArtifactsQuery, opts?: ListArtifactsOptions): Promise<ListArtifactsResult>;
|
|
@@ -5,10 +5,10 @@ import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Inline `POST /artifacts` body cap (matches backend
|
|
7
7
|
* `ARTIFACT_PUBLIC_TEXT_MAX_CHARS`). Over this → file rail or multi-part; the
|
|
8
|
-
* server does not auto-split
|
|
8
|
+
* server does not auto-split.
|
|
9
9
|
*/
|
|
10
10
|
export const ARTIFACT_INLINE_TEXT_MAX_CHARS = 50_000;
|
|
11
|
-
/**
|
|
11
|
+
/** Named escape hatch for agents that hit the inline cap. */
|
|
12
12
|
export const ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT = `text exceeds ${ARTIFACT_INLINE_TEXT_MAX_CHARS} characters. ` +
|
|
13
13
|
'For larger content use ziggs_artifact_upload_url (file rail), or split into ' +
|
|
14
14
|
'an index artifact plus part artifacts and list the part ids in the index. ' +
|
|
@@ -55,7 +55,7 @@ export class ArtifactsClient {
|
|
|
55
55
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
56
56
|
* @param laneId the wake's lane (chat id, or `agrn-<agreementId>`),
|
|
57
57
|
* sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
|
|
58
|
-
*
|
|
58
|
+
* rather than spanning every engagement the agent has authored in.
|
|
59
59
|
*/
|
|
60
60
|
constructor(operatorKey, agentId, laneId) {
|
|
61
61
|
if (!operatorKey)
|
|
@@ -134,7 +134,7 @@ export class ArtifactsClient {
|
|
|
134
134
|
}
|
|
135
135
|
this._assertScopeXor(input);
|
|
136
136
|
const text = input.text.trim();
|
|
137
|
-
//
|
|
137
|
+
// Refuse before the wire so MCP/SDK get a named escape hatch
|
|
138
138
|
// instead of a bare class-validator string.
|
|
139
139
|
if (text.length > ARTIFACT_INLINE_TEXT_MAX_CHARS) {
|
|
140
140
|
throw new Error(`ArtifactsClient.writeStrict: ${ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT}`);
|
|
@@ -309,10 +309,8 @@ export class ArtifactsClient {
|
|
|
309
309
|
if (input.chatId && input.agreementId) {
|
|
310
310
|
// the refusal names the recovery, because this is the one gate
|
|
311
311
|
// for every caller — the exposed tool surfaces inherit it rather than each
|
|
312
|
-
// wording their own
|
|
313
|
-
//
|
|
314
|
-
// "pick one" at the last step of a finished task is what the drop was
|
|
315
|
-
// added to avoid (dogfood).
|
|
312
|
+
// wording their own. Wording matters: this fires at the last step of a
|
|
313
|
+
// finished task, where a bare "pick one" leaves the deliverable unfiled.
|
|
316
314
|
throw new Error('pass at most one of chatId or agreementId — a deliverable under an ' +
|
|
317
315
|
'agreement wants agreementId alone (its parties see it); use chatId ' +
|
|
318
316
|
'only for a chat-scoped note. To put it in both places, record it ' +
|
|
@@ -21,6 +21,7 @@ export interface SendChatMessageInput {
|
|
|
21
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
|
+
* Sent on the wire as `{ id }`; the SDK still takes a string.
|
|
24
25
|
*/
|
|
25
26
|
receiverId?: string;
|
|
26
27
|
text: string;
|
package/dist/http/ChatClient.js
CHANGED
|
@@ -93,7 +93,10 @@ export async function sendChatMessage(input, creds) {
|
|
|
93
93
|
text: input.text,
|
|
94
94
|
entryType,
|
|
95
95
|
contentType,
|
|
96
|
-
|
|
96
|
+
// Canonical wire shape is `{ id, type? }`; bare strings are still
|
|
97
|
+
// accepted by the backend after edge normalize, but first-party
|
|
98
|
+
// clients send the object.
|
|
99
|
+
receiver: input.receiverId ? { id: input.receiverId } : undefined,
|
|
97
100
|
underAgreementId: input.underAgreementId,
|
|
98
101
|
}),
|
|
99
102
|
});
|
|
@@ -93,10 +93,21 @@ export class ConnectionsClient {
|
|
|
93
93
|
async listForHolder() {
|
|
94
94
|
const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl);
|
|
95
95
|
// All pages of the agent's live connection grants (not just the first page).
|
|
96
|
-
const items = await grantsClient.
|
|
96
|
+
const { items, unreadableRails } = await grantsClient.listAllGrantsWithRails({
|
|
97
97
|
scopeKind: 'connection',
|
|
98
98
|
health: 'active',
|
|
99
99
|
});
|
|
100
|
+
// An unreadable rail is not an empty one. Returning [] here made
|
|
101
|
+
// every caller say "no connection grant" when the truth was "this key may not
|
|
102
|
+
// look" — the needs gate refused work the agent could do, and the MCP tool
|
|
103
|
+
// told the owner to issue a grant that already existed. Throwing puts the
|
|
104
|
+
// missing scope in the message, where the person who can fix it will read it.
|
|
105
|
+
const railBlocked = unreadableRails.find((r) => r.rail === 'connection');
|
|
106
|
+
if (railBlocked && items.length === 0) {
|
|
107
|
+
throw new Error(`Cannot read this agent's connection grants: the operator key is missing the ` +
|
|
108
|
+
`"${railBlocked.requiredScope}" scope, so the grant list came back empty whether or not ` +
|
|
109
|
+
'a grant exists. Re-mint the key with that scope (the "launcher-mcp-broker" preset includes it).');
|
|
110
|
+
}
|
|
100
111
|
const byConnection = new Map();
|
|
101
112
|
for (const g of items) {
|
|
102
113
|
const connectionId = g.scope.id;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GrantView } from './grants.js';
|
|
1
|
+
import type { GrantAccessKind, GrantHolderKind, 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
|
|
@@ -21,12 +21,26 @@ export interface ContextGrantScope {
|
|
|
21
21
|
export type ContextGrantRecord = GrantView;
|
|
22
22
|
export interface IssueContextGrantInput {
|
|
23
23
|
holderId: string;
|
|
24
|
+
/**
|
|
25
|
+
* What kind of principal the holder is: a person, an organization, or an
|
|
26
|
+
* agent. Always stated, because an id says nothing about what it names — and
|
|
27
|
+
* an organization holder is how one grant covers a whole team.
|
|
28
|
+
*/
|
|
29
|
+
holderKind: GrantHolderKind;
|
|
30
|
+
/**
|
|
31
|
+
* What the holder may do: `read` to see the scope, `write` to take part in
|
|
32
|
+
* it, `admit` to bring others in as well. Defaults to `write`.
|
|
33
|
+
*/
|
|
34
|
+
kind?: GrantAccessKind;
|
|
24
35
|
scope: ContextGrantScope;
|
|
25
36
|
temporal?: ContextTemporal;
|
|
26
37
|
expiresAt?: string | null;
|
|
27
38
|
}
|
|
28
39
|
export interface DelegateContextGrantInput {
|
|
29
40
|
holderId: string;
|
|
41
|
+
holderKind: GrantHolderKind;
|
|
42
|
+
/** Never stronger than the grant it comes from; defaults to the same. */
|
|
43
|
+
kind?: GrantAccessKind;
|
|
30
44
|
scope: ContextGrantScope;
|
|
31
45
|
temporal: ContextTemporal;
|
|
32
46
|
expiresAt?: string | null;
|
|
@@ -43,17 +43,26 @@ export interface ContextReadEnvelope<T = unknown> {
|
|
|
43
43
|
latestSequence?: string | null;
|
|
44
44
|
}
|
|
45
45
|
/** Aggregated chat snapshot from `GET /context/snapshot` (follow-up). */
|
|
46
|
+
/**
|
|
47
|
+
* One participant on the snapshot roster. `kind` is the declared holder kind the
|
|
48
|
+
* server stamped on the row, so a reader takes what a participant IS from the
|
|
49
|
+
* row rather than from which array it arrived in. Optional so a backend that
|
|
50
|
+
* predates it still parses.
|
|
51
|
+
*/
|
|
52
|
+
export interface ContextSnapshotParticipant {
|
|
53
|
+
role: string;
|
|
54
|
+
kind?: string;
|
|
55
|
+
isYou?: boolean;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
}
|
|
46
58
|
export interface ContextSnapshotResult {
|
|
47
59
|
history: unknown[];
|
|
48
60
|
agreements: unknown[];
|
|
49
|
-
agents: Array<{
|
|
61
|
+
agents: Array<ContextSnapshotParticipant & {
|
|
50
62
|
agentId: string;
|
|
51
|
-
role: string;
|
|
52
|
-
isYou: boolean;
|
|
53
63
|
}>;
|
|
54
|
-
users: Array<{
|
|
64
|
+
users: Array<ContextSnapshotParticipant & {
|
|
55
65
|
userId: string;
|
|
56
|
-
role: string;
|
|
57
66
|
}>;
|
|
58
67
|
latestSequence?: string | null;
|
|
59
68
|
chatMissing?: boolean;
|
|
@@ -70,9 +79,10 @@ export declare class ContextReadClient {
|
|
|
70
79
|
/**
|
|
71
80
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
72
81
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
73
|
-
* @param laneId the wake's lane, sent as X-Ziggs-Lane.
|
|
74
|
-
*
|
|
75
|
-
*
|
|
82
|
+
* @param laneId the wake's lane, sent as X-Ziggs-Lane. The server fences a
|
|
83
|
+
* read to the lane's parties; without it, reach falls back to a wider
|
|
84
|
+
* test and a read can be authorised on a weaker basis than the caller
|
|
85
|
+
* intended. Send it on every read made while acting on a wake.
|
|
76
86
|
*/
|
|
77
87
|
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
78
88
|
read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
|
|
@@ -60,9 +60,10 @@ export class ContextReadClient {
|
|
|
60
60
|
/**
|
|
61
61
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
62
62
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
63
|
-
* @param laneId the wake's lane, sent as X-Ziggs-Lane.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* @param laneId the wake's lane, sent as X-Ziggs-Lane. The server fences a
|
|
64
|
+
* read to the lane's parties; without it, reach falls back to a wider
|
|
65
|
+
* test and a read can be authorised on a weaker basis than the caller
|
|
66
|
+
* intended. Send it on every read made while acting on a wake.
|
|
66
67
|
*/
|
|
67
68
|
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
68
69
|
if (!operatorKey)
|
|
@@ -66,4 +66,18 @@ export declare class GrantsClient {
|
|
|
66
66
|
* truncated. `maxPages` bounds the loop as a runaway guard.
|
|
67
67
|
*/
|
|
68
68
|
listAllGrants(query?: Omit<ListGrantsQuery, 'cursor'>, maxPages?: number): Promise<GrantView[]>;
|
|
69
|
+
/**
|
|
70
|
+
* The same sweep, keeping the part `listAllGrants` throws away: which rails the
|
|
71
|
+
* caller was not allowed to read.
|
|
72
|
+
*
|
|
73
|
+
* The distinction is not cosmetic. An empty page from an unreadable rail looks
|
|
74
|
+
* exactly like "you hold no grants", and a caller that cannot tell them apart
|
|
75
|
+
* reports the wrong one: a brokered connector agent with a live grant refused
|
|
76
|
+
* its task and told the buyer to ask for the grant they had just issued, because
|
|
77
|
+
* its key lacked `connections:read` and this method answered "none".
|
|
78
|
+
*/
|
|
79
|
+
listAllGrantsWithRails(query?: Omit<ListGrantsQuery, 'cursor'>, maxPages?: number): Promise<{
|
|
80
|
+
items: GrantView[];
|
|
81
|
+
unreadableRails: UnreadableRail[];
|
|
82
|
+
}>;
|
|
69
83
|
}
|
|
@@ -65,15 +65,31 @@ export class GrantsClient {
|
|
|
65
65
|
* truncated. `maxPages` bounds the loop as a runaway guard.
|
|
66
66
|
*/
|
|
67
67
|
async listAllGrants(query = {}, maxPages = 50) {
|
|
68
|
+
return (await this.listAllGrantsWithRails(query, maxPages)).items;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The same sweep, keeping the part `listAllGrants` throws away: which rails the
|
|
72
|
+
* caller was not allowed to read.
|
|
73
|
+
*
|
|
74
|
+
* The distinction is not cosmetic. An empty page from an unreadable rail looks
|
|
75
|
+
* exactly like "you hold no grants", and a caller that cannot tell them apart
|
|
76
|
+
* reports the wrong one: a brokered connector agent with a live grant refused
|
|
77
|
+
* its task and told the buyer to ask for the grant they had just issued, because
|
|
78
|
+
* its key lacked `connections:read` and this method answered "none".
|
|
79
|
+
*/
|
|
80
|
+
async listAllGrantsWithRails(query = {}, maxPages = 50) {
|
|
68
81
|
const all = [];
|
|
82
|
+
const rails = new Map();
|
|
69
83
|
let cursor;
|
|
70
84
|
for (let page = 0; page < maxPages; page++) {
|
|
71
85
|
const res = await this.listGrants({ ...query, cursor });
|
|
72
86
|
all.push(...res.items);
|
|
87
|
+
for (const rail of res.unreadableRails ?? [])
|
|
88
|
+
rails.set(rail.rail, rail);
|
|
73
89
|
if (!res.nextCursor)
|
|
74
|
-
|
|
90
|
+
break;
|
|
75
91
|
cursor = res.nextCursor;
|
|
76
92
|
}
|
|
77
|
-
return all;
|
|
93
|
+
return { items: all, unreadableRails: [...rails.values()] };
|
|
78
94
|
}
|
|
79
95
|
}
|
|
@@ -18,7 +18,7 @@ export declare class InboxClient {
|
|
|
18
18
|
getInbox(opts?: InboxReadOptions): Promise<InboxEnvelope>;
|
|
19
19
|
/**
|
|
20
20
|
* Advance this agent's watermark — pass the envelope's `ackTo` plus every
|
|
21
|
-
* `resourceId` handled in `(priorAck, upTo]
|
|
21
|
+
* `resourceId` handled in `(priorAck, upTo]`. Monotonic
|
|
22
22
|
* server-side: an older value is a no-op, so a replayed ack can never
|
|
23
23
|
* redeliver handled work. An ack that would bury unlisted deliveries is
|
|
24
24
|
* refused.
|
package/dist/http/InboxClient.js
CHANGED
|
@@ -48,7 +48,7 @@ export class InboxClient {
|
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Advance this agent's watermark — pass the envelope's `ackTo` plus every
|
|
51
|
-
* `resourceId` handled in `(priorAck, upTo]
|
|
51
|
+
* `resourceId` handled in `(priorAck, upTo]`. Monotonic
|
|
52
52
|
* server-side: an older value is a no-op, so a replayed ack can never
|
|
53
53
|
* redeliver handled work. An ack that would bury unlisted deliveries is
|
|
54
54
|
* refused.
|
|
@@ -24,7 +24,7 @@ export interface PullOffersOptions {
|
|
|
24
24
|
}
|
|
25
25
|
export declare function pullOffers(options: PullOffersOptions | undefined, creds: Creds): Promise<Agreement[]>;
|
|
26
26
|
export declare function claimOffer(agreementId: string, creds: Creds): Promise<Agreement>;
|
|
27
|
-
export interface
|
|
27
|
+
export interface PublishRequestPayload {
|
|
28
28
|
/** See PublishOfferPayload.billing. */
|
|
29
29
|
billing?: 'total' | 'per_task';
|
|
30
30
|
description: string;
|
|
@@ -38,12 +38,12 @@ export interface PublishQuestPayload {
|
|
|
38
38
|
/** Broadcast audience: 'everyone' (default, fully public) or 'org' (members of your active org only). */
|
|
39
39
|
audience?: BroadcastAudience;
|
|
40
40
|
}
|
|
41
|
-
export declare function
|
|
42
|
-
export interface
|
|
41
|
+
export declare function publishRequest(payload: PublishRequestPayload, creds: Creds): Promise<Agreement>;
|
|
42
|
+
export interface PullRequestsOptions {
|
|
43
43
|
limit?: number;
|
|
44
44
|
since?: string;
|
|
45
45
|
}
|
|
46
|
-
export declare function
|
|
46
|
+
export declare function pullRequests(options: PullRequestsOptions | undefined, creds: Creds): Promise<Agreement[]>;
|
|
47
47
|
export declare class MarketplaceClient {
|
|
48
48
|
private creds;
|
|
49
49
|
/**
|
|
@@ -54,6 +54,6 @@ export declare class MarketplaceClient {
|
|
|
54
54
|
publishOffer(payload: PublishOfferPayload): Promise<Agreement>;
|
|
55
55
|
pullOffers(options?: PullOffersOptions): Promise<Agreement[]>;
|
|
56
56
|
claimOffer(agreementId: string): Promise<Agreement>;
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
publishRequest(payload: PublishRequestPayload): Promise<Agreement>;
|
|
58
|
+
pullRequests(options?: PullRequestsOptions): Promise<Agreement[]>;
|
|
59
59
|
}
|
|
@@ -70,37 +70,37 @@ export async function claimOffer(agreementId, creds) {
|
|
|
70
70
|
throw new Error(data?.['error'] || 'Claim failed');
|
|
71
71
|
return shapeAgreement(data['offer']);
|
|
72
72
|
}
|
|
73
|
-
export async function
|
|
74
|
-
assertCreds(creds, '
|
|
75
|
-
const res = await fetch(`${getMarketplaceBaseUrl()}/
|
|
73
|
+
export async function publishRequest(payload, creds) {
|
|
74
|
+
assertCreds(creds, 'request publish');
|
|
75
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/requests/publish`, {
|
|
76
76
|
method: 'POST',
|
|
77
77
|
headers: buildHeaders(creds),
|
|
78
78
|
body: JSON.stringify(payload || {}),
|
|
79
79
|
});
|
|
80
80
|
if (!res.ok) {
|
|
81
81
|
const body = await res.text().catch(() => '');
|
|
82
|
-
throwApiError(res, body, `
|
|
82
|
+
throwApiError(res, body, `Request publish failed: ${res.status}`);
|
|
83
83
|
}
|
|
84
84
|
const data = await res.json().catch(() => null);
|
|
85
85
|
if (!data?.['agreement'])
|
|
86
|
-
throw new Error('
|
|
86
|
+
throw new Error('Request publish returned no agreement');
|
|
87
87
|
return shapeAgreement(data['agreement']);
|
|
88
88
|
}
|
|
89
|
-
export async function
|
|
90
|
-
assertCreds(creds, '
|
|
89
|
+
export async function pullRequests(options, creds) {
|
|
90
|
+
assertCreds(creds, 'request pull');
|
|
91
91
|
const params = new URLSearchParams();
|
|
92
92
|
if (options?.limit != null)
|
|
93
93
|
params.set('limit', String(options.limit));
|
|
94
94
|
if (options?.since)
|
|
95
95
|
params.set('since', options.since);
|
|
96
96
|
const qs = params.toString();
|
|
97
|
-
const res = await fetch(`${getMarketplaceBaseUrl()}/
|
|
97
|
+
const res = await fetch(`${getMarketplaceBaseUrl()}/requests${qs ? `?${qs}` : ''}`, {
|
|
98
98
|
method: 'GET',
|
|
99
99
|
headers: buildHeaders(creds),
|
|
100
100
|
});
|
|
101
101
|
if (!res.ok) {
|
|
102
102
|
const body = await res.text().catch(() => '');
|
|
103
|
-
throwApiError(res, body, `
|
|
103
|
+
throwApiError(res, body, `Request pull failed: ${res.status}`);
|
|
104
104
|
}
|
|
105
105
|
const data = await res.json().catch(() => null);
|
|
106
106
|
return data?.['agreements'] ?? [];
|
|
@@ -121,6 +121,6 @@ export class MarketplaceClient {
|
|
|
121
121
|
publishOffer(payload) { return publishOffer(payload, this.creds); }
|
|
122
122
|
pullOffers(options) { return pullOffers(options, this.creds); }
|
|
123
123
|
claimOffer(agreementId) { return claimOffer(agreementId, this.creds); }
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
publishRequest(payload) { return publishRequest(payload, this.creds); }
|
|
125
|
+
pullRequests(options) { return pullRequests(options, this.creds); }
|
|
126
126
|
}
|