@canonmsg/agent-tools 0.9.1 → 0.11.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.
@@ -36,14 +36,15 @@ const DIRECT_SELECTION_SCHEMA = {
36
36
  export function canonCommunicateToolDefinition(name = CANON_COMMUNICATE_TOOL_NAME) {
37
37
  return {
38
38
  name,
39
- description: 'Find discoverable agents or communicate in Canon. Message a known conversation, start/continue a '
40
- + 'direct conversation, create a group, forward an exact message, share a '
41
- + 'contact, or manage group members. Canon '
42
- + 'enforces the recipient and agent-deployer policies; a new direct '
43
- + 'conversation or group member may require approval.',
39
+ description: 'Find agents, list current conversations with their participants, message an explicit conversation, '
40
+ + 'start_conversation with a person or agent (new by default), create_conversation with an optional title, '
41
+ + 'manage_participants, leave, forward a message or share a contact. Check the current participants '
42
+ + 'before selecting an existing conversation. Canon enforces admission and operator permissions.',
44
43
  inputSchema: {
45
44
  type: 'object',
46
45
  oneOf: [
46
+ { type: 'object', required: ['action'], additionalProperties: false, properties: { action: { const: 'list_conversations' }, limit: { type: 'integer', minimum: 1, maximum: 100 } } },
47
+ { type: 'object', required: ['action', 'conversationId'], additionalProperties: false, properties: { action: { const: 'leave_conversation' }, conversationId: NON_EMPTY_ID } },
47
48
  {
48
49
  type: 'object',
49
50
  required: ['action'],
@@ -84,7 +85,7 @@ export function canonCommunicateToolDefinition(name = CANON_COMMUNICATE_TOOL_NAM
84
85
  required: ['action', 'principalId', 'text'],
85
86
  additionalProperties: false,
86
87
  properties: {
87
- action: { const: 'start_direct' },
88
+ action: { enum: ['start_conversation', 'start_direct'] },
88
89
  principalId: NON_EMPTY_ID,
89
90
  text: MESSAGE_TEXT,
90
91
  selection: DIRECT_SELECTION_SCHEMA,
@@ -93,10 +94,10 @@ export function canonCommunicateToolDefinition(name = CANON_COMMUNICATE_TOOL_NAM
93
94
  },
94
95
  {
95
96
  type: 'object',
96
- required: ['action', 'name', 'memberIds'],
97
+ required: ['action', 'memberIds'],
97
98
  additionalProperties: false,
98
99
  properties: {
99
- action: { const: 'create_group' },
100
+ action: { enum: ['create_conversation', 'create_group'] },
100
101
  name: { type: 'string', minLength: 1, maxLength: VERB_LIMITS.groupNameChars },
101
102
  memberIds: {
102
103
  type: 'array',
@@ -141,7 +142,7 @@ export function canonCommunicateToolDefinition(name = CANON_COMMUNICATE_TOOL_NAM
141
142
  required: ['action', 'conversationId', 'userId', 'operation'],
142
143
  additionalProperties: false,
143
144
  properties: {
144
- action: { const: 'manage_group_members' },
145
+ action: { enum: ['manage_participants', 'manage_group_members'] },
145
146
  conversationId: NON_EMPTY_ID,
146
147
  userId: NON_EMPTY_ID,
147
148
  operation: { enum: ['add', 'remove'] },
@@ -164,6 +165,17 @@ function hasOnlyKeys(record, allowed) {
164
165
  export function parseCommunicateToolInput(value) {
165
166
  if (!isRecord(value))
166
167
  throw new Error('communicate arguments must be an object');
168
+ if (value.action === 'list_conversations') {
169
+ if (!hasOnlyKeys(value, new Set(['action', 'limit'])) || (value.limit !== undefined && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > 100)))
170
+ throw new Error('list_conversations requires an optional limit from 1 to 100');
171
+ return { action: 'list_conversations', ...(typeof value.limit === 'number' ? { limit: value.limit } : {}) };
172
+ }
173
+ if (value.action === 'leave_conversation') {
174
+ const conversationId = readRequiredString(value.conversationId);
175
+ if (!hasOnlyKeys(value, new Set(['action', 'conversationId'])) || !conversationId)
176
+ throw new Error('leave_conversation requires conversationId');
177
+ return { action: 'leave_conversation', conversationId };
178
+ }
167
179
  if (value.action === 'discover_agents') {
168
180
  if (!hasOnlyKeys(value, new Set(['action', 'query', 'limit', 'cursor']))) {
169
181
  throw new Error('discover_agents contains unsupported fields');
@@ -194,21 +206,21 @@ export function parseCommunicateToolInput(value) {
194
206
  ...(typeof value.cursor === 'string' ? { cursor: value.cursor } : {}),
195
207
  };
196
208
  }
197
- if (value.action === 'create_group') {
209
+ if (value.action === 'create_group' || value.action === 'create_conversation') {
198
210
  if (!hasOnlyKeys(value, new Set(['action', 'name', 'memberIds']))) {
199
211
  throw new Error('create_group contains unsupported fields');
200
212
  }
201
- const name = readRequiredString(value.name);
202
- if (!name)
203
- throw new Error('communicate.name is required');
213
+ const name = value.name === undefined ? undefined : readRequiredString(value.name);
214
+ if (name === null)
215
+ throw new Error('communicate.name must be a non-empty string when supplied');
204
216
  if (!Array.isArray(value.memberIds)
205
217
  || value.memberIds.length === 0
206
218
  || !value.memberIds.every((entry) => readRequiredString(entry) !== null)) {
207
219
  throw new Error('communicate.memberIds must contain at least one principal id');
208
220
  }
209
221
  return {
210
- action: 'create_group',
211
- name,
222
+ action: value.action,
223
+ ...(name ? { name } : {}),
212
224
  memberIds: [...new Set(value.memberIds)],
213
225
  };
214
226
  }
@@ -275,7 +287,7 @@ export function parseCommunicateToolInput(value) {
275
287
  ...(messageId ? { messageId } : {}),
276
288
  };
277
289
  }
278
- if (value.action === 'manage_group_members') {
290
+ if (value.action === 'manage_group_members' || value.action === 'manage_participants') {
279
291
  if (!hasOnlyKeys(value, new Set([
280
292
  'action',
281
293
  'conversationId',
@@ -294,7 +306,7 @@ export function parseCommunicateToolInput(value) {
294
306
  throw new Error('communicate.operation must be add or remove');
295
307
  }
296
308
  return {
297
- action: 'manage_group_members',
309
+ action: value.action,
298
310
  conversationId,
299
311
  userId,
300
312
  operation: value.operation,
@@ -323,7 +335,7 @@ export function parseCommunicateToolInput(value) {
323
335
  ...(messageId ? { messageId } : {}),
324
336
  };
325
337
  }
326
- if (value.action === 'start_direct') {
338
+ if (value.action === 'start_direct' || value.action === 'start_conversation') {
327
339
  if (!hasOnlyKeys(value, new Set(['action', 'principalId', 'text', 'selection', 'messageId']))) {
328
340
  throw new Error('start_direct contains unsupported fields');
329
341
  }
@@ -331,10 +343,10 @@ export function parseCommunicateToolInput(value) {
331
343
  if (!principalId)
332
344
  throw new Error('communicate.principalId is required');
333
345
  const selection = value.selection === undefined
334
- ? undefined
346
+ ? value.action === 'start_conversation' ? { mode: 'new' } : undefined
335
347
  : parseDirectConversationSelection(value.selection);
336
348
  return {
337
- action: 'start_direct',
349
+ action: value.action,
338
350
  principalId,
339
351
  text,
340
352
  ...(selection ? { selection } : {}),
@@ -39,9 +39,9 @@ const CANON_VERB_TOOL_DESCRIPTIONS = {
39
39
  share_contact: "Share a contact card into a conversation. The shared user must be in "
40
40
  + 'your contacts.',
41
41
  react: 'Toggle an emoji reaction on a Canon message.',
42
- add_member: 'Add a member to a Canon group (policy approval and/or agent-owner setup may return a pending invite).',
43
- remove_member: 'Remove a member from a Canon group (requires owner/admin role).',
44
- leave_conversation: 'Leave a Canon group conversation.',
42
+ add_member: 'Add a participant to a Canon conversation (policy approval and/or agent-owner setup may return a pending invite).',
43
+ remove_member: 'Remove a participant from a Canon conversation (requires admin authority).',
44
+ leave_conversation: 'Leave a Canon conversation.',
45
45
  list_contacts: 'List your Canon contacts.',
46
46
  list_conversations: 'List your Canon conversations (optionally limited).',
47
47
  no_reply: 'End your turn without posting anything to the conversation. Use it in '
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-tools",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "Canonical Canon verb tools — shared projections of canon.verbs.v1 for runtime bindings",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,7 +23,7 @@
23
23
  "prepack": "npm run build"
24
24
  },
25
25
  "engines": {
26
- "node": ">=18.0.0"
26
+ "node": ">=22.22.3"
27
27
  },
28
28
  "keywords": [
29
29
  "canon",
@@ -36,8 +36,8 @@
36
36
  "access": "public"
37
37
  },
38
38
  "dependencies": {
39
- "@canonmsg/core": "^12.4.0",
40
- "@canonmsg/rich-cards": "^0.10.5",
39
+ "@canonmsg/core": "^13.0.0",
40
+ "@canonmsg/rich-cards": "^0.10.6",
41
41
  "@modelcontextprotocol/sdk": "^1.30.0"
42
42
  },
43
43
  "devDependencies": {