@ziggs-ai/api-client 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/ConnectionManager.d.ts +1 -0
  2. package/dist/ConnectionManager.js +18 -4
  3. package/dist/http/AgreementClient.js +33 -13
  4. package/dist/http/ChatClient.d.ts +20 -0
  5. package/dist/http/ChatClient.js +39 -4
  6. package/dist/http/ContextDiscoveryClient.d.ts +23 -0
  7. package/dist/http/ContextDiscoveryClient.js +35 -0
  8. package/dist/http/ContextReadClient.d.ts +33 -0
  9. package/dist/http/ContextReadClient.js +54 -0
  10. package/dist/http/MessagesClient.d.ts +6 -2
  11. package/dist/http/MessagesClient.js +14 -6
  12. package/dist/http/TaskClient.js +12 -3
  13. package/dist/http/index.d.ts +4 -0
  14. package/dist/http/index.js +2 -0
  15. package/dist/types.d.ts +17 -7
  16. package/dist/types.js +1 -1
  17. package/dist/websocket/ControlSocket.js +2 -1
  18. package/dist/websocket/WebSocketClient.js +23 -7
  19. package/package.json +15 -9
  20. package/src/ConnectionManager.ts +172 -0
  21. package/src/http/AgentSearchClient.ts +115 -0
  22. package/src/http/AgreementClient.ts +721 -0
  23. package/src/http/ArtifactsClient.ts +133 -0
  24. package/src/http/ChatClient.ts +147 -0
  25. package/src/http/ContextDiscoveryClient.ts +52 -0
  26. package/src/http/ContextReadClient.ts +83 -0
  27. package/src/http/MarketplaceClient.ts +94 -0
  28. package/src/http/MessagesClient.ts +71 -0
  29. package/src/http/ScopeClient.ts +64 -0
  30. package/src/http/TaskClient.ts +450 -0
  31. package/src/http/TelemetryClient.ts +57 -0
  32. package/src/http/index.ts +26 -0
  33. package/src/index.ts +27 -0
  34. package/src/shared/runtimeLog.ts +68 -0
  35. package/src/types.ts +158 -0
  36. package/src/utils/urlUtils.ts +9 -0
  37. package/src/websocket/ControlSocket.ts +51 -0
  38. package/src/websocket/WebSocketClient.ts +315 -0
  39. package/src/websocket/index.ts +1 -0
@@ -25,6 +25,7 @@ export declare class ConnectionManager {
25
25
  private _entries;
26
26
  private _active;
27
27
  private _meta;
28
+ private _waking;
28
29
  constructor({ maxActive, idleTimeoutMs, control }?: ConnectionManagerOptions);
29
30
  register(id: string, openFn: OpenFn, closeFn: CloseFn, meta?: ConnectionManagerMeta): void;
30
31
  start(): void;
@@ -8,6 +8,7 @@ export class ConnectionManager {
8
8
  _entries;
9
9
  _active;
10
10
  _meta;
11
+ _waking;
11
12
  constructor({ maxActive = 50, idleTimeoutMs = 60_000, control } = {}) {
12
13
  this.maxActive = maxActive;
13
14
  this.idleTimeoutMs = idleTimeoutMs;
@@ -16,6 +17,7 @@ export class ConnectionManager {
16
17
  this._entries = new Map();
17
18
  this._active = new Map();
18
19
  this._meta = new Map();
20
+ this._waking = new Map();
19
21
  }
20
22
  register(id, openFn, closeFn, meta) {
21
23
  if (!id)
@@ -48,15 +50,27 @@ export class ConnectionManager {
48
50
  this._resetTimer(id);
49
51
  return existing.handle;
50
52
  }
53
+ const pending = this._waking.get(id);
54
+ if (pending)
55
+ return pending;
51
56
  const entry = this._entries.get(id);
52
57
  if (!entry)
53
58
  throw new Error(`[ConnectionManager] unknown id: "${id}"`);
54
59
  if (this._active.size >= this.maxActive)
55
60
  await this._evictLRU();
56
- const handle = await entry.openFn();
57
- this._active.set(id, { handle, timer: null, lastActive: Date.now() });
58
- this._scheduleIdle(id);
59
- return handle;
61
+ const wakePromise = (async () => {
62
+ try {
63
+ const handle = await entry.openFn();
64
+ this._active.set(id, { handle, timer: null, lastActive: Date.now() });
65
+ this._scheduleIdle(id);
66
+ return handle;
67
+ }
68
+ finally {
69
+ this._waking.delete(id);
70
+ }
71
+ })();
72
+ this._waking.set(id, wakePromise);
73
+ return wakePromise;
60
74
  }
61
75
  async sleep(id) {
62
76
  const entry = this._active.get(id);
@@ -44,7 +44,9 @@ export async function proposeAgreement(proposalData, creds) {
44
44
  const headers = buildHeaders(creds);
45
45
  if (idempotencyKey)
46
46
  headers['Idempotency-Key'] = idempotencyKey;
47
- const res = await fetch(`${getAgreementBaseUrl()}/propose`, {
47
+ // ZIG-207: canonical REST path. Backend keeps /propose serving the
48
+ // identical handler with a Deprecation/Sunset header until PR-F removes it.
49
+ const res = await fetch(`${getAgreementBaseUrl()}/proposals`, {
48
50
  method: 'POST',
49
51
  headers,
50
52
  body: JSON.stringify(bodyData),
@@ -55,7 +57,7 @@ export async function proposeAgreement(proposalData, creds) {
55
57
  }
56
58
  const data = await res.json().catch(() => null);
57
59
  if (!data?.['agreement']) {
58
- throw new Error('Invalid response: expected { agreement } from /agreements/propose');
60
+ throw new Error('Invalid response: expected { agreement } from POST /agreements/proposals');
59
61
  }
60
62
  return data['agreement'];
61
63
  }
@@ -70,15 +72,26 @@ export async function proposeBroadcast(input, creds) {
70
72
  export async function delegateAgreement(proposalData, creds) {
71
73
  if (!proposalData)
72
74
  throw new Error('Proposal data is required for delegation');
75
+ if (!proposalData.parentAgreementId)
76
+ throw new Error('parentAgreementId is required for delegation');
73
77
  assertCreds(creds, 'proposal creation');
74
- const { idempotencyKey, ...bodyData } = proposalData;
78
+ const { idempotencyKey, parentAgreementId } = proposalData;
75
79
  const headers = buildHeaders(creds);
76
80
  if (idempotencyKey)
77
81
  headers['Idempotency-Key'] = idempotencyKey;
78
- const res = await fetch(`${getAgreementBaseUrl()}/delegate`, {
82
+ // ZIG-207 / ZIG-270: parentAgreementId in path AND body. Backend's
83
+ // `DelegateTaskDto` validates `parentAgreementId` as a required string;
84
+ // controller (`POST /agreements/:parentAgreementId/delegations`) merges
85
+ // path over body, so duplicating the value is harmless. Stripping it
86
+ // from the body (as ZIG-207 #72 did) trips DTO validation and returns
87
+ // `400: parentAgreementId must be a string` → infinite retry loop in
88
+ // `agreement_subcontract`. Send both, let the path win.
89
+ const bodyWithParent = { ...proposalData };
90
+ delete bodyWithParent.idempotencyKey;
91
+ const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(parentAgreementId)}/delegations`, {
79
92
  method: 'POST',
80
93
  headers,
81
- body: JSON.stringify(bodyData),
94
+ body: JSON.stringify(bodyWithParent),
82
95
  });
83
96
  if (!res.ok) {
84
97
  const body = await res.text().catch(() => '');
@@ -86,7 +99,7 @@ export async function delegateAgreement(proposalData, creds) {
86
99
  }
87
100
  const data = await res.json().catch(() => null);
88
101
  if (!data?.['agreement']) {
89
- throw new Error('Invalid response: expected { agreement } from /agreements/delegate');
102
+ throw new Error('Invalid response: expected { agreement } from POST /agreements/:parentAgreementId/delegations');
90
103
  }
91
104
  return data['agreement'];
92
105
  }
@@ -154,7 +167,8 @@ export async function getAgreementStatus(agreementId, creds) {
154
167
  export async function listAgreements(filters = {}, creds) {
155
168
  assertCreds(creds, 'list agreements');
156
169
  try {
157
- const url = new URL(`${getAgreementBaseUrl()}/list`);
170
+ // Canonical query path: GET /agreements?scope=&status=&engagementKind=&...
171
+ const url = new URL(getAgreementBaseUrl());
158
172
  if (filters.status)
159
173
  url.searchParams.set('status', filters.status);
160
174
  if (filters.engagementKind)
@@ -180,7 +194,10 @@ export async function listAgreements(filters = {}, creds) {
180
194
  export async function getMyAgreements(filters = {}, creds) {
181
195
  assertCreds(creds, 'get my agreements');
182
196
  try {
183
- const url = new URL(`${getAgreementBaseUrl()}/mine`);
197
+ // Canonical query path: GET /agreements?scope=mine returns the enriched
198
+ // shape (tasks + originChatId + isYou flags).
199
+ const url = new URL(getAgreementBaseUrl());
200
+ url.searchParams.set('scope', 'mine');
184
201
  if (filters.engagementKind)
185
202
  url.searchParams.set('engagementKind', filters.engagementKind);
186
203
  if (filters.proposalStatus)
@@ -229,7 +246,8 @@ export async function createAgreement(body, creds) {
229
246
  if (!body)
230
247
  throw new Error('Body is required for agreement creation');
231
248
  assertCreds(creds, 'agreement creation');
232
- const res = await fetch(`${getAgreementBaseUrl()}/create`, {
249
+ // Canonical REST create: POST /agreements.
250
+ const res = await fetch(getAgreementBaseUrl(), {
233
251
  method: 'POST',
234
252
  headers: buildHeaders(creds),
235
253
  body: JSON.stringify(body),
@@ -240,7 +258,7 @@ export async function createAgreement(body, creds) {
240
258
  }
241
259
  const data = await res.json().catch(() => null);
242
260
  if (!data?.['agreement']) {
243
- throw new Error('Invalid response: expected { ok, agreement } from /agreements/create');
261
+ throw new Error('Invalid response: expected { ok, agreement } from POST /agreements');
244
262
  }
245
263
  return data;
246
264
  }
@@ -248,8 +266,10 @@ export async function revokeAgreement(agreementId, creds) {
248
266
  if (!agreementId)
249
267
  throw new Error('agreementId is required for revocation');
250
268
  assertCreds(creds, 'agreement revocation');
251
- const res = await fetch(`${getAgreementBaseUrl()}/${agreementId}/revoke`, {
252
- method: 'POST',
269
+ // ZIG-207: canonical REST verb. Legacy POST /agreements/:id/revoke stays
270
+ // serving the identical handler (with Deprecation headers) until PR-F.
271
+ const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}`, {
272
+ method: 'DELETE',
253
273
  headers: buildHeaders(creds),
254
274
  });
255
275
  if (!res.ok) {
@@ -258,7 +278,7 @@ export async function revokeAgreement(agreementId, creds) {
258
278
  }
259
279
  const data = await res.json().catch(() => null);
260
280
  if (!data?.['agreement']) {
261
- throw new Error('Invalid response: expected { ok, agreement } from /agreements/:id/revoke');
281
+ throw new Error('Invalid response: expected { ok, agreement } from DELETE /agreements/:id');
262
282
  }
263
283
  return data;
264
284
  }
@@ -11,4 +11,24 @@ export interface ChatSummary {
11
11
  export declare function openConversation(participantId: string, creds: Creds): Promise<{
12
12
  chatId: string;
13
13
  }>;
14
+ export interface SendChatMessageInput {
15
+ chatId: string;
16
+ receiverId: string;
17
+ text: string;
18
+ messageId: string;
19
+ entryType?: string;
20
+ contentType?: string;
21
+ underAgreementId?: string;
22
+ }
23
+ export interface SendChatMessageResult {
24
+ success: boolean;
25
+ message: string;
26
+ messageId: string;
27
+ chatId: string;
28
+ }
29
+ /**
30
+ * POST /chats/:chatId/messages as an impersonated delegate agent.
31
+ * Requires operator token + X-Agent-Id (ZIG-222).
32
+ */
33
+ export declare function sendChatMessage(input: SendChatMessageInput, creds: Creds): Promise<SendChatMessageResult>;
14
34
  export declare function listMyChats(creds: Creds): Promise<ChatSummary[]>;
@@ -32,7 +32,8 @@ export async function openConversation(participantId, creds) {
32
32
  if (!participantId)
33
33
  throw new Error('participantId is required for openConversation');
34
34
  assertCreds(creds, 'open conversation');
35
- const res = await fetch(`${getBackendUrl()}/chat/openConversation`, {
35
+ // Canonical REST: POST /chats with { participantId }, returns { chatId }.
36
+ const res = await fetch(`${getBackendUrl()}/chats`, {
36
37
  method: 'POST',
37
38
  headers: buildHeaders(creds),
38
39
  body: JSON.stringify({ participantId }),
@@ -43,15 +44,49 @@ export async function openConversation(participantId, creds) {
43
44
  }
44
45
  const data = await res.json().catch(() => null);
45
46
  if (!data?.['chatId'] || typeof data['chatId'] !== 'string') {
46
- throw new Error('Invalid response: expected { chatId } from /chat/openConversation');
47
+ throw new Error('Invalid response: expected { chatId } from POST /chats');
47
48
  }
48
49
  return { chatId: data['chatId'] };
49
50
  }
51
+ /**
52
+ * POST /chats/:chatId/messages as an impersonated delegate agent.
53
+ * Requires operator token + X-Agent-Id (ZIG-222).
54
+ */
55
+ export async function sendChatMessage(input, creds) {
56
+ assertCreds(creds, 'send chat message');
57
+ const entryType = input.entryType ?? 'message';
58
+ const contentType = input.contentType ?? 'text';
59
+ const res = await fetch(`${getBackendUrl()}/chats/${encodeURIComponent(input.chatId)}/messages`, {
60
+ method: 'POST',
61
+ headers: buildHeaders(creds),
62
+ body: JSON.stringify({
63
+ chatId: input.chatId,
64
+ messageId: input.messageId,
65
+ text: input.text,
66
+ entryType,
67
+ contentType,
68
+ receiver: input.receiverId,
69
+ underAgreementId: input.underAgreementId,
70
+ }),
71
+ });
72
+ if (!res.ok) {
73
+ const body = await res.text().catch(() => '');
74
+ throwApiError(res, body, `sendChatMessage failed: ${res.status} ${res.statusText}`);
75
+ }
76
+ const data = (await res.json().catch(() => null));
77
+ return {
78
+ success: Boolean(data?.['success']),
79
+ message: String(data?.['message'] ?? 'ok'),
80
+ messageId: String(data?.['messageId'] ?? input.messageId),
81
+ chatId: String(data?.['chatId'] ?? input.chatId),
82
+ };
83
+ }
50
84
  export async function listMyChats(creds) {
51
85
  assertCreds(creds, 'list my chats');
52
86
  try {
53
- const res = await fetch(`${getBackendUrl()}/chat/listMyChats`, {
54
- method: 'POST',
87
+ // Canonical REST: GET /chats/mine.
88
+ const res = await fetch(`${getBackendUrl()}/chats/mine`, {
89
+ method: 'GET',
55
90
  headers: buildHeaders(creds),
56
91
  });
57
92
  if (!res.ok) {
@@ -0,0 +1,23 @@
1
+ import 'dotenv/config';
2
+ export interface ContextReachDescriptor {
3
+ grantId: string;
4
+ scope: {
5
+ kind: 'chat' | 'agreement' | 'org';
6
+ id: string;
7
+ };
8
+ temporal: 'from-now' | 'from-start';
9
+ watermarkAt: string;
10
+ expiresAt: string | null;
11
+ parentGrantId: string | null;
12
+ createdAt: string;
13
+ }
14
+ /**
15
+ * ZIG-412 discovery — scope descriptors only, no content.
16
+ */
17
+ export declare class ContextDiscoveryClient {
18
+ private readonly operatorKey;
19
+ private readonly agentId;
20
+ private readonly baseUrl;
21
+ constructor(operatorKey: string, agentId: string, baseUrl?: string);
22
+ discover(): Promise<ContextReachDescriptor[]>;
23
+ }
@@ -0,0 +1,35 @@
1
+ import 'dotenv/config';
2
+ import { getBackendUrl } from '../utils/urlUtils.js';
3
+ /**
4
+ * ZIG-412 discovery — scope descriptors only, no content.
5
+ */
6
+ export class ContextDiscoveryClient {
7
+ operatorKey;
8
+ agentId;
9
+ baseUrl;
10
+ constructor(operatorKey, agentId, baseUrl) {
11
+ if (!operatorKey) {
12
+ throw new Error('ContextDiscoveryClient: operatorKey is required');
13
+ }
14
+ if (!agentId) {
15
+ throw new Error('ContextDiscoveryClient: agentId is required (operator-token impersonation)');
16
+ }
17
+ this.operatorKey = operatorKey;
18
+ this.agentId = agentId;
19
+ this.baseUrl = baseUrl || getBackendUrl();
20
+ }
21
+ async discover() {
22
+ const res = await fetch(`${this.baseUrl}/context/discovery`, {
23
+ headers: {
24
+ Authorization: `Bearer ${this.operatorKey}`,
25
+ 'X-Agent-Id': this.agentId,
26
+ },
27
+ });
28
+ const body = await res.text().catch(() => '');
29
+ if (!res.ok) {
30
+ throw new Error(`ContextDiscoveryClient.discover ${res.status} ${body.slice(0, 200)}`);
31
+ }
32
+ const parsed = JSON.parse(body);
33
+ return parsed.reach ?? [];
34
+ }
35
+ }
@@ -0,0 +1,33 @@
1
+ import 'dotenv/config';
2
+ export type ContextReadType = 'messages' | 'artifacts' | 'agreements' | 'tasks';
3
+ export interface ContextReadQuery {
4
+ via: string;
5
+ cursor?: string;
6
+ limit?: number;
7
+ after?: string;
8
+ direction?: 'forward';
9
+ state?: string;
10
+ contextGrantId?: string;
11
+ }
12
+ export interface ContextReadEnvelope<T = unknown> {
13
+ type: ContextReadType;
14
+ via: {
15
+ kind: string;
16
+ id: string;
17
+ };
18
+ items: T[];
19
+ hasMore: boolean;
20
+ nextCursor: string | null;
21
+ latestSequence?: string | null;
22
+ }
23
+ /**
24
+ * Protocol-first uniform context reads (ZIG-427).
25
+ * Wraps `GET /context/read/:type` — one client, one envelope, four types.
26
+ */
27
+ export declare class ContextReadClient {
28
+ private readonly operatorKey;
29
+ private readonly agentId;
30
+ private readonly baseUrl;
31
+ constructor(operatorKey: string, agentId: string, baseUrl?: string);
32
+ read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
33
+ }
@@ -0,0 +1,54 @@
1
+ import 'dotenv/config';
2
+ import { getBackendUrl } from '../utils/urlUtils.js';
3
+ /**
4
+ * Protocol-first uniform context reads (ZIG-427).
5
+ * Wraps `GET /context/read/:type` — one client, one envelope, four types.
6
+ */
7
+ export class ContextReadClient {
8
+ operatorKey;
9
+ agentId;
10
+ baseUrl;
11
+ constructor(operatorKey, agentId, baseUrl) {
12
+ if (!operatorKey)
13
+ throw new Error('ContextReadClient: operatorKey is required');
14
+ if (!agentId) {
15
+ throw new Error('ContextReadClient: agentId is required (operator-token impersonation)');
16
+ }
17
+ this.operatorKey = operatorKey;
18
+ this.agentId = agentId;
19
+ this.baseUrl = baseUrl || getBackendUrl();
20
+ }
21
+ async read(type, query) {
22
+ if (!query.via?.trim()) {
23
+ throw new Error('ContextReadClient.read: via is required');
24
+ }
25
+ const url = new URL(`${this.baseUrl}/context/read/${encodeURIComponent(type)}`);
26
+ url.searchParams.set('via', query.via.trim());
27
+ if (query.cursor)
28
+ url.searchParams.set('cursor', query.cursor);
29
+ if (query.limit != null)
30
+ url.searchParams.set('limit', String(query.limit));
31
+ if (query.after)
32
+ url.searchParams.set('after', query.after);
33
+ if (query.direction)
34
+ url.searchParams.set('direction', query.direction);
35
+ if (query.state)
36
+ url.searchParams.set('state', query.state);
37
+ if (query.contextGrantId) {
38
+ url.searchParams.set('contextGrantId', query.contextGrantId);
39
+ }
40
+ const headers = {
41
+ Authorization: `Bearer ${this.operatorKey}`,
42
+ 'X-Agent-Id': this.agentId,
43
+ };
44
+ if (query.contextGrantId) {
45
+ headers['X-Context-Grant-Id'] = query.contextGrantId;
46
+ }
47
+ const res = await fetch(url.toString(), { headers });
48
+ const body = await res.text().catch(() => '');
49
+ if (!res.ok) {
50
+ throw new Error(`ContextReadClient.read ${type} ${res.status} ${body.slice(0, 200)}`);
51
+ }
52
+ return JSON.parse(body);
53
+ }
54
+ }
@@ -10,8 +10,12 @@ export interface ListMessagesResult {
10
10
  latestSequence: string | null;
11
11
  }
12
12
  /**
13
- * Read-side client for `GET /messages?chatId=&after=&limit=`. Replaces the
14
- * `history[]` slice of the old context blob.
13
+ * Read-side client for forward-delta message reads.
14
+ *
15
+ * Hits the canonical `GET /chats/:chatId/messages?after=&direction=forward&limit=`
16
+ * endpoint and returns a `{ messages, latestSequence }` envelope
17
+ * (`chat-messaging.controller.listMessagesForward` →
18
+ * `MessagesService.listForChat`).
15
19
  */
16
20
  export declare class MessagesClient {
17
21
  private readonly operatorKey;
@@ -1,8 +1,12 @@
1
1
  import 'dotenv/config';
2
2
  import { getBackendUrl } from '../utils/urlUtils.js';
3
3
  /**
4
- * Read-side client for `GET /messages?chatId=&after=&limit=`. Replaces the
5
- * `history[]` slice of the old context blob.
4
+ * Read-side client for forward-delta message reads.
5
+ *
6
+ * Hits the canonical `GET /chats/:chatId/messages?after=&direction=forward&limit=`
7
+ * endpoint and returns a `{ messages, latestSequence }` envelope
8
+ * (`chat-messaging.controller.listMessagesForward` →
9
+ * `MessagesService.listForChat`).
6
10
  */
7
11
  export class MessagesClient {
8
12
  operatorKey;
@@ -18,10 +22,14 @@ export class MessagesClient {
18
22
  async list(chatId, opts = {}) {
19
23
  if (!chatId)
20
24
  return { messages: [], latestSequence: null };
21
- const url = new URL(`${getBackendUrl()}/messages`);
22
- url.searchParams.set('chatId', chatId);
23
- if (opts.after)
24
- url.searchParams.set('after', opts.after);
25
+ const url = new URL(`${getBackendUrl()}/chats/${encodeURIComponent(chatId)}/messages`);
26
+ // Forward-delta read mode. Backend routes to `listMessagesForward`
27
+ // only when BOTH `after` and `direction=forward` are present; without
28
+ // `after` the same handler falls into backward pagination and returns
29
+ // a different envelope. Default `after` to the unix epoch so the
30
+ // forward branch always wins when callers want "from the beginning".
31
+ url.searchParams.set('direction', 'forward');
32
+ url.searchParams.set('after', opts.after || '1970-01-01T00:00:00.000Z');
25
33
  if (opts.limit != null)
26
34
  url.searchParams.set('limit', String(opts.limit));
27
35
  const res = await fetch(url.toString(), { headers: this._headers() });
@@ -302,10 +302,19 @@ export async function setTaskSatisfaction(taskId, satisfaction, creds) {
302
302
  }
303
303
  export async function countTasks(filters = {}, creds) {
304
304
  assertCreds(creds, 'count tasks');
305
- const res = await fetch(`${getTaskBaseUrl()}/countTasks`, {
306
- method: 'POST',
305
+ // Canonical REST: GET /tasks/count.
306
+ const url = new URL(`${getTaskBaseUrl()}/count`);
307
+ if (filters.chatId)
308
+ url.searchParams.set('chatId', filters.chatId);
309
+ if (filters.state)
310
+ url.searchParams.set('state', filters.state);
311
+ if (filters.userId)
312
+ url.searchParams.set('userId', filters.userId);
313
+ if (filters.agentId)
314
+ url.searchParams.set('agentId', filters.agentId);
315
+ const res = await fetch(url.toString(), {
316
+ method: 'GET',
307
317
  headers: buildHeaders(creds),
308
- body: JSON.stringify(filters),
309
318
  });
310
319
  if (!res.ok) {
311
320
  const body = await res.text().catch(() => '');
@@ -8,5 +8,9 @@ export { ArtifactsClient } from './ArtifactsClient.js';
8
8
  export type { ArtifactVisibility, ListArtifactsOptions, ListArtifactsQuery, ListArtifactsResult, WriteArtifactInput, } from './ArtifactsClient.js';
9
9
  export { ScopeClient } from './ScopeClient.js';
10
10
  export type { ScopeKind, ScopeResult, PartyRef } from './ScopeClient.js';
11
+ export { ContextReadClient } from './ContextReadClient.js';
12
+ export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, } from './ContextReadClient.js';
13
+ export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
14
+ export type { ContextReachDescriptor } from './ContextDiscoveryClient.js';
11
15
  export { AgentSearchClient } from './AgentSearchClient.js';
12
16
  export { TelemetryClient } from './TelemetryClient.js';
@@ -5,5 +5,7 @@ export * from './ChatClient.js';
5
5
  export { MessagesClient } from './MessagesClient.js';
6
6
  export { ArtifactsClient } from './ArtifactsClient.js';
7
7
  export { ScopeClient } from './ScopeClient.js';
8
+ export { ContextReadClient } from './ContextReadClient.js';
9
+ export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
8
10
  export { AgentSearchClient } from './AgentSearchClient.js';
9
11
  export { TelemetryClient } from './TelemetryClient.js';
package/dist/types.d.ts CHANGED
@@ -49,11 +49,19 @@ export interface Agreement {
49
49
  engagementKind?: EngagementKind;
50
50
  proposalStatus?: ProposalStatus;
51
51
  parties?: {
52
- proposedToId?: string;
53
- providerId?: string;
54
- payerId?: string;
52
+ proposedTo?: string;
53
+ provider?: string;
54
+ payer?: string;
55
+ creator?: string;
56
+ creatorAgent?: string;
57
+ };
58
+ /** Legacy root-level price — always null in practice. Use money.price instead. */
59
+ price?: number | null;
60
+ /** Price and payment state. price is in cents (e.g. 6000 = 6000 KRW). */
61
+ money?: {
62
+ price?: number | null;
63
+ paymentStatus?: string;
55
64
  };
56
- price?: number;
57
65
  lifecycle?: string;
58
66
  expiresAt?: string;
59
67
  maxExecutions?: number;
@@ -87,7 +95,7 @@ export declare const ContentTypes: {
87
95
  export type ContentType = (typeof ContentTypes)[keyof typeof ContentTypes];
88
96
  /** ⚠️ SYNC: Mirrors backend src/chat/chat.types.ts isValidContentType */
89
97
  export declare function isValidContentType(contentType: string, entryType: string): boolean;
90
- /** ⚠️ SYNC: Keep in sync with backend and agent-sdk/src/tasks/taskCore.js */
98
+ /** ⚠️ SYNC: Keep in sync with backend. This is the canonical SDK-side definition. */
91
99
  export declare const OPEN_AGREEMENT_TARGET: "everyone";
92
100
  export interface MessageMetadata {
93
101
  chatId: string;
@@ -111,10 +119,12 @@ export interface MessageMetadata {
111
119
  * `normalizeIncomingEvent` reads `metadata.task` to upgrade structured
112
120
  * task notifications into `task_result` events (so executors see
113
121
  * `task-assigned` outcomes when the orchestrator calls `task_spawn`),
114
- * and the agent FSM treats `metadata.agreement` as the source of truth
115
- * for proposal lifecycle without needing a separate fetch.
122
+ * and maps explicit wire `operation` + `agreementId` lifecycle fields
123
+ * into `agreement_lifecycle` events for proposal approve/reject.
116
124
  */
117
125
  task?: Record<string, unknown> | null;
118
126
  agreement?: Record<string, unknown> | null;
127
+ operation?: string | null;
128
+ agreementId?: string | null;
119
129
  }
120
130
  export type MessageHandler = (text: string, metadata: MessageMetadata) => Promise<void>;
package/dist/types.js CHANGED
@@ -46,5 +46,5 @@ const VALID_CONTENT_TYPES = {
46
46
  export function isValidContentType(contentType, entryType) {
47
47
  return VALID_CONTENT_TYPES[entryType]?.includes(contentType) ?? false;
48
48
  }
49
- /** ⚠️ SYNC: Keep in sync with backend and agent-sdk/src/tasks/taskCore.js */
49
+ /** ⚠️ SYNC: Keep in sync with backend. This is the canonical SDK-side definition. */
50
50
  export const OPEN_AGREEMENT_TARGET = 'everyone';
@@ -14,7 +14,8 @@ export function createControlSocket({ wsUrl, operatorKey, agentIds, onWake }) {
14
14
  transports: ['websocket'],
15
15
  reconnection: true,
16
16
  reconnectionDelay: 1000,
17
- reconnectionDelayMax: 5000,
17
+ reconnectionDelayMax: 30000,
18
+ randomizationFactor: 0.5,
18
19
  });
19
20
  socket.on('connect', () => {
20
21
  const ids = agentIds();