@canonmsg/agent-sdk 3.0.0 → 3.1.1

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 { type AddMemberResult, type CanonContact, type CanonRuntimeActivityItem, type CanonRuntimeCommandDescriptor, type CanonRuntimeFact, type CanonRuntimePrimitiveId, type ContactCardPayload, type ClearRuntimeActivityOptions, type CreateContactRequestResult } from '@canonmsg/core';
1
+ import { type AddMemberResult, type CanonContact, type CanonConversation, type CreateConversationResult, type CanonRuntimeActivityItem, type CanonRuntimeCommandDescriptor, type CanonRuntimeFact, type CanonRuntimePrimitiveId, type ContactCardPayload, type ClearRuntimeActivityOptions, type CreateContactRequestResult } from '@canonmsg/core';
2
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
@@ -18,6 +18,11 @@ export interface AgentUsersAPI {
18
18
  block(userId: string): Promise<void>;
19
19
  unblock(userId: string): Promise<void>;
20
20
  }
21
+ export interface AgentConversationsAPI {
22
+ list(options?: {
23
+ targetUserId?: string;
24
+ }): Promise<CanonConversation[]>;
25
+ }
21
26
  export declare class CanonAgent {
22
27
  private options;
23
28
  private apiClient;
@@ -40,6 +45,8 @@ export declare class CanonAgent {
40
45
  readonly contacts: AgentContactsAPI;
41
46
  /** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
42
47
  readonly users: AgentUsersAPI;
48
+ /** Conversation discovery for choosing existing sessions intentionally. */
49
+ readonly conversations: AgentConversationsAPI;
43
50
  private readonly reachOutInFlight;
44
51
  private agentId;
45
52
  private agentContext;
@@ -85,9 +92,7 @@ export declare class CanonAgent {
85
92
  reachOut(card: ContactCardPayload, options?: ReachOutOptions): Promise<ReachOutResult>;
86
93
  private executeReachOut;
87
94
  start(): Promise<void>;
88
- createConversation(options: CreateConversationOptions): Promise<{
89
- conversationId: string;
90
- }>;
95
+ createConversation(options: CreateConversationOptions): Promise<CreateConversationResult>;
91
96
  updateTopic(conversationId: string, topic: string): Promise<void>;
92
97
  leaveConversation(conversationId: string): Promise<void>;
93
98
  updateConversationName(conversationId: string, name: string): Promise<void>;
@@ -245,6 +245,8 @@ export class CanonAgent {
245
245
  contacts;
246
246
  /** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
247
247
  users;
248
+ /** Conversation discovery for choosing existing sessions intentionally. */
249
+ conversations;
248
250
  reachOutInFlight = new Map();
249
251
  agentId = null;
250
252
  agentContext = null;
@@ -292,6 +294,14 @@ export class CanonAgent {
292
294
  block: (userId) => apiClient.blockUser(userId),
293
295
  unblock: (userId) => apiClient.unblockUser(userId),
294
296
  };
297
+ this.conversations = {
298
+ list: async (options = {}) => {
299
+ const conversations = await apiClient.getConversations();
300
+ if (!options.targetUserId)
301
+ return conversations;
302
+ return conversations.filter((conversation) => conversation.memberIds.includes(options.targetUserId));
303
+ },
304
+ };
295
305
  if (options.sessions?.enabled) {
296
306
  this.sessionManager = new SessionManager({
297
307
  contextLimit: options.sessions.contextLimit,
@@ -457,7 +467,7 @@ export class CanonAgent {
457
467
  const contextualKey = options?.selfContext
458
468
  ? `${options.sourceConversationId ?? ''}\u0000${options.selfContext.type}\u0000${options.selfContext.context}`
459
469
  : '';
460
- const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}\u0000${JSON.stringify(options?.sessionConfig ?? null)}\u0000${contextualKey}`;
470
+ const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}\u0000${JSON.stringify(options?.sessionConfig ?? null)}\u0000${JSON.stringify(options?.sessionSelection ?? null)}\u0000${contextualKey}`;
461
471
  const inFlight = this.reachOutInFlight.get(inFlightKey);
462
472
  if (inFlight)
463
473
  return inFlight;
@@ -482,6 +492,7 @@ export class CanonAgent {
482
492
  selfContext: options.selfContext,
483
493
  requestMessage: options.requestMessage ?? null,
484
494
  sessionConfig: options.sessionConfig ?? null,
495
+ sessionSelection: options.sessionSelection,
485
496
  });
486
497
  return result.status === 'messaged'
487
498
  ? {
@@ -489,6 +500,9 @@ export class CanonAgent {
489
500
  conversationId: result.conversationId,
490
501
  messageId: result.messageId,
491
502
  selfContextId: result.selfContextId,
503
+ created: result.created,
504
+ reused: result.reused,
505
+ sessionSelection: result.sessionSelection,
492
506
  }
493
507
  : result;
494
508
  }
@@ -497,6 +511,7 @@ export class CanonAgent {
497
511
  text: options?.text ?? null,
498
512
  requestMessage: options?.requestMessage ?? null,
499
513
  sessionConfig: options?.sessionConfig ?? null,
514
+ sessionSelection: options?.sessionSelection,
500
515
  });
501
516
  }
502
517
  async start() {
@@ -1363,6 +1378,9 @@ export class CanonAgent {
1363
1378
  replyContext,
1364
1379
  message: latestMessage ?? undefined,
1365
1380
  });
1381
+ const requestedTurnMode = latestMessage
1382
+ ? normalizeTurnMetadata(latestMessage.metadata)?.requestedTurnMode ?? null
1383
+ : null;
1366
1384
  // Build context methods bound to this conversation
1367
1385
  const deleteMessage = (messageId) => this.apiClient.deleteMessage(conversationId, messageId);
1368
1386
  const markAsRead = () => this.apiClient.markAsRead(conversationId);
@@ -1844,6 +1862,7 @@ export class CanonAgent {
1844
1862
  selfContexts,
1845
1863
  provenance,
1846
1864
  turnContext,
1865
+ requestedTurnMode,
1847
1866
  requestApproval,
1848
1867
  requestRuntimeInput,
1849
1868
  requestCard,
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export { CanonAgent } from './canon-agent.js';
2
- export type { AgentContactsAPI, AgentUsersAPI } from './canon-agent.js';
2
+ export type { AgentContactsAPI, AgentConversationsAPI, AgentUsersAPI } from './canon-agent.js';
3
3
  export { ApprovalManager, buildApprovalOutcome, buildApprovalReply, buildApprovalRequest, CanonApiError, DEFAULT_APPROVAL_CONFIG, generateApprovalId, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, parseApprovalReplyMetadata, parseApprovalRequestMetadata, parseSessionRule, redactSecrets, } from '@canonmsg/core';
4
- export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, SessionRule, } from '@canonmsg/core';
4
+ export type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalOutcomeMetadata, ApprovalReplyMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalResult, ApprovalRisk, CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeTurnModeActivation, CanonRuntimeTurnModeDescriptor, CanonRuntimeTurnModeScope, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, SessionRule, } from '@canonmsg/core';
5
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, CanonTurnContextV2, MessageUpdatedPayload, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
9
+ export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, CanonMessage, CanonConversation, CanonReplyContext, CanonSelfContext, CanonTurnContextV2, CanonRuntimeDescriptor, MessageUpdatedPayload, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, CreateConversationResult, DirectSessionSelection, } from '@canonmsg/core';
10
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';
package/dist/realtime.js CHANGED
@@ -32,6 +32,10 @@ export class RealtimeManager {
32
32
  streamUrl,
33
33
  handler: {
34
34
  onMessage: (payload) => {
35
+ if (payload.turnDispatch && payload.turnDispatch.kind !== 'run_turn') {
36
+ console.error(`[canon-sdk] Ignoring server-dispatched observe-only message in ${payload.conversationId}: ${payload.turnDispatch.reason}`);
37
+ return;
38
+ }
35
39
  const m = payload.message;
36
40
  const message = {
37
41
  id: m.id,
@@ -1,8 +1,9 @@
1
- import { type ResolvedAgentBehaviorPolicy, type CanonMessage } from '@canonmsg/core';
1
+ import { type ResolvedAgentBehaviorPolicy, type CanonMessage, type MessageCreatedPayload } from '@canonmsg/core';
2
2
  export declare function shouldDispatchInboundMessage(_conversationId: string, agentId: string, message: CanonMessage, options?: {
3
3
  conversationType?: 'direct' | 'group' | 'unknown';
4
4
  behavior?: ResolvedAgentBehaviorPolicy | null;
5
5
  recentHumanCount?: number;
6
6
  consecutiveAgentTurns?: number;
7
7
  currentAgentStreakStartedByHuman?: boolean;
8
+ turnDispatch?: MessageCreatedPayload['turnDispatch'];
8
9
  }): Promise<boolean>;
@@ -2,6 +2,9 @@ import { evaluateParticipationPolicy, shouldTriggerAgentTurn, } from '@canonmsg/
2
2
  export async function shouldDispatchInboundMessage(_conversationId, agentId, message, options) {
3
3
  if (message.senderId === agentId)
4
4
  return false;
5
+ if (options?.turnDispatch) {
6
+ return options.turnDispatch.kind === 'run_turn';
7
+ }
5
8
  const triggerDecision = shouldTriggerAgentTurn({
6
9
  senderType: message.senderType,
7
10
  metadata: message.metadata,
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, CanonMessage, CanonConversation, CanonReplyContext, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, TurnOutputBlock, TurnOutputBlockInput, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalRisk, ApprovalReplyMetadata, ApprovalOutcomeMetadata, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, SessionRule, ApprovalResult, } from '@canonmsg/core';
2
- import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CanonReplyContext, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, SendMessageOptions, SendContextualSelfContextInput, SessionConfig, TurnOutputBlock, TurnOutputBlockInput } from '@canonmsg/core';
1
+ export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, CanonMessage, CanonConversation, CanonReplyContext, CanonContact, CanonContactRequest, CanonResolveAdmissionResult, ContactAddedPayload, ContactRemovedPayload, ContactSource, AgentContext, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, CanonSelfContext, CreateConversationResult, DirectSessionSelection, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, SessionConfig, CreateConversationOptions, TurnLifecycleState, TurnOutputBlock, TurnOutputBlockInput, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRequestMetadata, ApprovalRisk, ApprovalReplyMetadata, ApprovalOutcomeMetadata, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, SessionRule, ApprovalResult, } from '@canonmsg/core';
2
+ import type { AddMemberResult, CanonGroupContext, CanonMessage, CanonConversation, CanonReplyContext, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, CanonRuntimeProvenance, CanonTurnContextV2, ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk, RuntimeInputChoice, RuntimeInputAnswers, RuntimeInputKind, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1, SendMessageOptions, SendContextualSelfContextInput, SessionConfig, DirectSessionSelection, TurnOutputBlock, TurnOutputBlockInput } from '@canonmsg/core';
3
3
  import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
4
4
  export interface ProgressMessageOptions extends SendMessageOptions {
5
5
  /**
@@ -150,6 +150,8 @@ export interface MessageHandlerContext {
150
150
  provenance: CanonRuntimeProvenance;
151
151
  /** Compact structured Canon turn context for the latest inbound message in this handler batch. */
152
152
  turnContext: CanonTurnContextV2;
153
+ /** Runtime turn mode requested by the sender for this inbound turn, if any. */
154
+ requestedTurnMode: string | null;
153
155
  /**
154
156
  * Ask the conversation owner to approve a native runtime action. This only
155
157
  * renders Canon's inline approval card; runtimes must explicitly wait for
@@ -280,6 +282,9 @@ export type ReachOutResult = {
280
282
  conversationId: string;
281
283
  messageId?: string;
282
284
  selfContextId?: string;
285
+ created?: boolean;
286
+ reused?: boolean;
287
+ sessionSelection?: DirectSessionSelection['mode'];
283
288
  } | {
284
289
  status: 'requested';
285
290
  requestId: string | null;
@@ -291,6 +296,9 @@ export type ReachOutResult = {
291
296
  } | {
292
297
  status: 'setup_required';
293
298
  reason: string;
299
+ } | {
300
+ status: 'no_session';
301
+ reason: string;
294
302
  } | {
295
303
  status: 'blocked' | 'unavailable';
296
304
  reason: string;
@@ -302,6 +310,8 @@ export interface ReachOutOptions {
302
310
  requestMessage?: string;
303
311
  /** Explicit session setup to use when the contact-card target is an agent. */
304
312
  sessionConfig?: SessionConfig | null;
313
+ /** Whether to continue an existing direct agent session or start a fresh one. */
314
+ sessionSelection?: DirectSessionSelection;
305
315
  /** Source conversation for contextual cross-session reach-outs. */
306
316
  sourceConversationId?: string;
307
317
  /** Private context for the agent when this reach-out sends a cross-session message. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
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": "^2.0.0"
31
+ "@canonmsg/core": "^2.1.1"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"