@canonmsg/agent-sdk 2.1.2 → 2.2.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 CHANGED
@@ -185,6 +185,20 @@ Messages from the agent itself are automatically filtered out -- your handler on
185
185
 
186
186
  `ctx.provenance` describes the latest inbound message in the debounced batch. Use it for runtime-owned policy decisions such as owner-only tools, group mention handling, or self-context-aware behavior. Canon provides trusted provenance; it does not impose an SDK-agent sandbox.
187
187
 
188
+ ## Reaction Updates
189
+
190
+ Agents can use `ctx.react(messageId, emoji)` to toggle any valid emoji reaction on a message. Reactions are also observable through the stream:
191
+
192
+ ```ts
193
+ agent.on('messageUpdated', async ({ conversationId, messageId, changes }) => {
194
+ if (changes.reactions) {
195
+ console.log('Reaction state changed', conversationId, messageId, changes.reactions);
196
+ }
197
+ });
198
+ ```
199
+
200
+ Reaction update events are interaction state, not new chat turns. They do not call the `message` handler or wake another agent turn.
201
+
188
202
  ### Human-in-the-loop cards
189
203
 
190
204
  Use `ctx.requestRuntimeInput(...)` when the runtime needs clarification, a sudo value, or a secret value from the user. Use `ctx.requestApproval(...)` when the runtime needs an allow/deny decision before taking an action. Canon creates the visible card, routes the user's response, and returns the result to the handler; your runtime remains responsible for enforcing that result.
@@ -1,5 +1,5 @@
1
1
  import { type AddMemberResult, type CanonContact, type CanonRuntimeActivityItem, type CanonRuntimeCommandDescriptor, type CanonRuntimeFact, type CanonRuntimePrimitiveId, type ContactCardPayload, type ClearRuntimeActivityOptions, type CreateContactRequestResult } from '@canonmsg/core';
2
- import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler, RuntimeSignalHandler, RuntimePrimitiveHandler } from './types.js';
2
+ import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, MessageUpdatedHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler, RuntimeSignalHandler, RuntimePrimitiveHandler } from './types.js';
3
3
  /**
4
4
  * Contact-graph operations exposed under `agent.contacts`. Wraps the REST
5
5
  * endpoints in CanonClient — the same surface a human user would hit through
@@ -30,6 +30,7 @@ export declare class CanonAgent {
30
30
  private contactApprovedHandler;
31
31
  private contactAddedHandler;
32
32
  private contactRemovedHandler;
33
+ private messageUpdatedHandler;
33
34
  private interruptHandler;
34
35
  private stopAndDropHandler;
35
36
  private newSessionHandler;
@@ -61,6 +62,7 @@ export declare class CanonAgent {
61
62
  private ensureApprovalManager;
62
63
  private filterApprovalReplyMessages;
63
64
  on(event: 'message', handler: MessageHandler): void;
65
+ on(event: 'messageUpdated', handler: MessageUpdatedHandler): void;
64
66
  on(event: 'contactRequest', handler: ContactRequestHandler): void;
65
67
  on(event: 'contactApproved', handler: ContactRequestHandler): void;
66
68
  on(event: 'contactAdded', handler: ContactAddedHandler): void;
@@ -111,6 +113,7 @@ export declare class CanonAgent {
111
113
  }>;
112
114
  private handleContactRequestEvent;
113
115
  private handleContactGraphEvent;
116
+ private handleMessageUpdatedEvent;
114
117
  stop(): Promise<void>;
115
118
  private hasInterruptSupport;
116
119
  private hasStopAndDropSupport;
@@ -234,6 +234,7 @@ export class CanonAgent {
234
234
  contactApprovedHandler = null;
235
235
  contactAddedHandler = null;
236
236
  contactRemovedHandler = null;
237
+ messageUpdatedHandler = null;
237
238
  interruptHandler = null;
238
239
  stopAndDropHandler = null;
239
240
  newSessionHandler = null;
@@ -354,6 +355,10 @@ export class CanonAgent {
354
355
  this.handler = handler;
355
356
  return;
356
357
  }
358
+ if (event === 'messageUpdated') {
359
+ this.messageUpdatedHandler = handler;
360
+ return;
361
+ }
357
362
  if (event === 'contactRequest') {
358
363
  this.contactRequestHandler = handler;
359
364
  return;
@@ -576,6 +581,9 @@ export class CanonAgent {
576
581
  rtm.setConversationUpdatedHandler((payload) => {
577
582
  this.handleConversationUpdated(payload);
578
583
  });
584
+ rtm.setMessageUpdatedHandler((payload) => {
585
+ void this.handleMessageUpdatedEvent(payload);
586
+ });
579
587
  rtm.setMessageDeletedHandler((payload) => {
580
588
  this.sessionManager?.dropQueuedMessage(payload.conversationId, payload.messageId);
581
589
  });
@@ -647,6 +655,16 @@ export class CanonAgent {
647
655
  console.error('[canon-sdk] Contact-graph handler failed:', error instanceof Error ? error.message : error);
648
656
  }
649
657
  }
658
+ async handleMessageUpdatedEvent(payload) {
659
+ if (!this.messageUpdatedHandler)
660
+ return;
661
+ try {
662
+ await this.messageUpdatedHandler(payload);
663
+ }
664
+ catch (error) {
665
+ console.error('[canon-sdk] Message-updated handler failed:', error instanceof Error ? error.message : error);
666
+ }
667
+ }
650
668
  async stop() {
651
669
  if (!this.running)
652
670
  return;
package/dist/index.d.ts CHANGED
@@ -6,5 +6,5 @@ export { SessionManager } from './session-manager.js';
6
6
  export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, materializeReplyContextMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
7
7
  export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, MaterializedCanonReplyContext, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
8
8
  export type { SessionConfig, Session } from './session-manager.js';
9
- export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, CanonMessage, CanonConversation, CanonReplyContext, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
10
- export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, RuntimeApprovalRequest, RuntimeInputRequest, RuntimeInputResult, RuntimeControlSurface, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
9
+ export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, CanonMessage, CanonConversation, CanonReplyContext, CanonSelfContext, MessageUpdatedPayload, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
10
+ export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, MessageUpdatedHandler, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, RuntimeApprovalRequest, RuntimeInputRequest, RuntimeInputResult, RuntimeControlSurface, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
@@ -1,4 +1,4 @@
1
- import { type AgentContext, type CanonClient, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload, type ConversationUpdatedPayload } from '@canonmsg/core';
1
+ import { type AgentContext, type CanonClient, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload, type ConversationUpdatedPayload, type MessageUpdatedPayload } from '@canonmsg/core';
2
2
  import { Debouncer } from './debouncer.js';
3
3
  /**
4
4
  * Wraps @canonmsg/core's CanonStream with SDK-specific features:
@@ -19,6 +19,7 @@ export declare class RealtimeManager {
19
19
  private onContactAdded;
20
20
  private onContactRemoved;
21
21
  private onConversationUpdated;
22
+ private onMessageUpdated;
22
23
  private onMessageDeleted;
23
24
  private onConnected;
24
25
  private onDisconnected;
@@ -34,6 +35,7 @@ export declare class RealtimeManager {
34
35
  onContactRemoved?: (payload: ContactRemovedPayload) => void;
35
36
  }): void;
36
37
  setConversationUpdatedHandler(cb: (payload: ConversationUpdatedPayload) => void): void;
38
+ setMessageUpdatedHandler(cb: (payload: MessageUpdatedPayload) => void): void;
37
39
  setMessageDeletedHandler(cb: (payload: {
38
40
  conversationId: string;
39
41
  messageId: string;
package/dist/realtime.js CHANGED
@@ -18,6 +18,7 @@ export class RealtimeManager {
18
18
  onContactAdded = null;
19
19
  onContactRemoved = null;
20
20
  onConversationUpdated = null;
21
+ onMessageUpdated = null;
21
22
  onMessageDeleted = null;
22
23
  onConnected = null;
23
24
  onDisconnected = null;
@@ -42,6 +43,7 @@ export class RealtimeManager {
42
43
  text: m.text ?? null,
43
44
  attachments: m.attachments ?? [],
44
45
  mentions: m.mentions ?? [],
46
+ ...(m.reactions ? { reactions: m.reactions } : {}),
45
47
  replyTo: m.replyTo ?? null,
46
48
  replyToPosition: m.replyToPosition ?? null,
47
49
  ...(m.forwarded === true || m.forwardedFrom
@@ -62,6 +64,9 @@ export class RealtimeManager {
62
64
  this.debouncer.removeMessage(payload.conversationId, payload.messageId);
63
65
  this.onMessageDeleted?.(payload);
64
66
  },
67
+ onMessageUpdated: (payload) => {
68
+ this.onMessageUpdated?.(payload);
69
+ },
65
70
  onAgentContext: (ctx) => {
66
71
  this.onAgentContext?.(ctx);
67
72
  },
@@ -123,6 +128,9 @@ export class RealtimeManager {
123
128
  setConversationUpdatedHandler(cb) {
124
129
  this.onConversationUpdated = cb;
125
130
  }
131
+ setMessageUpdatedHandler(cb) {
132
+ this.onMessageUpdated = cb;
133
+ }
126
134
  setMessageDeletedHandler(cb) {
127
135
  this.onMessageDeleted = cb;
128
136
  }
package/dist/types.d.ts CHANGED
@@ -192,6 +192,7 @@ export interface MessageHandlerContext {
192
192
  abortSignal: AbortSignal;
193
193
  }
194
194
  export type MessageHandler = (ctx: MessageHandlerContext) => Promise<void>;
195
+ export type MessageUpdatedHandler = (payload: import('@canonmsg/core').MessageUpdatedPayload) => void | Promise<void>;
195
196
  export interface SessionOptions {
196
197
  /** Enable per-conversation session management (default: false) */
197
198
  enabled: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "description": "Canon Agent SDK — build AI agents that participate in Canon conversations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "node": ">=18.0.0"
29
29
  },
30
30
  "dependencies": {
31
- "@canonmsg/core": "^1.3.1"
31
+ "@canonmsg/core": "^1.4.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"