@ai-matrx/agents 0.32.0 → 0.33.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../models/index.ts","../../models/capabilities.ts","../../models/model-normalizer.ts"],"sourcesContent":["/**\n * `@ai-matrx/agents/models` — AI model data types: the capability contract and the\n * model-row normalizer. Pure. Moved from matrx-frontend `features/ai-models/`\n * (chat package independence P15). The model registry store is not here.\n */\nexport * from \"./capabilities\";\nexport * from \"./model-normalizer\";\n","// features/ai-models/capabilities/types.ts\n//\n// Canonical shape for `ai.model_definition.capabilities`. Source of truth for\n// what a model accepts as input, produces as output, and supports as a feature.\n//\n// Before May 2026 this column was a zoo — null / empty string / flat\n// string arrays / Google-style boolean object / OpenAI-style I/O object /\n// literal `\"[transcription]\"`. The backfill landed and every row is now\n// canonical, so `parseCapabilities` validates rather than infers, and the\n// dead `api_class` sniffing fallback is gone. Route all reads through it.\n//\n// Why `{input, output, features, interaction}` instead of a flat boolean\n// map: content-type acceptance is a SET, not 20 bools. `input: [\"text\",\n// \"image\"]` reads instantly as \"this model takes text and images.\" A\n// flat `text_input: true, image_input: true, audio_input: false, ...`\n// hides the shape behind ceremony. Features stay as flags because they\n// genuinely are boolean. `interaction` is its own field because it gates\n// runtime selection (turn-based vs realtime) at launch time.\n//\n// The audit system's flat `CapabilitiesRecord` is now a DERIVED\n// projection (`toAuditRecord`) — existing consumers see the same view.\n\n// `entities` and `embedding` are OUTPUT-only modalities in practice\n// (extraction models emit structured entities; vector models emit embeddings),\n// while `document` appears on inputs. All are members of the one shared set\n// because input/output share the vocabulary.\nexport const CONTENT_TYPES = [\n \"text\",\n \"image\",\n \"audio\",\n \"video\",\n \"document\",\n \"entities\",\n \"embedding\",\n \"decision\",\n] as const;\nexport type ContentType = (typeof CONTENT_TYPES)[number];\n\n// Full live vocabulary (verified against ai.model_definition 2026-08-16:\n// turn, single, extraction, realtime, embedding, agent).\n// turn — ordinary multi-turn chat request/response.\n// single — one-shot generation (image/video gen); no conversation.\n// extraction — NER/classification models (GLiNER2 family); NOT chat models.\n// realtime — live voice/audio transport.\n// embedding — vectorization models; NOT chat models.\n// agent — provider-managed background agent; uses its dedicated API.\n// decision — typed state/question evaluation; never a conversational turn.\nexport const INTERACTION_MODES = [\n \"turn\",\n \"single\",\n \"extraction\",\n \"realtime\",\n \"embedding\",\n \"agent\",\n \"decision\",\n] as const;\nexport type InteractionMode = (typeof INTERACTION_MODES)[number];\n\n// The complete feature vocabulary present on live `ai.model_definition` rows\n// (swept 2026-09-12). Adding a NEW feature value to the DB requires adding it\n// here — `parseCapabilities` screams (captureError \"data-shape\") on any value\n// it doesn't recognize instead of silently dropping it.\n//\n// THE OTHER HALF OF THIS LIST lives server-side in\n// `aidream/packages/matrx-ai/matrx_ai/providers/capability_vocabulary.py`,\n// which is what the write path (the agent `sql`/`db_*` tools and\n// `scripts/set_model_capabilities.py`) now validates against, and what\n// `scripts/check_model_capability_vocabulary.py` proves the live rows obey.\n// Keep the two lists in lockstep; a term added to one belongs in both.\n//\n// Why the scream is worth keeping even with a write guard: on 2026-09-12 this\n// parser was the ONLY thing on the platform that noticed the model-sync agent\n// had written provider-doc spellings (\"structured_outputs\", \"reasoning\",\n// \"code_interpreter\", \"batch\", \"pdf_input\"). Server-side, nothing matched\n// them and nothing said so — gpt-6-astra resolved to free-text structured\n// output with schema output declared on its row. Provider aliases are\n// normalized at the catalog write boundary — they must never become stored\n// feature keys here.\nexport const FEATURE_KEYS = [\n \"streaming\",\n \"function_calling\",\n \"tool_calling\",\n \"thinking\",\n \"structured_output\",\n \"json_mode\",\n \"web_search\",\n \"x_search\",\n \"vision\",\n \"code_execution\",\n \"computer_use\",\n \"multi_turn\",\n \"system_prompt\",\n \"embeddings\",\n \"dimension_reduction\",\n \"fine_tuning\",\n \"batch_api\",\n \"prompt_caching\",\n \"context_caching\",\n // Anthropic context editing / tool-result clearing: the model manages what\n // stays in its own context window. Distinct from the two caching features,\n // which are about billing and latency, not retention.\n \"context_management\",\n \"partial_mode\",\n // The model continues a trailing assistant message (response prefill).\n // Anthropic removed it on Sonnet 5 / Opus 5 / Fable / 4.6-4.8; older Claude\n // models keep it. Read by features/agents/message-flags (the Prefill flag).\n \"assistant_prefill\",\n // Extraction family (GLiNER2 / fastino models)\n \"ner\",\n \"classification\",\n \"structured_extraction\",\n \"relation_extraction\",\n \"pii_detection\",\n // Media generation / editing\n \"image_generation\",\n \"image_editing\",\n // Mask-based region editing. A model can edit a whole image without\n // accepting a mask, so this is not the same declaration as image_editing.\n \"inpainting\",\n \"image_to_video\",\n \"video_generation\",\n \"video_editing\",\n \"audio_generation\",\n \"conversational_editing\",\n \"stateful_editing\",\n \"dialogue\",\n // Provider-managed agents / hosted tools\n \"file_search\",\n \"mcp\",\n \"background_execution\",\n \"collaborative_planning\",\n \"visualization\",\n \"citations\",\n \"sandboxed_execution\",\n // Google live, grounding, embedding, and music families\n \"live_api\",\n \"url_context\",\n \"grounding_maps\",\n \"multimodal_embedding\",\n \"music_generation\",\n \"real_time_translation\",\n] as const;\nexport type FeatureKey = (typeof FEATURE_KEYS)[number];\n\nexport interface ModelCapabilities {\n input: ContentType[];\n output: ContentType[];\n features: FeatureKey[];\n interaction: InteractionMode;\n /** Model is explicitly multilingual (canonical `capabilities.multilingual`; absent → false). */\n multilingual: boolean;\n}\n\n/**\n * Provider-managed research agents (Google Deep Research) run as an agent's\n * model through the normal agent run path — a long background interaction\n * that streams its research and returns a cited report.\n */\nexport function isManagedAgentModelCapability(\n capabilities: Pick<ModelCapabilities, \"interaction\">,\n): boolean {\n return capabilities.interaction === \"agent\";\n}\n\n/** Decision models have a non-conversational wire contract. */\nexport function isDecisionModelCapability(\n capabilities: Pick<ModelCapabilities, \"interaction\">,\n): boolean {\n return capabilities.interaction === \"decision\";\n}\n\n/**\n * What a picker is allowed to select, by execution contract.\n *\n * chat — a conversational turn only (`/chat`, launchers, prompt runs).\n * agent — an AGENT's model. An agent is not necessarily a conversation:\n * its message may carry a `decision_questions` part and be\n * answered by a decision holder that emits no text at all, or it\n * may be a provider-managed research agent (Deep Research) that\n * runs for minutes and returns a cited report. So this purpose\n * admits all three contracts and the picker labels the rows for\n * what they are (Arman, 2026-09-26: \"An agent can be a text\n * model, video, image, speech, live, decision, and now a research\n * model. It makes no difference.\"). Ruled by\n * `common-docs/systems/agents/typed-messages/FEATURE.md`\n * (\"the picker shows only parts the selected model can consume\"\n * — the author chooses the model first, so the model list may\n * never be the thing that hides the decision modality).\n * decision — a decision holder only (the decision playground, a decision\n * knob): a conversational model would answer in prose.\n * admin — catalog editing; every contract stays selectable.\n *\n * Whether a model is listed in the catalog never changes the execution contract of a picker.\n */\nexport type ModelSelectionPurpose = \"chat\" | \"agent\" | \"decision\" | \"admin\";\n\nexport function modelsForSelectionPurpose<\n T extends Pick<ModelCapabilities, \"interaction\">,\n>(\n models: readonly T[],\n purpose: ModelSelectionPurpose = \"chat\",\n): T[] {\n return models.filter((model) => {\n if (purpose === \"admin\") return true;\n if (purpose === \"decision\") return isDecisionModelCapability(model);\n if (purpose === \"agent\")\n return (\n isConversationalModelCapability(model) ||\n isDecisionModelCapability(model) ||\n isManagedAgentModelCapability(model)\n );\n return isConversationalModelCapability(model);\n });\n}\n\n/**\n * An agent's or a chat turn's instructions are TEXT. A non-live model whose\n * declared input has no text (speech-to-text: Whisper, Scribe) cannot take them,\n * whatever its interaction says (2026-09-27: Whisper sat in the chat set). A live\n * (realtime) session is configured rather than instructed per turn, so it is exempt.\n * Mirrors aidream `agent_service/resources.py::_is_agent_assignable`.\n */\nexport function acceptsTextInstructions(\n capabilities: Pick<ModelCapabilities, \"interaction\"> & {\n input?: readonly string[];\n },\n): boolean {\n if (capabilities.interaction === \"realtime\") return true;\n const input = capabilities.input ?? [];\n return input.length === 0 || input.includes(\"text\");\n}\n\n/** Chat and agent launchers may select turn/single models that take text only. */\nexport function isConversationalModelCapability(\n capabilities: Pick<ModelCapabilities, \"interaction\"> & {\n input?: readonly string[];\n },\n): boolean {\n return (\n (capabilities.interaction === \"turn\" ||\n capabilities.interaction === \"single\") &&\n acceptsTextInstructions(capabilities)\n );\n}\n\n/** Fallback/replacement models must keep the same decision wire contract. */\nexport function hasCompatibleDecisionInteraction(\n source: Pick<ModelCapabilities, \"interaction\">,\n candidate: Pick<ModelCapabilities, \"interaction\">,\n): boolean {\n return (\n isDecisionModelCapability(source) === isDecisionModelCapability(candidate)\n );\n}\n\n/**\n * Safe fallback used by `parseCapabilities` when no signal is available.\n * A text-only turn-based model — the most common shape.\n */\nexport const DEFAULT_CAPABILITIES: ModelCapabilities = {\n input: [\"text\"],\n output: [\"text\"],\n features: [],\n interaction: \"turn\",\n multilingual: false,\n};\n\n/** True iff `value` is a member of the const tuple `arr`. Type-narrow helper. */\nexport function isContentType(value: unknown): value is ContentType {\n return (\n typeof value === \"string\" &&\n (CONTENT_TYPES as readonly string[]).includes(value)\n );\n}\n\nexport function isFeatureKey(value: unknown): value is FeatureKey {\n return (\n typeof value === \"string\" &&\n (FEATURE_KEYS as readonly string[]).includes(value)\n );\n}\n\nexport function isInteractionMode(value: unknown): value is InteractionMode {\n return (\n typeof value === \"string\" &&\n (INTERACTION_MODES as readonly string[]).includes(value)\n );\n}\n","/**\n * Normalizes legacy model control keys and prompt settings keys to their\n * current backend equivalents.\n *\n * This runs once at the Redux boundary (hydrate / fetch) so downstream code\n * never encounters deprecated field names.\n *\n * Legacy → Current mapping:\n * max_tokens → max_output_tokens\n * output_format → response_format ONLY when it carries text response\n * formats (text / json_object / json_schema); an image\n * model's output_format (png / jpeg / webp) is the file\n * format — its own LLMParams field — and is never renamed.\n * n → count\n */\n\nconst LEGACY_KEY_MAP: Record<string, string> = {\n max_tokens: \"max_output_tokens\",\n n: \"count\",\n};\n\n/** The text response formats a legacy `output_format` carries. */\nconst TEXT_RESPONSE_FORMATS = new Set([\"text\", \"json_object\", \"json_schema\"]);\n\nfunction flattenFormat(option: unknown): string {\n return option !== null &&\n typeof option === \"object\" &&\n \"type\" in (option as Record<string, unknown>)\n ? String((option as Record<string, unknown>).type)\n : String(option);\n}\n\n/**\n * THE output_format rule — every parser of model controls and settings uses it.\n * `output_format` is ambiguous in the catalog: on a legacy text model it IS the\n * response format (text / json_object / json_schema) and reads as\n * `response_format`; on an image model (GPT Image 2: png / jpeg / webp) it is\n * the image FILE format, a real LLMParams field of its own. Renaming the image\n * one showed GPT Image 2 a \"Response Format\" row whose choices stored nothing,\n * and made a stored `output_format` read as an unknown key. A control with no\n * enum keeps the legacy (text) reading.\n */\nexport function outputFormatControlKey(key: string, control: unknown): string {\n if (key !== \"output_format\") return key;\n const options =\n control !== null && typeof control === \"object\" && \"enum\" in control\n ? (control as { enum?: unknown }).enum\n : undefined;\n if (!Array.isArray(options) || options.length === 0) return \"response_format\";\n return options.every((o) => TEXT_RESPONSE_FORMATS.has(flattenFormat(o)))\n ? \"response_format\"\n : \"output_format\";\n}\n\nfunction normalizeResponseFormat(value: unknown): unknown {\n if (typeof value === \"string\") {\n if (value === \"text\" || value === \"\") return undefined;\n return { type: value };\n }\n return value;\n}\n\n/**\n * Normalize a single model's `controls` JSONB blob in-place.\n *\n * For each top-level key in controls:\n * - Renames legacy keys (max_tokens, output_format, n)\n * - Converts output_format string defaults to { type: string } dicts\n *\n * If the target key already exists, the legacy key is silently dropped\n * (the modern key wins).\n */\nfunction normalizeControls(\n controls: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(controls)) {\n if (key === \"output_format\") {\n if (outputFormatControlKey(key, value) === \"output_format\") {\n result[key] = value;\n continue;\n }\n if (!(\"response_format\" in controls)) {\n const controlDef = value as Record<string, unknown> | null;\n if (controlDef && typeof controlDef === \"object\") {\n const normalized = { ...controlDef };\n if (normalized.default !== undefined) {\n const converted = normalizeResponseFormat(normalized.default);\n if (converted === undefined) {\n delete normalized.default;\n } else {\n normalized.default = converted;\n }\n }\n result[\"response_format\"] = normalized;\n }\n }\n continue;\n }\n\n const mappedKey = LEGACY_KEY_MAP[key];\n if (mappedKey) {\n if (!(mappedKey in controls)) {\n result[mappedKey] = value;\n }\n continue;\n }\n\n result[key] = value;\n }\n\n return result;\n}\n\ninterface HasControls {\n // Json from database.types.ts is a union that includes Record<string, Json>.\n // We widen to unknown here so the constraint accepts both hand-rolled types\n // and the generated DB Row type without requiring a cast at call sites.\n controls: unknown;\n}\n\n/**\n * Normalize a full model record fresh from the database.\n * Normalizes legacy keys inside `controls`.\n */\nexport function normalizeModel<T extends HasControls>(model: T): T {\n const normalized = { ...model };\n\n if (normalized.controls && typeof normalized.controls === \"object\") {\n normalized.controls = normalizeControls(\n normalized.controls as Record<string, unknown>,\n );\n }\n\n return normalized;\n}\n\n/**\n * Normalize a batch of models (array version of normalizeModel).\n */\nexport function normalizeModels<T extends HasControls>(models: T[]): T[] {\n return models.map(normalizeModel);\n}\n\n/**\n * Normalize a prompt settings object from the database.\n * Converts legacy keys so downstream code only sees modern names.\n */\nexport function normalizePromptSettings(\n settings: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(settings)) {\n if (key === \"output_format\") {\n // A stored file format (\"png\") is the image output format, not a\n // response format — keep it under its own key.\n if (typeof value === \"string\" && !TEXT_RESPONSE_FORMATS.has(value) && value !== \"\") {\n result[key] = value;\n continue;\n }\n if (!(\"response_format\" in settings)) {\n const converted = normalizeResponseFormat(value);\n if (converted !== undefined) {\n result[\"response_format\"] = converted;\n }\n }\n continue;\n }\n\n const mappedKey = LEGACY_KEY_MAP[key];\n if (mappedKey) {\n if (!(mappedKey in settings)) {\n result[mappedKey] = value;\n }\n continue;\n }\n\n result[key] = value;\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,118 @@
1
+ declare const CONTENT_TYPES: readonly ["text", "image", "audio", "video", "document", "entities", "embedding", "decision"];
2
+ type ContentType = (typeof CONTENT_TYPES)[number];
3
+ declare const INTERACTION_MODES: readonly ["turn", "single", "extraction", "realtime", "embedding", "agent", "decision"];
4
+ type InteractionMode = (typeof INTERACTION_MODES)[number];
5
+ declare const FEATURE_KEYS: readonly ["streaming", "function_calling", "tool_calling", "thinking", "structured_output", "json_mode", "web_search", "x_search", "vision", "code_execution", "computer_use", "multi_turn", "system_prompt", "embeddings", "dimension_reduction", "fine_tuning", "batch_api", "prompt_caching", "context_caching", "context_management", "partial_mode", "assistant_prefill", "ner", "classification", "structured_extraction", "relation_extraction", "pii_detection", "image_generation", "image_editing", "inpainting", "image_to_video", "video_generation", "video_editing", "audio_generation", "conversational_editing", "stateful_editing", "dialogue", "file_search", "mcp", "background_execution", "collaborative_planning", "visualization", "citations", "sandboxed_execution", "live_api", "url_context", "grounding_maps", "multimodal_embedding", "music_generation", "real_time_translation"];
6
+ type FeatureKey = (typeof FEATURE_KEYS)[number];
7
+ interface ModelCapabilities {
8
+ input: ContentType[];
9
+ output: ContentType[];
10
+ features: FeatureKey[];
11
+ interaction: InteractionMode;
12
+ /** Model is explicitly multilingual (canonical `capabilities.multilingual`; absent → false). */
13
+ multilingual: boolean;
14
+ }
15
+ /**
16
+ * Provider-managed research agents (Google Deep Research) run as an agent's
17
+ * model through the normal agent run path — a long background interaction
18
+ * that streams its research and returns a cited report.
19
+ */
20
+ declare function isManagedAgentModelCapability(capabilities: Pick<ModelCapabilities, "interaction">): boolean;
21
+ /** Decision models have a non-conversational wire contract. */
22
+ declare function isDecisionModelCapability(capabilities: Pick<ModelCapabilities, "interaction">): boolean;
23
+ /**
24
+ * What a picker is allowed to select, by execution contract.
25
+ *
26
+ * chat — a conversational turn only (`/chat`, launchers, prompt runs).
27
+ * agent — an AGENT's model. An agent is not necessarily a conversation:
28
+ * its message may carry a `decision_questions` part and be
29
+ * answered by a decision holder that emits no text at all, or it
30
+ * may be a provider-managed research agent (Deep Research) that
31
+ * runs for minutes and returns a cited report. So this purpose
32
+ * admits all three contracts and the picker labels the rows for
33
+ * what they are (Arman, 2026-09-26: "An agent can be a text
34
+ * model, video, image, speech, live, decision, and now a research
35
+ * model. It makes no difference."). Ruled by
36
+ * `common-docs/systems/agents/typed-messages/FEATURE.md`
37
+ * ("the picker shows only parts the selected model can consume"
38
+ * — the author chooses the model first, so the model list may
39
+ * never be the thing that hides the decision modality).
40
+ * decision — a decision holder only (the decision playground, a decision
41
+ * knob): a conversational model would answer in prose.
42
+ * admin — catalog editing; every contract stays selectable.
43
+ *
44
+ * Whether a model is listed in the catalog never changes the execution contract of a picker.
45
+ */
46
+ type ModelSelectionPurpose = "chat" | "agent" | "decision" | "admin";
47
+ declare function modelsForSelectionPurpose<T extends Pick<ModelCapabilities, "interaction">>(models: readonly T[], purpose?: ModelSelectionPurpose): T[];
48
+ /**
49
+ * An agent's or a chat turn's instructions are TEXT. A non-live model whose
50
+ * declared input has no text (speech-to-text: Whisper, Scribe) cannot take them,
51
+ * whatever its interaction says (2026-09-27: Whisper sat in the chat set). A live
52
+ * (realtime) session is configured rather than instructed per turn, so it is exempt.
53
+ * Mirrors aidream `agent_service/resources.py::_is_agent_assignable`.
54
+ */
55
+ declare function acceptsTextInstructions(capabilities: Pick<ModelCapabilities, "interaction"> & {
56
+ input?: readonly string[];
57
+ }): boolean;
58
+ /** Chat and agent launchers may select turn/single models that take text only. */
59
+ declare function isConversationalModelCapability(capabilities: Pick<ModelCapabilities, "interaction"> & {
60
+ input?: readonly string[];
61
+ }): boolean;
62
+ /** Fallback/replacement models must keep the same decision wire contract. */
63
+ declare function hasCompatibleDecisionInteraction(source: Pick<ModelCapabilities, "interaction">, candidate: Pick<ModelCapabilities, "interaction">): boolean;
64
+ /**
65
+ * Safe fallback used by `parseCapabilities` when no signal is available.
66
+ * A text-only turn-based model — the most common shape.
67
+ */
68
+ declare const DEFAULT_CAPABILITIES: ModelCapabilities;
69
+ /** True iff `value` is a member of the const tuple `arr`. Type-narrow helper. */
70
+ declare function isContentType(value: unknown): value is ContentType;
71
+ declare function isFeatureKey(value: unknown): value is FeatureKey;
72
+ declare function isInteractionMode(value: unknown): value is InteractionMode;
73
+
74
+ /**
75
+ * Normalizes legacy model control keys and prompt settings keys to their
76
+ * current backend equivalents.
77
+ *
78
+ * This runs once at the Redux boundary (hydrate / fetch) so downstream code
79
+ * never encounters deprecated field names.
80
+ *
81
+ * Legacy → Current mapping:
82
+ * max_tokens → max_output_tokens
83
+ * output_format → response_format ONLY when it carries text response
84
+ * formats (text / json_object / json_schema); an image
85
+ * model's output_format (png / jpeg / webp) is the file
86
+ * format — its own LLMParams field — and is never renamed.
87
+ * n → count
88
+ */
89
+ /**
90
+ * THE output_format rule — every parser of model controls and settings uses it.
91
+ * `output_format` is ambiguous in the catalog: on a legacy text model it IS the
92
+ * response format (text / json_object / json_schema) and reads as
93
+ * `response_format`; on an image model (GPT Image 2: png / jpeg / webp) it is
94
+ * the image FILE format, a real LLMParams field of its own. Renaming the image
95
+ * one showed GPT Image 2 a "Response Format" row whose choices stored nothing,
96
+ * and made a stored `output_format` read as an unknown key. A control with no
97
+ * enum keeps the legacy (text) reading.
98
+ */
99
+ declare function outputFormatControlKey(key: string, control: unknown): string;
100
+ interface HasControls {
101
+ controls: unknown;
102
+ }
103
+ /**
104
+ * Normalize a full model record fresh from the database.
105
+ * Normalizes legacy keys inside `controls`.
106
+ */
107
+ declare function normalizeModel<T extends HasControls>(model: T): T;
108
+ /**
109
+ * Normalize a batch of models (array version of normalizeModel).
110
+ */
111
+ declare function normalizeModels<T extends HasControls>(models: T[]): T[];
112
+ /**
113
+ * Normalize a prompt settings object from the database.
114
+ * Converts legacy keys so downstream code only sees modern names.
115
+ */
116
+ declare function normalizePromptSettings(settings: Record<string, unknown>): Record<string, unknown>;
117
+
118
+ export { CONTENT_TYPES, type ContentType, DEFAULT_CAPABILITIES, FEATURE_KEYS, type FeatureKey, INTERACTION_MODES, type InteractionMode, type ModelCapabilities, type ModelSelectionPurpose, acceptsTextInstructions, hasCompatibleDecisionInteraction, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isInteractionMode, isManagedAgentModelCapability, modelsForSelectionPurpose, normalizeModel, normalizeModels, normalizePromptSettings, outputFormatControlKey };
@@ -0,0 +1,118 @@
1
+ declare const CONTENT_TYPES: readonly ["text", "image", "audio", "video", "document", "entities", "embedding", "decision"];
2
+ type ContentType = (typeof CONTENT_TYPES)[number];
3
+ declare const INTERACTION_MODES: readonly ["turn", "single", "extraction", "realtime", "embedding", "agent", "decision"];
4
+ type InteractionMode = (typeof INTERACTION_MODES)[number];
5
+ declare const FEATURE_KEYS: readonly ["streaming", "function_calling", "tool_calling", "thinking", "structured_output", "json_mode", "web_search", "x_search", "vision", "code_execution", "computer_use", "multi_turn", "system_prompt", "embeddings", "dimension_reduction", "fine_tuning", "batch_api", "prompt_caching", "context_caching", "context_management", "partial_mode", "assistant_prefill", "ner", "classification", "structured_extraction", "relation_extraction", "pii_detection", "image_generation", "image_editing", "inpainting", "image_to_video", "video_generation", "video_editing", "audio_generation", "conversational_editing", "stateful_editing", "dialogue", "file_search", "mcp", "background_execution", "collaborative_planning", "visualization", "citations", "sandboxed_execution", "live_api", "url_context", "grounding_maps", "multimodal_embedding", "music_generation", "real_time_translation"];
6
+ type FeatureKey = (typeof FEATURE_KEYS)[number];
7
+ interface ModelCapabilities {
8
+ input: ContentType[];
9
+ output: ContentType[];
10
+ features: FeatureKey[];
11
+ interaction: InteractionMode;
12
+ /** Model is explicitly multilingual (canonical `capabilities.multilingual`; absent → false). */
13
+ multilingual: boolean;
14
+ }
15
+ /**
16
+ * Provider-managed research agents (Google Deep Research) run as an agent's
17
+ * model through the normal agent run path — a long background interaction
18
+ * that streams its research and returns a cited report.
19
+ */
20
+ declare function isManagedAgentModelCapability(capabilities: Pick<ModelCapabilities, "interaction">): boolean;
21
+ /** Decision models have a non-conversational wire contract. */
22
+ declare function isDecisionModelCapability(capabilities: Pick<ModelCapabilities, "interaction">): boolean;
23
+ /**
24
+ * What a picker is allowed to select, by execution contract.
25
+ *
26
+ * chat — a conversational turn only (`/chat`, launchers, prompt runs).
27
+ * agent — an AGENT's model. An agent is not necessarily a conversation:
28
+ * its message may carry a `decision_questions` part and be
29
+ * answered by a decision holder that emits no text at all, or it
30
+ * may be a provider-managed research agent (Deep Research) that
31
+ * runs for minutes and returns a cited report. So this purpose
32
+ * admits all three contracts and the picker labels the rows for
33
+ * what they are (Arman, 2026-09-26: "An agent can be a text
34
+ * model, video, image, speech, live, decision, and now a research
35
+ * model. It makes no difference."). Ruled by
36
+ * `common-docs/systems/agents/typed-messages/FEATURE.md`
37
+ * ("the picker shows only parts the selected model can consume"
38
+ * — the author chooses the model first, so the model list may
39
+ * never be the thing that hides the decision modality).
40
+ * decision — a decision holder only (the decision playground, a decision
41
+ * knob): a conversational model would answer in prose.
42
+ * admin — catalog editing; every contract stays selectable.
43
+ *
44
+ * Whether a model is listed in the catalog never changes the execution contract of a picker.
45
+ */
46
+ type ModelSelectionPurpose = "chat" | "agent" | "decision" | "admin";
47
+ declare function modelsForSelectionPurpose<T extends Pick<ModelCapabilities, "interaction">>(models: readonly T[], purpose?: ModelSelectionPurpose): T[];
48
+ /**
49
+ * An agent's or a chat turn's instructions are TEXT. A non-live model whose
50
+ * declared input has no text (speech-to-text: Whisper, Scribe) cannot take them,
51
+ * whatever its interaction says (2026-09-27: Whisper sat in the chat set). A live
52
+ * (realtime) session is configured rather than instructed per turn, so it is exempt.
53
+ * Mirrors aidream `agent_service/resources.py::_is_agent_assignable`.
54
+ */
55
+ declare function acceptsTextInstructions(capabilities: Pick<ModelCapabilities, "interaction"> & {
56
+ input?: readonly string[];
57
+ }): boolean;
58
+ /** Chat and agent launchers may select turn/single models that take text only. */
59
+ declare function isConversationalModelCapability(capabilities: Pick<ModelCapabilities, "interaction"> & {
60
+ input?: readonly string[];
61
+ }): boolean;
62
+ /** Fallback/replacement models must keep the same decision wire contract. */
63
+ declare function hasCompatibleDecisionInteraction(source: Pick<ModelCapabilities, "interaction">, candidate: Pick<ModelCapabilities, "interaction">): boolean;
64
+ /**
65
+ * Safe fallback used by `parseCapabilities` when no signal is available.
66
+ * A text-only turn-based model — the most common shape.
67
+ */
68
+ declare const DEFAULT_CAPABILITIES: ModelCapabilities;
69
+ /** True iff `value` is a member of the const tuple `arr`. Type-narrow helper. */
70
+ declare function isContentType(value: unknown): value is ContentType;
71
+ declare function isFeatureKey(value: unknown): value is FeatureKey;
72
+ declare function isInteractionMode(value: unknown): value is InteractionMode;
73
+
74
+ /**
75
+ * Normalizes legacy model control keys and prompt settings keys to their
76
+ * current backend equivalents.
77
+ *
78
+ * This runs once at the Redux boundary (hydrate / fetch) so downstream code
79
+ * never encounters deprecated field names.
80
+ *
81
+ * Legacy → Current mapping:
82
+ * max_tokens → max_output_tokens
83
+ * output_format → response_format ONLY when it carries text response
84
+ * formats (text / json_object / json_schema); an image
85
+ * model's output_format (png / jpeg / webp) is the file
86
+ * format — its own LLMParams field — and is never renamed.
87
+ * n → count
88
+ */
89
+ /**
90
+ * THE output_format rule — every parser of model controls and settings uses it.
91
+ * `output_format` is ambiguous in the catalog: on a legacy text model it IS the
92
+ * response format (text / json_object / json_schema) and reads as
93
+ * `response_format`; on an image model (GPT Image 2: png / jpeg / webp) it is
94
+ * the image FILE format, a real LLMParams field of its own. Renaming the image
95
+ * one showed GPT Image 2 a "Response Format" row whose choices stored nothing,
96
+ * and made a stored `output_format` read as an unknown key. A control with no
97
+ * enum keeps the legacy (text) reading.
98
+ */
99
+ declare function outputFormatControlKey(key: string, control: unknown): string;
100
+ interface HasControls {
101
+ controls: unknown;
102
+ }
103
+ /**
104
+ * Normalize a full model record fresh from the database.
105
+ * Normalizes legacy keys inside `controls`.
106
+ */
107
+ declare function normalizeModel<T extends HasControls>(model: T): T;
108
+ /**
109
+ * Normalize a batch of models (array version of normalizeModel).
110
+ */
111
+ declare function normalizeModels<T extends HasControls>(models: T[]): T[];
112
+ /**
113
+ * Normalize a prompt settings object from the database.
114
+ * Converts legacy keys so downstream code only sees modern names.
115
+ */
116
+ declare function normalizePromptSettings(settings: Record<string, unknown>): Record<string, unknown>;
117
+
118
+ export { CONTENT_TYPES, type ContentType, DEFAULT_CAPABILITIES, FEATURE_KEYS, type FeatureKey, INTERACTION_MODES, type InteractionMode, type ModelCapabilities, type ModelSelectionPurpose, acceptsTextInstructions, hasCompatibleDecisionInteraction, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isInteractionMode, isManagedAgentModelCapability, modelsForSelectionPurpose, normalizeModel, normalizeModels, normalizePromptSettings, outputFormatControlKey };
@@ -0,0 +1,244 @@
1
+ // models/capabilities.ts
2
+ var CONTENT_TYPES = [
3
+ "text",
4
+ "image",
5
+ "audio",
6
+ "video",
7
+ "document",
8
+ "entities",
9
+ "embedding",
10
+ "decision"
11
+ ];
12
+ var INTERACTION_MODES = [
13
+ "turn",
14
+ "single",
15
+ "extraction",
16
+ "realtime",
17
+ "embedding",
18
+ "agent",
19
+ "decision"
20
+ ];
21
+ var FEATURE_KEYS = [
22
+ "streaming",
23
+ "function_calling",
24
+ "tool_calling",
25
+ "thinking",
26
+ "structured_output",
27
+ "json_mode",
28
+ "web_search",
29
+ "x_search",
30
+ "vision",
31
+ "code_execution",
32
+ "computer_use",
33
+ "multi_turn",
34
+ "system_prompt",
35
+ "embeddings",
36
+ "dimension_reduction",
37
+ "fine_tuning",
38
+ "batch_api",
39
+ "prompt_caching",
40
+ "context_caching",
41
+ // Anthropic context editing / tool-result clearing: the model manages what
42
+ // stays in its own context window. Distinct from the two caching features,
43
+ // which are about billing and latency, not retention.
44
+ "context_management",
45
+ "partial_mode",
46
+ // The model continues a trailing assistant message (response prefill).
47
+ // Anthropic removed it on Sonnet 5 / Opus 5 / Fable / 4.6-4.8; older Claude
48
+ // models keep it. Read by features/agents/message-flags (the Prefill flag).
49
+ "assistant_prefill",
50
+ // Extraction family (GLiNER2 / fastino models)
51
+ "ner",
52
+ "classification",
53
+ "structured_extraction",
54
+ "relation_extraction",
55
+ "pii_detection",
56
+ // Media generation / editing
57
+ "image_generation",
58
+ "image_editing",
59
+ // Mask-based region editing. A model can edit a whole image without
60
+ // accepting a mask, so this is not the same declaration as image_editing.
61
+ "inpainting",
62
+ "image_to_video",
63
+ "video_generation",
64
+ "video_editing",
65
+ "audio_generation",
66
+ "conversational_editing",
67
+ "stateful_editing",
68
+ "dialogue",
69
+ // Provider-managed agents / hosted tools
70
+ "file_search",
71
+ "mcp",
72
+ "background_execution",
73
+ "collaborative_planning",
74
+ "visualization",
75
+ "citations",
76
+ "sandboxed_execution",
77
+ // Google live, grounding, embedding, and music families
78
+ "live_api",
79
+ "url_context",
80
+ "grounding_maps",
81
+ "multimodal_embedding",
82
+ "music_generation",
83
+ "real_time_translation"
84
+ ];
85
+ function isManagedAgentModelCapability(capabilities) {
86
+ return capabilities.interaction === "agent";
87
+ }
88
+ function isDecisionModelCapability(capabilities) {
89
+ return capabilities.interaction === "decision";
90
+ }
91
+ function modelsForSelectionPurpose(models, purpose = "chat") {
92
+ return models.filter((model) => {
93
+ if (purpose === "admin") return true;
94
+ if (purpose === "decision") return isDecisionModelCapability(model);
95
+ if (purpose === "agent")
96
+ return isConversationalModelCapability(model) || isDecisionModelCapability(model) || isManagedAgentModelCapability(model);
97
+ return isConversationalModelCapability(model);
98
+ });
99
+ }
100
+ function acceptsTextInstructions(capabilities) {
101
+ if (capabilities.interaction === "realtime") return true;
102
+ const input = capabilities.input ?? [];
103
+ return input.length === 0 || input.includes("text");
104
+ }
105
+ function isConversationalModelCapability(capabilities) {
106
+ return (capabilities.interaction === "turn" || capabilities.interaction === "single") && acceptsTextInstructions(capabilities);
107
+ }
108
+ function hasCompatibleDecisionInteraction(source, candidate) {
109
+ return isDecisionModelCapability(source) === isDecisionModelCapability(candidate);
110
+ }
111
+ var DEFAULT_CAPABILITIES = {
112
+ input: ["text"],
113
+ output: ["text"],
114
+ features: [],
115
+ interaction: "turn",
116
+ multilingual: false
117
+ };
118
+ function isContentType(value) {
119
+ return typeof value === "string" && CONTENT_TYPES.includes(value);
120
+ }
121
+ function isFeatureKey(value) {
122
+ return typeof value === "string" && FEATURE_KEYS.includes(value);
123
+ }
124
+ function isInteractionMode(value) {
125
+ return typeof value === "string" && INTERACTION_MODES.includes(value);
126
+ }
127
+
128
+ // models/model-normalizer.ts
129
+ var LEGACY_KEY_MAP = {
130
+ max_tokens: "max_output_tokens",
131
+ n: "count"
132
+ };
133
+ var TEXT_RESPONSE_FORMATS = /* @__PURE__ */ new Set(["text", "json_object", "json_schema"]);
134
+ function flattenFormat(option) {
135
+ return option !== null && typeof option === "object" && "type" in option ? String(option.type) : String(option);
136
+ }
137
+ function outputFormatControlKey(key, control) {
138
+ if (key !== "output_format") return key;
139
+ const options = control !== null && typeof control === "object" && "enum" in control ? control.enum : void 0;
140
+ if (!Array.isArray(options) || options.length === 0) return "response_format";
141
+ return options.every((o) => TEXT_RESPONSE_FORMATS.has(flattenFormat(o))) ? "response_format" : "output_format";
142
+ }
143
+ function normalizeResponseFormat(value) {
144
+ if (typeof value === "string") {
145
+ if (value === "text" || value === "") return void 0;
146
+ return { type: value };
147
+ }
148
+ return value;
149
+ }
150
+ function normalizeControls(controls) {
151
+ const result = {};
152
+ for (const [key, value] of Object.entries(controls)) {
153
+ if (key === "output_format") {
154
+ if (outputFormatControlKey(key, value) === "output_format") {
155
+ result[key] = value;
156
+ continue;
157
+ }
158
+ if (!("response_format" in controls)) {
159
+ const controlDef = value;
160
+ if (controlDef && typeof controlDef === "object") {
161
+ const normalized = { ...controlDef };
162
+ if (normalized.default !== void 0) {
163
+ const converted = normalizeResponseFormat(normalized.default);
164
+ if (converted === void 0) {
165
+ delete normalized.default;
166
+ } else {
167
+ normalized.default = converted;
168
+ }
169
+ }
170
+ result["response_format"] = normalized;
171
+ }
172
+ }
173
+ continue;
174
+ }
175
+ const mappedKey = LEGACY_KEY_MAP[key];
176
+ if (mappedKey) {
177
+ if (!(mappedKey in controls)) {
178
+ result[mappedKey] = value;
179
+ }
180
+ continue;
181
+ }
182
+ result[key] = value;
183
+ }
184
+ return result;
185
+ }
186
+ function normalizeModel(model) {
187
+ const normalized = { ...model };
188
+ if (normalized.controls && typeof normalized.controls === "object") {
189
+ normalized.controls = normalizeControls(
190
+ normalized.controls
191
+ );
192
+ }
193
+ return normalized;
194
+ }
195
+ function normalizeModels(models) {
196
+ return models.map(normalizeModel);
197
+ }
198
+ function normalizePromptSettings(settings) {
199
+ const result = {};
200
+ for (const [key, value] of Object.entries(settings)) {
201
+ if (key === "output_format") {
202
+ if (typeof value === "string" && !TEXT_RESPONSE_FORMATS.has(value) && value !== "") {
203
+ result[key] = value;
204
+ continue;
205
+ }
206
+ if (!("response_format" in settings)) {
207
+ const converted = normalizeResponseFormat(value);
208
+ if (converted !== void 0) {
209
+ result["response_format"] = converted;
210
+ }
211
+ }
212
+ continue;
213
+ }
214
+ const mappedKey = LEGACY_KEY_MAP[key];
215
+ if (mappedKey) {
216
+ if (!(mappedKey in settings)) {
217
+ result[mappedKey] = value;
218
+ }
219
+ continue;
220
+ }
221
+ result[key] = value;
222
+ }
223
+ return result;
224
+ }
225
+ export {
226
+ CONTENT_TYPES,
227
+ DEFAULT_CAPABILITIES,
228
+ FEATURE_KEYS,
229
+ INTERACTION_MODES,
230
+ acceptsTextInstructions,
231
+ hasCompatibleDecisionInteraction,
232
+ isContentType,
233
+ isConversationalModelCapability,
234
+ isDecisionModelCapability,
235
+ isFeatureKey,
236
+ isInteractionMode,
237
+ isManagedAgentModelCapability,
238
+ modelsForSelectionPurpose,
239
+ normalizeModel,
240
+ normalizeModels,
241
+ normalizePromptSettings,
242
+ outputFormatControlKey
243
+ };
244
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../models/capabilities.ts","../../models/model-normalizer.ts"],"sourcesContent":["// features/ai-models/capabilities/types.ts\n//\n// Canonical shape for `ai.model_definition.capabilities`. Source of truth for\n// what a model accepts as input, produces as output, and supports as a feature.\n//\n// Before May 2026 this column was a zoo — null / empty string / flat\n// string arrays / Google-style boolean object / OpenAI-style I/O object /\n// literal `\"[transcription]\"`. The backfill landed and every row is now\n// canonical, so `parseCapabilities` validates rather than infers, and the\n// dead `api_class` sniffing fallback is gone. Route all reads through it.\n//\n// Why `{input, output, features, interaction}` instead of a flat boolean\n// map: content-type acceptance is a SET, not 20 bools. `input: [\"text\",\n// \"image\"]` reads instantly as \"this model takes text and images.\" A\n// flat `text_input: true, image_input: true, audio_input: false, ...`\n// hides the shape behind ceremony. Features stay as flags because they\n// genuinely are boolean. `interaction` is its own field because it gates\n// runtime selection (turn-based vs realtime) at launch time.\n//\n// The audit system's flat `CapabilitiesRecord` is now a DERIVED\n// projection (`toAuditRecord`) — existing consumers see the same view.\n\n// `entities` and `embedding` are OUTPUT-only modalities in practice\n// (extraction models emit structured entities; vector models emit embeddings),\n// while `document` appears on inputs. All are members of the one shared set\n// because input/output share the vocabulary.\nexport const CONTENT_TYPES = [\n \"text\",\n \"image\",\n \"audio\",\n \"video\",\n \"document\",\n \"entities\",\n \"embedding\",\n \"decision\",\n] as const;\nexport type ContentType = (typeof CONTENT_TYPES)[number];\n\n// Full live vocabulary (verified against ai.model_definition 2026-08-16:\n// turn, single, extraction, realtime, embedding, agent).\n// turn — ordinary multi-turn chat request/response.\n// single — one-shot generation (image/video gen); no conversation.\n// extraction — NER/classification models (GLiNER2 family); NOT chat models.\n// realtime — live voice/audio transport.\n// embedding — vectorization models; NOT chat models.\n// agent — provider-managed background agent; uses its dedicated API.\n// decision — typed state/question evaluation; never a conversational turn.\nexport const INTERACTION_MODES = [\n \"turn\",\n \"single\",\n \"extraction\",\n \"realtime\",\n \"embedding\",\n \"agent\",\n \"decision\",\n] as const;\nexport type InteractionMode = (typeof INTERACTION_MODES)[number];\n\n// The complete feature vocabulary present on live `ai.model_definition` rows\n// (swept 2026-09-12). Adding a NEW feature value to the DB requires adding it\n// here — `parseCapabilities` screams (captureError \"data-shape\") on any value\n// it doesn't recognize instead of silently dropping it.\n//\n// THE OTHER HALF OF THIS LIST lives server-side in\n// `aidream/packages/matrx-ai/matrx_ai/providers/capability_vocabulary.py`,\n// which is what the write path (the agent `sql`/`db_*` tools and\n// `scripts/set_model_capabilities.py`) now validates against, and what\n// `scripts/check_model_capability_vocabulary.py` proves the live rows obey.\n// Keep the two lists in lockstep; a term added to one belongs in both.\n//\n// Why the scream is worth keeping even with a write guard: on 2026-09-12 this\n// parser was the ONLY thing on the platform that noticed the model-sync agent\n// had written provider-doc spellings (\"structured_outputs\", \"reasoning\",\n// \"code_interpreter\", \"batch\", \"pdf_input\"). Server-side, nothing matched\n// them and nothing said so — gpt-6-astra resolved to free-text structured\n// output with schema output declared on its row. Provider aliases are\n// normalized at the catalog write boundary — they must never become stored\n// feature keys here.\nexport const FEATURE_KEYS = [\n \"streaming\",\n \"function_calling\",\n \"tool_calling\",\n \"thinking\",\n \"structured_output\",\n \"json_mode\",\n \"web_search\",\n \"x_search\",\n \"vision\",\n \"code_execution\",\n \"computer_use\",\n \"multi_turn\",\n \"system_prompt\",\n \"embeddings\",\n \"dimension_reduction\",\n \"fine_tuning\",\n \"batch_api\",\n \"prompt_caching\",\n \"context_caching\",\n // Anthropic context editing / tool-result clearing: the model manages what\n // stays in its own context window. Distinct from the two caching features,\n // which are about billing and latency, not retention.\n \"context_management\",\n \"partial_mode\",\n // The model continues a trailing assistant message (response prefill).\n // Anthropic removed it on Sonnet 5 / Opus 5 / Fable / 4.6-4.8; older Claude\n // models keep it. Read by features/agents/message-flags (the Prefill flag).\n \"assistant_prefill\",\n // Extraction family (GLiNER2 / fastino models)\n \"ner\",\n \"classification\",\n \"structured_extraction\",\n \"relation_extraction\",\n \"pii_detection\",\n // Media generation / editing\n \"image_generation\",\n \"image_editing\",\n // Mask-based region editing. A model can edit a whole image without\n // accepting a mask, so this is not the same declaration as image_editing.\n \"inpainting\",\n \"image_to_video\",\n \"video_generation\",\n \"video_editing\",\n \"audio_generation\",\n \"conversational_editing\",\n \"stateful_editing\",\n \"dialogue\",\n // Provider-managed agents / hosted tools\n \"file_search\",\n \"mcp\",\n \"background_execution\",\n \"collaborative_planning\",\n \"visualization\",\n \"citations\",\n \"sandboxed_execution\",\n // Google live, grounding, embedding, and music families\n \"live_api\",\n \"url_context\",\n \"grounding_maps\",\n \"multimodal_embedding\",\n \"music_generation\",\n \"real_time_translation\",\n] as const;\nexport type FeatureKey = (typeof FEATURE_KEYS)[number];\n\nexport interface ModelCapabilities {\n input: ContentType[];\n output: ContentType[];\n features: FeatureKey[];\n interaction: InteractionMode;\n /** Model is explicitly multilingual (canonical `capabilities.multilingual`; absent → false). */\n multilingual: boolean;\n}\n\n/**\n * Provider-managed research agents (Google Deep Research) run as an agent's\n * model through the normal agent run path — a long background interaction\n * that streams its research and returns a cited report.\n */\nexport function isManagedAgentModelCapability(\n capabilities: Pick<ModelCapabilities, \"interaction\">,\n): boolean {\n return capabilities.interaction === \"agent\";\n}\n\n/** Decision models have a non-conversational wire contract. */\nexport function isDecisionModelCapability(\n capabilities: Pick<ModelCapabilities, \"interaction\">,\n): boolean {\n return capabilities.interaction === \"decision\";\n}\n\n/**\n * What a picker is allowed to select, by execution contract.\n *\n * chat — a conversational turn only (`/chat`, launchers, prompt runs).\n * agent — an AGENT's model. An agent is not necessarily a conversation:\n * its message may carry a `decision_questions` part and be\n * answered by a decision holder that emits no text at all, or it\n * may be a provider-managed research agent (Deep Research) that\n * runs for minutes and returns a cited report. So this purpose\n * admits all three contracts and the picker labels the rows for\n * what they are (Arman, 2026-09-26: \"An agent can be a text\n * model, video, image, speech, live, decision, and now a research\n * model. It makes no difference.\"). Ruled by\n * `common-docs/systems/agents/typed-messages/FEATURE.md`\n * (\"the picker shows only parts the selected model can consume\"\n * — the author chooses the model first, so the model list may\n * never be the thing that hides the decision modality).\n * decision — a decision holder only (the decision playground, a decision\n * knob): a conversational model would answer in prose.\n * admin — catalog editing; every contract stays selectable.\n *\n * Whether a model is listed in the catalog never changes the execution contract of a picker.\n */\nexport type ModelSelectionPurpose = \"chat\" | \"agent\" | \"decision\" | \"admin\";\n\nexport function modelsForSelectionPurpose<\n T extends Pick<ModelCapabilities, \"interaction\">,\n>(\n models: readonly T[],\n purpose: ModelSelectionPurpose = \"chat\",\n): T[] {\n return models.filter((model) => {\n if (purpose === \"admin\") return true;\n if (purpose === \"decision\") return isDecisionModelCapability(model);\n if (purpose === \"agent\")\n return (\n isConversationalModelCapability(model) ||\n isDecisionModelCapability(model) ||\n isManagedAgentModelCapability(model)\n );\n return isConversationalModelCapability(model);\n });\n}\n\n/**\n * An agent's or a chat turn's instructions are TEXT. A non-live model whose\n * declared input has no text (speech-to-text: Whisper, Scribe) cannot take them,\n * whatever its interaction says (2026-09-27: Whisper sat in the chat set). A live\n * (realtime) session is configured rather than instructed per turn, so it is exempt.\n * Mirrors aidream `agent_service/resources.py::_is_agent_assignable`.\n */\nexport function acceptsTextInstructions(\n capabilities: Pick<ModelCapabilities, \"interaction\"> & {\n input?: readonly string[];\n },\n): boolean {\n if (capabilities.interaction === \"realtime\") return true;\n const input = capabilities.input ?? [];\n return input.length === 0 || input.includes(\"text\");\n}\n\n/** Chat and agent launchers may select turn/single models that take text only. */\nexport function isConversationalModelCapability(\n capabilities: Pick<ModelCapabilities, \"interaction\"> & {\n input?: readonly string[];\n },\n): boolean {\n return (\n (capabilities.interaction === \"turn\" ||\n capabilities.interaction === \"single\") &&\n acceptsTextInstructions(capabilities)\n );\n}\n\n/** Fallback/replacement models must keep the same decision wire contract. */\nexport function hasCompatibleDecisionInteraction(\n source: Pick<ModelCapabilities, \"interaction\">,\n candidate: Pick<ModelCapabilities, \"interaction\">,\n): boolean {\n return (\n isDecisionModelCapability(source) === isDecisionModelCapability(candidate)\n );\n}\n\n/**\n * Safe fallback used by `parseCapabilities` when no signal is available.\n * A text-only turn-based model — the most common shape.\n */\nexport const DEFAULT_CAPABILITIES: ModelCapabilities = {\n input: [\"text\"],\n output: [\"text\"],\n features: [],\n interaction: \"turn\",\n multilingual: false,\n};\n\n/** True iff `value` is a member of the const tuple `arr`. Type-narrow helper. */\nexport function isContentType(value: unknown): value is ContentType {\n return (\n typeof value === \"string\" &&\n (CONTENT_TYPES as readonly string[]).includes(value)\n );\n}\n\nexport function isFeatureKey(value: unknown): value is FeatureKey {\n return (\n typeof value === \"string\" &&\n (FEATURE_KEYS as readonly string[]).includes(value)\n );\n}\n\nexport function isInteractionMode(value: unknown): value is InteractionMode {\n return (\n typeof value === \"string\" &&\n (INTERACTION_MODES as readonly string[]).includes(value)\n );\n}\n","/**\n * Normalizes legacy model control keys and prompt settings keys to their\n * current backend equivalents.\n *\n * This runs once at the Redux boundary (hydrate / fetch) so downstream code\n * never encounters deprecated field names.\n *\n * Legacy → Current mapping:\n * max_tokens → max_output_tokens\n * output_format → response_format ONLY when it carries text response\n * formats (text / json_object / json_schema); an image\n * model's output_format (png / jpeg / webp) is the file\n * format — its own LLMParams field — and is never renamed.\n * n → count\n */\n\nconst LEGACY_KEY_MAP: Record<string, string> = {\n max_tokens: \"max_output_tokens\",\n n: \"count\",\n};\n\n/** The text response formats a legacy `output_format` carries. */\nconst TEXT_RESPONSE_FORMATS = new Set([\"text\", \"json_object\", \"json_schema\"]);\n\nfunction flattenFormat(option: unknown): string {\n return option !== null &&\n typeof option === \"object\" &&\n \"type\" in (option as Record<string, unknown>)\n ? String((option as Record<string, unknown>).type)\n : String(option);\n}\n\n/**\n * THE output_format rule — every parser of model controls and settings uses it.\n * `output_format` is ambiguous in the catalog: on a legacy text model it IS the\n * response format (text / json_object / json_schema) and reads as\n * `response_format`; on an image model (GPT Image 2: png / jpeg / webp) it is\n * the image FILE format, a real LLMParams field of its own. Renaming the image\n * one showed GPT Image 2 a \"Response Format\" row whose choices stored nothing,\n * and made a stored `output_format` read as an unknown key. A control with no\n * enum keeps the legacy (text) reading.\n */\nexport function outputFormatControlKey(key: string, control: unknown): string {\n if (key !== \"output_format\") return key;\n const options =\n control !== null && typeof control === \"object\" && \"enum\" in control\n ? (control as { enum?: unknown }).enum\n : undefined;\n if (!Array.isArray(options) || options.length === 0) return \"response_format\";\n return options.every((o) => TEXT_RESPONSE_FORMATS.has(flattenFormat(o)))\n ? \"response_format\"\n : \"output_format\";\n}\n\nfunction normalizeResponseFormat(value: unknown): unknown {\n if (typeof value === \"string\") {\n if (value === \"text\" || value === \"\") return undefined;\n return { type: value };\n }\n return value;\n}\n\n/**\n * Normalize a single model's `controls` JSONB blob in-place.\n *\n * For each top-level key in controls:\n * - Renames legacy keys (max_tokens, output_format, n)\n * - Converts output_format string defaults to { type: string } dicts\n *\n * If the target key already exists, the legacy key is silently dropped\n * (the modern key wins).\n */\nfunction normalizeControls(\n controls: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(controls)) {\n if (key === \"output_format\") {\n if (outputFormatControlKey(key, value) === \"output_format\") {\n result[key] = value;\n continue;\n }\n if (!(\"response_format\" in controls)) {\n const controlDef = value as Record<string, unknown> | null;\n if (controlDef && typeof controlDef === \"object\") {\n const normalized = { ...controlDef };\n if (normalized.default !== undefined) {\n const converted = normalizeResponseFormat(normalized.default);\n if (converted === undefined) {\n delete normalized.default;\n } else {\n normalized.default = converted;\n }\n }\n result[\"response_format\"] = normalized;\n }\n }\n continue;\n }\n\n const mappedKey = LEGACY_KEY_MAP[key];\n if (mappedKey) {\n if (!(mappedKey in controls)) {\n result[mappedKey] = value;\n }\n continue;\n }\n\n result[key] = value;\n }\n\n return result;\n}\n\ninterface HasControls {\n // Json from database.types.ts is a union that includes Record<string, Json>.\n // We widen to unknown here so the constraint accepts both hand-rolled types\n // and the generated DB Row type without requiring a cast at call sites.\n controls: unknown;\n}\n\n/**\n * Normalize a full model record fresh from the database.\n * Normalizes legacy keys inside `controls`.\n */\nexport function normalizeModel<T extends HasControls>(model: T): T {\n const normalized = { ...model };\n\n if (normalized.controls && typeof normalized.controls === \"object\") {\n normalized.controls = normalizeControls(\n normalized.controls as Record<string, unknown>,\n );\n }\n\n return normalized;\n}\n\n/**\n * Normalize a batch of models (array version of normalizeModel).\n */\nexport function normalizeModels<T extends HasControls>(models: T[]): T[] {\n return models.map(normalizeModel);\n}\n\n/**\n * Normalize a prompt settings object from the database.\n * Converts legacy keys so downstream code only sees modern names.\n */\nexport function normalizePromptSettings(\n settings: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(settings)) {\n if (key === \"output_format\") {\n // A stored file format (\"png\") is the image output format, not a\n // response format — keep it under its own key.\n if (typeof value === \"string\" && !TEXT_RESPONSE_FORMATS.has(value) && value !== \"\") {\n result[key] = value;\n continue;\n }\n if (!(\"response_format\" in settings)) {\n const converted = normalizeResponseFormat(value);\n if (converted !== undefined) {\n result[\"response_format\"] = converted;\n }\n }\n continue;\n }\n\n const mappedKey = LEGACY_KEY_MAP[key];\n if (mappedKey) {\n if (!(mappedKey in settings)) {\n result[mappedKey] = value;\n }\n continue;\n }\n\n result[key] = value;\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}