acn-client 0.12.0 → 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/dist/index.mjs CHANGED
@@ -202,6 +202,29 @@ var ACNClient = class {
202
202
  async getSubnet(subnetId) {
203
203
  return this.get(`/api/v1/subnets/${subnetId}`);
204
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
+ }
205
228
  /** Delete a subnet you own (requires Agent API Key — only the owning agent can delete) */
206
229
  async deleteSubnet(subnetId) {
207
230
  return this.request("DELETE", `/api/v1/subnets/${subnetId}`);
@@ -235,6 +258,207 @@ var ACNClient = class {
235
258
  return this.get(`/api/v1/agents/${agentId}/subnets`);
236
259
  }
237
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
+ // ============================================
238
462
  // Communication
239
463
  // ============================================
240
464
  /** Send message to an agent */
@@ -880,6 +1104,7 @@ var ACNError = class extends Error {
880
1104
  this.errorCode = options?.errorCode;
881
1105
  this.requestId = options?.requestId;
882
1106
  }
1107
+ status;
883
1108
  /** ACN internal error code (present on sanitised 5xx responses) */
884
1109
  errorCode;
885
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.12.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.0.0"
54
+ "vitest": "^2.1.9"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "typescript": ">=4.7.0"