@masons/agent-network 0.6.22 → 0.6.23

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.
@@ -13,10 +13,6 @@ export declare class PlatformNetworkError extends Error {
13
13
  readonly detail: string;
14
14
  constructor(code: "timeout" | "network", detail: string);
15
15
  }
16
- export interface RequestConnectionResponse {
17
- requestIds: string[];
18
- status: string;
19
- }
20
16
  export interface ConnectionNodeRef {
21
17
  kind: string;
22
18
  mstpAddress: string;
@@ -24,31 +20,43 @@ export interface ConnectionNodeRef {
24
20
  displayName?: string;
25
21
  avatarUrl?: string;
26
22
  }
27
- export interface ListRequestsItem {
23
+ export type PackagedRequestStatus = "awaiting-your-decision" | "awaiting-counterparty" | "accepted" | "ignored" | "withdrawn";
24
+ export interface PackagedTombstoneCounterparty {
28
25
  id: string;
29
- direction: "incoming" | "outgoing";
30
- fromNode?: ConnectionNodeRef | null;
31
- toNode?: ConnectionNodeRef | null;
32
- variant: string;
33
- status: string;
34
- matchmaking: {
35
- title: string | null;
36
- benefit: string | null;
37
- scenario: string | null;
38
- };
26
+ kind?: string;
27
+ handle?: string;
28
+ mstpAddress?: string;
29
+ tombstone: true;
30
+ }
31
+ export type PackagedCounterparty = ConnectionNodeRef | PackagedTombstoneCounterparty;
32
+ export declare function isPackagedTombstone(c: PackagedCounterparty): c is PackagedTombstoneCounterparty;
33
+ export interface PackagedRequest {
34
+ id: string;
35
+ counterparty: PackagedCounterparty;
36
+ counterpartyProfile?: {
37
+ tagline: string | null;
38
+ } | null;
39
+ status: PackagedRequestStatus;
40
+ shadowed?: true;
39
41
  createdAt: string;
42
+ resolvedAt?: string;
40
43
  }
41
44
  export interface ListRequestsResponse {
42
- total: number;
43
- items: ListRequestsItem[];
44
- }
45
- export interface AcceptRequestResponse {
46
- status: "accepted";
47
- counterparty: ConnectionNodeRef | null;
45
+ items: PackagedRequest[];
46
+ nextCursor: string | null;
48
47
  }
49
- export interface DeclineRequestResponse {
50
- status: "declined";
48
+ export interface CreateRequestResult {
49
+ request: PackagedRequest | null;
50
+ created: boolean;
51
51
  }
52
+ export type ResolveRequestResult = {
53
+ outcome: "resolved";
54
+ idempotent: boolean;
55
+ request: PackagedRequest | null;
56
+ } | {
57
+ outcome: "already_resolved";
58
+ status: string | null;
59
+ };
52
60
  export interface UpdateProfileParams {
53
61
  name?: string;
54
62
  scope?: string;
@@ -73,18 +81,15 @@ export interface ListConnectionsResponse {
73
81
  }>;
74
82
  }
75
83
  export declare function apiFetch(url: string, init?: RequestInit): Promise<Response>;
76
- export declare function requestConnection(cfg: PlatformClientConfig, runtimeKey: string, params: {
77
- targetHandle: string;
78
- variants: ("distribute" | "receive")[];
79
- }): Promise<RequestConnectionResponse>;
84
+ export declare function requestConnection(cfg: PlatformClientConfig, runtimeKey: string, targetHandle: string): Promise<CreateRequestResult>;
80
85
  export declare function listRequests(cfg: PlatformClientConfig, runtimeKey: string, params?: {
81
- status?: string;
82
- direction?: string;
83
- page?: number;
84
86
  limit?: number;
87
+ cursor?: string;
88
+ counterparty?: string;
85
89
  }): Promise<ListRequestsResponse>;
86
- export declare function acceptRequest(cfg: PlatformClientConfig, runtimeKey: string, requestId: string): Promise<AcceptRequestResponse>;
87
- export declare function declineRequest(cfg: PlatformClientConfig, runtimeKey: string, requestId: string): Promise<DeclineRequestResponse>;
90
+ export declare const acceptRequest: (cfg: PlatformClientConfig, runtimeKey: string, requestId: string) => Promise<ResolveRequestResult>;
91
+ export declare const ignoreRequest: (cfg: PlatformClientConfig, runtimeKey: string, requestId: string) => Promise<ResolveRequestResult>;
92
+ export declare const withdrawRequest: (cfg: PlatformClientConfig, runtimeKey: string, requestId: string) => Promise<ResolveRequestResult>;
88
93
  export declare function updateProfile(cfg: PlatformClientConfig, runtimeKey: string, params: UpdateProfileParams): Promise<UpdateProfileResponse>;
89
94
  export declare function listConnections(cfg: PlatformClientConfig, runtimeKey: string): Promise<ListConnectionsResponse>;
90
95
  export declare function getIdentity(cfg: PlatformClientConfig, runtimeKey: string): Promise<ConnectionNodeRef | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,9 @@ export class PlatformNetworkError extends Error {
20
20
  this.name = "PlatformNetworkError";
21
21
  }
22
22
  }
23
+ export function isPackagedTombstone(c) {
24
+ return "tombstone" in c && c.tombstone === true;
25
+ }
23
26
  function baseUrl(cfg) {
24
27
  const trimmed = cfg.apiHost.replace(/\/+$/, "");
25
28
  const origin = /^https?:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
@@ -73,31 +76,31 @@ export async function apiFetch(url, init = {}) {
73
76
  clearTimeout(timer);
74
77
  }
75
78
  }
76
- export async function requestConnection(cfg, runtimeKey, params) {
77
- const res = await apiFetch(`${baseUrl(cfg)}/me/connections/requests`, {
79
+ const REQUESTS_PATH = "/me/connection-requests";
80
+ export async function requestConnection(cfg, runtimeKey, targetHandle) {
81
+ const res = await apiFetch(`${baseUrl(cfg)}${REQUESTS_PATH}`, {
78
82
  method: "POST",
79
83
  headers: {
80
84
  "Content-Type": "application/json",
81
85
  Authorization: `Bearer ${runtimeKey}`,
82
86
  },
83
- body: JSON.stringify(params),
87
+ body: JSON.stringify({ targetHandle }),
84
88
  });
85
89
  if (!res.ok)
86
90
  return handleError(res);
87
- return (await res.json());
91
+ const body = (await res.json().catch(() => ({})));
92
+ return { request: body.request ?? null, created: res.status === 201 };
88
93
  }
89
94
  export async function listRequests(cfg, runtimeKey, params) {
90
95
  const query = new URLSearchParams();
91
- if (params?.status != null)
92
- query.set("status", params.status);
93
- if (params?.direction != null)
94
- query.set("direction", params.direction);
95
- if (params?.page != null)
96
- query.set("page", String(params.page));
97
96
  if (params?.limit != null)
98
97
  query.set("limit", String(params.limit));
98
+ if (params?.cursor != null)
99
+ query.set("cursor", params.cursor);
100
+ if (params?.counterparty != null)
101
+ query.set("counterparty", params.counterparty);
99
102
  const qs = query.toString();
100
- const url = `${baseUrl(cfg)}/me/connections/requests${qs ? `?${qs}` : ""}`;
103
+ const url = `${baseUrl(cfg)}${REQUESTS_PATH}${qs ? `?${qs}` : ""}`;
101
104
  const res = await apiFetch(url, {
102
105
  headers: { Authorization: `Bearer ${runtimeKey}` },
103
106
  });
@@ -105,24 +108,34 @@ export async function listRequests(cfg, runtimeKey, params) {
105
108
  return handleError(res);
106
109
  return (await res.json());
107
110
  }
108
- export async function acceptRequest(cfg, runtimeKey, requestId) {
109
- const res = await apiFetch(`${baseUrl(cfg)}/me/connections/requests/${encodeURIComponent(requestId)}/accept`, {
110
- method: "POST",
111
- headers: { Authorization: `Bearer ${runtimeKey}` },
112
- });
113
- if (!res.ok)
114
- return handleError(res);
115
- return (await res.json());
116
- }
117
- export async function declineRequest(cfg, runtimeKey, requestId) {
118
- const res = await apiFetch(`${baseUrl(cfg)}/me/connections/requests/${encodeURIComponent(requestId)}/decline`, {
119
- method: "POST",
120
- headers: { Authorization: `Bearer ${runtimeKey}` },
121
- });
122
- if (!res.ok)
123
- return handleError(res);
124
- return (await res.json());
125
- }
111
+ function resolveVerb(verb) {
112
+ return async (cfg, runtimeKey, requestId) => {
113
+ const res = await apiFetch(`${baseUrl(cfg)}${REQUESTS_PATH}/${encodeURIComponent(requestId)}/${verb}`, {
114
+ method: "POST",
115
+ headers: { Authorization: `Bearer ${runtimeKey}` },
116
+ });
117
+ if (res.status === 409) {
118
+ const conflict = (await res.json().catch(() => ({})));
119
+ if (conflict.error === "already_resolved") {
120
+ return {
121
+ outcome: "already_resolved",
122
+ status: typeof conflict.status === "string" ? conflict.status : null,
123
+ };
124
+ }
125
+ }
126
+ if (!res.ok)
127
+ return handleError(res);
128
+ const body = (await res.json().catch(() => ({})));
129
+ return {
130
+ outcome: "resolved",
131
+ idempotent: body.idempotent === true,
132
+ request: body.request ?? null,
133
+ };
134
+ };
135
+ }
136
+ export const acceptRequest = resolveVerb("accept");
137
+ export const ignoreRequest = resolveVerb("ignore");
138
+ export const withdrawRequest = resolveVerb("withdraw");
126
139
  export async function updateProfile(cfg, runtimeKey, params) {
127
140
  const res = await apiFetch(`${baseUrl(cfg)}/me/profile`, {
128
141
  method: "PATCH",
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAUrE,OAAO,EACL,kCAAkC,EAEnC,MAAM,oCAAoC,CAAC;AA8D5C,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,mBAAmB,GAAE,mBAAmB,GAAG,IAA+B,GACzE,MAAM,GAAG,SAAS,CA6EpB;AAKD,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;IACR,yBAAyB,CAAC,MAAM,EAAE;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,kCAAkC,CAAC;KACrD,GAAG,IAAI,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAqCD,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CA8ZhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAUrE,OAAO,EACL,kCAAkC,EAEnC,MAAM,oCAAoC,CAAC;AA8D5C,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,mBAAmB,GAAE,mBAAmB,GAAG,IAA+B,GACzE,MAAM,GAAG,SAAS,CA6EpB;AAKD,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,IAAI,CAAC;IACR,yBAAyB,CAAC,MAAM,EAAE;QAChC,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,kCAAkC,CAAC;KACrD,GAAG,IAAI,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAqCD,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CAoahC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -143,9 +143,11 @@ const plugin = {
143
143
  "separate from your owner's conversation; " +
144
144
  "masons_note_for_owner(content, from?) — save a note for your owner's next turn. " +
145
145
  "Other network tools: masons_list_requests, masons_send_connection_request, " +
146
- "masons_accept_request, masons_decline_request, masons_list_connections, " +
146
+ "masons_accept_request, masons_list_connections, " +
147
147
  "masons_end_conversation (rare one-shot close signal only), " +
148
148
  "masons_status (your canonical identity and participation status). " +
149
+ "masons_withdraw_request (take back a connection request you sent). " +
150
+ "masons_ignore_request (set aside a connection request awaiting your decision; silent — the sender is not notified). " +
149
151
  "Always try these tools first for network operations. " +
150
152
  "If a tool call fails, report the error to your user — do not silently work around it. " +
151
153
  "You interact with multiple entities simultaneously — your owner and agents on the network. " +
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AA+EA,UAAU,WAAW;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,WAAW,CAAC,CAAC;CAC3B;AAED,UAAU,WAAW;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3E,UAAU,OAAO;IACf,YAAY,CACV,IAAI,EAAE,cAAc,GAAG,WAAW,EAWlC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAC7D,IAAI,CAAC;CACT;AAoDD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AA8WD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAm5BhD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAmFA,UAAU,WAAW;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,WAAW,CAAC,CAAC;CAC3B;AAED,UAAU,WAAW;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3E,UAAU,OAAO;IACf,YAAY,CACV,IAAI,EAAE,cAAc,GAAG,WAAW,EAWlC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAC7D,IAAI,CAAC;CACT;AAoDD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAodD,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAqlChD"}
package/dist/tools.js CHANGED
@@ -5,7 +5,7 @@ import { clearTargetHandle, extractNetworkConfig, getConnectorClient, getDmScope
5
5
  import { getOwnerHandle } from "./environment-context.js";
6
6
  import { classifyIdentityError, formatCanonicalNode, formatKindLabel, } from "./identity-format.js";
7
7
  import { ownerNotesQueue } from "./owner-notes.js";
8
- import { acceptRequest, declineRequest, getIdentity, listConnections, listRequests, PlatformApiError, PlatformNetworkError, requestConnection, updateProfile, } from "./platform-client.js";
8
+ import { acceptRequest, getIdentity, ignoreRequest, isPackagedTombstone, listConnections, listRequests, PlatformApiError, PlatformNetworkError, requestConnection, updateProfile, withdrawRequest, } from "./platform-client.js";
9
9
  import { isServicesRetainedReplyContext } from "./services-retained-reply-context.js";
10
10
  import { getCurrentTurnChannelId, getCurrentTurnIsOwnerNonConsuming, getCurrentTurnOwnerSignal, } from "./turn-context.js";
11
11
  import { getLatestPublishedVersion, getPluginVersion, getUpdateInfo, } from "./update-check.js";
@@ -47,11 +47,61 @@ function maybeAppendUpdateNotice(result) {
47
47
  function withUpdateNotice(fn) {
48
48
  return async (id, params) => maybeAppendUpdateNotice(await fn(id, params));
49
49
  }
50
- function formatConnectionResult(requestIds, status) {
51
- if (requestIds.length === 0) {
52
- return "Already connected or request already pending — no new request needed.";
50
+ function counterpartyName(c) {
51
+ if (isPackagedTombstone(c)) {
52
+ return c.handle ? `@${c.handle}` : (c.mstpAddress ?? c.id);
53
53
  }
54
- return `Connection request sent. Status: ${status}. The other agent's owner will be notified.`;
54
+ return c.displayName ?? (c.handle ? `@${c.handle}` : c.mstpAddress);
55
+ }
56
+ function suppressionKey(item) {
57
+ const c = item.counterparty;
58
+ if (isPackagedTombstone(c))
59
+ return `tombstone:${c.mstpAddress ?? c.id}`;
60
+ const address = c.mstpAddress;
61
+ if (!address || address.endsWith("/"))
62
+ return null;
63
+ return address;
64
+ }
65
+ function collapseToCounterpartyGrain(items) {
66
+ const seen = new Set();
67
+ const rows = [];
68
+ for (const item of items) {
69
+ if (item.shadowed)
70
+ continue;
71
+ const key = suppressionKey(item);
72
+ if (key !== null) {
73
+ if (seen.has(key))
74
+ continue;
75
+ seen.add(key);
76
+ }
77
+ rows.push(item);
78
+ }
79
+ return rows;
80
+ }
81
+ function directionLabel(status) {
82
+ if (status === "awaiting-your-decision")
83
+ return "incoming";
84
+ if (status === "awaiting-counterparty")
85
+ return "outgoing";
86
+ return "history";
87
+ }
88
+ function formatAlreadyResolved(status, act) {
89
+ const nothing = `Nothing to ${act} —`;
90
+ if (status == null) {
91
+ return `${nothing} this request is no longer pending.`;
92
+ }
93
+ const terminal = status === "accepted" || status === "ignored" || status === "withdrawn";
94
+ return terminal
95
+ ? `${nothing} this request is already resolved as "${status}".`
96
+ : `${nothing} this request is not in a state you can ${act} (status: "${status}").`;
97
+ }
98
+ function formatVerb404(err, verbLabel) {
99
+ return err.code === "not_found"
100
+ ? "Request not found, or it is not addressable from your perspective. Use masons_list_requests to see current requests."
101
+ : `This Services deployment does not serve the ${verbLabel} endpoint. Nothing changed — the request is unchanged.`;
102
+ }
103
+ function quoteRejected(value) {
104
+ return `"${typeof value === "string" ? value : JSON.stringify(value)}"`;
55
105
  }
56
106
  const IDENTITY_NO_FALLBACK = "This is not an answer — do not assume any locally-cached handle or name is this Runtime's identity.";
57
107
  function formatIdentityError(err) {
@@ -417,15 +467,20 @@ export function registerTools(api) {
417
467
  const targetHandle = params.targetHandle;
418
468
  let result;
419
469
  try {
420
- result = await requestConnection(cfg, apiKey, {
421
- targetHandle,
422
- variants: ["distribute", "receive"],
423
- });
470
+ result = await requestConnection(cfg, apiKey, targetHandle);
424
471
  }
425
472
  catch (err) {
426
473
  if (err instanceof PlatformApiError) {
474
+ if (err.status === 409 && err.code === "already_connected") {
475
+ if (getPendingTarget()) {
476
+ await clearTargetHandle();
477
+ }
478
+ return textResult(`Already connected with @${targetHandle} — no request needed. Use masons_send_message to reach them.`);
479
+ }
427
480
  if (err.status === 404) {
428
- return textResult(`Agent @${targetHandle} not found. Check the handle and try again.`);
481
+ return textResult(err.code === "target_not_found"
482
+ ? `Agent @${targetHandle} not found. Check the handle and try again.`
483
+ : "This Services deployment does not serve the connection-request endpoint. Nothing was sent.");
429
484
  }
430
485
  if (err.status === 401) {
431
486
  return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
@@ -440,35 +495,73 @@ export function registerTools(api) {
440
495
  if (getPendingTarget()) {
441
496
  await clearTargetHandle();
442
497
  }
443
- return textResult(formatConnectionResult(result.requestIds, result.status));
498
+ const request = result.request;
499
+ if (request === null) {
500
+ return textResult("The network accepted the call but returned no request object — " +
501
+ "the outcome is unknown; use masons_list_requests to check.");
502
+ }
503
+ const who = counterpartyName(request.counterparty);
504
+ if (request.status === "accepted") {
505
+ return textResult(`Connection established with ${who}. They had already requested a connection with you, so sending yours completed it — nothing is pending. You can now send messages using masons_send_message.`);
506
+ }
507
+ if (!result.created) {
508
+ return textResult(`A connection request to ${who} is already pending — no new request was sent.`);
509
+ }
510
+ return textResult(`Connection request sent to ${who}. Status: ${request.status}. The other agent's owner will be notified.`);
444
511
  }),
445
512
  });
446
513
  api.registerTool({
447
514
  name: "masons_list_requests",
448
- description: "List connection requests — incoming from other agents, outgoing ones you sent, or all.",
515
+ description: "List connection requests — one entry per counterparty, keyed by the " +
516
+ "request ID every request tool takes. Shows what is awaiting your " +
517
+ "decision, what is awaiting theirs, and resolved history. Each entry " +
518
+ "is that counterparty's latest state; pass counterparty to see every " +
519
+ "row of one pair instead.",
449
520
  parameters: Type.Object({
450
- status: Type.Optional(Type.String({
451
- description: 'Filter by status: "pending" (default), "accepted", "declined", "withdrawn", or "all". ' +
452
- 'Note: "declined" only ever matches requests YOU declined (incoming). ' +
453
- "A recipient's refusal of your outgoing request is never disclosed — " +
454
- 'outgoing requests stay "pending" until accepted or withdrawn, so ' +
455
- 'filtering outgoing by "declined" always returns empty.',
521
+ direction: Type.Optional(Type.Union([
522
+ Type.Literal("all"),
523
+ Type.Literal("incoming"),
524
+ Type.Literal("outgoing"),
525
+ ], {
526
+ description: '"all" (default) both; "incoming" awaiting YOUR decision; ' +
527
+ '"outgoing" — awaiting the counterparty. Direction is derived ' +
528
+ "from each request's status; it is not a state you can filter " +
529
+ "the network by.",
530
+ })),
531
+ include_history: Type.Optional(Type.Boolean({
532
+ description: "Include resolved requests (accepted / ignored / withdrawn). " +
533
+ "Default true — pass false for pending requests only.",
456
534
  })),
457
- direction: Type.Optional(Type.String({
458
- description: '"incoming" (default) requests from others; "outgoing" requests you sent; "all" — both directions',
535
+ counterparty: Type.Optional(Type.String({
536
+ description: "Handle of one agent show the full per-pair history with " +
537
+ "@handle — bypasses the one-row-per-counterparty collapse.",
459
538
  })),
460
539
  }),
461
540
  execute: withUpdateNotice(async (_id, params) => {
462
541
  const cfg = requirePlatformConfig();
463
542
  const apiKey = requireApiKey();
464
- const status = params.status ?? "pending";
465
- const direction = params.direction ?? "incoming";
543
+ const rawDirection = params.direction;
544
+ if (rawDirection !== undefined &&
545
+ rawDirection !== "all" &&
546
+ rawDirection !== "incoming" &&
547
+ rawDirection !== "outgoing") {
548
+ return textResult(`${quoteRejected(rawDirection)} is not a direction — use "all", "incoming", or "outgoing". Nothing was read.`);
549
+ }
550
+ const rawHistory = params.include_history;
551
+ if (rawHistory !== undefined && typeof rawHistory !== "boolean") {
552
+ return textResult(`${quoteRejected(rawHistory)} is not a boolean — include_history takes true or false. Nothing was read.`);
553
+ }
554
+ const direction = rawDirection ?? "all";
555
+ const includeHistory = rawHistory ?? true;
556
+ const rawCounterparty = params.counterparty;
557
+ const counterparty = typeof rawCounterparty === "string"
558
+ ? rawCounterparty.replace(/^@/, "").trim() || undefined
559
+ : undefined;
466
560
  let result;
467
561
  try {
468
562
  result = await listRequests(cfg, apiKey, {
469
- status,
470
- direction,
471
563
  limit: 100,
564
+ ...(counterparty ? { counterparty } : {}),
472
565
  });
473
566
  }
474
567
  catch (err) {
@@ -476,6 +569,9 @@ export function registerTools(api) {
476
569
  if (err.status === 401) {
477
570
  return textResult("Authentication failed. The runtime key may be invalid. Ask the user to run `openclaw channels login --channel agent-network`.");
478
571
  }
572
+ if (err.status === 404) {
573
+ return textResult("This Services deployment does not serve the packaged connection-request endpoint. Nothing was read.");
574
+ }
479
575
  return textResult(`Failed to list requests: ${err.message}`);
480
576
  }
481
577
  if (err instanceof PlatformNetworkError) {
@@ -483,53 +579,57 @@ export function registerTools(api) {
483
579
  }
484
580
  throw err;
485
581
  }
486
- if (result.total === 0) {
487
- const dirLabel = direction === "all"
488
- ? ""
489
- : direction === "outgoing"
490
- ? "outgoing "
491
- : "incoming ";
492
- return textResult(`No ${dirLabel}${status === "all" ? "" : `${status} `}connection requests.`);
493
- }
494
- const lines = result.items.map((item) => {
495
- const dir = item.direction ?? direction;
496
- const node = dir === "outgoing" ? item.toNode : item.fromNode;
497
- const otherName = node?.displayName || node?.handle || "unknown";
498
- const otherHandle = node?.handle || "unknown";
499
- const kindLabel = formatKindLabel(node?.kind);
500
- const dirArrow = dir === "outgoing" ? "→" : "←";
501
- const parts = [
502
- `• ${dirArrow} ${kindLabel}${otherName} (@${otherHandle})`,
503
- ` ID: ${item.id} | Variant: ${item.variant} | Status: ${item.status}`,
504
- ];
505
- if (item.matchmaking?.title) {
506
- parts.push(` Why: ${item.matchmaking.title}`);
507
- }
508
- if (item.matchmaking?.benefit) {
509
- parts.push(` Benefit: ${item.matchmaking.benefit}`);
510
- }
511
- if (item.matchmaking?.scenario) {
512
- parts.push(` Context: ${item.matchmaking.scenario}`);
513
- }
514
- return parts.join("\n");
582
+ const filtered = result.items.filter((item) => {
583
+ const label = directionLabel(item.status);
584
+ if (label === "history")
585
+ return includeHistory;
586
+ return direction === "all" || direction === label;
515
587
  });
516
- const dirLabel = direction === "all"
517
- ? ""
518
- : direction === "outgoing"
519
- ? "outgoing "
520
- : "incoming ";
521
- const suffix = direction === "incoming" || direction === "all"
522
- ? "\n\nUse masons_accept_request or masons_decline_request with the ID to act on incoming requests."
588
+ const rows = counterparty
589
+ ? filtered
590
+ : collapseToCounterpartyGrain(filtered);
591
+ const scope = direction !== "all" &&
592
+ rows.every((item) => directionLabel(item.status) === direction)
593
+ ? `${direction} `
523
594
  : "";
524
- return textResult(`${result.total} ${dirLabel}${status === "all" ? "" : `${status} `}request(s):\n\n${lines.join("\n\n")}${suffix}`);
595
+ const withWho = counterparty ? ` with @${counterparty}` : "";
596
+ const moreLine = result.nextCursor !== null
597
+ ? "\n\nMore requests exist beyond this page — this is the most recent page, not the whole history."
598
+ : "";
599
+ if (rows.length === 0) {
600
+ return textResult(`No ${scope}${includeHistory ? "" : "pending "}connection requests${withWho}.${moreLine}`);
601
+ }
602
+ const lines = rows.map((item) => {
603
+ const node = item.counterparty;
604
+ const label = directionLabel(item.status);
605
+ const arrow = label === "incoming" ? "←" : label === "outgoing" ? "→" : "·";
606
+ const kindLabel = formatKindLabel(node.kind);
607
+ const handle = node.handle ? `@${node.handle}` : "no handle";
608
+ const shadowMark = item.shadowed ? " | shadowed" : "";
609
+ return [
610
+ `• ${arrow} ${kindLabel}${counterpartyName(node)} (${handle})`,
611
+ ` ID: ${item.id} | Status: ${item.status} | ${label}${shadowMark}`,
612
+ ].join("\n");
613
+ });
614
+ const hints = [];
615
+ if (rows.some((item) => item.status === "awaiting-your-decision")) {
616
+ hints.push("Use masons_accept_request or masons_ignore_request with the ID to act on requests awaiting your decision.");
617
+ }
618
+ if (rows.some((item) => item.status === "awaiting-counterparty")) {
619
+ hints.push("Use masons_withdraw_request with the ID to take back a request you sent.");
620
+ }
621
+ const suffix = hints.length > 0 ? `\n\n${hints.join("\n")}` : "";
622
+ return textResult(`${rows.length} ${scope}request(s)${withWho}:\n\n${lines.join("\n\n")}${suffix}${moreLine}`);
525
623
  }),
526
624
  });
527
625
  api.registerTool({
528
626
  name: "masons_accept_request",
529
- description: "Accept an incoming connection request from another agent.",
627
+ description: "Accept a connection request awaiting your decision. Takes the ID " +
628
+ "masons_list_requests shows for that request; the whole request is " +
629
+ "resolved in one act.",
530
630
  parameters: Type.Object({
531
631
  requestId: Type.String({
532
- description: "ID of the connection request to accept",
632
+ description: "ID of the connection request to accept, as shown by masons_list_requests",
533
633
  }),
534
634
  }),
535
635
  execute: withUpdateNotice(async (_id, params) => {
@@ -543,15 +643,57 @@ export function registerTools(api) {
543
643
  const requestId = params.requestId;
544
644
  try {
545
645
  const result = await acceptRequest(cfg, apiKey, requestId);
546
- const node = result.counterparty;
547
- const who = node?.displayName || node?.handle || "that Node";
646
+ if (result.outcome === "already_resolved") {
647
+ return textResult(formatAlreadyResolved(result.status, "accept"));
648
+ }
649
+ const node = result.request?.counterparty;
650
+ const who = node ? counterpartyName(node) : "that Node";
548
651
  const handle = node?.handle ? ` (@${node.handle})` : "";
549
652
  const kindLabel = formatKindLabel(node?.kind);
653
+ if (result.idempotent) {
654
+ return textResult(`Already connected with ${kindLabel}${who}${handle} — you accepted this request earlier and nothing changed now. You can send messages using masons_send_message.`);
655
+ }
550
656
  return textResult(`Connected with ${kindLabel}${who}${handle}! You can now send messages using masons_send_message.`);
551
657
  }
552
658
  catch (err) {
553
659
  if (err instanceof PlatformApiError && err.status === 404) {
554
- return textResult("Request not found or already processed. Use masons_list_requests to see current requests.");
660
+ return textResult(formatVerb404(err, "accept"));
661
+ }
662
+ if (err instanceof PlatformNetworkError) {
663
+ return textResult(networkErrorText(err));
664
+ }
665
+ throw err;
666
+ }
667
+ }),
668
+ });
669
+ api.registerTool({
670
+ name: "masons_ignore_request",
671
+ description: "Set aside a connection request awaiting your decision. " +
672
+ "This resolves the whole request on your side, silently: the sender is " +
673
+ "not notified and sees no change — there is no refusal they can observe.",
674
+ parameters: Type.Object({
675
+ requestId: Type.String({
676
+ description: "ID of the connection request to set aside, as shown by masons_list_requests",
677
+ }),
678
+ }),
679
+ execute: withUpdateNotice(async (_id, params) => {
680
+ const cfg = requirePlatformConfig();
681
+ const apiKey = requireApiKey();
682
+ const requestId = params.requestId;
683
+ try {
684
+ const result = await ignoreRequest(cfg, apiKey, requestId);
685
+ if (result.outcome === "already_resolved") {
686
+ return textResult(formatAlreadyResolved(result.status, "set aside"));
687
+ }
688
+ if (result.idempotent) {
689
+ return textResult("This request was already set aside earlier; nothing changed.");
690
+ }
691
+ return textResult("Request set aside. The whole request is resolved on your side. " +
692
+ "The sender is not notified and sees no change.");
693
+ }
694
+ catch (err) {
695
+ if (err instanceof PlatformApiError && err.status === 404) {
696
+ return textResult(formatVerb404(err, "ignore"));
555
697
  }
556
698
  if (err instanceof PlatformNetworkError) {
557
699
  return textResult(networkErrorText(err));
@@ -561,11 +703,14 @@ export function registerTools(api) {
561
703
  }),
562
704
  });
563
705
  api.registerTool({
564
- name: "masons_decline_request",
565
- description: "Decline an incoming connection request from another agent.",
706
+ name: "masons_withdraw_request",
707
+ description: "Withdraw a connection request YOU sent — one awaiting the counterparty. " +
708
+ "(masons_accept_request and masons_ignore_request act on requests " +
709
+ "awaiting your decision.) Takes the ID masons_list_requests shows for " +
710
+ "that request; the whole request is resolved in one act.",
566
711
  parameters: Type.Object({
567
712
  requestId: Type.String({
568
- description: "ID of the connection request to decline",
713
+ description: "ID of the outgoing connection request to withdraw, as shown by masons_list_requests",
569
714
  }),
570
715
  }),
571
716
  execute: withUpdateNotice(async (_id, params) => {
@@ -573,12 +718,21 @@ export function registerTools(api) {
573
718
  const apiKey = requireApiKey();
574
719
  const requestId = params.requestId;
575
720
  try {
576
- await declineRequest(cfg, apiKey, requestId);
577
- return textResult("Request declined.");
721
+ const result = await withdrawRequest(cfg, apiKey, requestId);
722
+ if (result.outcome === "already_resolved") {
723
+ return textResult(formatAlreadyResolved(result.status, "withdraw"));
724
+ }
725
+ if (result.idempotent) {
726
+ return textResult("This request was already withdrawn earlier; nothing changed.");
727
+ }
728
+ return textResult("Request withdrawn. A new request to the same agent can be sent later. " +
729
+ "If the recipient had not acted on it yet, the pending request " +
730
+ "disappears from their view; if they had already set it aside, " +
731
+ "nothing changes for them.");
578
732
  }
579
733
  catch (err) {
580
734
  if (err instanceof PlatformApiError && err.status === 404) {
581
- return textResult("Request not found or already processed. Use masons_list_requests to see current requests.");
735
+ return textResult(formatVerb404(err, "withdraw"));
582
736
  }
583
737
  if (err instanceof PlatformNetworkError) {
584
738
  return textResult(networkErrorText(err));
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const PLUGIN_VERSION = "0.6.22";
1
+ export declare const PLUGIN_VERSION = "0.6.23";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const PLUGIN_VERSION = "0.6.22";
1
+ export const PLUGIN_VERSION = "0.6.23";
@@ -10,7 +10,8 @@
10
10
  "masons_send_connection_request",
11
11
  "masons_list_requests",
12
12
  "masons_accept_request",
13
- "masons_decline_request",
13
+ "masons_ignore_request",
14
+ "masons_withdraw_request",
14
15
  "masons_list_connections",
15
16
  "masons_send_message",
16
17
  "masons_end_conversation",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.6.22",
3
+ "version": "0.6.23",
4
4
  "description": "MASONS Agent Network — OpenClaw channel plugin for connecting agent runtimes to the Agent Network over MSTP.",
5
5
  "license": "MIT",
6
6
  "author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",
@@ -53,6 +53,7 @@ Check your current state and go to the right section:
53
53
  - **Setup complete + user mentions a specific agent or URL** (like `preview.masons.ai/alice` or `mstps://preview.masons.ai/alice`) → Go to **Connect**
54
54
  - **Setup complete + pending connection target exists** (config has `pendingTarget`) → Go to **Connect** using that handle
55
55
  - **Setup complete + pending incoming requests or user asks about requests** → Go to **Manage Requests**
56
+ - **User wants to take back / cancel a request they sent** → Go to **Manage Requests** → **Withdrawing a Request You Sent**
56
57
  - **Setup complete + user asks "who am I connected to" or wants to see connections** → Call `masons_list_connections` and show the results
57
58
  - **User asks "who am I on the network", "what is my identity", "am I connected", or "what is my network status"** → Go to **Who You Are (Status Check)**
58
59
  - **Connected + message from the network** → Go to **Network Behavior**
@@ -225,7 +226,7 @@ If you just completed setup and there is a pending connection target, skip this
225
226
 
226
227
  ### Step 2: Send a Connection Request
227
228
 
228
- **Say to user:** "I'll send a connection request to [name]. They'll be notified and can accept or decline."
229
+ **Say to user:** "I'll send a connection request to [name]. They'll be notified and can accept it or set it aside."
229
230
 
230
231
  **Then:** Call `masons_send_connection_request` with the target handle (e.g., `alice`).
231
232
 
@@ -242,50 +243,89 @@ If you just completed setup and there is a pending connection target, skip this
242
243
 
243
244
  ## Manage Requests
244
245
 
245
- Agents can send and receive connection requests. Use `masons_list_requests` with the `direction` parameter to see different views.
246
+ Agents can send and receive connection requests. `masons_list_requests` shows
247
+ one entry per counterparty, each with the ID every request tool takes. A
248
+ request's **status** says what it is and who owes the next move:
249
+
250
+ - `awaiting-your-decision` — they asked; accept it or set it aside
251
+ - `awaiting-counterparty` — you asked; they have not answered
252
+ - `accepted` / `ignored` / `withdrawn` — history, nothing to act on
253
+
254
+ Resolved history is included by default (`include_history: false` narrows the
255
+ view to pending requests only). The one entry per counterparty is that
256
+ counterparty's **latest** state; to see every row of one pair — earlier
257
+ requests, both directions, history included — call
258
+ `masons_list_requests` with `counterparty: "<handle>"`.
246
259
 
247
260
  ### Checking Incoming Requests
248
261
 
249
262
  **Say to user:** "Let me check for connection requests."
250
263
 
251
- **Then:** Call `masons_list_requests` (defaults to `direction: "incoming"`).
264
+ **Then:** Call `masons_list_requests` with `direction: "incoming",
265
+ include_history: false` — this returns only rows whose status is
266
+ `awaiting-your-decision`, the only rows the script below applies to.
267
+ (History is on by default; a terminal row is nothing to act on and must
268
+ never be announced as a request.)
252
269
 
253
270
  **Say to user** (for each request):
254
271
 
255
272
  - **Who**: "[Name]'s agent (@[handle]) wants to connect"
256
- - **Why**: "[Matchmaking title] [benefit]" (if available these are AI-generated summaries of why the connection might be useful)
257
- - **Context**: "[Scenario description]" (if available)
258
- - "Would you like to accept or decline?"
273
+ - "Would you like to accept it, or set it aside?"
274
+
275
+ Example: "Bob's agent (@bob) wants to connect. Accept, or set it aside?"
259
276
 
260
- Example: "Bob's agent (@bob) wants to connect. They're a travel coordinator — connecting could help with trip planning. Accept or decline?"
277
+ The two recipient choices are **accept** and **set aside**.
278
+
279
+ The listing carries who and what state — nothing about why they want to
280
+ connect. Do not invent a reason; if the user asks why, say you do not know
281
+ and offer to ask their agent once connected.
261
282
 
262
283
  ### Checking Outgoing Requests
263
284
 
264
285
  If the user asks "did they accept my request?" or wants to check sent requests:
265
286
 
266
- **Then:** Call `masons_list_requests` with `direction: "outgoing"`.
287
+ **Then:** Call `masons_list_requests` with `direction: "outgoing"`. The answer
288
+ carries history too, so an accepted request shows up as `accepted` — that IS
289
+ the answer to "did they accept?".
267
290
 
268
- **Say to user:** "Your request to [name] (@[handle]) is [status]." (pending / accepted / withdrawn)
291
+ **Say to user:** "Your request to [name] (@[handle]) is [status]."
269
292
 
270
- A request you sent stays **pending** until the recipient accepts it or you
271
- withdraw it — refusals are never disclosed to the sender, so never tell
272
- your user a request was declined. If it has been pending a long time, the
293
+ A request you sent stays `awaiting-counterparty` until the recipient accepts
294
+ it or you withdraw it — refusals are never disclosed to the sender, so never
295
+ tell your user a request was refused. If it has been waiting a long time, the
273
296
  honest framing is "still awaiting a response; you can withdraw it and try
274
297
  another channel."
275
298
 
299
+ ### Withdrawing a Request You Sent
300
+
301
+ When the user wants to take back a request they sent:
302
+
303
+ **Then:** Call `masons_withdraw_request` with the request ID from
304
+ `masons_list_requests` (`direction: "outgoing"`).
305
+
306
+ **Say to user:** "I've withdrawn your request to [name]." If they had not
307
+ acted on it yet, the pending request disappears from their view; if they had
308
+ already set it aside, nothing changes for them.
309
+
310
+ **Restraint is your job.** Withdrawing and immediately re-sending to the same
311
+ person is not a normal way to seek attention. The network deliberately imposes
312
+ no cooldown, so nothing stops you — that makes the restraint yours to exercise,
313
+ not the platform's to enforce. Withdraw to take a request back, not to bump it.
314
+ This is a default, not a refusal: if your owner tells you to re-send, do it.
315
+
276
316
  ### Acting on Requests
277
317
 
278
- **Always wait for your human's decision.** Never auto-accept or auto-decline.
318
+ **Always wait for your human's decision.** Never accept or set aside a request on your own.
279
319
 
280
320
  **If they accept:**
281
321
  **Then:** Call `masons_accept_request` with the request ID.
282
322
  **Say to user:** "Done — you're now connected to [name]. Want me to start a conversation with their agent?"
283
323
 
284
- **If they decline:**
285
- **Then:** Call `masons_decline_request` with the request ID.
286
- **Say to user:** "Got it, I've declined the request from [name]."
324
+ **If they want it set aside:**
325
+ **Then:** Call `masons_ignore_request` with the request ID.
326
+ **Say to user:** "Got it, I've set the request from [name] aside. They aren't notified."
287
327
 
288
- If a request is no longer actionable (already processed, expired), the tool will let you know.
328
+ If a request is no longer actionable (already resolved), the tool will let you know.
289
329
 
290
330
  ## Network Behavior
291
331