acn-client 0.11.2 → 0.13.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/README.md +34 -35
- package/dist/index.d.mts +357 -5
- package/dist/index.d.ts +357 -5
- package/dist/index.js +246 -0
- package/dist/index.mjs +246 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -194,6 +194,27 @@ var ACNClient = class {
|
|
|
194
194
|
async unregisterAgent(agentId) {
|
|
195
195
|
return this.delete(`/api/v1/agents/${agentId}`);
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Rotate the agent's API key (H1).
|
|
199
|
+
*
|
|
200
|
+
* Returns a fresh `acn_*` plaintext key exactly once. The old key
|
|
201
|
+
* stops working immediately — including any in-process auth caches
|
|
202
|
+
* the gateway holds for it. Update the local SDK's stored key with
|
|
203
|
+
* the returned value before the next request:
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* const { api_key } = await client.rotateApiKey(agentId);
|
|
207
|
+
* client.config.apiKey = api_key; // or rebuild the client
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* Authorization is dual-track on the server: any one of the agent's
|
|
211
|
+
* current key (the common scheduled-rotation path) or the owner's
|
|
212
|
+
* Auth0 JWT (the recovery path when the agent has lost its key) is
|
|
213
|
+
* accepted.
|
|
214
|
+
*/
|
|
215
|
+
async rotateApiKey(agentId) {
|
|
216
|
+
return this.post(`/api/v1/agents/${agentId}/rotate-key`);
|
|
217
|
+
}
|
|
197
218
|
/** Send agent heartbeat */
|
|
198
219
|
async heartbeat(agentId) {
|
|
199
220
|
return this.post(`/api/v1/agents/${agentId}/heartbeat`);
|
|
@@ -221,6 +242,29 @@ var ACNClient = class {
|
|
|
221
242
|
async getSubnet(subnetId) {
|
|
222
243
|
return this.get(`/api/v1/subnets/${subnetId}`);
|
|
223
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* List immediate children of a subnet (ADR-0003).
|
|
247
|
+
*
|
|
248
|
+
* Wraps `GET /api/v1/subnets/{parentSubnetId}/children`. Returns
|
|
249
|
+
* `SUBNET_NOT_FOUND` when the parent does not exist. Visibility
|
|
250
|
+
* matches `listSubnets` — private children you cannot see are
|
|
251
|
+
* omitted from the result set.
|
|
252
|
+
*/
|
|
253
|
+
async listChildren(parentSubnetId) {
|
|
254
|
+
const data = await this.get(
|
|
255
|
+
`/api/v1/subnets/${parentSubnetId}/children`
|
|
256
|
+
);
|
|
257
|
+
return data.subnets;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Promote a `task_scoped` subnet to `persistent` (ADR-0003).
|
|
261
|
+
*
|
|
262
|
+
* Owner-only. Idempotent — promoting an already-persistent subnet
|
|
263
|
+
* returns its current state unchanged.
|
|
264
|
+
*/
|
|
265
|
+
async promoteSubnet(subnetId) {
|
|
266
|
+
return this.post(`/api/v1/subnets/${subnetId}/promote`);
|
|
267
|
+
}
|
|
224
268
|
/** Delete a subnet you own (requires Agent API Key — only the owning agent can delete) */
|
|
225
269
|
async deleteSubnet(subnetId) {
|
|
226
270
|
return this.request("DELETE", `/api/v1/subnets/${subnetId}`);
|
|
@@ -254,6 +298,207 @@ var ACNClient = class {
|
|
|
254
298
|
return this.get(`/api/v1/agents/${agentId}/subnets`);
|
|
255
299
|
}
|
|
256
300
|
// ============================================
|
|
301
|
+
// ADR-0004 Subnet Admission
|
|
302
|
+
// ============================================
|
|
303
|
+
//
|
|
304
|
+
// 13 verbs gated by `subnet.join_policy === 'approval'`:
|
|
305
|
+
// - Allowlist (3): owner pre-authorisation.
|
|
306
|
+
// - Join requests (4): applicant-initiated path.
|
|
307
|
+
// - Invitations (5): owner-initiated path.
|
|
308
|
+
// - Agent-side (1): invitee's cross-subnet pending view.
|
|
309
|
+
//
|
|
310
|
+
// The plain `joinSubnet` verb dispatches the six-branch decision
|
|
311
|
+
// tree on the server side — these methods are the admin-side
|
|
312
|
+
// controls used by subnet owners and the per-row decisions used
|
|
313
|
+
// by applicants and invitees.
|
|
314
|
+
//
|
|
315
|
+
// Method names use the `subnet*` prefix to avoid colliding with
|
|
316
|
+
// the existing inbox `addToAllowlist` surface (which lives at
|
|
317
|
+
// `/api/v1/agents/{a}/allowlist/{target}` and is unrelated).
|
|
318
|
+
// ----- Allowlist (owner-only, 3 verbs) ---------------------------------
|
|
319
|
+
/**
|
|
320
|
+
* Pre-authorise `agentId` on `subnetId`'s allowlist (owner only).
|
|
321
|
+
*
|
|
322
|
+
* Allowlisted agents skip the approval queue: their next
|
|
323
|
+
* `joinSubnet` lands in branch 4 (allowlist hit) and becomes an
|
|
324
|
+
* immediate member with an `allowlist_auto` audit row.
|
|
325
|
+
*
|
|
326
|
+
* Server returns 201 with the persisted entry; duplicate adds
|
|
327
|
+
* return 409 ALREADY_ON_ALLOWLIST (raised as an error, never
|
|
328
|
+
* silently no-op'd).
|
|
329
|
+
*/
|
|
330
|
+
async subnetAllowlistAdd(subnetId, agentId) {
|
|
331
|
+
return this.post(`/api/v1/subnets/${subnetId}/allowlist`, {
|
|
332
|
+
agent_id: agentId
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Remove `agentId` from `subnetId`'s allowlist (owner only).
|
|
337
|
+
*
|
|
338
|
+
* Idempotent — removing an entry that doesn't exist still
|
|
339
|
+
* returns 204. Per ADR-0004 §"Allowlist mutation does not
|
|
340
|
+
* affect agents who already joined", this does NOT revoke
|
|
341
|
+
* membership for agents already admitted via the allowlist.
|
|
342
|
+
*/
|
|
343
|
+
async subnetAllowlistRemove(subnetId, agentId) {
|
|
344
|
+
await this.delete(`/api/v1/subnets/${subnetId}/allowlist/${agentId}`);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* List `subnetId`'s allowlist entries (owner only).
|
|
348
|
+
*
|
|
349
|
+
* Owner-only by design — the allowlist is a privacy-sensitive
|
|
350
|
+
* trust signal and exposing it publicly would leak relationship
|
|
351
|
+
* metadata.
|
|
352
|
+
*/
|
|
353
|
+
async subnetAllowlistList(subnetId, options) {
|
|
354
|
+
const params = {
|
|
355
|
+
limit: options?.limit ?? 100,
|
|
356
|
+
offset: options?.offset ?? 0
|
|
357
|
+
};
|
|
358
|
+
return this.get(`/api/v1/subnets/${subnetId}/allowlist`, params);
|
|
359
|
+
}
|
|
360
|
+
// ----- Join requests (4 verbs: 3 owner-side + 1 applicant-side) --------
|
|
361
|
+
/**
|
|
362
|
+
* Owner approves a pending join_request (CAS pending → approved).
|
|
363
|
+
*
|
|
364
|
+
* Side effects: applicant added to `subnet.member_agent_ids` and
|
|
365
|
+
* the `subnet.join_approved` webhook fires. The applicant is
|
|
366
|
+
* still expected to call `joinSubnet` to register the
|
|
367
|
+
* `agent.subnet_ids` back-reference (per ADR-0004 §"State
|
|
368
|
+
* machine edges").
|
|
369
|
+
*
|
|
370
|
+
* Optional `note` (≤500 chars) is recorded on the audit row.
|
|
371
|
+
*/
|
|
372
|
+
async subnetJoinRequestApprove(subnetId, requestId, options) {
|
|
373
|
+
return this.post(
|
|
374
|
+
`/api/v1/subnets/${subnetId}/join-requests/${requestId}/approve`,
|
|
375
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Owner rejects a pending join_request (CAS pending → rejected).
|
|
380
|
+
*
|
|
381
|
+
* No membership change. `subnet.join_rejected` webhook fires.
|
|
382
|
+
*/
|
|
383
|
+
async subnetJoinRequestReject(subnetId, requestId, options) {
|
|
384
|
+
return this.post(
|
|
385
|
+
`/api/v1/subnets/${subnetId}/join-requests/${requestId}/reject`,
|
|
386
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Applicant withdraws their own pending join_request.
|
|
391
|
+
*
|
|
392
|
+
* Self-only — caller must be the agent who originally created
|
|
393
|
+
* the request. `subnet.join_withdrawn` webhook fires.
|
|
394
|
+
*/
|
|
395
|
+
async subnetJoinRequestWithdraw(subnetId, requestId, options) {
|
|
396
|
+
return this.request(
|
|
397
|
+
"DELETE",
|
|
398
|
+
`/api/v1/subnets/${subnetId}/join-requests/${requestId}`,
|
|
399
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Owner lists join_request / allowlist_auto rows for `subnetId`.
|
|
404
|
+
*
|
|
405
|
+
* `kind` defaults to `'join_request'`; pass `'allowlist_auto'`
|
|
406
|
+
* to inspect synthesised allowlist-hit audit rows. Server
|
|
407
|
+
* rejects `kind='invitation'` with 400 INVALID_KIND_FILTER —
|
|
408
|
+
* use `subnetInvitationList` instead.
|
|
409
|
+
*/
|
|
410
|
+
async subnetJoinRequestList(subnetId, options) {
|
|
411
|
+
const params = {
|
|
412
|
+
kind: options?.kind ?? "join_request",
|
|
413
|
+
limit: options?.limit ?? 100,
|
|
414
|
+
offset: options?.offset ?? 0
|
|
415
|
+
};
|
|
416
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
417
|
+
return this.get(`/api/v1/subnets/${subnetId}/join-requests`, params);
|
|
418
|
+
}
|
|
419
|
+
// ----- Invitations (5 + 1 verbs) ---------------------------------------
|
|
420
|
+
/**
|
|
421
|
+
* Owner sends an invitation to `agentId` (or merges into a
|
|
422
|
+
* pending join_request from the same target).
|
|
423
|
+
*
|
|
424
|
+
* Two response shapes per ADR-0004 §"Invitation merge path":
|
|
425
|
+
*
|
|
426
|
+
* - **Normal path** (server returns 202): `{ invitation_id, status: 'pending' }`.
|
|
427
|
+
* - **Merge path** (server returns 200, request auto-approved):
|
|
428
|
+
* `{ auto_resolved: true, resolved_kind: 'join_request', request_id }`.
|
|
429
|
+
*
|
|
430
|
+
* Discriminate on `auto_resolved` to dispatch.
|
|
431
|
+
*/
|
|
432
|
+
async subnetInvitationSend(subnetId, agentId, options) {
|
|
433
|
+
const body = { agent_id: agentId };
|
|
434
|
+
if (options?.note !== void 0) body.note = options.note;
|
|
435
|
+
return this.post(`/api/v1/subnets/${subnetId}/invitations`, body);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Invitee accepts a pending invitation (CAS pending → approved).
|
|
439
|
+
*
|
|
440
|
+
* Self-only against the row's `agent_id`. Side effects: invitee
|
|
441
|
+
* added to `subnet.member_agent_ids`, the agent's `subnet_ids`
|
|
442
|
+
* gains the back-reference, and `subnet.invitation_accepted`
|
|
443
|
+
* webhook fires.
|
|
444
|
+
*/
|
|
445
|
+
async subnetInvitationAccept(subnetId, requestId, options) {
|
|
446
|
+
return this.post(
|
|
447
|
+
`/api/v1/subnets/${subnetId}/invitations/${requestId}/accept`,
|
|
448
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Invitee rejects a pending invitation (CAS pending → rejected).
|
|
453
|
+
*
|
|
454
|
+
* No membership change. `subnet.invitation_rejected` webhook
|
|
455
|
+
* fires.
|
|
456
|
+
*/
|
|
457
|
+
async subnetInvitationReject(subnetId, requestId, options) {
|
|
458
|
+
return this.post(
|
|
459
|
+
`/api/v1/subnets/${subnetId}/invitations/${requestId}/reject`,
|
|
460
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Owner cancels a pending invitation (CAS pending → withdrawn).
|
|
465
|
+
*
|
|
466
|
+
* Owner-only counterpart to applicant withdraw. The row goes to
|
|
467
|
+
* `withdrawn` (not `rejected`) — distinct audit token so
|
|
468
|
+
* consumers can tell "owner gave up" from "invitee said no".
|
|
469
|
+
*/
|
|
470
|
+
async subnetInvitationCancel(subnetId, requestId, options) {
|
|
471
|
+
return this.request(
|
|
472
|
+
"DELETE",
|
|
473
|
+
`/api/v1/subnets/${subnetId}/invitations/${requestId}`,
|
|
474
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Owner lists invitation rows for `subnetId`.
|
|
479
|
+
*
|
|
480
|
+
* Owner-only — invitees use `agentSubnetInvitations` for their
|
|
481
|
+
* own cross-subnet view.
|
|
482
|
+
*/
|
|
483
|
+
async subnetInvitationList(subnetId, options) {
|
|
484
|
+
const params = {
|
|
485
|
+
limit: options?.limit ?? 100,
|
|
486
|
+
offset: options?.offset ?? 0
|
|
487
|
+
};
|
|
488
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
489
|
+
return this.get(`/api/v1/subnets/${subnetId}/invitations`, params);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Invitee's cross-subnet pending-invitation list (self only).
|
|
493
|
+
*
|
|
494
|
+
* Returns only `status='pending'` rows. Historical decisions
|
|
495
|
+
* are queryable per-subnet through the owner-only
|
|
496
|
+
* `subnetInvitationList`.
|
|
497
|
+
*/
|
|
498
|
+
async agentSubnetInvitations(agentId) {
|
|
499
|
+
return this.get(`/api/v1/agents/${agentId}/subnet-invitations`);
|
|
500
|
+
}
|
|
501
|
+
// ============================================
|
|
257
502
|
// Communication
|
|
258
503
|
// ============================================
|
|
259
504
|
/** Send message to an agent */
|
|
@@ -899,6 +1144,7 @@ var ACNError = class extends Error {
|
|
|
899
1144
|
this.errorCode = options?.errorCode;
|
|
900
1145
|
this.requestId = options?.requestId;
|
|
901
1146
|
}
|
|
1147
|
+
status;
|
|
902
1148
|
/** ACN internal error code (present on sanitised 5xx responses) */
|
|
903
1149
|
errorCode;
|
|
904
1150
|
/** Request ID minted by ACN for 5xx responses (useful for support) */
|
package/dist/index.mjs
CHANGED
|
@@ -154,6 +154,27 @@ var ACNClient = class {
|
|
|
154
154
|
async unregisterAgent(agentId) {
|
|
155
155
|
return this.delete(`/api/v1/agents/${agentId}`);
|
|
156
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Rotate the agent's API key (H1).
|
|
159
|
+
*
|
|
160
|
+
* Returns a fresh `acn_*` plaintext key exactly once. The old key
|
|
161
|
+
* stops working immediately — including any in-process auth caches
|
|
162
|
+
* the gateway holds for it. Update the local SDK's stored key with
|
|
163
|
+
* the returned value before the next request:
|
|
164
|
+
*
|
|
165
|
+
* ```ts
|
|
166
|
+
* const { api_key } = await client.rotateApiKey(agentId);
|
|
167
|
+
* client.config.apiKey = api_key; // or rebuild the client
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* Authorization is dual-track on the server: any one of the agent's
|
|
171
|
+
* current key (the common scheduled-rotation path) or the owner's
|
|
172
|
+
* Auth0 JWT (the recovery path when the agent has lost its key) is
|
|
173
|
+
* accepted.
|
|
174
|
+
*/
|
|
175
|
+
async rotateApiKey(agentId) {
|
|
176
|
+
return this.post(`/api/v1/agents/${agentId}/rotate-key`);
|
|
177
|
+
}
|
|
157
178
|
/** Send agent heartbeat */
|
|
158
179
|
async heartbeat(agentId) {
|
|
159
180
|
return this.post(`/api/v1/agents/${agentId}/heartbeat`);
|
|
@@ -181,6 +202,29 @@ var ACNClient = class {
|
|
|
181
202
|
async getSubnet(subnetId) {
|
|
182
203
|
return this.get(`/api/v1/subnets/${subnetId}`);
|
|
183
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* List immediate children of a subnet (ADR-0003).
|
|
207
|
+
*
|
|
208
|
+
* Wraps `GET /api/v1/subnets/{parentSubnetId}/children`. Returns
|
|
209
|
+
* `SUBNET_NOT_FOUND` when the parent does not exist. Visibility
|
|
210
|
+
* matches `listSubnets` — private children you cannot see are
|
|
211
|
+
* omitted from the result set.
|
|
212
|
+
*/
|
|
213
|
+
async listChildren(parentSubnetId) {
|
|
214
|
+
const data = await this.get(
|
|
215
|
+
`/api/v1/subnets/${parentSubnetId}/children`
|
|
216
|
+
);
|
|
217
|
+
return data.subnets;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Promote a `task_scoped` subnet to `persistent` (ADR-0003).
|
|
221
|
+
*
|
|
222
|
+
* Owner-only. Idempotent — promoting an already-persistent subnet
|
|
223
|
+
* returns its current state unchanged.
|
|
224
|
+
*/
|
|
225
|
+
async promoteSubnet(subnetId) {
|
|
226
|
+
return this.post(`/api/v1/subnets/${subnetId}/promote`);
|
|
227
|
+
}
|
|
184
228
|
/** Delete a subnet you own (requires Agent API Key — only the owning agent can delete) */
|
|
185
229
|
async deleteSubnet(subnetId) {
|
|
186
230
|
return this.request("DELETE", `/api/v1/subnets/${subnetId}`);
|
|
@@ -214,6 +258,207 @@ var ACNClient = class {
|
|
|
214
258
|
return this.get(`/api/v1/agents/${agentId}/subnets`);
|
|
215
259
|
}
|
|
216
260
|
// ============================================
|
|
261
|
+
// ADR-0004 Subnet Admission
|
|
262
|
+
// ============================================
|
|
263
|
+
//
|
|
264
|
+
// 13 verbs gated by `subnet.join_policy === 'approval'`:
|
|
265
|
+
// - Allowlist (3): owner pre-authorisation.
|
|
266
|
+
// - Join requests (4): applicant-initiated path.
|
|
267
|
+
// - Invitations (5): owner-initiated path.
|
|
268
|
+
// - Agent-side (1): invitee's cross-subnet pending view.
|
|
269
|
+
//
|
|
270
|
+
// The plain `joinSubnet` verb dispatches the six-branch decision
|
|
271
|
+
// tree on the server side — these methods are the admin-side
|
|
272
|
+
// controls used by subnet owners and the per-row decisions used
|
|
273
|
+
// by applicants and invitees.
|
|
274
|
+
//
|
|
275
|
+
// Method names use the `subnet*` prefix to avoid colliding with
|
|
276
|
+
// the existing inbox `addToAllowlist` surface (which lives at
|
|
277
|
+
// `/api/v1/agents/{a}/allowlist/{target}` and is unrelated).
|
|
278
|
+
// ----- Allowlist (owner-only, 3 verbs) ---------------------------------
|
|
279
|
+
/**
|
|
280
|
+
* Pre-authorise `agentId` on `subnetId`'s allowlist (owner only).
|
|
281
|
+
*
|
|
282
|
+
* Allowlisted agents skip the approval queue: their next
|
|
283
|
+
* `joinSubnet` lands in branch 4 (allowlist hit) and becomes an
|
|
284
|
+
* immediate member with an `allowlist_auto` audit row.
|
|
285
|
+
*
|
|
286
|
+
* Server returns 201 with the persisted entry; duplicate adds
|
|
287
|
+
* return 409 ALREADY_ON_ALLOWLIST (raised as an error, never
|
|
288
|
+
* silently no-op'd).
|
|
289
|
+
*/
|
|
290
|
+
async subnetAllowlistAdd(subnetId, agentId) {
|
|
291
|
+
return this.post(`/api/v1/subnets/${subnetId}/allowlist`, {
|
|
292
|
+
agent_id: agentId
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Remove `agentId` from `subnetId`'s allowlist (owner only).
|
|
297
|
+
*
|
|
298
|
+
* Idempotent — removing an entry that doesn't exist still
|
|
299
|
+
* returns 204. Per ADR-0004 §"Allowlist mutation does not
|
|
300
|
+
* affect agents who already joined", this does NOT revoke
|
|
301
|
+
* membership for agents already admitted via the allowlist.
|
|
302
|
+
*/
|
|
303
|
+
async subnetAllowlistRemove(subnetId, agentId) {
|
|
304
|
+
await this.delete(`/api/v1/subnets/${subnetId}/allowlist/${agentId}`);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* List `subnetId`'s allowlist entries (owner only).
|
|
308
|
+
*
|
|
309
|
+
* Owner-only by design — the allowlist is a privacy-sensitive
|
|
310
|
+
* trust signal and exposing it publicly would leak relationship
|
|
311
|
+
* metadata.
|
|
312
|
+
*/
|
|
313
|
+
async subnetAllowlistList(subnetId, options) {
|
|
314
|
+
const params = {
|
|
315
|
+
limit: options?.limit ?? 100,
|
|
316
|
+
offset: options?.offset ?? 0
|
|
317
|
+
};
|
|
318
|
+
return this.get(`/api/v1/subnets/${subnetId}/allowlist`, params);
|
|
319
|
+
}
|
|
320
|
+
// ----- Join requests (4 verbs: 3 owner-side + 1 applicant-side) --------
|
|
321
|
+
/**
|
|
322
|
+
* Owner approves a pending join_request (CAS pending → approved).
|
|
323
|
+
*
|
|
324
|
+
* Side effects: applicant added to `subnet.member_agent_ids` and
|
|
325
|
+
* the `subnet.join_approved` webhook fires. The applicant is
|
|
326
|
+
* still expected to call `joinSubnet` to register the
|
|
327
|
+
* `agent.subnet_ids` back-reference (per ADR-0004 §"State
|
|
328
|
+
* machine edges").
|
|
329
|
+
*
|
|
330
|
+
* Optional `note` (≤500 chars) is recorded on the audit row.
|
|
331
|
+
*/
|
|
332
|
+
async subnetJoinRequestApprove(subnetId, requestId, options) {
|
|
333
|
+
return this.post(
|
|
334
|
+
`/api/v1/subnets/${subnetId}/join-requests/${requestId}/approve`,
|
|
335
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Owner rejects a pending join_request (CAS pending → rejected).
|
|
340
|
+
*
|
|
341
|
+
* No membership change. `subnet.join_rejected` webhook fires.
|
|
342
|
+
*/
|
|
343
|
+
async subnetJoinRequestReject(subnetId, requestId, options) {
|
|
344
|
+
return this.post(
|
|
345
|
+
`/api/v1/subnets/${subnetId}/join-requests/${requestId}/reject`,
|
|
346
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Applicant withdraws their own pending join_request.
|
|
351
|
+
*
|
|
352
|
+
* Self-only — caller must be the agent who originally created
|
|
353
|
+
* the request. `subnet.join_withdrawn` webhook fires.
|
|
354
|
+
*/
|
|
355
|
+
async subnetJoinRequestWithdraw(subnetId, requestId, options) {
|
|
356
|
+
return this.request(
|
|
357
|
+
"DELETE",
|
|
358
|
+
`/api/v1/subnets/${subnetId}/join-requests/${requestId}`,
|
|
359
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Owner lists join_request / allowlist_auto rows for `subnetId`.
|
|
364
|
+
*
|
|
365
|
+
* `kind` defaults to `'join_request'`; pass `'allowlist_auto'`
|
|
366
|
+
* to inspect synthesised allowlist-hit audit rows. Server
|
|
367
|
+
* rejects `kind='invitation'` with 400 INVALID_KIND_FILTER —
|
|
368
|
+
* use `subnetInvitationList` instead.
|
|
369
|
+
*/
|
|
370
|
+
async subnetJoinRequestList(subnetId, options) {
|
|
371
|
+
const params = {
|
|
372
|
+
kind: options?.kind ?? "join_request",
|
|
373
|
+
limit: options?.limit ?? 100,
|
|
374
|
+
offset: options?.offset ?? 0
|
|
375
|
+
};
|
|
376
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
377
|
+
return this.get(`/api/v1/subnets/${subnetId}/join-requests`, params);
|
|
378
|
+
}
|
|
379
|
+
// ----- Invitations (5 + 1 verbs) ---------------------------------------
|
|
380
|
+
/**
|
|
381
|
+
* Owner sends an invitation to `agentId` (or merges into a
|
|
382
|
+
* pending join_request from the same target).
|
|
383
|
+
*
|
|
384
|
+
* Two response shapes per ADR-0004 §"Invitation merge path":
|
|
385
|
+
*
|
|
386
|
+
* - **Normal path** (server returns 202): `{ invitation_id, status: 'pending' }`.
|
|
387
|
+
* - **Merge path** (server returns 200, request auto-approved):
|
|
388
|
+
* `{ auto_resolved: true, resolved_kind: 'join_request', request_id }`.
|
|
389
|
+
*
|
|
390
|
+
* Discriminate on `auto_resolved` to dispatch.
|
|
391
|
+
*/
|
|
392
|
+
async subnetInvitationSend(subnetId, agentId, options) {
|
|
393
|
+
const body = { agent_id: agentId };
|
|
394
|
+
if (options?.note !== void 0) body.note = options.note;
|
|
395
|
+
return this.post(`/api/v1/subnets/${subnetId}/invitations`, body);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Invitee accepts a pending invitation (CAS pending → approved).
|
|
399
|
+
*
|
|
400
|
+
* Self-only against the row's `agent_id`. Side effects: invitee
|
|
401
|
+
* added to `subnet.member_agent_ids`, the agent's `subnet_ids`
|
|
402
|
+
* gains the back-reference, and `subnet.invitation_accepted`
|
|
403
|
+
* webhook fires.
|
|
404
|
+
*/
|
|
405
|
+
async subnetInvitationAccept(subnetId, requestId, options) {
|
|
406
|
+
return this.post(
|
|
407
|
+
`/api/v1/subnets/${subnetId}/invitations/${requestId}/accept`,
|
|
408
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Invitee rejects a pending invitation (CAS pending → rejected).
|
|
413
|
+
*
|
|
414
|
+
* No membership change. `subnet.invitation_rejected` webhook
|
|
415
|
+
* fires.
|
|
416
|
+
*/
|
|
417
|
+
async subnetInvitationReject(subnetId, requestId, options) {
|
|
418
|
+
return this.post(
|
|
419
|
+
`/api/v1/subnets/${subnetId}/invitations/${requestId}/reject`,
|
|
420
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Owner cancels a pending invitation (CAS pending → withdrawn).
|
|
425
|
+
*
|
|
426
|
+
* Owner-only counterpart to applicant withdraw. The row goes to
|
|
427
|
+
* `withdrawn` (not `rejected`) — distinct audit token so
|
|
428
|
+
* consumers can tell "owner gave up" from "invitee said no".
|
|
429
|
+
*/
|
|
430
|
+
async subnetInvitationCancel(subnetId, requestId, options) {
|
|
431
|
+
return this.request(
|
|
432
|
+
"DELETE",
|
|
433
|
+
`/api/v1/subnets/${subnetId}/invitations/${requestId}`,
|
|
434
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Owner lists invitation rows for `subnetId`.
|
|
439
|
+
*
|
|
440
|
+
* Owner-only — invitees use `agentSubnetInvitations` for their
|
|
441
|
+
* own cross-subnet view.
|
|
442
|
+
*/
|
|
443
|
+
async subnetInvitationList(subnetId, options) {
|
|
444
|
+
const params = {
|
|
445
|
+
limit: options?.limit ?? 100,
|
|
446
|
+
offset: options?.offset ?? 0
|
|
447
|
+
};
|
|
448
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
449
|
+
return this.get(`/api/v1/subnets/${subnetId}/invitations`, params);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Invitee's cross-subnet pending-invitation list (self only).
|
|
453
|
+
*
|
|
454
|
+
* Returns only `status='pending'` rows. Historical decisions
|
|
455
|
+
* are queryable per-subnet through the owner-only
|
|
456
|
+
* `subnetInvitationList`.
|
|
457
|
+
*/
|
|
458
|
+
async agentSubnetInvitations(agentId) {
|
|
459
|
+
return this.get(`/api/v1/agents/${agentId}/subnet-invitations`);
|
|
460
|
+
}
|
|
461
|
+
// ============================================
|
|
217
462
|
// Communication
|
|
218
463
|
// ============================================
|
|
219
464
|
/** Send message to an agent */
|
|
@@ -859,6 +1104,7 @@ var ACNError = class extends Error {
|
|
|
859
1104
|
this.errorCode = options?.errorCode;
|
|
860
1105
|
this.requestId = options?.requestId;
|
|
861
1106
|
}
|
|
1107
|
+
status;
|
|
862
1108
|
/** ACN internal error code (present on sanitised 5xx responses) */
|
|
863
1109
|
errorCode;
|
|
864
1110
|
/** Request ID minted by ACN for 5xx responses (useful for support) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "acn-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Official TypeScript/JavaScript client for ACN (Agent Collaboration Network)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@types/node": "^20.0.0",
|
|
52
52
|
"tsup": "^8.0.0",
|
|
53
53
|
"typescript": "^5.0.0",
|
|
54
|
-
"vitest": "^1.
|
|
54
|
+
"vitest": "^2.1.9"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"typescript": ">=4.7.0"
|