@ziggs-ai/api-client 0.38.2 → 0.39.1

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +14 -36
  3. package/dist/capabilities/agreementVerbs.js +13 -21
  4. package/dist/capabilities/agreements.d.ts +2 -3
  5. package/dist/capabilities/agreements.js +7 -42
  6. package/dist/capabilities/artifacts.js +41 -43
  7. package/dist/capabilities/connections.js +10 -7
  8. package/dist/capabilities/context.d.ts +1 -1
  9. package/dist/capabilities/context.js +41 -45
  10. package/dist/capabilities/discovery.js +5 -5
  11. package/dist/capabilities/everyday.d.ts +1 -1
  12. package/dist/capabilities/everyday.js +1 -2
  13. package/dist/capabilities/index.d.ts +1 -2
  14. package/dist/capabilities/index.js +1 -2
  15. package/dist/capabilities/introductions.d.ts +1 -1
  16. package/dist/capabilities/introductions.js +3 -11
  17. package/dist/capabilities/links.d.ts +1 -9
  18. package/dist/capabilities/links.js +7 -28
  19. package/dist/capabilities/mcpBriefs.js +22 -28
  20. package/dist/capabilities/messaging.d.ts +9 -0
  21. package/dist/capabilities/messaging.js +11 -0
  22. package/dist/capabilities/nextCall.js +8 -17
  23. package/dist/capabilities/sources.d.ts +2 -3
  24. package/dist/capabilities/sources.js +1 -2
  25. package/dist/capabilities/tasks.js +1 -1
  26. package/dist/capabilities/toolLane.js +2 -2
  27. package/dist/engagementGuide.d.ts +2 -2
  28. package/dist/engagementGuide.js +6 -6
  29. package/dist/handles.d.ts +1 -1
  30. package/dist/handles.js +2 -2
  31. package/dist/http/AgreementClient.d.ts +17 -64
  32. package/dist/http/AgreementClient.js +14 -65
  33. package/dist/http/ArtifactsClient.d.ts +17 -19
  34. package/dist/http/ArtifactsClient.js +30 -62
  35. package/dist/http/ConnectionsClient.d.ts +6 -7
  36. package/dist/http/ConnectionsClient.js +5 -3
  37. package/dist/http/ContextDiscoveryClient.d.ts +2 -2
  38. package/dist/http/ContextGrantsClient.d.ts +8 -15
  39. package/dist/http/ContextGrantsClient.js +3 -5
  40. package/dist/http/ContextOpenClient.d.ts +5 -6
  41. package/dist/http/ContextOpenClient.js +11 -4
  42. package/dist/http/ContextReadClient.d.ts +23 -27
  43. package/dist/http/ContextReadClient.js +24 -23
  44. package/dist/http/ExecutionContextClient.d.ts +5 -1
  45. package/dist/http/ExecutionContextClient.js +48 -1
  46. package/dist/http/IntroductionsClient.d.ts +6 -4
  47. package/dist/http/MessageRecordsClient.d.ts +36 -0
  48. package/dist/http/MessageRecordsClient.js +30 -0
  49. package/dist/http/SessionsClient.d.ts +5 -5
  50. package/dist/http/SessionsClient.js +6 -6
  51. package/dist/http/TaskClient.d.ts +11 -5
  52. package/dist/http/TaskClient.js +11 -34
  53. package/dist/http/agreementFlows.d.ts +2 -3
  54. package/dist/http/agreementFlows.js +5 -5
  55. package/dist/http/index.d.ts +3 -5
  56. package/dist/http/index.js +3 -4
  57. package/dist/http/operatorHeaders.d.ts +3 -0
  58. package/dist/http/operatorHeaders.js +14 -5
  59. package/dist/inboxAck.d.ts +2 -2
  60. package/dist/inboxAck.js +2 -2
  61. package/dist/index.d.ts +4 -4
  62. package/dist/index.js +2 -2
  63. package/dist/types.d.ts +15 -55
  64. package/package.json +2 -2
  65. package/dist/capabilities/chat.d.ts +0 -43
  66. package/dist/capabilities/chat.js +0 -196
  67. package/dist/capabilities/chatListFields.d.ts +0 -14
  68. package/dist/capabilities/chatListFields.js +0 -61
  69. package/dist/http/ChatClient.d.ts +0 -187
  70. package/dist/http/ChatClient.js +0 -219
  71. package/dist/http/MessagesClient.d.ts +0 -40
  72. package/dist/http/MessagesClient.js +0 -81
@@ -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
- }
@@ -1,219 +0,0 @@
1
- import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
2
- import { runtimeLog } from '../shared/runtimeLog.js';
3
- import { getBackendUrl } from '../utils/urlUtils.js';
4
- import { throwApiError } from '../shared/apiError.js';
5
- import { buildCredsHeaders } from './operatorHeaders.js';
6
- // Auth, agent and the wake's lane, from the one helper — see operatorHeaders.
7
- const buildHeaders = buildCredsHeaders;
8
- function assertCreds(creds, op) {
9
- if (!creds?.operatorKey)
10
- throw new Error(`operatorKey is required for ${op}`);
11
- if (!creds?.agentId && !decodeOperatorKeyClaims(creds.operatorKey)?.connectionClientId)
12
- throw new Error(`agentId is required for ${op}`);
13
- }
14
- export async function openConversation(participant, creds, { newChat = false, agreementId, createIfMissing } = {}) {
15
- const input = typeof participant === 'string'
16
- ? { participantId: participant, ...(newChat ? { newChat } : {}), ...(agreementId ? { agreementId } : {}), ...(createIfMissing !== undefined ? { createIfMissing } : {}) }
17
- : participant;
18
- const resumeHire = !!input?.chatId && !!input.agreementId;
19
- if (resumeHire && (input.participantId || input.participantIds || input.newChat !== undefined || input.name !== undefined || input.idempotencyKey !== undefined || input.createIfMissing !== undefined)) {
20
- throw new Error('To resume hire admission, pass only chatId and agreementId');
21
- }
22
- if (!input || (!resumeHire && !input.participantId && !input.participantIds?.length)) {
23
- throw new Error('participantId is required, or use participantIds to invite people');
24
- }
25
- assertCreds(creds, 'open conversation');
26
- const route = resumeHire
27
- ? `/chats/${encodeURIComponent(input.chatId)}/hires/${encodeURIComponent(input.agreementId)}/admission`
28
- : '/chats';
29
- const res = await fetch(`${getBackendUrl()}${route}`, {
30
- method: 'POST',
31
- headers: buildHeaders(creds),
32
- // Only sent when asked for: an older backend ignores the field, so a
33
- // caller that never wants a separate room behaves identically either way.
34
- body: JSON.stringify(resumeHire ? {} : input),
35
- });
36
- if (!res.ok) {
37
- const body = await res.text().catch(() => '');
38
- throwApiError(res, body, `openConversation failed: ${res.status} ${res.statusText}`);
39
- }
40
- const data = await res.json().catch(() => null);
41
- if (!data?.['chatId'] || typeof data['chatId'] !== 'string') {
42
- throw new Error('Invalid response: expected { chatId } from POST /chats');
43
- }
44
- // left UNDEFINED rather than defaulted when the backend does not
45
- // send it. A backend from before this field cannot be assumed to have created
46
- // the room — defaulting to false would report a reused conversation as fresh,
47
- // which is worse than saying nothing.
48
- if (resumeHire && (data.chatId !== input.chatId || data.agreementId !== input.agreementId || !['ready', 'admission_failed'].includes(String(data.status)))) {
49
- throw new Error('Invalid hire-admission response: expected the selected chat, hire and admission status');
50
- }
51
- const reused = data['reused'];
52
- const name = data['name'];
53
- const invited = Array.isArray(data['invited'])
54
- ? data['invited']
55
- : undefined;
56
- return {
57
- chatId: data['chatId'],
58
- ...(resumeHire ? {
59
- agreementId: input.agreementId,
60
- status: data.status,
61
- ...(typeof data.reason === 'string' ? { reason: data.reason } : {}),
62
- } : {}),
63
- ...(typeof reused === 'boolean' ? { reused } : {}),
64
- ...(typeof name === 'string' && name ? { name } : {}),
65
- ...(invited ? { invited } : {}),
66
- };
67
- }
68
- function coverageFrom(value) {
69
- if (!value || typeof value !== 'object')
70
- return null;
71
- const row = value;
72
- return typeof row['agreementId'] === 'string' &&
73
- typeof row['message'] === 'string' &&
74
- typeof row['messagesUsed'] === 'number' &&
75
- (row['messageLimit'] === null || typeof row['messageLimit'] === 'number')
76
- ? {
77
- agreementId: row['agreementId'],
78
- messagesUsed: row['messagesUsed'],
79
- messageLimit: row['messageLimit'],
80
- message: row['message'],
81
- }
82
- : null;
83
- }
84
- /**
85
- * POST /chats/:chatId/members — add user/agent; agent-invite may return pending admission.
86
- */
87
- export async function addChatMember(input, creds) {
88
- assertCreds(creds, 'add chat member');
89
- const res = await fetch(`${getBackendUrl()}/chats/${encodeURIComponent(input.chatId)}/members`, {
90
- method: 'POST',
91
- headers: buildHeaders(creds),
92
- body: JSON.stringify({
93
- chatId: input.chatId,
94
- memberId: input.memberId,
95
- memberType: input.memberType,
96
- temporal: input.temporal,
97
- }),
98
- });
99
- const body = await res.text().catch(() => '');
100
- if (!res.ok) {
101
- throwApiError(res, body, `addChatMember failed: ${res.status} ${res.statusText}`);
102
- }
103
- const data = JSON.parse(body);
104
- if (data['status'] === 'pending' && data['pendingAdmission']) {
105
- return {
106
- status: 'pending',
107
- pendingAdmission: data['pendingAdmission'],
108
- };
109
- }
110
- if (data['status'] === 'approved') {
111
- return { status: 'approved', chat: data['chat'] };
112
- }
113
- return { chat: data['chat'] };
114
- }
115
- /**
116
- * POST /chats/:chatId/messages, or POST /chats/messages when `to` names
117
- * an existing participant and `chatId` is omitted.
118
- */
119
- export async function sendChatMessage(input, creds) {
120
- assertCreds(creds, 'send chat message');
121
- const chatId = typeof input.chatId === 'string' ? input.chatId.trim() : '';
122
- const to = typeof input.to === 'string' ? input.to.trim() : '';
123
- if (!chatId && !to) {
124
- throw new Error('chatId or to is required for send chat message');
125
- }
126
- const entryType = input.entryType ?? 'message';
127
- const contentType = input.contentType ?? 'text';
128
- const personSend = !chatId && Boolean(to);
129
- const url = personSend
130
- ? `${getBackendUrl()}/chats/messages`
131
- : `${getBackendUrl()}/chats/${encodeURIComponent(chatId)}/messages`;
132
- const res = await fetch(url, {
133
- method: 'POST',
134
- headers: buildHeaders(creds),
135
- body: JSON.stringify({
136
- ...(personSend ? { to } : { chatId }),
137
- messageId: input.messageId,
138
- text: input.text,
139
- entryType,
140
- contentType,
141
- // Canonical wire shape is `{ id, type? }`; bare strings are still
142
- // accepted by the backend after edge normalize, but first-party
143
- // clients send the object.
144
- receiver: input.receiverId ? { id: input.receiverId } : undefined,
145
- underAgreementId: input.underAgreementId,
146
- conversationAgreementId: input.conversationAgreementId,
147
- taskId: input.taskId,
148
- ...(input.sources ? { sources: input.sources } : {}),
149
- }),
150
- });
151
- if (!res.ok) {
152
- const body = await res.text().catch(() => '');
153
- throwApiError(res, body, `sendChatMessage failed: ${res.status} ${res.statusText}`);
154
- }
155
- const data = (await res.json().catch(() => null));
156
- const delivery = data?.['delivery'];
157
- const coverage = coverageFrom(data?.['coverage']);
158
- return {
159
- success: Boolean(data?.['success']),
160
- message: String(data?.['message'] ?? 'ok'),
161
- messageId: String(data?.['messageId'] ?? input.messageId),
162
- chatId: String(data?.['chatId'] ?? chatId),
163
- // Passed through as the server reported it, or absent. Never reconstructed
164
- // from the request: the whole point is that the destination may not be the
165
- // one the caller named.
166
- ...(delivery && typeof delivery === 'object'
167
- ? { delivery: delivery }
168
- : {}),
169
- ...(coverage ? { coverage } : {}),
170
- };
171
- }
172
- export async function listMyChats(creds, filters = {}) {
173
- assertCreds(creds, 'list my chats');
174
- const query = new URLSearchParams();
175
- if (filters.participantId)
176
- query.set('participantId', filters.participantId);
177
- if (filters.readable !== undefined)
178
- query.set('readable', String(filters.readable));
179
- const qs = query.toString();
180
- // empty array only for a genuine empty 200; any failure (non-2xx /
181
- // network) throws so ziggs_chat_list reports a real error instead of "you
182
- // have no chats". HTTP failures are ApiError (status/body/code).
183
- let res;
184
- try {
185
- res = await fetch(`${getBackendUrl()}/chats/mine${qs ? `?${qs}` : ''}`, {
186
- method: 'GET',
187
- headers: buildHeaders(creds),
188
- });
189
- }
190
- catch (e) {
191
- runtimeLog.warn('ChatClient', `⚠️ listMyChats failed: ${e.message}`);
192
- throw new Error(`GET /chats/mine network error: ${e.message}`);
193
- }
194
- if (!res.ok) {
195
- const body = await res.text().catch(() => '');
196
- runtimeLog.warn('ChatClient', `⚠️ listMyChats failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
197
- throwApiError(res, body, `GET /chats/mine failed: ${res.status}`);
198
- }
199
- const data = await res.json().catch(() => null);
200
- return Array.isArray(data?.['chats']) ? data['chats'] : [];
201
- }
202
- export class ChatClient {
203
- creds;
204
- /**
205
- * @param operatorKey Agent-scoped or fleet operator key.
206
- * @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
207
- */
208
- constructor(operatorKey, agentId) {
209
- if (!operatorKey)
210
- throw new Error('ChatClient: operatorKey is required');
211
- // agentId may be absent for agent-scoped keys; the Creds-based
212
- // standalone functions still assert it per call.
213
- this.creds = { operatorKey, agentId };
214
- }
215
- open(participant, opts) { return openConversation(participant, this.creds, opts); }
216
- addMember(input) { return addChatMember(input, this.creds); }
217
- sendMessage(input) { return sendChatMessage(input, this.creds); }
218
- listMine() { return listMyChats(this.creds); }
219
- }
@@ -1,40 +0,0 @@
1
- export interface ListMessagesOptions {
2
- /** ISO timestamp; only entries with `timestamp > after` are returned. */
3
- after?: string;
4
- /** Page size, default 100, hard cap 1000. */
5
- limit?: number;
6
- }
7
- /**
8
- * The chat message URL.
9
- *
10
- * `after` is a forward delta: the server returns that page only when both
11
- * `after` and `direction=forward` are set. Omitting both is the newest page,
12
- * which is how a person asks what just happened without already knowing a time.
13
- */
14
- export declare function messagesListUrl(base: string, chatId: string, opts?: ListMessagesOptions): string;
15
- export interface ListMessagesResult {
16
- messages: unknown[];
17
- latestSequence: string | null;
18
- }
19
- /**
20
- * Read-side client for forward-delta message reads.
21
- *
22
- * Hits the canonical `GET /chats/:chatId/messages?after=&direction=forward&limit=`
23
- * endpoint and returns a `{ messages, latestSequence }` envelope
24
- * (`chat-messaging.controller.listMessagesForward` →
25
- * `MessagesService.listForChat`).
26
- */
27
- export declare class MessagesClient {
28
- private readonly operatorKey;
29
- private readonly agentId;
30
- constructor(operatorKey: string, agentId: string);
31
- list(chatId: string, opts?: ListMessagesOptions): Promise<ListMessagesResult>;
32
- /**
33
- * Newest page. No `after`, so the server walks backward from now.
34
- * `list` is the forward delta and cannot answer "what just happened".
35
- */
36
- listLatest(chatId: string, opts?: {
37
- limit?: number;
38
- }): Promise<ListMessagesResult>;
39
- private _headers;
40
- }
@@ -1,81 +0,0 @@
1
- import { decodeOperatorKeyClaims } from '../shared/operatorKey.js';
2
- import { buildOperatorHeaders } from './operatorHeaders.js';
3
- import { getBackendUrl } from '../utils/urlUtils.js';
4
- import { throwApiError } from '../shared/apiError.js';
5
- /**
6
- * The chat message URL.
7
- *
8
- * `after` is a forward delta: the server returns that page only when both
9
- * `after` and `direction=forward` are set. Omitting both is the newest page,
10
- * which is how a person asks what just happened without already knowing a time.
11
- */
12
- export function messagesListUrl(base, chatId, opts = {}) {
13
- const url = new URL(`${base}/chats/${encodeURIComponent(chatId)}/messages`);
14
- if (opts.after) {
15
- url.searchParams.set('direction', 'forward');
16
- url.searchParams.set('after', opts.after);
17
- }
18
- if (opts.limit != null)
19
- url.searchParams.set('limit', String(opts.limit));
20
- return url.toString();
21
- }
22
- /**
23
- * Read-side client for forward-delta message reads.
24
- *
25
- * Hits the canonical `GET /chats/:chatId/messages?after=&direction=forward&limit=`
26
- * endpoint and returns a `{ messages, latestSequence }` envelope
27
- * (`chat-messaging.controller.listMessagesForward` →
28
- * `MessagesService.listForChat`).
29
- */
30
- export class MessagesClient {
31
- operatorKey;
32
- agentId;
33
- constructor(operatorKey, agentId) {
34
- if (!operatorKey)
35
- throw new Error('MessagesClient: operatorKey is required');
36
- if (!agentId && !decodeOperatorKeyClaims(operatorKey)?.connectionClientId)
37
- throw new Error('MessagesClient: agentId is required (operator-token impersonation)');
38
- this.operatorKey = operatorKey;
39
- this.agentId = agentId;
40
- }
41
- async list(chatId, opts = {}) {
42
- if (!chatId)
43
- return { messages: [], latestSequence: null };
44
- // Forward-delta read mode. Backend routes to `listMessagesForward`
45
- // only when BOTH `after` and `direction=forward` are present; without
46
- // `after` the same handler falls into backward pagination and returns
47
- // a different envelope. Default `after` to the unix epoch so the
48
- // forward branch always wins when callers want "from the beginning".
49
- const url = messagesListUrl(getBackendUrl(), chatId, {
50
- after: opts.after || '1970-01-01T00:00:00.000Z',
51
- limit: opts.limit,
52
- });
53
- const res = await fetch(url, { headers: this._headers() });
54
- if (!res.ok) {
55
- const body = await res.text().catch(() => '');
56
- // Callers branch on ApiError.status (404 = chat deleted/not visible).
57
- throwApiError(res, body, `MessagesClient.list failed: ${res.status}`);
58
- }
59
- return (await res.json());
60
- }
61
- /**
62
- * Newest page. No `after`, so the server walks backward from now.
63
- * `list` is the forward delta and cannot answer "what just happened".
64
- */
65
- async listLatest(chatId, opts = {}) {
66
- if (!chatId)
67
- return { messages: [], latestSequence: null };
68
- const res = await fetch(messagesListUrl(getBackendUrl(), chatId, { limit: opts.limit }), { headers: this._headers() });
69
- if (!res.ok) {
70
- const body = await res.text().catch(() => '');
71
- throwApiError(res, body, `MessagesClient.listLatest failed: ${res.status}`);
72
- }
73
- return (await res.json());
74
- }
75
- _headers() {
76
- return {
77
- 'content-type': 'application/json',
78
- ...buildOperatorHeaders(this.operatorKey, this.agentId),
79
- };
80
- }
81
- }