opencode-cmd-provider 0.1.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +172 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +7 -0
  6. package/dist/src/env.d.ts +7 -0
  7. package/dist/src/env.js +24 -0
  8. package/dist/src/plugin/auth-server.d.ts +30 -0
  9. package/dist/src/plugin/auth-server.js +158 -0
  10. package/dist/src/plugin/auth.d.ts +6 -0
  11. package/dist/src/plugin/auth.js +38 -0
  12. package/dist/src/plugin/index.d.ts +6 -0
  13. package/dist/src/plugin/index.js +39 -0
  14. package/dist/src/plugin/models.d.ts +7 -0
  15. package/dist/src/plugin/models.js +50 -0
  16. package/dist/src/provider/aisdk-types.d.ts +9 -0
  17. package/dist/src/provider/aisdk-types.js +1 -0
  18. package/dist/src/provider/auth-key.d.ts +7 -0
  19. package/dist/src/provider/auth-key.js +65 -0
  20. package/dist/src/provider/command-code-model.d.ts +39 -0
  21. package/dist/src/provider/command-code-model.js +425 -0
  22. package/dist/src/provider/converters.d.ts +33 -0
  23. package/dist/src/provider/converters.js +256 -0
  24. package/dist/src/provider/cost.d.ts +19 -0
  25. package/dist/src/provider/cost.js +19 -0
  26. package/dist/src/provider/index.d.ts +5 -0
  27. package/dist/src/provider/index.js +9 -0
  28. package/dist/src/provider/json-schema.d.ts +1 -0
  29. package/dist/src/provider/json-schema.js +374 -0
  30. package/dist/src/provider/modalities.d.ts +9 -0
  31. package/dist/src/provider/modalities.js +53 -0
  32. package/dist/src/provider/models.d.ts +29 -0
  33. package/dist/src/provider/models.js +229 -0
  34. package/dist/src/provider/pricing.d.ts +24 -0
  35. package/dist/src/provider/pricing.js +188 -0
  36. package/dist/src/provider/project-slug.d.ts +1 -0
  37. package/dist/src/provider/project-slug.js +10 -0
  38. package/dist/src/provider/reasoning.d.ts +29 -0
  39. package/dist/src/provider/reasoning.js +74 -0
  40. package/dist/src/provider/redact.d.ts +2 -0
  41. package/dist/src/provider/redact.js +59 -0
  42. package/dist/src/provider/retry.d.ts +8 -0
  43. package/dist/src/provider/retry.js +83 -0
  44. package/dist/src/provider/stream.d.ts +5 -0
  45. package/dist/src/provider/stream.js +105 -0
  46. package/package.json +58 -0
@@ -0,0 +1,256 @@
1
+ // src/provider/converters.ts — AI SDK v3 messages → Command Code payload (PLAN #2 Part B)
2
+ //
3
+ // Port of pi-commandcode-provider/src/converters.ts. Input is the AI SDK v3
4
+ // prompt format (LanguageModelV3Message / LanguageModelV3Prompt):
5
+ //
6
+ // - { role: "system"; content: string }
7
+ // - { role: "user"; content: string | Array<{ type: "text"; text } | { type: "file"; data; mediaType }> }
8
+ // - { role: "assistant"; content: Array<{ type: "text" } | { type: "reasoning" } | { type: "tool-call" } | { type: "tool-result" }> }
9
+ // - { role: "tool"; content: Array<{ type: "tool-result"; toolCallId; toolName; output }> }
10
+ //
11
+ // Image parts: v3 names them `file` parts with `data`/`mediaType` (image/*).
12
+ // We treat `image`-typed parts as well for forward-compat with other SDK
13
+ // shapes. `getApiKey` lives in ./auth-key.ts; `parseStreamEventLine` and
14
+ // `mapFinishReason` are issue #3.
15
+ import { toJsonSchema } from "./json-schema.js";
16
+ export { toJsonSchema } from "./json-schema.js";
17
+ export function isRecord(value) {
18
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19
+ }
20
+ export function stringValue(value) {
21
+ return typeof value === "string" ? value : undefined;
22
+ }
23
+ export function recordArray(value) {
24
+ if (!Array.isArray(value))
25
+ return [];
26
+ return value.filter(isRecord);
27
+ }
28
+ export function recordOrEmpty(value) {
29
+ if (isRecord(value))
30
+ return value;
31
+ if (typeof value === "string") {
32
+ try {
33
+ const parsed = JSON.parse(value);
34
+ if (isRecord(parsed))
35
+ return parsed;
36
+ }
37
+ catch {
38
+ // Some providers stream incomplete JSON argument fragments.
39
+ }
40
+ }
41
+ return {};
42
+ }
43
+ export function numberValue(value) {
44
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
45
+ }
46
+ function imageParts(value) {
47
+ if (isRecord(value))
48
+ return value.type === "image" || value.type === "file" ? [value] : [];
49
+ return recordArray(value).filter((part) => part.type === "image" ||
50
+ (part.type === "file" && stringValue(part.mediaType)?.startsWith("image/")));
51
+ }
52
+ function imageContentError(role) {
53
+ return new Error(`Selected Command Code model does not support image content in ${role}`);
54
+ }
55
+ export function assertTextOnlyMessages(messages) {
56
+ for (const message of messages ?? []) {
57
+ if (imageParts(message.content).length > 0) {
58
+ const role = message.role === "tool" ? "tool results" : `${String(message.role)} messages`;
59
+ throw imageContentError(role);
60
+ }
61
+ }
62
+ }
63
+ function imageToCommandCode(part) {
64
+ const raw = stringValue(part.image) ?? stringValue(part.data);
65
+ const mimeType = stringValue(part.mimeType) ?? stringValue(part.mediaType);
66
+ if (!raw || raw.length === 0) {
67
+ throw new Error("Invalid image content: expected a base64 data URL string");
68
+ }
69
+ if (raw.startsWith("data:")) {
70
+ const [mime] = raw.slice(5).split(";base64,");
71
+ return { type: "image", image: raw, mimeType: mime ?? mimeType ?? "application/octet-stream" };
72
+ }
73
+ return {
74
+ type: "image",
75
+ image: `data:${mimeType ?? "application/octet-stream"};base64,${raw}`,
76
+ mimeType: mimeType ?? "application/octet-stream",
77
+ };
78
+ }
79
+ function userContentToCommandCode(content, allowImages) {
80
+ if (typeof content === "string")
81
+ return content;
82
+ return recordArray(content).flatMap((part) => {
83
+ if (part.type === "text")
84
+ return [{ type: "text", text: stringValue(part.text) ?? "" }];
85
+ if (part.type === "image" || part.type === "file") {
86
+ if (!allowImages)
87
+ throw imageContentError("user messages");
88
+ return [imageToCommandCode(part)];
89
+ }
90
+ return [];
91
+ });
92
+ }
93
+ export function textContent(message) {
94
+ return recordArray(message.content)
95
+ .filter((part) => part.type === "text")
96
+ .map((part) => stringValue(part.text) ?? "")
97
+ .join("\n");
98
+ }
99
+ export function getEnvironmentInfo() {
100
+ return `${process.platform}-${process.arch}, Node.js ${process.version}`;
101
+ }
102
+ export function toolsToJson(tools) {
103
+ if (!tools)
104
+ return [];
105
+ if (Array.isArray(tools)) {
106
+ return tools.map((tool) => ({
107
+ type: "function",
108
+ name: tool.name ?? "",
109
+ description: tool.description,
110
+ input_schema: tool.parameters ? toJsonSchema(tool.parameters) : {},
111
+ }));
112
+ }
113
+ return Object.entries(tools).map(([name, tool]) => ({
114
+ type: "function",
115
+ name,
116
+ description: tool.description,
117
+ input_schema: tool.parameters ? toJsonSchema(tool.parameters) : {},
118
+ }));
119
+ }
120
+ function completeToolCallIds(messages) {
121
+ const callIds = new Set();
122
+ const resultIds = new Set();
123
+ for (const message of messages ?? []) {
124
+ if (message.role === "assistant") {
125
+ for (const content of recordArray(message.content)) {
126
+ if (content.type === "tool-call") {
127
+ const id = stringValue(content.toolCallId);
128
+ if (id)
129
+ callIds.add(id);
130
+ }
131
+ }
132
+ }
133
+ else if (message.role === "tool") {
134
+ for (const content of recordArray(message.content)) {
135
+ const id = stringValue(content.toolCallId);
136
+ if (id)
137
+ resultIds.add(id);
138
+ }
139
+ }
140
+ }
141
+ return new Set([...callIds].filter((id) => resultIds.has(id)));
142
+ }
143
+ function resultText(result) {
144
+ if (typeof result === "string")
145
+ return result;
146
+ if (Array.isArray(result))
147
+ return result.map(resultText).filter(Boolean).join("\n");
148
+ if (result && typeof result === "object") {
149
+ const text = result.text;
150
+ if (typeof text === "string")
151
+ return text;
152
+ const content = result.content;
153
+ if (content !== undefined)
154
+ return resultText(content);
155
+ return JSON.stringify(result);
156
+ }
157
+ return String(result ?? "");
158
+ }
159
+ export function messagesToCC(messages, options = {}) {
160
+ const allowImages = options.allowImages ?? false;
161
+ if (!allowImages)
162
+ assertTextOnlyMessages(messages);
163
+ const out = [];
164
+ const pairedToolCallIds = completeToolCallIds(messages);
165
+ for (const message of messages ?? []) {
166
+ if (message.role === "user") {
167
+ out.push({
168
+ role: "user",
169
+ content: userContentToCommandCode(message.content, allowImages),
170
+ });
171
+ }
172
+ else if (message.role === "assistant") {
173
+ const parts = [];
174
+ for (const content of recordArray(message.content)) {
175
+ if (content.type === "text") {
176
+ parts.push({ type: "text", text: stringValue(content.text) ?? "" });
177
+ }
178
+ else if (content.type === "tool-call") {
179
+ const toolCallId = stringValue(content.toolCallId) ?? "";
180
+ if (!pairedToolCallIds.has(toolCallId))
181
+ continue;
182
+ parts.push({
183
+ type: "tool-call",
184
+ toolCallId,
185
+ toolName: stringValue(content.toolName) ?? "",
186
+ input: recordOrEmpty(content.args ?? content.arguments),
187
+ });
188
+ }
189
+ }
190
+ if (parts.length > 0)
191
+ out.push({ role: "assistant", content: parts });
192
+ }
193
+ else if (message.role === "tool") {
194
+ for (const content of recordArray(message.content)) {
195
+ const toolCallId = stringValue(content.toolCallId) ?? "";
196
+ if (!pairedToolCallIds.has(toolCallId))
197
+ continue;
198
+ out.push({
199
+ role: "tool",
200
+ content: [
201
+ {
202
+ type: "tool-result",
203
+ toolCallId,
204
+ toolName: stringValue(content.toolName) ?? "",
205
+ output: content.isError
206
+ ? { type: "error-text", value: resultText(content.result ?? content.output) }
207
+ : { type: "text", value: resultText(content.result ?? content.output) },
208
+ },
209
+ ],
210
+ });
211
+ const images = imageParts(content);
212
+ if (images.length > 0) {
213
+ if (!allowImages)
214
+ throw imageContentError("tool results");
215
+ out.push({
216
+ role: "user",
217
+ content: images.map(imageToCommandCode),
218
+ });
219
+ }
220
+ }
221
+ }
222
+ }
223
+ return out;
224
+ }
225
+ function promptPartToText(value, depth = 0) {
226
+ if (depth > 10)
227
+ return "";
228
+ if (typeof value === "string")
229
+ return value;
230
+ if (Array.isArray(value))
231
+ return value
232
+ .map((v) => promptPartToText(v, depth + 1))
233
+ .filter(Boolean)
234
+ .join("\n");
235
+ if (!isRecord(value))
236
+ return "";
237
+ const text = stringValue(value.text);
238
+ if (text)
239
+ return text;
240
+ const content = promptPartToText(value.content, depth + 1);
241
+ if (content)
242
+ return content;
243
+ return "";
244
+ }
245
+ export function systemPromptToText(value) {
246
+ if (value === undefined || value === null)
247
+ return "";
248
+ if (typeof value === "string")
249
+ return value;
250
+ if (Array.isArray(value))
251
+ return value
252
+ .map((v) => promptPartToText(v, 0))
253
+ .filter(Boolean)
254
+ .join("\n\n");
255
+ return promptPartToText(value, 0);
256
+ }
@@ -0,0 +1,19 @@
1
+ import type { CommandCodeModelCost } from "./pricing.js";
2
+ export interface CostUsage {
3
+ input: number;
4
+ output: number;
5
+ cacheRead: number;
6
+ cacheWrite: number;
7
+ cacheWrite1h?: number;
8
+ cost: {
9
+ input: number;
10
+ output: number;
11
+ cacheRead: number;
12
+ cacheWrite: number;
13
+ total: number;
14
+ };
15
+ }
16
+ export interface CostModel {
17
+ cost: CommandCodeModelCost;
18
+ }
19
+ export declare function calculateCommandCodeCost(model: CostModel, usage: CostUsage): void;
@@ -0,0 +1,19 @@
1
+ export function calculateCommandCodeCost(model, usage) {
2
+ const inputTokens = usage.input + usage.cacheRead + usage.cacheWrite;
3
+ let rates = model.cost;
4
+ let matchedThreshold = -1;
5
+ for (const tier of model.cost.tiers ?? []) {
6
+ if (inputTokens > tier.inputTokensAbove && tier.inputTokensAbove > matchedThreshold) {
7
+ rates = tier;
8
+ matchedThreshold = tier.inputTokensAbove;
9
+ }
10
+ }
11
+ const longWrite = usage.cacheWrite1h ?? 0;
12
+ const shortWrite = usage.cacheWrite - longWrite;
13
+ usage.cost.input = (rates.input / 1_000_000) * usage.input;
14
+ usage.cost.output = (rates.output / 1_000_000) * usage.output;
15
+ usage.cost.cacheRead = (rates.cacheRead / 1_000_000) * usage.cacheRead;
16
+ usage.cost.cacheWrite = (rates.cacheWrite * shortWrite + rates.input * 2 * longWrite) / 1_000_000;
17
+ usage.cost.total =
18
+ usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
19
+ }
@@ -0,0 +1,5 @@
1
+ import { CommandCodeLanguageModel, type CommandCodeModelOptions } from "./command-code-model.js";
2
+ export declare function createCommandCode(options?: CommandCodeModelOptions): {
3
+ languageModel(modelId: string): CommandCodeLanguageModel;
4
+ };
5
+ export type { CommandCodeModelOptions } from "./command-code-model.js";
@@ -0,0 +1,9 @@
1
+ // src/provider/index.ts — public provider factory (PLAN #8)
2
+ import { CommandCodeLanguageModel } from "./command-code-model.js";
3
+ export function createCommandCode(options = {}) {
4
+ return {
5
+ languageModel(modelId) {
6
+ return new CommandCodeLanguageModel(options, modelId);
7
+ },
8
+ };
9
+ }
@@ -0,0 +1 @@
1
+ export declare function toJsonSchema(schema: unknown): unknown;
@@ -0,0 +1,374 @@
1
+ function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ function stringValue(value) {
5
+ return typeof value === "string" ? value : undefined;
6
+ }
7
+ function booleanValue(value) {
8
+ return typeof value === "boolean" ? value : undefined;
9
+ }
10
+ const JSON_SCHEMA_TYPES = new Set([
11
+ "array",
12
+ "boolean",
13
+ "integer",
14
+ "null",
15
+ "number",
16
+ "object",
17
+ "string",
18
+ ]);
19
+ const LEGACY_KINDS = new Set([
20
+ "any",
21
+ "array",
22
+ "boolean",
23
+ "enum",
24
+ "integer",
25
+ "intersect",
26
+ "intersection",
27
+ "literal",
28
+ "never",
29
+ "null",
30
+ "nullable",
31
+ "number",
32
+ "object",
33
+ "optional",
34
+ "string",
35
+ "undefined",
36
+ "union",
37
+ "unknown",
38
+ ]);
39
+ const LEGACY_FIELDS = new Set([
40
+ "element",
41
+ "kind",
42
+ "inner",
43
+ "optional",
44
+ "value",
45
+ "values",
46
+ "variants",
47
+ "wrapped",
48
+ ]);
49
+ const SCHEMA_MAP_FIELDS = new Set([
50
+ "$defs",
51
+ "definitions",
52
+ "dependentSchemas",
53
+ "patternProperties",
54
+ "properties",
55
+ ]);
56
+ const SCHEMA_ARRAY_FIELDS = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
57
+ const SCHEMA_VALUE_FIELDS = new Set([
58
+ "additionalItems",
59
+ "additionalProperties",
60
+ "contains",
61
+ "contentSchema",
62
+ "else",
63
+ "if",
64
+ "items",
65
+ "not",
66
+ "propertyNames",
67
+ "then",
68
+ "unevaluatedItems",
69
+ "unevaluatedProperties",
70
+ ]);
71
+ const SCHEMA_KEYWORDS = new Set([
72
+ "$anchor",
73
+ "$comment",
74
+ "$defs",
75
+ "$dynamicAnchor",
76
+ "$dynamicRef",
77
+ "$id",
78
+ "$ref",
79
+ "$schema",
80
+ "$vocabulary",
81
+ "additionalItems",
82
+ "additionalProperties",
83
+ "allOf",
84
+ "anyOf",
85
+ "const",
86
+ "contains",
87
+ "contentEncoding",
88
+ "contentMediaType",
89
+ "contentSchema",
90
+ "default",
91
+ "definitions",
92
+ "dependentRequired",
93
+ "dependentSchemas",
94
+ "description",
95
+ "else",
96
+ "enum",
97
+ "examples",
98
+ "exclusiveMaximum",
99
+ "exclusiveMinimum",
100
+ "format",
101
+ "if",
102
+ "items",
103
+ "maxContains",
104
+ "maxItems",
105
+ "maxLength",
106
+ "maxProperties",
107
+ "maximum",
108
+ "minContains",
109
+ "minItems",
110
+ "minLength",
111
+ "minProperties",
112
+ "minimum",
113
+ "multipleOf",
114
+ "not",
115
+ "oneOf",
116
+ "pattern",
117
+ "patternProperties",
118
+ "prefixItems",
119
+ "properties",
120
+ "propertyNames",
121
+ "readOnly",
122
+ "required",
123
+ "title",
124
+ "type",
125
+ "unevaluatedItems",
126
+ "unevaluatedProperties",
127
+ "uniqueItems",
128
+ "writeOnly",
129
+ ]);
130
+ function stringArray(value) {
131
+ if (!Array.isArray(value))
132
+ return undefined;
133
+ const values = value.filter((item) => typeof item === "string");
134
+ return values.length === value.length ? values : undefined;
135
+ }
136
+ function validSchemaType(value) {
137
+ if (typeof value === "string")
138
+ return JSON_SCHEMA_TYPES.has(value);
139
+ if (!Array.isArray(value) || value.length === 0)
140
+ return false;
141
+ return value.every((item) => typeof item === "string" && JSON_SCHEMA_TYPES.has(item));
142
+ }
143
+ function legacyKind(schema) {
144
+ const explicitKind = stringValue(schema.kind)?.toLowerCase();
145
+ if (explicitKind && LEGACY_KINDS.has(explicitKind))
146
+ return explicitKind;
147
+ const type = stringValue(schema.type);
148
+ const normalized = type?.toLowerCase();
149
+ if (!normalized || !LEGACY_KINDS.has(normalized))
150
+ return undefined;
151
+ if (!validSchemaType(type) || Object.keys(schema).some((key) => LEGACY_FIELDS.has(key))) {
152
+ return normalized;
153
+ }
154
+ return undefined;
155
+ }
156
+ function looksLikeJsonSchema(schema) {
157
+ if (Object.keys(schema).length === 0)
158
+ return true;
159
+ if (schema.type !== undefined && !validSchemaType(schema.type))
160
+ return false;
161
+ return Object.keys(schema).some((key) => SCHEMA_KEYWORDS.has(key));
162
+ }
163
+ function isOptionalSchema(schema) {
164
+ if (!isRecord(schema))
165
+ return false;
166
+ if (booleanValue(schema.optional) === true)
167
+ return true;
168
+ const kind = legacyKind(schema);
169
+ if (kind === "optional")
170
+ return true;
171
+ if (kind !== "union")
172
+ return false;
173
+ const variants = Array.isArray(schema.variants)
174
+ ? schema.variants
175
+ : Array.isArray(schema.anyOf)
176
+ ? schema.anyOf
177
+ : [];
178
+ return variants.some((variant) => legacyKind(isRecord(variant) ? variant : {}) === "undefined");
179
+ }
180
+ function schemaValue(value, seen) {
181
+ if (typeof value === "boolean")
182
+ return value;
183
+ if (!isRecord(value))
184
+ return {};
185
+ return convertSchema(value, seen);
186
+ }
187
+ function setSchemaProperty(target, key, value) {
188
+ Object.defineProperty(target, key, {
189
+ configurable: true,
190
+ enumerable: true,
191
+ value,
192
+ writable: true,
193
+ });
194
+ }
195
+ function schemaMap(value, seen) {
196
+ if (!isRecord(value))
197
+ return {};
198
+ const out = {};
199
+ for (const [key, item] of Object.entries(value)) {
200
+ setSchemaProperty(out, key, schemaValue(item, seen));
201
+ }
202
+ return out;
203
+ }
204
+ function schemaArray(value, seen) {
205
+ if (!Array.isArray(value))
206
+ return [];
207
+ return value.map((item) => schemaValue(item, seen));
208
+ }
209
+ function isSchemaValue(value) {
210
+ return typeof value === "boolean" || isRecord(value);
211
+ }
212
+ function copySchemaObject(source, seen, legacy, forcedType) {
213
+ const out = {};
214
+ for (const [key, value] of Object.entries(source)) {
215
+ if (legacy && LEGACY_FIELDS.has(key))
216
+ continue;
217
+ if (key === "nullable" || (forcedType !== undefined && key === "type"))
218
+ continue;
219
+ if (key === "required") {
220
+ const required = stringArray(value);
221
+ if (required)
222
+ out.required = required;
223
+ }
224
+ else if (SCHEMA_MAP_FIELDS.has(key)) {
225
+ out[key] = schemaMap(value, seen);
226
+ }
227
+ else if (SCHEMA_ARRAY_FIELDS.has(key)) {
228
+ out[key] = schemaArray(value, seen);
229
+ }
230
+ else if (SCHEMA_VALUE_FIELDS.has(key)) {
231
+ out[key] =
232
+ Array.isArray(value) && key === "items"
233
+ ? schemaArray(value, seen)
234
+ : schemaValue(value, seen);
235
+ }
236
+ else {
237
+ out[key] = value;
238
+ }
239
+ }
240
+ if (forcedType !== undefined)
241
+ out.type = forcedType;
242
+ if (booleanValue(source.nullable) === true)
243
+ return makeNullable(out);
244
+ return out;
245
+ }
246
+ function makeNullable(schema) {
247
+ const type = schema.type;
248
+ if (typeof type === "string") {
249
+ if (type === "null")
250
+ return schema;
251
+ return { ...schema, type: [type, "null"] };
252
+ }
253
+ if (Array.isArray(type) && !type.includes("null")) {
254
+ return { ...schema, type: [...type, "null"] };
255
+ }
256
+ if (Array.isArray(schema.anyOf)) {
257
+ return { ...schema, anyOf: [...schema.anyOf, { type: "null" }] };
258
+ }
259
+ return { anyOf: [schema, { type: "null" }] };
260
+ }
261
+ function legacyVariants(schema) {
262
+ if (Array.isArray(schema.variants))
263
+ return schema.variants;
264
+ if (Array.isArray(schema.anyOf))
265
+ return schema.anyOf;
266
+ return [];
267
+ }
268
+ function convertLegacySchema(source, kind, seen) {
269
+ if (kind === "optional")
270
+ return schemaValue(source.wrapped ?? source.inner, seen);
271
+ if (kind === "nullable") {
272
+ const wrapped = schemaValue(source.wrapped ?? source.inner, seen);
273
+ return typeof wrapped === "boolean" ? wrapped : makeNullable(wrapped);
274
+ }
275
+ if (kind === "undefined" || kind === "never" || kind === "any" || kind === "unknown")
276
+ return {};
277
+ if (kind === "union" || kind === "intersect" || kind === "intersection") {
278
+ const variants = legacyVariants(source)
279
+ .map((variant) => schemaValue(variant, seen))
280
+ .filter((variant) => isSchemaValue(variant) &&
281
+ (typeof variant === "boolean" || Object.keys(variant).length > 0));
282
+ if (variants.length === 0)
283
+ return copySchemaObject(source, seen, true);
284
+ if (variants.length === 1)
285
+ return variants[0] ?? {};
286
+ const out = copySchemaObject(source, seen, true);
287
+ if (typeof out !== "boolean")
288
+ out[kind === "union" ? "anyOf" : "allOf"] = variants;
289
+ return out;
290
+ }
291
+ if (kind === "object") {
292
+ const converted = copySchemaObject(source, seen, true, "object");
293
+ if (typeof converted === "boolean")
294
+ return converted;
295
+ const out = converted;
296
+ const sourceProperties = isRecord(source.properties) ? source.properties : undefined;
297
+ if (!sourceProperties)
298
+ return out;
299
+ const properties = {};
300
+ const optional = stringArray(source.optional) ?? [];
301
+ for (const [key, value] of Object.entries(sourceProperties)) {
302
+ setSchemaProperty(properties, key, schemaValue(value, seen));
303
+ }
304
+ out.properties = properties;
305
+ const explicitRequired = stringArray(source.required);
306
+ const required = explicitRequired ??
307
+ Object.entries(sourceProperties)
308
+ .filter(([key, value]) => !optional.includes(key) && !isOptionalSchema(value))
309
+ .map(([key]) => key);
310
+ if (required.length > 0)
311
+ out.required = required;
312
+ else
313
+ delete out.required;
314
+ return out;
315
+ }
316
+ if (kind === "array") {
317
+ const converted = copySchemaObject(source, seen, true, "array");
318
+ if (typeof converted === "boolean")
319
+ return converted;
320
+ const out = converted;
321
+ if (!("items" in source) && "element" in source)
322
+ out.items = schemaValue(source.element, seen);
323
+ return out;
324
+ }
325
+ if (kind === "enum") {
326
+ const converted = copySchemaObject(source, seen, true);
327
+ if (typeof converted === "boolean")
328
+ return converted;
329
+ const out = converted;
330
+ if (!("enum" in out) && Array.isArray(source.values))
331
+ out.enum = source.values;
332
+ return out;
333
+ }
334
+ if (kind === "literal") {
335
+ const converted = copySchemaObject(source, seen, true);
336
+ if (typeof converted === "boolean")
337
+ return converted;
338
+ const out = converted;
339
+ if (!("const" in out) && "value" in source)
340
+ out.const = source.value;
341
+ return out;
342
+ }
343
+ const scalarType = kind === "string" ||
344
+ kind === "number" ||
345
+ kind === "boolean" ||
346
+ kind === "integer" ||
347
+ kind === "null"
348
+ ? kind
349
+ : undefined;
350
+ return scalarType ? copySchemaObject(source, seen, true, scalarType) : {};
351
+ }
352
+ function convertSchema(source, seen) {
353
+ if (seen.has(source))
354
+ return {};
355
+ seen.add(source);
356
+ try {
357
+ const kind = legacyKind(source);
358
+ if (kind)
359
+ return convertLegacySchema(source, kind, seen);
360
+ if (!looksLikeJsonSchema(source))
361
+ return {};
362
+ return copySchemaObject(source, seen, false);
363
+ }
364
+ finally {
365
+ seen.delete(source);
366
+ }
367
+ }
368
+ export function toJsonSchema(schema) {
369
+ if (typeof schema === "boolean")
370
+ return schema;
371
+ if (!isRecord(schema))
372
+ return {};
373
+ return convertSchema(schema, new WeakSet());
374
+ }
@@ -0,0 +1,9 @@
1
+ export type CommandCodeInputType = "text" | "image";
2
+ /**
3
+ * Model input modalities from the command-code@1.15.1 bundled catalog.
4
+ * Models omitted here remain text-only so newly discovered IDs never claim
5
+ * image support without upstream evidence.
6
+ */
7
+ export declare const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>>;
8
+ export declare function inputModalitiesForModel(modelId: string): readonly CommandCodeInputType[];
9
+ export declare function modelSupportsImageInput(modelId: string): boolean;