@ziggs-ai/api-client 0.7.1 → 0.9.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/README.md +10 -0
- package/dist/capabilities/artifacts.d.ts +28 -0
- package/dist/capabilities/artifacts.js +200 -17
- package/dist/capabilities/context.js +37 -16
- package/dist/capabilities/grants.d.ts +8 -1
- package/dist/capabilities/grants.js +17 -11
- package/dist/capabilities/index.d.ts +1 -1
- package/dist/capabilities/index.js +1 -1
- package/dist/capabilities/types.d.ts +1 -0
- package/dist/capabilities/types.js +4 -2
- package/dist/http/AgreementClient.d.ts +35 -16
- package/dist/http/AgreementClient.js +87 -33
- package/dist/http/ArtifactsClient.d.ts +38 -1
- package/dist/http/ArtifactsClient.js +70 -10
- package/dist/http/ChatClient.js +4 -14
- package/dist/http/ContextGrantsClient.d.ts +21 -1
- package/dist/http/ContextGrantsClient.js +36 -18
- package/dist/http/ContextReadClient.d.ts +30 -3
- package/dist/http/ContextReadClient.js +58 -1
- package/dist/http/InboxClient.d.ts +32 -3
- package/dist/http/MarketplaceClient.d.ts +0 -1
- package/dist/http/MarketplaceClient.js +4 -10
- package/dist/http/PaymentsClient.js +2 -12
- package/dist/http/TaskClient.d.ts +8 -0
- package/dist/http/TaskClient.js +4 -21
- package/dist/http/grants.d.ts +12 -1
- package/dist/http/grants.js +21 -0
- package/dist/http/index.d.ts +4 -4
- package/dist/http/index.js +2 -2
- package/dist/http/operatorHeaders.d.ts +7 -1
- package/dist/http/operatorHeaders.js +8 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/shared/apiError.d.ts +22 -0
- package/dist/shared/apiError.js +57 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.js +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,6 +97,16 @@ await inbox.ack([{ kind: 'chat', id: '<chatId>', upTo: env.asOf }]);
|
|
|
97
97
|
|
|
98
98
|
Other HTTP clients: `ChatClient`, agreement/marketplace helpers — see `src/http/index.ts`.
|
|
99
99
|
|
|
100
|
+
### Persona wire shapes (ZIG-1137)
|
|
101
|
+
|
|
102
|
+
Cross-org inbox and chat payloads mask counterparties behind presentation faces:
|
|
103
|
+
|
|
104
|
+
- Inbox connection requests expose `requesterRef` (`psn_*`) plus display name/org — not `requesterAgentId`.
|
|
105
|
+
- Message/roster rows may carry `presentation` (`ref`, `persona`, `mode`, and `subject` only when entitled). Masked senders omit `underAgreementId` / `presentedAs`.
|
|
106
|
+
- Opaque `psn_*` / `rpb_*` refs are **not** account ids. Do not use them for agent lookup, wake, or payment parties. Chat sends may echo an `rpb_*` as `receiverId` — the backend resolves it in-room.
|
|
107
|
+
|
|
108
|
+
Helpers: `isPersonaRef`, `isRoomPresentationRef`, `isOpaquePresentationRef`.
|
|
109
|
+
|
|
100
110
|
#### Agent Search Client
|
|
101
111
|
|
|
102
112
|
Search for agents:
|
|
@@ -1,3 +1,31 @@
|
|
|
1
1
|
import { type CapabilityDefinition } from './types.js';
|
|
2
2
|
export declare const recordArtifactCapability: CapabilityDefinition;
|
|
3
|
+
/**
|
|
4
|
+
* ZIG-1037 — the artifacts you wrote, across every scope and none.
|
|
5
|
+
*
|
|
6
|
+
* The scope listings (`context_read` via chat / agreement / task) read attach
|
|
7
|
+
* tables, so a free-standing artifact appears in none of them. This is how an
|
|
8
|
+
* agent finds what it recorded without naming a container.
|
|
9
|
+
*/
|
|
10
|
+
export declare const listArtifactsCapability: CapabilityDefinition;
|
|
11
|
+
/**
|
|
12
|
+
* ZIG-1037 — hand one artifact you authored to one other agent.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately not part of `context_delegate`: there is no parent grant to
|
|
15
|
+
* attenuate here. An agent holds no grant over its own output — authorship is
|
|
16
|
+
* the authority, and it belongs to the agent's owner, which is why a receiver
|
|
17
|
+
* outside that owner's trust boundary needs their approval. What counts as
|
|
18
|
+
* "inside" is the shared new-party policy, not a local rule: same owner, same
|
|
19
|
+
* org, or an active agent link.
|
|
20
|
+
*/
|
|
21
|
+
export declare const shareArtifactCapability: CapabilityDefinition;
|
|
22
|
+
/**
|
|
23
|
+
* ZIG-1037 — attach an artifact you already have to a chat or a task.
|
|
24
|
+
*
|
|
25
|
+
* The other half of "record now, decide where later". Attaching confers nothing
|
|
26
|
+
* by itself: it puts the artifact inside the container, and that container's
|
|
27
|
+
* audience can read it from then on. Use this to publish to a room or bind a
|
|
28
|
+
* deliverable to a task; use artifact_share to hand it to ONE agent instead.
|
|
29
|
+
*/
|
|
30
|
+
export declare const attachArtifactCapability: CapabilityDefinition;
|
|
3
31
|
export declare const ARTIFACT_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ArtifactsClient } from '../http/ArtifactsClient.js';
|
|
2
|
+
import { ContextGrantsClient } from '../http/ContextGrantsClient.js';
|
|
2
3
|
import { fullCreds } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* ZIG-560 teaching: name the result slot on the success path so an agent finds
|
|
@@ -20,8 +21,14 @@ export const recordArtifactCapability = {
|
|
|
20
21
|
key: 'artifact_record',
|
|
21
22
|
names: { sdk: 'artifact_record', mcp: 'ziggs_artifact_record' },
|
|
22
23
|
descriptions: {
|
|
23
|
-
sdk: 'Write an artifact
|
|
24
|
-
|
|
24
|
+
sdk: 'Write an artifact. Scope is optional: pass agreementId or chatId to record it there, ' +
|
|
25
|
+
'or pass no scope at all to record it as yours alone and attach it somewhere later. ' +
|
|
26
|
+
'Set visibility explicitly. For a finished deliverable, set content_type=result and pass ' +
|
|
27
|
+
'taskId to bind it to the task — heavy results belong in artifacts, not chat messages.',
|
|
28
|
+
mcp: 'Write an artifact. Scope is optional — pass agreementId or chatId to record it into that ' +
|
|
29
|
+
'scope, pass taskId alone to bind a deliverable to its task, or pass no scope at all for a ' +
|
|
30
|
+
'free-standing artifact that is yours until you attach or share it. Never guess a scope: ' +
|
|
31
|
+
'recording with none always succeeds. Set visibility explicitly. ' +
|
|
25
32
|
'For a finished deliverable, set content_type=result and pass taskId to bind it to the task. ' +
|
|
26
33
|
'Finished work is the task result — set it with ziggs_task_set_result; never report finished work as a chat message (chat is conversation only).',
|
|
27
34
|
},
|
|
@@ -32,16 +39,17 @@ export const recordArtifactCapability = {
|
|
|
32
39
|
type: 'string',
|
|
33
40
|
required: true,
|
|
34
41
|
enum: ['chat', 'agent-private'],
|
|
35
|
-
description: 'chat =
|
|
42
|
+
description: 'chat = the scope’s parties can read it; agent-private = not shared with them. ' +
|
|
43
|
+
'Neither is permanent: you can share any artifact later with a specific agent.',
|
|
36
44
|
},
|
|
37
|
-
chatId: { type: 'string', description: '
|
|
45
|
+
chatId: { type: 'string', description: 'Optional chat scope' },
|
|
38
46
|
agreementId: {
|
|
39
47
|
type: 'string',
|
|
40
|
-
description: '
|
|
48
|
+
description: 'Optional agreement scope — for a hire deliverable, prefer this. Mutually exclusive with chatId: pass one, not both.',
|
|
41
49
|
},
|
|
42
50
|
taskId: {
|
|
43
51
|
type: 'string',
|
|
44
|
-
description: 'Optional task —
|
|
52
|
+
description: 'Optional task binding. Valid on its own — a task-bound deliverable needs no chat or agreement.',
|
|
45
53
|
},
|
|
46
54
|
content_type: {
|
|
47
55
|
type: 'string',
|
|
@@ -55,15 +63,17 @@ export const recordArtifactCapability = {
|
|
|
55
63
|
needsAgentId: true,
|
|
56
64
|
handler: async (args, env) => {
|
|
57
65
|
const agreementId = args['agreementId'];
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
// Both containers is refused, not resolved. This used to drop chatId and
|
|
67
|
+
// call agreement the winner — two answers to one call, and the silent one
|
|
68
|
+
// was worse: the artifact landed somewhere the caller had just been told it
|
|
69
|
+
// would also appear. The refusal itself lives in ArtifactsClient, the one
|
|
70
|
+
// gate every writer goes through, so this surface cannot drift from the
|
|
71
|
+
// others by wording its own verdict (ZIG-1075).
|
|
72
|
+
const chatId = args['chatId'];
|
|
73
|
+
// ZIG-1037: no scope is legal. The throw that used to live here ("Pass
|
|
74
|
+
// chatId or agreementId") is the exact failure this ticket removed — it cost
|
|
75
|
+
// a live agent a turn mid-delivery for naming no container, when the record
|
|
76
|
+
// itself never needed one.
|
|
67
77
|
const visibility = args['visibility'];
|
|
68
78
|
if (visibility !== 'chat' && visibility !== 'agent-private') {
|
|
69
79
|
throw new Error('visibility must be chat or agent-private');
|
|
@@ -71,7 +81,7 @@ export const recordArtifactCapability = {
|
|
|
71
81
|
const contentType = args['content_type'];
|
|
72
82
|
const taskId = args['taskId'];
|
|
73
83
|
const creds = fullCreds(env);
|
|
74
|
-
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId).writeStrict({
|
|
84
|
+
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).writeStrict({
|
|
75
85
|
text: args['text'],
|
|
76
86
|
visibility,
|
|
77
87
|
chatId,
|
|
@@ -91,4 +101,177 @@ export const recordArtifactCapability = {
|
|
|
91
101
|
};
|
|
92
102
|
},
|
|
93
103
|
};
|
|
94
|
-
|
|
104
|
+
/**
|
|
105
|
+
* ZIG-1037 — the artifacts you wrote, across every scope and none.
|
|
106
|
+
*
|
|
107
|
+
* The scope listings (`context_read` via chat / agreement / task) read attach
|
|
108
|
+
* tables, so a free-standing artifact appears in none of them. This is how an
|
|
109
|
+
* agent finds what it recorded without naming a container.
|
|
110
|
+
*/
|
|
111
|
+
export const listArtifactsCapability = {
|
|
112
|
+
key: 'artifact_list',
|
|
113
|
+
names: { sdk: 'artifact_list', mcp: 'ziggs_artifact_list' },
|
|
114
|
+
descriptions: {
|
|
115
|
+
sdk: 'List artifacts you authored, in any scope or none — including free-standing ones no scope read can reach. Forward-delta with `after`.',
|
|
116
|
+
mcp: 'List artifacts YOU authored, across every scope and none. Use this to find something you ' +
|
|
117
|
+
'recorded free-standing (no chat/agreement/task), which the scope reads cannot return. ' +
|
|
118
|
+
'To read an artifact someone shared WITH you, use ziggs_context_read with via=artifact:<id>; ' +
|
|
119
|
+
'to see what has been shared with you, use ziggs_grant_list with scopeKind=artifact.',
|
|
120
|
+
},
|
|
121
|
+
annotation: 'read-only',
|
|
122
|
+
params: {
|
|
123
|
+
after: {
|
|
124
|
+
type: 'string',
|
|
125
|
+
description: 'ISO timestamp — return only artifacts written strictly after this',
|
|
126
|
+
},
|
|
127
|
+
limit: { type: 'number', description: 'Page size (server default when omitted)' },
|
|
128
|
+
},
|
|
129
|
+
needsAgentId: true,
|
|
130
|
+
handler: async (args, env) => {
|
|
131
|
+
const creds = fullCreds(env);
|
|
132
|
+
return new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).list({ authoredBy: 'me' }, {
|
|
133
|
+
after: args['after'],
|
|
134
|
+
limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* ZIG-1037 — hand one artifact you authored to one other agent.
|
|
140
|
+
*
|
|
141
|
+
* Deliberately not part of `context_delegate`: there is no parent grant to
|
|
142
|
+
* attenuate here. An agent holds no grant over its own output — authorship is
|
|
143
|
+
* the authority, and it belongs to the agent's owner, which is why a receiver
|
|
144
|
+
* outside that owner's trust boundary needs their approval. What counts as
|
|
145
|
+
* "inside" is the shared new-party policy, not a local rule: same owner, same
|
|
146
|
+
* org, or an active agent link.
|
|
147
|
+
*/
|
|
148
|
+
export const shareArtifactCapability = {
|
|
149
|
+
key: 'artifact_share',
|
|
150
|
+
names: { sdk: 'artifact_share', mcp: 'ziggs_artifact_share' },
|
|
151
|
+
descriptions: {
|
|
152
|
+
sdk: 'Share an artifact you authored with another agent — just that artifact, no chat or agreement. An agent already connected to you (same owner, same org, or an active link) gets it immediately; anyone else is offered to your owner to approve.',
|
|
153
|
+
mcp: 'Share ONE artifact you authored with another agent, without sharing any chat or agreement ' +
|
|
154
|
+
'it sits in. Only the authoring agent can call this. A receiver already connected to you — ' +
|
|
155
|
+
'same owner, same org, or an active link — is granted immediately. Any other agent is ' +
|
|
156
|
+
'offered to your owner to approve, and the reply is { status: "pending_approval", ' +
|
|
157
|
+
'agreementId } — nothing is shared until they do. Pass chatId to have that approval appear ' +
|
|
158
|
+
'in the chat you are working in. The receiver then reads it with ziggs_context_read ' +
|
|
159
|
+
'via=artifact:<id>.',
|
|
160
|
+
},
|
|
161
|
+
annotation: 'write',
|
|
162
|
+
params: {
|
|
163
|
+
artifactId: {
|
|
164
|
+
type: 'string',
|
|
165
|
+
required: true,
|
|
166
|
+
description: 'The artifact to share — you must be its author',
|
|
167
|
+
},
|
|
168
|
+
holderId: { type: 'string', required: true, description: 'Agent id that receives it' },
|
|
169
|
+
expiresAt: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
description: 'ISO-8601 expiry; omit for the default grant lifetime',
|
|
172
|
+
},
|
|
173
|
+
chatId: {
|
|
174
|
+
type: 'string',
|
|
175
|
+
description: 'Chat to surface the approval request in, when the receiver’s owner has to approve',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
needsAgentId: true,
|
|
179
|
+
handler: async (args, env) => {
|
|
180
|
+
const creds = fullCreds(env);
|
|
181
|
+
const artifactId = args['artifactId'];
|
|
182
|
+
if (!artifactId)
|
|
183
|
+
throw new Error('artifactId is required');
|
|
184
|
+
const holderId = args['holderId'];
|
|
185
|
+
if (!holderId)
|
|
186
|
+
throw new Error('holderId is required');
|
|
187
|
+
const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).shareArtifact(artifactId, {
|
|
188
|
+
holderId,
|
|
189
|
+
expiresAt: args['expiresAt'],
|
|
190
|
+
chatId: args['chatId'],
|
|
191
|
+
});
|
|
192
|
+
if (result.status === 'pending_approval') {
|
|
193
|
+
return {
|
|
194
|
+
ok: true,
|
|
195
|
+
...result,
|
|
196
|
+
note: `Not shared yet — ${result.ownerId ?? 'the owner'} must approve agreement ${result.agreementId}.`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
ok: true,
|
|
201
|
+
...result,
|
|
202
|
+
note: `${holderId} can now read artifact ${artifactId} with via=artifact:${artifactId}.`,
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* ZIG-1037 — attach an artifact you already have to a chat or a task.
|
|
208
|
+
*
|
|
209
|
+
* The other half of "record now, decide where later". Attaching confers nothing
|
|
210
|
+
* by itself: it puts the artifact inside the container, and that container's
|
|
211
|
+
* audience can read it from then on. Use this to publish to a room or bind a
|
|
212
|
+
* deliverable to a task; use artifact_share to hand it to ONE agent instead.
|
|
213
|
+
*/
|
|
214
|
+
export const attachArtifactCapability = {
|
|
215
|
+
key: 'artifact_attach',
|
|
216
|
+
names: { sdk: 'artifact_attach', mcp: 'ziggs_artifact_attach' },
|
|
217
|
+
descriptions: {
|
|
218
|
+
sdk: 'Attach an artifact you can read to a chat or a task. Everyone in that chat / party to that task can read it from then on. Use artifact_share to give it to one specific agent instead.',
|
|
219
|
+
mcp: 'Attach an existing artifact to a chat or a task — the way a free-standing artifact (one you ' +
|
|
220
|
+
'recorded with no scope) reaches an audience. Pass exactly one of chatId or taskId. Everyone ' +
|
|
221
|
+
'in that chat, or party to that task, can read it from then on; attaching gives you nothing ' +
|
|
222
|
+
'new yourself. You must already be able to read the artifact AND belong to the container. ' +
|
|
223
|
+
'To hand it to ONE specific agent without opening a chat, use ziggs_artifact_share instead.',
|
|
224
|
+
},
|
|
225
|
+
annotation: 'write',
|
|
226
|
+
params: {
|
|
227
|
+
artifactId: { type: 'string', required: true, description: 'Artifact to attach' },
|
|
228
|
+
chatId: { type: 'string', description: 'Chat to attach it to' },
|
|
229
|
+
taskId: { type: 'string', description: 'Task to attach it to' },
|
|
230
|
+
role: {
|
|
231
|
+
type: 'string',
|
|
232
|
+
enum: ['input', 'output'],
|
|
233
|
+
description: 'Task attach only: output (a deliverable, default) or input (something the task consumes). ' +
|
|
234
|
+
'A link’s role is set once — re-attaching with a different role is refused, not changed.',
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
needsAgentId: true,
|
|
238
|
+
handler: async (args, env) => {
|
|
239
|
+
const artifactId = args['artifactId'];
|
|
240
|
+
if (!artifactId)
|
|
241
|
+
throw new Error('artifactId is required');
|
|
242
|
+
const chatId = args['chatId'];
|
|
243
|
+
const taskId = args['taskId'];
|
|
244
|
+
if (!!chatId === !!taskId) {
|
|
245
|
+
throw new Error('Pass exactly one of chatId or taskId');
|
|
246
|
+
}
|
|
247
|
+
const role = args['role'] ?? 'output';
|
|
248
|
+
if (role !== 'input' && role !== 'output') {
|
|
249
|
+
throw new Error('role must be input or output');
|
|
250
|
+
}
|
|
251
|
+
const creds = fullCreds(env);
|
|
252
|
+
const client = new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId);
|
|
253
|
+
if (chatId) {
|
|
254
|
+
await client.attachToChat(artifactId, chatId);
|
|
255
|
+
return {
|
|
256
|
+
ok: true,
|
|
257
|
+
artifactId,
|
|
258
|
+
chatId,
|
|
259
|
+
note: `Everyone in chat ${chatId} can now read artifact ${artifactId}.`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
await client.attachToTask(artifactId, taskId, role);
|
|
263
|
+
return {
|
|
264
|
+
ok: true,
|
|
265
|
+
artifactId,
|
|
266
|
+
taskId,
|
|
267
|
+
role,
|
|
268
|
+
note: `Artifact ${artifactId} is attached to task ${taskId} as ${role}; the task's parties can read it.`,
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
export const ARTIFACT_CAPABILITIES = [
|
|
273
|
+
recordArtifactCapability,
|
|
274
|
+
listArtifactsCapability,
|
|
275
|
+
shareArtifactCapability,
|
|
276
|
+
attachArtifactCapability,
|
|
277
|
+
];
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { ContextReadClient } from '../http/ContextReadClient.js';
|
|
1
|
+
import { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, parseVia, viaHint, } from '../http/ContextReadClient.js';
|
|
2
2
|
import { ContextGrantsClient, } from '../http/ContextGrantsClient.js';
|
|
3
3
|
import { ContextDiscoveryClient } from '../http/ContextDiscoveryClient.js';
|
|
4
|
-
import { grantCaveat } from '../http/grants.js';
|
|
4
|
+
import { grantCaveat, CONTEXT_GRANT_SCOPE_KINDS, } from '../http/grants.js';
|
|
5
5
|
import { fetchMyOrgs, resolveOrgSelector } from '../http/OrgsClient.js';
|
|
6
6
|
import { fullCreds } from './types.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'tasks',
|
|
12
|
-
];
|
|
13
|
-
const GRANT_SCOPE_KINDS = ['chat', 'agreement', 'org'];
|
|
7
|
+
// ZIG-1037: `artifact` joined the context rail. Delegating an artifact grant
|
|
8
|
+
// onward works (same kind, same id — an exact re-grant); narrowing a container
|
|
9
|
+
// grant DOWN to an artifact inside it is deliberately not supported yet.
|
|
10
|
+
const GRANT_SCOPE_KINDS = CONTEXT_GRANT_SCOPE_KINDS;
|
|
14
11
|
const CONTEXT_TEMPORALS = ['from-now', 'from-start'];
|
|
15
12
|
/**
|
|
16
13
|
* Human/LLM-readable bounds for a context grant. ZIG-646 folded a context
|
|
@@ -46,12 +43,26 @@ export async function resolveOrgScopeId(env, scopeId) {
|
|
|
46
43
|
const lister = env.surface === 'mcp' ? 'ziggs_org_list' : 'your org list';
|
|
47
44
|
throw new Error(`No org named "${scopeId}" in your memberships — use ${lister} to see them, or pass the org id.`);
|
|
48
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Which `via` each read type accepts, rendered for the tool text. Generated from
|
|
48
|
+
* CONTEXT_READ_VIA so the pairing an agent is told about and the pairing the
|
|
49
|
+
* call actually accepts are one statement — the prose that used to enumerate
|
|
50
|
+
* these by hand had already drifted from the server's list.
|
|
51
|
+
*/
|
|
52
|
+
const VIA_BY_TYPE = CONTEXT_READ_TYPES.map((t) => `${t} via ${viaHint(t)}`).join('; ');
|
|
53
|
+
/**
|
|
54
|
+
* The one fact about `artifact:<id>` that both surfaces must state: it is how you
|
|
55
|
+
* reach an artifact no container can return. Shared for the same reason the
|
|
56
|
+
* pairings above are generated — hand-copied prose is what drifted.
|
|
57
|
+
*/
|
|
58
|
+
const ARTIFACT_VIA_NOTE = 'via=artifact:<id> is a point read of one named artifact and the ONLY way to ' +
|
|
59
|
+
'read one attached to no chat, agreement or task';
|
|
49
60
|
export const contextReadCapability = {
|
|
50
61
|
key: 'context_read',
|
|
51
62
|
names: { sdk: 'context_read', mcp: 'ziggs_context_read' },
|
|
52
63
|
descriptions: {
|
|
53
|
-
sdk:
|
|
54
|
-
mcp:
|
|
64
|
+
sdk: `Read the contents of a scope you already hold a grant for: ${CONTEXT_READ_TYPES.join(' | ')}. Each type reads through its own entries — ${VIA_BY_TYPE}. Use grant_list first to see which scopes your grants cover, then read through any of them. ${ARTIFACT_VIA_NOTE}. Cursored; all access is grant-fenced server-side.`,
|
|
65
|
+
mcp: `Read the contents of a scope you already hold: ${CONTEXT_READ_TYPES.join(' | ')} (the type param). Each type accepts its own via entries — ${VIA_BY_TYPE} — and any other pairing is refused. ${ARTIFACT_VIA_NOTE} (e.g. an artifact someone shared with you; ziggs_grant_list scopeKind=artifact shows those). Forward-delta with after+direction=forward; cursor pagination; contextGrantId pins a grant. The response carries a \`readPlan\` with the next page and/or forward-delta call pre-filled (after=this page's latestSequence), so you can keep reading without rebuilding args. This is the single read path for all four types — to discover which scopes exist, use the listers: ziggs_chat_list, ziggs_task_list, ziggs_agreement_list, ziggs_grant_list, ziggs_link_list.`,
|
|
55
66
|
},
|
|
56
67
|
annotation: 'read-only',
|
|
57
68
|
params: {
|
|
@@ -64,7 +75,7 @@ export const contextReadCapability = {
|
|
|
64
75
|
via: {
|
|
65
76
|
type: 'string',
|
|
66
77
|
required: true,
|
|
67
|
-
description:
|
|
78
|
+
description: `Scope entry you hold, as <kind>:<id>. Accepted per type — ${VIA_BY_TYPE}.`,
|
|
68
79
|
},
|
|
69
80
|
cursor: { type: 'string', description: 'Opaque cursor from a prior nextCursor to page' },
|
|
70
81
|
after: { type: 'string', description: 'ISO timestamp for forward-delta (messages/artifacts)' },
|
|
@@ -90,11 +101,21 @@ export const contextReadCapability = {
|
|
|
90
101
|
const via = args['via'];
|
|
91
102
|
if (!via)
|
|
92
103
|
throw new Error('via is required');
|
|
104
|
+
// Same verdict the server gives, given at the call site so the reason
|
|
105
|
+
// arrives with the mistake. Never a softer one: a client that "fixes" an
|
|
106
|
+
// argument the server would reject is a second answer to one call.
|
|
107
|
+
const parsed = parseVia(via);
|
|
108
|
+
if (!parsed) {
|
|
109
|
+
throw new Error(`via must be <kind>:<id> — one of ${viaHint(type)} for type=${type}`);
|
|
110
|
+
}
|
|
111
|
+
if (!CONTEXT_READ_VIA[type].includes(parsed.kind)) {
|
|
112
|
+
throw new Error(`${type} reads accept via=${viaHint(type)} — not ${parsed.kind}:<id>`);
|
|
113
|
+
}
|
|
93
114
|
const direction = args['direction'];
|
|
94
115
|
if (direction !== undefined && direction !== 'forward') {
|
|
95
116
|
throw new Error('direction must be "forward"');
|
|
96
117
|
}
|
|
97
|
-
return new ContextReadClient(creds.operatorKey, creds.agentId).read(type, {
|
|
118
|
+
return new ContextReadClient(creds.operatorKey, creds.agentId, undefined, creds.laneId).read(type, {
|
|
98
119
|
via,
|
|
99
120
|
cursor: args['cursor'],
|
|
100
121
|
after: args['after'],
|
|
@@ -109,8 +130,8 @@ export const contextExpandReachCapability = {
|
|
|
109
130
|
key: 'context_expand_reach',
|
|
110
131
|
names: { sdk: 'context_expand_reach', mcp: 'ziggs_context_expand_reach' },
|
|
111
132
|
descriptions: {
|
|
112
|
-
sdk: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can read through it. grant_list tells you that you hold e.g. org:acme or agreement:x; this returns the actual { chats, agreements } (ids + labels only, no content) that scope covers — feed an id to context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. Holder-only, grant-fenced.',
|
|
113
|
-
mcp: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can actually read through it. ziggs_grant_list tells you that you hold e.g. org:acme or agreement:x; this returns the { chats, agreements } (ids + labels only, never content) that scope covers — feed an id to ziggs_context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. Holder-only, grant-fenced.',
|
|
133
|
+
sdk: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can read through it. grant_list tells you that you hold e.g. org:acme or agreement:x; this returns the actual { chats, agreements } (ids + labels only, no content) that scope covers — feed an id to context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. An artifact-scope grant returns empty by design — the grant IS one artifact, so read it directly with context_read via=artifact:<the scope id>. Holder-only, grant-fenced.',
|
|
134
|
+
mcp: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can actually read through it. ziggs_grant_list tells you that you hold e.g. org:acme or agreement:x; this returns the { chats, agreements } (ids + labels only, never content) that scope covers — feed an id to ziggs_context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. An artifact-scope grant returns empty by design — that grant IS a single artifact, so do not read the empty map as a dead grant: read the artifact with ziggs_context_read via=artifact:<the scope id>. Holder-only, grant-fenced.',
|
|
114
135
|
},
|
|
115
136
|
annotation: 'read-only',
|
|
116
137
|
params: {
|
|
@@ -179,7 +200,7 @@ export const contextDelegateCapability = {
|
|
|
179
200
|
}
|
|
180
201
|
const scopeKind = args['scopeKind'];
|
|
181
202
|
if (!GRANT_SCOPE_KINDS.includes(scopeKind)) {
|
|
182
|
-
throw new Error(
|
|
203
|
+
throw new Error(`scopeKind must be one of ${GRANT_SCOPE_KINDS.join(', ')}`);
|
|
183
204
|
}
|
|
184
205
|
// ZIG-941 #6 — an org scope may be named rather than pasted as org_… id.
|
|
185
206
|
let scopeId = args['scopeId'];
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { type CapabilityDefinition } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* ZIG-893 — the single "what authority do I hold?" tool. One name, every rail
|
|
4
|
-
* (context chat/agreement/org, connection, wallet), holder-scoped,
|
|
4
|
+
* (context chat/agreement/org/artifact, connection, wallet), holder-scoped,
|
|
5
5
|
* cross-session. `unreadableRails` comes from the backend (ZIG-956) so a short
|
|
6
6
|
* list is never presented as complete when the key can't read a rail.
|
|
7
|
+
*
|
|
8
|
+
* ZIG-1088 — HOLD, not reach. `GET /grants` queries three row collections and
|
|
9
|
+
* nothing else; the implicit arms in AccessService (authorship, chat
|
|
10
|
+
* membership, agreement party, org membership) leave no row behind, so a reader
|
|
11
|
+
* can be entitled to something this list will never mention. The description
|
|
12
|
+
* says so, because the old "the single answer" wording was read as completeness
|
|
13
|
+
* and an empty list as "no access".
|
|
7
14
|
*/
|
|
8
15
|
export declare const listGrantsCapability: CapabilityDefinition;
|
|
9
16
|
export declare const GRANTS_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { GrantsClient } from '../http/GrantsClient.js';
|
|
2
|
+
import { GRANT_SCOPE_KINDS, } from '../http/grants.js';
|
|
2
3
|
import { fullCreds } from './types.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'wallet',
|
|
9
|
-
];
|
|
4
|
+
// BOTH the tool's param enum and its validator, so a missing kind makes the
|
|
5
|
+
// filter the description advertises fail validation. Derived from the canonical
|
|
6
|
+
// list next to the type rather than restated, because that is exactly how
|
|
7
|
+
// `artifact` came to be advertised but not accepted.
|
|
8
|
+
const SCOPE_KINDS = GRANT_SCOPE_KINDS;
|
|
10
9
|
const HEALTHS = ['active', 'expired', 'revoked'];
|
|
11
10
|
function parseScopeKinds(raw) {
|
|
12
11
|
if (raw == null)
|
|
@@ -24,23 +23,30 @@ function parseScopeKinds(raw) {
|
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
25
|
* ZIG-893 — the single "what authority do I hold?" tool. One name, every rail
|
|
27
|
-
* (context chat/agreement/org, connection, wallet), holder-scoped,
|
|
26
|
+
* (context chat/agreement/org/artifact, connection, wallet), holder-scoped,
|
|
28
27
|
* cross-session. `unreadableRails` comes from the backend (ZIG-956) so a short
|
|
29
28
|
* list is never presented as complete when the key can't read a rail.
|
|
29
|
+
*
|
|
30
|
+
* ZIG-1088 — HOLD, not reach. `GET /grants` queries three row collections and
|
|
31
|
+
* nothing else; the implicit arms in AccessService (authorship, chat
|
|
32
|
+
* membership, agreement party, org membership) leave no row behind, so a reader
|
|
33
|
+
* can be entitled to something this list will never mention. The description
|
|
34
|
+
* says so, because the old "the single answer" wording was read as completeness
|
|
35
|
+
* and an empty list as "no access".
|
|
30
36
|
*/
|
|
31
37
|
export const listGrantsCapability = {
|
|
32
38
|
key: 'grant_list',
|
|
33
39
|
names: { sdk: 'grant_list', mcp: 'ziggs_grant_list' },
|
|
34
40
|
descriptions: {
|
|
35
|
-
sdk: 'List every grant this agent holds across all rails in one call — context (chat/agreement/org), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no message/artifact content or credentials).
|
|
36
|
-
mcp: 'List every grant this delegate holds across all rails in one call — context (chat/agreement/org), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials).
|
|
41
|
+
sdk: 'List every grant this agent holds across all rails in one call — context (chat/agreement/org/artifact), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no message/artifact content or credentials). Holder-scoped and cross-session. It answers "what grants do I HOLD?", which is narrower than "what can I reach?": reach you have by authoring something, by sitting in a chat, by being a party to an agreement, or through your org is not a grant row and never appears here — so an empty list means "no grants", never "no access". Filter by scopeKind (rail) and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails, not silently dropped. Cursor-paginated: pass cursor from a prior nextCursor. To answer "what can I reach?" instead, use context_expand_reach to enumerate a scope, or context_read to read through a grant.',
|
|
42
|
+
mcp: 'List every grant this delegate holds across all rails in one call — context (chat/agreement/org/artifact), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no content or credentials). Holder-scoped and cross-session. It answers "what grants do I HOLD?", which is narrower than "what can I reach?": reach you have by authoring something, by sitting in a chat, by being a party to an agreement, or through your org is not a grant row and never appears here — so an empty list means "no grants", never "no access". Filter by scopeKind (rail) and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails, not silently dropped. Cursor-paginated: pass cursor from a prior nextCursor to page. To answer "what can I reach?" instead, use ziggs_context_expand_reach to enumerate a scope, or pass a grantId to ziggs_context_read to pin a specific grant.',
|
|
37
43
|
},
|
|
38
44
|
annotation: 'read-only',
|
|
39
45
|
params: {
|
|
40
46
|
scopeKind: {
|
|
41
47
|
type: 'array',
|
|
42
48
|
items: { type: 'string', enum: SCOPE_KINDS },
|
|
43
|
-
description: 'Rail filter (repeatable): chat | agreement | org (context) | connection | wallet. Omit for every rail you can read.',
|
|
49
|
+
description: 'Rail filter (repeatable): chat | agreement | org | artifact (context) | connection | wallet. Omit for every rail you can read.',
|
|
44
50
|
},
|
|
45
51
|
health: {
|
|
46
52
|
type: 'string',
|
|
@@ -7,5 +7,5 @@ export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
|
|
|
7
7
|
export { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
|
|
8
8
|
export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
|
|
9
9
|
export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
|
|
10
|
-
export { ARTIFACT_CAPABILITIES, recordArtifactCapability } from './artifacts.js';
|
|
10
|
+
export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, } from './artifacts.js';
|
|
11
11
|
export { CHAT_CAPABILITIES, openConversationCapability } from './chat.js';
|
|
@@ -7,5 +7,5 @@ export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
|
|
|
7
7
|
export { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
|
|
8
8
|
export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
|
|
9
9
|
export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
|
|
10
|
-
export { ARTIFACT_CAPABILITIES, recordArtifactCapability } from './artifacts.js';
|
|
10
|
+
export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, } from './artifacts.js';
|
|
11
11
|
export { CHAT_CAPABILITIES, openConversationCapability } from './chat.js';
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
|
|
2
2
|
export function fullCreds(env) {
|
|
3
|
-
const { operatorKey, agentId } = env.creds;
|
|
3
|
+
const { operatorKey, agentId, laneId } = env.creds;
|
|
4
4
|
if (!operatorKey)
|
|
5
5
|
throw new Error('operatorKey missing from tool context');
|
|
6
6
|
if (!agentId)
|
|
7
7
|
throw new Error('agentId missing from tool context');
|
|
8
|
-
|
|
8
|
+
// ZIG-1092 — the lane rides along so every Creds-based client sends
|
|
9
|
+
// X-Ziggs-Lane without each capability having to remember to.
|
|
10
|
+
return { operatorKey, agentId, ...(laneId ? { laneId } : {}) };
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Re-throw a client error with a capability-level prefix, preserving the HTTP
|