@canonmsg/agent-sdk 1.4.0 → 1.4.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.
@@ -49,6 +49,7 @@ export declare class CanonAgent {
49
49
  private readonly lastSeenSignal;
50
50
  private readonly primitiveRequestDedupe;
51
51
  private readonly activeAbortControllers;
52
+ private readonly activeTurns;
52
53
  private readonly conversationMemberIds;
53
54
  private readonly pendingMembershipChanges;
54
55
  constructor(options: CanonAgentOptions);
@@ -128,6 +129,8 @@ export declare class CanonAgent {
128
129
  private clearRuntimePrimitiveRequest;
129
130
  private prunePrimitiveRequestDedupe;
130
131
  private handleRuntimeSignal;
132
+ private firstActiveTurn;
133
+ private publishAcceptedRuntimeSignal;
131
134
  private abortActiveTurns;
132
135
  private resolveBatchDeliveryIntent;
133
136
  private notifyMessageInterrupt;
@@ -1,8 +1,8 @@
1
- import { CanonClient, buildCanonGroupContext, createRuntimeStatePublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, resolveMessageActiveSelfContextId, selectActiveSelfContexts, } from '@canonmsg/core';
1
+ import { CanonClient, buildCanonGroupContext, createRuntimeStatePublisher, diffCanonMemberIds, FINAL_MESSAGE_HANDOFF_MS, RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, initRTDBAuth, rtdbRead, rtdbWrite, normalizeTurnMetadata, reachOutToCanonContact, resolveCanonReplyContext, resolveMessageActiveSelfContextId, selectActiveSelfContexts, } from '@canonmsg/core';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { AuthManager } from './auth.js';
4
4
  import { Debouncer } from './debouncer.js';
5
- import { materializeMessageMedia, sendMediaFileMessage, uploadMediaFile, } from './media.js';
5
+ import { materializeMessageMedia, materializeReplyContextMedia, sendMediaFileMessage, uploadMediaFile, } from './media.js';
6
6
  import { SessionManager } from './session-manager.js';
7
7
  const AGENT_RUNTIME_HEARTBEAT_MS = 30_000;
8
8
  const RUNTIME_PRIMITIVE_DEDUPE_TTL_MS = 5 * 60 * 1000;
@@ -168,6 +168,19 @@ function normalizeRuntimeActivityItem(item) {
168
168
  updatedAt: item.updatedAt || Date.now(),
169
169
  };
170
170
  }
171
+ function createTurnAbortError() {
172
+ const error = new Error('Canon turn was interrupted before reply delivery.');
173
+ error.name = 'AbortError';
174
+ return error;
175
+ }
176
+ function isAbortLikeError(error) {
177
+ if (!error || typeof error !== 'object')
178
+ return false;
179
+ const record = error;
180
+ if (record.name === 'AbortError' || record.code === 'ABORT_ERR')
181
+ return true;
182
+ return typeof record.message === 'string' && /\babort(?:ed)?\b/i.test(record.message);
183
+ }
171
184
  export class CanonAgent {
172
185
  options;
173
186
  apiClient;
@@ -199,6 +212,7 @@ export class CanonAgent {
199
212
  lastSeenSignal = new Map();
200
213
  primitiveRequestDedupe = new Map();
201
214
  activeAbortControllers = new Map();
215
+ activeTurns = new Map();
202
216
  conversationMemberIds = new Map();
203
217
  pendingMembershipChanges = new Map();
204
218
  constructor(options) {
@@ -832,6 +846,7 @@ export class CanonAgent {
832
846
  await Promise.resolve(rtdbWrite(`/control/${conversationId}/${this.agentId}/signal`, null)).catch(() => { });
833
847
  return;
834
848
  }
849
+ const activeTurn = this.firstActiveTurn(conversationId);
835
850
  const abortSignal = this.abortActiveTurns(conversationId);
836
851
  const droppedMessages = signal === 'new_session'
837
852
  ? this.sessionManager?.resetSession(conversationId) ?? []
@@ -844,6 +859,10 @@ export class CanonAgent {
844
859
  return Promise.resolve();
845
860
  return this.apiClient.updateMessageDisposition(conversationId, message.id, 'rejected').catch(() => { });
846
861
  }));
862
+ await this.publishAcceptedRuntimeSignal(conversationId, signal, activeTurn, {
863
+ hasActiveTurn: Boolean(abortSignal),
864
+ droppedCount: droppedMessages.length,
865
+ });
847
866
  await Promise.resolve(handler?.({
848
867
  conversationId,
849
868
  signal: signal,
@@ -855,6 +874,39 @@ export class CanonAgent {
855
874
  });
856
875
  await Promise.resolve(rtdbWrite(`/control/${conversationId}/${this.agentId}/signal`, null)).catch(() => { });
857
876
  }
877
+ firstActiveTurn(conversationId) {
878
+ const turns = this.activeTurns.get(conversationId);
879
+ if (!turns || turns.size === 0)
880
+ return null;
881
+ return turns.values().next().value ?? null;
882
+ }
883
+ async publishAcceptedRuntimeSignal(conversationId, signal, activeTurn, outcome) {
884
+ if (!this.agentId)
885
+ return;
886
+ const shouldPublishInterrupted = signal === 'interrupt'
887
+ || signal === 'stop_and_drop'
888
+ || outcome.hasActiveTurn
889
+ || outcome.droppedCount > 0;
890
+ const runtimeState = shouldPublishInterrupted
891
+ ? this.createRuntimeStatePublisher()
892
+ : null;
893
+ if (runtimeState) {
894
+ await Promise.resolve(runtimeState.writeTurnState(conversationId, {
895
+ turnId: activeTurn?.turnId ?? null,
896
+ state: 'interrupted',
897
+ queueDepth: this.sessionManager?.getQueueDepth(conversationId) ?? 0,
898
+ currentSpeakerId: this.agentId,
899
+ activeMessageIds: activeTurn?.activeMessageIds ?? [],
900
+ capabilities: this.buildRuntimeCapabilities(),
901
+ ...(activeTurn?.openedAt ? { openedAt: activeTurn.openedAt } : {}),
902
+ completedAt: { '.sv': 'timestamp' },
903
+ })).catch(() => { });
904
+ }
905
+ await Promise.all([
906
+ this.apiClient.clearStreaming(conversationId).catch(() => { }),
907
+ this.apiClient.setTyping(conversationId, false).catch(() => { }),
908
+ ]);
909
+ }
858
910
  abortActiveTurns(conversationId) {
859
911
  const controllers = this.activeAbortControllers.get(conversationId);
860
912
  if (!controllers || controllers.size === 0)
@@ -927,9 +979,21 @@ export class CanonAgent {
927
979
  const runtimeState = this.createRuntimeStatePublisher();
928
980
  const queueDepth = () => this.sessionManager?.getQueueDepth(conversationId) ?? 0;
929
981
  const abortController = new AbortController();
982
+ const throwIfAborted = () => {
983
+ if (abortController.signal.aborted) {
984
+ throw createTurnAbortError();
985
+ }
986
+ };
930
987
  const activeControllers = this.activeAbortControllers.get(conversationId) ?? new Set();
931
988
  activeControllers.add(abortController);
932
989
  this.activeAbortControllers.set(conversationId, activeControllers);
990
+ const activeTurns = this.activeTurns.get(conversationId) ?? new Map();
991
+ activeTurns.set(abortController, {
992
+ turnId,
993
+ openedAt: turnOpenedAt,
994
+ activeMessageIds: messages.map((message) => message.id).filter(Boolean),
995
+ });
996
+ this.activeTurns.set(conversationId, activeTurns);
933
997
  const writeTurn = async (state) => {
934
998
  if (!runtimeState || !agentId)
935
999
  return;
@@ -947,6 +1011,7 @@ export class CanonAgent {
947
1011
  })).catch(() => { });
948
1012
  };
949
1013
  const setLiveState = async (state, text, streamingStatus) => {
1014
+ throwIfAborted();
950
1015
  await writeTurn(state);
951
1016
  if (streamingStatus) {
952
1017
  try {
@@ -1007,10 +1072,12 @@ export class CanonAgent {
1007
1072
  return;
1008
1073
  // Build reply functions
1009
1074
  const replyFinal = async (text, options) => {
1075
+ throwIfAborted();
1010
1076
  try {
1011
1077
  await this.apiClient.setTyping(conversationId, true, 'typing');
1012
1078
  }
1013
1079
  catch { }
1080
+ throwIfAborted();
1014
1081
  const sendOptions = withActiveSelfContext(options);
1015
1082
  const result = await this.apiClient.sendMessage(conversationId, text, {
1016
1083
  ...sendOptions,
@@ -1029,10 +1096,12 @@ export class CanonAgent {
1029
1096
  return result;
1030
1097
  };
1031
1098
  const replyProgress = async (text, options) => {
1099
+ throwIfAborted();
1032
1100
  await setLiveState('streaming', text, 'streaming');
1033
1101
  if (!options?.durable) {
1034
1102
  return { turnId, durable: false, messageId: null };
1035
1103
  }
1104
+ throwIfAborted();
1036
1105
  const { durable: _durable, ...sendOptions } = options;
1037
1106
  const sendOptionsWithContext = withActiveSelfContext(sendOptions);
1038
1107
  const result = await this.apiClient.sendMessage(conversationId, text, {
@@ -1054,6 +1123,9 @@ export class CanonAgent {
1054
1123
  }
1055
1124
  }
1056
1125
  const latestMessage = hydratedMessages[hydratedMessages.length - 1] ?? null;
1126
+ let replyContext = latestMessage
1127
+ ? resolveCanonReplyContext({ message: latestMessage, messages: history })
1128
+ : null;
1057
1129
  const resolvedActiveSelfContextId = resolveMessageActiveSelfContextId({
1058
1130
  messageId: latestMessage?.id,
1059
1131
  activeSelfContextIdByMessageId: page.activeSelfContextIdByMessageId,
@@ -1075,6 +1147,18 @@ export class CanonAgent {
1075
1147
  inboundPolicy: 'approval-required',
1076
1148
  groupJoinPolicy: 'approval-required',
1077
1149
  };
1150
+ if (replyContext?.found && replyContext.attachments?.length) {
1151
+ try {
1152
+ const materializedReply = await materializeReplyContextMedia(replyContext, {
1153
+ agentId: agent.agentId,
1154
+ conversationId,
1155
+ });
1156
+ replyContext = materializedReply.replyContext;
1157
+ }
1158
+ catch (error) {
1159
+ console.error(`[canon-sdk] Failed to materialize reply context media for ${conversationId}:`, error instanceof Error ? error.message : error);
1160
+ }
1161
+ }
1078
1162
  const membershipChange = this.pendingMembershipChanges.get(conversationId) ?? null;
1079
1163
  this.pendingMembershipChanges.delete(conversationId);
1080
1164
  const groupContext = this.buildGroupContext({
@@ -1112,10 +1196,12 @@ export class CanonAgent {
1112
1196
  });
1113
1197
  const uploadFile = (filePath, options) => uploadMediaFile(this.apiClient, conversationId, filePath, options);
1114
1198
  const replyWithFile = async (filePath, text = '', options) => {
1199
+ throwIfAborted();
1115
1200
  try {
1116
1201
  await this.apiClient.setTyping(conversationId, true, 'typing');
1117
1202
  }
1118
1203
  catch { }
1204
+ throwIfAborted();
1119
1205
  try {
1120
1206
  const result = await sendMediaFileMessage(this.apiClient, conversationId, filePath, text, {
1121
1207
  ...(options?.replyTo ? { replyTo: options.replyTo } : {}),
@@ -1147,9 +1233,11 @@ export class CanonAgent {
1147
1233
  }
1148
1234
  };
1149
1235
  // Invoke handler
1236
+ throwIfAborted();
1150
1237
  await this.handler({
1151
1238
  messages: hydratedMessages,
1152
1239
  history,
1240
+ replyContext,
1153
1241
  conversationId,
1154
1242
  conversation,
1155
1243
  ...(groupContext ? { groupContext } : {}),
@@ -1236,6 +1324,10 @@ export class CanonAgent {
1236
1324
  }
1237
1325
  }
1238
1326
  catch (err) {
1327
+ if (abortController.signal.aborted || isAbortLikeError(err)) {
1328
+ await writeTurn('interrupted');
1329
+ return;
1330
+ }
1239
1331
  console.error(`[canon-sdk] Handler error for ${conversationId}:`, err);
1240
1332
  await writeTurn('interrupted');
1241
1333
  }
@@ -1245,6 +1337,11 @@ export class CanonAgent {
1245
1337
  if (activeControllers?.size === 0) {
1246
1338
  this.activeAbortControllers.delete(conversationId);
1247
1339
  }
1340
+ const activeTurns = this.activeTurns.get(conversationId);
1341
+ activeTurns?.delete(abortController);
1342
+ if (activeTurns?.size === 0) {
1343
+ this.activeTurns.delete(conversationId);
1344
+ }
1248
1345
  clearInterval(thinkingKeepalive);
1249
1346
  // Always clear typing when done
1250
1347
  try {
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ export type { AgentContactsAPI, AgentUsersAPI } from './canon-agent.js';
3
3
  export { CanonApiError, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, } from '@canonmsg/core';
4
4
  export type { CanonContact, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeFact, CanonRuntimeFactGroup, CanonResolveAdmissionResult, ContactAddedPayload, ContactCardPayload, ContactRemovedPayload, ContactSource, HostAdmissionActionCapabilities, ResolvedAdmissionState, ResolvedAdmissionTargetSummary, ResolvedTargetAdmissionPayload, } from '@canonmsg/core';
5
5
  export { SessionManager } from './session-manager.js';
6
- export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
7
- export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
6
+ export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, materializeReplyContextMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
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, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
9
+ export type { AgentContext, CanonGroupContext, CanonKnownRecentParticipant, CanonMembershipChange, CanonContactRequest, CanonMessage, CanonConversation, CanonReplyContext, CanonSelfContext, SendContextualMessageOptions, SendContextualMessageResult, SendContextualSelfContextInput, SendMessageOptions, CreateConversationOptions, } from '@canonmsg/core';
10
10
  export type { CanonAgentOptions, ContactAddedHandler, ContactRemovedHandler, ContactRequestHandler, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, ReachOutOptions, ReachOutResult, RuntimePrimitiveContext, RuntimePrimitiveHandler, RuntimePrimitiveHandlers, SessionInfo, SessionOptions, DeliveryMode, } from './types.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { CanonAgent } from './canon-agent.js';
2
2
  export { CanonApiError, HOST_ADMISSION_ACTION_CAPABILITIES, HOST_ADMISSION_ACTIONS_DISABLED, } from '@canonmsg/core';
3
3
  export { SessionManager } from './session-manager.js';
4
- export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
4
+ export { DEFAULT_MEDIA_CACHE_DIR, getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, materializeReplyContextMedia, resolveAttachmentMimeType, sendMediaFileMessage, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
package/dist/media.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CanonClient, type CanonMessage, type MediaAttachment, type SendMessageOptions } from '@canonmsg/core';
1
+ import { CanonClient, type CanonReplyContext, type CanonMessage, type MediaAttachment, type SendMessageOptions } from '@canonmsg/core';
2
2
  export interface MaterializeMediaOptions {
3
3
  agentId: string;
4
4
  conversationId: string;
@@ -21,6 +21,10 @@ export interface MaterializedCanonAttachment extends MediaAttachment {
21
21
  conversationId: string;
22
22
  messageId: string;
23
23
  }
24
+ export interface MaterializedCanonReplyContext {
25
+ replyContext: CanonReplyContext | null;
26
+ materialized: MaterializedCanonAttachment[];
27
+ }
24
28
  /**
25
29
  * Anthropic `image` content blocks only accept these MIME types for
26
30
  * base64 sources. Anything outside this set must either be re-encoded or
@@ -46,6 +50,7 @@ export declare function materializeAttachment(attachment: MediaAttachment, optio
46
50
  index?: number;
47
51
  }): Promise<MaterializedCanonAttachment>;
48
52
  export declare function materializeMessageMedia(message: Pick<CanonMessage, 'id' | 'attachments'>, options: Omit<MaterializeMediaOptions, 'messageId'>): Promise<MaterializedCanonAttachment[]>;
53
+ export declare function materializeReplyContextMedia(replyContext: CanonReplyContext | null, options: Omit<MaterializeMediaOptions, 'messageId'>): Promise<MaterializedCanonReplyContext>;
49
54
  export declare function inferUploadMimeType(filePath: string, overrideMimeType?: string): string;
50
55
  export declare function uploadMediaFile(client: CanonClient, conversationId: string, filePath: string, options?: UploadMediaFileOptions): Promise<{
51
56
  url: string;
package/dist/media.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
2
  import { basename, dirname, extname, join } from 'node:path';
3
- import { CANON_DIR, } from '@canonmsg/core';
3
+ import { CANON_DIR, renderCanonHostInboundContent, } from '@canonmsg/core';
4
4
  const ANTHROPIC_IMAGE_MIME_TYPES = new Set([
5
5
  'image/jpeg',
6
6
  'image/png',
@@ -141,6 +141,31 @@ export async function materializeMessageMedia(message, options) {
141
141
  index,
142
142
  })));
143
143
  }
144
+ export async function materializeReplyContextMedia(replyContext, options) {
145
+ if (!replyContext?.found || !replyContext.attachments?.length) {
146
+ return { replyContext, materialized: [] };
147
+ }
148
+ const materialized = (await Promise.all(replyContext.attachments.map((attachment, index) => attachment.url
149
+ ? materializeAttachment(attachment, {
150
+ ...options,
151
+ messageId: replyContext.messageId,
152
+ index,
153
+ })
154
+ : Promise.resolve(null)))).filter((attachment) => attachment !== null);
155
+ return {
156
+ replyContext: {
157
+ ...replyContext,
158
+ body: renderCanonHostInboundContent({
159
+ text: replyContext.text,
160
+ contentType: replyContext.contentType,
161
+ attachments: replyContext.attachments,
162
+ contactCard: replyContext.contactCard,
163
+ senderType: replyContext.senderType ?? undefined,
164
+ }, materialized),
165
+ },
166
+ materialized,
167
+ };
168
+ }
144
169
  export function inferUploadMimeType(filePath, overrideMimeType) {
145
170
  if (overrideMimeType)
146
171
  return overrideMimeType;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, 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, CanonGroupContext, CanonMessage, CanonConversation, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, SendMessageOptions, SendContextualSelfContextInput, SessionConfig } from '@canonmsg/core';
1
+ export type { AddMemberResult, AgentClientType, CanonGroupContext, CanonRuntimeActivityItem, CanonRuntimeActivityKind, CanonRuntimeActivityStatus, CanonRuntimeDescriptor, CanonRuntimeFact, CanonRuntimeFactGroup, CanonRuntimePrimitiveId, CanonMessage, CanonConversation, CanonReplyContext, 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, CanonGroupContext, CanonMessage, CanonConversation, CanonReplyContext, ContactCardPayload, CanonRuntimeActionDispatch, CanonRuntimePrimitiveId, 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
  /**
@@ -37,6 +37,8 @@ export interface TurnController {
37
37
  export interface MessageHandlerContext {
38
38
  messages: CanonMessage[];
39
39
  history: CanonMessage[];
40
+ /** Resolved message/media content for the latest swipe-reply target, if any. */
41
+ replyContext: CanonReplyContext | null;
40
42
  conversationId: string;
41
43
  conversation: CanonConversation;
42
44
  /** Lightweight group awareness, present for group conversations. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "1.4.0",
3
+ "version": "1.4.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": "^0.18.0"
31
+ "@canonmsg/core": "^0.18.1"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"