@canonmsg/agent-sdk 1.1.2 → 1.1.3

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.
@@ -1,4 +1,4 @@
1
- import { CanonClient, createRuntimeStatePublisher, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, rtdbRead, rtdbWrite, mergeWorkSessionContexts, normalizeTurnMetadata, reachOutToCanonContact, } from '@canonmsg/core';
1
+ import { CanonClient, createRuntimeStatePublisher, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, } from '@canonmsg/core';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { AuthManager } from './auth.js';
4
4
  import { Debouncer } from './debouncer.js';
@@ -152,7 +152,10 @@ export class CanonAgent {
152
152
  // Include the opener/request payloads in the dedupe key so two concurrent
153
153
  // calls with different `text`, `requestMessage`, or setup choices don't silently collapse
154
154
  // and lose the second caller's intended side effect.
155
- const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}\u0000${JSON.stringify(options?.sessionConfig ?? null)}`;
155
+ const contextualKey = options?.selfContext
156
+ ? `${options.sourceConversationId ?? ''}\u0000${options.selfContext.type}\u0000${options.selfContext.context}`
157
+ : '';
158
+ const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}\u0000${JSON.stringify(options?.sessionConfig ?? null)}\u0000${contextualKey}`;
156
159
  const inFlight = this.reachOutInFlight.get(inFlightKey);
157
160
  if (inFlight)
158
161
  return inFlight;
@@ -163,6 +166,29 @@ export class CanonAgent {
163
166
  return promise;
164
167
  }
165
168
  async executeReachOut(targetUserId, options) {
169
+ if (options?.selfContext) {
170
+ if (!options.sourceConversationId) {
171
+ throw new Error('sourceConversationId is required for contextual reachOut');
172
+ }
173
+ if (!options.text) {
174
+ throw new Error('text is required for contextual reachOut');
175
+ }
176
+ const result = await this.apiClient.sendContextualMessage({
177
+ sourceConversationId: options.sourceConversationId,
178
+ targetUserId,
179
+ text: options.text,
180
+ selfContext: options.selfContext,
181
+ requestMessage: options.requestMessage ?? null,
182
+ sessionConfig: options.sessionConfig ?? null,
183
+ });
184
+ return result.status === 'messaged'
185
+ ? {
186
+ status: 'messaged',
187
+ conversationId: result.conversationId,
188
+ messageId: result.messageId,
189
+ }
190
+ : result;
191
+ }
166
192
  return reachOutToCanonContact(this.apiClient, {
167
193
  targetUserId,
168
194
  text: options?.text ?? null,
@@ -634,6 +660,9 @@ export class CanonAgent {
634
660
  catch { }
635
661
  const result = await this.apiClient.sendMessage(conversationId, text, {
636
662
  ...(options ?? {}),
663
+ ...(options?.selfContextId === undefined && activeSelfContextId
664
+ ? { selfContextId: activeSelfContextId }
665
+ : {}),
637
666
  metadata: {
638
667
  ...(options?.metadata ?? {}),
639
668
  turnId,
@@ -672,11 +701,8 @@ export class CanonAgent {
672
701
  m.isOwner = m.senderId === ownerId;
673
702
  }
674
703
  }
675
- const explicitWorkSession = messages.find((message) => message.workSession)?.workSession
676
- ?? history.find((message) => message.workSession)?.workSession
677
- ?? null;
678
- const activeWorkSessions = mergeWorkSessionContexts(explicitWorkSession, page.workSessions ?? []);
679
- const workSession = explicitWorkSession;
704
+ const selfContexts = page.selfContexts ?? [];
705
+ const activeSelfContextId = selfContexts[0]?.id;
680
706
  // Build agent context (fallback to minimal if not yet received)
681
707
  const agent = this.agentContext ?? {
682
708
  agentId: this.agentId,
@@ -693,32 +719,21 @@ export class CanonAgent {
693
719
  const react = (messageId, emoji) => this.apiClient.react(conversationId, messageId, emoji);
694
720
  const addMember = (userId) => this.apiClient.addMember(conversationId, userId);
695
721
  const removeMember = (userId) => this.apiClient.removeMember(conversationId, userId);
696
- const createWorkSession = (options) => this.apiClient.createWorkSession({
697
- conversationId,
698
- ...(options ?? {}),
699
- });
700
- const getWorkSession = (workSessionId, targetConversationId = conversationId) => this.apiClient.getWorkSession(workSessionId, targetConversationId);
701
- const updateWorkSessionContext = (workSessionId, options) => this.apiClient.upsertWorkSessionConversation(workSessionId, conversationId, options);
702
- const sendLinkedMessage = (targetConversationId, text, options) => {
703
- if (!options?.workSessionId && !options?.createWorkSession) {
704
- throw new Error('sendLinkedMessage requires workSessionId or createWorkSession');
705
- }
706
- return this.apiClient.sendLinkedMessage({
707
- sourceConversationId: conversationId,
708
- targetConversationId,
709
- text,
710
- ...(options ?? {}),
711
- messageOptions: {
712
- ...(options?.messageOptions ?? {}),
713
- metadata: {
714
- ...(options?.messageOptions?.metadata ?? {}),
715
- turnId,
716
- turnSemantics: 'turn_complete',
717
- turnComplete: true,
718
- },
722
+ const sendContextualMessage = (target, text, options) => this.apiClient.sendContextualMessage({
723
+ sourceConversationId: conversationId,
724
+ ...target,
725
+ text,
726
+ ...options,
727
+ messageOptions: {
728
+ ...(options.messageOptions ?? {}),
729
+ metadata: {
730
+ ...(options.messageOptions?.metadata ?? {}),
731
+ turnId,
732
+ turnSemantics: 'turn_complete',
733
+ turnComplete: true,
719
734
  },
720
- });
721
- };
735
+ },
736
+ });
722
737
  const uploadFile = (filePath, options) => uploadMediaFile(this.apiClient, conversationId, filePath, options);
723
738
  const replyWithFile = async (filePath, text = '', options) => {
724
739
  try {
@@ -733,13 +748,15 @@ export class CanonAgent {
733
748
  ? { replyToPosition: options.replyToPosition }
734
749
  : {}),
735
750
  ...(options?.mentions ? { mentions: options.mentions } : {}),
751
+ ...(options?.selfContextId === undefined && activeSelfContextId
752
+ ? { selfContextId: activeSelfContextId }
753
+ : {}),
736
754
  metadata: {
737
755
  ...(options?.metadata ?? {}),
738
756
  turnId,
739
757
  turnSemantics: 'turn_complete',
740
758
  turnComplete: true,
741
759
  },
742
- ...(options?.workSessionId ? { workSessionId: options.workSessionId } : {}),
743
760
  contentType: uploaded.attachment.kind,
744
761
  attachments: [uploaded.attachment],
745
762
  });
@@ -767,13 +784,9 @@ export class CanonAgent {
767
784
  react,
768
785
  addMember,
769
786
  removeMember,
770
- createWorkSession,
771
- getWorkSession,
772
- updateWorkSessionContext,
773
- sendLinkedMessage,
787
+ sendContextualMessage,
774
788
  agent,
775
- workSession,
776
- activeWorkSessions,
789
+ selfContexts,
777
790
  abortSignal: abortController.signal,
778
791
  media: {
779
792
  materialize: (message = hydratedMessages[hydratedMessages.length - 1], options) => {
package/dist/index.d.ts CHANGED
@@ -6,5 +6,5 @@ export { SessionManager } from './session-manager.js';
6
6
  export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
7
7
  export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
8
8
  export type { SessionConfig, Session } from './session-manager.js';
9
- export type { AgentContext, CanonContactRequest, CanonMessage, CanonConversation, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
9
+ export type { AgentContext, CanonContactRequest, CanonMessage, CanonConversation, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
10
10
  export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { AddMemberResult, AgentClientType, CanonRuntimeDescriptor, CanonMessage, CanonConversation, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
2
- import type { AddMemberResult, CanonMessage, CanonConversation, CanonRuntimeActionDispatch, CreateWorkSessionOptions, SendMessageOptions, SessionConfig, UpdateWorkSessionConversationOptions } from '@canonmsg/core';
1
+ export type { AddMemberResult, AgentClientType, CanonRuntimeDescriptor, CanonMessage, CanonConversation, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, } from '@canonmsg/core';
2
+ import type { AddMemberResult, CanonMessage, CanonConversation, CanonRuntimeActionDispatch, SendMessageOptions, SendContextualSelfContextInput, SessionConfig } from '@canonmsg/core';
3
3
  import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
4
4
  export interface ProgressMessageOptions extends SendMessageOptions {
5
5
  /**
@@ -61,20 +61,16 @@ export interface MessageHandlerContext {
61
61
  addMember: (userId: string) => Promise<AddMemberResult>;
62
62
  /** Remove a member from this conversation (requires owner/admin role) */
63
63
  removeMember: (userId: string) => Promise<void>;
64
- /** Create a Canon work session rooted in this conversation. */
65
- createWorkSession: (options?: Omit<CreateWorkSessionOptions, 'conversationId'>) => Promise<import('@canonmsg/core').CanonResolvedWorkSession>;
66
- /** Load this conversation's scoped view of a Canon work session. */
67
- getWorkSession: (workSessionId: string, conversationId?: string) => Promise<import('@canonmsg/core').CanonResolvedWorkSession>;
68
- /** Update or attach this conversation's scoped work-session context. */
69
- updateWorkSessionContext: (workSessionId: string, options?: UpdateWorkSessionConversationOptions) => Promise<import('@canonmsg/core').CanonResolvedWorkSession>;
70
- /** Send into another conversation under an existing or lazily created Canon work session. */
71
- sendLinkedMessage: (targetConversationId: string, text: string, options?: Omit<import('@canonmsg/core').SendLinkedMessageOptions, 'sourceConversationId' | 'targetConversationId' | 'text'>) => Promise<import('@canonmsg/core').SendLinkedMessageResult>;
64
+ /** Send into another Canon conversation with private cross-session self-context. */
65
+ sendContextualMessage: (target: {
66
+ targetConversationId: string;
67
+ } | {
68
+ targetUserId: string;
69
+ }, text: string, options: Omit<import('@canonmsg/core').SendContextualMessageOptions, 'sourceConversationId' | 'targetConversationId' | 'targetUserId' | 'text'>) => Promise<import('@canonmsg/core').SendContextualMessageResult>;
72
70
  /** Trusted agent identity & access context */
73
71
  agent: import('@canonmsg/core').AgentContext;
74
- /** Canon-provided shared task context for this turn, when attached to inbound messages. */
75
- workSession?: import('@canonmsg/core').CanonWorkSessionContext | null;
76
- /** All active Canon work sessions currently linked to this conversation. */
77
- activeWorkSessions?: import('@canonmsg/core').CanonWorkSessionContext[];
72
+ /** Canon-provided private context explaining this agent's cross-session actions. */
73
+ selfContexts?: import('@canonmsg/core').CanonSelfContext[];
78
74
  /** Canon-managed local media access for the current conversation. */
79
75
  media: {
80
76
  materialize: (message?: CanonMessage, options?: Omit<MaterializeMediaOptions, 'agentId' | 'conversationId' | 'messageId'>) => Promise<MaterializedCanonAttachment[]>;
@@ -179,4 +175,8 @@ export interface ReachOutOptions {
179
175
  requestMessage?: string;
180
176
  /** Explicit session setup to use when the contact-card target is an agent. */
181
177
  sessionConfig?: SessionConfig | null;
178
+ /** Source conversation for contextual cross-session reach-outs. */
179
+ sourceConversationId?: string;
180
+ /** Private context for the agent when this reach-out sends a cross-session message. */
181
+ selfContext?: SendContextualSelfContextInput;
182
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
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": "^0.15.4"
31
+ "@canonmsg/core": "^0.15.5"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"