@canonmsg/agent-sdk 0.7.0 → 0.8.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/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { CanonAgent } from './canon-agent.js';
2
2
  export { CanonApiError } from '@canonmsg/core';
3
3
  export { SessionManager } from './session-manager.js';
4
- export { getMessageAttachments, inferUploadMimeType, materializeAttachment, materializeMessageMedia, uploadMediaFile, } from './media.js';
5
- export type { MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
4
+ export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
5
+ export type { AnthropicImageBlock, AnthropicImageMimeType, MaterializeMediaOptions, MaterializedCanonAttachment, ReplyWithFileOptions, UploadMediaFileOptions, } from './media.js';
6
6
  export type { SessionConfig, Session } from './session-manager.js';
7
7
  export type { AgentContext, CanonMessage, CanonConversation, CanonResolvedWorkSession, CanonWorkSession, CanonWorkSessionContext, CanonWorkSessionConversationRole, CanonWorkSessionDisclosureMode, CanonWorkSessionParticipant, CanonWorkSessionStatus, CreateWorkSessionOptions, SendLinkedMessageOptions, SendLinkedMessageResult, SendMessageOptions, CreateConversationOptions, UpdateWorkSessionConversationOptions, } from '@canonmsg/core';
8
8
  export type { SDKMessage, SDKConversation, CanonAgentOptions, MessageHandler, MessageHandlerContext, ProgressMessageOptions, ProgressMessageResult, 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 } from '@canonmsg/core';
3
3
  export { SessionManager } from './session-manager.js';
4
- export { getMessageAttachments, inferUploadMimeType, materializeAttachment, materializeMessageMedia, uploadMediaFile, } from './media.js';
4
+ export { getCodexImagePath, getMessageAttachments, inferUploadMimeType, isAnthropicImageAttachment, materializeAttachment, materializeMessageMedia, resolveAttachmentMimeType, toAnthropicImageBlock, uploadMediaFile, } from './media.js';
package/dist/media.d.ts CHANGED
@@ -20,6 +20,25 @@ export interface MaterializedCanonAttachment extends MediaAttachment {
20
20
  conversationId: string;
21
21
  messageId: string;
22
22
  }
23
+ /**
24
+ * Anthropic `image` content blocks only accept these MIME types for
25
+ * base64 sources. Anything outside this set must either be re-encoded or
26
+ * degraded to a text reference.
27
+ */
28
+ export type AnthropicImageMimeType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
29
+ /**
30
+ * Structural shape of an Anthropic `ImageBlockParam` (base64 source). We
31
+ * do not import from `@anthropic-ai/sdk` here because the agent-sdk must
32
+ * stay dependency-free; the Claude Code plugin re-casts to the SDK type.
33
+ */
34
+ export interface AnthropicImageBlock {
35
+ type: 'image';
36
+ source: {
37
+ type: 'base64';
38
+ media_type: AnthropicImageMimeType;
39
+ data: string;
40
+ };
41
+ }
23
42
  export declare function getMessageAttachments(message: Pick<CanonMessage, 'attachments' | 'imageUrl' | 'audioUrl' | 'audioDurationMs'>): MediaAttachment[];
24
43
  export declare function materializeAttachment(attachment: MediaAttachment, options: MaterializeMediaOptions & {
25
44
  index?: number;
@@ -30,3 +49,26 @@ export declare function uploadMediaFile(client: CanonClient, conversationId: str
30
49
  url: string;
31
50
  attachment: MediaAttachment;
32
51
  }>;
52
+ /**
53
+ * Resolve the effective MIME type of a materialized attachment, falling back
54
+ * to filename/URL extensions when the server didn't tell us explicitly.
55
+ */
56
+ export declare function resolveAttachmentMimeType(attachment: MaterializedCanonAttachment): string | null;
57
+ /**
58
+ * True when a materialized attachment can be sent to Anthropic as a base64
59
+ * `image` block (jpeg, png, gif, webp). Other images (heic, tiff, svg, …)
60
+ * must be re-encoded before they can be delivered as a vision block.
61
+ */
62
+ export declare function isAnthropicImageAttachment(attachment: MaterializedCanonAttachment): boolean;
63
+ /**
64
+ * Read the materialized bytes and build an Anthropic `image` content block.
65
+ * Callers should first check `isAnthropicImageAttachment`; this function
66
+ * throws if the MIME type is not supported.
67
+ */
68
+ export declare function toAnthropicImageBlock(attachment: MaterializedCanonAttachment): Promise<AnthropicImageBlock>;
69
+ /**
70
+ * Return the local path to pass to Codex via its `-i/--image` flag when the
71
+ * attachment is an image Codex can consume. Codex accepts the same MIME types
72
+ * as Anthropic in practice (jpeg/png/gif/webp), so we reuse the allowlist.
73
+ */
74
+ export declare function getCodexImagePath(attachment: MaterializedCanonAttachment): string | null;
package/dist/media.js CHANGED
@@ -1,6 +1,12 @@
1
1
  import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
2
  import { basename, dirname, extname, join } from 'node:path';
3
3
  import { CANON_DIR, } from '@canonmsg/core';
4
+ const ANTHROPIC_IMAGE_MIME_TYPES = new Set([
5
+ 'image/jpeg',
6
+ 'image/png',
7
+ 'image/gif',
8
+ 'image/webp',
9
+ ]);
4
10
  const DEFAULT_MEDIA_CACHE_DIR = join(CANON_DIR, 'media-cache');
5
11
  const EXTENSION_BY_MIME = {
6
12
  'application/json': 'json',
@@ -164,3 +170,63 @@ export async function uploadMediaFile(client, conversationId, filePath, options)
164
170
  const fileName = options?.fileName ?? basename(filePath);
165
171
  return client.uploadMedia(conversationId, buffer.toString('base64'), mimeType, fileName);
166
172
  }
173
+ /**
174
+ * Resolve the effective MIME type of a materialized attachment, falling back
175
+ * to filename/URL extensions when the server didn't tell us explicitly.
176
+ */
177
+ export function resolveAttachmentMimeType(attachment) {
178
+ if (attachment.mimeType)
179
+ return attachment.mimeType.toLowerCase();
180
+ const pathExt = extname(attachment.path).toLowerCase();
181
+ const fromPath = MIME_BY_EXTENSION[pathExt];
182
+ if (fromPath)
183
+ return fromPath;
184
+ const fileExt = extname(attachment.fileName ?? '').toLowerCase();
185
+ const fromFileName = MIME_BY_EXTENSION[fileExt];
186
+ if (fromFileName)
187
+ return fromFileName;
188
+ const urlExt = extname(attachment.sourceUrl.split('?')[0] ?? '').toLowerCase();
189
+ const fromUrl = MIME_BY_EXTENSION[urlExt];
190
+ if (fromUrl)
191
+ return fromUrl;
192
+ return null;
193
+ }
194
+ /**
195
+ * True when a materialized attachment can be sent to Anthropic as a base64
196
+ * `image` block (jpeg, png, gif, webp). Other images (heic, tiff, svg, …)
197
+ * must be re-encoded before they can be delivered as a vision block.
198
+ */
199
+ export function isAnthropicImageAttachment(attachment) {
200
+ if (attachment.kind !== 'image')
201
+ return false;
202
+ const mime = resolveAttachmentMimeType(attachment);
203
+ return mime != null && ANTHROPIC_IMAGE_MIME_TYPES.has(mime);
204
+ }
205
+ /**
206
+ * Read the materialized bytes and build an Anthropic `image` content block.
207
+ * Callers should first check `isAnthropicImageAttachment`; this function
208
+ * throws if the MIME type is not supported.
209
+ */
210
+ export async function toAnthropicImageBlock(attachment) {
211
+ if (!isAnthropicImageAttachment(attachment)) {
212
+ throw new Error(`Canon attachment ${attachment.index} is not a supported Anthropic image (kind=${attachment.kind}, mime=${attachment.mimeType ?? 'unknown'})`);
213
+ }
214
+ const mediaType = resolveAttachmentMimeType(attachment);
215
+ const buffer = await readFile(attachment.path);
216
+ return {
217
+ type: 'image',
218
+ source: {
219
+ type: 'base64',
220
+ media_type: mediaType,
221
+ data: buffer.toString('base64'),
222
+ },
223
+ };
224
+ }
225
+ /**
226
+ * Return the local path to pass to Codex via its `-i/--image` flag when the
227
+ * attachment is an image Codex can consume. Codex accepts the same MIME types
228
+ * as Anthropic in practice (jpeg/png/gif/webp), so we reuse the allowlist.
229
+ */
230
+ export function getCodexImagePath(attachment) {
231
+ return isAnthropicImageAttachment(attachment) ? attachment.path : null;
232
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.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.6.0"
31
+ "@canonmsg/core": "^0.7.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"