@canonmsg/agent-sdk 1.1.0 → 1.1.2

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.
@@ -62,7 +62,6 @@ export declare class CanonAgent {
62
62
  */
63
63
  reachOut(card: ContactCardPayload, options?: ReachOutOptions): Promise<ReachOutResult>;
64
64
  private executeReachOut;
65
- private openConversationAndMaybeMessage;
66
65
  start(): Promise<void>;
67
66
  createConversation(options: CreateConversationOptions): Promise<{
68
67
  conversationId: string;
@@ -1,4 +1,4 @@
1
- import { CanonClient, createRuntimeStatePublisher, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, rtdbRead, rtdbWrite, mergeWorkSessionContexts, normalizeTurnMetadata, } from '@canonmsg/core';
1
+ import { CanonClient, createRuntimeStatePublisher, FINAL_MESSAGE_HANDOFF_MS, initRTDBAuth, rtdbRead, rtdbWrite, mergeWorkSessionContexts, 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';
@@ -150,9 +150,9 @@ export class CanonAgent {
150
150
  async reachOut(card, options) {
151
151
  const targetUserId = card.userId;
152
152
  // Include the opener/request payloads in the dedupe key so two concurrent
153
- // calls with different `text` or `requestMessage` don't silently collapse
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 ?? ''}`;
155
+ const inFlightKey = `${targetUserId}\u0000${options?.text ?? ''}\u0000${options?.requestMessage ?? ''}\u0000${JSON.stringify(options?.sessionConfig ?? null)}`;
156
156
  const inFlight = this.reachOutInFlight.get(inFlightKey);
157
157
  if (inFlight)
158
158
  return inFlight;
@@ -163,45 +163,12 @@ export class CanonAgent {
163
163
  return promise;
164
164
  }
165
165
  async executeReachOut(targetUserId, options) {
166
- const { admission } = await this.apiClient.resolveAdmission(targetUserId);
167
- if (admission.state === 'allowed' && admission.canMessage) {
168
- return this.openConversationAndMaybeMessage(targetUserId, options);
169
- }
170
- if (admission.state === 'pending-outbound') {
171
- return { status: 'pending', requestId: admission.pendingRequestId ?? null };
172
- }
173
- if (admission.state === 'request-required' && admission.canRequestContact) {
174
- const result = await this.apiClient.createContactRequest(targetUserId, options?.requestMessage ?? null);
175
- // The server may report 'open' if the target's policy flipped between
176
- // the resolveAdmission call and the request. No request doc was
177
- // written — fall through to the messaging path so the caller's intent
178
- // ("reach this user") is honored end-to-end. Without this, the caller
179
- // would get { status: 'requested' } despite no request existing.
180
- if (result.status === 'open') {
181
- return this.openConversationAndMaybeMessage(targetUserId, options);
182
- }
183
- // 'duplicate' means a pending request already existed; surface as
184
- // 'pending'. 'created' is the normal "request just landed" path.
185
- if (result.status === 'duplicate') {
186
- return { status: 'pending', requestId: result.requestId };
187
- }
188
- return { status: 'requested', requestId: result.requestId };
189
- }
190
- if (admission.state === 'blocked') {
191
- return { status: 'blocked', reason: 'blocked' };
192
- }
193
- return { status: 'unavailable', reason: admission.state };
194
- }
195
- async openConversationAndMaybeMessage(targetUserId, options) {
196
- const { conversationId } = await this.apiClient.createConversation({
197
- type: 'direct',
166
+ return reachOutToCanonContact(this.apiClient, {
198
167
  targetUserId,
168
+ text: options?.text ?? null,
169
+ requestMessage: options?.requestMessage ?? null,
170
+ sessionConfig: options?.sessionConfig ?? null,
199
171
  });
200
- if (options?.text) {
201
- const { messageId } = await this.apiClient.sendMessage(conversationId, options.text);
202
- return { status: 'messaged', conversationId, messageId };
203
- }
204
- return { status: 'messaged', conversationId };
205
172
  }
206
173
  async start() {
207
174
  if (this.running)
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, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
2
- import type { AddMemberResult, CanonMessage, CanonConversation, CanonRuntimeActionDispatch, 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, SessionConfig, CreateConversationOptions, TurnLifecycleState, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
2
+ import type { AddMemberResult, CanonMessage, CanonConversation, CanonRuntimeActionDispatch, CreateWorkSessionOptions, SendMessageOptions, SessionConfig, UpdateWorkSessionConversationOptions } from '@canonmsg/core';
3
3
  import type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions } from './media.js';
4
4
  export interface ProgressMessageOptions extends SendMessageOptions {
5
5
  /**
@@ -165,6 +165,9 @@ export type ReachOutResult = {
165
165
  } | {
166
166
  status: 'pending';
167
167
  requestId: string | null;
168
+ } | {
169
+ status: 'setup_required';
170
+ reason: string;
168
171
  } | {
169
172
  status: 'blocked' | 'unavailable';
170
173
  reason: string;
@@ -172,6 +175,8 @@ export type ReachOutResult = {
172
175
  export interface ReachOutOptions {
173
176
  /** Optional first message to send when admission is `allowed`. */
174
177
  text?: string;
175
- /** Optional message to attach to the contact request when admission is `request-required`. */
178
+ /** Optional contact-request note. Defaults to `text` when admission is `request-required`. */
176
179
  requestMessage?: string;
180
+ /** Explicit session setup to use when the contact-card target is an agent. */
181
+ sessionConfig?: SessionConfig | null;
177
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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.0"
31
+ "@canonmsg/core": "^0.15.4"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"