@canonmsg/agent-sdk 0.9.2 → 0.10.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
@@ -250,16 +250,16 @@ The approved response only includes the API key the first time it is delivered.
250
250
 
251
251
  ## Error Handling
252
252
 
253
- The SDK exports `ApiError` for typed error handling:
253
+ The SDK exports `CanonApiError` for typed error handling:
254
254
 
255
255
  ```typescript
256
- import { CanonAgent, ApiError } from '@canonmsg/agent-sdk';
256
+ import { CanonAgent, CanonApiError } from '@canonmsg/agent-sdk';
257
257
 
258
258
  agent.on('message', async ({ messages, reply }) => {
259
259
  try {
260
260
  await reply('Hello!');
261
261
  } catch (err) {
262
- if (err instanceof ApiError) {
262
+ if (err instanceof CanonApiError) {
263
263
  console.error(`API error ${err.status}: ${err.message}`);
264
264
  }
265
265
  }
@@ -1,4 +1,23 @@
1
- import type { CanonAgentOptions, CreateConversationOptions, MessageHandler, ContactRequestHandler } from './types.js';
1
+ import { type AddMemberResult, type CanonContact, type ContactCardPayload, type CreateContactRequestResult } from '@canonmsg/core';
2
+ import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler } from './types.js';
3
+ /**
4
+ * Contact-graph operations exposed under `agent.contacts`. Wraps the REST
5
+ * endpoints in CanonClient — the same surface a human user would hit through
6
+ * the app — so plugin runtimes can treat them as natural-language tools.
7
+ */
8
+ export interface AgentContactsAPI {
9
+ list(): Promise<CanonContact[]>;
10
+ get(contactId: string): Promise<CanonContact | null>;
11
+ remove(contactId: string): Promise<void>;
12
+ request(targetUserId: string, message?: string | null): Promise<CreateContactRequestResult>;
13
+ }
14
+ /**
15
+ * User-level moderation actions exposed under `agent.users`.
16
+ */
17
+ export interface AgentUsersAPI {
18
+ block(userId: string): Promise<void>;
19
+ unblock(userId: string): Promise<void>;
20
+ }
2
21
  export declare class CanonAgent {
3
22
  private options;
4
23
  private apiClient;
@@ -10,6 +29,13 @@ export declare class CanonAgent {
10
29
  private handler;
11
30
  private contactRequestHandler;
12
31
  private contactApprovedHandler;
32
+ private contactAddedHandler;
33
+ private contactRemovedHandler;
34
+ /** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
35
+ readonly contacts: AgentContactsAPI;
36
+ /** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
37
+ readonly users: AgentUsersAPI;
38
+ private readonly reachOutInFlight;
13
39
  private agentId;
14
40
  private agentContext;
15
41
  private cachedConversationIds;
@@ -19,6 +45,18 @@ export declare class CanonAgent {
19
45
  on(event: 'message', handler: MessageHandler): void;
20
46
  on(event: 'contactRequest', handler: ContactRequestHandler): void;
21
47
  on(event: 'contactApproved', handler: ContactRequestHandler): void;
48
+ on(event: 'contactAdded', handler: ContactAddedHandler): void;
49
+ on(event: 'contactRemoved', handler: ContactRemovedHandler): void;
50
+ /**
51
+ * Resolve admission live for a target user (typically read off a shared
52
+ * contact card) and route into either an immediate message or a contact
53
+ * request. Never reads `card.accessLevel` — that snapshot is stale by the
54
+ * time an LLM acts on it. Instead defers to `resolveAdmission` so the
55
+ * answer reflects the target's *current* inbound policy.
56
+ */
57
+ reachOut(card: ContactCardPayload, options?: ReachOutOptions): Promise<ReachOutResult>;
58
+ private executeReachOut;
59
+ private openConversationAndMaybeMessage;
22
60
  start(): Promise<void>;
23
61
  createConversation(options: CreateConversationOptions): Promise<{
24
62
  conversationId: string;
@@ -26,13 +64,28 @@ export declare class CanonAgent {
26
64
  updateTopic(conversationId: string, topic: string): Promise<void>;
27
65
  leaveConversation(conversationId: string): Promise<void>;
28
66
  updateConversationName(conversationId: string, name: string): Promise<void>;
29
- addMember(conversationId: string, userId: string): Promise<void>;
67
+ /**
68
+ * Add a member to a group conversation.
69
+ *
70
+ * Outcome depends on the target's `groupJoinPolicy` and the relationship
71
+ * graph:
72
+ * - `{ status: 'added' }` — the member was added immediately.
73
+ * - `{ status: 'pending', requestId }` — the target requires approval; the
74
+ * server created a contact-request (kind: 'group_invite') routed to the
75
+ * approver. The actual group join happens when that request is approved
76
+ * (you can listen for `contact.approved` SSE events to know when).
77
+ *
78
+ * Throws `CanonApiError` for hard failures (block, inactive, owner-only,
79
+ * member cap, requester not authorized).
80
+ */
81
+ addMember(conversationId: string, userId: string): Promise<AddMemberResult>;
30
82
  removeMember(conversationId: string, userId: string): Promise<void>;
31
83
  uploadMedia(conversationId: string, data: string, mimeType: string, fileName?: string): Promise<{
32
84
  url: string;
33
85
  attachment: import('@canonmsg/core').MediaAttachment;
34
86
  }>;
35
87
  private handleContactRequestEvent;
88
+ private handleContactGraphEvent;
36
89
  stop(): Promise<void>;
37
90
  private publishAgentRuntime;
38
91
  private startRuntimeHeartbeat;
@@ -33,6 +33,13 @@ export class CanonAgent {
33
33
  handler = null;
34
34
  contactRequestHandler = null;
35
35
  contactApprovedHandler = null;
36
+ contactAddedHandler = null;
37
+ contactRemovedHandler = null;
38
+ /** Contact-graph operations (`agent.contacts.*`). Initialized in the constructor. */
39
+ contacts;
40
+ /** Block/unblock operations (`agent.users.*`). Initialized in the constructor. */
41
+ users;
42
+ reachOutInFlight = new Map();
36
43
  agentId = null;
37
44
  agentContext = null;
38
45
  cachedConversationIds = [];
@@ -51,6 +58,17 @@ export class CanonAgent {
51
58
  this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
52
59
  this.authManager = new AuthManager(this.apiClient);
53
60
  this.debouncer = new Debouncer(this.options.debounceMs);
61
+ const apiClient = this.apiClient;
62
+ this.contacts = {
63
+ list: () => apiClient.listContacts(),
64
+ get: (contactId) => apiClient.getContact(contactId),
65
+ remove: (contactId) => apiClient.deleteContact(contactId),
66
+ request: (targetUserId, message) => apiClient.createContactRequest(targetUserId, message ?? null),
67
+ };
68
+ this.users = {
69
+ block: (userId) => apiClient.blockUser(userId),
70
+ unblock: (userId) => apiClient.unblockUser(userId),
71
+ };
54
72
  if (options.sessions?.enabled) {
55
73
  this.sessionManager = new SessionManager({
56
74
  contextLimit: options.sessions.contextLimit,
@@ -68,7 +86,78 @@ export class CanonAgent {
68
86
  this.contactRequestHandler = handler;
69
87
  return;
70
88
  }
71
- this.contactApprovedHandler = handler;
89
+ if (event === 'contactApproved') {
90
+ this.contactApprovedHandler = handler;
91
+ return;
92
+ }
93
+ if (event === 'contactAdded') {
94
+ this.contactAddedHandler = handler;
95
+ return;
96
+ }
97
+ this.contactRemovedHandler = handler;
98
+ }
99
+ /**
100
+ * Resolve admission live for a target user (typically read off a shared
101
+ * contact card) and route into either an immediate message or a contact
102
+ * request. Never reads `card.accessLevel` — that snapshot is stale by the
103
+ * time an LLM acts on it. Instead defers to `resolveAdmission` so the
104
+ * answer reflects the target's *current* inbound policy.
105
+ */
106
+ async reachOut(card, options) {
107
+ const targetUserId = card.userId;
108
+ // Include the opener/request payloads in the dedupe key so two concurrent
109
+ // calls with different `text` or `requestMessage` don't silently collapse
110
+ // and lose the second caller's intended side effect.
111
+ const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}`;
112
+ const inFlight = this.reachOutInFlight.get(inFlightKey);
113
+ if (inFlight)
114
+ return inFlight;
115
+ const promise = this.executeReachOut(targetUserId, options).finally(() => {
116
+ this.reachOutInFlight.delete(inFlightKey);
117
+ });
118
+ this.reachOutInFlight.set(inFlightKey, promise);
119
+ return promise;
120
+ }
121
+ async executeReachOut(targetUserId, options) {
122
+ const { admission } = await this.apiClient.resolveAdmission(targetUserId);
123
+ if (admission.state === 'allowed' && admission.canMessage) {
124
+ return this.openConversationAndMaybeMessage(targetUserId, options);
125
+ }
126
+ if (admission.state === 'pending-outbound') {
127
+ return { status: 'pending', requestId: admission.pendingRequestId ?? null };
128
+ }
129
+ if (admission.state === 'request-required' && admission.canRequestContact) {
130
+ const result = await this.apiClient.createContactRequest(targetUserId, options?.requestMessage ?? null);
131
+ // The server may report 'open' if the target's policy flipped between
132
+ // the resolveAdmission call and the request. No request doc was
133
+ // written — fall through to the messaging path so the caller's intent
134
+ // ("reach this user") is honored end-to-end. Without this, the caller
135
+ // would get { status: 'requested' } despite no request existing.
136
+ if (result.status === 'open') {
137
+ return this.openConversationAndMaybeMessage(targetUserId, options);
138
+ }
139
+ // 'duplicate' means a pending request already existed; surface as
140
+ // 'pending'. 'created' is the normal "request just landed" path.
141
+ if (result.status === 'duplicate') {
142
+ return { status: 'pending', requestId: result.requestId };
143
+ }
144
+ return { status: 'requested', requestId: result.requestId };
145
+ }
146
+ if (admission.state === 'blocked') {
147
+ return { status: 'blocked', reason: 'blocked' };
148
+ }
149
+ return { status: 'unavailable', reason: admission.state };
150
+ }
151
+ async openConversationAndMaybeMessage(targetUserId, options) {
152
+ const { conversationId } = await this.apiClient.createConversation({
153
+ type: 'direct',
154
+ targetUserId,
155
+ });
156
+ if (options?.text) {
157
+ const { messageId } = await this.apiClient.sendMessage(conversationId, options.text);
158
+ return { status: 'messaged', conversationId, messageId };
159
+ }
160
+ return { status: 'messaged', conversationId };
72
161
  }
73
162
  async start() {
74
163
  if (this.running)
@@ -133,6 +222,14 @@ export class CanonAgent {
133
222
  void this.handleContactRequestEvent(this.contactApprovedHandler, request);
134
223
  },
135
224
  });
225
+ rtm.setContactGraphHandlers({
226
+ onContactAdded: (payload) => {
227
+ void this.handleContactGraphEvent(this.contactAddedHandler, payload);
228
+ },
229
+ onContactRemoved: (payload) => {
230
+ void this.handleContactGraphEvent(this.contactRemovedHandler, payload);
231
+ },
232
+ });
136
233
  rtm.setConnectionHandlers({
137
234
  onConnected: () => this.startRuntimeHeartbeat(),
138
235
  onDisconnected: () => this.stopRuntimeHeartbeat(),
@@ -159,6 +256,20 @@ export class CanonAgent {
159
256
  async updateConversationName(conversationId, name) {
160
257
  return this.apiClient.updateConversationName(conversationId, name);
161
258
  }
259
+ /**
260
+ * Add a member to a group conversation.
261
+ *
262
+ * Outcome depends on the target's `groupJoinPolicy` and the relationship
263
+ * graph:
264
+ * - `{ status: 'added' }` — the member was added immediately.
265
+ * - `{ status: 'pending', requestId }` — the target requires approval; the
266
+ * server created a contact-request (kind: 'group_invite') routed to the
267
+ * approver. The actual group join happens when that request is approved
268
+ * (you can listen for `contact.approved` SSE events to know when).
269
+ *
270
+ * Throws `CanonApiError` for hard failures (block, inactive, owner-only,
271
+ * member cap, requester not authorized).
272
+ */
162
273
  async addMember(conversationId, userId) {
163
274
  return this.apiClient.addMember(conversationId, userId);
164
275
  }
@@ -178,6 +289,16 @@ export class CanonAgent {
178
289
  console.error('[canon-sdk] Contact-request handler failed:', error instanceof Error ? error.message : error);
179
290
  }
180
291
  }
292
+ async handleContactGraphEvent(handler, payload) {
293
+ if (!handler)
294
+ return;
295
+ try {
296
+ await handler(payload);
297
+ }
298
+ catch (error) {
299
+ console.error('[canon-sdk] Contact-graph handler failed:', error instanceof Error ? error.message : error);
300
+ }
301
+ }
181
302
  async stop() {
182
303
  if (!this.running)
183
304
  return;
@@ -385,7 +506,9 @@ export class CanonAgent {
385
506
  agentId: this.agentId,
386
507
  ownerId: '',
387
508
  ownerName: '',
388
- accessLevel: 'open',
509
+ discoverable: false,
510
+ inboundPolicy: 'approval-required',
511
+ groupJoinPolicy: 'approval-required',
389
512
  };
390
513
  // Build context methods bound to this conversation
391
514
  const deleteMessage = (messageId) => this.apiClient.deleteMessage(conversationId, messageId);
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { CanonAgent } from './canon-agent.js';
2
- export { CanonApiError } from '@canonmsg/core';
2
+ export type { AgentContactsAPI, AgentUsersAPI } from './canon-agent.js';
3
+ export { CanonApiError, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, } from '@canonmsg/core';
4
+ export type { CanonContact, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, } from '@canonmsg/core';
3
5
  export { SessionManager } from './session-manager.js';
4
6
  export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
5
7
  export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
6
8
  export type { SessionConfig, Session } from './session-manager.js';
7
9
  export type { AgentContext, CanonContactRequest, CanonMessage, CanonConversation, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
8
- export type { SDKMessage, SDKConversation, CanonAgentOptions, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
10
+ export type { SDKMessage, SDKConversation, CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { CanonAgent } from './canon-agent.js';
2
- export { CanonApiError } from '@canonmsg/core';
2
+ export { CanonApiError, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, } from '@canonmsg/core';
3
3
  export { SessionManager } from './session-manager.js';
4
4
  export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
@@ -1,4 +1,4 @@
1
- import { type AgentContext, type CanonClient, type ContactApprovedPayload, type ContactRequestPayload } from '@canonmsg/core';
1
+ import { type AgentContext, type CanonClient, type ContactAddedPayload, type ContactApprovedPayload, type ContactRemovedPayload, type ContactRequestPayload } from '@canonmsg/core';
2
2
  import { Debouncer } from './debouncer.js';
3
3
  /**
4
4
  * Wraps @canonmsg/core's CanonStream with SDK-specific features:
@@ -13,6 +13,8 @@ export declare class RealtimeManager {
13
13
  private onAgentContext;
14
14
  private onContactRequest;
15
15
  private onContactApproved;
16
+ private onContactAdded;
17
+ private onContactRemoved;
16
18
  private onConnected;
17
19
  private onDisconnected;
18
20
  constructor(apiKey: string, debouncer: Debouncer, agentId: string, streamUrl?: string, apiClient?: CanonClient);
@@ -21,6 +23,10 @@ export declare class RealtimeManager {
21
23
  onContactRequest?: (payload: ContactRequestPayload) => void;
22
24
  onContactApproved?: (payload: ContactApprovedPayload) => void;
23
25
  }): void;
26
+ setContactGraphHandlers(handlers: {
27
+ onContactAdded?: (payload: ContactAddedPayload) => void;
28
+ onContactRemoved?: (payload: ContactRemovedPayload) => void;
29
+ }): void;
24
30
  setConnectionHandlers(handlers: {
25
31
  onConnected?: () => void;
26
32
  onDisconnected?: () => void;
package/dist/realtime.js CHANGED
@@ -12,6 +12,8 @@ export class RealtimeManager {
12
12
  onAgentContext = null;
13
13
  onContactRequest = null;
14
14
  onContactApproved = null;
15
+ onContactAdded = null;
16
+ onContactRemoved = null;
15
17
  onConnected = null;
16
18
  onDisconnected = null;
17
19
  constructor(apiKey, debouncer, agentId, streamUrl, apiClient) {
@@ -59,6 +61,12 @@ export class RealtimeManager {
59
61
  onContactApproved: (payload) => {
60
62
  this.onContactApproved?.(payload);
61
63
  },
64
+ onContactAdded: (payload) => {
65
+ this.onContactAdded?.(payload);
66
+ },
67
+ onContactRemoved: (payload) => {
68
+ this.onContactRemoved?.(payload);
69
+ },
62
70
  onConnected: () => {
63
71
  // Reset backoff is handled internally by CanonStream
64
72
  this.onConnected?.();
@@ -79,6 +87,10 @@ export class RealtimeManager {
79
87
  this.onContactRequest = handlers.onContactRequest ?? null;
80
88
  this.onContactApproved = handlers.onContactApproved ?? null;
81
89
  }
90
+ setContactGraphHandlers(handlers) {
91
+ this.onContactAdded = handlers.onContactAdded ?? null;
92
+ this.onContactRemoved = handlers.onContactRemoved ?? null;
93
+ }
82
94
  setConnectionHandlers(handlers) {
83
95
  this.onConnected = handlers.onConnected ?? null;
84
96
  this.onDisconnected = handlers.onDisconnected ?? null;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { AgentClientType, CanonRuntimeDescriptor, CanonMessage, CanonConversation, CanonContactRequest, AgentContext, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
2
- import type { CanonMessage, CanonConversation, CreateWorkSessionOptions, SendMessageOptions, UpdateWorkSessionConversationOptions } from '@canonmsg/core';
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, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
2
+ import type { AddMemberResult, CanonMessage, CanonConversation, CreateWorkSessionOptions, SendMessageOptions, UpdateWorkSessionConversationOptions } from '@canonmsg/core';
3
3
  import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
4
4
  export type SDKMessage = CanonMessage;
5
5
  export type SDKConversation = CanonConversation;
@@ -56,8 +56,14 @@ export interface MessageHandlerContext {
56
56
  leave: () => Promise<void>;
57
57
  /** Toggle emoji reaction on a message */
58
58
  react: (messageId: string, emoji: string) => Promise<void>;
59
- /** Add a member to this conversation (requires owner/admin role) */
60
- addMember: (userId: string) => Promise<void>;
59
+ /**
60
+ * Add a member to this conversation (requires owner/admin role).
61
+ * Returns `{ status: 'added' }` on immediate add, or
62
+ * `{ status: 'pending', requestId }` if the target requires approval —
63
+ * a contact-request (kind: 'group_invite') is created and the join
64
+ * lands when the approver accepts.
65
+ */
66
+ addMember: (userId: string) => Promise<AddMemberResult>;
61
67
  /** Remove a member from this conversation (requires owner/admin role) */
62
68
  removeMember: (userId: string) => Promise<void>;
63
69
  /** Create a Canon work session rooted in this conversation. */
@@ -126,3 +132,33 @@ export interface CanonAgentOptions {
126
132
  sessionState?: boolean;
127
133
  }
128
134
  export type ContactRequestHandler = (request: import('@canonmsg/core').CanonContactRequest) => void | Promise<void>;
135
+ export type ContactAddedHandler = (contact: import('@canonmsg/core').ContactAddedPayload) => void | Promise<void>;
136
+ export type ContactRemovedHandler = (payload: import('@canonmsg/core').ContactRemovedPayload) => void | Promise<void>;
137
+ /**
138
+ * Result of `agent.reachOut(card)` — describes which side-effect ran so the
139
+ * caller can decide what to tell the LLM. `messaged` means the agent opened
140
+ * (or sent into) a direct conversation; `requested` means the target's
141
+ * inbound policy required a contact request, which has been created;
142
+ * `pending` means a prior outbound request is still awaiting approval; and
143
+ * `blocked` / `unavailable` describe terminal states with `reason` set.
144
+ */
145
+ export type ReachOutResult = {
146
+ status: 'messaged';
147
+ conversationId: string;
148
+ messageId?: string;
149
+ } | {
150
+ status: 'requested';
151
+ requestId: string | null;
152
+ } | {
153
+ status: 'pending';
154
+ requestId: string | null;
155
+ } | {
156
+ status: 'blocked' | 'unavailable';
157
+ reason: string;
158
+ };
159
+ export interface ReachOutOptions {
160
+ /** Optional first message to send when admission is `allowed`. */
161
+ text?: string;
162
+ /** Optional message to attach to the contact request when admission is `request-required`. */
163
+ requestMessage?: string;
164
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "0.9.2",
3
+ "version": "0.10.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": "^0.10.0"
31
+ "@canonmsg/core": "^0.12.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"