@ziggs-ai/api-client 0.38.0 → 0.39.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/CHANGELOG.md +34 -0
- package/README.md +14 -36
- package/dist/capabilities/agreementVerbs.js +13 -21
- package/dist/capabilities/agreements.d.ts +2 -3
- package/dist/capabilities/agreements.js +8 -43
- package/dist/capabilities/artifacts.d.ts +6 -0
- package/dist/capabilities/artifacts.js +84 -43
- package/dist/capabilities/connections.js +10 -7
- package/dist/capabilities/context.d.ts +1 -1
- package/dist/capabilities/context.js +52 -45
- package/dist/capabilities/discovery.js +5 -5
- package/dist/capabilities/everyday.d.ts +1 -1
- package/dist/capabilities/everyday.js +1 -2
- package/dist/capabilities/index.d.ts +3 -4
- package/dist/capabilities/index.js +3 -4
- package/dist/capabilities/introductions.d.ts +1 -0
- package/dist/capabilities/introductions.js +64 -17
- package/dist/capabilities/links.d.ts +1 -9
- package/dist/capabilities/links.js +40 -39
- package/dist/capabilities/mcpBriefs.js +41 -34
- package/dist/capabilities/messaging.d.ts +9 -0
- package/dist/capabilities/messaging.js +11 -0
- package/dist/capabilities/nextCall.js +8 -17
- package/dist/capabilities/sources.d.ts +2 -3
- package/dist/capabilities/sources.js +1 -2
- package/dist/capabilities/tasks.d.ts +1 -0
- package/dist/capabilities/tasks.js +43 -2
- package/dist/capabilities/toolLane.js +2 -2
- package/dist/engagementGuide.d.ts +2 -2
- package/dist/engagementGuide.js +6 -6
- package/dist/handles.d.ts +1 -1
- package/dist/handles.js +2 -2
- package/dist/http/AgreementClient.d.ts +25 -63
- package/dist/http/AgreementClient.js +14 -65
- package/dist/http/ArtifactsClient.d.ts +31 -19
- package/dist/http/ArtifactsClient.js +43 -62
- package/dist/http/ConnectionsClient.d.ts +6 -7
- package/dist/http/ConnectionsClient.js +5 -3
- package/dist/http/ContextDiscoveryClient.d.ts +2 -2
- package/dist/http/ContextGrantsClient.d.ts +9 -15
- package/dist/http/ContextGrantsClient.js +3 -5
- package/dist/http/ContextOpenClient.d.ts +5 -6
- package/dist/http/ContextOpenClient.js +11 -4
- package/dist/http/ContextReadClient.d.ts +23 -27
- package/dist/http/ContextReadClient.js +24 -23
- package/dist/http/ExecutionContextClient.d.ts +5 -1
- package/dist/http/ExecutionContextClient.js +48 -1
- package/dist/http/IntroductionsClient.d.ts +33 -4
- package/dist/http/IntroductionsClient.js +6 -2
- package/dist/http/MessageRecordsClient.d.ts +36 -0
- package/dist/http/MessageRecordsClient.js +30 -0
- package/dist/http/SessionsClient.d.ts +5 -5
- package/dist/http/SessionsClient.js +6 -6
- package/dist/http/TaskClient.d.ts +35 -6
- package/dist/http/TaskClient.js +38 -36
- package/dist/http/agreementFlows.d.ts +2 -3
- package/dist/http/agreementFlows.js +5 -5
- package/dist/http/index.d.ts +3 -5
- package/dist/http/index.js +3 -4
- package/dist/http/operatorHeaders.d.ts +3 -0
- package/dist/http/operatorHeaders.js +14 -5
- package/dist/inboxAck.d.ts +2 -2
- package/dist/inboxAck.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/types.d.ts +25 -55
- package/package.json +2 -2
- package/dist/capabilities/chat.d.ts +0 -43
- package/dist/capabilities/chat.js +0 -196
- package/dist/capabilities/chatListFields.d.ts +0 -14
- package/dist/capabilities/chatListFields.js +0 -61
- package/dist/http/ChatClient.d.ts +0 -187
- package/dist/http/ChatClient.js +0 -219
- package/dist/http/MessagesClient.d.ts +0 -40
- package/dist/http/MessagesClient.js +0 -81
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, COMPANY_AGREEMENT_TARGET, 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';
|
|
1
|
+
import { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, COMPANY_AGREEMENT_TARGET, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, type AgreementPartySide, type AgreementParties, type AgreementStatusValue, type BroadcastAudience, type InboxDeliveryRef, type InboxPartyKind, type InboxPlanAckChannel, type InboxRequestChannel, type PrincipalPresentation, type TaskStateValue } from '@ziggs-ai/contracts';
|
|
2
2
|
/**
|
|
3
3
|
* One failure shape for every HTTP client path.
|
|
4
4
|
*
|
|
@@ -81,6 +81,16 @@ export interface Task {
|
|
|
81
81
|
updatedAt?: string;
|
|
82
82
|
parentTaskId?: string;
|
|
83
83
|
rootTaskId?: string;
|
|
84
|
+
requiredInputArtifactIds?: string[];
|
|
85
|
+
delegations?: Array<{
|
|
86
|
+
taskId: string;
|
|
87
|
+
state: TaskState;
|
|
88
|
+
returnedArtifactId: string | null;
|
|
89
|
+
}>;
|
|
90
|
+
delegation?: {
|
|
91
|
+
instructionArtifactId: string;
|
|
92
|
+
returnedArtifactId?: string;
|
|
93
|
+
} | null;
|
|
84
94
|
/** Tasks this one waits for before it may start. */
|
|
85
95
|
waitsOn?: string[];
|
|
86
96
|
/**
|
|
@@ -289,57 +299,6 @@ export declare function isValidContentType(contentType: string, entryType: strin
|
|
|
289
299
|
export { isPersonaFaceRef as isPersonaRef, isRoomBindingRef as isRoomPresentationRef, isPresentationRef as isOpaquePresentationRef, } from '@ziggs-ai/contracts';
|
|
290
300
|
/** Display + entitlement face for a principal on the message/roster wire. */
|
|
291
301
|
export type { PrincipalPresentation };
|
|
292
|
-
export interface MessageMetadata {
|
|
293
|
-
chatId: string;
|
|
294
|
-
/** Stable id for dedup across push + inbox catch-up. */
|
|
295
|
-
messageId?: string;
|
|
296
|
-
userId: string;
|
|
297
|
-
sender: {
|
|
298
|
-
id: string;
|
|
299
|
-
type?: string;
|
|
300
|
-
presentation?: PrincipalPresentation | null;
|
|
301
|
-
/** Absent when the viewer is masked (persona layer). */
|
|
302
|
-
underAgreementId?: string | null;
|
|
303
|
-
presentedAs?: string | null;
|
|
304
|
-
};
|
|
305
|
-
senderId: string;
|
|
306
|
-
senderType?: string;
|
|
307
|
-
receiver?: {
|
|
308
|
-
id: string;
|
|
309
|
-
presentation?: PrincipalPresentation | null;
|
|
310
|
-
} | null;
|
|
311
|
-
receiverId?: string | null;
|
|
312
|
-
/**
|
|
313
|
-
* Message-level presentation stamp when the backend attaches one
|
|
314
|
-
* (same shape as sender.presentation). Prefer sender.presentation.
|
|
315
|
-
*/
|
|
316
|
-
presentation?: PrincipalPresentation | null;
|
|
317
|
-
entryType?: string;
|
|
318
|
-
contentType?: string;
|
|
319
|
-
taskId?: string | null;
|
|
320
|
-
/**
|
|
321
|
-
* Optional task / agreement snapshot pinned by the backend on
|
|
322
|
-
* `task.notify.chat` / `agreement.notify.direct` traffic. The agent SDK's
|
|
323
|
-
* `normalizeIncomingEvent` reads `metadata.task` to upgrade structured
|
|
324
|
-
* task notifications into `task_result` events (so executors see
|
|
325
|
-
* `task-assigned` outcomes when the orchestrator calls `task_create`),
|
|
326
|
-
* and maps explicit wire `operation` + `agreementId` lifecycle fields
|
|
327
|
-
* into `agreement_lifecycle` events for proposal approve/reject.
|
|
328
|
-
*/
|
|
329
|
-
task?: Record<string, unknown> | null;
|
|
330
|
-
agreement?: Record<string, unknown> | null;
|
|
331
|
-
operation?: string | null;
|
|
332
|
-
agreementId?: string | null;
|
|
333
|
-
/**
|
|
334
|
-
* Message send time from the wire payload. The live-path inbox
|
|
335
|
-
* watermark ack (Agent, P4) needs it — without a timestamp the
|
|
336
|
-
* ack never fires and catch-up re-delivers the chat's whole backlog after a
|
|
337
|
-
* host eviction, re-running already-answered goals.
|
|
338
|
-
*/
|
|
339
|
-
timestamp?: string;
|
|
340
|
-
sentTimestamp?: string;
|
|
341
|
-
}
|
|
342
|
-
export type MessageHandler = (text: string, metadata: MessageMetadata) => Promise<void>;
|
|
343
302
|
/**
|
|
344
303
|
* What a delivery can be about. A closed union, not a comment: a consumer that
|
|
345
304
|
* dispatches on `kind` (the MCP read plan) is only safe if the compiler can tell
|
|
@@ -362,7 +321,7 @@ export interface InboxSourceRef {
|
|
|
362
321
|
export type { InboxDeliveryRef };
|
|
363
322
|
/** Message/artifact deliveries folded by chat, so you can open chats directly. */
|
|
364
323
|
export interface InboxChatNews {
|
|
365
|
-
|
|
324
|
+
addressId: string;
|
|
366
325
|
count: number;
|
|
367
326
|
latestAt: string;
|
|
368
327
|
}
|
|
@@ -443,7 +402,17 @@ export interface InboxTaskRef {
|
|
|
443
402
|
* exact-match triage with zero LLM tokens before any wake.
|
|
444
403
|
*/
|
|
445
404
|
export type { InboxOpenRequestRef as InboxRequestRef } from '@ziggs-ai/contracts';
|
|
446
|
-
|
|
405
|
+
/**
|
|
406
|
+
* A task whose plan waits on this reader's acknowledgement: work the reader's
|
|
407
|
+
* side pays for. A plan on work the reader was hired to do is the hirer's and
|
|
408
|
+
* is not listed.
|
|
409
|
+
*/
|
|
410
|
+
export type { InboxPlanAckRef } from '@ziggs-ai/contracts';
|
|
411
|
+
/**
|
|
412
|
+
* `planAcksAwaitingMe` / `truncatedPlanAcks` are absent on older servers;
|
|
413
|
+
* read them as empty.
|
|
414
|
+
*/
|
|
415
|
+
export interface InboxEnvelope extends InboxRequestChannel, Partial<InboxPlanAckChannel> {
|
|
447
416
|
asOf: string;
|
|
448
417
|
/**
|
|
449
418
|
* The mailboxes this reader's view merges — its owner's, plus whatever
|
|
@@ -488,6 +457,7 @@ export interface InboxEnvelope extends InboxRequestChannel {
|
|
|
488
457
|
*/
|
|
489
458
|
ackTo: string | null;
|
|
490
459
|
/** Open tasks assigned to this agent — the work channel. */
|
|
460
|
+
answersAwaitingMe?: InboxDeliveryRef[];
|
|
491
461
|
tasksAwaitingMe: InboxTaskRef[];
|
|
492
462
|
truncatedTasks: number;
|
|
493
463
|
proposalsAwaitingMe: InboxProposalRef[];
|
|
@@ -535,7 +505,7 @@ export interface InboxChangesResult {
|
|
|
535
505
|
cursor: string;
|
|
536
506
|
expiresAt: string;
|
|
537
507
|
hasMore: boolean;
|
|
538
|
-
deliveries: Array<Pick<InboxDeliveryRef, 'eventId' | 'kind' | 'resourceId' | '
|
|
508
|
+
deliveries: Array<Pick<InboxDeliveryRef, 'eventId' | 'kind' | 'resourceId' | 'addressId' | 'taskId' | 'agreementId' | 'assigneeId' | 'needsHuman' | 'ts'> & {
|
|
539
509
|
orgId?: string | null;
|
|
540
510
|
}>;
|
|
541
511
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/api-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
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": "
|
|
34
|
+
"@ziggs-ai/contracts": "0.37.0",
|
|
35
35
|
"socket.io-client": "^4.7.0"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type SendChatMessageResult } from '../http/ChatClient.js';
|
|
2
|
-
import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
|
|
3
|
-
import { type WorkContext } from './nextCall.js';
|
|
4
|
-
export interface SendPresentation {
|
|
5
|
-
ok: true;
|
|
6
|
-
chatId: string;
|
|
7
|
-
messageId: string;
|
|
8
|
-
/** Keep the server's delivery guidance on every client surface. */
|
|
9
|
-
message?: string;
|
|
10
|
-
/** What the server did with this send, stated so it cannot be over-read. */
|
|
11
|
-
delivered: string;
|
|
12
|
-
/** Who it was addressed to, exactly as the server resolved it. */
|
|
13
|
-
to?: {
|
|
14
|
-
id: string;
|
|
15
|
-
type: string;
|
|
16
|
-
};
|
|
17
|
-
workContext?: WorkContext;
|
|
18
|
-
delivery?: SendChatMessageResult['delivery'];
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Present a send as what it is: a message that now exists, addressed to
|
|
22
|
-
* somebody, which may or may not wake anyone.
|
|
23
|
-
*
|
|
24
|
-
* An agent that sent an answer and read `success: true` had every reason to
|
|
25
|
-
* report the answer delivered. Three different things hide behind that word.
|
|
26
|
-
* The message exists — that much is certain. Who it reached is the server's
|
|
27
|
-
* decision, not the caller's, because an omitted recipient may be inferred.
|
|
28
|
-
* Mailbox routing is a separate asynchronous decision, including coverage by
|
|
29
|
-
* an assistant when the addressee is a person.
|
|
30
|
-
*
|
|
31
|
-
* A server that does not report the destination gets one honest line about the
|
|
32
|
-
* message existing, and nothing invented about where it went.
|
|
33
|
-
*/
|
|
34
|
-
export declare function presentSendResult(result: SendChatMessageResult, env: CapabilityEnv): SendPresentation;
|
|
35
|
-
/**
|
|
36
|
-
* the decided chat surface for agents: chat_open only.
|
|
37
|
-
* There is deliberately NO chat-listing tool on the SDK: taking part in a room
|
|
38
|
-
* IS a grant on it, so "what chats can I see" is already answered by grant_list
|
|
39
|
-
* scopeKind=chat. MCP keeps its rich session-UX lister
|
|
40
|
-
* (ziggs_chat_list) for humans in Cursor/Claude.
|
|
41
|
-
*/
|
|
42
|
-
export declare const openConversationCapability: CapabilityDefinition;
|
|
43
|
-
export declare const CHAT_CAPABILITIES: CapabilityDefinition[];
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { openConversation } from '../http/ChatClient.js';
|
|
2
|
-
import { chatAppUrl } from '../utils/appUrls.js';
|
|
3
|
-
import { fullCreds } from './types.js';
|
|
4
|
-
import { webAppOrigin } from './links.js';
|
|
5
|
-
import { workContextFromEnv } from './nextCall.js';
|
|
6
|
-
/**
|
|
7
|
-
* Present a send as what it is: a message that now exists, addressed to
|
|
8
|
-
* somebody, which may or may not wake anyone.
|
|
9
|
-
*
|
|
10
|
-
* An agent that sent an answer and read `success: true` had every reason to
|
|
11
|
-
* report the answer delivered. Three different things hide behind that word.
|
|
12
|
-
* The message exists — that much is certain. Who it reached is the server's
|
|
13
|
-
* decision, not the caller's, because an omitted recipient may be inferred.
|
|
14
|
-
* Mailbox routing is a separate asynchronous decision, including coverage by
|
|
15
|
-
* an assistant when the addressee is a person.
|
|
16
|
-
*
|
|
17
|
-
* A server that does not report the destination gets one honest line about the
|
|
18
|
-
* message existing, and nothing invented about where it went.
|
|
19
|
-
*/
|
|
20
|
-
export function presentSendResult(result, env) {
|
|
21
|
-
const workContext = workContextFromEnv(env);
|
|
22
|
-
const delivery = result.delivery;
|
|
23
|
-
const base = {
|
|
24
|
-
ok: true,
|
|
25
|
-
chatId: result.chatId,
|
|
26
|
-
messageId: result.messageId,
|
|
27
|
-
...(result.message ? { message: result.message } : {}),
|
|
28
|
-
...(workContext ? { workContext } : {}),
|
|
29
|
-
};
|
|
30
|
-
if (!delivery) {
|
|
31
|
-
return {
|
|
32
|
-
...base,
|
|
33
|
-
delivered: `Message ${result.messageId} was accepted into chat ${result.chatId}. Accepted is not read: nothing here says anyone has seen it.`,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
const destination = delivery.broadcast
|
|
37
|
-
? 'the humans in the room, not one addressee'
|
|
38
|
-
: `${delivery.to.id} (${delivery.to.type})`;
|
|
39
|
-
// What counted it, in the server's words: a stranger's free messages with a
|
|
40
|
-
// listed agent run out, and the person writing needs to know before they do.
|
|
41
|
-
const counted = result.coverage?.message ? ` ${result.coverage.message}` : '';
|
|
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.';
|
|
47
|
-
return {
|
|
48
|
-
...base,
|
|
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}`,
|
|
50
|
-
to: delivery.to,
|
|
51
|
-
delivery,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* the decided chat surface for agents: chat_open only.
|
|
56
|
-
* There is deliberately NO chat-listing tool on the SDK: taking part in a room
|
|
57
|
-
* IS a grant on it, so "what chats can I see" is already answered by grant_list
|
|
58
|
-
* scopeKind=chat. MCP keeps its rich session-UX lister
|
|
59
|
-
* (ziggs_chat_list) for humans in Cursor/Claude.
|
|
60
|
-
*/
|
|
61
|
-
export const openConversationCapability = {
|
|
62
|
-
key: 'chat_open',
|
|
63
|
-
names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
|
|
64
|
-
title: 'Open, invite to, or resume a conversation',
|
|
65
|
-
descriptions: {
|
|
66
|
-
sdk: 'Open a conversation or explicitly invite people to a room. To resume admission of an existing hire, pass only chatId and agreementId; this uses the same recovery as the room UI and never hires or charges again. 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"].',
|
|
67
|
-
},
|
|
68
|
-
annotation: 'write',
|
|
69
|
-
params: {
|
|
70
|
-
participantId: {
|
|
71
|
-
type: 'string',
|
|
72
|
-
description: 'Known user or agent id from search, a directory, a chat, or an agreement. For a teammate or a linked person, their user id, not an agent of theirs. No existing chat is required; do not guess ids.',
|
|
73
|
-
},
|
|
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.' },
|
|
75
|
-
chatId: { type: 'string', description: 'Existing room. With participantIds: invite into it. With agreementId only: resume the approved hire admission without another hire or charge.' },
|
|
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.' },
|
|
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.' },
|
|
79
|
-
newChat: {
|
|
80
|
-
type: 'boolean',
|
|
81
|
-
description: 'Open a separate chat even though one is already open with the named participants. ' +
|
|
82
|
-
'For when the conversation is genuinely its own subject and would confuse an ' +
|
|
83
|
-
'existing thread. The separate chat does not become the main one, so a later ' +
|
|
84
|
-
'call without this still returns the original. Leave unset to continue where ' +
|
|
85
|
-
'you left off.',
|
|
86
|
-
},
|
|
87
|
-
agreementId: {
|
|
88
|
-
type: 'string',
|
|
89
|
-
description: 'With chatId: resume that existing hire’s room admission. Otherwise, the hire this open is the working room for. On a store-claim kickoff, pass the ' +
|
|
90
|
-
'claimed agreement id so the payer is stamped from-start in that room.',
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
needsAgentId: true,
|
|
94
|
-
handler: async (args, env) => {
|
|
95
|
-
if (args.chatId && args.agreementId) {
|
|
96
|
-
const result = await openConversation(args, fullCreds(env));
|
|
97
|
-
return {
|
|
98
|
-
...result,
|
|
99
|
-
appUrl: chatAppUrl(webAppOrigin(env), result.chatId),
|
|
100
|
-
message: result.status === 'ready'
|
|
101
|
-
? 'The hired agent is admitted under this agreement. No new hire or charge was made.'
|
|
102
|
-
: `Admission is still incomplete${result.reason ? `: ${result.reason}` : ''}. Retry this same call; do not claim again.`,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
if (!args.participantId && !Array.isArray(args.participantIds)) {
|
|
106
|
-
throw new Error('participantId is required, or use participantIds to invite people');
|
|
107
|
-
}
|
|
108
|
-
if (args.participantIds !== undefined) {
|
|
109
|
-
if (args.participantId !== undefined || !Array.isArray(args.participantIds) ||
|
|
110
|
-
args.participantIds.length < 1 || args.participantIds.length > 20 ||
|
|
111
|
-
args.participantIds.some(id => typeof id !== 'string' || !id.trim())) {
|
|
112
|
-
throw new Error('Use participantIds containing 1–20 ids, without participantId');
|
|
113
|
-
}
|
|
114
|
-
if (!args.chatId && (typeof args.idempotencyKey !== 'string' || !args.idempotencyKey.trim())) {
|
|
115
|
-
throw new Error('Group creation requires an idempotencyKey; reuse it when retrying');
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
else if (args.chatId) {
|
|
119
|
-
throw new Error('chatId requires participantIds');
|
|
120
|
-
}
|
|
121
|
-
if (typeof args.name === 'string' && args.name.trim() && !Array.isArray(args.participantIds) && args.newChat !== true) {
|
|
122
|
-
throw new Error('A name is for a new group or a newChat room. Pair rooms take no name; the person is the name.');
|
|
123
|
-
}
|
|
124
|
-
const { chatId, reused, invited, name } = await openConversation({
|
|
125
|
-
...(typeof args.participantId === 'string' ? { participantId: args.participantId } : {}),
|
|
126
|
-
...(Array.isArray(args.participantIds) ? { participantIds: args.participantIds } : {}),
|
|
127
|
-
...(typeof args.chatId === 'string' ? { chatId: args.chatId } : {}),
|
|
128
|
-
...(typeof args.idempotencyKey === 'string' ? { idempotencyKey: args.idempotencyKey } : {}),
|
|
129
|
-
...(args.newChat === true ? { newChat: true } : {}),
|
|
130
|
-
...(typeof args.createIfMissing === 'boolean' ? { createIfMissing: args.createIfMissing } : {}),
|
|
131
|
-
...(typeof args.agreementId === 'string' && args.agreementId ? { agreementId: args.agreementId } : {}),
|
|
132
|
-
...(typeof args.name === 'string' && args.name.trim() ? { name: args.name.trim() } : {}),
|
|
133
|
-
}, fullCreds(env));
|
|
134
|
-
const agentId = fullCreds(env).agentId;
|
|
135
|
-
const { people, pending } = roomRoster(args, invited, agentId);
|
|
136
|
-
const lister = env.surface === 'mcp' ? 'ziggs_grant_list' : 'grant_list';
|
|
137
|
-
const grantNote = `Use ${lister} with scopeKind ["chat"] to inspect your room grants. Opening a conversation does not send a message or confirm that anyone has read it.`;
|
|
138
|
-
// word the note from the outcome instead of covering both cases.
|
|
139
|
-
// "Open (or reused)" told the agent the distinction existed and then
|
|
140
|
-
// withheld it — worse than silence, because the agent cannot even tell
|
|
141
|
-
// there is something to look up. A backend too old to report it keeps the
|
|
142
|
-
// old both-cases wording, and `reused` is simply absent from the result.
|
|
143
|
-
// A new group that named agents reports each of them too.
|
|
144
|
-
const created = reused === false ? 'Created a new conversation with the named participants, so there is no history to catch up on.' : null;
|
|
145
|
-
const outcomeNote = args.chatId
|
|
146
|
-
? inviteNote(invited)
|
|
147
|
-
: created && invited?.length
|
|
148
|
-
? `${created} ${inviteNote(invited)}`
|
|
149
|
-
: reused === true
|
|
150
|
-
? 'Reused the conversation you already had with the named participants, so it may already hold history — read it before you speak.'
|
|
151
|
-
: reused === false
|
|
152
|
-
? 'Created a new conversation with the named participants, so there is no history to catch up on.'
|
|
153
|
-
: 'Conversation is open (or reused).';
|
|
154
|
-
return {
|
|
155
|
-
chatId,
|
|
156
|
-
appUrl: chatAppUrl(webAppOrigin(env), chatId),
|
|
157
|
-
...(agentId ? { actingAgentId: agentId } : {}),
|
|
158
|
-
people,
|
|
159
|
-
pending,
|
|
160
|
-
...(typeof reused === 'boolean' ? { reused } : {}),
|
|
161
|
-
...(name ? { name } : {}),
|
|
162
|
-
...(invited ? { invited } : {}),
|
|
163
|
-
note: `${outcomeNote} ${grantNote}`,
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
/** Ids this open knows are in the room, and ids still waiting for approval. */
|
|
168
|
-
function roomRoster(args, invited, agentId) {
|
|
169
|
-
const named = Array.isArray(args.participantIds)
|
|
170
|
-
? args.participantIds.filter((id) => typeof id === 'string' && id.trim().length > 0).map((id) => id.trim())
|
|
171
|
-
: typeof args.participantId === 'string' && args.participantId.trim()
|
|
172
|
-
? [args.participantId.trim()]
|
|
173
|
-
: [];
|
|
174
|
-
const pending = (invited ?? []).filter((row) => row.status === 'pending').map((row) => row.id);
|
|
175
|
-
const waiting = new Set(pending);
|
|
176
|
-
const here = (invited ?? []).filter((row) => row.status !== 'pending').map((row) => row.id);
|
|
177
|
-
const people = [...new Set([...named.filter((id) => !waiting.has(id)), ...here, ...(agentId ? [agentId] : [])])];
|
|
178
|
-
return { people, pending };
|
|
179
|
-
}
|
|
180
|
-
/** Say what happened to each invited id, so the agent reports it truthfully. */
|
|
181
|
-
function inviteNote(invited) {
|
|
182
|
-
if (!invited?.length) {
|
|
183
|
-
return 'The named people can participate in this room. Their history access remains bounded by their grants.';
|
|
184
|
-
}
|
|
185
|
-
const ids = (status) => invited.filter((row) => row.status === status).map((row) => row.id);
|
|
186
|
-
const parts = [];
|
|
187
|
-
if (ids('added').length)
|
|
188
|
-
parts.push(`Added: ${ids('added').join(', ')}.`);
|
|
189
|
-
if (ids('already-here').length)
|
|
190
|
-
parts.push(`Already in the room: ${ids('already-here').join(', ')}.`);
|
|
191
|
-
if (ids('pending').length) {
|
|
192
|
-
parts.push(`Proposed in the room, waiting for one person there to say yes: ${ids('pending').join(', ')}. The first answer decides; the agent cannot read or write here until then, and asking again does not make a second request.`);
|
|
193
|
-
}
|
|
194
|
-
return `${parts.join(' ')} Invited people read from now on.`;
|
|
195
|
-
}
|
|
196
|
-
export const CHAT_CAPABILITIES = [openConversationCapability];
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default `ziggs_chat_list` projection.
|
|
3
|
-
*
|
|
4
|
-
* The wire row carries three web-app rosters (`members`, `rosterSummary`,
|
|
5
|
-
* `participantSummary`) and repeats the persona on each. The MCP default
|
|
6
|
-
* keeps the people who actually receive (`participantSummary`) and drops the
|
|
7
|
-
* other two unless `fields` asks for them.
|
|
8
|
-
*/
|
|
9
|
-
export declare const CHAT_LIST_DEFAULT_FIELDS: readonly ["chatId", "name", "updatedAt", "lastMessage", "unreadCount", "participantSummary", "readable", "requestAccess"];
|
|
10
|
-
/** Apply `fields`, or the MCP default when the caller omitted it. */
|
|
11
|
-
export declare function projectChatListRows(chats: unknown[], fieldsRaw?: unknown): unknown[];
|
|
12
|
-
export declare function utf8Bytes(value: unknown): number;
|
|
13
|
-
/** Rough token count: 4 UTF-8 bytes per token, rounded up. */
|
|
14
|
-
export declare function approxTokens(value: unknown): number;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { pickListedRow, parseListFields } from './listedFields.js';
|
|
2
|
-
/**
|
|
3
|
-
* Default `ziggs_chat_list` projection.
|
|
4
|
-
*
|
|
5
|
-
* The wire row carries three web-app rosters (`members`, `rosterSummary`,
|
|
6
|
-
* `participantSummary`) and repeats the persona on each. The MCP default
|
|
7
|
-
* keeps the people who actually receive (`participantSummary`) and drops the
|
|
8
|
-
* other two unless `fields` asks for them.
|
|
9
|
-
*/
|
|
10
|
-
export const CHAT_LIST_DEFAULT_FIELDS = [
|
|
11
|
-
'chatId',
|
|
12
|
-
'name',
|
|
13
|
-
'updatedAt',
|
|
14
|
-
'lastMessage',
|
|
15
|
-
'unreadCount',
|
|
16
|
-
'participantSummary',
|
|
17
|
-
// A room you may only list says so, with the read request ready to file.
|
|
18
|
-
'readable',
|
|
19
|
-
'requestAccess',
|
|
20
|
-
];
|
|
21
|
-
function slimParticipant(row) {
|
|
22
|
-
if (!row || typeof row !== 'object' || Array.isArray(row))
|
|
23
|
-
return row;
|
|
24
|
-
const rec = row;
|
|
25
|
-
const view = rec.presentationView;
|
|
26
|
-
const { presentationView: _drop, ...rest } = rec;
|
|
27
|
-
if (view && typeof view === 'object' && !Array.isArray(view)) {
|
|
28
|
-
const presented = view;
|
|
29
|
-
return {
|
|
30
|
-
...rest,
|
|
31
|
-
...(rest.persona == null && presented.persona != null
|
|
32
|
-
? { persona: presented.persona }
|
|
33
|
-
: {}),
|
|
34
|
-
...(rest.presentation == null && presented.mode != null
|
|
35
|
-
? { presentation: presented.mode }
|
|
36
|
-
: {}),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
return rest;
|
|
40
|
-
}
|
|
41
|
-
function projectOneChat(row, fields) {
|
|
42
|
-
const picked = pickListedRow(row, fields);
|
|
43
|
-
if (Array.isArray(picked.participantSummary)) {
|
|
44
|
-
picked.participantSummary = picked.participantSummary.map(slimParticipant);
|
|
45
|
-
}
|
|
46
|
-
return picked;
|
|
47
|
-
}
|
|
48
|
-
/** Apply `fields`, or the MCP default when the caller omitted it. */
|
|
49
|
-
export function projectChatListRows(chats, fieldsRaw) {
|
|
50
|
-
const fields = parseListFields(fieldsRaw) ?? [...CHAT_LIST_DEFAULT_FIELDS];
|
|
51
|
-
return chats.map((row) => row && typeof row === 'object' && !Array.isArray(row)
|
|
52
|
-
? projectOneChat(row, fields)
|
|
53
|
-
: row);
|
|
54
|
-
}
|
|
55
|
-
export function utf8Bytes(value) {
|
|
56
|
-
return Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
57
|
-
}
|
|
58
|
-
/** Rough token count: 4 UTF-8 bytes per token, rounded up. */
|
|
59
|
-
export function approxTokens(value) {
|
|
60
|
-
return Math.ceil(utf8Bytes(value) / 4);
|
|
61
|
-
}
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import type { WriteSource } from '../capabilities/sources.js';
|
|
2
|
-
import type { ChatReadDto } from '@ziggs-ai/contracts';
|
|
3
|
-
import type { MessageDelivery as WireMessageDelivery, SendChatMessageResult as WireSendChatMessageResult } from '@ziggs-ai/contracts';
|
|
4
|
-
import { type Creds } from '../types.js';
|
|
5
|
-
/**
|
|
6
|
-
* One room from `GET /chats/mine`, as the server sends it.
|
|
7
|
-
*
|
|
8
|
-
* The backend answers `toChatReadDto(...)`, so this is that type rather than
|
|
9
|
-
* a restatement of it. The restatement had drifted both ways: it declared
|
|
10
|
-
* `orgId` and `isOrgChat`, neither of which the wire carries — a caller
|
|
11
|
-
* branching on `isOrgChat` was branching on `undefined` — and it omitted
|
|
12
|
-
* seven fields the wire does send, including `name`, `updatedAt` and
|
|
13
|
-
* `lastMessage`, which are the ones this list exists to let an agent triage
|
|
14
|
-
* with without a second call.
|
|
15
|
-
*/
|
|
16
|
-
export type ChatSummary = ChatReadDto;
|
|
17
|
-
/** What happened to one id when chat_open invited into an existing room. */
|
|
18
|
-
export interface ChatOpenInvite {
|
|
19
|
-
id: string;
|
|
20
|
-
kind: 'person' | 'agent';
|
|
21
|
-
/** pending: an agent's joining waits for the people in the room to approve it. */
|
|
22
|
-
status: 'added' | 'already-here' | 'pending';
|
|
23
|
-
admissionId?: string;
|
|
24
|
-
approvers?: Array<{
|
|
25
|
-
id: string;
|
|
26
|
-
name: string | null;
|
|
27
|
-
}>;
|
|
28
|
-
approveIn?: {
|
|
29
|
-
kind: 'chat';
|
|
30
|
-
chatId: string;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export interface OpenConversationInput {
|
|
34
|
-
participantId?: string;
|
|
35
|
-
participantIds?: string[];
|
|
36
|
-
chatId?: string;
|
|
37
|
-
idempotencyKey?: string;
|
|
38
|
-
/** Name for a new group. Pair rooms take no name. */
|
|
39
|
-
name?: string;
|
|
40
|
-
newChat?: boolean;
|
|
41
|
-
agreementId?: string;
|
|
42
|
-
createIfMissing?: boolean;
|
|
43
|
-
}
|
|
44
|
-
export interface OpenConversationResult {
|
|
45
|
-
chatId: string;
|
|
46
|
-
reused?: boolean;
|
|
47
|
-
invited?: ChatOpenInvite[];
|
|
48
|
-
name?: string;
|
|
49
|
-
agreementId?: string;
|
|
50
|
-
status?: 'ready' | 'admission_failed';
|
|
51
|
-
reason?: string;
|
|
52
|
-
}
|
|
53
|
-
export declare function openConversation(participant: string | OpenConversationInput, creds: Creds, { newChat, agreementId, createIfMissing }?: {
|
|
54
|
-
newChat?: boolean;
|
|
55
|
-
agreementId?: string;
|
|
56
|
-
createIfMissing?: boolean;
|
|
57
|
-
}): Promise<OpenConversationResult>;
|
|
58
|
-
export interface SendChatMessageInput {
|
|
59
|
-
/**
|
|
60
|
-
* Chat to send in. Required unless `to` names a person you already
|
|
61
|
-
* share a conversation with.
|
|
62
|
-
*/
|
|
63
|
-
chatId?: string;
|
|
64
|
-
/**
|
|
65
|
-
* User or agent id of someone you already have a conversation with.
|
|
66
|
-
* Resolves that pair room and sends. Does not open contact. Ignored
|
|
67
|
-
* when `chatId` is set (`chatId` wins).
|
|
68
|
-
*/
|
|
69
|
-
to?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Recipient id. Optional: when omitted, the one other participant in the
|
|
72
|
-
* room is the receiver; with several, the backend infers one when Jev is
|
|
73
|
-
* confident. Otherwise it stores room context without a specific wake.
|
|
74
|
-
* Explicit addressing is required for a guaranteed target.
|
|
75
|
-
*/
|
|
76
|
-
receiverId?: string;
|
|
77
|
-
text: string;
|
|
78
|
-
messageId: string;
|
|
79
|
-
entryType?: string;
|
|
80
|
-
contentType?: string;
|
|
81
|
-
underAgreementId?: string;
|
|
82
|
-
/** Governing service agreement; independent of representation. */
|
|
83
|
-
conversationAgreementId?: string;
|
|
84
|
-
taskId?: string;
|
|
85
|
-
/** What this text drew on. See {@link WriteSource}. */
|
|
86
|
-
sources?: WriteSource[];
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Where the server says a send landed.
|
|
90
|
-
*
|
|
91
|
-
* The contract's own shape, re-exported rather than restated: it shipped in
|
|
92
|
-
* contracts 0.7.7, and the hand-written stand-in that carried it until then is
|
|
93
|
-
* gone. What the fields mean is worth keeping in front of a caller, because
|
|
94
|
-
* the receipt is easy to over-read:
|
|
95
|
-
*
|
|
96
|
-
* The caller may omit the receiver and have it inferred, or have it widened to
|
|
97
|
-
* the room's humans, so the destination is the server's answer rather than the
|
|
98
|
-
* caller's request. `accepted` is the message being created — not a claim that
|
|
99
|
-
* anyone read it. Routing is asynchronous and unconfirmed by this receipt; the
|
|
100
|
-
* `wakesAgent` field that once rode along was removed for exactly that reason
|
|
101
|
-
* and is not coming back.
|
|
102
|
-
*/
|
|
103
|
-
export type MessageDelivery = WireMessageDelivery & {
|
|
104
|
-
assignee?: string | null;
|
|
105
|
-
reachable?: 'yes' | 'no';
|
|
106
|
-
reason?: string;
|
|
107
|
-
lastRead?: string | null;
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* The send result as a caller here holds it.
|
|
111
|
-
*
|
|
112
|
-
* `delivery` is optional on the way in and required in the contract, which is
|
|
113
|
-
* not a disagreement about the wire: this client is installed independently of
|
|
114
|
-
* the backend it talks to, and a server from before the field existed answers
|
|
115
|
-
* without one.
|
|
116
|
-
*/
|
|
117
|
-
export type SendChatMessageResult = Omit<WireSendChatMessageResult, 'delivery'> & {
|
|
118
|
-
delivery?: MessageDelivery;
|
|
119
|
-
/**
|
|
120
|
-
* What covers this message, when something counts it: a hire's shared
|
|
121
|
-
* allowance, or a listed agent's free messages for a stranger ("2 of 3 free
|
|
122
|
-
* messages used with seo-growth."). Absent for ordinary conversation.
|
|
123
|
-
*/
|
|
124
|
-
coverage?: ConversationCoverageReceipt;
|
|
125
|
-
};
|
|
126
|
-
/** The server's receipt for a counted message. */
|
|
127
|
-
export interface ConversationCoverageReceipt {
|
|
128
|
-
agreementId: string;
|
|
129
|
-
messagesUsed: number;
|
|
130
|
-
messageLimit: number | null;
|
|
131
|
-
message: string;
|
|
132
|
-
}
|
|
133
|
-
export type ContextTemporal = 'from-now' | 'from-start';
|
|
134
|
-
export interface AddChatMemberInput {
|
|
135
|
-
chatId: string;
|
|
136
|
-
memberId: string;
|
|
137
|
-
memberType: 'user' | 'agent';
|
|
138
|
-
temporal?: ContextTemporal;
|
|
139
|
-
}
|
|
140
|
-
export interface ChatPendingAdmission {
|
|
141
|
-
admissionId: string;
|
|
142
|
-
agentId: string;
|
|
143
|
-
issuerId: string;
|
|
144
|
-
invitedBy: 'user' | 'agent';
|
|
145
|
-
temporal: ContextTemporal;
|
|
146
|
-
status: 'pending' | 'approved' | 'rejected';
|
|
147
|
-
approvals?: unknown[];
|
|
148
|
-
createdAt?: string;
|
|
149
|
-
}
|
|
150
|
-
export type AddChatMemberResult = {
|
|
151
|
-
status: 'pending';
|
|
152
|
-
pendingAdmission: ChatPendingAdmission;
|
|
153
|
-
} | {
|
|
154
|
-
status: 'approved';
|
|
155
|
-
chat?: unknown;
|
|
156
|
-
} | {
|
|
157
|
-
chat?: unknown;
|
|
158
|
-
};
|
|
159
|
-
/**
|
|
160
|
-
* POST /chats/:chatId/members — add user/agent; agent-invite may return pending admission.
|
|
161
|
-
*/
|
|
162
|
-
export declare function addChatMember(input: AddChatMemberInput, creds: Creds): Promise<AddChatMemberResult>;
|
|
163
|
-
/**
|
|
164
|
-
* POST /chats/:chatId/messages, or POST /chats/messages when `to` names
|
|
165
|
-
* an existing participant and `chatId` is omitted.
|
|
166
|
-
*/
|
|
167
|
-
export declare function sendChatMessage(input: SendChatMessageInput, creds: Creds): Promise<SendChatMessageResult>;
|
|
168
|
-
export declare function listMyChats(creds: Creds, filters?: {
|
|
169
|
-
participantId?: string;
|
|
170
|
-
readable?: boolean;
|
|
171
|
-
}): Promise<ChatSummary[]>;
|
|
172
|
-
export declare class ChatClient {
|
|
173
|
-
private creds;
|
|
174
|
-
/**
|
|
175
|
-
* @param operatorKey Agent-scoped or fleet operator key.
|
|
176
|
-
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
177
|
-
*/
|
|
178
|
-
constructor(operatorKey: string, agentId?: string);
|
|
179
|
-
open(participant: string | OpenConversationInput, opts?: {
|
|
180
|
-
newChat?: boolean;
|
|
181
|
-
agreementId?: string;
|
|
182
|
-
createIfMissing?: boolean;
|
|
183
|
-
}): Promise<OpenConversationResult>;
|
|
184
|
-
addMember(input: AddChatMemberInput): Promise<AddChatMemberResult>;
|
|
185
|
-
sendMessage(input: SendChatMessageInput): Promise<SendChatMessageResult>;
|
|
186
|
-
listMine(): Promise<ChatReadDto[]>;
|
|
187
|
-
}
|