@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.
- package/dist/canon-agent.d.ts +3 -0
- package/dist/canon-agent.js +99 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/media.d.ts +6 -1
- package/dist/media.js +26 -1
- package/dist/types.d.ts +4 -2
- package/package.json +2 -2
package/dist/canon-agent.d.ts
CHANGED
|
@@ -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;
|
package/dist/canon-agent.js
CHANGED
|
@@ -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.
|
|
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.
|
|
31
|
+
"@canonmsg/core": "^0.18.1"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|