@ziggs-ai/api-client 0.40.4 → 0.40.6
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/CHANGELOG.md +8 -0
- package/dist/capabilities/agreementVerbs.d.ts +6 -0
- package/dist/capabilities/agreementVerbs.js +18 -0
- package/dist/capabilities/agreements.js +3 -1
- package/dist/capabilities/artifacts.d.ts +1 -12
- package/dist/capabilities/artifacts.js +2 -83
- package/dist/capabilities/buckets.d.ts +15 -0
- package/dist/capabilities/buckets.js +353 -0
- package/dist/capabilities/context.js +7 -4
- package/dist/capabilities/grants.js +1 -1
- package/dist/capabilities/index.d.ts +2 -1
- package/dist/capabilities/index.js +2 -1
- package/dist/capabilities/links.js +0 -1
- package/dist/capabilities/mcpBriefs.js +51 -4
- package/dist/capabilities/nextCall.js +0 -2
- package/dist/http/AgreementClient.d.ts +15 -0
- package/dist/http/AgreementClient.js +1 -1
- package/dist/http/BucketsClient.d.ts +219 -0
- package/dist/http/BucketsClient.js +109 -0
- package/dist/http/ContextGrantsClient.d.ts +0 -16
- package/dist/http/ContextGrantsClient.js +0 -27
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.js +1 -0
- package/dist/shared/operationError.d.ts +11 -1
- package/dist/shared/operationError.js +18 -20
- package/package.json +2 -2
|
@@ -173,18 +173,21 @@ export const contextDiscoverGrantableCapability = {
|
|
|
173
173
|
})) };
|
|
174
174
|
},
|
|
175
175
|
};
|
|
176
|
+
/** What a grant may be narrowed to when passed on. An artifact is not among
|
|
177
|
+
* them: it reaches people only by being added to a bucket. */
|
|
178
|
+
const DELEGABLE_SCOPE_KINDS = GRANT_SCOPE_KINDS.filter((kind) => kind !== 'artifact');
|
|
176
179
|
export const contextDelegateCapability = {
|
|
177
180
|
key: 'context_delegate',
|
|
178
181
|
names: { sdk: 'context_delegate', mcp: 'ziggs_context_delegate' },
|
|
179
182
|
title: 'Pass on a narrower grant',
|
|
180
183
|
descriptions: {
|
|
181
|
-
sdk: 'Delegate a narrower child context grant (tighter scope, shorter expiry, or from-now). Holder-only. Delegating a grant whose original owner is another party opens an approval request rather than minting immediately (status: pending_approval).',
|
|
184
|
+
sdk: 'Delegate a narrower child context grant (tighter scope, shorter expiry, or from-now). Holder-only. Delegating a grant whose original owner is another party opens an approval request rather than minting immediately (status: pending_approval). An artifact is not passed on this way: add it to a bucket.',
|
|
182
185
|
},
|
|
183
186
|
annotation: 'write',
|
|
184
187
|
params: {
|
|
185
188
|
parentGrantId: { type: 'string', required: true },
|
|
186
189
|
holderId: { type: 'string', required: true, description: 'Agent receiving the delegated grant' },
|
|
187
|
-
scopeKind: { type: 'string', required: true, enum:
|
|
190
|
+
scopeKind: { type: 'string', required: true, enum: DELEGABLE_SCOPE_KINDS },
|
|
188
191
|
scopeId: { type: 'string', required: true },
|
|
189
192
|
temporal: {
|
|
190
193
|
type: 'string',
|
|
@@ -210,8 +213,8 @@ export const contextDelegateCapability = {
|
|
|
210
213
|
throw new Error('temporal must be from-now or from-start');
|
|
211
214
|
}
|
|
212
215
|
const scopeKind = args['scopeKind'];
|
|
213
|
-
if (!
|
|
214
|
-
throw new Error(`scopeKind must be one of ${
|
|
216
|
+
if (!DELEGABLE_SCOPE_KINDS.includes(scopeKind)) {
|
|
217
|
+
throw new Error(`scopeKind must be one of ${DELEGABLE_SCOPE_KINDS.join(', ')}; an artifact is shared by adding it to a bucket`);
|
|
215
218
|
}
|
|
216
219
|
// #6 — an org scope may be named rather than pasted as org_… id.
|
|
217
220
|
let scopeId = args['scopeId'];
|
|
@@ -42,7 +42,7 @@ export const listGrantsCapability = {
|
|
|
42
42
|
names: { sdk: 'grant_list', mcp: 'ziggs_grant_list' },
|
|
43
43
|
title: 'List grants you hold',
|
|
44
44
|
descriptions: {
|
|
45
|
-
sdk: 'List grants this agent holds — or, with role=issuer, grants this agent caused (author-shares and delegate children). Context (chat/agreement/org/artifact/task), connection, wallet, consent, and inbox — canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). A `task` scope is a BRANCH of a work graph: the named task and everything under it, which is what holding a pass on a job looks like. Default role=holder answers "what grants do I HOLD?", which is narrower than "what can I reach?": your own authorship, and grants held by an org you belong to rather than by you, leave nothing under your own id — an empty holder list means "no grants of your own", never "no access". role=issuer answers "who did I share with / what did I mint?" after
|
|
45
|
+
sdk: 'List grants this agent holds — or, with role=issuer, grants this agent caused (author-shares and delegate children). Context (chat/agreement/org/artifact/task), connection, wallet, consent, and inbox — canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). A `task` scope is a BRANCH of a work graph: the named task and everything under it, which is what holding a pass on a job looks like. Default role=holder answers "what grants do I HOLD?", which is narrower than "what can I reach?": your own authorship, and grants held by an org you belong to rather than by you, leave nothing under your own id — an empty holder list means "no grants of your own", never "no access". role=issuer answers "who did I share with / what did I mint?" after a bucket share. Filter by scopeKind, scopeId, and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails. Cursor-paginated.',
|
|
46
46
|
},
|
|
47
47
|
annotation: 'read-only',
|
|
48
48
|
params: {
|
|
@@ -13,10 +13,11 @@ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, delegateT
|
|
|
13
13
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
14
14
|
export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
|
|
15
15
|
export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
|
|
16
|
+
export { BUCKET_CAPABILITIES, bucketCreateCapability, bucketListCapability, bucketReadCapability, bucketInsertCapability, bucketRemoveCapability, bucketMoveCapability, bucketAddMembersCapability, bucketJoinCapability, bucketSendCapability, bucketRemoveMemberCapability, bucketDuplicateCapability, bucketRenameCapability, bucketDeleteCapability, } from './buckets.js';
|
|
16
17
|
export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, RESOURCE_UNREADABLE_MESSAGE, type RefusedRead, } from './context.js';
|
|
17
18
|
export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
|
|
18
19
|
export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
|
|
19
|
-
export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, type UnsearchedContext,
|
|
20
|
+
export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, type UnsearchedContext, attachArtifactCapability, reviewArtifactContributionCapability, contributeArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
|
|
20
21
|
export { EVERYDAY_MCP_TOOLS, EVERYDAY_NO_SDK_TWIN, EVERYDAY_SDK_TOOLS, } from './everyday.js';
|
|
21
22
|
export { annotationForSdkTool } from './toolLane.js';
|
|
22
23
|
export { presentResourceActions } from './resourceActions.js';
|
|
@@ -13,10 +13,11 @@ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, delegateT
|
|
|
13
13
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
14
14
|
export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
|
|
15
15
|
export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
|
|
16
|
+
export { BUCKET_CAPABILITIES, bucketCreateCapability, bucketListCapability, bucketReadCapability, bucketInsertCapability, bucketRemoveCapability, bucketMoveCapability, bucketAddMembersCapability, bucketJoinCapability, bucketSendCapability, bucketRemoveMemberCapability, bucketDuplicateCapability, bucketRenameCapability, bucketDeleteCapability, } from './buckets.js';
|
|
16
17
|
export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, RESOURCE_UNREADABLE_MESSAGE, } from './context.js';
|
|
17
18
|
export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
|
|
18
19
|
export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
|
|
19
|
-
export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability,
|
|
20
|
+
export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, attachArtifactCapability, reviewArtifactContributionCapability, contributeArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
|
|
20
21
|
export { EVERYDAY_MCP_TOOLS, EVERYDAY_NO_SDK_TWIN, EVERYDAY_SDK_TOOLS, } from './everyday.js';
|
|
21
22
|
export { annotationForSdkTool } from './toolLane.js';
|
|
22
23
|
export { presentResourceActions } from './resourceActions.js';
|
|
@@ -66,7 +66,6 @@ export const listLinksCapability = {
|
|
|
66
66
|
// is now, as `peer`, resolved relative to whoever asked.
|
|
67
67
|
const readPlan = active.length ? [
|
|
68
68
|
nextCall(env, 'message_send', undefined, 'Send an explicit message command to a returned peer.id; choose readers, delivery recipients and the intent (fyi or request) separately. A link grants no history access.'),
|
|
69
|
-
nextCall(env, 'artifact_share', undefined, 'Share only the selected artifact under its grant and consent policy.'),
|
|
70
69
|
] : [];
|
|
71
70
|
return {
|
|
72
71
|
count: links.length,
|
|
@@ -165,10 +165,6 @@ const BRIEFS = {
|
|
|
165
165
|
ziggs_artifact_find: {
|
|
166
166
|
does: 'Searches artifacts you can read by name, filename or body. coverage and notSearched say what was and was not searched.',
|
|
167
167
|
},
|
|
168
|
-
ziggs_artifact_share: {
|
|
169
|
-
does: 'Shares one artifact you authored with another agent.',
|
|
170
|
-
changes: 'grants it at once to a connected agent; otherwise asks your owner to approve (pending_approval)',
|
|
171
|
-
},
|
|
172
168
|
ziggs_message_send: { does: 'Sends a canonical command with independent readers, recipients, an intent (fyi or request) and optional mandate.', changes: 'delivers a message and grants its readers access; a request wakes the agents it reaches' },
|
|
173
169
|
ziggs_message_replies: { does: 'Reads authorized causal replies by messageId; a reference grants no access and receipt expiry does not erase history.' },
|
|
174
170
|
ziggs_message_read: { does: 'Reads an exact message by messageId under current access and execution policy. Does not acknowledge its receipt.' },
|
|
@@ -291,6 +287,57 @@ const BRIEFS = {
|
|
|
291
287
|
does: 'Calls one tool on a connected MCP server through the Ziggs gateway, within your grant. ziggs_connection_proxy refuses MCP servers (Unknown provider: mcp).',
|
|
292
288
|
changes: 'whatever that tool changes',
|
|
293
289
|
},
|
|
290
|
+
// Buckets: pointers to items and people; access is computed from the items' grants.
|
|
291
|
+
ziggs_bucket_create: {
|
|
292
|
+
does: 'Creates a bucket, with items if given.',
|
|
293
|
+
changes: 'creates it with what fits',
|
|
294
|
+
},
|
|
295
|
+
ziggs_bucket_list: {
|
|
296
|
+
does: 'Lists the buckets you are in.',
|
|
297
|
+
},
|
|
298
|
+
ziggs_bucket_read: {
|
|
299
|
+
does: 'Reads a bucket and a page of its items; those someone cannot read are set apart.',
|
|
300
|
+
},
|
|
301
|
+
ziggs_bucket_insert: {
|
|
302
|
+
does: 'Puts items in a bucket if everyone in it can read them.',
|
|
303
|
+
changes: 'adds what fits; share may share',
|
|
304
|
+
},
|
|
305
|
+
ziggs_bucket_remove: {
|
|
306
|
+
does: 'Takes items out of a bucket. The items are untouched.',
|
|
307
|
+
changes: 'removes them',
|
|
308
|
+
},
|
|
309
|
+
ziggs_bucket_duplicate: {
|
|
310
|
+
does: 'Copies items into a new bucket for another audience. The original stays.',
|
|
311
|
+
changes: 'creates it with what fits',
|
|
312
|
+
},
|
|
313
|
+
ziggs_bucket_move: {
|
|
314
|
+
does: 'Moves items to another or a new bucket, if its audience can read them.',
|
|
315
|
+
changes: 'moves what fits',
|
|
316
|
+
},
|
|
317
|
+
ziggs_bucket_add_members: {
|
|
318
|
+
does: 'Adds to a bucket audience. Who cannot read an item needs onMisfit share or split.',
|
|
319
|
+
changes: 'adds them',
|
|
320
|
+
},
|
|
321
|
+
ziggs_bucket_join: {
|
|
322
|
+
does: "Brings another bucket's people and items in. Who cannot read an item needs onMisfit share.",
|
|
323
|
+
changes: 'adds them',
|
|
324
|
+
},
|
|
325
|
+
ziggs_bucket_send: {
|
|
326
|
+
does: "Sends a message inside a bucket, to its people.",
|
|
327
|
+
changes: 'sends it',
|
|
328
|
+
},
|
|
329
|
+
ziggs_bucket_rename: {
|
|
330
|
+
does: 'Renames a bucket. Its creator does this.',
|
|
331
|
+
changes: 'the name',
|
|
332
|
+
},
|
|
333
|
+
ziggs_bucket_delete: {
|
|
334
|
+
does: 'Deletes a bucket. The items are untouched. Its creator does this.',
|
|
335
|
+
changes: 'deletes it',
|
|
336
|
+
},
|
|
337
|
+
ziggs_bucket_remove_member: {
|
|
338
|
+
does: 'Takes someone out of a bucket. Its creator does this.',
|
|
339
|
+
changes: 'removes them',
|
|
340
|
+
},
|
|
294
341
|
ziggs_payment_balance: {
|
|
295
342
|
does: 'Shows your points balance and what is available after holds. Points settle through work; here they cannot be bought, cashed out or transferred.',
|
|
296
343
|
},
|
|
@@ -30,7 +30,6 @@ const REQUIRED_ARGS = {
|
|
|
30
30
|
agreement_subcontract: ['parentAgreementId', 'executorId', 'description'],
|
|
31
31
|
agreement_respond: ['agreementId'],
|
|
32
32
|
introduction_redeem: ['token'],
|
|
33
|
-
artifact_share: ['artifactId', 'holderId'],
|
|
34
33
|
context_delegate: ['parentGrantId', 'holderId', 'scopeKind', 'scopeId', 'temporal'],
|
|
35
34
|
task_create: ['agreementId', 'description'],
|
|
36
35
|
task_cancel: ['taskId'],
|
|
@@ -61,7 +60,6 @@ const HTTP_BINDINGS = {
|
|
|
61
60
|
path: '/agreements/:agreementId/approvals/:partyId',
|
|
62
61
|
},
|
|
63
62
|
marketplace_view: { method: 'GET', path: '/marketplace/offers' },
|
|
64
|
-
artifact_share: { method: 'POST', path: '/context/artifacts/:artifactId/share' },
|
|
65
63
|
context_delegate: { method: 'POST', path: '/context/grants/:parentGrantId/delegate' },
|
|
66
64
|
open: { method: 'POST', path: '/context/open' },
|
|
67
65
|
access_explain: { method: 'POST', path: '/context/access/explain' },
|
|
@@ -45,6 +45,11 @@ export interface ProposeDirectInput extends ProposeTerms {
|
|
|
45
45
|
proposedTo: string;
|
|
46
46
|
/** Optional display-reference context for resolving a masked party; does not publish or share. */
|
|
47
47
|
addressId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The bucket the agreement goes into, one you are in. Left out: the bucket
|
|
50
|
+
* your run works in, else a new one. The other side comes in when it starts.
|
|
51
|
+
*/
|
|
52
|
+
bucketId?: string;
|
|
48
53
|
}
|
|
49
54
|
/**
|
|
50
55
|
* Open buyer-broadcast. `audience` selects who may see/claim it:
|
|
@@ -134,6 +139,11 @@ export interface DelegateAgreementData {
|
|
|
134
139
|
interactionMode?: 'messages' | 'tasks' | 'both';
|
|
135
140
|
agreementDescription?: string;
|
|
136
141
|
payerId?: string;
|
|
142
|
+
/**
|
|
143
|
+
* The bucket the sub-agreement goes into, one you are in. Left out: the
|
|
144
|
+
* bucket your run works in, else the parent's buckets, else a new one.
|
|
145
|
+
*/
|
|
146
|
+
bucketId?: string;
|
|
137
147
|
idempotencyKey?: string;
|
|
138
148
|
}
|
|
139
149
|
export declare function delegateAgreement(proposalData: DelegateAgreementData, creds: Creds): Promise<Agreement>;
|
|
@@ -328,6 +338,11 @@ export type ClaimedKind = 'link' | 'offer' | 'request' | 'hand-off';
|
|
|
328
338
|
*/
|
|
329
339
|
export interface ClaimOptions {
|
|
330
340
|
parentAgreementId?: string;
|
|
341
|
+
/**
|
|
342
|
+
* The bucket the hire goes into, one you are in. Left out: the bucket your
|
|
343
|
+
* run works in, else a new one. The other side comes in when it starts.
|
|
344
|
+
*/
|
|
345
|
+
bucketId?: string;
|
|
331
346
|
/**
|
|
332
347
|
* The active agreement whose work this claim is part of. An agent acting
|
|
333
348
|
* inside a job its human approved does not need a second consent for the
|
|
@@ -559,7 +559,7 @@ export async function claimAgreement(agreementId, creds, opts = {}) {
|
|
|
559
559
|
const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}/claim`, {
|
|
560
560
|
method: 'POST',
|
|
561
561
|
headers: buildHeaders(creds),
|
|
562
|
-
body: JSON.stringify({ ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}), ...(opts.parentAgreementId ? { parentAgreementId: opts.parentAgreementId } : {}), ...(opts.connections && Object.keys(opts.connections).length ? { connections: opts.connections } : {}), ...(opts.hireFor === 'workspace' ? { hireFor: 'workspace' } : {}) }),
|
|
562
|
+
body: JSON.stringify({ ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}), ...(opts.parentAgreementId ? { parentAgreementId: opts.parentAgreementId } : {}), ...(opts.connections && Object.keys(opts.connections).length ? { connections: opts.connections } : {}), ...(opts.hireFor === 'workspace' ? { hireFor: 'workspace' } : {}), ...(opts.bucketId ? { bucketId: opts.bucketId } : {}) }),
|
|
563
563
|
});
|
|
564
564
|
if (!res.ok) {
|
|
565
565
|
const body = await res.text().catch(() => '');
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import type { MessageCommand, MessageCommandResult } from '@ziggs-ai/contracts';
|
|
2
|
+
/** What a bucket can hold. Each item is a pointer, never a copy. */
|
|
3
|
+
export declare const BUCKET_ITEM_KINDS: readonly ["message", "artifact", "task", "agreement"];
|
|
4
|
+
export type BucketItemKind = (typeof BUCKET_ITEM_KINDS)[number];
|
|
5
|
+
export interface BucketItemRef {
|
|
6
|
+
kind: BucketItemKind;
|
|
7
|
+
id: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An item that does not fit where it was asked to go, or no longer fits where
|
|
11
|
+
* it is. Other members are named only when the caller can read the item.
|
|
12
|
+
*/
|
|
13
|
+
export type BucketMisfit = {
|
|
14
|
+
item: BucketItemRef;
|
|
15
|
+
reason: 'you-cannot-read';
|
|
16
|
+
} | {
|
|
17
|
+
item: BucketItemRef;
|
|
18
|
+
reason: 'members-cannot-read';
|
|
19
|
+
memberIds: string[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* What to do when someone being added cannot read an item: share it with
|
|
23
|
+
* them, or split: start a narrower bucket with them and only what everyone
|
|
24
|
+
* can read, leaving this one as it is.
|
|
25
|
+
*/
|
|
26
|
+
export type BucketMisfitChoice = 'share' | 'split';
|
|
27
|
+
export interface BucketView {
|
|
28
|
+
bucketId: string;
|
|
29
|
+
name: string;
|
|
30
|
+
createdBy: string;
|
|
31
|
+
/** Who it is for, as given: people, agents, workspaces. */
|
|
32
|
+
audience: string[];
|
|
33
|
+
/** Goes up when an item leaves or the audience changes. */
|
|
34
|
+
revision: number;
|
|
35
|
+
/** Items held; whether each still fits is computed when the items are read. */
|
|
36
|
+
itemCount: number;
|
|
37
|
+
createdAt: string | null;
|
|
38
|
+
}
|
|
39
|
+
/** One share made because the caller chose to share. */
|
|
40
|
+
export interface BucketShare {
|
|
41
|
+
item: BucketItemRef;
|
|
42
|
+
holderId: string;
|
|
43
|
+
/** `pending` waits for the item owner's yes; the item stays out until then. */
|
|
44
|
+
outcome: 'shared' | 'pending' | 'refused';
|
|
45
|
+
}
|
|
46
|
+
export interface BucketInsertResult {
|
|
47
|
+
bucket: BucketView;
|
|
48
|
+
inserted: BucketItemRef[];
|
|
49
|
+
alreadyIn: BucketItemRef[];
|
|
50
|
+
misfits: BucketMisfit[];
|
|
51
|
+
shared: BucketShare[];
|
|
52
|
+
}
|
|
53
|
+
export interface BucketMoveResult {
|
|
54
|
+
source: BucketView;
|
|
55
|
+
bucket: BucketView;
|
|
56
|
+
moved: BucketItemRef[];
|
|
57
|
+
notIn: BucketItemRef[];
|
|
58
|
+
misfits: BucketMisfit[];
|
|
59
|
+
}
|
|
60
|
+
export interface BucketAddMembersResult {
|
|
61
|
+
bucket: BucketView;
|
|
62
|
+
added: string[];
|
|
63
|
+
shared: BucketShare[];
|
|
64
|
+
/** The narrower bucket a split started, and what it holds. This bucket is left as it was. */
|
|
65
|
+
split: {
|
|
66
|
+
bucket: BucketView;
|
|
67
|
+
items: BucketItemRef[];
|
|
68
|
+
} | null;
|
|
69
|
+
}
|
|
70
|
+
export interface BucketJoinResult {
|
|
71
|
+
bucket: BucketView;
|
|
72
|
+
added: string[];
|
|
73
|
+
joined: BucketItemRef[];
|
|
74
|
+
shared: BucketShare[];
|
|
75
|
+
}
|
|
76
|
+
/** One thread in a bucket, as the reader sees it. */
|
|
77
|
+
export interface BucketThread {
|
|
78
|
+
rootMessageId: string;
|
|
79
|
+
title: string;
|
|
80
|
+
messageCount: number;
|
|
81
|
+
lastAt: string | null;
|
|
82
|
+
/** The reader wrote in it or it was delivered to them. */
|
|
83
|
+
yours: boolean;
|
|
84
|
+
/** The thread holding the message asked about as `current`. */
|
|
85
|
+
current: boolean;
|
|
86
|
+
/** The reader's own threads, other than the current one, come with their latest messages. */
|
|
87
|
+
recent?: Array<{
|
|
88
|
+
messageId: string;
|
|
89
|
+
from: string;
|
|
90
|
+
text: string;
|
|
91
|
+
at: string;
|
|
92
|
+
}>;
|
|
93
|
+
}
|
|
94
|
+
/** What is in a bucket as the reader can read it now: what a run starts from. */
|
|
95
|
+
export interface BucketContents {
|
|
96
|
+
bucket: {
|
|
97
|
+
bucketId: string;
|
|
98
|
+
name: string;
|
|
99
|
+
audience: string[];
|
|
100
|
+
revision: number;
|
|
101
|
+
};
|
|
102
|
+
threads: BucketThread[];
|
|
103
|
+
artifacts: Array<{
|
|
104
|
+
artifactId: string;
|
|
105
|
+
name: string | null;
|
|
106
|
+
}>;
|
|
107
|
+
tasks: Array<{
|
|
108
|
+
taskId: string;
|
|
109
|
+
title: string;
|
|
110
|
+
state: string;
|
|
111
|
+
}>;
|
|
112
|
+
agreements: Array<{
|
|
113
|
+
agreementId: string;
|
|
114
|
+
title: string;
|
|
115
|
+
status: string;
|
|
116
|
+
}>;
|
|
117
|
+
/** Items held that the reader cannot read here, as they ask now. */
|
|
118
|
+
outOfReach: number;
|
|
119
|
+
}
|
|
120
|
+
/** A message sent inside a bucket: its readers are the bucket's people. */
|
|
121
|
+
export type BucketMessage = Omit<MessageCommand, 'audience'>;
|
|
122
|
+
export interface BucketItemPage {
|
|
123
|
+
/** Items everyone it is for can read right now. */
|
|
124
|
+
items: Array<BucketItemRef & {
|
|
125
|
+
addedBy: string;
|
|
126
|
+
addedAt: string | null;
|
|
127
|
+
}>;
|
|
128
|
+
/** Items held that someone it is for can no longer read. */
|
|
129
|
+
outOfReach: BucketMisfit[];
|
|
130
|
+
nextCursor: string | null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The bucket operations (`/buckets`). A bucket is pointers to items and to who
|
|
134
|
+
* it is for; it stores no permission. Whether everyone can read each item is
|
|
135
|
+
* computed from the items' own grants whenever it is asked.
|
|
136
|
+
*
|
|
137
|
+
* Every message is sent through a bucket and the bucket points at it; no
|
|
138
|
+
* message names its bucket. `listMine` with an item finds the buckets holding
|
|
139
|
+
* it, the first being where a message was sent.
|
|
140
|
+
*
|
|
141
|
+
* Changing a bucket is not part of a run, so those calls never carry the
|
|
142
|
+
* selected execution. Sending inside a bucket is: it carries the run, and the
|
|
143
|
+
* run's own guards apply to it.
|
|
144
|
+
*/
|
|
145
|
+
export declare class BucketsClient {
|
|
146
|
+
private readonly operatorKey;
|
|
147
|
+
private readonly agentId?;
|
|
148
|
+
private readonly baseUrl?;
|
|
149
|
+
constructor(operatorKey: string, agentId?: string | undefined, baseUrl?: string | undefined);
|
|
150
|
+
create(input: {
|
|
151
|
+
name: string;
|
|
152
|
+
audience?: string[];
|
|
153
|
+
items?: BucketItemRef[];
|
|
154
|
+
share?: boolean;
|
|
155
|
+
}): Promise<BucketInsertResult>;
|
|
156
|
+
/** The buckets you are in; given an item, those holding it, in the order it went in. */
|
|
157
|
+
listMine(item?: BucketItemRef): Promise<{
|
|
158
|
+
buckets: BucketView[];
|
|
159
|
+
}>;
|
|
160
|
+
/** Send a message inside a bucket, under the selected run when there is one. */
|
|
161
|
+
sendMessage(bucketId: string, message: BucketMessage): Promise<MessageCommandResult>;
|
|
162
|
+
/**
|
|
163
|
+
* What is in a bucket as you can read it now, under the selected run when
|
|
164
|
+
* there is one: your threads with their latest messages, the rest by title.
|
|
165
|
+
* `current` names the message being answered; its thread is marked.
|
|
166
|
+
*/
|
|
167
|
+
contents(bucketId: string, opts?: {
|
|
168
|
+
current?: string;
|
|
169
|
+
}): Promise<BucketContents>;
|
|
170
|
+
/** Bring another bucket's people and items in. The other bucket stays. */
|
|
171
|
+
join(bucketId: string, input: {
|
|
172
|
+
from: string;
|
|
173
|
+
onMisfit?: 'share';
|
|
174
|
+
}): Promise<BucketJoinResult>;
|
|
175
|
+
get(bucketId: string): Promise<BucketView>;
|
|
176
|
+
items(bucketId: string, opts?: {
|
|
177
|
+
before?: string;
|
|
178
|
+
limit?: number;
|
|
179
|
+
}): Promise<BucketItemPage>;
|
|
180
|
+
insert(bucketId: string, items: BucketItemRef[], opts?: {
|
|
181
|
+
share?: boolean;
|
|
182
|
+
}): Promise<BucketInsertResult>;
|
|
183
|
+
remove(bucketId: string, items: BucketItemRef[]): Promise<{
|
|
184
|
+
bucket: BucketView;
|
|
185
|
+
removed: BucketItemRef[];
|
|
186
|
+
notIn: BucketItemRef[];
|
|
187
|
+
}>;
|
|
188
|
+
duplicate(bucketId: string, input: {
|
|
189
|
+
name: string;
|
|
190
|
+
audience?: string[];
|
|
191
|
+
items?: BucketItemRef[];
|
|
192
|
+
share?: boolean;
|
|
193
|
+
}): Promise<BucketInsertResult & {
|
|
194
|
+
notIn: BucketItemRef[];
|
|
195
|
+
}>;
|
|
196
|
+
move(bucketId: string, input: {
|
|
197
|
+
to: string;
|
|
198
|
+
items: BucketItemRef[];
|
|
199
|
+
}): Promise<BucketMoveResult>;
|
|
200
|
+
split(bucketId: string, input: {
|
|
201
|
+
items: BucketItemRef[];
|
|
202
|
+
name: string;
|
|
203
|
+
audience?: string[];
|
|
204
|
+
}): Promise<BucketMoveResult>;
|
|
205
|
+
addMembers(bucketId: string, principalIds: string[], opts?: {
|
|
206
|
+
onMisfit?: BucketMisfitChoice;
|
|
207
|
+
}): Promise<BucketAddMembersResult>;
|
|
208
|
+
removeMember(bucketId: string, principalId: string): Promise<{
|
|
209
|
+
bucket: BucketView;
|
|
210
|
+
removed: boolean;
|
|
211
|
+
}>;
|
|
212
|
+
rename(bucketId: string, name: string): Promise<BucketView>;
|
|
213
|
+
delete(bucketId: string): Promise<{
|
|
214
|
+
bucketId: string;
|
|
215
|
+
deleted: boolean;
|
|
216
|
+
}>;
|
|
217
|
+
private path;
|
|
218
|
+
private request;
|
|
219
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
3
|
+
import { buildOperatorHeaders, withoutExecution } from './operatorHeaders.js';
|
|
4
|
+
/** What a bucket can hold. Each item is a pointer, never a copy. */
|
|
5
|
+
export const BUCKET_ITEM_KINDS = ['message', 'artifact', 'task', 'agreement'];
|
|
6
|
+
/**
|
|
7
|
+
* The bucket operations (`/buckets`). A bucket is pointers to items and to who
|
|
8
|
+
* it is for; it stores no permission. Whether everyone can read each item is
|
|
9
|
+
* computed from the items' own grants whenever it is asked.
|
|
10
|
+
*
|
|
11
|
+
* Every message is sent through a bucket and the bucket points at it; no
|
|
12
|
+
* message names its bucket. `listMine` with an item finds the buckets holding
|
|
13
|
+
* it, the first being where a message was sent.
|
|
14
|
+
*
|
|
15
|
+
* Changing a bucket is not part of a run, so those calls never carry the
|
|
16
|
+
* selected execution. Sending inside a bucket is: it carries the run, and the
|
|
17
|
+
* run's own guards apply to it.
|
|
18
|
+
*/
|
|
19
|
+
export class BucketsClient {
|
|
20
|
+
operatorKey;
|
|
21
|
+
agentId;
|
|
22
|
+
baseUrl;
|
|
23
|
+
constructor(operatorKey, agentId, baseUrl) {
|
|
24
|
+
this.operatorKey = operatorKey;
|
|
25
|
+
this.agentId = agentId;
|
|
26
|
+
this.baseUrl = baseUrl;
|
|
27
|
+
if (!operatorKey)
|
|
28
|
+
throw new Error('BucketsClient: operatorKey is required');
|
|
29
|
+
}
|
|
30
|
+
create(input) {
|
|
31
|
+
return this.request('POST', '/buckets', input, 'bucket create');
|
|
32
|
+
}
|
|
33
|
+
/** The buckets you are in; given an item, those holding it, in the order it went in. */
|
|
34
|
+
listMine(item) {
|
|
35
|
+
const query = item ? `?${new URLSearchParams({ kind: item.kind, id: item.id })}` : '';
|
|
36
|
+
return this.request('GET', `/buckets${query}`, undefined, 'bucket list');
|
|
37
|
+
}
|
|
38
|
+
/** Send a message inside a bucket, under the selected run when there is one. */
|
|
39
|
+
sendMessage(bucketId, message) {
|
|
40
|
+
return this.request('POST', `${this.path(bucketId)}/send`, message, 'bucket send', { inRun: true });
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What is in a bucket as you can read it now, under the selected run when
|
|
44
|
+
* there is one: your threads with their latest messages, the rest by title.
|
|
45
|
+
* `current` names the message being answered; its thread is marked.
|
|
46
|
+
*/
|
|
47
|
+
contents(bucketId, opts = {}) {
|
|
48
|
+
const query = opts.current ? `?${new URLSearchParams({ current: opts.current })}` : '';
|
|
49
|
+
return this.request('GET', `${this.path(bucketId)}/contents${query}`, undefined, 'bucket contents', { inRun: true });
|
|
50
|
+
}
|
|
51
|
+
/** Bring another bucket's people and items in. The other bucket stays. */
|
|
52
|
+
join(bucketId, input) {
|
|
53
|
+
return this.request('POST', `${this.path(bucketId)}/join`, input, 'bucket join');
|
|
54
|
+
}
|
|
55
|
+
get(bucketId) {
|
|
56
|
+
return this.request('GET', this.path(bucketId), undefined, 'bucket read');
|
|
57
|
+
}
|
|
58
|
+
items(bucketId, opts = {}) {
|
|
59
|
+
const query = new URLSearchParams();
|
|
60
|
+
if (opts.before)
|
|
61
|
+
query.set('before', opts.before);
|
|
62
|
+
if (opts.limit !== undefined)
|
|
63
|
+
query.set('limit', String(opts.limit));
|
|
64
|
+
const suffix = query.size ? `?${query}` : '';
|
|
65
|
+
return this.request('GET', `${this.path(bucketId)}/items${suffix}`, undefined, 'bucket items');
|
|
66
|
+
}
|
|
67
|
+
insert(bucketId, items, opts = {}) {
|
|
68
|
+
return this.request('POST', `${this.path(bucketId)}/insert`, { items, ...(opts.share ? { share: true } : {}) }, 'bucket insert');
|
|
69
|
+
}
|
|
70
|
+
remove(bucketId, items) {
|
|
71
|
+
return this.request('POST', `${this.path(bucketId)}/remove`, { items }, 'bucket remove');
|
|
72
|
+
}
|
|
73
|
+
duplicate(bucketId, input) {
|
|
74
|
+
return this.request('POST', `${this.path(bucketId)}/duplicate`, input, 'bucket duplicate');
|
|
75
|
+
}
|
|
76
|
+
move(bucketId, input) {
|
|
77
|
+
return this.request('POST', `${this.path(bucketId)}/move`, input, 'bucket move');
|
|
78
|
+
}
|
|
79
|
+
split(bucketId, input) {
|
|
80
|
+
return this.request('POST', `${this.path(bucketId)}/split`, input, 'bucket split');
|
|
81
|
+
}
|
|
82
|
+
addMembers(bucketId, principalIds, opts = {}) {
|
|
83
|
+
return this.request('POST', `${this.path(bucketId)}/members`, { principalIds, ...(opts.onMisfit ? { onMisfit: opts.onMisfit } : {}) }, 'bucket add members');
|
|
84
|
+
}
|
|
85
|
+
removeMember(bucketId, principalId) {
|
|
86
|
+
return this.request('DELETE', `${this.path(bucketId)}/members/${encodeURIComponent(principalId)}`, undefined, 'bucket remove member');
|
|
87
|
+
}
|
|
88
|
+
rename(bucketId, name) {
|
|
89
|
+
return this.request('PATCH', this.path(bucketId), { name }, 'bucket rename');
|
|
90
|
+
}
|
|
91
|
+
delete(bucketId) {
|
|
92
|
+
return this.request('DELETE', this.path(bucketId), undefined, 'bucket delete');
|
|
93
|
+
}
|
|
94
|
+
path(bucketId) {
|
|
95
|
+
return `/buckets/${encodeURIComponent(bucketId)}`;
|
|
96
|
+
}
|
|
97
|
+
async request(method, path, body, label, opts = {}) {
|
|
98
|
+
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, body === undefined ? undefined : { 'content-type': 'application/json' });
|
|
99
|
+
const res = await fetch(`${this.baseUrl ?? getBackendUrl()}${path}`, {
|
|
100
|
+
method,
|
|
101
|
+
headers: opts.inRun ? headers : withoutExecution(headers),
|
|
102
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
103
|
+
});
|
|
104
|
+
const text = await res.text().catch(() => '');
|
|
105
|
+
if (!res.ok)
|
|
106
|
+
throwApiError(res, text, `${label} failed: ${res.status} ${res.statusText}`);
|
|
107
|
+
return (text ? JSON.parse(text) : {});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -124,22 +124,6 @@ export declare class ContextGrantsClient {
|
|
|
124
124
|
* labels-only, grant-fenced server-side.
|
|
125
125
|
*/
|
|
126
126
|
getReach(grantId: string): Promise<GrantReachResult>;
|
|
127
|
-
/**
|
|
128
|
-
* share an artifact THIS agent authored with another agent.
|
|
129
|
-
*
|
|
130
|
-
* Not a delegation: an agent holds no grant over its own output, so there is
|
|
131
|
-
* no parent to attenuate. Authorship is the authority, held by the agent's
|
|
132
|
-
* owner — which is why the receiver decides the outcome. A receiver already
|
|
133
|
-
* inside the sharer's trust boundary (same owner, same org, or an active link)
|
|
134
|
-
* is granted immediately; anyone else opens a request the owner approves,
|
|
135
|
-
* exactly like `delegateGrant`'s new-party path.
|
|
136
|
-
*/
|
|
137
|
-
shareArtifact(artifactId: string, input: {
|
|
138
|
-
holderId: string;
|
|
139
|
-
expiresAt?: string | null;
|
|
140
|
-
}, opts?: {
|
|
141
|
-
idempotencyKey?: string;
|
|
142
|
-
}): Promise<DelegateContextGrantResult>;
|
|
143
127
|
revokeGrant(grantId: string): Promise<{
|
|
144
128
|
status: string;
|
|
145
129
|
revokedCount?: number;
|
|
@@ -136,33 +136,6 @@ export class ContextGrantsClient {
|
|
|
136
136
|
truncatedAgreements: parsed.truncatedAgreements ?? 0,
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
/**
|
|
140
|
-
* share an artifact THIS agent authored with another agent.
|
|
141
|
-
*
|
|
142
|
-
* Not a delegation: an agent holds no grant over its own output, so there is
|
|
143
|
-
* no parent to attenuate. Authorship is the authority, held by the agent's
|
|
144
|
-
* owner — which is why the receiver decides the outcome. A receiver already
|
|
145
|
-
* inside the sharer's trust boundary (same owner, same org, or an active link)
|
|
146
|
-
* is granted immediately; anyone else opens a request the owner approves,
|
|
147
|
-
* exactly like `delegateGrant`'s new-party path.
|
|
148
|
-
*/
|
|
149
|
-
async shareArtifact(artifactId, input, opts) {
|
|
150
|
-
const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/share`, {
|
|
151
|
-
method: 'POST',
|
|
152
|
-
headers: buildOperatorHeaders(this.operatorKey, this.agentId, {
|
|
153
|
-
'content-type': 'application/json',
|
|
154
|
-
...(opts?.idempotencyKey
|
|
155
|
-
? { 'Idempotency-Key': opts.idempotencyKey }
|
|
156
|
-
: {}),
|
|
157
|
-
}),
|
|
158
|
-
body: JSON.stringify(input),
|
|
159
|
-
});
|
|
160
|
-
const body = await res.text().catch(() => '');
|
|
161
|
-
if (!res.ok) {
|
|
162
|
-
throwApiError(res, body, `shareArtifact failed: ${res.status} ${res.statusText}`);
|
|
163
|
-
}
|
|
164
|
-
return parseGrantOrPending(body, 'POST /context/artifacts/:id/share');
|
|
165
|
-
}
|
|
166
139
|
async revokeGrant(grantId) {
|
|
167
140
|
const res = await fetch(`${this.baseUrl}/context/grants/${encodeURIComponent(grantId)}`, {
|
|
168
141
|
method: 'DELETE',
|
package/dist/http/index.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export type { MyOrg, OrgResolution } from './OrgsClient.js';
|
|
|
28
28
|
export { AgentSearchClient } from './AgentSearchClient.js';
|
|
29
29
|
export { TelemetryClient } from './TelemetryClient.js';
|
|
30
30
|
export { InboxClient } from './InboxClient.js';
|
|
31
|
+
export { BucketsClient, BUCKET_ITEM_KINDS } from './BucketsClient.js';
|
|
32
|
+
export type { BucketItemKind, BucketItemRef, BucketItemPage, BucketAddMembersResult, BucketContents, BucketThread, BucketJoinResult, BucketMessage, BucketInsertResult, BucketMisfit, BucketMisfitChoice, BucketMoveResult, BucketShare, BucketView, } from './BucketsClient.js';
|
|
31
33
|
export { SessionsClient } from './SessionsClient.js';
|
|
32
34
|
export type { AssistantSessionRow } from './SessionsClient.js';
|
|
33
35
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from '../inboxAck.js';
|
package/dist/http/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export { fetchMyOrgs, resolveOrgSelector, fetchDelegateAccess, fetchHostedAgentA
|
|
|
19
19
|
export { AgentSearchClient } from './AgentSearchClient.js';
|
|
20
20
|
export { TelemetryClient } from './TelemetryClient.js';
|
|
21
21
|
export { InboxClient } from './InboxClient.js';
|
|
22
|
+
export { BucketsClient, BUCKET_ITEM_KINDS } from './BucketsClient.js';
|
|
22
23
|
export { SessionsClient } from './SessionsClient.js';
|
|
23
24
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from '../inboxAck.js';
|
|
24
25
|
// The lane header, built in one place — see operatorHeaders.ts. Exported
|
|
@@ -12,7 +12,8 @@ export interface OperationError {
|
|
|
12
12
|
/**
|
|
13
13
|
* What the caller may pick from, when the refusal is a choice: the
|
|
14
14
|
* agreements of WORK_SELECTION_REQUIRED, the workspaces of
|
|
15
|
-
* WORKSPACE_SELECTION_REQUIRED
|
|
15
|
+
* WORKSPACE_SELECTION_REQUIRED, the onMisfit values (share, split) of
|
|
16
|
+
* BUCKET_MEMBERS_CANNOT_READ. Carried through verbatim so an assistant can
|
|
16
17
|
* ask its human instead of re-reading the message.
|
|
17
18
|
*/
|
|
18
19
|
choices?: unknown[];
|
|
@@ -22,6 +23,15 @@ export interface OperationError {
|
|
|
22
23
|
* the same shape an open's action recovery has.
|
|
23
24
|
*/
|
|
24
25
|
recovery?: Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* What the refusal is about, when the server said: the misfits of
|
|
28
|
+
* BUCKET_MEMBERS_CANNOT_READ (with any shares made before it stopped), the
|
|
29
|
+
* bucket and missing readers of BUCKET_READERS_MISSING, the live outside
|
|
30
|
+
* engagements that stop a workspace blocking outside agents. Carried
|
|
31
|
+
* through verbatim so an assistant can list
|
|
32
|
+
* them instead of re-reading the message.
|
|
33
|
+
*/
|
|
34
|
+
impact?: Record<string, unknown>;
|
|
25
35
|
/** Scoped restart position returned when too many observation streams exist. */
|
|
26
36
|
recoveryCursor?: string;
|
|
27
37
|
/** hidden | unreadable, the same outcome open and access_explain give. */
|