@ziggs-ai/api-client 0.39.3 → 0.39.5
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
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.39.5
|
|
6
|
+
|
|
7
|
+
- `message_send` explains its "Nothing authorizes giving … access to this message" refusal: the reader has no contact basis, so ask its owner with `access_request` `scopeKind: "contact"`, then send once they approve.
|
|
8
|
+
|
|
9
|
+
## 0.39.4
|
|
10
|
+
|
|
11
|
+
- `access_request` accepts `scopeKind: "contact"`: ask whoever answers for a person, an agent or a workspace inbox to let you send messages to it. It is one-way: it carries no permission, names at most one holder (yourself or your own owner), reads nothing and gives no reply path. After approval the resume action is a `message_send` to that destination. A send grant is never delegated, so `context_delegate` does not offer the scope.
|
|
12
|
+
|
|
5
13
|
## 0.39.3
|
|
6
14
|
|
|
7
15
|
- Message queries accept `conversationOf` to page messages connected by readable reply relationships. Readers never identify history, and every message still requires current access.
|
|
@@ -10,7 +10,8 @@ import { fullCreds } from './types.js';
|
|
|
10
10
|
// `artifact` joined the context rail. Delegating an artifact grant
|
|
11
11
|
// onward works (same kind, same id — an exact re-grant); narrowing a container
|
|
12
12
|
// grant DOWN to an artifact inside it is deliberately not supported yet.
|
|
13
|
-
|
|
13
|
+
// A send grant (`contact`) is never passed on, so it is not a delegation scope.
|
|
14
|
+
const GRANT_SCOPE_KINDS = CONTEXT_GRANT_SCOPE_KINDS.filter(kind => kind !== 'address' && kind !== 'person' && kind !== 'contact');
|
|
14
15
|
const CONTEXT_TEMPORALS = ['from-now', 'from-start'];
|
|
15
16
|
/**
|
|
16
17
|
* Human/LLM-readable bounds for a context grant. folded a context
|
|
@@ -267,6 +268,9 @@ const OPEN_REF_PARAMS = {
|
|
|
267
268
|
}])),
|
|
268
269
|
};
|
|
269
270
|
function resumeResource(kind, id, env) {
|
|
271
|
+
// After a send grant is approved there is nothing to read: send to the destination.
|
|
272
|
+
if (kind === 'contact')
|
|
273
|
+
return { tool: env.surface === 'mcp' ? 'ziggs_message_send' : 'message_send', args: { command: { audience: { kind: 'explicit', principalIds: [id] }, deliveries: { kind: 'explicit', principalIds: [id] }, effect: { kind: 'none' } } } };
|
|
270
274
|
const tool = kind === 'message' ? 'message_read' : 'open';
|
|
271
275
|
return {
|
|
272
276
|
tool: env.surface === 'mcp' ? `ziggs_${tool}` : tool,
|
|
@@ -405,10 +409,10 @@ export const accessRequestCapability = {
|
|
|
405
409
|
title: 'Request access for yourself or named readers',
|
|
406
410
|
annotation: 'destructive', needsAgentId: true,
|
|
407
411
|
descriptions: {
|
|
408
|
-
sdk: 'Request optional bounded access to one message, artifact, agreement context or task. Creates only a pending approval; never grants access or approves. Choose recipientId when several approvers are offered; with one, it is selected automatically: your work brief offers them for this wake — workBrief.actingFor.customer.id (the customer of the hire you are working in, for their own accounts and resources) or workBrief.actingFor.operator.id (your operator, for tools you bring). Anyone else is refused, and the refusal lists who you can ask. Artifact requests go to the selected approver inbox; no conversation is needed. To create work under an agreement you can read but not work on (a task create refused with WORK_ACCESS_REQUIRED), request permission=work on it; only someone who can work on it may approve, and it lasts until the agreement ends. Use scopeId for a known artifact; or scopeKind=artifact with agreementId of your active hire to request its system-maintained workspace summary (workspace name/type and agents, including future updates, not private conversations or document bodies). Send the returned appUrl, wait for human approval, then follow the returned resume action. To arrange reading of one artifact for others, name them in holders (agents by id, people or workspaces by their authorized principal IDs); include yourself only if you need to read it too. The owner approves that exact list once and each reader gets its own grant; readers who can already read it come back as already_readable. For releaseSource, explicitly name its authority in recipientId; approval releases only the exact output. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
|
|
412
|
+
sdk: 'Request optional bounded access to one message, artifact, agreement context or task, or ask to send messages to a destination (scopeKind=contact). Creates only a pending approval; never grants access or approves. Choose recipientId when several approvers are offered; with one, it is selected automatically: your work brief offers them for this wake — workBrief.actingFor.customer.id (the customer of the hire you are working in, for their own accounts and resources) or workBrief.actingFor.operator.id (your operator, for tools you bring). Anyone else is refused, and the refusal lists who you can ask. Artifact requests go to the selected approver inbox; no conversation is needed. To create work under an agreement you can read but not work on (a task create refused with WORK_ACCESS_REQUIRED), request permission=work on it; only someone who can work on it may approve, and it lasts until the agreement ends. Use scopeId for a known artifact; or scopeKind=artifact with agreementId of your active hire to request its system-maintained workspace summary (workspace name/type and agents, including future updates, not private conversations or document bodies). Send the returned appUrl, wait for human approval, then follow the returned resume action. To arrange reading of one artifact for others, name them in holders (agents by id, people or workspaces by their authorized principal IDs); include yourself only if you need to read it too. The owner approves that exact list once and each reader gets its own grant; readers who can already read it come back as already_readable. For releaseSource, explicitly name its authority in recipientId; approval releases only the exact output. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
|
|
409
413
|
},
|
|
410
414
|
params: {
|
|
411
|
-
scopeKind: { type: 'string', required: true, enum: ['message', 'artifact', 'agreement', 'task'] },
|
|
415
|
+
scopeKind: { type: 'string', required: true, enum: ['message', 'artifact', 'agreement', 'task', 'contact'], description: 'contact: ask to send messages to scopeId (a person, an agent or a workspace inbox). One-way; it reads nothing and gives no reply path.' },
|
|
412
416
|
scopeId: { type: 'string', description: 'Original resource id. Omit when using a hire agreementId to request its workspace summary.' },
|
|
413
417
|
agreementId: { type: 'string', description: 'Artifact scope only: current hire id, used instead of scopeId to request the system-maintained workspace summary.' },
|
|
414
418
|
reason: { type: 'string', required: true, description: 'Why access is needed and who will receive the reply or summary' },
|
|
@@ -447,8 +451,9 @@ export const accessRequestCapability = {
|
|
|
447
451
|
throw new Error(`${key} is required`);
|
|
448
452
|
}
|
|
449
453
|
const scopeKind = args.scopeKind;
|
|
450
|
-
if (!['message', 'artifact', 'agreement', 'task'].includes(scopeKind))
|
|
454
|
+
if (!['message', 'artifact', 'agreement', 'task', 'contact'].includes(scopeKind))
|
|
451
455
|
throw new Error('Unsupported scopeKind');
|
|
456
|
+
const contact = scopeKind === 'contact';
|
|
452
457
|
const agreementId = typeof args.agreementId === 'string' ? args.agreementId.trim() : undefined;
|
|
453
458
|
const scopeId = typeof args.scopeId === 'string' ? args.scopeId.trim() : undefined;
|
|
454
459
|
if (Boolean(agreementId) === Boolean(scopeId) || (agreementId && scopeKind !== 'artifact'))
|
|
@@ -458,13 +463,17 @@ export const accessRequestCapability = {
|
|
|
458
463
|
throw new Error('permission must be read or work');
|
|
459
464
|
if (permission === 'work' && scopeKind !== 'agreement')
|
|
460
465
|
throw new Error('Only agreement access may request work permission');
|
|
466
|
+
if (contact && args.permission !== undefined && permission !== 'read')
|
|
467
|
+
throw new Error('A contact request asks to send only; omit permission');
|
|
461
468
|
if (args.temporal !== undefined && args.temporal !== 'from-start')
|
|
462
469
|
throw new Error('Retained resources use from-start access');
|
|
463
470
|
const durationHours = args.durationHours ?? 24;
|
|
464
471
|
if (typeof durationHours !== 'number' || !Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
|
|
465
472
|
throw new Error('durationHours must be an integer between 1 and 168');
|
|
466
473
|
const holders = args.holders === undefined ? undefined : readersOf(args.holders);
|
|
467
|
-
if (holders &&
|
|
474
|
+
if (holders && contact && holders.length !== 1)
|
|
475
|
+
throw new Error('A contact request names one holder: yourself or your own owner');
|
|
476
|
+
if (holders && !contact && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
|
|
468
477
|
throw new Error('holders is for one artifact named by scopeId, to read');
|
|
469
478
|
let contributionTo;
|
|
470
479
|
if (args.contributionTo !== undefined) {
|
|
@@ -483,7 +492,8 @@ export const accessRequestCapability = {
|
|
|
483
492
|
const creds = fullCreds(env);
|
|
484
493
|
const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
|
|
485
494
|
scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
|
|
486
|
-
permission:
|
|
495
|
+
// A contact request carries no permission: the server makes it send-only.
|
|
496
|
+
...(contact ? {} : { permission: permission }), temporal: args.temporal,
|
|
487
497
|
durationHours,
|
|
488
498
|
recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
|
|
489
499
|
...(holders ? { holders } : {}),
|
|
@@ -2,7 +2,7 @@ import { MessageRecordsClient } from '../http/MessageRecordsClient.js';
|
|
|
2
2
|
import { fullCreds } from './types.js';
|
|
3
3
|
const client = (env) => { const creds = fullCreds(env); return new MessageRecordsClient(creds.operatorKey, creds.agentId, env.baseUrl); };
|
|
4
4
|
export const readMessageCapability = { key: 'message_read', names: { sdk: 'message_read', mcp: 'ziggs_message_read' }, title: 'Read exact message', descriptions: { sdk: 'Read one authorized message. Returns content, independent readers/recipients, requested-answer action slots and authorized relationships. Does not acknowledge delivery.' }, annotation: 'read-only', needsAgentId: true, params: { messageId: { type: 'string', required: true } }, handler: (args, env) => client(env).get(String(args.messageId)) };
|
|
5
|
-
export const sendMessageCapability = { key: 'message_send', names: { sdk: 'message_send', mcp: 'ziggs_message_send' }, title: 'Send message', descriptions: { sdk: 'Send the canonical command: operationId; content:{text}; audience:{kind:"explicit",principalIds:[...]}; deliveries:{kind:"explicit",principalIds:[...]}; effect:{kind:"none"} or request-answers:{requests:[{requestId,responsiblePrincipalId}]} or answer:{action:{kind:"message",id,slot:"answer",requestId},artifacts:[]} or provide-input:{action}. Optional mandate:{agreementId,taskId}, relationships:{replyToMessageId,taskIds,artifacts}, exact sources. Reuse operationId unchanged on retry. A causal reply alone never resolves an answer. No room required.' }, annotation: 'destructive', needsAgentId: true, params: { command: { type: 'object', required: true, description: 'MessageCommand with explicit independent audience, deliveries, action effect and optional mandate.' } }, handler: (args, env) => client(env).send(args.command) };
|
|
5
|
+
export const sendMessageCapability = { key: 'message_send', names: { sdk: 'message_send', mcp: 'ziggs_message_send' }, title: 'Send message', descriptions: { sdk: 'Send the canonical command: operationId; content:{text}; audience:{kind:"explicit",principalIds:[...]}; deliveries:{kind:"explicit",principalIds:[...]}; effect:{kind:"none"} or request-answers:{requests:[{requestId,responsiblePrincipalId}]} or answer:{action:{kind:"message",id,slot:"answer",requestId},artifacts:[]} or provide-input:{action}. Optional mandate:{agreementId,taskId}, relationships:{replyToMessageId,taskIds,artifacts}, exact sources. Reuse operationId unchanged on retry. A causal reply alone never resolves an answer. No room required. Refused with "Nothing authorizes giving … access to this message" means you have no contact basis for that reader (membership, a link, an agreement or a listing): ask its owner with access_request scopeKind=contact and scopeId=that reader, then send once they approve.' }, annotation: 'destructive', needsAgentId: true, params: { command: { type: 'object', required: true, description: 'MessageCommand with explicit independent audience, deliveries, action effect and optional mandate.' } }, handler: (args, env) => client(env).send(args.command) };
|
|
6
6
|
export const readRepliesCapability = { key: 'message_replies', names: { sdk: 'message_replies', mcp: 'ziggs_message_replies' }, title: 'Read replies', descriptions: { sdk: 'Read currently authorized causal replies; links never grant access. Continue using nextCursor as before.' }, annotation: 'read-only', needsAgentId: true, params: { messageId: { type: 'string', required: true }, before: { type: 'string' }, limit: { type: 'number' } }, handler: (args, env) => client(env).replies(String(args.messageId), { before: args.before, limit: args.limit }) };
|
|
7
7
|
export const queryMessagesCapability = { key: 'message_list', names: { sdk: 'message_list', mcp: 'ziggs_message_list' }, title: 'Query messages', descriptions: { sdk: 'Query authorized messages. Use conversationOf with an exact message id to browse messages connected by readable reply relationships; use nextCursor as before for earlier turns. Select conversationOf independently of agreementId, taskId or participantIds. History never selects authority.' }, annotation: 'read-only', needsAgentId: true, params: { conversationOf: { type: 'string', description: 'Authorized anchor message; follows readable causal links, never a reader roster. Each result still requires read access and any execution input authorization.' }, q: { type: 'string' }, senderId: { type: 'string' }, recipientIds: { type: 'array', items: { type: 'string' } }, agreementId: { type: 'string' }, taskId: { type: 'string' }, participantIds: { type: 'array', items: { type: 'string' } }, before: { type: 'string' }, limit: { type: 'number' } }, handler: (args, env) => client(env).list(args) };
|
|
8
8
|
export const messageControlsCapability = { key: 'message_controls', names: { sdk: 'message_controls', mcp: 'ziggs_message_controls' }, title: 'Inspect message mandate', descriptions: { sdk: 'Discover recipients and current authority under an explicitly selected agreement/task without reading message history.' }, annotation: 'read-only', needsAgentId: true, params: { q: { type: 'string' }, senderId: { type: 'string' }, recipientIds: { type: 'array', items: { type: 'string' } }, agreementId: { type: 'string', required: true }, taskId: { type: 'string' } }, handler: (args, env) => client(env).controls(String(args.agreementId), args.taskId) };
|
|
@@ -96,7 +96,7 @@ export declare class ContextGrantsClient {
|
|
|
96
96
|
issueGrant(input: IssueContextGrantInput): Promise<GrantView>;
|
|
97
97
|
/** Request-only endpoint: a successful call must still have granted no access. */
|
|
98
98
|
requestAccess(input: {
|
|
99
|
-
scopeKind: 'message' | 'artifact' | 'address' | 'agreement' | 'task' | 'person';
|
|
99
|
+
scopeKind: 'message' | 'artifact' | 'address' | 'agreement' | 'task' | 'person' | 'contact';
|
|
100
100
|
scopeId?: string;
|
|
101
101
|
agreementId?: string;
|
|
102
102
|
reason: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/api-client",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.5",
|
|
4
4
|
"description": "HTTP and WebSocket client for the Ziggs backend API",
|
|
5
5
|
"homepage": "https://ziggsai.com/docs",
|
|
6
6
|
"bugs": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"test:watch": "node --import tsx/esm --test --watch test/*.test.ts"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ziggs-ai/contracts": "0.
|
|
34
|
+
"@ziggs-ai/contracts": "0.45.0",
|
|
35
35
|
"socket.io-client": "^4.7.0"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|