acn-client 0.12.0 → 0.14.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 +380 -15
- package/dist/index.d.ts +380 -15
- package/dist/index.js +272 -12
- package/dist/index.mjs +271 -12
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -83,6 +83,9 @@ var ACNClient = class {
|
|
|
83
83
|
post(path, body) {
|
|
84
84
|
return this.request("POST", path, { body });
|
|
85
85
|
}
|
|
86
|
+
patch(path, body) {
|
|
87
|
+
return this.request("PATCH", path, { body });
|
|
88
|
+
}
|
|
86
89
|
delete(path) {
|
|
87
90
|
return this.request("DELETE", path);
|
|
88
91
|
}
|
|
@@ -199,16 +202,39 @@ var ACNClient = class {
|
|
|
199
202
|
return this.get("/api/v1/subnets");
|
|
200
203
|
}
|
|
201
204
|
/** Get subnet by ID */
|
|
202
|
-
async getSubnet(
|
|
203
|
-
return this.get(`/api/v1/subnets/${
|
|
205
|
+
async getSubnet(slug) {
|
|
206
|
+
return this.get(`/api/v1/subnets/${slug}`);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* List immediate children of a subnet (ADR-0003).
|
|
210
|
+
*
|
|
211
|
+
* Wraps `GET /api/v1/subnets/{parentSlug}/children`. Returns
|
|
212
|
+
* `SUBNET_NOT_FOUND` when the parent does not exist. Visibility
|
|
213
|
+
* matches `listSubnets` — private children you cannot see are
|
|
214
|
+
* omitted from the result set.
|
|
215
|
+
*/
|
|
216
|
+
async listChildren(parentSlug) {
|
|
217
|
+
const data = await this.get(
|
|
218
|
+
`/api/v1/subnets/${parentSlug}/children`
|
|
219
|
+
);
|
|
220
|
+
return data.subnets;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Promote a `task_scoped` subnet to `persistent` (ADR-0003).
|
|
224
|
+
*
|
|
225
|
+
* Owner-only. Idempotent — promoting an already-persistent subnet
|
|
226
|
+
* returns its current state unchanged.
|
|
227
|
+
*/
|
|
228
|
+
async promoteSubnet(slug) {
|
|
229
|
+
return this.post(`/api/v1/subnets/${slug}/promote`);
|
|
204
230
|
}
|
|
205
231
|
/** Delete a subnet you own (requires Agent API Key — only the owning agent can delete) */
|
|
206
|
-
async deleteSubnet(
|
|
207
|
-
return this.request("DELETE", `/api/v1/subnets/${
|
|
232
|
+
async deleteSubnet(slug) {
|
|
233
|
+
return this.request("DELETE", `/api/v1/subnets/${slug}`);
|
|
208
234
|
}
|
|
209
235
|
/** Get agents in a subnet */
|
|
210
|
-
async getSubnetAgents(
|
|
211
|
-
return this.get(`/api/v1/subnets/${
|
|
236
|
+
async getSubnetAgents(slug) {
|
|
237
|
+
return this.get(`/api/v1/subnets/${slug}/agents`);
|
|
212
238
|
}
|
|
213
239
|
// ──────────────────────────────────────────────────────────────────────
|
|
214
240
|
// Subnet membership (agent-side)
|
|
@@ -223,18 +249,219 @@ var ACNClient = class {
|
|
|
223
249
|
// scheduled for removal. Requires ACN backend ≥ post-PR-#42.
|
|
224
250
|
// ──────────────────────────────────────────────────────────────────────
|
|
225
251
|
/** Join agent to subnet */
|
|
226
|
-
async joinSubnet(agentId,
|
|
227
|
-
return this.post(`/api/v1/agents/${agentId}/subnets/${
|
|
252
|
+
async joinSubnet(agentId, slug) {
|
|
253
|
+
return this.post(`/api/v1/agents/${agentId}/subnets/${slug}`);
|
|
228
254
|
}
|
|
229
255
|
/** Remove agent from subnet */
|
|
230
|
-
async leaveSubnet(agentId,
|
|
231
|
-
return this.delete(`/api/v1/agents/${agentId}/subnets/${
|
|
256
|
+
async leaveSubnet(agentId, slug) {
|
|
257
|
+
return this.delete(`/api/v1/agents/${agentId}/subnets/${slug}`);
|
|
232
258
|
}
|
|
233
259
|
/** Get agent's subnets */
|
|
234
260
|
async getAgentSubnets(agentId) {
|
|
235
261
|
return this.get(`/api/v1/agents/${agentId}/subnets`);
|
|
236
262
|
}
|
|
237
263
|
// ============================================
|
|
264
|
+
// ADR-0004 Subnet Admission
|
|
265
|
+
// ============================================
|
|
266
|
+
//
|
|
267
|
+
// 13 verbs gated by `subnet.join_policy === 'approval'`:
|
|
268
|
+
// - Allowlist (3): owner pre-authorisation.
|
|
269
|
+
// - Join requests (4): applicant-initiated path.
|
|
270
|
+
// - Invitations (5): owner-initiated path.
|
|
271
|
+
// - Agent-side (1): invitee's cross-subnet pending view.
|
|
272
|
+
//
|
|
273
|
+
// The plain `joinSubnet` verb dispatches the six-branch decision
|
|
274
|
+
// tree on the server side — these methods are the admin-side
|
|
275
|
+
// controls used by subnet owners and the per-row decisions used
|
|
276
|
+
// by applicants and invitees.
|
|
277
|
+
//
|
|
278
|
+
// Method names use the `subnet*` prefix to avoid colliding with
|
|
279
|
+
// the existing inbox `addToAllowlist` surface (which lives at
|
|
280
|
+
// `/api/v1/agents/{a}/allowlist/{target}` and is unrelated).
|
|
281
|
+
// ----- Allowlist (owner-only, 3 verbs) ---------------------------------
|
|
282
|
+
/**
|
|
283
|
+
* Pre-authorise `agentId` on `slug`'s allowlist (owner only).
|
|
284
|
+
*
|
|
285
|
+
* Allowlisted agents skip the approval queue: their next
|
|
286
|
+
* `joinSubnet` lands in branch 4 (allowlist hit) and becomes an
|
|
287
|
+
* immediate member with an `allowlist_auto` audit row.
|
|
288
|
+
*
|
|
289
|
+
* Server returns 201 with the persisted entry; duplicate adds
|
|
290
|
+
* return 409 ALREADY_ON_ALLOWLIST (raised as an error, never
|
|
291
|
+
* silently no-op'd).
|
|
292
|
+
*/
|
|
293
|
+
async subnetAllowlistAdd(slug, agentId) {
|
|
294
|
+
return this.post(`/api/v1/subnets/${slug}/allowlist`, {
|
|
295
|
+
agent_id: agentId
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Remove `agentId` from `slug`'s allowlist (owner only).
|
|
300
|
+
*
|
|
301
|
+
* Idempotent — removing an entry that doesn't exist still
|
|
302
|
+
* returns 204. Per ADR-0004 §"Allowlist mutation does not
|
|
303
|
+
* affect agents who already joined", this does NOT revoke
|
|
304
|
+
* membership for agents already admitted via the allowlist.
|
|
305
|
+
*/
|
|
306
|
+
async subnetAllowlistRemove(slug, agentId) {
|
|
307
|
+
await this.delete(`/api/v1/subnets/${slug}/allowlist/${agentId}`);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* List `slug`'s allowlist entries (owner only).
|
|
311
|
+
*
|
|
312
|
+
* Owner-only by design — the allowlist is a privacy-sensitive
|
|
313
|
+
* trust signal and exposing it publicly would leak relationship
|
|
314
|
+
* metadata.
|
|
315
|
+
*/
|
|
316
|
+
async subnetAllowlistList(slug, options) {
|
|
317
|
+
const params = {
|
|
318
|
+
limit: options?.limit ?? 100,
|
|
319
|
+
offset: options?.offset ?? 0
|
|
320
|
+
};
|
|
321
|
+
return this.get(`/api/v1/subnets/${slug}/allowlist`, params);
|
|
322
|
+
}
|
|
323
|
+
// ----- Join requests (4 verbs: 3 owner-side + 1 applicant-side) --------
|
|
324
|
+
/**
|
|
325
|
+
* Owner approves a pending join_request (CAS pending → approved).
|
|
326
|
+
*
|
|
327
|
+
* Side effects: applicant added to `subnet.member_agent_ids` and
|
|
328
|
+
* the `subnet.join_approved` webhook fires. The applicant is
|
|
329
|
+
* still expected to call `joinSubnet` to register the
|
|
330
|
+
* `agent.subnet_ids` back-reference (per ADR-0004 §"State
|
|
331
|
+
* machine edges").
|
|
332
|
+
*
|
|
333
|
+
* Optional `note` (≤500 chars) is recorded on the audit row.
|
|
334
|
+
*/
|
|
335
|
+
async subnetJoinRequestApprove(slug, requestId, options) {
|
|
336
|
+
return this.post(
|
|
337
|
+
`/api/v1/subnets/${slug}/join-requests/${requestId}/approve`,
|
|
338
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Owner rejects a pending join_request (CAS pending → rejected).
|
|
343
|
+
*
|
|
344
|
+
* No membership change. `subnet.join_rejected` webhook fires.
|
|
345
|
+
*/
|
|
346
|
+
async subnetJoinRequestReject(slug, requestId, options) {
|
|
347
|
+
return this.post(
|
|
348
|
+
`/api/v1/subnets/${slug}/join-requests/${requestId}/reject`,
|
|
349
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Applicant withdraws their own pending join_request.
|
|
354
|
+
*
|
|
355
|
+
* Self-only — caller must be the agent who originally created
|
|
356
|
+
* the request. `subnet.join_withdrawn` webhook fires.
|
|
357
|
+
*/
|
|
358
|
+
async subnetJoinRequestWithdraw(slug, requestId, options) {
|
|
359
|
+
return this.request(
|
|
360
|
+
"DELETE",
|
|
361
|
+
`/api/v1/subnets/${slug}/join-requests/${requestId}`,
|
|
362
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Owner lists join_request / allowlist_auto rows for `slug`.
|
|
367
|
+
*
|
|
368
|
+
* `kind` defaults to `'join_request'`; pass `'allowlist_auto'`
|
|
369
|
+
* to inspect synthesised allowlist-hit audit rows. Server
|
|
370
|
+
* rejects `kind='invitation'` with 400 INVALID_KIND_FILTER —
|
|
371
|
+
* use `subnetInvitationList` instead.
|
|
372
|
+
*/
|
|
373
|
+
async subnetJoinRequestList(slug, options) {
|
|
374
|
+
const params = {
|
|
375
|
+
kind: options?.kind ?? "join_request",
|
|
376
|
+
limit: options?.limit ?? 100,
|
|
377
|
+
offset: options?.offset ?? 0
|
|
378
|
+
};
|
|
379
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
380
|
+
return this.get(`/api/v1/subnets/${slug}/join-requests`, params);
|
|
381
|
+
}
|
|
382
|
+
// ----- Invitations (5 + 1 verbs) ---------------------------------------
|
|
383
|
+
/**
|
|
384
|
+
* Owner sends an invitation to `agentId` (or merges into a
|
|
385
|
+
* pending join_request from the same target).
|
|
386
|
+
*
|
|
387
|
+
* Two response shapes per ADR-0004 §"Invitation merge path":
|
|
388
|
+
*
|
|
389
|
+
* - **Normal path** (server returns 202): `{ invitation_id, status: 'pending' }`.
|
|
390
|
+
* - **Merge path** (server returns 200, request auto-approved):
|
|
391
|
+
* `{ auto_resolved: true, resolved_kind: 'join_request', request_id }`.
|
|
392
|
+
*
|
|
393
|
+
* Discriminate on `auto_resolved` to dispatch.
|
|
394
|
+
*/
|
|
395
|
+
async subnetInvitationSend(slug, agentId, options) {
|
|
396
|
+
const body = { agent_id: agentId };
|
|
397
|
+
if (options?.note !== void 0) body.note = options.note;
|
|
398
|
+
return this.post(`/api/v1/subnets/${slug}/invitations`, body);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Invitee accepts a pending invitation (CAS pending → approved).
|
|
402
|
+
*
|
|
403
|
+
* Self-only against the row's `agent_id`. Side effects: invitee
|
|
404
|
+
* added to `subnet.member_agent_ids`, the agent's `subnet_ids`
|
|
405
|
+
* gains the back-reference, and `subnet.invitation_accepted`
|
|
406
|
+
* webhook fires.
|
|
407
|
+
*/
|
|
408
|
+
async subnetInvitationAccept(slug, requestId, options) {
|
|
409
|
+
return this.post(
|
|
410
|
+
`/api/v1/subnets/${slug}/invitations/${requestId}/accept`,
|
|
411
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Invitee rejects a pending invitation (CAS pending → rejected).
|
|
416
|
+
*
|
|
417
|
+
* No membership change. `subnet.invitation_rejected` webhook
|
|
418
|
+
* fires.
|
|
419
|
+
*/
|
|
420
|
+
async subnetInvitationReject(slug, requestId, options) {
|
|
421
|
+
return this.post(
|
|
422
|
+
`/api/v1/subnets/${slug}/invitations/${requestId}/reject`,
|
|
423
|
+
options?.note !== void 0 ? { note: options.note } : void 0
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Owner cancels a pending invitation (CAS pending → withdrawn).
|
|
428
|
+
*
|
|
429
|
+
* Owner-only counterpart to applicant withdraw. The row goes to
|
|
430
|
+
* `withdrawn` (not `rejected`) — distinct audit token so
|
|
431
|
+
* consumers can tell "owner gave up" from "invitee said no".
|
|
432
|
+
*/
|
|
433
|
+
async subnetInvitationCancel(slug, requestId, options) {
|
|
434
|
+
return this.request(
|
|
435
|
+
"DELETE",
|
|
436
|
+
`/api/v1/subnets/${slug}/invitations/${requestId}`,
|
|
437
|
+
options?.note !== void 0 ? { body: { note: options.note } } : void 0
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Owner lists invitation rows for `slug`.
|
|
442
|
+
*
|
|
443
|
+
* Owner-only — invitees use `agentSubnetInvitations` for their
|
|
444
|
+
* own cross-subnet view.
|
|
445
|
+
*/
|
|
446
|
+
async subnetInvitationList(slug, options) {
|
|
447
|
+
const params = {
|
|
448
|
+
limit: options?.limit ?? 100,
|
|
449
|
+
offset: options?.offset ?? 0
|
|
450
|
+
};
|
|
451
|
+
if (options?.status !== void 0) params.status = options.status;
|
|
452
|
+
return this.get(`/api/v1/subnets/${slug}/invitations`, params);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Invitee's cross-subnet pending-invitation list (self only).
|
|
456
|
+
*
|
|
457
|
+
* Returns only `status='pending'` rows. Historical decisions
|
|
458
|
+
* are queryable per-subnet through the owner-only
|
|
459
|
+
* `subnetInvitationList`.
|
|
460
|
+
*/
|
|
461
|
+
async agentSubnetInvitations(agentId) {
|
|
462
|
+
return this.get(`/api/v1/agents/${agentId}/subnet-invitations`);
|
|
463
|
+
}
|
|
464
|
+
// ============================================
|
|
238
465
|
// Communication
|
|
239
466
|
// ============================================
|
|
240
467
|
/** Send message to an agent */
|
|
@@ -287,6 +514,31 @@ var ACNClient = class {
|
|
|
287
514
|
if (options?.consume) params.ack = true;
|
|
288
515
|
return this.get(`/api/v1/communication/history/${agentId}`, params);
|
|
289
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Precisely acknowledge (remove) specific messages from the inbox.
|
|
519
|
+
*
|
|
520
|
+
* Unlike `getMessageHistory({ consume: true })` which clears the entire inbox,
|
|
521
|
+
* this method removes only the messages whose `route_id` values are listed.
|
|
522
|
+
*
|
|
523
|
+
* @param agentId Must match the authenticated agent's ID.
|
|
524
|
+
* @param routeIds List of `route_id` values to remove (up to 500).
|
|
525
|
+
* @returns Number of messages actually removed.
|
|
526
|
+
*/
|
|
527
|
+
async ackInbox(agentId, routeIds) {
|
|
528
|
+
return this.post(`/api/v1/communication/history/${agentId}/ack`, { route_ids: routeIds });
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Update the lifecycle status of a specific inbox message.
|
|
532
|
+
*
|
|
533
|
+
* @param agentId Must match the authenticated agent's ID.
|
|
534
|
+
* @param routeId `route_id` of the target message (from inbox listing).
|
|
535
|
+
* @param status New status: `"unread"` | `"read"` | `"processed"`.
|
|
536
|
+
* @returns Object with `agent_id`, `route_id`, and `status`.
|
|
537
|
+
* @throws 404 (`inbox_message_not_found`) if route_id is absent from inbox.
|
|
538
|
+
*/
|
|
539
|
+
async updateInboxMessageStatus(agentId, routeId, status) {
|
|
540
|
+
return this.patch(`/api/v1/communication/history/${agentId}/${routeId}`, { status });
|
|
541
|
+
}
|
|
290
542
|
// ============================================
|
|
291
543
|
// Manifest Queue (Phase 2/3)
|
|
292
544
|
// ============================================
|
|
@@ -863,8 +1115,8 @@ var ACNClient = class {
|
|
|
863
1115
|
* Register (or clear) an org-harness webhook URL for a subnet.
|
|
864
1116
|
* Pass `harnessUrl: null` to deregister.
|
|
865
1117
|
*/
|
|
866
|
-
async registerSubnetHarness(
|
|
867
|
-
await this.request("PATCH", `/api/v1/subnets/${
|
|
1118
|
+
async registerSubnetHarness(slug, harnessUrl, harnessSecret) {
|
|
1119
|
+
await this.request("PATCH", `/api/v1/subnets/${slug}/harness`, {
|
|
868
1120
|
body: {
|
|
869
1121
|
harness_url: harnessUrl,
|
|
870
1122
|
harness_secret: harnessSecret ?? null
|
|
@@ -880,6 +1132,7 @@ var ACNError = class extends Error {
|
|
|
880
1132
|
this.errorCode = options?.errorCode;
|
|
881
1133
|
this.requestId = options?.requestId;
|
|
882
1134
|
}
|
|
1135
|
+
status;
|
|
883
1136
|
/** ACN internal error code (present on sanitised 5xx responses) */
|
|
884
1137
|
errorCode;
|
|
885
1138
|
/** Request ID minted by ACN for 5xx responses (useful for support) */
|
|
@@ -1098,10 +1351,16 @@ var KNOWN_PAYMENT_TASK_STATUSES = [
|
|
|
1098
1351
|
"payment_failed",
|
|
1099
1352
|
"refunded"
|
|
1100
1353
|
];
|
|
1354
|
+
var KNOWN_INBOX_MESSAGE_STATUSES = [
|
|
1355
|
+
"unread",
|
|
1356
|
+
"read",
|
|
1357
|
+
"processed"
|
|
1358
|
+
];
|
|
1101
1359
|
export {
|
|
1102
1360
|
ACNClient,
|
|
1103
1361
|
ACNError,
|
|
1104
1362
|
ACNRealtime,
|
|
1363
|
+
KNOWN_INBOX_MESSAGE_STATUSES,
|
|
1105
1364
|
KNOWN_PAYMENT_TASK_STATUSES,
|
|
1106
1365
|
subscribeToACN
|
|
1107
1366
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "acn-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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"
|