@ziggs-ai/api-client 0.10.4 → 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 +168 -33
- 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 +5 -7
- package/dist/capabilities/marketplace.js +20 -14
- 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 +24 -19
- package/dist/http/AgreementClient.js +22 -14
- 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 +14 -5
- package/dist/http/GrantsClient.d.ts +14 -0
- package/dist/http/GrantsClient.js +18 -2
- package/dist/http/MarketplaceClient.d.ts +6 -6
- package/dist/http/MarketplaceClient.js +11 -11
- package/dist/http/TaskClient.d.ts +5 -0
- package/dist/http/TaskClient.js +2 -0
- 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 +44 -15
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CapabilityDefinition } from './types.js';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const agreementBuyCapability: CapabilityDefinition;
|
|
3
3
|
export declare const agreementBidCapability: CapabilityDefinition;
|
|
4
4
|
export declare const agreementBrokerCapability: CapabilityDefinition;
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const agreementRequestCapability: CapabilityDefinition;
|
|
6
6
|
export declare const agreementOfferCapability: CapabilityDefinition;
|
|
7
7
|
export declare const agreementHandoffCapability: CapabilityDefinition;
|
|
8
8
|
export declare const AGREEMENT_VERB_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -30,7 +30,7 @@ const TERMS_PARAMS = {
|
|
|
30
30
|
},
|
|
31
31
|
price: {
|
|
32
32
|
type: 'number',
|
|
33
|
-
description: '
|
|
33
|
+
description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Recording a price here does not itself move any. Convert BOTH ways or you are off by 100x: a human who says "ϟ2" means 200, and quoting 500 back as "ϟ500" is the same mistake inverted.',
|
|
34
34
|
},
|
|
35
35
|
billing: {
|
|
36
36
|
type: 'string',
|
|
@@ -101,13 +101,13 @@ function publishedNext(env) {
|
|
|
101
101
|
];
|
|
102
102
|
}
|
|
103
103
|
/* ─────────────────────────── direct, to one counterparty ─────────────────── */
|
|
104
|
-
export const
|
|
105
|
-
key: '
|
|
106
|
-
names: { sdk: '
|
|
107
|
-
title: '
|
|
104
|
+
export const agreementBuyCapability = {
|
|
105
|
+
key: 'agreement_buy',
|
|
106
|
+
names: { sdk: 'agreement_buy', mcp: 'ziggs_agreement_buy' },
|
|
107
|
+
title: 'They work, you pay (named counterparty)',
|
|
108
108
|
descriptions: {
|
|
109
|
-
sdk: 'Ask a named counterparty to do work your side pays for. They provide, you pay. For work you would do for them instead, use agreement_bid; to reach whoever is available rather than someone named, use
|
|
110
|
-
mcp: 'Ask a named counterparty to do work your side pays for: they provide, you pay. Prefer claiming their listing first if they have one (ziggs_marketplace_view then ziggs_agreement_claim) — listings are take-it-or-leave-it and most published agents refuse direct proposals. Use this for bespoke terms, renegotiation, or a counterparty with no listing. For work you would do for them, use ziggs_agreement_bid; to reach whoever is available, use
|
|
109
|
+
sdk: 'Ask a named counterparty to do work your side pays for. They provide, you pay. For work you would do for them instead, use agreement_bid; to reach whoever is available rather than someone named, use agreement_request.',
|
|
110
|
+
mcp: 'Ask a named counterparty to do work your side pays for: they provide, you pay. Prefer claiming their listing first if they have one (ziggs_marketplace_view then ziggs_agreement_claim) — listings are take-it-or-leave-it and most published agents refuse direct proposals. Use this for bespoke terms, renegotiation, or a counterparty with no listing. For work you would do for them, use ziggs_agreement_bid; to reach whoever is available, use ziggs_agreement_request.',
|
|
111
111
|
},
|
|
112
112
|
annotation: 'write',
|
|
113
113
|
params: {
|
|
@@ -133,10 +133,10 @@ export const agreementCommissionCapability = {
|
|
|
133
133
|
export const agreementBidCapability = {
|
|
134
134
|
key: 'agreement_bid',
|
|
135
135
|
names: { sdk: 'agreement_bid', mcp: 'ziggs_agreement_bid' },
|
|
136
|
-
title: '
|
|
136
|
+
title: 'You work, they pay (named counterparty)',
|
|
137
137
|
descriptions: {
|
|
138
138
|
sdk: 'Offer to do work for a named counterparty, who pays. You provide. To publish the same offer to whoever wants it rather than one named party, use agreement_offer.',
|
|
139
|
-
mcp: 'Offer to do work for a named counterparty, who pays: you provide. This is the direct form of a listing — to publish the same offer to whoever wants it, use ziggs_agreement_offer instead. To ask someone else to do work you pay for, use
|
|
139
|
+
mcp: 'Offer to do work for a named counterparty, who pays: you provide. This is the direct form of a listing — to publish the same offer to whoever wants it, use ziggs_agreement_offer instead. To ask someone else to do work you pay for, use ziggs_agreement_buy.',
|
|
140
140
|
},
|
|
141
141
|
annotation: 'write',
|
|
142
142
|
params: {
|
|
@@ -162,7 +162,7 @@ export const agreementBidCapability = {
|
|
|
162
162
|
export const agreementBrokerCapability = {
|
|
163
163
|
key: 'agreement_broker',
|
|
164
164
|
names: { sdk: 'agreement_broker', mcp: 'ziggs_agreement_broker' },
|
|
165
|
-
title: '
|
|
165
|
+
title: 'A third party does the work, you arrange it',
|
|
166
166
|
descriptions: {
|
|
167
167
|
sdk: 'Introduce a provider to a customer: the provider does the work, the counterparty pays, and neither side is you. The provider must already have a matching active offer.',
|
|
168
168
|
mcp: 'Introduce a provider to a customer: the named provider does the work, the counterparty pays, and you are neither. The provider needs a matching active offer for this to stand. This is the one verb where naming a provider is the point — every other agreement verb infers it from the direction of work.',
|
|
@@ -195,10 +195,10 @@ export const agreementBrokerCapability = {
|
|
|
195
195
|
sdkOptions: { isAgreementCreation: true },
|
|
196
196
|
};
|
|
197
197
|
/* ──────────────────────────────── broadcast ──────────────────────────────── */
|
|
198
|
-
export const
|
|
199
|
-
key: '
|
|
200
|
-
names: { sdk: '
|
|
201
|
-
title: '
|
|
198
|
+
export const agreementRequestCapability = {
|
|
199
|
+
key: 'agreement_request',
|
|
200
|
+
names: { sdk: 'agreement_request', mcp: 'ziggs_agreement_request' },
|
|
201
|
+
title: 'They work, you pay (open to anyone)',
|
|
202
202
|
descriptions: {
|
|
203
203
|
sdk: 'Post work you want done and will pay for, for whoever claims it. Whoever claims does the work. To offer work you would do instead, use agreement_offer.',
|
|
204
204
|
mcp: 'Post work you want done and will pay for: whoever claims it does the work. Use this when nothing already listed fits, rather than proposing to named counterparties one at a time. To offer work you would do, use ziggs_agreement_offer. Claimable via ziggs_agreement_claim; visible in ziggs_marketplace_view.',
|
|
@@ -220,10 +220,10 @@ export const agreementQuestCapability = {
|
|
|
220
220
|
export const agreementOfferCapability = {
|
|
221
221
|
key: 'agreement_offer',
|
|
222
222
|
names: { sdk: 'agreement_offer', mcp: 'ziggs_agreement_offer' },
|
|
223
|
-
title: '
|
|
223
|
+
title: 'You work, they pay (open to anyone)',
|
|
224
224
|
descriptions: {
|
|
225
225
|
sdk: 'Publish work you will do, for whoever claims it and pays. You provide. This is your listing: it stands until revoked or exhausted. To offer one named counterparty instead, use agreement_bid.',
|
|
226
|
-
mcp: 'Publish work you will do, for whoever claims it and pays: you provide. This is your listing — it stands until revoked or exhausted, and claimers take it as posted rather than countering it. To offer one named counterparty instead, use ziggs_agreement_bid. To ask for work rather than supply it, use
|
|
226
|
+
mcp: 'Publish work you will do, for whoever claims it and pays: you provide. This is your listing — it stands until revoked or exhausted, and claimers take it as posted rather than countering it. To offer one named counterparty instead, use ziggs_agreement_bid. To ask for work rather than supply it, use ziggs_agreement_request.',
|
|
227
227
|
},
|
|
228
228
|
annotation: 'write',
|
|
229
229
|
params: { audience: AUDIENCE_PARAM, ...TERMS_PARAMS },
|
|
@@ -248,7 +248,7 @@ export const agreementOfferCapability = {
|
|
|
248
248
|
export const agreementHandoffCapability = {
|
|
249
249
|
key: 'agreement_handoff',
|
|
250
250
|
names: { sdk: 'agreement_handoff', mcp: 'ziggs_agreement_handoff' },
|
|
251
|
-
title: '
|
|
251
|
+
title: 'Pass a hire you hold to whoever claims it',
|
|
252
252
|
descriptions: {
|
|
253
253
|
sdk: 'Share an agent you have hired: the provider stays pinned, and whoever claims or approves becomes the customer the work is done for. Omit `to` to make it claimable by anyone. Handing off someone else\'s agent leaves that provider\'s approval pending — it must accept once before the hand-off can be claimed.',
|
|
254
254
|
mcp: 'Share an agent you have hired: the provider stays pinned, and whoever claims or approves is the CUSTOMER the work is done for, never the worker. Omit `to` to publish it claimable; name `to` and that party approves directly. On a priced hand-off the claimer is also the payer; omit price (or 0) and nobody is billed for their tasks. Handing off someone else\'s agent leaves that provider\'s approval pending — it must accept once before the hand-off can be claimed. The provider is read from the parent hire, so you do not pass it.',
|
|
@@ -296,10 +296,10 @@ export const agreementHandoffCapability = {
|
|
|
296
296
|
sdkOptions: { isAgreementCreation: true },
|
|
297
297
|
};
|
|
298
298
|
export const AGREEMENT_VERB_CAPABILITIES = [
|
|
299
|
-
|
|
299
|
+
agreementBuyCapability,
|
|
300
300
|
agreementBidCapability,
|
|
301
301
|
agreementBrokerCapability,
|
|
302
|
-
|
|
302
|
+
agreementRequestCapability,
|
|
303
303
|
agreementOfferCapability,
|
|
304
304
|
agreementHandoffCapability,
|
|
305
305
|
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CapabilityDefinition } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* the one claim verb.
|
|
3
|
+
* the one claim verb. Requests, standing offers, and link invites
|
|
4
4
|
* are all open broadcasts; claiming any of them is this call. The respond
|
|
5
5
|
* tool no longer claims — it approves/rejects direct proposals only.
|
|
6
6
|
*/
|
|
@@ -2,7 +2,7 @@ import { claimOpenAgreement } from '../http/agreementFlows.js';
|
|
|
2
2
|
import { linkIsReachOnly } from './links.js';
|
|
3
3
|
import { fullCreds } from './types.js';
|
|
4
4
|
/**
|
|
5
|
-
* the one claim verb.
|
|
5
|
+
* the one claim verb. Requests, standing offers, and link invites
|
|
6
6
|
* are all open broadcasts; claiming any of them is this call. The respond
|
|
7
7
|
* tool no longer claims — it approves/rejects direct proposals only.
|
|
8
8
|
*/
|
|
@@ -11,15 +11,15 @@ export const agreementClaimCapability = {
|
|
|
11
11
|
names: { sdk: 'agreement_claim', mcp: 'ziggs_agreement_claim' },
|
|
12
12
|
title: 'Claim a posted agreement',
|
|
13
13
|
descriptions: {
|
|
14
|
-
sdk: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim, activation is instant, no negotiation turns. Claims a
|
|
15
|
-
mcp: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim, activation is instant, no negotiation turns. Claims a
|
|
14
|
+
sdk: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim, activation is instant, no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party and the agreement activates. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with agreement_respond instead, not claimed.',
|
|
15
|
+
mcp: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim, activation is instant, no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party and the agreement activates. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with ziggs_marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with ziggs_agreement_respond instead, not claimed. You cannot claim your own broadcast.',
|
|
16
16
|
},
|
|
17
17
|
annotation: 'write',
|
|
18
18
|
params: {
|
|
19
19
|
agreementId: {
|
|
20
20
|
type: 'string',
|
|
21
21
|
required: true,
|
|
22
|
-
description: 'The open agreement to claim (
|
|
22
|
+
description: 'The open agreement to claim (request / offer / link invite id)',
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
needsAgentId: true,
|
|
@@ -40,7 +40,7 @@ export const agreementClaimCapability = {
|
|
|
40
40
|
? 'Standing offer claimed — the publisher provides, your side pays. Spawn work under it with the task-create tool.'
|
|
41
41
|
: kind === 'hand-off'
|
|
42
42
|
? 'Hand-off claimed — the pinned agent works FOR you: you are the customer (and the payer when priced), never the worker. Spawn work under it with the task-create tool.'
|
|
43
|
-
: '
|
|
43
|
+
: 'Request claimed — you provide the work. Read the terms, then post progress and set the task result under this agreement.',
|
|
44
44
|
agreement,
|
|
45
45
|
};
|
|
46
46
|
},
|
|
@@ -29,8 +29,10 @@ export declare const shareArtifactCapability: CapabilityDefinition;
|
|
|
29
29
|
*/
|
|
30
30
|
export declare const attachArtifactCapability: CapabilityDefinition;
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* The escape hatch for bytes too big to travel inside a tool call
|
|
33
|
+
* (the remote MCP transport caps a request body at 1MB). The ordinary way to
|
|
34
|
+
* record a file is `artifact_record` with contentBase64, which runs this presign
|
|
35
|
+
* → PUT → complete sequence internally.
|
|
34
36
|
*/
|
|
35
37
|
export declare const uploadArtifactUrlCapability: CapabilityDefinition;
|
|
36
38
|
export declare const completeArtifactFileCapability: CapabilityDefinition;
|
|
@@ -18,33 +18,153 @@ function reportingHint(env, contentType, taskId) {
|
|
|
18
18
|
return `Reporting finished work? Record it with contentType=result bound to the task (taskId), then close the task with ${close} — chat messages are conversation only.`;
|
|
19
19
|
}
|
|
20
20
|
/** Inline body cap + escape hatch, shared by SDK/MCP descriptions. */
|
|
21
|
-
const ARTIFACT_RECORD_INLINE_CAP = 'Inline text max 50000 characters. Over that:
|
|
22
|
-
'(
|
|
23
|
-
'part ids in the index.
|
|
21
|
+
const ARTIFACT_RECORD_INLINE_CAP = 'Inline text max 50000 characters. Over that: pass the body as a file ' +
|
|
22
|
+
'(filename + mime + contentBase64, handled in this one call), or record an ' +
|
|
23
|
+
'index artifact plus part artifacts and list the part ids in the index. ' +
|
|
24
|
+
'The server does not auto-split.';
|
|
25
|
+
/**
|
|
26
|
+
* The file rail, as one call.
|
|
27
|
+
*
|
|
28
|
+
* Recording a file used to be three steps a caller had to perform in order:
|
|
29
|
+
* ask for a presigned URL, PUT the bytes to object storage, then report the
|
|
30
|
+
* sha256 back. That is our storage design surfaced as a caller obligation —
|
|
31
|
+
* one of those two tools spent 1,421 characters explaining it — and every
|
|
32
|
+
* agent paid for both schemas in every session whether or not it ever wrote a
|
|
33
|
+
* file. Hand `contentBase64` here and the presign, the PUT and the completion
|
|
34
|
+
* all happen inside this handler.
|
|
35
|
+
*
|
|
36
|
+
* `artifact_upload_url` / `artifact_complete_file` still exist for the one case
|
|
37
|
+
* that genuinely needs them: bytes too large to travel inside a tool call (the
|
|
38
|
+
* remote MCP transport caps a request body at 1MB). They are no longer the
|
|
39
|
+
* documented path.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* The file's bytes, or a refusal that says the payload arrived damaged.
|
|
43
|
+
*
|
|
44
|
+
* `Buffer.from(s, 'base64')` does not validate: it silently drops every
|
|
45
|
+
* character it does not recognise and stops at the first bad padding, so a
|
|
46
|
+
* truncated payload — the ordinary failure when a model inlines a long file —
|
|
47
|
+
* decodes to FEWER bytes and everything downstream agrees with itself. The
|
|
48
|
+
* sha256 is taken over what survived, the upload completes, and the caller is
|
|
49
|
+
* told the file stored. Nothing surfaces until someone reads a garbled extract
|
|
50
|
+
* with no way back to the call that wrote it.
|
|
51
|
+
*
|
|
52
|
+
* Re-encoding is the cheap question Node will not ask: if every character
|
|
53
|
+
* carried data, the bytes round-trip to the same string. Whitespace is stripped
|
|
54
|
+
* first (wrapping is normal in transports) and base64url is folded into
|
|
55
|
+
* standard base64, so only genuine corruption is refused.
|
|
56
|
+
*/
|
|
57
|
+
function decodeFileBytes(contentBase64) {
|
|
58
|
+
const normalized = contentBase64
|
|
59
|
+
.replace(/\s+/g, '')
|
|
60
|
+
.replace(/-/g, '+')
|
|
61
|
+
.replace(/_/g, '/')
|
|
62
|
+
.replace(/=+$/, '');
|
|
63
|
+
const bytes = Buffer.from(normalized, 'base64');
|
|
64
|
+
if (bytes.byteLength < 1) {
|
|
65
|
+
throw new Error('contentBase64 decoded to zero bytes — pass the base64 of the file, or use text for a text artifact.');
|
|
66
|
+
}
|
|
67
|
+
if (bytes.toString('base64').replace(/=+$/, '') !== normalized) {
|
|
68
|
+
throw new Error(`contentBase64 is not intact base64: ${normalized.length} characters decoded to ${bytes.byteLength} bytes and do not re-encode to what was sent, so part of the payload was dropped. ` +
|
|
69
|
+
'Send the whole encoding — storing it would give you a smaller, corrupt file whose checksum matches the corruption. ' +
|
|
70
|
+
'Bytes over ~1MB do not fit in a tool call at all: use artifact_upload_url for those.');
|
|
71
|
+
}
|
|
72
|
+
return bytes;
|
|
73
|
+
}
|
|
74
|
+
async function recordFileArtifact(args, env, visibility) {
|
|
75
|
+
const filename = args['filename']?.trim();
|
|
76
|
+
const mime = args['mime']?.trim();
|
|
77
|
+
if (!filename)
|
|
78
|
+
throw new Error('filename is required when passing contentBase64');
|
|
79
|
+
if (!mime)
|
|
80
|
+
throw new Error('mime is required when passing contentBase64');
|
|
81
|
+
const contentBase64 = args['contentBase64'];
|
|
82
|
+
// Decoded once, here, and handed on as bytes: `uploadUrl` takes `content`
|
|
83
|
+
// directly, so passing the string instead made every file call decode the
|
|
84
|
+
// same payload twice — and a stdio caller has no 1MB cap, so "the payload"
|
|
85
|
+
// can be tens of megabytes.
|
|
86
|
+
const bytes = decodeFileBytes(contentBase64);
|
|
87
|
+
const creds = fullCreds(env);
|
|
88
|
+
const client = new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId);
|
|
89
|
+
const taskId = args['taskId'];
|
|
90
|
+
const byteSize = bytes.byteLength;
|
|
91
|
+
const uploaded = await client.uploadUrl({
|
|
92
|
+
filename,
|
|
93
|
+
mime,
|
|
94
|
+
// The byte length of what will be PUT, not of the base64 envelope — the
|
|
95
|
+
// presign signs this number and S3 rejects a mismatch.
|
|
96
|
+
byteSize,
|
|
97
|
+
format: args['format'],
|
|
98
|
+
visibility,
|
|
99
|
+
chatId: args['chatId'],
|
|
100
|
+
agreementId: args['agreementId'],
|
|
101
|
+
taskId,
|
|
102
|
+
idempotencyKey: args['idempotencyKey'],
|
|
103
|
+
// ArtifactsClient does the PUT and hands back the sha256 of what it sent,
|
|
104
|
+
// so the checksum is never something the caller has to compute.
|
|
105
|
+
content: bytes,
|
|
106
|
+
});
|
|
107
|
+
if (!uploaded.checksum) {
|
|
108
|
+
throw new Error(`Uploaded ${filename} but the storage layer returned no checksum, so the artifact is incomplete. ` +
|
|
109
|
+
`Finish it with artifact_complete_file (artifactId ${uploaded.artifactId}) once you have the sha256 of the bytes.`);
|
|
110
|
+
}
|
|
111
|
+
const artifact = await client.completeFile(uploaded.artifactId, uploaded.checksum);
|
|
112
|
+
return {
|
|
113
|
+
ok: true,
|
|
114
|
+
artifactId: uploaded.artifactId,
|
|
115
|
+
artifact,
|
|
116
|
+
visibility,
|
|
117
|
+
chatId: args['chatId'],
|
|
118
|
+
agreementId: args['agreementId'],
|
|
119
|
+
taskId,
|
|
120
|
+
byteSize,
|
|
121
|
+
note: 'File stored and completed. Text extraction runs asynchronously — when extractionStatus is ok, read the extracted text rather than downloading the bytes again.',
|
|
122
|
+
reportingHint: reportingHint(env, args['contentType'], taskId),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
24
125
|
export const recordArtifactCapability = {
|
|
25
126
|
key: 'artifact_record',
|
|
26
127
|
names: { sdk: 'artifact_record', mcp: 'ziggs_artifact_record' },
|
|
27
128
|
title: 'Record an artifact',
|
|
28
129
|
descriptions: {
|
|
29
|
-
sdk: 'Write an artifact
|
|
130
|
+
sdk: 'Write an artifact — text (text) or a file (filename + mime + contentBase64; the upload ' +
|
|
131
|
+
'happens inside this call). Scope is optional: pass agreementId or chatId to record it there, ' +
|
|
30
132
|
'or pass no scope at all to record it as yours alone and attach it somewhere later. ' +
|
|
31
133
|
'Set visibility explicitly. For a finished deliverable, set contentType=result and pass ' +
|
|
32
|
-
'taskId to bind it to the task
|
|
134
|
+
'taskId to bind it to the task. A heavy deliverable belongs in an artifact rather than pasted into a message. ' +
|
|
33
135
|
ARTIFACT_RECORD_INLINE_CAP,
|
|
34
|
-
mcp: 'Write an artifact
|
|
136
|
+
mcp: 'Write an artifact — text (text) or a file (filename + mime + contentBase64; presign, upload ' +
|
|
137
|
+
'and completion all happen inside this one call, so there is no separate upload dance). ' +
|
|
138
|
+
'Scope is optional — pass agreementId or chatId to record it into that ' +
|
|
35
139
|
'scope, pass taskId alone to bind a deliverable to its task, or pass no scope at all for a ' +
|
|
36
140
|
'free-standing artifact that is yours until you attach or share it. Never guess a scope: ' +
|
|
37
141
|
'recording with none always succeeds. Set visibility explicitly. ' +
|
|
38
142
|
'For a finished deliverable, set contentType=result and pass taskId to bind it to the task. ' +
|
|
39
|
-
'
|
|
143
|
+
'When the work rides a task, close it with ziggs_task_set_result too: that is what the next agent reads from its own inbox. ' +
|
|
40
144
|
ARTIFACT_RECORD_INLINE_CAP,
|
|
41
145
|
},
|
|
42
146
|
annotation: 'write',
|
|
43
147
|
params: {
|
|
44
148
|
text: {
|
|
45
149
|
type: 'string',
|
|
46
|
-
|
|
47
|
-
|
|
150
|
+
description: 'Artifact body for a text artifact (max 50000 chars). Pass this or contentBase64, not both.',
|
|
151
|
+
},
|
|
152
|
+
contentBase64: {
|
|
153
|
+
type: 'string',
|
|
154
|
+
description: 'Base64 file bytes for a file artifact — requires filename and mime. This call presigns, uploads and completes; you never compute a checksum. Bytes over ~1MB will not fit in a tool call: use artifact_upload_url for those.',
|
|
155
|
+
},
|
|
156
|
+
filename: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
description: 'Original filename — required with contentBase64',
|
|
159
|
+
},
|
|
160
|
+
mime: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
description: 'Content-Type of the file bytes (e.g. application/pdf) — required with contentBase64',
|
|
163
|
+
},
|
|
164
|
+
format: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
enum: ['pdf', 'docx', 'hwpx', 'md', 'txt', 'html'],
|
|
167
|
+
description: 'Optional file format; inferred from filename when omitted',
|
|
48
168
|
},
|
|
49
169
|
visibility: {
|
|
50
170
|
type: 'string',
|
|
@@ -89,11 +209,24 @@ export const recordArtifactCapability = {
|
|
|
89
209
|
if (visibility !== 'chat' && visibility !== 'agent-private') {
|
|
90
210
|
throw new Error('visibility must be chat or agent-private');
|
|
91
211
|
}
|
|
212
|
+
// One tool, two body forms. Both at once is refused rather
|
|
213
|
+
// than resolved: which one won would be invisible in the response.
|
|
214
|
+
const text = args['text'];
|
|
215
|
+
const contentBase64 = args['contentBase64'];
|
|
216
|
+
if (text != null && contentBase64 != null) {
|
|
217
|
+
throw new Error('Pass text or contentBase64, not both');
|
|
218
|
+
}
|
|
219
|
+
if (contentBase64 != null) {
|
|
220
|
+
return recordFileArtifact(args, env, visibility);
|
|
221
|
+
}
|
|
222
|
+
if (text == null) {
|
|
223
|
+
throw new Error('Pass text for a text artifact, or contentBase64 (with filename and mime) for a file');
|
|
224
|
+
}
|
|
92
225
|
const contentType = args['contentType'];
|
|
93
226
|
const taskId = args['taskId'];
|
|
94
227
|
const creds = fullCreds(env);
|
|
95
228
|
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).writeStrict({
|
|
96
|
-
text
|
|
229
|
+
text,
|
|
97
230
|
visibility,
|
|
98
231
|
chatId,
|
|
99
232
|
agreementId,
|
|
@@ -284,26 +417,27 @@ export const attachArtifactCapability = {
|
|
|
284
417
|
},
|
|
285
418
|
};
|
|
286
419
|
/**
|
|
287
|
-
*
|
|
288
|
-
*
|
|
420
|
+
* The escape hatch for bytes too big to travel inside a tool call
|
|
421
|
+
* (the remote MCP transport caps a request body at 1MB). The ordinary way to
|
|
422
|
+
* record a file is `artifact_record` with contentBase64, which runs this presign
|
|
423
|
+
* → PUT → complete sequence internally.
|
|
289
424
|
*/
|
|
290
425
|
export const uploadArtifactUrlCapability = {
|
|
291
426
|
key: 'artifact_upload_url',
|
|
292
427
|
names: { sdk: 'artifact_upload_url', mcp: 'ziggs_artifact_upload_url' },
|
|
293
|
-
title: 'Start a file upload',
|
|
428
|
+
title: 'Start a large file upload',
|
|
294
429
|
descriptions: {
|
|
295
|
-
sdk: '
|
|
296
|
-
'
|
|
297
|
-
'
|
|
298
|
-
'
|
|
299
|
-
'
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
'
|
|
303
|
-
'
|
|
304
|
-
'
|
|
305
|
-
'
|
|
306
|
-
'After extract finishes (extractionStatus=ok), read text from context / the derived ' +
|
|
430
|
+
sdk: 'Large-file escape hatch. Ordinary files go through artifact_record (filename + mime + ' +
|
|
431
|
+
'contentBase64), which does all of this in one call — use this only when the bytes are too ' +
|
|
432
|
+
'large to travel inside a tool call. Returns a short-lived uploadUrl: PUT the exact byteSize ' +
|
|
433
|
+
'bytes to it (Content-Type = mime), then call artifact_complete_file with the sha256 hex of ' +
|
|
434
|
+
'those bytes.',
|
|
435
|
+
mcp: 'Large-file escape hatch (pdf/docx/hwpx/md/txt/html). Ordinary files go through ' +
|
|
436
|
+
'ziggs_artifact_record (filename + mime + contentBase64), which presigns, uploads and ' +
|
|
437
|
+
'completes in one call — use this only when the bytes are too large to travel inside a tool ' +
|
|
438
|
+
'call. Returns uploadUrl + artifactId: PUT the bytes with the declared mime, then call ' +
|
|
439
|
+
'ziggs_artifact_complete_file with sha256 hex. Scope optional (chatId / agreementId / ' +
|
|
440
|
+
'taskId). After extract finishes (extractionStatus=ok), read text from context / the derived ' +
|
|
307
441
|
'extracted-text artifact.',
|
|
308
442
|
},
|
|
309
443
|
annotation: 'write',
|
|
@@ -387,15 +521,16 @@ export const uploadArtifactUrlCapability = {
|
|
|
387
521
|
export const completeArtifactFileCapability = {
|
|
388
522
|
key: 'artifact_complete_file',
|
|
389
523
|
names: { sdk: 'artifact_complete_file', mcp: 'ziggs_artifact_complete_file' },
|
|
390
|
-
title: 'Finish a file upload',
|
|
524
|
+
title: 'Finish a large file upload',
|
|
391
525
|
descriptions: {
|
|
392
|
-
sdk: '
|
|
393
|
-
'you
|
|
394
|
-
'or context until extractionStatus is ok/failed;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
'
|
|
398
|
-
'
|
|
526
|
+
sdk: 'Second half of the large-file escape hatch — only needed after artifact_upload_url. ' +
|
|
527
|
+
'Pass artifactId + sha256 hex of the bytes you PUT. Sets extractionStatus=pending and ' +
|
|
528
|
+
'enqueues extract. Poll artifact_list or context until extractionStatus is ok/failed; ' +
|
|
529
|
+
'download still works while pending.',
|
|
530
|
+
mcp: 'Second half of the large-file escape hatch — only needed after ziggs_artifact_upload_url. ' +
|
|
531
|
+
'Pass artifactId + sha256 hex of the bytes you PUT. Enqueues text extract. When ' +
|
|
532
|
+
'extractionStatus is ok, read extracted text via ziggs_context_read / grants — file bytes ' +
|
|
533
|
+
'stay on ziggs_artifact_download.',
|
|
399
534
|
},
|
|
400
535
|
annotation: 'write',
|
|
401
536
|
params: {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { type CapabilityDefinition } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* the decided chat surface for agents: chat_open only.
|
|
4
|
-
* There is deliberately NO chat-listing tool on the SDK:
|
|
5
|
-
*
|
|
6
|
-
* so "what chats can I see" is already answered by grant_list
|
|
4
|
+
* There is deliberately NO chat-listing tool on the SDK: taking part in a room
|
|
5
|
+
* IS a grant on it, so "what chats can I see" is already answered by grant_list
|
|
7
6
|
* scopeKind=chat. MCP keeps its rich session-UX lister
|
|
8
7
|
* (ziggs_chat_list) for humans in Cursor/Claude.
|
|
9
8
|
*/
|
|
@@ -2,9 +2,8 @@ import { openConversation } from '../http/ChatClient.js';
|
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* the decided chat surface for agents: chat_open only.
|
|
5
|
-
* There is deliberately NO chat-listing tool on the SDK:
|
|
6
|
-
*
|
|
7
|
-
* so "what chats can I see" is already answered by grant_list
|
|
5
|
+
* There is deliberately NO chat-listing tool on the SDK: taking part in a room
|
|
6
|
+
* IS a grant on it, so "what chats can I see" is already answered by grant_list
|
|
8
7
|
* scopeKind=chat. MCP keeps its rich session-UX lister
|
|
9
8
|
* (ziggs_chat_list) for humans in Cursor/Claude.
|
|
10
9
|
*/
|
|
@@ -13,8 +12,8 @@ export const openConversationCapability = {
|
|
|
13
12
|
names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
|
|
14
13
|
title: 'Open a chat',
|
|
15
14
|
descriptions: {
|
|
16
|
-
sdk: 'Open or reuse a chat with a user or agent participant. Calling it again for the same participant returns the SAME chat, so it is how you find the conversation you already have with someone — pass newChat only when this really is a separate subject.
|
|
17
|
-
mcp: 'Open or reuse a chat with a user or agent participant. Calling it again for the same participant returns the SAME chat; pass newChat only when this really is a separate subject.
|
|
15
|
+
sdk: 'Open or reuse a chat with a user or agent participant. Calling it again for the same participant returns the SAME chat, so it is how you find the conversation you already have with someone — pass newChat only when this really is a separate subject. This is also how you reach the HUMAN who hired you when the work needs an answer only they have: pass their user id (context_snapshot lists it under users; on your agreement they are the payer/creator party), then chat_send in the chat it returns with no receiverId. Opening a room is issuing the other side access to it, so it needs something that authorizes that: they publish a listing, you share an org, you hold a live agreement with them (a link counts, so link_propose is how you get one with a peer in another org), or they claimed your invite. With none of those the call is refused and the refusal names the levers. To list chats you can already read, use grant_list scopeKind=chat.',
|
|
16
|
+
mcp: 'Open or reuse a chat with a user or agent participant. Calling it again for the same participant returns the SAME chat; pass newChat only when this really is a separate subject. Opening a room is issuing the other side access to it, so it needs something that authorizes that: they publish a listing, you share an org, you hold a live agreement with them (a link counts, so ziggs_link_propose is how you get one with a peer in another org), or they claimed your invite. With none of those the call is refused and names the levers.',
|
|
18
17
|
},
|
|
19
18
|
annotation: 'write',
|
|
20
19
|
params: {
|
|
@@ -38,7 +37,7 @@ export const openConversationCapability = {
|
|
|
38
37
|
throw new Error('participantId is required');
|
|
39
38
|
const { chatId, reused } = await openConversation(args['participantId'], fullCreds(env), { newChat: args['newChat'] === true });
|
|
40
39
|
const lister = env.surface === 'mcp' ? 'ziggs_grant_list' : 'grant_list';
|
|
41
|
-
const grantNote = `
|
|
40
|
+
const grantNote = `Opening the room issued you a grant on it, so the chat also appears in ${lister} scopeKind=chat for context reads.`;
|
|
42
41
|
// word the note from the outcome instead of covering both cases.
|
|
43
42
|
// "Open (or reused)" told the agent the distinction existed and then
|
|
44
43
|
// withheld it — worse than silence, because the agent cannot even tell
|
|
@@ -63,7 +63,7 @@ export const requestConnectionCapability = {
|
|
|
63
63
|
'On approval the server is connected (browser OAuth if needed) and you are granted the tools; call them with mcp_tool_call / mcp_tools_list (not connection_proxy).',
|
|
64
64
|
mcp: 'Ask your principal (the human) to connect a remote MCP server and grant you the listed tools. ' +
|
|
65
65
|
'Opens a connection-consent agreement as an approvable card in the chat you pass — the human approves it there like any other agreement (there is no MCP tool to approve it, so tell them to approve it in the chat). ' +
|
|
66
|
-
'On approval the server is connected (browser OAuth if needed) and you are granted the tools; the result shows up in ziggs_connection_list for use with ziggs_mcp_tools_list / ziggs_mcp_tool_call
|
|
66
|
+
'On approval the server is connected (browser OAuth if needed) and you are granted the tools; the result shows up in ziggs_connection_list for use with ziggs_mcp_tools_list / ziggs_mcp_tool_call.',
|
|
67
67
|
},
|
|
68
68
|
annotation: 'write',
|
|
69
69
|
params: {
|
|
@@ -214,6 +214,9 @@ export const contextDelegateCapability = {
|
|
|
214
214
|
const client = new ContextGrantsClient(creds.operatorKey, creds.agentId);
|
|
215
215
|
const result = await client.delegateGrant(args['parentGrantId'], {
|
|
216
216
|
holderId: args['holderId'],
|
|
217
|
+
// An agent passing part of its own grant onward hands it to another
|
|
218
|
+
// agent; a person is granted through their own surface.
|
|
219
|
+
holderKind: 'agent',
|
|
217
220
|
scope: { kind: scopeKind, id: scopeId },
|
|
218
221
|
temporal: temporal,
|
|
219
222
|
expiresAt: args['expiresAt'] ?? undefined,
|
|
@@ -5,12 +5,13 @@ import { type CapabilityDefinition } from './types.js';
|
|
|
5
5
|
* cross-session. `unreadableRails` comes from the backend so a short
|
|
6
6
|
* list is never presented as complete when the key can't read a rail.
|
|
7
7
|
*
|
|
8
|
-
* HOLD, not reach. `GET /grants` lists
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* HOLD, not reach. `GET /grants` lists rows this holder is named on, and taking
|
|
9
|
+
* part in a room or being party to a live agreement now IS such a row. Two kinds
|
|
10
|
+
* of access still leave none: authoring an artifact, and the org memberships
|
|
11
|
+
* that make an ORG-held grant cover you (the row names the org, not you). So a
|
|
12
|
+
* reader can still be entitled to something this list will never mention, and
|
|
13
|
+
* the description says so, because the old "the single answer" wording was read
|
|
14
|
+
* as completeness and an empty list as "no access".
|
|
14
15
|
*/
|
|
15
16
|
export declare const listGrantsCapability: CapabilityDefinition;
|
|
16
17
|
export declare const GRANTS_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -27,20 +27,21 @@ function parseScopeKinds(raw) {
|
|
|
27
27
|
* cross-session. `unreadableRails` comes from the backend so a short
|
|
28
28
|
* list is never presented as complete when the key can't read a rail.
|
|
29
29
|
*
|
|
30
|
-
* HOLD, not reach. `GET /grants` lists
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
30
|
+
* HOLD, not reach. `GET /grants` lists rows this holder is named on, and taking
|
|
31
|
+
* part in a room or being party to a live agreement now IS such a row. Two kinds
|
|
32
|
+
* of access still leave none: authoring an artifact, and the org memberships
|
|
33
|
+
* that make an ORG-held grant cover you (the row names the org, not you). So a
|
|
34
|
+
* reader can still be entitled to something this list will never mention, and
|
|
35
|
+
* the description says so, because the old "the single answer" wording was read
|
|
36
|
+
* as completeness and an empty list as "no access".
|
|
36
37
|
*/
|
|
37
38
|
export const listGrantsCapability = {
|
|
38
39
|
key: 'grant_list',
|
|
39
40
|
names: { sdk: 'grant_list', mcp: 'ziggs_grant_list' },
|
|
40
41
|
title: 'List grants you hold',
|
|
41
42
|
descriptions: {
|
|
42
|
-
sdk: 'List grants this agent holds — or, with role=issuer, grants this agent caused (author-shares and delegate children). Context (chat/agreement/org/artifact), connection, and wallet — canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). Default role=holder answers "what grants do I HOLD?", which is narrower than "what can I reach?": authorship,
|
|
43
|
-
mcp: 'List grants this delegate holds — or, with role=issuer, grants this delegate caused (author-shares and delegate children). Context (chat/agreement/org/artifact), connection, and wallet — canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). Default role=holder answers "what grants do I HOLD?", which is narrower than "what can I reach?": authorship,
|
|
43
|
+
sdk: 'List grants this agent holds — or, with role=issuer, grants this agent caused (author-shares and delegate children). Context (chat/agreement/org/artifact), connection, and wallet — canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). 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 artifact_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.',
|
|
44
|
+
mcp: 'List grants this delegate holds — or, with role=issuer, grants this delegate caused (author-shares and delegate children). Context (chat/agreement/org/artifact), connection, and wallet — canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). 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". After ziggs_artifact_share, pass role=issuer (and usually scopeKind=["artifact"]) to recover grantIds and revoke with ziggs_context_revoke_grant. Filter by scopeKind, scopeId, and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails. Cursor-paginated.',
|
|
44
45
|
},
|
|
45
46
|
annotation: 'read-only',
|
|
46
47
|
params: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { type CapabilitySurface, type CapabilityAnnotation, type CapabilityParam, type CapabilityEnv, type CapabilityDefinition, fullCreds, rethrowWithContext, } from './types.js';
|
|
2
2
|
export { nextCall, peerAgentId, type NextCall } from './nextCall.js';
|
|
3
|
-
export { AGREEMENT_VERB_CAPABILITIES,
|
|
4
|
-
export { PAYMENT_CAPABILITIES, paymentBalanceCapability
|
|
3
|
+
export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, } from './agreementVerbs.js';
|
|
4
|
+
export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
|
|
5
5
|
export { LINK_CAPABILITIES, createLinkInviteCapability, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
|
|
6
6
|
export { AGREEMENT_CAPABILITIES, agreementClaimCapability } from './agreements.js';
|
|
7
7
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { fullCreds, rethrowWithContext, } from './types.js';
|
|
2
2
|
export { nextCall, peerAgentId } from './nextCall.js';
|
|
3
|
-
export { AGREEMENT_VERB_CAPABILITIES,
|
|
4
|
-
export { PAYMENT_CAPABILITIES, paymentBalanceCapability
|
|
3
|
+
export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, } from './agreementVerbs.js';
|
|
4
|
+
export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
|
|
5
5
|
export { LINK_CAPABILITIES, createLinkInviteCapability, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
|
|
6
6
|
export { AGREEMENT_CAPABILITIES, agreementClaimCapability } from './agreements.js';
|
|
7
7
|
export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
|