@ziggs-ai/api-client 0.37.0 → 0.37.4
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/chat.d.ts +2 -0
- package/dist/capabilities/chat.js +11 -8
- package/dist/capabilities/context.js +45 -2
- package/dist/capabilities/mcpBriefs.js +2 -2
- package/dist/http/ContextGrantsClient.d.ts +28 -10
- package/dist/http/ContextGrantsClient.js +13 -1
- package/dist/http/grants.d.ts +2 -10
- package/dist/http/grants.js +2 -26
- package/dist/inboxAck.d.ts +0 -1
- package/dist/inboxAck.js +0 -4
- package/dist/types.d.ts +6 -35
- package/dist/types.js +19 -53
- package/package.json +2 -2
|
@@ -5,6 +5,8 @@ export interface SendPresentation {
|
|
|
5
5
|
ok: true;
|
|
6
6
|
chatId: string;
|
|
7
7
|
messageId: string;
|
|
8
|
+
/** Keep the server's delivery guidance on every client surface. */
|
|
9
|
+
message?: string;
|
|
8
10
|
/** What the server did with this send, stated so it cannot be over-read. */
|
|
9
11
|
delivered: string;
|
|
10
12
|
/** Who it was addressed to, exactly as the server resolved it. */
|
|
@@ -24,6 +24,7 @@ export function presentSendResult(result, env) {
|
|
|
24
24
|
ok: true,
|
|
25
25
|
chatId: result.chatId,
|
|
26
26
|
messageId: result.messageId,
|
|
27
|
+
...(result.message ? { message: result.message } : {}),
|
|
27
28
|
...(workContext ? { workContext } : {}),
|
|
28
29
|
};
|
|
29
30
|
if (!delivery) {
|
|
@@ -38,9 +39,11 @@ export function presentSendResult(result, env) {
|
|
|
38
39
|
// What counted it, in the server's words: a stranger's free messages with a
|
|
39
40
|
// listed agent run out, and the person writing needs to know before they do.
|
|
40
41
|
const counted = result.coverage?.message ? ` ${result.coverage.message}` : '';
|
|
41
|
-
const routing = delivery.
|
|
42
|
-
?
|
|
43
|
-
:
|
|
42
|
+
const routing = delivery.broadcast
|
|
43
|
+
? "This human-only broadcast does not wake assistants or stand-ins. If you need a reply, send a new message in this same chat to the person's returned participant id (receiverId), with a fresh message id or idempotency key. Existing conversation permissions still apply."
|
|
44
|
+
: delivery.reachable
|
|
45
|
+
? `Assigned to ${delivery.assignee ?? 'nobody'}; reachable: ${delivery.reachable}${delivery.reason ? ` (${delivery.reason})` : ''}.${delivery.lastRead ? ` Last inbox read: ${delivery.lastRead}.` : ''}`
|
|
46
|
+
: 'Mailbox routing is unconfirmed; a person may be covered by an assistant.';
|
|
44
47
|
return {
|
|
45
48
|
...base,
|
|
46
49
|
delivered: `Message ${result.messageId} was accepted into chat ${result.chatId}, addressed to ${destination}. ${routing} Accepted is not read and does not confirm a wake or reply.${counted}`,
|
|
@@ -60,7 +63,7 @@ export const openConversationCapability = {
|
|
|
60
63
|
names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
|
|
61
64
|
title: 'Start or reuse a conversation',
|
|
62
65
|
descriptions: {
|
|
63
|
-
sdk: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses
|
|
66
|
+
sdk: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses your person’s conversation with one person or agent, the same room each of their agents finds; newChat opens a separate subject, which can take a name. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. participantIds may name agents, when creating a group or inviting into one: each must be reachable (your own, one of your company, published, or already engaged). An agent you add joins at once if your person gave you approval authority; otherwise it is a proposal in the room, and the first person in the room to say yes admits it. The result lists each id as added, already-here or pending. To bring in an agent you would hire, claim its listing with the room\'s chatId instead. Your own missing seat is asked for with access_request. Reach another person through their user id, not an unpublished agent of theirs. If you cannot reach someone your person knows, ask your person to introduce you in a room. Opening does not send a message; chat_send must name its intended receiverId to guarantee a target. To list accessible rooms, use grant_list with scopeKind ["chat"].',
|
|
64
67
|
},
|
|
65
68
|
annotation: 'write',
|
|
66
69
|
params: {
|
|
@@ -71,8 +74,8 @@ export const openConversationCapability = {
|
|
|
71
74
|
participantIds: { type: 'array', items: { type: 'string' }, description: '1–20 known ids. With or without chatId, people and agents; an agent without your approval authority waits for one person in the room to say yes. Use instead of participantId. Each invitation is authorized separately.' },
|
|
72
75
|
chatId: { type: 'string', description: 'Existing room to invite participantIds into. Never creates another room. Omit creation options and agreementId.' },
|
|
73
76
|
idempotencyKey: { type: 'string', description: 'Required for group creation: a unique key for this conversation, reused unchanged on retries. A different participant set needs a new key.' },
|
|
74
|
-
name: { type: 'string', description: 'Name for a new group, 1–80 characters. Pair rooms and team channels take no name. A retry of the same idempotencyKey keeps the first name. Rename later is PATCH /chats/:chatId by someone who can admit people.' },
|
|
75
|
-
createIfMissing: { type: 'boolean', description: 'Set false to reuse only an existing room
|
|
77
|
+
name: { type: 'string', description: 'Name for a new group or a newChat room, 1–80 characters. Pair rooms and team channels take no name. A retry of the same idempotencyKey keeps the first name. Rename later is PATCH /chats/:chatId by someone who can admit people.' },
|
|
78
|
+
createIfMissing: { type: 'boolean', description: 'Set false to reuse only an existing room with this participant. A missing room is refused without creating a replacement.' },
|
|
76
79
|
newChat: {
|
|
77
80
|
type: 'boolean',
|
|
78
81
|
description: 'Open a separate chat even though one is already open with the named participants. ' +
|
|
@@ -105,8 +108,8 @@ export const openConversationCapability = {
|
|
|
105
108
|
else if (args.chatId || args.idempotencyKey) {
|
|
106
109
|
throw new Error('chatId and idempotencyKey require participantIds');
|
|
107
110
|
}
|
|
108
|
-
if (typeof args.name === 'string' && args.name.trim() && !Array.isArray(args.participantIds)) {
|
|
109
|
-
throw new Error('A name is for a new group. Pair rooms take no name; the person is the name.');
|
|
111
|
+
if (typeof args.name === 'string' && args.name.trim() && !Array.isArray(args.participantIds) && args.newChat !== true) {
|
|
112
|
+
throw new Error('A name is for a new group or a newChat room. Pair rooms take no name; the person is the name.');
|
|
110
113
|
}
|
|
111
114
|
const { chatId, reused, invited, name } = await openConversation({
|
|
112
115
|
...(typeof args.participantId === 'string' ? { participantId: args.participantId } : {}),
|
|
@@ -430,13 +430,32 @@ function requestAccessAction(args, env) {
|
|
|
430
430
|
requiresApproval: true,
|
|
431
431
|
};
|
|
432
432
|
}
|
|
433
|
+
/** The readers an access request names: kinds stated, ids distinct, at most 20. */
|
|
434
|
+
function readersOf(value) {
|
|
435
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 20)
|
|
436
|
+
throw new Error('holders must list 1 to 20 readers');
|
|
437
|
+
const seen = new Set();
|
|
438
|
+
return value.map((entry, i) => {
|
|
439
|
+
const kind = entry?.kind;
|
|
440
|
+
const id = entry?.id;
|
|
441
|
+
if (kind !== 'agent' && kind !== 'user' && kind !== 'org')
|
|
442
|
+
throw new Error(`holders[${i}].kind must be agent, user or org`);
|
|
443
|
+
if (typeof id !== 'string' || !id.trim())
|
|
444
|
+
throw new Error(`holders[${i}].id is required`);
|
|
445
|
+
const key = `${kind}:${id.trim()}`;
|
|
446
|
+
if (seen.has(key))
|
|
447
|
+
throw new Error(`holders names the same reader twice (holders[${i}])`);
|
|
448
|
+
seen.add(key);
|
|
449
|
+
return { kind, id: id.trim() };
|
|
450
|
+
});
|
|
451
|
+
}
|
|
433
452
|
export const accessRequestCapability = {
|
|
434
453
|
key: 'access_request',
|
|
435
454
|
names: { sdk: 'access_request', mcp: 'ziggs_access_request' },
|
|
436
|
-
title: 'Request access for yourself',
|
|
455
|
+
title: 'Request access for yourself or named readers',
|
|
437
456
|
annotation: 'destructive', needsAgentId: true,
|
|
438
457
|
descriptions: {
|
|
439
|
-
sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves. Name who to ask in recipientId: 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 also need chatId, the conversation they approve in. 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 open the returned scope. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
|
|
458
|
+
sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves. Name who to ask in recipientId: 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 also need chatId, the conversation they approve in. 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 open the returned scope. To arrange reading of one artifact for others, name them in holders (agents by id, people or workspaces by id or the participant reference this room shows you); 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. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
|
|
440
459
|
},
|
|
441
460
|
params: {
|
|
442
461
|
scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
|
|
@@ -451,6 +470,18 @@ export const accessRequestCapability = {
|
|
|
451
470
|
// comes back refused with the people this agent may ask. Required in the
|
|
452
471
|
// schema, a host refused the call before that list could arrive.
|
|
453
472
|
recipientId: { type: 'string', description: 'Who to ask: workBrief.actingFor.customer.id (the customer of this hire) or workBrief.actingFor.operator.id (your operator). Without it, the refusal lists who you can ask.' },
|
|
473
|
+
holders: {
|
|
474
|
+
type: 'array',
|
|
475
|
+
description: 'Artifact only, read only: up to 20 readers, each { kind: "agent" | "user" | "org", id }. Leave yourself out to arrange it for others. Omit to ask for yourself.',
|
|
476
|
+
items: {
|
|
477
|
+
type: 'object',
|
|
478
|
+
properties: {
|
|
479
|
+
kind: { type: 'string', enum: ['agent', 'user', 'org'] },
|
|
480
|
+
id: { type: 'string' },
|
|
481
|
+
},
|
|
482
|
+
required: ['kind', 'id'],
|
|
483
|
+
},
|
|
484
|
+
},
|
|
454
485
|
idempotencyKey: { type: 'string', description: 'Reuse for the same logical request' },
|
|
455
486
|
},
|
|
456
487
|
handler: async (args, env) => {
|
|
@@ -477,14 +508,26 @@ export const accessRequestCapability = {
|
|
|
477
508
|
const durationHours = args.durationHours ?? 24;
|
|
478
509
|
if (typeof durationHours !== 'number' || !Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
|
|
479
510
|
throw new Error('durationHours must be an integer between 1 and 168');
|
|
511
|
+
const holders = args.holders === undefined ? undefined : readersOf(args.holders);
|
|
512
|
+
if (holders && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
|
|
513
|
+
throw new Error('holders is for one artifact named by scopeId, to read');
|
|
480
514
|
const creds = fullCreds(env);
|
|
481
515
|
const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
|
|
482
516
|
scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
|
|
483
517
|
permission: permission, temporal: args.temporal,
|
|
484
518
|
durationHours, chatId: args.chatId,
|
|
485
519
|
recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
|
|
520
|
+
...(holders ? { holders } : {}),
|
|
486
521
|
}, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
|
|
487
522
|
const actingAgentId = result.actingAgentId ?? (creds.agentId || undefined);
|
|
523
|
+
if (result.status === 'already_readable') {
|
|
524
|
+
return {
|
|
525
|
+
...result, outcome: 'already_readable', state: 'not_requested',
|
|
526
|
+
...(actingAgentId ? { actingAgentId } : {}),
|
|
527
|
+
scope: result.scope ?? { kind: scopeKind, id: scopeId },
|
|
528
|
+
note: result.message ?? 'Every reader you named can already read it. Nothing was asked.',
|
|
529
|
+
};
|
|
530
|
+
}
|
|
488
531
|
return {
|
|
489
532
|
...result, outcome: 'pending', state: 'requested',
|
|
490
533
|
...(actingAgentId ? { actingAgentId } : {}),
|
|
@@ -21,11 +21,11 @@ const BRIEFS = {
|
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
23
|
ziggs_inbox: {
|
|
24
|
-
does: 'Reads your inbox: deliveries
|
|
24
|
+
does: 'Reads your inbox: deliveries by recipient, tasks, decisions, sessionChatCard; waitSeconds holds for news. Observes only: no claim or ack.',
|
|
25
25
|
returns: 'a readPlan of next calls; backlog if more is unread',
|
|
26
26
|
next: 'ziggs_inbox_claim to handle them',
|
|
27
27
|
compact: {
|
|
28
|
-
does: 'Reads your inbox: deliveries
|
|
28
|
+
does: 'Reads your inbox: deliveries by recipient, tasks, decisions, sessionChatCard; waitSeconds holds for news.',
|
|
29
29
|
changes: 'takes or renews this host’s lease',
|
|
30
30
|
next: 'ziggs_inbox_ack after handling',
|
|
31
31
|
},
|
|
@@ -109,19 +109,12 @@ export declare class ContextGrantsClient {
|
|
|
109
109
|
ownerId?: string;
|
|
110
110
|
/** Who to ask, from the people the server offers this wake. */
|
|
111
111
|
recipientId?: string;
|
|
112
|
+
/** Artifact only: who should read it, if not only the caller. One grant each. */
|
|
113
|
+
holders?: AccessReader[];
|
|
112
114
|
}, opts?: {
|
|
113
115
|
idempotencyKey?: string;
|
|
114
116
|
laneId?: string;
|
|
115
|
-
}): Promise<
|
|
116
|
-
status: 'pending_approval';
|
|
117
|
-
agreementId: string;
|
|
118
|
-
accessGranted: false;
|
|
119
|
-
actingAgentId?: string;
|
|
120
|
-
scope?: {
|
|
121
|
-
kind: string;
|
|
122
|
-
id: string;
|
|
123
|
-
};
|
|
124
|
-
} & RequestSentTo>;
|
|
117
|
+
}): Promise<AccessRequestResult>;
|
|
125
118
|
requestArtifactAccess(artifactId: string, input: {
|
|
126
119
|
chatId: string;
|
|
127
120
|
ownerId: string;
|
|
@@ -167,6 +160,31 @@ export declare class ContextGrantsClient {
|
|
|
167
160
|
}>;
|
|
168
161
|
}
|
|
169
162
|
/** Who got a request card and where they answer it, as the server says. */
|
|
163
|
+
/** One reader an artifact access request asks for, named with its kind. */
|
|
164
|
+
export interface AccessReader {
|
|
165
|
+
kind: 'agent' | 'user' | 'org';
|
|
166
|
+
id: string;
|
|
167
|
+
}
|
|
168
|
+
/** What a request did for one named reader. */
|
|
169
|
+
export interface AccessReaderOutcome extends AccessReader {
|
|
170
|
+
outcome: 'already_readable' | 'awaiting_approval';
|
|
171
|
+
}
|
|
172
|
+
export type AccessRequestResult = {
|
|
173
|
+
accessGranted: false;
|
|
174
|
+
actingAgentId?: string;
|
|
175
|
+
scope?: {
|
|
176
|
+
kind: string;
|
|
177
|
+
id: string;
|
|
178
|
+
};
|
|
179
|
+
/** Per named reader, when the request named readers. */
|
|
180
|
+
readers?: AccessReaderOutcome[];
|
|
181
|
+
} & RequestSentTo & ({
|
|
182
|
+
status: 'pending_approval';
|
|
183
|
+
agreementId: string;
|
|
184
|
+
} | {
|
|
185
|
+
status: 'already_readable';
|
|
186
|
+
agreementId: null;
|
|
187
|
+
});
|
|
170
188
|
export interface RequestSentTo {
|
|
171
189
|
sentTo?: {
|
|
172
190
|
id: string;
|
|
@@ -77,10 +77,22 @@ export class ContextGrantsClient {
|
|
|
77
77
|
if (!res.ok)
|
|
78
78
|
throwApiError(res, body, `requestAccess failed: ${res.status}`);
|
|
79
79
|
const result = JSON.parse(body);
|
|
80
|
+
const readers = Array.isArray(result.readers) ? { readers: result.readers } : {};
|
|
81
|
+
const common = {
|
|
82
|
+
accessGranted: false,
|
|
83
|
+
...(typeof result.actingAgentId === 'string' && result.actingAgentId ? { actingAgentId: result.actingAgentId } : {}),
|
|
84
|
+
...(result.scope ? { scope: result.scope } : {}),
|
|
85
|
+
...readers,
|
|
86
|
+
...sentToOf(result),
|
|
87
|
+
};
|
|
88
|
+
// Every named reader already reads it: nothing was asked.
|
|
89
|
+
if (result.status === 'already_readable' && result.accessGranted === false) {
|
|
90
|
+
return { status: 'already_readable', agreementId: null, ...common };
|
|
91
|
+
}
|
|
80
92
|
if (result.status !== 'pending_approval' || !result.agreementId || result.accessGranted !== false) {
|
|
81
93
|
throw new Error('Expected a pending access request with accessGranted=false');
|
|
82
94
|
}
|
|
83
|
-
return { status: 'pending_approval', agreementId: result.agreementId,
|
|
95
|
+
return { status: 'pending_approval', agreementId: result.agreementId, ...common };
|
|
84
96
|
}
|
|
85
97
|
async requestArtifactAccess(artifactId, input, opts) {
|
|
86
98
|
const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
|
package/dist/http/grants.d.ts
CHANGED
|
@@ -36,16 +36,8 @@ export type GrantBasisKind = 'creation' | 'publication' | 'membership' | 'accept
|
|
|
36
36
|
* stopped showing a whole scope kind after `artifact` was added; anything that
|
|
37
37
|
* means "every context scope" should read this.
|
|
38
38
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* Every scope kind across every rail: context + connection + payment + consent
|
|
42
|
-
* + carrier. Mirrors the server's `GRANT_SCOPE_KINDS` (grants/grant-view.ts),
|
|
43
|
-
* which is the list `GET /grants?scopeKind=` validates against — a kind missing
|
|
44
|
-
* here cannot be asked for at all, which is the failure the comment above
|
|
45
|
-
* describes.
|
|
46
|
-
*/
|
|
47
|
-
export declare const GRANT_SCOPE_KINDS: readonly ["chat", "agreement", "org", "artifact", "task", "connection", "wallet", "consent", "inbox"];
|
|
48
|
-
export type GrantScopeKind = (typeof GRANT_SCOPE_KINDS)[number];
|
|
39
|
+
import { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, type ContextGrantScopeKind, type GrantScopeKind } from '@ziggs-ai/contracts';
|
|
40
|
+
export { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, type ContextGrantScopeKind, type GrantScopeKind, };
|
|
49
41
|
/**
|
|
50
42
|
* The scope a grant is over, and one caveat on it — the wire's shapes.
|
|
51
43
|
*
|
package/dist/http/grants.js
CHANGED
|
@@ -6,32 +6,8 @@
|
|
|
6
6
|
* stopped showing a whole scope kind after `artifact` was added; anything that
|
|
7
7
|
* means "every context scope" should read this.
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'agreement',
|
|
12
|
-
'org',
|
|
13
|
-
// narrowest context scope
|
|
14
|
-
'artifact',
|
|
15
|
-
// a branch of a work graph: the named task and everything under it, now and
|
|
16
|
-
// in future. Holding one is what being in a job means.
|
|
17
|
-
'task',
|
|
18
|
-
];
|
|
19
|
-
/**
|
|
20
|
-
* Every scope kind across every rail: context + connection + payment + consent
|
|
21
|
-
* + carrier. Mirrors the server's `GRANT_SCOPE_KINDS` (grants/grant-view.ts),
|
|
22
|
-
* which is the list `GET /grants?scopeKind=` validates against — a kind missing
|
|
23
|
-
* here cannot be asked for at all, which is the failure the comment above
|
|
24
|
-
* describes.
|
|
25
|
-
*/
|
|
26
|
-
export const GRANT_SCOPE_KINDS = [
|
|
27
|
-
...CONTEXT_GRANT_SCOPE_KINDS,
|
|
28
|
-
'connection',
|
|
29
|
-
'wallet',
|
|
30
|
-
// approval authority — who may answer a slot
|
|
31
|
-
'consent',
|
|
32
|
-
// carrier grants — who reads whose mailbox
|
|
33
|
-
'inbox',
|
|
34
|
-
];
|
|
9
|
+
import { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, } from '@ziggs-ai/contracts';
|
|
10
|
+
export { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, };
|
|
35
11
|
/** Value of the first caveat of `type` on a grant, or undefined. */
|
|
36
12
|
export function grantCaveat(grant, type) {
|
|
37
13
|
return grant.caveats.find((c) => c.type === type)?.value;
|
package/dist/inboxAck.d.ts
CHANGED
package/dist/inboxAck.js
CHANGED
|
@@ -31,8 +31,6 @@ export function inboxAckHandledIds(envelope, ownAgentId, ownAgentIds) {
|
|
|
31
31
|
export function inboxAckAllowed(envelope, opts = {}) {
|
|
32
32
|
if (!envelope.ackTo)
|
|
33
33
|
return false;
|
|
34
|
-
if ((envelope.truncatedRequests ?? 0) !== 0)
|
|
35
|
-
return false;
|
|
36
34
|
const failures = opts.failures ?? 0;
|
|
37
35
|
if (failures > 0 && !opts.forceAck)
|
|
38
36
|
return false;
|
|
@@ -80,8 +78,6 @@ function gatingRows(envelope, ownAgentId, ownAgentIds) {
|
|
|
80
78
|
* backend is handled: the caller keeps its single end-of-pass ack.
|
|
81
79
|
*/
|
|
82
80
|
export function planPartialInboxAck(envelope, handled, opts = {}) {
|
|
83
|
-
if ((envelope.truncatedRequests ?? 0) !== 0)
|
|
84
|
-
return null;
|
|
85
81
|
const failures = opts.failures ?? 0;
|
|
86
82
|
if (failures > 0 && !opts.forceAck)
|
|
87
83
|
return null;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { InboxRequestChannel } from '@ziggs-ai/contracts';
|
|
3
|
-
import type { InboxPartyKind } from '@ziggs-ai/contracts';
|
|
1
|
+
import { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, type AgreementPartySide, type AgreementParties, type AgreementStatusValue, type BroadcastAudience, type InboxDeliveryRef, type InboxPartyKind, type InboxRequestChannel, type PrincipalPresentation, type TaskStateValue } from '@ziggs-ai/contracts';
|
|
4
2
|
/**
|
|
5
3
|
* One failure shape for every HTTP client path.
|
|
6
4
|
*
|
|
@@ -60,7 +58,7 @@ export interface Creds {
|
|
|
60
58
|
/** Person-readable window name, sent as `X-Ziggs-Session-Name`. */
|
|
61
59
|
sessionName?: string;
|
|
62
60
|
}
|
|
63
|
-
export type TaskState =
|
|
61
|
+
export type TaskState = TaskStateValue;
|
|
64
62
|
export type PlanStepStatus = 'pending' | 'in_progress' | 'completed' | 'skipped';
|
|
65
63
|
export interface PlanStep {
|
|
66
64
|
stepId: string;
|
|
@@ -139,15 +137,9 @@ export interface Task {
|
|
|
139
137
|
payerIsYou?: boolean;
|
|
140
138
|
proposedToIsYou?: boolean;
|
|
141
139
|
}
|
|
142
|
-
export type AgreementStatus =
|
|
140
|
+
export type AgreementStatus = AgreementStatusValue;
|
|
143
141
|
export type ProposalStatus = 'pending' | 'approved' | 'rejected' | 'countered' | 'expired' | (string & {});
|
|
144
|
-
|
|
145
|
-
export declare const AGREEMENT_ENGAGEMENT_KIND: {
|
|
146
|
-
readonly HIRE: "hire";
|
|
147
|
-
readonly SERVICE: "service";
|
|
148
|
-
/** a bilateral reach link between two agents (no money, no work). */
|
|
149
|
-
readonly LINK: "link";
|
|
150
|
-
};
|
|
142
|
+
export { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, type BroadcastAudience, };
|
|
151
143
|
export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGREEMENT_ENGAGEMENT_KIND];
|
|
152
144
|
/**
|
|
153
145
|
* One side of an agreement, as the wire carries it.
|
|
@@ -161,15 +153,6 @@ export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGR
|
|
|
161
153
|
export type { AgreementPartySide };
|
|
162
154
|
/** The four sides of an agreement, each {@link AgreementPartySide}. */
|
|
163
155
|
export type { AgreementParties };
|
|
164
|
-
/** Both ids on one side, principal first, absent ones dropped. */
|
|
165
|
-
export declare function partySideIds(side: AgreementPartySide | null | undefined): string[];
|
|
166
|
-
/**
|
|
167
|
-
* Every agent that acted on this agreement, deduped.
|
|
168
|
-
*
|
|
169
|
-
* The read for "is this agent a party?" — an agent id lives in `actor` alone,
|
|
170
|
-
* so widening the match to `principal` would hit an unrelated estate.
|
|
171
|
-
*/
|
|
172
|
-
export declare function partyActorIds(parties: AgreementParties | null | undefined): string[];
|
|
173
156
|
/** Compact agreement reference carried by task reads. */
|
|
174
157
|
export interface AgreementSummary {
|
|
175
158
|
agreementId: string;
|
|
@@ -289,7 +272,6 @@ export declare const EntryTypes: {
|
|
|
289
272
|
readonly TASK_HISTORY: "task_history";
|
|
290
273
|
};
|
|
291
274
|
export type EntryType = (typeof EntryTypes)[keyof typeof EntryTypes];
|
|
292
|
-
/** ⚠️ Mirrors the server's content-type constant; the server is authoritative. */
|
|
293
275
|
export declare const ContentTypes: {
|
|
294
276
|
readonly TEXT: "text";
|
|
295
277
|
readonly OPERATION: "operation";
|
|
@@ -297,24 +279,13 @@ export declare const ContentTypes: {
|
|
|
297
279
|
readonly RESULT: "result";
|
|
298
280
|
readonly AGREEMENT_UPDATE: "agreement_update";
|
|
299
281
|
readonly TASK: "task";
|
|
300
|
-
readonly
|
|
282
|
+
readonly TASK_PLAN_STEP: "task_plan_step";
|
|
301
283
|
readonly THOUGHT: "thought";
|
|
302
|
-
|
|
303
|
-
readonly TASK_UPDATE: "task_update";
|
|
284
|
+
readonly PROPOSAL: "proposal";
|
|
304
285
|
};
|
|
305
286
|
export type ContentType = (typeof ContentTypes)[keyof typeof ContentTypes];
|
|
306
287
|
/** ⚠️ Mirrors the server's entry/content-type validation; the server is authoritative. */
|
|
307
288
|
export declare function isValidContentType(contentType: string, entryType: string): boolean;
|
|
308
|
-
/** ⚠️ Mirrors the server's sentinel. Fully-public broadcast target. */
|
|
309
|
-
export declare const OPEN_AGREEMENT_TARGET: "everyone";
|
|
310
|
-
/** ⚠️ Mirrors the server's sentinel. Org-scoped broadcast: visible/claimable only by members of the agreement's orgId. */
|
|
311
|
-
export declare const ORG_AGREEMENT_TARGET: "org";
|
|
312
|
-
/** Audience for a broadcast proposal: fully public ('everyone') or org-scoped ('org'). */
|
|
313
|
-
export type BroadcastAudience = typeof OPEN_AGREEMENT_TARGET | typeof ORG_AGREEMENT_TARGET;
|
|
314
|
-
/** Both broadcast sentinels — values that occupy a party slot but are NOT real principal ids. */
|
|
315
|
-
export declare const BROADCAST_TARGETS: readonly ["everyone", "org"];
|
|
316
|
-
/** True when `id` is a broadcast sentinel ('everyone' | 'org') rather than a concrete principal id. */
|
|
317
|
-
export declare function isBroadcastTarget(id: string | null | undefined): boolean;
|
|
318
289
|
export { isPersonaFaceRef as isPersonaRef, isRoomBindingRef as isRoomPresentationRef, isPresentationRef as isOpaquePresentationRef, } from '@ziggs-ai/contracts';
|
|
319
290
|
/** Display + entitlement face for a principal on the message/roster wire. */
|
|
320
291
|
export type { PrincipalPresentation };
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, MESSAGE_CONTENT_TYPE, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, } from '@ziggs-ai/contracts';
|
|
1
2
|
/**
|
|
2
3
|
* One failure shape for every HTTP client path.
|
|
3
4
|
*
|
|
@@ -27,32 +28,7 @@ export class RateLimitedError extends ApiError {
|
|
|
27
28
|
this.retryAfterMs = retryAfterMs;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
export const AGREEMENT_ENGAGEMENT_KIND = {
|
|
32
|
-
HIRE: 'hire',
|
|
33
|
-
SERVICE: 'service',
|
|
34
|
-
/** a bilateral reach link between two agents (no money, no work). */
|
|
35
|
-
LINK: 'link',
|
|
36
|
-
};
|
|
37
|
-
/** The four sides, in the order every fan-out walks them. */
|
|
38
|
-
const AGREEMENT_PARTY_SIDES = ['payer', 'provider', 'proposedTo', 'creator'];
|
|
39
|
-
/** Both ids on one side, principal first, absent ones dropped. */
|
|
40
|
-
export function partySideIds(side) {
|
|
41
|
-
return [side?.principal, side?.actor].filter((id) => typeof id === 'string' && id.length > 0);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Every agent that acted on this agreement, deduped.
|
|
45
|
-
*
|
|
46
|
-
* The read for "is this agent a party?" — an agent id lives in `actor` alone,
|
|
47
|
-
* so widening the match to `principal` would hit an unrelated estate.
|
|
48
|
-
*/
|
|
49
|
-
export function partyActorIds(parties) {
|
|
50
|
-
// The wire always sends all four sides; a caller may still hand us nothing.
|
|
51
|
-
const p = parties ?? {};
|
|
52
|
-
return [
|
|
53
|
-
...new Set(AGREEMENT_PARTY_SIDES.map((name) => p[name]?.actor).filter((id) => typeof id === 'string' && id.length > 0)),
|
|
54
|
-
];
|
|
55
|
-
}
|
|
31
|
+
export { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, };
|
|
56
32
|
/** ⚠️ Mirrors the server's entry-type constant; the server is authoritative. */
|
|
57
33
|
export const EntryTypes = {
|
|
58
34
|
MESSAGE: 'message',
|
|
@@ -62,38 +38,28 @@ export const EntryTypes = {
|
|
|
62
38
|
/** @deprecated legacy alias for AGREEMENT_HISTORY */
|
|
63
39
|
TASK_HISTORY: 'task_history',
|
|
64
40
|
};
|
|
65
|
-
|
|
66
|
-
export const ContentTypes = {
|
|
67
|
-
TEXT: 'text',
|
|
68
|
-
OPERATION: 'operation',
|
|
69
|
-
CONTEXT: 'context',
|
|
70
|
-
RESULT: 'result',
|
|
71
|
-
AGREEMENT_UPDATE: 'agreement_update',
|
|
72
|
-
TASK: 'task',
|
|
73
|
-
PROPOSAL: 'proposal',
|
|
74
|
-
THOUGHT: 'thought',
|
|
75
|
-
/** @deprecated legacy alias for AGREEMENT_UPDATE */
|
|
76
|
-
TASK_UPDATE: 'task_update',
|
|
77
|
-
};
|
|
41
|
+
export const ContentTypes = MESSAGE_CONTENT_TYPE;
|
|
78
42
|
const VALID_CONTENT_TYPES = {
|
|
79
43
|
[EntryTypes.MESSAGE]: [ContentTypes.TEXT],
|
|
80
|
-
[EntryTypes.NOTIFICATION]: [
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
44
|
+
[EntryTypes.NOTIFICATION]: [
|
|
45
|
+
ContentTypes.TEXT,
|
|
46
|
+
ContentTypes.TASK,
|
|
47
|
+
ContentTypes.TASK_PLAN_STEP,
|
|
48
|
+
ContentTypes.PROPOSAL,
|
|
49
|
+
ContentTypes.AGREEMENT_UPDATE,
|
|
50
|
+
],
|
|
51
|
+
[EntryTypes.ARTIFACT]: [
|
|
52
|
+
ContentTypes.OPERATION,
|
|
53
|
+
ContentTypes.CONTEXT,
|
|
54
|
+
ContentTypes.RESULT,
|
|
55
|
+
ContentTypes.TEXT,
|
|
56
|
+
ContentTypes.THOUGHT,
|
|
57
|
+
],
|
|
58
|
+
[EntryTypes.AGREEMENT_HISTORY]: [ContentTypes.AGREEMENT_UPDATE],
|
|
59
|
+
[EntryTypes.TASK_HISTORY]: [ContentTypes.AGREEMENT_UPDATE],
|
|
84
60
|
};
|
|
85
61
|
/** ⚠️ Mirrors the server's entry/content-type validation; the server is authoritative. */
|
|
86
62
|
export function isValidContentType(contentType, entryType) {
|
|
87
63
|
return VALID_CONTENT_TYPES[entryType]?.includes(contentType) ?? false;
|
|
88
64
|
}
|
|
89
|
-
/** ⚠️ Mirrors the server's sentinel. Fully-public broadcast target. */
|
|
90
|
-
export const OPEN_AGREEMENT_TARGET = 'everyone';
|
|
91
|
-
/** ⚠️ Mirrors the server's sentinel. Org-scoped broadcast: visible/claimable only by members of the agreement's orgId. */
|
|
92
|
-
export const ORG_AGREEMENT_TARGET = 'org';
|
|
93
|
-
/** Both broadcast sentinels — values that occupy a party slot but are NOT real principal ids. */
|
|
94
|
-
export const BROADCAST_TARGETS = [OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET];
|
|
95
|
-
/** True when `id` is a broadcast sentinel ('everyone' | 'org') rather than a concrete principal id. */
|
|
96
|
-
export function isBroadcastTarget(id) {
|
|
97
|
-
return id === OPEN_AGREEMENT_TARGET || id === ORG_AGREEMENT_TARGET;
|
|
98
|
-
}
|
|
99
65
|
export { isPersonaFaceRef as isPersonaRef, isRoomBindingRef as isRoomPresentationRef, isPresentationRef as isOpaquePresentationRef, } from '@ziggs-ai/contracts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/api-client",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.4",
|
|
4
4
|
"description": "HTTP and WebSocket client for the Ziggs backend API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test:watch": "node --import tsx/esm --test --watch test/*.test.ts"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ziggs-ai/contracts": "^0.
|
|
30
|
+
"@ziggs-ai/contracts": "^0.23.3",
|
|
31
31
|
"socket.io-client": "^4.7.0"
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|