@ziggs-ai/api-client 0.11.0 → 0.13.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 +7 -0
- package/dist/capabilities/agreementVerbs.js +55 -2
- package/dist/capabilities/agreements.js +13 -1
- package/dist/capabilities/context.js +2 -2
- package/dist/capabilities/grants.d.ts +2 -1
- package/dist/capabilities/grants.js +4 -3
- package/dist/capabilities/index.d.ts +2 -2
- package/dist/capabilities/index.js +2 -2
- package/dist/capabilities/links.d.ts +16 -5
- package/dist/capabilities/links.js +69 -82
- package/dist/capabilities/marketplace.js +3 -3
- package/dist/capabilities/nextCall.d.ts +36 -5
- package/dist/capabilities/nextCall.js +53 -7
- package/dist/http/AgreementClient.d.ts +56 -28
- package/dist/http/AgreementClient.js +36 -95
- package/dist/http/ContextGrantsClient.d.ts +10 -4
- package/dist/http/InboxClient.js +4 -0
- package/dist/http/MarketplaceClient.d.ts +0 -18
- package/dist/http/MarketplaceClient.js +0 -36
- package/dist/http/TaskClient.d.ts +15 -1
- package/dist/http/TaskClient.js +2 -7
- package/dist/http/agreementFlows.d.ts +3 -4
- package/dist/http/agreementFlows.js +8 -13
- package/dist/http/grants.d.ts +9 -3
- package/dist/http/grants.js +14 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/instanceIdentity.d.ts +4 -0
- package/dist/instanceIdentity.js +44 -0
- package/dist/relay/provisionRelayWorkers.d.ts +68 -2
- package/dist/relay/provisionRelayWorkers.js +138 -18
- package/dist/types.d.ts +53 -16
- package/dist/types.js +18 -0
- package/package.json +1 -1
|
@@ -14,19 +14,65 @@ export function nextCall(env, capabilityKey, args, why) {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* The other
|
|
17
|
+
* The other PRINCIPAL in a two-party agreement, from the perspective of
|
|
18
|
+
* `selfId`.
|
|
19
|
+
*
|
|
20
|
+
* This was `peerAgentId`, and it read the `actor` columns — the agents that
|
|
21
|
+
* carried the paperwork. Those became courier info when a link was re-keyed to
|
|
22
|
+
* the two people it belongs to, and on most links they are null, so the hint it
|
|
23
|
+
* fed either pre-filled a call with an agent that is no longer a door or
|
|
24
|
+
* silently vanished because the helper returned nothing.
|
|
25
|
+
*
|
|
26
|
+
* Named for the SLOT, not for what a link happens to put in it. On a link both
|
|
27
|
+
* principals are guaranteed to be people, because links are person-only and
|
|
28
|
+
* both sides are userIds by construction — but the same slots hold an org or an
|
|
29
|
+
* agent on other engagement kinds, so a caller who read "person" here and
|
|
30
|
+
* trusted it on a hire would be wrong. The person guarantee is a link-only
|
|
31
|
+
* property; ask the kind before relying on it.
|
|
18
32
|
*
|
|
19
33
|
* Returns null rather than guessing when the row does not identify one, because
|
|
20
34
|
* a pre-filled call naming the wrong counterparty is worse than no pre-filled
|
|
21
35
|
* call: the caller would run it, and it would do something they did not ask for.
|
|
22
36
|
*/
|
|
23
|
-
export function
|
|
37
|
+
export function peerPrincipalId(parties, selfId) {
|
|
24
38
|
if (!parties || !selfId)
|
|
25
39
|
return null;
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
const creator = parties.creator?.principal;
|
|
41
|
+
const provider = parties.provider?.principal;
|
|
42
|
+
if (creator && creator !== selfId)
|
|
43
|
+
return creator;
|
|
44
|
+
if (provider && provider !== selfId)
|
|
45
|
+
return provider;
|
|
31
46
|
return null;
|
|
32
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* The peer's principal, located by finding MY side rather than by knowing my
|
|
50
|
+
* own principal id.
|
|
51
|
+
*
|
|
52
|
+
* An agent knows its own agent id and not the id of the person it answers for,
|
|
53
|
+
* so it cannot ask {@link peerPrincipalId} which of two people it is. What it
|
|
54
|
+
* CAN recognise is its own courier stamp: if my agent id is in one side's
|
|
55
|
+
* `actor`, that side is mine and the other side's principal is the peer.
|
|
56
|
+
*
|
|
57
|
+
* Note the difference from the bug this replaced. Reading the peer's `actor` as
|
|
58
|
+
* the peer's address was wrong — those slots are courier info, null on most
|
|
59
|
+
* links, and never a door. Reading MY OWN `actor` to work out which side I am on
|
|
60
|
+
* is sound, because I am comparing against an id I hold.
|
|
61
|
+
*
|
|
62
|
+
* Returns null when neither side carries my stamp, which is the common case for
|
|
63
|
+
* a link two people formed from the web. No pre-filled call is the right answer
|
|
64
|
+
* there: naming the wrong counterparty would get run.
|
|
65
|
+
*/
|
|
66
|
+
export function peerPrincipalForCourier(parties, myAgentId) {
|
|
67
|
+
if (!parties || !myAgentId)
|
|
68
|
+
return null;
|
|
69
|
+
const mineIsCreator = parties.creator?.actor === myAgentId;
|
|
70
|
+
const mineIsProvider = parties.provider?.actor === myAgentId;
|
|
71
|
+
// Both sides stamped with me is one estate on both ends, not a peer.
|
|
72
|
+
if (mineIsCreator === mineIsProvider)
|
|
73
|
+
return null;
|
|
74
|
+
const peer = mineIsCreator
|
|
75
|
+
? parties.provider?.principal
|
|
76
|
+
: parties.creator?.principal;
|
|
77
|
+
return peer && peer !== myAgentId ? peer : null;
|
|
78
|
+
}
|
|
@@ -44,6 +44,12 @@ export interface ProposeDirectInput extends ProposeTerms {
|
|
|
44
44
|
*/
|
|
45
45
|
export type ProposeBroadcastInput = Omit<ProposeDirectInput, 'proposedTo'> & {
|
|
46
46
|
audience?: BroadcastAudience;
|
|
47
|
+
/**
|
|
48
|
+
* Exact-match triage for supplier hosts: a plain string they compare
|
|
49
|
+
* verbatim, no LLM turn. Absent or empty still delivers the request; the
|
|
50
|
+
* supplier just has nothing to triage on.
|
|
51
|
+
*/
|
|
52
|
+
match?: string;
|
|
47
53
|
};
|
|
48
54
|
/**
|
|
49
55
|
* A trust link's agreement, as every caller sees it.
|
|
@@ -124,8 +130,12 @@ export declare function respondToAgreement(agreementId: string, action: 'approve
|
|
|
124
130
|
export interface PendingApprovalFacts {
|
|
125
131
|
/** Party ids with a pending entry on the approvals ledger. */
|
|
126
132
|
pendingPartyIds: readonly string[];
|
|
127
|
-
/**
|
|
128
|
-
|
|
133
|
+
/**
|
|
134
|
+
* The direct responder slot, when the proposal names one. A side names up to
|
|
135
|
+
* two ids — the accountable principal and the delegate acting for it — and
|
|
136
|
+
* either may be the one holding the decision.
|
|
137
|
+
*/
|
|
138
|
+
proposedToIds?: readonly string[];
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
131
141
|
* Which of `candidateIds` holds the pending approval slot, first match wins —
|
|
@@ -182,17 +192,33 @@ export declare function getMyAgreements(filters: GetMyAgreementsFilters | undefi
|
|
|
182
192
|
/** One agreement, shaped: a link comes back as its summary. */
|
|
183
193
|
export declare function getAgreement(agreementId: string, creds: Creds): Promise<Agreement | null>;
|
|
184
194
|
export interface CreateLinkBody {
|
|
185
|
-
/**
|
|
186
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Who to connect with: an email address, or the id of an agent that answers
|
|
197
|
+
* to them. Either way the link is with the PERSON — an id is only a way to
|
|
198
|
+
* find its owner, and an agent that answers to an org is refused. Omit for an
|
|
199
|
+
* open invite you share yourself.
|
|
200
|
+
*/
|
|
201
|
+
to?: string;
|
|
187
202
|
/** Message shown to whoever is asked to approve or claim it. */
|
|
188
203
|
description?: string;
|
|
189
204
|
/** Open invites only: how many people may claim this one link. Default 1. */
|
|
190
205
|
maxClaims?: number;
|
|
191
|
-
/** Which of the caller's own agents stands on their side of the link. */
|
|
192
|
-
asAgentId?: string;
|
|
193
206
|
}
|
|
194
207
|
/**
|
|
195
|
-
*
|
|
208
|
+
* The route's one answer, whatever was in `to`.
|
|
209
|
+
*
|
|
210
|
+
* Deliberately carries no agreement. Returning the row for an id target and a
|
|
211
|
+
* bare "sent" for an address made one of the two an existence oracle over the
|
|
212
|
+
* user table, so both say the same thing now: it went. `shareUrl` is null
|
|
213
|
+
* exactly when `to` was named, which the caller already knows.
|
|
214
|
+
*/
|
|
215
|
+
export interface CreateLinkResult {
|
|
216
|
+
ok: boolean;
|
|
217
|
+
status: 'sent';
|
|
218
|
+
shareUrl: string | null;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Connect with someone — by email, by agent id, or with a shareable invite.
|
|
196
222
|
*
|
|
197
223
|
* This posts to `POST /agreements/links`. It used to post to `POST /agreements`,
|
|
198
224
|
* which also created work agreements inline and skipped the gates the proposal
|
|
@@ -200,10 +226,7 @@ export interface CreateLinkBody {
|
|
|
200
226
|
* (`POST /agreements/proposals`) and the marketplace claim routes; this rail
|
|
201
227
|
* carries links and nothing else.
|
|
202
228
|
*/
|
|
203
|
-
export declare function createLink(body: CreateLinkBody, creds: Creds): Promise<
|
|
204
|
-
ok: boolean;
|
|
205
|
-
agreement: Agreement;
|
|
206
|
-
}>;
|
|
229
|
+
export declare function createLink(body: CreateLinkBody, creds: Creds): Promise<CreateLinkResult>;
|
|
207
230
|
export declare function revokeAgreement(agreementId: string, creds: Creds): Promise<{
|
|
208
231
|
ok: boolean;
|
|
209
232
|
agreement: Agreement;
|
|
@@ -225,19 +248,33 @@ export type ClaimedKind = 'link' | 'offer' | 'request' | 'hand-off';
|
|
|
225
248
|
*
|
|
226
249
|
* POST /agreements/:id/claim — canonical; no partyId in body.
|
|
227
250
|
*/
|
|
228
|
-
|
|
251
|
+
/**
|
|
252
|
+
* What a claimer can say about WHY it is claiming.
|
|
253
|
+
*
|
|
254
|
+
* A claim binds a party to terms somebody else posted, so the only thing there
|
|
255
|
+
* is to declare is the authority behind it: the job the claiming agent is
|
|
256
|
+
* already doing. Nothing here changes the row — the lineage of a posted
|
|
257
|
+
* agreement belongs to whoever posted it.
|
|
258
|
+
*/
|
|
259
|
+
export interface ClaimOptions {
|
|
260
|
+
/**
|
|
261
|
+
* The active agreement whose work this claim is part of. An agent acting
|
|
262
|
+
* inside a job its human approved does not need a second consent for the
|
|
263
|
+
* engagements that job requires, but it has to NAME the job — the server
|
|
264
|
+
* verifies the claim against the acting agent and never guesses one.
|
|
265
|
+
*/
|
|
266
|
+
mandateAgreementId?: string;
|
|
267
|
+
}
|
|
268
|
+
export declare function claimAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<{
|
|
229
269
|
ok: boolean;
|
|
230
270
|
agreement: Agreement;
|
|
231
271
|
kind?: ClaimedKind;
|
|
232
272
|
}>;
|
|
233
273
|
/**
|
|
234
|
-
* Link types a caller may
|
|
235
|
-
*
|
|
236
|
-
* exactly one system-minted room, and letting a request name that type would
|
|
237
|
-
* burn the root's single slot on an unrelated chat. Shorter than the server
|
|
238
|
-
* union on purpose, which is why this list carries the reason with it.
|
|
274
|
+
* Link types a caller may ask for. `origin` and `delegation` are workflow-owned;
|
|
275
|
+
* callers may only pin an agreement into a chat as a reference (`mention`).
|
|
239
276
|
*/
|
|
240
|
-
export declare const CHAT_LINK_TYPES: readonly ["
|
|
277
|
+
export declare const CHAT_LINK_TYPES: readonly ["mention"];
|
|
241
278
|
export type ChatLinkType = (typeof CHAT_LINK_TYPES)[number];
|
|
242
279
|
export declare function linkAgreementToChat(agreementId: string, chatId: string, linkType: ChatLinkType | undefined, creds: Creds): Promise<unknown | null>;
|
|
243
280
|
export declare function getChatsForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
|
|
@@ -245,10 +282,6 @@ export declare const ARTIFACT_LINK_TYPES: readonly ["produced", "referenced"];
|
|
|
245
282
|
export type ArtifactLinkType = (typeof ARTIFACT_LINK_TYPES)[number];
|
|
246
283
|
export declare function linkArtifactToAgreement(agreementId: string, artifactId: string, linkType: ArtifactLinkType | undefined, creds: Creds): Promise<unknown | null>;
|
|
247
284
|
export declare function getArtifactsForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
|
|
248
|
-
export declare const AGREEMENT_USER_ROLES: readonly ["payer", "provider", "participant", "observer"];
|
|
249
|
-
export type UserRole = (typeof AGREEMENT_USER_ROLES)[number];
|
|
250
|
-
export declare function linkUserToAgreement(agreementId: string, userId: string, role: UserRole, creds: Creds): Promise<unknown | null>;
|
|
251
|
-
export declare function getUsersForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
|
|
252
285
|
export declare class AgreementClient {
|
|
253
286
|
private creds;
|
|
254
287
|
/**
|
|
@@ -267,10 +300,7 @@ export declare class AgreementClient {
|
|
|
267
300
|
list(filters?: ListAgreementsFilters): Promise<Agreement[]>;
|
|
268
301
|
listMine(filters?: GetMyAgreementsFilters): Promise<Agreement[]>;
|
|
269
302
|
get(id: string): Promise<Agreement | null>;
|
|
270
|
-
createLink(data: CreateLinkBody): Promise<
|
|
271
|
-
ok: boolean;
|
|
272
|
-
agreement: Agreement;
|
|
273
|
-
}>;
|
|
303
|
+
createLink(data: CreateLinkBody): Promise<CreateLinkResult>;
|
|
274
304
|
revoke(id: string): Promise<{
|
|
275
305
|
ok: boolean;
|
|
276
306
|
agreement: Agreement;
|
|
@@ -288,6 +318,4 @@ export declare class AgreementClient {
|
|
|
288
318
|
listChats(id: string): Promise<unknown[]>;
|
|
289
319
|
linkArtifact(id: string, artifactId: string, linkType?: ArtifactLinkType): Promise<unknown>;
|
|
290
320
|
listArtifacts(id: string): Promise<unknown[]>;
|
|
291
|
-
linkUser(id: string, userId: string, role: UserRole): Promise<unknown>;
|
|
292
|
-
listUsers(id: string): Promise<unknown[]>;
|
|
293
321
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
-
import { OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget } from '../types.js';
|
|
3
|
+
import { OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partySideIds } from '../types.js';
|
|
4
4
|
import { throwApiError } from '../shared/apiError.js';
|
|
5
5
|
// Lazy: read at call time so a `configureApiClient` call that lands after this
|
|
6
6
|
// module is imported still takes effect. Baking it at module-load time would
|
|
@@ -37,6 +37,10 @@ function assertCreds(creds, op) {
|
|
|
37
37
|
* unchanged.
|
|
38
38
|
*/
|
|
39
39
|
export function linkSummary(a) {
|
|
40
|
+
const side = (s) => ({
|
|
41
|
+
principal: s?.principal ?? null,
|
|
42
|
+
actor: s?.actor ?? null,
|
|
43
|
+
});
|
|
40
44
|
return {
|
|
41
45
|
agreementId: a.agreementId,
|
|
42
46
|
// Kept deliberately: callers branch on this, and a summary that hides what
|
|
@@ -44,11 +48,11 @@ export function linkSummary(a) {
|
|
|
44
48
|
engagementKind: a.engagementKind,
|
|
45
49
|
status: a.status,
|
|
46
50
|
proposalStatus: a.proposalStatus,
|
|
51
|
+
// No `payer`: a link carries no money, so the paying side is never occupied.
|
|
47
52
|
parties: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
proposedTo: a.parties?.proposedTo ?? null,
|
|
53
|
+
creator: side(a.parties?.creator),
|
|
54
|
+
provider: side(a.parties?.provider),
|
|
55
|
+
proposedTo: side(a.parties?.proposedTo),
|
|
52
56
|
},
|
|
53
57
|
...(a.description ? { description: a.description } : {}),
|
|
54
58
|
// Seat bookkeeping on an open invite — link state, not commerce.
|
|
@@ -169,8 +173,8 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
|
|
|
169
173
|
ownerUserId: opts.ownerUserId,
|
|
170
174
|
agentId: creds.agentId,
|
|
171
175
|
});
|
|
172
|
-
const proposedTo = agreement.parties?.proposedTo;
|
|
173
|
-
if (isBroadcastTarget(proposedTo) || isBroadcastTarget(agreement.parties?.payer)) {
|
|
176
|
+
const proposedTo = agreement.parties?.proposedTo?.principal;
|
|
177
|
+
if (isBroadcastTarget(proposedTo) || isBroadcastTarget(agreement.parties?.payer?.principal)) {
|
|
174
178
|
// respond is approve/reject on DIRECT proposals only. An open
|
|
175
179
|
// broadcast (request, standing offer, link invite) has no per-recipient
|
|
176
180
|
// approval slot — a BYSTANDER can neither approve nor reject it
|
|
@@ -206,11 +210,12 @@ export function resolvePendingApprovalPartyId(facts, candidateIds) {
|
|
|
206
210
|
}
|
|
207
211
|
// A row with no pending ledger entries is a legacy one (approvals[] predates
|
|
208
212
|
//), and there the named responder slot IS the decision.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
actorIds
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
if (facts.pendingPartyIds.length === 0) {
|
|
214
|
+
const responderIds = facts.proposedToIds ?? [];
|
|
215
|
+
for (const id of actorIds) {
|
|
216
|
+
if (responderIds.includes(id))
|
|
217
|
+
return id;
|
|
218
|
+
}
|
|
214
219
|
}
|
|
215
220
|
return null;
|
|
216
221
|
}
|
|
@@ -223,7 +228,7 @@ export function resolveMyPendingApprovalPartyId(agreement, opts) {
|
|
|
223
228
|
pendingPartyIds: (agreement.approvals ?? [])
|
|
224
229
|
.filter((a) => a.status === 'pending' || a.status === 'PENDING')
|
|
225
230
|
.map((a) => a.partyId),
|
|
226
|
-
|
|
231
|
+
proposedToIds: partySideIds(agreement.parties?.proposedTo),
|
|
227
232
|
}, [opts.agentId, opts.ownerUserId]);
|
|
228
233
|
}
|
|
229
234
|
/**
|
|
@@ -407,7 +412,7 @@ export async function getAgreement(agreementId, creds) {
|
|
|
407
412
|
return agreement ? shapeAgreement(agreement) : null;
|
|
408
413
|
}
|
|
409
414
|
/**
|
|
410
|
-
*
|
|
415
|
+
* Connect with someone — by email, by agent id, or with a shareable invite.
|
|
411
416
|
*
|
|
412
417
|
* This posts to `POST /agreements/links`. It used to post to `POST /agreements`,
|
|
413
418
|
* which also created work agreements inline and skipped the gates the proposal
|
|
@@ -429,11 +434,15 @@ export async function createLink(body, creds) {
|
|
|
429
434
|
throwApiError(res, responseBody, `Link creation failed: ${res.status} ${res.statusText}`);
|
|
430
435
|
}
|
|
431
436
|
const data = await res.json().catch(() => null);
|
|
432
|
-
if (
|
|
433
|
-
throw new Error('Invalid response: expected { ok,
|
|
437
|
+
if (data?.['status'] !== 'sent') {
|
|
438
|
+
throw new Error('Invalid response: expected { ok, status: "sent", shareUrl } from POST /agreements/links');
|
|
434
439
|
}
|
|
435
|
-
const
|
|
436
|
-
return {
|
|
440
|
+
const shareUrl = data['shareUrl'];
|
|
441
|
+
return {
|
|
442
|
+
ok: data['ok'] === true,
|
|
443
|
+
status: 'sent',
|
|
444
|
+
shareUrl: typeof shareUrl === 'string' ? shareUrl : null,
|
|
445
|
+
};
|
|
437
446
|
}
|
|
438
447
|
export async function revokeAgreement(agreementId, creds) {
|
|
439
448
|
if (!agreementId)
|
|
@@ -477,20 +486,15 @@ export async function fulfillAgreement(agreementId, creds) {
|
|
|
477
486
|
const envelope = data;
|
|
478
487
|
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
479
488
|
}
|
|
480
|
-
|
|
481
|
-
* Claim an open agreement. Three shapes are claimable:
|
|
482
|
-
* - open link invite (`engagementKind: link`, proposedTo everyone)
|
|
483
|
-
* - open broadcast request (proposedTo 'everyone', status open)
|
|
484
|
-
* - org-scoped broadcast request (proposedTo 'org') — the server rejects the
|
|
485
|
-
* claim with 403 if the caller is not a member of the agreement's orgId
|
|
486
|
-
*
|
|
487
|
-
* POST /agreements/:id/claim — canonical; no partyId in body.
|
|
488
|
-
*/
|
|
489
|
-
export async function claimAgreement(agreementId, creds) {
|
|
489
|
+
export async function claimAgreement(agreementId, creds, opts = {}) {
|
|
490
490
|
if (!agreementId)
|
|
491
491
|
throw new Error('agreementId is required to claim an open agreement');
|
|
492
492
|
assertCreds(creds, 'open agreement claim');
|
|
493
|
-
const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}/claim`, {
|
|
493
|
+
const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}/claim`, {
|
|
494
|
+
method: 'POST',
|
|
495
|
+
headers: buildHeaders(creds),
|
|
496
|
+
body: JSON.stringify(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}),
|
|
497
|
+
});
|
|
494
498
|
if (!res.ok) {
|
|
495
499
|
const body = await res.text().catch(() => '');
|
|
496
500
|
throwApiError(res, body, `Open agreement claim failed: ${res.status} ${res.statusText}`);
|
|
@@ -510,18 +514,10 @@ export async function claimAgreement(agreementId, creds) {
|
|
|
510
514
|
// Chat links
|
|
511
515
|
// ---------------------------------------------------------------------------
|
|
512
516
|
/**
|
|
513
|
-
* Link types a caller may
|
|
514
|
-
*
|
|
515
|
-
* exactly one system-minted room, and letting a request name that type would
|
|
516
|
-
* burn the root's single slot on an unrelated chat. Shorter than the server
|
|
517
|
-
* union on purpose, which is why this list carries the reason with it.
|
|
517
|
+
* Link types a caller may ask for. `origin` and `delegation` are workflow-owned;
|
|
518
|
+
* callers may only pin an agreement into a chat as a reference (`mention`).
|
|
518
519
|
*/
|
|
519
|
-
export const CHAT_LINK_TYPES = [
|
|
520
|
-
'origin',
|
|
521
|
-
'mention',
|
|
522
|
-
'delegation',
|
|
523
|
-
'join',
|
|
524
|
-
];
|
|
520
|
+
export const CHAT_LINK_TYPES = ['mention'];
|
|
525
521
|
export async function linkAgreementToChat(agreementId, chatId, linkType = 'mention', creds) {
|
|
526
522
|
if (!agreementId || !chatId)
|
|
527
523
|
return null;
|
|
@@ -614,59 +610,6 @@ export async function getArtifactsForAgreement(agreementId, creds) {
|
|
|
614
610
|
return [];
|
|
615
611
|
}
|
|
616
612
|
}
|
|
617
|
-
// ---------------------------------------------------------------------------
|
|
618
|
-
// User links
|
|
619
|
-
// ---------------------------------------------------------------------------
|
|
620
|
-
export const AGREEMENT_USER_ROLES = [
|
|
621
|
-
'payer',
|
|
622
|
-
'provider',
|
|
623
|
-
'participant',
|
|
624
|
-
'observer',
|
|
625
|
-
];
|
|
626
|
-
export async function linkUserToAgreement(agreementId, userId, role, creds) {
|
|
627
|
-
if (!agreementId || !userId || !role)
|
|
628
|
-
return null;
|
|
629
|
-
assertCreds(creds, 'link user to agreement');
|
|
630
|
-
try {
|
|
631
|
-
const res = await fetch(`${getAgreementBaseUrl()}/${agreementId}/users`, {
|
|
632
|
-
method: 'POST',
|
|
633
|
-
headers: buildHeaders(creds),
|
|
634
|
-
body: JSON.stringify({ userId, role }),
|
|
635
|
-
});
|
|
636
|
-
if (!res.ok) {
|
|
637
|
-
const body = await res.text().catch(() => '');
|
|
638
|
-
runtimeLog.warn('AgreementClient', `⚠️ Link user failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
|
|
639
|
-
return null;
|
|
640
|
-
}
|
|
641
|
-
return await res.json().catch(() => null);
|
|
642
|
-
}
|
|
643
|
-
catch (e) {
|
|
644
|
-
runtimeLog.warn('AgreementClient', `⚠️ Link user failed: ${e.message}`);
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
export async function getUsersForAgreement(agreementId, creds) {
|
|
649
|
-
if (!agreementId)
|
|
650
|
-
return [];
|
|
651
|
-
assertCreds(creds, 'get users for agreement');
|
|
652
|
-
try {
|
|
653
|
-
const res = await fetch(`${getAgreementBaseUrl()}/${agreementId}/users`, {
|
|
654
|
-
method: 'GET',
|
|
655
|
-
headers: buildHeaders(creds),
|
|
656
|
-
});
|
|
657
|
-
if (!res.ok) {
|
|
658
|
-
const body = await res.text().catch(() => '');
|
|
659
|
-
runtimeLog.warn('AgreementClient', `⚠️ Get users failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
|
|
660
|
-
return [];
|
|
661
|
-
}
|
|
662
|
-
const data = await res.json().catch(() => null);
|
|
663
|
-
return Array.isArray(data?.['users']) ? data['users'] : [];
|
|
664
|
-
}
|
|
665
|
-
catch (e) {
|
|
666
|
-
runtimeLog.warn('AgreementClient', `⚠️ Get users failed: ${e.message}`);
|
|
667
|
-
return [];
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
613
|
export class AgreementClient {
|
|
671
614
|
creds;
|
|
672
615
|
/**
|
|
@@ -699,6 +642,4 @@ export class AgreementClient {
|
|
|
699
642
|
listChats(id) { return getChatsForAgreement(id, this.creds); }
|
|
700
643
|
linkArtifact(id, artifactId, linkType) { return linkArtifactToAgreement(id, artifactId, linkType ?? 'produced', this.creds); }
|
|
701
644
|
listArtifacts(id) { return getArtifactsForAgreement(id, this.creds); }
|
|
702
|
-
linkUser(id, userId, role) { return linkUserToAgreement(id, userId, role, this.creds); }
|
|
703
|
-
listUsers(id) { return getUsersForAgreement(id, this.creds); }
|
|
704
645
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { CONTEXT_GRANT_SCOPE_KINDS } from './grants.js';
|
|
1
2
|
import type { GrantAccessKind, GrantHolderKind, GrantView } from './grants.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
* shared without sharing any chat or agreement it sits in.
|
|
5
|
-
*
|
|
4
|
+
* The context rail's scopes. `artifact` is the narrowest — one specific
|
|
5
|
+
* artifact, shared without sharing any chat or agreement it sits in. `task` is
|
|
6
|
+
* a branch of a work graph: the named task and everything under it. Both are
|
|
7
|
+
* still the context rail, not new grant primitives.
|
|
8
|
+
*
|
|
9
|
+
* Derived from {@link CONTEXT_GRANT_SCOPE_KINDS} rather than written out again:
|
|
10
|
+
* a hand-copied subset still typechecks against the union, which is how this
|
|
11
|
+
* list sat a scope kind behind the rail.
|
|
6
12
|
*/
|
|
7
|
-
export type ContextGrantScopeKind =
|
|
13
|
+
export type ContextGrantScopeKind = (typeof CONTEXT_GRANT_SCOPE_KINDS)[number];
|
|
8
14
|
export type ContextTemporal = 'from-now' | 'from-start';
|
|
9
15
|
export interface ContextGrantScope {
|
|
10
16
|
kind: ContextGrantScopeKind;
|
package/dist/http/InboxClient.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
2
|
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
3
|
+
import { INBOX_HOST_CLAIMANT_HEADER, instanceIdentity, } from '../instanceIdentity.js';
|
|
3
4
|
/**
|
|
4
5
|
* The doorbell, not the door: references addressed to this agent
|
|
5
6
|
* since its last ack — never content. Flow: inbox → read → act → ack
|
|
@@ -24,6 +25,9 @@ export class InboxClient {
|
|
|
24
25
|
const headers = {
|
|
25
26
|
Authorization: `Bearer ${this.operatorKey}`,
|
|
26
27
|
'Content-Type': 'application/json',
|
|
28
|
+
// This process, not the API. Two fleets of the same agent share
|
|
29
|
+
// one backend task; without this they both wake.
|
|
30
|
+
[INBOX_HOST_CLAIMANT_HEADER]: instanceIdentity(),
|
|
27
31
|
};
|
|
28
32
|
if (this.agentId)
|
|
29
33
|
headers['X-Agent-Id'] = this.agentId;
|
|
@@ -23,22 +23,6 @@ export interface PullOffersOptions {
|
|
|
23
23
|
since?: string;
|
|
24
24
|
}
|
|
25
25
|
export declare function pullOffers(options: PullOffersOptions | undefined, creds: Creds): Promise<Agreement[]>;
|
|
26
|
-
export declare function claimOffer(agreementId: string, creds: Creds): Promise<Agreement>;
|
|
27
|
-
export interface PublishRequestPayload {
|
|
28
|
-
/** See PublishOfferPayload.billing. */
|
|
29
|
-
billing?: 'total' | 'per_task';
|
|
30
|
-
description: string;
|
|
31
|
-
chatId?: string;
|
|
32
|
-
payerId?: string;
|
|
33
|
-
price?: number;
|
|
34
|
-
lifecycle?: string;
|
|
35
|
-
expiresAt?: string;
|
|
36
|
-
maxExecutions?: number;
|
|
37
|
-
agreementDescription?: string;
|
|
38
|
-
/** Broadcast audience: 'everyone' (default, fully public) or 'org' (members of your active org only). */
|
|
39
|
-
audience?: BroadcastAudience;
|
|
40
|
-
}
|
|
41
|
-
export declare function publishRequest(payload: PublishRequestPayload, creds: Creds): Promise<Agreement>;
|
|
42
26
|
export interface PullRequestsOptions {
|
|
43
27
|
limit?: number;
|
|
44
28
|
since?: string;
|
|
@@ -53,7 +37,5 @@ export declare class MarketplaceClient {
|
|
|
53
37
|
constructor(operatorKey: string, agentId?: string);
|
|
54
38
|
publishOffer(payload: PublishOfferPayload): Promise<Agreement>;
|
|
55
39
|
pullOffers(options?: PullOffersOptions): Promise<Agreement[]>;
|
|
56
|
-
claimOffer(agreementId: string): Promise<Agreement>;
|
|
57
|
-
publishRequest(payload: PublishRequestPayload): Promise<Agreement>;
|
|
58
40
|
pullRequests(options?: PullRequestsOptions): Promise<Agreement[]>;
|
|
59
41
|
}
|
|
@@ -52,40 +52,6 @@ export async function pullOffers(options, creds) {
|
|
|
52
52
|
const data = await res.json().catch(() => null);
|
|
53
53
|
return data?.['offers'] ?? [];
|
|
54
54
|
}
|
|
55
|
-
export async function claimOffer(agreementId, creds) {
|
|
56
|
-
if (!agreementId)
|
|
57
|
-
throw new Error('agreementId is required');
|
|
58
|
-
assertCreds(creds, 'marketplace offer claim');
|
|
59
|
-
const res = await fetch(`${getMarketplaceBaseUrl()}/offers/claim`, {
|
|
60
|
-
method: 'POST',
|
|
61
|
-
headers: buildHeaders(creds),
|
|
62
|
-
body: JSON.stringify({ agreementId }),
|
|
63
|
-
});
|
|
64
|
-
if (!res.ok) {
|
|
65
|
-
const body = await res.text().catch(() => '');
|
|
66
|
-
throwApiError(res, body, `Marketplace offer claim failed: ${res.status}`);
|
|
67
|
-
}
|
|
68
|
-
const data = await res.json().catch(() => null);
|
|
69
|
-
if (!data?.['ok'])
|
|
70
|
-
throw new Error(data?.['error'] || 'Claim failed');
|
|
71
|
-
return shapeAgreement(data['offer']);
|
|
72
|
-
}
|
|
73
|
-
export async function publishRequest(payload, creds) {
|
|
74
|
-
assertCreds(creds, 'request publish');
|
|
75
|
-
const res = await fetch(`${getMarketplaceBaseUrl()}/requests/publish`, {
|
|
76
|
-
method: 'POST',
|
|
77
|
-
headers: buildHeaders(creds),
|
|
78
|
-
body: JSON.stringify(payload || {}),
|
|
79
|
-
});
|
|
80
|
-
if (!res.ok) {
|
|
81
|
-
const body = await res.text().catch(() => '');
|
|
82
|
-
throwApiError(res, body, `Request publish failed: ${res.status}`);
|
|
83
|
-
}
|
|
84
|
-
const data = await res.json().catch(() => null);
|
|
85
|
-
if (!data?.['agreement'])
|
|
86
|
-
throw new Error('Request publish returned no agreement');
|
|
87
|
-
return shapeAgreement(data['agreement']);
|
|
88
|
-
}
|
|
89
55
|
export async function pullRequests(options, creds) {
|
|
90
56
|
assertCreds(creds, 'request pull');
|
|
91
57
|
const params = new URLSearchParams();
|
|
@@ -120,7 +86,5 @@ export class MarketplaceClient {
|
|
|
120
86
|
}
|
|
121
87
|
publishOffer(payload) { return publishOffer(payload, this.creds); }
|
|
122
88
|
pullOffers(options) { return pullOffers(options, this.creds); }
|
|
123
|
-
claimOffer(agreementId) { return claimOffer(agreementId, this.creds); }
|
|
124
|
-
publishRequest(payload) { return publishRequest(payload, this.creds); }
|
|
125
89
|
pullRequests(options) { return pullRequests(options, this.creds); }
|
|
126
90
|
}
|
|
@@ -37,10 +37,24 @@ export interface CreateTaskData {
|
|
|
37
37
|
planReviewTiming?: PlanReviewTiming;
|
|
38
38
|
requireMidWorkPlanAck?: boolean;
|
|
39
39
|
idempotencyKey?: string;
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Explicit delegation target — must hold a grant on the agreement, so work
|
|
42
|
+
* can only be handed to someone that contract already reaches; validated
|
|
43
|
+
* server-side.
|
|
44
|
+
*/
|
|
41
45
|
assigneeId?: string;
|
|
42
46
|
/** prior-step artifact handles consumed as structured input links. */
|
|
43
47
|
inputArtifactIds?: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Tasks this one waits for. The task is created immediately, but its assignee
|
|
50
|
+
* is not woken and cannot start it until every task named here is terminal.
|
|
51
|
+
* Declaring an edge needs read reach on the task you name.
|
|
52
|
+
*
|
|
53
|
+
* Declared rather than enacted: state the join up front and the platform
|
|
54
|
+
* wakes the joiner once, instead of a live agent polling for it and holding
|
|
55
|
+
* the shape in its own control flow where nothing else can see it.
|
|
56
|
+
*/
|
|
57
|
+
waitsOn?: string[];
|
|
44
58
|
}
|
|
45
59
|
export declare function createTask(taskData: CreateTaskData, creds: Creds): Promise<Task>;
|
|
46
60
|
export declare function getTask(taskId: string, creds: Creds): Promise<Task>;
|
package/dist/http/TaskClient.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
|
+
import { partyActorIds } from '../types.js';
|
|
2
3
|
import { throwApiError } from '../shared/apiError.js';
|
|
3
4
|
function getTaskBaseUrl() { return `${getBackendUrl()}/tasks`; }
|
|
4
5
|
function buildHeaders(creds) {
|
|
@@ -159,13 +160,7 @@ async function getActiveTasksForAgentViaPartyAgreements(agentId, creds) {
|
|
|
159
160
|
const { listAgreements } = await import('./AgreementClient.js');
|
|
160
161
|
const agreements = await listAgreements({ status: 'active' }, creds);
|
|
161
162
|
const partyIds = agreements
|
|
162
|
-
.filter((a) =>
|
|
163
|
-
const p = a.parties ?? {};
|
|
164
|
-
return (p.provider === agentId ||
|
|
165
|
-
p.providerAgent === agentId ||
|
|
166
|
-
p.creator === agentId ||
|
|
167
|
-
p.payer === agentId);
|
|
168
|
-
})
|
|
163
|
+
.filter((a) => partyActorIds(a.parties).includes(agentId))
|
|
169
164
|
.map((a) => a.agreementId)
|
|
170
165
|
.filter(Boolean);
|
|
171
166
|
if (partyIds.length === 0)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ClaimedKind, type ProposeTerms } from './AgreementClient.js';
|
|
1
|
+
import { type ClaimedKind, type ClaimOptions, type ProposeTerms } from './AgreementClient.js';
|
|
2
2
|
import { type Agreement, type Creds, type EngagementKind } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* one propose grammar. Direct, broadcast (request and standing
|
|
@@ -6,7 +6,6 @@ import { type Agreement, type Creds, type EngagementKind } from '../types.js';
|
|
|
6
6
|
* single propose tool instead of dedicated publish/request tools.
|
|
7
7
|
*
|
|
8
8
|
* Routing:
|
|
9
|
-
* - engagementKind 'link' → POST /agreements (link proposal / open invite)
|
|
10
9
|
* - proposedTo 'everyone' | 'org', providerId = self → seller-broadcast standing offer
|
|
11
10
|
* - proposedTo 'everyone' | 'org', no providerId → buyer-broadcast request
|
|
12
11
|
* - anything else → direct proposal
|
|
@@ -17,7 +16,7 @@ export interface UnifiedProposeInput extends ProposeTerms {
|
|
|
17
16
|
chatId?: string;
|
|
18
17
|
engagementKind?: EngagementKind;
|
|
19
18
|
}
|
|
20
|
-
export type ProposeShape = 'direct' | 'request' | 'offer'
|
|
19
|
+
export type ProposeShape = 'direct' | 'request' | 'offer';
|
|
21
20
|
export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds): Promise<{
|
|
22
21
|
agreement: Agreement;
|
|
23
22
|
shape: ProposeShape;
|
|
@@ -36,7 +35,7 @@ export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds)
|
|
|
36
35
|
* `kind` arrives on the claim response — the route that did the routing reports
|
|
37
36
|
* which broadcast kind this turned out to be.
|
|
38
37
|
*/
|
|
39
|
-
export declare function claimOpenAgreement(agreementId: string, creds: Creds): Promise<{
|
|
38
|
+
export declare function claimOpenAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<{
|
|
40
39
|
agreement: Agreement;
|
|
41
40
|
kind: ClaimedKind;
|
|
42
41
|
}>;
|