@canonmsg/agent-sdk 5.1.3 → 6.0.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
@@ -11,6 +11,7 @@ import { CanonAgent } from '@canonmsg/agent-sdk';
11
11
 
12
12
  const agent = new CanonAgent({
13
13
  apiKey: process.env.CANON_API_KEY!,
14
+ environmentId: process.env.CANON_ENVIRONMENT_ID!,
14
15
  historyLimit: 30,
15
16
  });
16
17
 
@@ -35,8 +36,11 @@ No additional dependencies required — the SDK uses native `fetch` and `Readabl
35
36
  | Option | Type | Default | Description |
36
37
  |---|---|---|---|
37
38
  | `apiKey` | `string` | **required** | API key obtained after agent registration approval |
38
- | `baseUrl` | `string` | Canon production URL | Override the API base URL |
39
- | `streamUrl` | `string` | Canon stream service URL | Override the SSE stream URL |
39
+ | `environmentId` | `string` | **required** | Canon trust domain shared by API, stream, RTDB, and future cryptographic state |
40
+ | `baseUrl` | `string` | Environment default | Override the API base URL when the selected environment has no packaged default |
41
+ | `streamUrl` | `string` | Environment default | Override the SSE stream URL when the selected environment has no packaged default |
42
+ | `rtdbUrl` | `string` | Environment default | Override the Realtime Database URL for runtime state |
43
+ | `firebaseApiKey` | `string` | Environment default | Override the public Firebase web API key used for RTDB token exchange |
40
44
  | `deliveryMode` | `'auto' \| 'sse'` | `'auto'` | How the SDK receives new messages |
41
45
  | `debounceMs` | `number` | `2000` | Batching window for incoming messages per conversation |
42
46
  | `historyLimit` | `number` | `50` | Number of historical messages to fetch (max 100) |
@@ -56,6 +60,7 @@ Generic SDK agents publish no setup controls by default. If your SDK runtime has
56
60
  ```typescript
57
61
  const agent = new CanonAgent({
58
62
  apiKey: process.env.CANON_API_KEY!,
63
+ environmentId: process.env.CANON_ENVIRONMENT_ID!,
59
64
  runtimeDescriptor: {
60
65
  coreControls: [
61
66
  {
@@ -99,6 +104,7 @@ SDK agents only advertise Stop or Send Now when they register runtime-control ha
99
104
  ```typescript
100
105
  const agent = new CanonAgent({
101
106
  apiKey: process.env.CANON_API_KEY!,
107
+ environmentId: process.env.CANON_ENVIRONMENT_ID!,
102
108
  sessions: { enabled: true },
103
109
  runtimeControls: {
104
110
  onInterrupt: ({ conversationId }) => {
@@ -310,8 +316,17 @@ Register a new agent using the static helpers (no API key needed):
310
316
  ```typescript
311
317
  import { CanonAgent } from '@canonmsg/agent-sdk';
312
318
 
319
+ const canonConnection = {
320
+ environmentId: process.env.CANON_ENVIRONMENT_ID!,
321
+ baseUrl: process.env.CANON_BASE_URL,
322
+ streamUrl: process.env.CANON_STREAM_URL,
323
+ rtdbUrl: process.env.CANON_RTDB_URL,
324
+ firebaseApiKey: process.env.CANON_FIREBASE_API_KEY,
325
+ };
326
+
313
327
  // 1. Submit registration request
314
328
  const { requestId, pollToken } = await CanonAgent.register({
329
+ ...canonConnection,
315
330
  name: 'My Agent',
316
331
  description: 'A helpful assistant',
317
332
  ownerPhone: '+1234567890',
@@ -322,19 +337,25 @@ console.log('Registration submitted:', requestId);
322
337
  await saveRegistrationPickup({ requestId, pollToken });
323
338
 
324
339
  // 2. Poll for approval
325
- const status = await CanonAgent.checkStatus(requestId, { pollToken });
340
+ const status = await CanonAgent.checkStatus(requestId, { ...canonConnection, pollToken });
326
341
  console.log('Status:', status.status); // 'pending' | 'approved' | 'rejected'
327
342
 
328
343
  if (status.status === 'approved' && status.apiKey) {
329
344
  console.log('Agent ID:', status.agentId);
330
345
  await saveAgentCredentials({ agentId: status.agentId, apiKey: status.apiKey });
331
- await CanonAgent.ackStatus(requestId, { pollToken });
346
+ await CanonAgent.ackStatus(requestId, { ...canonConnection, pollToken });
332
347
  }
333
348
  ```
334
349
 
335
350
  The approved response only includes the API key until you acknowledge delivery. Persist it on the first approved poll, then call `ackStatus()` so Canon clears the plaintext key from the request.
336
351
  Replace `saveRegistrationPickup` and `saveAgentCredentials` with your own encrypted/local secret-store writes; do not print these values in logs.
337
352
 
353
+ Registration and startup verify that the API and stream advertise the selected
354
+ environment before sending credentials. `canon-legacy-v1` currently has
355
+ packaged endpoint defaults; Canon dev/prod environments must provide all four
356
+ endpoint overrides. Persist the environment ID and complete endpoint
357
+ snapshot beside the API key so a profile cannot mix Canon trust domains.
358
+
338
359
  ## Error Handling
339
360
 
340
361
  The SDK exports `CanonApiError` for typed error handling:
@@ -1,5 +1,5 @@
1
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
- import type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, CreateConversationOptions, MessageHandler, MessageUpdatedHandler, ReachOutOptions, ReachOutResult, ContactRequestHandler, RuntimeSignalHandler, RuntimePrimitiveHandler } from './types.js';
2
+ import type { CanonAgentConnectionOptions, 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
@@ -25,6 +25,7 @@ export interface AgentConversationsAPI {
25
25
  }
26
26
  export declare class CanonAgent {
27
27
  private options;
28
+ private readonly runtimeConnection;
28
29
  private apiClient;
29
30
  private authManager;
30
31
  private debouncer;
@@ -192,13 +193,11 @@ export declare class CanonAgent {
192
193
  ownerPhone: string;
193
194
  developerInfo: string;
194
195
  avatarUrl?: string;
195
- baseUrl?: string;
196
- }): Promise<{
196
+ } & CanonAgentConnectionOptions): Promise<{
197
197
  requestId: string;
198
198
  pollToken?: string;
199
199
  }>;
200
- static checkStatus(requestId: string, options?: string | {
201
- baseUrl?: string;
200
+ static checkStatus(requestId: string, options: CanonAgentConnectionOptions & {
202
201
  pollToken?: string;
203
202
  }): Promise<{
204
203
  status: string;
@@ -207,8 +206,7 @@ export declare class CanonAgent {
207
206
  apiKey?: string;
208
207
  apiKeyDelivered?: boolean;
209
208
  }>;
210
- static ackStatus(requestId: string, options?: string | {
211
- baseUrl?: string;
209
+ static ackStatus(requestId: string, options: CanonAgentConnectionOptions & {
212
210
  pollToken?: string;
213
211
  }): Promise<void>;
214
212
  }
@@ -1,4 +1,4 @@
1
- import { ApprovalManager, RuntimeRequestManager, runtimeInputDescriptor, runtimeCardDescriptor, CanonClient, ControlChannelPoller, buildCanonTurnContextV2, buildCanonGroupContext, buildParticipationHistorySnapshot, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, normalizeRuntimeCommandDescriptors, normalizeTurnMetadata, reachOutToCanonContact, resolveCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, selectActiveSelfContexts, renderCanonHostInboundContent, sendMessageWithRetry, } from '@canonmsg/core';
1
+ import { ApprovalManager, RuntimeRequestManager, runtimeInputDescriptor, runtimeCardDescriptor, CanonClient, ControlChannelPoller, buildCanonTurnContextV2, buildCanonGroupContext, buildParticipationHistorySnapshot, createTurnOutputController, createRuntimeStatePublisher, createTypingStatusPublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, buildRuntimeInputOutcome, initRTDBAuth, normalizeRuntimeCommandDescriptors, normalizeTurnMetadata, reachOutToCanonContact, resolveCanonReplyContext, resolveMessageActiveSelfContextId, resolveRuntimeProvenance, selectActiveSelfContexts, renderCanonHostInboundContent, resolveCanonRuntimeConnection, sendMessageWithRetry, verifyCanonRuntimeConnection, } from '@canonmsg/core';
2
2
  import { createHash, randomUUID } from 'node:crypto';
3
3
  import { AuthManager } from './auth.js';
4
4
  import { Debouncer } from './debouncer.js';
@@ -242,6 +242,7 @@ function isAbortLikeError(error) {
242
242
  }
243
243
  export class CanonAgent {
244
244
  options;
245
+ runtimeConnection;
245
246
  apiClient;
246
247
  authManager;
247
248
  debouncer;
@@ -291,14 +292,25 @@ export class CanonAgent {
291
292
  typingSignals;
292
293
  sseConnectedLogged = false;
293
294
  constructor(options) {
295
+ this.runtimeConnection = resolveCanonRuntimeConnection({
296
+ environmentId: options.environmentId,
297
+ apiBaseUrl: options.baseUrl,
298
+ streamUrl: options.streamUrl,
299
+ rtdbUrl: options.rtdbUrl,
300
+ firebaseWebApiKey: options.firebaseApiKey,
301
+ });
294
302
  this.options = {
295
- baseUrl: 'https://api-6m6mlelskq-uc.a.run.app',
296
303
  deliveryMode: 'auto',
297
304
  debounceMs: 2000,
298
305
  historyLimit: 50,
299
306
  autoMarkRead: true,
300
307
  runtimeControlSurface: 'agent',
301
308
  ...options,
309
+ environmentId: this.runtimeConnection.environmentId,
310
+ baseUrl: this.runtimeConnection.apiBaseUrl,
311
+ streamUrl: this.runtimeConnection.streamUrl,
312
+ rtdbUrl: this.runtimeConnection.rtdbUrl,
313
+ firebaseApiKey: this.runtimeConnection.firebaseWebApiKey,
302
314
  };
303
315
  this.apiClient = new CanonClient(this.options.apiKey, this.options.baseUrl);
304
316
  this.typingSignals = createTypingStatusPublisher({
@@ -576,6 +588,9 @@ export class CanonAgent {
576
588
  });
577
589
  }
578
590
  async start() {
591
+ if (this.running)
592
+ return;
593
+ await verifyCanonRuntimeConnection(this.runtimeConnection);
579
594
  if (this.running)
580
595
  return;
581
596
  this.running = true;
@@ -583,7 +598,10 @@ export class CanonAgent {
583
598
  // the SDK (control poller, runtime-state publishers) threads this handle;
584
599
  // the SDK never reads through core's deprecated module-global default,
585
600
  // so multiple CanonAgents in one process cannot clobber each other.
586
- this.rtdbHandle = initRTDBAuth(this.apiClient);
601
+ this.rtdbHandle = initRTDBAuth(this.apiClient, {
602
+ rtdbUrl: this.runtimeConnection.rtdbUrl,
603
+ firebaseApiKey: this.runtimeConnection.firebaseWebApiKey,
604
+ });
587
605
  // 1. Authenticate
588
606
  const { agentId } = await this.authManager.authenticate();
589
607
  this.agentId = agentId;
@@ -2019,17 +2037,37 @@ export class CanonAgent {
2019
2037
  }
2020
2038
  // Static registration helpers (unauthenticated)
2021
2039
  static async register(options) {
2022
- const { baseUrl, ...body } = options;
2023
- return CanonClient.register(baseUrl, body);
2040
+ const { environmentId, baseUrl, streamUrl, rtdbUrl, firebaseApiKey, ...body } = options;
2041
+ const connection = resolveCanonRuntimeConnection({
2042
+ environmentId,
2043
+ apiBaseUrl: baseUrl,
2044
+ streamUrl,
2045
+ rtdbUrl,
2046
+ firebaseWebApiKey: firebaseApiKey,
2047
+ });
2048
+ await verifyCanonRuntimeConnection(connection);
2049
+ return CanonClient.register(connection.apiBaseUrl, body);
2024
2050
  }
2025
2051
  static async checkStatus(requestId, options) {
2026
- const baseUrl = typeof options === 'string' ? options : options?.baseUrl;
2027
- const pollToken = typeof options === 'string' ? undefined : options?.pollToken;
2028
- return CanonClient.checkStatus(baseUrl, requestId, pollToken);
2052
+ const connection = resolveCanonRuntimeConnection({
2053
+ environmentId: options.environmentId,
2054
+ apiBaseUrl: options.baseUrl,
2055
+ streamUrl: options.streamUrl,
2056
+ rtdbUrl: options.rtdbUrl,
2057
+ firebaseWebApiKey: options.firebaseApiKey,
2058
+ });
2059
+ await verifyCanonRuntimeConnection(connection);
2060
+ return CanonClient.checkStatus(connection.apiBaseUrl, requestId, options.pollToken);
2029
2061
  }
2030
2062
  static async ackStatus(requestId, options) {
2031
- const baseUrl = typeof options === 'string' ? options : options?.baseUrl;
2032
- const pollToken = typeof options === 'string' ? undefined : options?.pollToken;
2033
- await CanonClient.ackRegistrationStatus(baseUrl, requestId, pollToken);
2063
+ const connection = resolveCanonRuntimeConnection({
2064
+ environmentId: options.environmentId,
2065
+ apiBaseUrl: options.baseUrl,
2066
+ streamUrl: options.streamUrl,
2067
+ rtdbUrl: options.rtdbUrl,
2068
+ firebaseWebApiKey: options.firebaseApiKey,
2069
+ });
2070
+ await verifyCanonRuntimeConnection(connection);
2071
+ await CanonClient.ackRegistrationStatus(connection.apiBaseUrl, requestId, options.pollToken);
2034
2072
  }
2035
2073
  }
package/dist/index.d.ts CHANGED
@@ -7,4 +7,4 @@ export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, infe
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
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
- 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';
10
+ export type { CanonAgentConnectionOptions, 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/types.d.ts CHANGED
@@ -247,10 +247,17 @@ export type RuntimePrimitiveHandler = (context: RuntimePrimitiveContext) => void
247
247
  export type RuntimePrimitiveHandlers = Partial<Record<CanonRuntimePrimitiveId, RuntimePrimitiveHandler>> & {
248
248
  '*'?: RuntimePrimitiveHandler;
249
249
  };
250
- export interface CanonAgentOptions {
251
- apiKey: string;
250
+ export interface CanonAgentConnectionOptions {
251
+ /** Canon trust domain for API, stream, RTDB, and future cryptographic state. */
252
+ environmentId: import('@canonmsg/core').CanonEnvironmentId;
252
253
  baseUrl?: string;
253
254
  streamUrl?: string;
255
+ rtdbUrl?: string;
256
+ /** Public Firebase client configuration used for RTDB custom-token exchange. */
257
+ firebaseApiKey?: string;
258
+ }
259
+ export interface CanonAgentOptions extends CanonAgentConnectionOptions {
260
+ apiKey: string;
254
261
  /** `auto` resolves to SSE. */
255
262
  deliveryMode?: DeliveryMode;
256
263
  debounceMs?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "5.1.3",
3
+ "version": "6.0.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": "^5.0.0"
31
+ "@canonmsg/core": "^6.0.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"