@velum-labs/routekit-tool-codex 0.17.2 → 0.17.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/launch.d.ts CHANGED
@@ -24,7 +24,7 @@ export declare function hasCodexLogin(home?: string): boolean;
24
24
  */
25
25
  export declare function createIsolatedCodexHome(prefix: string, env?: Record<string, string | undefined>): string;
26
26
  export declare function codexListedStockSlugs(home?: string): string[];
27
- export declare function codexCatalogEntries(spec: Pick<ToolLaunchSpec, "defaultModel" | "models">, template: CodexModelPreset, stockModels?: readonly CodexModelPreset[], options?: {
27
+ export declare function codexCatalogEntries(spec: Pick<ToolLaunchSpec, "defaultModel" | "modelSelection" | "models">, template: CodexModelPreset, stockModels?: readonly CodexModelPreset[], options?: {
28
28
  appendUnlistedStock?: boolean;
29
29
  }): Record<string, unknown>[];
30
30
  export declare function codexModelCatalogJson(spec: Pick<ToolLaunchSpec, "defaultModel" | "models">, template: CodexModelPreset, stockModels?: readonly CodexModelPreset[], options?: {
package/dist/launch.js CHANGED
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { homedir, tmpdir } from "node:os";
4
4
  import { isAbsolute, join } from "node:path";
5
- import { isCodexPickerEligibleModel, reasoningEffortDescriptors } from "@velum-labs/routekit-contracts";
5
+ import { codexCompatibility, isCodexPickerEligibleModel, reasoningEffortDescriptors } from "@velum-labs/routekit-contracts";
6
6
  import { trimTrailingSlashes } from "@velum-labs/routekit-runtime";
7
7
  import { stringify as tomlStringify } from "smol-toml";
8
8
  const PROVIDER_ID = "routekit";
@@ -88,9 +88,33 @@ function codexModelId(modelId) {
88
88
  return modelId.startsWith("codex/") ? modelId.slice("codex/".length) : modelId;
89
89
  }
90
90
  function catalogModels(spec) {
91
- return spec.models.filter((model) => model.id === spec.defaultModel ||
92
- model.aliases?.includes(spec.defaultModel) === true ||
93
- isCodexPickerEligibleModel(model));
91
+ return spec.models.filter((model) => {
92
+ const isDefault = model.id === spec.defaultModel ||
93
+ model.aliases?.includes(spec.defaultModel) === true;
94
+ if (spec.modelSelection === undefined) {
95
+ return isDefault || isCodexPickerEligibleModel(model);
96
+ }
97
+ if (isDefault && spec.modelSelection !== "implicit")
98
+ return true;
99
+ return codexCompatibility({
100
+ id: model.id,
101
+ ...(model.provider !== undefined ? { provider: model.provider } : {}),
102
+ ...(model.architecture !== undefined ? { architecture: model.architecture } : {}),
103
+ ...(model.supportedParameters !== undefined
104
+ ? { supportedParameters: model.supportedParameters }
105
+ : {}),
106
+ ...(model.features?.tools !== undefined
107
+ ? {
108
+ capabilities: {
109
+ tools: model.features.tools === "full"
110
+ ? "supported"
111
+ : model.features.tools
112
+ }
113
+ }
114
+ : {}),
115
+ ...(model.reasoning !== undefined ? { reasoning: model.reasoning } : {})
116
+ }).status === "compatible";
117
+ });
94
118
  }
95
119
  function catalogIds(spec) {
96
120
  return [
@@ -387,6 +411,13 @@ function spawnCodex(args, home, cwd, token, dependencies) {
387
411
  }
388
412
  /** Launch against the user's real Codex home; only generated catalog data is temporary. */
389
413
  export async function launchCodex(ctx, deps = {}) {
414
+ if (!ctx.spec.models.some((model) => model.id === ctx.spec.defaultModel)) {
415
+ throw new Error(`Codex startup model "${ctx.spec.defaultModel}" is absent from the advertised catalog`);
416
+ }
417
+ if (!catalogModels(ctx.spec).some((model) => model.id === ctx.spec.defaultModel ||
418
+ model.aliases?.includes(ctx.spec.defaultModel) === true)) {
419
+ throw new Error(`Codex startup model "${ctx.spec.defaultModel}" is not in the compatible model picker`);
420
+ }
390
421
  const home = resolveCodexHome(deps.env ?? process.env);
391
422
  const temp = mkdtempSync(join(tmpdir(), "rk-codex-"));
392
423
  ctx.registerDisposer(() => rmSync(temp, { recursive: true, force: true }));
@@ -3,7 +3,7 @@ import { existsSync, mkdtempSync, rmSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { test } from "node:test";
6
- import { codexAgentRoleToml, codexCatalogEntries, codexLaunchConfigToml, codexModelCatalogJson, codexPersistentModelCatalogJson, createIsolatedCodexHome, resolveCodexHome } from "../launch.js";
6
+ import { codexAgentRoleToml, codexCatalogEntries, codexLaunchConfigToml, codexModelCatalogJson, codexPersistentModelCatalogJson, createIsolatedCodexHome, launchCodex, resolveCodexHome } from "../launch.js";
7
7
  const SPEC = {
8
8
  gatewayUrl: "http://127.0.0.1:9999",
9
9
  defaultModel: "opaque-primary",
@@ -71,6 +71,72 @@ test("Codex launcher filters incompatible OpenRouter models and aliases", () =>
71
71
  };
72
72
  assert.deepEqual(codexCatalogEntries(spec, { slug: "stock" }, [], { appendUnlistedStock: false }).map((entry) => entry.slug), ["openai/unknown", "openrouter/reasoning", "reasoning-alias", "openai-alias"]);
73
73
  });
74
+ test("implicit Codex picker excludes embedding models and keeps the selected generation model", () => {
75
+ const spec = {
76
+ gatewayUrl: "http://127.0.0.1:9999",
77
+ defaultModel: "openai/a-generation",
78
+ modelSelection: "implicit",
79
+ models: [
80
+ {
81
+ id: "openai/text-embedding-ada-002",
82
+ provider: "openai",
83
+ architecture: {
84
+ inputModalities: ["text"],
85
+ outputModalities: ["embeddings"]
86
+ }
87
+ },
88
+ {
89
+ id: "openai/a-generation",
90
+ provider: "openai",
91
+ architecture: {
92
+ inputModalities: ["text"],
93
+ outputModalities: ["text"]
94
+ },
95
+ supportedParameters: ["tools"]
96
+ }
97
+ ],
98
+ args: []
99
+ };
100
+ assert.deepEqual(codexCatalogEntries(spec, { slug: "stock" }, [], {
101
+ appendUnlistedStock: false
102
+ }).map((entry) => entry.slug), ["openai/a-generation"]);
103
+ });
104
+ test("Codex fails before passthrough or child spawn when an implicit default is incompatible", async () => {
105
+ let prepared = false;
106
+ let spawned = false;
107
+ await assert.rejects(launchCodex({
108
+ spec: {
109
+ gatewayUrl: "http://127.0.0.1:9999",
110
+ defaultModel: "openai/text-embedding-ada-002",
111
+ modelSelection: "implicit",
112
+ models: [
113
+ {
114
+ id: "openai/text-embedding-ada-002",
115
+ provider: "openai",
116
+ architecture: {
117
+ inputModalities: ["text"],
118
+ outputModalities: ["embeddings"]
119
+ }
120
+ }
121
+ ],
122
+ args: []
123
+ },
124
+ log: () => { },
125
+ prepareForPassthrough: () => {
126
+ prepared = true;
127
+ },
128
+ registerPort: () => "",
129
+ unregisterPort: () => { },
130
+ registerDisposer: () => { }
131
+ }, {
132
+ spawnProcess: (() => {
133
+ spawned = true;
134
+ throw new Error("must not spawn");
135
+ })
136
+ }), /not in the compatible model picker/);
137
+ assert.equal(prepared, false);
138
+ assert.equal(spawned, false);
139
+ });
74
140
  test("Codex launcher neutralizes stock-model behavior for gateway-routed models", () => {
75
141
  const template = {
76
142
  slug: "gpt-stock",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@velum-labs/routekit-tool-codex",
3
3
  "private": false,
4
- "version": "0.17.2",
4
+ "version": "0.17.4",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/velum-labs/routekit.git",
@@ -29,11 +29,11 @@
29
29
  "@openai/codex-sdk": "0.145.0",
30
30
  "smol-toml": "1.7.0",
31
31
  "zod": "4.4.3",
32
- "@velum-labs/routekit-contracts": "0.17.2",
33
- "@velum-labs/routekit-harness-core": "0.17.2",
34
- "@velum-labs/routekit-runtime": "0.17.2",
35
- "@velum-labs/routekit-tools": "0.17.2",
36
- "@velum-labs/routekit-registry": "0.17.2"
32
+ "@velum-labs/routekit-contracts": "0.17.4",
33
+ "@velum-labs/routekit-harness-core": "0.17.4",
34
+ "@velum-labs/routekit-registry": "0.17.4",
35
+ "@velum-labs/routekit-tools": "0.17.4",
36
+ "@velum-labs/routekit-runtime": "0.17.4"
37
37
  },
38
38
  "keywords": [
39
39
  "llm",