Quick Reference
For per-provider setup, model defaults, and configuration, see the Media & Voice section.
Tool Details
image_analyze -- Analyze images with AI vision
image_analyze -- Analyze images with AI vision
Use The agent calls
image_analyze to have your agent look at an image and describe what it sees, answer questions about the content, or extract information like text in a screenshot.The tool supports multiple ways to provide an image:Source types explained:
- file — A path to an image file on the server (e.g.,
/tmp/screenshot.png) - url — A publicly accessible image URL
- base64 — Raw image data encoded as a base64 string
attachment_url parameter instead of source_type/source. The attachment_url is provided in the message hint and resolves platform-specific attachment URLs automatically.Example usage:
When a user sends a photo in a chat, the agent can analyze it:image_analyze with attachment_url from the message hint, then responds with a description of the image content.Vision follows the agent’s main provider. When the agent’s main model is itself vision-capable (Claude, GPT-4o, Gemini, and other models that accept image input), image_analyze routes the image through that main model, reusing the agent’s existing credentials — so no separate vision key is required in that case. The model that reasons for the agent is the model that “sees” the image, over one completion call.The resolver tries the tiers in this locked order and logs which tier served (the path field on the completion log):- main-vision — the agent’s main model, when it accepts image input (reuses the main provider’s key / Codex OAuth profile). No separate vision key needed.
- registry — the independent vision registry’s own OpenAI -> Anthropic -> Google chain (its own configured keys; unchanged for current setups). Used when the main model is not vision-capable, when an explicit
defaultProvideris set (see below), or as the fallback if a main-vision attempt fails at runtime. - honest “unavailable” — when no tier can serve, the tool reports an unavailable result with an error kind and a hint naming the knob to set, never a crash or a misroute to the wrong provider.
integrations.media.vision.defaultProvider in config.yaml overrides main-first (explicit operator config wins) — the registry tier serves that provider directly.tts_synthesize -- Generate spoken audio from text
tts_synthesize -- Generate spoken audio from text
Use
tts_synthesize to convert text into spoken audio. The tool returns a workspace file path (under media/tts/) along with the MIME type and size. The agent must then send that file via the message tool’s attach action to deliver it to a chat — TTS itself does not auto-deliver.The TTS provider (OpenAI, ElevenLabs, or Edge TTS) is determined by your
config.yaml — there is no per-call provider parameter.Supported providers (configured in config.yaml):- OpenAI — High-quality voices with natural intonation. Requires an OpenAI API key.
- ElevenLabs — Wide selection of realistic voices with emotion control. Requires an ElevenLabs API key.
- Edge TTS — Free text-to-speech using Microsoft Edge’s built-in voices. No API key required.
{ filePath, mimeType, sizeBytes }.Example two-step usage:tts_synthesizewith the summary text -> obtainfilePath.messageactionattachwithattachment_url: file://{filePath},attachment_type: audioto deliver the audio file.
transcribe_audio -- Convert speech to text
transcribe_audio -- Convert speech to text
Use
transcribe_audio to convert an audio or voice message into written text. This is useful for processing voice messages that users send in chat.Supported providers:
- OpenAI Whisper — High accuracy across many languages
- Groq — Fast transcription with Whisper models
- Deepgram — Real-time transcription with speaker detection
describe_video -- Describe video content
describe_video -- Describe video content
Use The agent watches the video and responds with a description like: “The video shows a step-by-step tutorial for configuring a Discord bot, starting with the developer portal and ending with the bot joining a server.”
describe_video to have your agent watch a video and describe what happens in it. The tool sends the raw video to a Gemini vision model (the only provider that accepts raw video natively in this release — there is no frame extraction); see the routing note below.The tool sends the raw video to a video-capable vision model and returns a coherent description of the content. This is useful for understanding short clips, tutorials, or screen recordings shared in chat.How it works:
- The video is downloaded from the provided URL.
- The raw video buffer is sent to a video-capable vision provider for analysis.
- The provider returns a description of the video content.
image_analyze, describe_video does not route through an arbitrary main model — only Gemini accepts the raw video buffer (there is no frame extraction in this release). So describe_video goes straight to the gemini-video tier (via the vision registry’s Google provider); if no video-capable provider is configured, the tool reports an honest “unavailable” result with a hint to configure Gemini, never a crash. A non-Gemini main model does not change this — the main-provider vision path applies to image_analyze, not raw video.Example usage:
When a user shares a video clip, the agent can describe what is happening:extract_document -- Extract text from documents
extract_document -- Extract text from documents
Use The agent extracts the text from the PDF, then uses that text to generate a summary. For spreadsheets (CSV), the data is returned in a structured format that the agent can analyze, filter, or summarize.
extract_document to pull readable text from documents. This is useful for processing PDFs, spreadsheets, and other files that users share in chat.Supported formats:
- PDF — Extracts text from PDF documents
- CSV — Reads comma-separated data
- TXT — Plain text files
- Other text-based formats
Document extraction works best with text-based PDFs. Scanned documents (images of text) may require the
image_analyze tool instead, since the content is stored as images rather than selectable text.image_generate -- Generate images from text prompts
image_generate -- Generate images from text prompts
Use
image_generate to create images from text descriptions. The generated image is delivered directly to the current channel via the daemon.By default (integrations.media.imageGeneration.provider: auto) image generation follows the agent’s main provider and reuses its credentials — no image-specific key is needed when the main provider can generate images. If the agent’s main provider cannot generate images (e.g. Anthropic) and no explicit provider is set, the tool returns an honest “unavailable” with a hint to set integrations.media.imageGeneration.provider — it never silently routes to a different paid provider. To opt in to OpenRouter, set provider: openrouter and supply OPENROUTER_API_KEY.Codex (ChatGPT-login) agents generate images with no API key. When the agent’s main provider is openai-codex (provider: auto), or you set provider: openai-codex explicitly, image_generate reuses the agent’s existing ChatGPT/Codex OAuth credentials — there is no image-specific API key to set. The OAuth bearer is resolved per call, so an expired token refreshes automatically; if the agent is not logged in, the tool returns an honest “unavailable” with a comis auth login --provider openai-codex hint (it never silently routes elsewhere).Native OpenAI and Google (key-auth) follow-main paths are wired. A key-auth openai main (with OPENAI_API_KEY) generates via the OpenAI Images API (default gpt-image-1), and a google main (with GOOGLE_API_KEY) generates via Gemini (default gemini-2.5-flash-image) — both under provider: auto, reusing the same key the main provider uses (no image-specific key). You can also pin them explicitly with provider: openai / provider: google.A
reference_image is resolved safely: a workspace file path is confined to the agent’s workspace (path-traversal blocked), a URL passes an SSRF guard before any fetch (and is size-capped), and a data-URI/base64 is decoded in place.The daemon-side handler applies rate limiting, safety checking, model validation, and provider execution before delivering the generated image to the current channel.Delivery, persistence, and degradation. A generated image is first persisted to the agent’s confined workspace under ~/.comis/workspace/media/photos/ (a UUID filename, size-capped) and then delivered from that durable path — never an ephemeral OS temp file. Delivery is capability-driven, not a hardcoded channel list: a channel whose adapter implements attachment sending receives the persisted file directly; a channel without it (currently only IRC) degrades to a bounded base64 result returned over RPC. The same base64 fallback is used when the request has no originating channel, or if persistence or channel delivery fails — so a generation is never lost, it is just delivered by the best means the channel supports.Cost ceiling. Beyond the maxPerHour count limit, an optional per-agent USD ceiling, integrations.media.imageGeneration.maxCostPerHourUsd, bounds spend: once an agent’s accumulated image-generation cost in the current hour reaches the ceiling, further requests are blocked with errorKind: quota_exceeded (and a hint naming the key) before the provider is called. Unset means no cost ceiling (the count limit still applies). See config-yaml.mdx.Observability. Each generation logs a single INFO completion line carrying the executing image provider, the agent’s main provider, the model, durationMs, costUsd, sizeBytes, and the mime type; every failure branch logs an errorKind + an actionable hint (never the raw provider message, a key, or a bearer). The image turn is reconstructable from observability: comis explain <sessionKey> surfaces the image block (provider / model / costUsd / outcome / delivered) from the session trajectory, and the cost also flows into the per-session cost rollups (comis fleet).When following a Codex main provider, Comis identifies itself to the ChatGPT backend as the official Codex client — it sends the same first-party client headers (
originator: codex_cli_rs, a Codex-shaped User-Agent, a ChatGPT-Account-ID decoded from your token, and per-request session-id/x-client-request-id identifiers) that the official Codex CLI sends. This is a first-party-client compatibility shim using your own ChatGPT/Codex OAuth credentials against the same endpoint the official Codex CLI uses — so Cloudflare does not 403 requests from non-residential IPs. It is authorized reuse of your own credentials, not detection evasion. If a future change to the ChatGPT backend rejects this image path, set integrations.media.imageGeneration.provider: openrouter (with OPENROUTER_API_KEY) as the explicit fallback.video_generate -- Generate videos from text prompts
video_generate -- Generate videos from text prompts
Use Cost ceiling (estimate-first). Video is dollars-per-clip and is already rendering once submitted, so unlike image generation the per-agent USD ceiling,
video_generate to create a short video clip from a text prompt. Every backend — native Google Veo, xAI Grok Imagine, and the FAL queue API — runs through one unified submit -> poll -> download contract.Async by default (the render outlives the turn). A video render takes 30 s-5 min — longer than a single agent turn and longer than the promptTimeout. So video_generate does not block: it submits the job, persists a durable job record, and returns a job handle ({ jobId, state: "submitted", estimatedCostUsd }) promptly. A background poller then drives the render to completion independent of the originating turn and across a daemon restart, downloads the clip before the (expiring) provider URL dies, persists it, and announces the finished video to the originating channel on its own. Use the video_status tool with the returned jobId to check progress in the meantime. Delivery is at-least-once: a clip is delivered once it completes; if the daemon restarts mid-render it resumes from the persisted job and still delivers — so a rare, bounded duplicate is possible, but a render is never lost and never redelivered indefinitely.Provider following (integrations.media.videoGeneration.provider: auto, the default). Video generation follows the agent’s main provider and reuses its credentials, mirroring image generation: a google main resolves to Google Veo and an xai main resolves to xAI Grok Imagine, each with the key the agent already uses (GOOGLE_API_KEY / XAI_API_KEY) — no video-specific key. Where the main provider has no video API (e.g. openai, anthropic), the tool returns an honest “unavailable” with a hint to set integrations.media.videoGeneration.provider (e.g. fal + FAL_KEY) — it never silently routes to a different paid provider.Under
provider: auto (the default), a google main generates via Google Veo (default veo-3.0-fast-generate-001, GA — generates audio by default) reusing GOOGLE_API_KEY, and an xai main generates via xAI Grok Imagine (grok-imagine-video, 480p/720p) reusing XAI_API_KEY (or a SuperGrok login) — no video-specific key. Where the main provider has no video API, the tool still returns an honest “unavailable” naming the opt-in path. The native-Veo path also has a FAL-hosted fallback: set provider: fal + model: fal-ai/veo3.1 to render Veo through the FAL queue instead (it needs a second credential, FAL_KEY, so it is not the follow-main default).Per-backend options (validated against the active backend). The clip-shape params are validated against the active backend’s capability matrix before the render is submitted: an unsupported value (a resolution the backend cannot produce, a duration outside its range, or
image_url on a text-to-video-only model) is rejected up front with a hint listing the valid set for the current backend — it is never silently routed to a wrong value or surfaced as a provider error. The concrete per-backend options today are:Because the active backend follows the agent’s main provider (or
integrations.media.videoGeneration.provider), the video_generate tool description the agent sees is built at daemon startup from the active backend’s real options — so the agent is told the active provider’s durations/resolutions/aspect ratios and whether image-to-video is supported up front, rather than a static superset. Changing provider therefore needs a daemon restart for the description to refresh (the same boot-bound contract as provider/credential selection).Image-to-video currently takes a single source image (
image_url). Supplying multiple reference images (e.g. distinct first-frame / last-frame / style-reference roles) is a planned follow-up and is not yet available.integrations.media.videoGeneration.maxCostPerHourUsd, is gated on a worst-case pre-submit estimate (duration x per-second rate, with audio/4k as the upper bound): if the estimate would push the agent’s accumulated hourly spend over the ceiling, the request is blocked with errorKind: quota_exceeded (and a hint naming the key) before the provider is called. The maxPerHour count limit is retained and orthogonal. Unset means no cost ceiling.Delivery, persistence, and degradation (in the background poller). When the render completes, the poller downloads the clip to a buffer and persists it to the agent’s confined workspace under ~/.comis/workspace/media/videos/ (a UUID filename, size-capped) before any delivery decision — an expiring provider URL cannot orphan it. Delivery is capability-driven: a channel whose adapter implements attachment sending receives the persisted file directly (as a video attachment); a channel without it (currently only IRC) degrades to a notice + the persisted path. Because delivery happens off-turn from the persisted job, the finished clip reaches the channel that requested it even after the turn has ended (and after a daemon restart). The persisted file is not deleted after delivery, so generated clips survive for later inspection.Oversized-clip degrade (never silently dropped). A 4K/long clip can exceed a channel’s video-upload limit. Rather than drop it, the poller checks the persisted clip’s size against a per-channel limit before sending and degrades gracefully — it is never routed through the silent “attachment too large -> fallback text” path. On a channel that renders URLs (Discord, Slack, Telegram, LINE, WhatsApp, Signal, iMessage, Email) the poller sends a link message to the retained provider URL when one is available; otherwise (or on a notice-only channel) it sends a notice message naming the saved workspace path. Either way the clip stays persisted and recoverable, and the message always carries the saved path. The per-channel limit follows each platform’s documented bot/upload cap (e.g. WhatsApp ~16 MB, Discord ~25 MB, Telegram ~50 MB, Slack/Signal/iMessage ~100 MB, LINE ~200 MB) and is overridable. IRC, which has no attachment surface at all, degrades to a notice via the same capability gate.Observability. When the render completes the poller logs a single INFO completion line carrying the executing video provider, the model, costUsd, sizeBytes, durationMs, and the mime type; every failure branch (submit failure, a failed render, or a poll timeout) logs an errorKind + an actionable hint (never the raw provider message, a key, a bearer, or the Veo keyed download URL — a raw provider/channel error message is scrubbed before it rides a log line). For a provider-native render (Veo/Grok) the poller preserves the specific classified failure kind off-turn — a safety-policy block is logged (and persisted to the job’s error) as content_blocked, a quota/credits limit as quota_exceeded, and so on — rather than collapsing every terminal failure to a generic empty result, so a content-policy block is distinguishable from a transient empty render in the logs alone. Because the completion runs off-turn, the poller stamps the originating traceId (captured at submit) on its log lines so the later completion stitches back to the request.comis explain reconstruction (incl. background completion). A video turn is reconstructable from observability: comis explain <sessionKey> surfaces a videoGenerated block (provider, model, jobId, estimatedCostUsd, the reconciled costUsd, outcome, delivered) from the session trajectory — including a job that completed in the background after the originating turn ended: the submit record (written in-turn) and the off-turn completion record are stitched by the carried jobId/traceId on the originating session_key (the column added to the video_jobs store for exactly this). The reconciled cost also flows into the per-session cost rollups (comis fleet). Cost provenance is per-backend: FAL and Veo report no per-call actual cost, so the reconstructed cost is the pre-submit estimate; xAI Grok reports an actual cost (from its usage ticks) that is reconciled in. (A live comis explain against a real background-completed render is an operator check; the deterministic reconstruction path is the binding contract.)video_status -- Check a video render's progress and result
video_status -- Check a video render's progress and result
Use
video_status to check the status of a video-generation job by the jobId that video_generate returned. Because a render outlives the turn (and survives a daemon restart), video_status is how the agent polls progress while the background poller drives the job to completion — the finished clip is then delivered to the channel automatically (no video_status call is required for delivery; it is for visibility).The response reports the job’s durable state:
Agent-scoped.
video_status is scoped to the calling agent: a jobId that belongs to a different agent returns a not-found result (state: "failed"), never another agent’s media path or cost. In a multi-agent daemon an agent can only see its own jobs.Common Workflows
Here are some typical ways agents use media tools together:- Voice message handling — When a user sends a voice note, the agent uses
transcribe_audioto convert it to text, processes the request, and optionally usestts_synthesizeto reply with audio. - Document Q&A — A user shares a PDF report. The agent uses
extract_documentto read it, then answers questions about the content. - Image moderation — In a group chat, the agent uses
image_analyzeto check shared images for content that violates community guidelines. - Accessibility — The agent uses
describe_videoandimage_analyzeto provide text descriptions of visual content for users who need them.
Provider Configuration
Media tools route to providers based on the credentials present in your secret store and the providers listed inconfig.yaml. None of the tools accept a provider parameter at call time — selection is config-driven, not per-call.
Cost notes
Media calls bill against your model providers, not Comis itself. Rough rules of thumb:- Vision is significantly more expensive per call than text generation — a single high-res image can cost 1-5K tokens of input. Use
promptto focus the analysis and avoid re-analyzing the same image across turns. - STT is generally cheap (Groq’s whisper-large-v3-turbo is the lowest-latency / lowest-cost option in the fallback chain).
- TTS is priced per character. ElevenLabs is the priciest tier; Edge TTS is free but voice quality is more limited.
- Image generation has the highest per-call cost. The image-gen rate limiter is conservative by default (~1 req/min on OpenAI, ~1 req/sec on FAL), and you can additionally cap spend per agent/hour with
integrations.media.imageGeneration.maxCostPerHourUsd(requests over the ceiling are blocked withquota_exceeded). Each generation’scostUsdis recorded in the session trajectory (comis explain) and the cost rollups (comis fleet).
image_analyze, vision follows the agent’s main provider first (reusing its key when the main model is vision-capable — no separate vision key needed), then the independent vision registry, then an honest “unavailable” result; an explicit integrations.media.vision.defaultProvider overrides main-first.
See the Media & Voice section for detailed provider setup, the Vision page for image flow details, and Voice for TTS/STT options.
All media tools process content on your server. Files are not sent to third parties beyond the configured AI provider for analysis.
Enabling Media Tools
Media tools are enabled by default in thefull tool policy profile. If you are using a restricted profile (like minimal or coding), you can enable specific media tools by adding them to your agent’s allow list:
Related
Media & Voice
Detailed media provider setup and configuration
Vision
How AI vision processing works
Voice
Text-to-speech and transcription configuration
Agent Tools Overview
See all available agent tools
