Add web multimodal image attachments
This commit is contained in:
116 files changed
+3177
-151
No files matched your search
@@ -0,0 +1,70 @@
|
||||
# Durable Image Attachments
|
||||
|
||||
The attachment seam separates binary image ownership from the session log. A producer gives validated encoded bytes to [`ctx.attachments`](../cordis-catalog/services.md#ctxattachments); the service publishes an immutable content-addressed reference only after the object is durable. Session events and model-visible `ImageBlock`s contain that reference and metadata, never a browser object URL, host temporary path, provider URL, or base64 payload.
|
||||
|
||||
Unsent browser drafts may stay in memory and native clients may stage them in operating-system temporary storage. Once the host accepts a user message, its images move below `<DSH_HOME>/attachments/v1` before the user event is appended. Structured model image output follows the same persist-before-event rule.
|
||||
|
||||
Source: [`packages/attachment/attachment/src/types.ts`](../../packages/attachment/attachment/src/types.ts)
|
||||
|
||||
## Identity and verified metadata
|
||||
|
||||
`AttachmentId` is a branded opaque string. The local backend currently emits `sha256:<digest>`, but consumers must neither parse that representation nor derive a filesystem path from it.
|
||||
|
||||
```ts type-equiv
|
||||
/** Raster image formats accepted by the version-one attachment path. */
|
||||
type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Durable, serializable metadata for one immutable image object. */
|
||||
interface ImageAttachmentRef {
|
||||
/** Opaque storage identifier; never a filesystem path or bearer URL. */
|
||||
attachmentId: AttachmentId
|
||||
/** Media type verified from the stored bytes. */
|
||||
mediaType: ImageMediaType
|
||||
/** Exact encoded byte length. */
|
||||
bytes: number
|
||||
/** Intrinsic encoded width in pixels. */
|
||||
width: number
|
||||
/** Intrinsic encoded height in pixels. */
|
||||
height: number
|
||||
/** Optional display name stripped of local path information. */
|
||||
name?: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Deployment-resolved limits shared by upload consumers and UI preflight. */
|
||||
interface ImageAttachmentLimits {
|
||||
maxImageBytes: number
|
||||
maxImagesPerMessage: number
|
||||
maxMessageImageBytes: number
|
||||
maxImagePixels: number
|
||||
mediaTypes: readonly ImageMediaType[]
|
||||
}
|
||||
```
|
||||
|
||||
The reference records intrinsic dimensions and encoded length so clients can lay out history without decoding first, while every authoritative read still re-checks digest, media signature, dimensions, and metadata against the object.
|
||||
|
||||
## Commit and verified-read payloads
|
||||
|
||||
```ts type-equiv
|
||||
/** Request to validate and durably commit one image. */
|
||||
interface SaveImageAttachment {
|
||||
data: Uint8Array
|
||||
/** Caller-declared media type, checked against magic bytes. */
|
||||
mediaType: ImageMediaType
|
||||
/** Optional browser/provider display name; it is never interpreted as a path. */
|
||||
name?: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Stored image bytes returned after reference and digest verification. */
|
||||
interface StoredImageAttachment {
|
||||
ref: ImageAttachmentRef
|
||||
data: Uint8Array
|
||||
}
|
||||
```
|
||||
|
||||
`saveImage()` validates bytes and atomically commits one object before returning its reference. `readImage()` accepts a reference from an authorized session path and returns bytes only after integrity verification. The service is deliberately retention-neutral: resumed and forked sessions may share objects, so reference-aware garbage collection is deferred rather than tied to any one session's deletion.
|
||||
@@ -28,6 +28,7 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t
|
||||
| [tools.md](tools.md) | `ToolDefinition` full fields, the schema DSL, `ToolExecution`/`ToolResult`, tool-presentation UI types, and the guarded execution pipeline |
|
||||
| [user-interaction.md](user-interaction.md) | the UI-backed human question/answer seam: `AskUserQuestionRequest`, answer/options vocabulary, provider API, error taxonomy |
|
||||
| [approval.md](approval.md) | the one-shot user-approval seam: `ApprovalRequest`, `ApprovalOutcome`, per-session policy, audit and answerer contracts |
|
||||
| [attachment.md](attachment.md) | durable image identity and metadata, validation inputs, verified reads, and the `AttachmentStore` seam |
|
||||
| [bash.md](bash.md) | the bash executor seam: `BashExecRequest`/`Spec`, `BashRunResult`, background `BashProcess` handles |
|
||||
| [pty.md](pty.md) | persistent terminal ids, backend/session contracts, send readiness, bounded reads, and owner-visible snapshots |
|
||||
| [sandbox.md](sandbox.md) | per-session policy resolution and the process-confinement seam: file-effect modes, execution/provider policies, `ConfinedArgv`, enforcement and fail-closed errors |
|
||||
@@ -106,12 +107,13 @@ Source: [`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts)
|
||||
interface ContentBlockMap {
|
||||
'text': TextBlock
|
||||
'reasoning': ReasoningBlock
|
||||
'image': ImageBlock
|
||||
'tool-call': ToolCallBlock
|
||||
'tool-result': ToolResultBlock
|
||||
}
|
||||
```
|
||||
|
||||
The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ToolCallBlock` (`id: CallId`, `name`, raw-JSON `arguments`), `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. The core set is limited to blocks every shipping path honors — multimodal content (images, audio, …) has no core block type; a feature that needs one adds it via the merge-extensible map together with the adapter/UI/compaction support that honors it.
|
||||
The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md) plus optional alternative text), `ToolCallBlock` (`id: CallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
|
||||
|
||||
A `Message` is a role plus blocks. Loop-derived assistant messages carry their durable provider/model identity and optional adapter-private replay metadata:
|
||||
|
||||
@@ -192,6 +194,10 @@ interface LlmModelInfo {
|
||||
name: string
|
||||
/** Optional user-facing distinction from otherwise similar models. */
|
||||
description?: string
|
||||
/** Accepted request modalities; absent means unknown, while an explicit omission is negative capability. */
|
||||
inputModalities?: readonly ModelModality[]
|
||||
/** Structured response modalities; absent means unknown, while an explicit omission is negative capability. */
|
||||
outputModalities?: readonly ModelModality[]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ declare abstract class LlmAdapter {
|
||||
interface ContentBlockMap {
|
||||
'text': TextBlock
|
||||
'reasoning': ReasoningBlock
|
||||
'image': ImageBlock
|
||||
'tool-call': ToolCallBlock
|
||||
'tool-result': ToolResultBlock
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user