impel-cli 0.20.38 → 0.20.40

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,103 @@
1
+ // The single model registry shared by every managed Codex surface. This
2
+ // module must stay a leaf (importing only other leaf modules such as
3
+ // runtimeBrand.js) so both the desktop app generation in apps.js and the CLI
4
+ // tenant profiles in cliProfiles.js can consume it without an import cycle —
5
+ // the 0.20.39 regression happened precisely because the CLI carried its own
6
+ // hand-frozen copy of this data.
7
+ import { RUNTIME_BRAND } from "./runtimeBrand.js";
8
+
9
+ const STANDARD_REASONING_LEVELS = [
10
+ { effort: "low", description: "Fast responses with lighter reasoning" },
11
+ { effort: "medium", description: "Balances speed and reasoning depth for everyday tasks" },
12
+ { effort: "high", description: "Greater reasoning depth for complex problems" },
13
+ { effort: "xhigh", description: "Extra high reasoning depth for complex problems" },
14
+ ];
15
+ const MAX_REASONING_LEVEL = { effort: "max", description: "Maximum reasoning depth for the hardest problems" };
16
+ const ULTRA_REASONING_LEVEL = { effort: "ultra", description: "Maximum reasoning with automatic task delegation" };
17
+ const FAST_SERVICE_TIER = { id: "priority", name: "Fast", description: "1.5x speed, increased usage" };
18
+
19
+ export const FALLBACK_MODELS = [
20
+ { id: "claude-opus-5", provider: "claude", display_name: "Claude Opus 5", family: "opus", family_default: true, default: true, supports_1m: true, context_window: 1000000 },
21
+ { id: "claude-opus-4-8", provider: "claude", display_name: "Claude Opus 4.8", family: "opus", supports_1m: true, context_window: 1000000 },
22
+ { id: "claude-sonnet-5", provider: "claude", display_name: "Claude Sonnet 5", family: "sonnet", family_default: true, context_window: 200000 },
23
+ { id: "claude-sonnet-4-6", provider: "claude", display_name: "Claude Sonnet 4.6", family: "sonnet", context_window: 200000 },
24
+ { id: "claude-haiku-4-5", provider: "claude", display_name: "Claude Haiku 4.5", family: "haiku", family_default: true, context_window: 200000 },
25
+ { id: "claude-fable-5", provider: "claude", display_name: "Claude Fable 5", family: "fable", family_default: true, context_window: 200000 },
26
+ { id: "gpt-5.6-sol", provider: "codex", display_name: "GPT-5.6-Sol", description: "Latest frontier agentic coding model.", default: true, priority: 1, default_reasoning_level: "medium", supported_reasoning_levels: [...STANDARD_REASONING_LEVELS, MAX_REASONING_LEVEL, ULTRA_REASONING_LEVEL], service_tiers: [FAST_SERVICE_TIER], context_window: 272000, max_context_window: 272000, use_responses_lite: true, tool_mode: "code_mode_only", multi_agent_version: "v2" },
27
+ { id: "gpt-5.6-terra", provider: "codex", display_name: "GPT-5.6-Terra", description: "Balanced agentic coding model for everyday work.", priority: 2, default_reasoning_level: "medium", supported_reasoning_levels: [...STANDARD_REASONING_LEVELS, MAX_REASONING_LEVEL, ULTRA_REASONING_LEVEL], service_tiers: [FAST_SERVICE_TIER], context_window: 272000, max_context_window: 272000, use_responses_lite: true, tool_mode: "code_mode_only", multi_agent_version: "v2" },
28
+ { id: "gpt-5.6-luna", provider: "codex", display_name: "GPT-5.6-Luna", description: "Fast and affordable agentic coding model.", priority: 3, default_reasoning_level: "medium", supported_reasoning_levels: [...STANDARD_REASONING_LEVELS, MAX_REASONING_LEVEL], service_tiers: [FAST_SERVICE_TIER], context_window: 272000, max_context_window: 272000, use_responses_lite: true, tool_mode: "code_mode_only", multi_agent_version: "v1" },
29
+ { id: "gpt-5.5", provider: "codex", display_name: "GPT-5.5", description: "Frontier model for complex coding, research, and real-world work.", priority: 7, default_reasoning_level: "medium", supported_reasoning_levels: STANDARD_REASONING_LEVELS, service_tiers: [FAST_SERVICE_TIER], context_window: 272000, max_context_window: 272000, use_responses_lite: false, tool_mode: null, multi_agent_version: null },
30
+ { id: "gpt-5.4", provider: "codex", display_name: "GPT-5.4", description: "Strong model for everyday coding.", priority: 16, default_reasoning_level: "medium", supported_reasoning_levels: STANDARD_REASONING_LEVELS, service_tiers: [FAST_SERVICE_TIER], context_window: 272000, max_context_window: 1000000, use_responses_lite: false, tool_mode: null, multi_agent_version: null },
31
+ { id: "gpt-5.4-mini", provider: "codex", display_name: "GPT-5.4-Mini", description: "Small, fast, and cost-efficient model for simpler coding tasks.", priority: 23, default_reasoning_level: "medium", supported_reasoning_levels: STANDARD_REASONING_LEVELS, service_tiers: [], context_window: 272000, max_context_window: 272000, use_responses_lite: false, tool_mode: null, multi_agent_version: null },
32
+ { id: "gpt-5.3-codex-spark", provider: "codex", display_name: "GPT-5.3-Codex-Spark", description: "Ultra-fast coding model.", priority: 26, default_reasoning_level: "high", supported_reasoning_levels: STANDARD_REASONING_LEVELS, service_tiers: [], context_window: 128000, max_context_window: 128000, use_responses_lite: false, tool_mode: null, multi_agent_version: null },
33
+ ];
34
+
35
+ export function codexCatalogEntry(model, index, vendorModel) {
36
+ const generated = {
37
+ slug: model.id,
38
+ display_name: model.display_name || model.id,
39
+ description: `Available through the Impel gateway (${model.id}).`,
40
+ default_reasoning_level: "medium",
41
+ supported_reasoning_levels: STANDARD_REASONING_LEVELS,
42
+ shell_type: "shell_command",
43
+ visibility: "list",
44
+ supported_in_api: true,
45
+ priority: index + 1,
46
+ base_instructions: "You are Codex, an AI coding agent. Follow repository instructions, collaborate with the user, make scoped changes, and verify your work.",
47
+ include_skills_usage_instructions: false,
48
+ supports_reasoning_summaries: true,
49
+ default_reasoning_summary: "none",
50
+ support_verbosity: true,
51
+ default_verbosity: "low",
52
+ apply_patch_tool_type: "freeform",
53
+ web_search_tool_type: "text_and_image",
54
+ truncation_policy: { mode: "tokens", limit: 10000 },
55
+ context_window: model.context_window || 200000,
56
+ max_context_window: model.context_window || 200000,
57
+ effective_context_window_percent: 95,
58
+ experimental_supported_tools: [],
59
+ input_modalities: ["text", "image"],
60
+ supports_parallel_tool_calls: true,
61
+ supports_image_detail_original: true,
62
+ supports_search_tool: true,
63
+ use_responses_lite: false,
64
+ tool_mode: null,
65
+ multi_agent_version: null,
66
+ };
67
+ const entry = vendorModel ? { ...generated, ...vendorModel } : generated;
68
+ return {
69
+ ...entry,
70
+ slug: model.id,
71
+ display_name: model.display_name || entry.display_name || model.id,
72
+ description: model.description || entry.description,
73
+ default_reasoning_level: model.default_reasoning_level || entry.default_reasoning_level,
74
+ supported_reasoning_levels: Array.isArray(model.supported_reasoning_levels)
75
+ ? model.supported_reasoning_levels
76
+ : entry.supported_reasoning_levels,
77
+ service_tiers: Array.isArray(model.service_tiers) ? model.service_tiers : (entry.service_tiers || []),
78
+ visibility: "list",
79
+ supported_in_api: true,
80
+ priority: Number.isInteger(model.priority) ? model.priority : (entry.priority || index + 1),
81
+ context_window: model.context_window || entry.context_window || 200000,
82
+ max_context_window: model.max_context_window || entry.max_context_window || model.context_window || 200000,
83
+ ...(typeof model.use_responses_lite === "boolean" ? { use_responses_lite: model.use_responses_lite } : {}),
84
+ ...(Object.hasOwn(model, "tool_mode") ? { tool_mode: model.tool_mode } : {}),
85
+ ...(Object.hasOwn(model, "multi_agent_version") ? { multi_agent_version: model.multi_agent_version } : {}),
86
+ };
87
+ }
88
+
89
+ /**
90
+ * The managed CLI Codex catalog, projected from the same model registry the
91
+ * desktop app uses so reasoning efforts, service tiers, and tool modes can
92
+ * never drift between the two surfaces. CLI profiles pin this file to keep
93
+ * launches offline; vendor enrichment stays a desktop-only concern.
94
+ */
95
+ export function managedCodexCliCatalog(now = new Date()) {
96
+ return {
97
+ fetched_at: now.toISOString(),
98
+ client_version: `${RUNTIME_BRAND.cli.command}-managed-2`,
99
+ models: FALLBACK_MODELS
100
+ .filter((model) => model.provider === "codex")
101
+ .map((model, index) => codexCatalogEntry(model, index)),
102
+ };
103
+ }
@@ -445,10 +445,18 @@ function resumedHandleForState(state, incomingHandle) {
445
445
  function normalizeHandle(value) {
446
446
  if (value?.schema === NATIVE_AGENT_CONTINUATION_SCHEMA) {
447
447
  exactObject(value, ["schema", "invocationId"], "native-agent continuation");
448
- if (typeof value.invocationId !== "string" || !SAFE_INVOCATION_RE.test(value.invocationId)) {
449
- throw new Error("native-agent continuation invocationId is invalid");
448
+ // Minted ids are lowercase UUIDs; tolerate a case-drifting reserialization
449
+ // but nothing that changes the id's shape.
450
+ const invocationId = typeof value.invocationId === "string"
451
+ ? value.invocationId.toLowerCase()
452
+ : null;
453
+ if (!invocationId || !SAFE_INVOCATION_RE.test(invocationId)) {
454
+ throw new Error(
455
+ "native-agent continuation invocationId is invalid; pass the exact continuation object "
456
+ + "returned by the answer call, or call the answer tool again if none was returned",
457
+ );
450
458
  }
451
- return { ...value };
459
+ return { ...value, invocationId };
452
460
  }
453
461
  exactObject(value, [
454
462
  "schema",
@@ -1854,7 +1862,7 @@ export class NativeAgentCompositeTransport {
1854
1862
  }
1855
1863
  }
1856
1864
 
1857
- async answer(args, { signal } = {}) {
1865
+ async answer(args, { signal, onProgress } = {}) {
1858
1866
  throwIfAborted(signal);
1859
1867
  const attachment = boundedAttachmentSignal(signal, this.attachmentWindowMs);
1860
1868
  let state = null;
@@ -1880,6 +1888,7 @@ export class NativeAgentCompositeTransport {
1880
1888
  if (!lock) return continuationForState(state);
1881
1889
  const result = await this.attach(state, prepared, {
1882
1890
  signal: attachment.signal,
1891
+ onProgress,
1883
1892
  lock,
1884
1893
  });
1885
1894
  if (result?.schema === NATIVE_AGENT_RESULT_SCHEMA && result.status === "succeeded") {
@@ -15,10 +15,8 @@ export async function preparePlatformClis(options = {}, dependencies = {}) {
15
15
  claude: find("claude", environment, platform),
16
16
  codex: find("codex", environment, platform),
17
17
  };
18
- const claudeProfile = (dependencies.ensureClaudeProfile || ensureImpelClaudeProfile)(options.gatewayUrl, options.tenantId);
19
- const codexProfile = (dependencies.ensureCodexProfile || ensureImpelCodexProfile)(options.gatewayUrl, options.tenantId);
20
18
  const missingAfter = Object.entries(binaries).filter(([, binary]) => !binary).map(([tool]) => tool);
21
- return {
19
+ const result = {
22
20
  binaries,
23
21
  missingBefore: [...missingAfter],
24
22
  missingAfter,
@@ -27,6 +25,14 @@ export async function preparePlatformClis(options = {}, dependencies = {}) {
27
25
  installFailure: null,
28
26
  installations: {},
29
27
  installCommands: {},
28
+ profiles: null,
29
+ };
30
+ // Discovery-only probes must not write profiles either.
31
+ if (options.inspectOnly) return result;
32
+ const claudeProfile = (dependencies.ensureClaudeProfile || ensureImpelClaudeProfile)(options.gatewayUrl, options.tenantId);
33
+ const codexProfile = (dependencies.ensureCodexProfile || ensureImpelCodexProfile)(options.gatewayUrl, options.tenantId);
34
+ return {
35
+ ...result,
30
36
  profiles: { claude: claudeProfile.configDir, codex: codexProfile.codexHome },
31
37
  };
32
38
  }
@@ -111,16 +111,45 @@ export function findReviewedVendorCliBinary(
111
111
  {
112
112
  find = findNativeBinary,
113
113
  verify = verifyReviewedMacVendorCli,
114
+ realpath = fs.realpathSync,
115
+ stat = fs.statSync,
114
116
  } = {},
115
117
  ) {
116
118
  if (platform !== "darwin") return find(tool, environment, platform, environmentPrefix);
117
- return find(
118
- tool,
119
- environment,
120
- platform,
121
- environmentPrefix,
122
- (binary) => verify(tool, binary, environment, { environmentPrefix }),
123
- );
119
+ const accept = (binary) => verify(tool, binary, environment, { environmentPrefix });
120
+ const reviewed = find(tool, environment, platform, environmentPrefix, accept);
121
+ if (reviewed || tool !== "claude") return reviewed;
122
+ // An explicit override remains authoritative even while unusable: callers
123
+ // asked for exactly that binary, so never substitute another install.
124
+ const override = vendorOverrideName(tool, environmentPrefix);
125
+ if (override && environmentValue(environment, override)) return null;
126
+
127
+ // The standalone Claude installer keeps releases side by side under
128
+ // versions/<release> and its auto-updater only moves the launcher symlink.
129
+ // When the symlink drifts past the reviewed release, resolve the exact
130
+ // reviewed release directly instead of failing the launch; every candidate
131
+ // still passes the full version + signature + team verification.
132
+ const candidates = [];
133
+ const drifted = find(tool, environment, platform, environmentPrefix);
134
+ if (drifted) {
135
+ try {
136
+ candidates.push(path.join(path.dirname(realpath(drifted)), PINNED_VENDOR_CLI_VERSIONS.claude));
137
+ } catch {
138
+ // An unresolvable shim only forfeits the sibling candidate.
139
+ }
140
+ }
141
+ const home = environmentValue(environment, "HOME");
142
+ if (home) {
143
+ candidates.push(path.join(home, ".local", "share", "claude", "versions", PINNED_VENDOR_CLI_VERSIONS.claude));
144
+ }
145
+ for (const candidate of candidates) {
146
+ try {
147
+ if (stat(candidate).isFile() && accept(candidate)) return candidate;
148
+ } catch {
149
+ // A missing side-by-side release keeps resolution fail-closed.
150
+ }
151
+ }
152
+ return null;
124
153
  }
125
154
 
126
155
  /**
@@ -1,5 +1,13 @@
1
+ import { CODEX_NATIVE_AGENT_ATTACHMENT_WINDOW_MS } from "./selfInvocation.js";
2
+
1
3
  export const VERBATIM_RELAY_OPT_IN_MARKER = "verbatimRelay enabled";
2
4
 
5
+ // The parent's single blocking wait must outlast the Codex child's bounded
6
+ // native-agent attachment window or the parent's visible working span
7
+ // truncates while the child is still running. Deriving it keeps the two
8
+ // contracts linked when the window moves again.
9
+ export const VERBATIM_RELAY_PARENT_WAIT_MS = CODEX_NATIVE_AGENT_ATTACHMENT_WINDOW_MS + 20_000;
10
+
3
11
  export const VERBATIM_SPAWN_REQUIREMENT = 'fork_turns="none"';
4
12
 
5
13
  export const VERBATIM_FINAL_TEXT_CONSTRAINTS =
@@ -14,7 +22,7 @@ export function parentVerbatimRelayAppendix() {
14
22
  `When an explicit custom agent's catalog-derived description declares ${VERBATIM_RELAY_OPT_IN_MARKER}, ` +
15
23
  `spawn it with ${VERBATIM_SPAWN_REQUIREMENT} and relay its finalText verbatim with ${VERBATIM_FINAL_TEXT_CONSTRAINTS}; ` +
16
24
  "preserve Sources sections and citations exactly. Start one child, then use one blocking wait sized for the child budget; " +
17
- "on Codex, call wait_agent exactly once with timeout_ms=60000. Do not send follow-ups, short-poll the child, or synthesize while it is running. " +
25
+ `on Codex, call wait_agent exactly once with timeout_ms=${VERBATIM_RELAY_PARENT_WAIT_MS}. Do not send follow-ups, short-poll the child, or synthesize while it is running. ` +
18
26
  "Custom agents without that declaration keep the default delegation behavior."
19
27
  );
20
28
  }
@@ -121,6 +121,7 @@ export async function prepareWindowsClis({
121
121
  gatewayUrl,
122
122
  tenantId,
123
123
  skipInstall = false,
124
+ inspectOnly = false,
124
125
  installTools = ["claude", "codex"],
125
126
  } = {}, dependencies = {}) {
126
127
  const io = {
@@ -149,6 +150,25 @@ export async function prepareWindowsClis({
149
150
  throw new Error("installTools must contain unique supported Windows CLI names");
150
151
  }
151
152
 
153
+ // A pure discovery probe: recovery goals and health checks re-evaluate
154
+ // this repeatedly, so it must never download git, install anything, write
155
+ // profiles, or sync skills — only report what is currently discoverable.
156
+ // This short-circuit MUST stay ahead of the git provisioning below, which
157
+ // performs an unprompted network install when git is absent.
158
+ if (inspectOnly) {
159
+ return {
160
+ binaries: before,
161
+ missingBefore,
162
+ missingAfter: [...missingBefore],
163
+ installAttempted: false,
164
+ installSucceeded: null,
165
+ installFailure: null,
166
+ installations: {},
167
+ installCommands: {},
168
+ profiles: null,
169
+ };
170
+ }
171
+
152
172
  // Both vendor CLIs need `git` to install the skills marketplace, and the
153
173
  // target machines are brand new — provision the Impel-managed MinGit before
154
174
  // any skill sync, on every setup/update pass (idempotent once installed).
@@ -1,56 +0,0 @@
1
- const LEVELS = ["low", "medium", "high", "xhigh"].map((effort) => ({
2
- effort,
3
- description: `${effort} reasoning effort`,
4
- }));
5
-
6
- const MODELS = [
7
- ["gpt-5.6-sol", "GPT-5.6-Sol", 1, "medium", "code_mode_only", "v2"],
8
- ["gpt-5.6-terra", "GPT-5.6-Terra", 2, "medium", "code_mode_only", "v2"],
9
- ["gpt-5.6-luna", "GPT-5.6-Luna", 3, "medium", "code_mode_only", "v1"],
10
- ["gpt-5.5", "GPT-5.5", 7, "medium", null, null],
11
- ["gpt-5.4", "GPT-5.4", 16, "medium", null, null],
12
- ["gpt-5.4-mini", "GPT-5.4-Mini", 23, "medium", null, null],
13
- ["gpt-5.3-codex-spark", "GPT-5.3-Codex-Spark", 26, "high", null, null],
14
- ];
15
-
16
- // Reviewed static projection of the exact pinned Codex 0.146 catalog. Keeping
17
- // it process-local prevents the client from issuing unsupported online model
18
- // refreshes during every managed launch.
19
- export function managedCodexCliCatalog(now = new Date()) {
20
- return {
21
- fetched_at: now.toISOString(),
22
- client_version: "impel-managed-codex-0.146",
23
- models: MODELS.map(([slug, displayName, priority, effort, toolMode, multiAgentVersion]) => ({
24
- slug,
25
- display_name: displayName,
26
- description: `Available through the Impel gateway (${slug}).`,
27
- default_reasoning_level: effort,
28
- supported_reasoning_levels: LEVELS,
29
- service_tiers: [],
30
- shell_type: "shell_command",
31
- visibility: "list",
32
- supported_in_api: true,
33
- priority,
34
- base_instructions: "You are Codex, an AI coding agent. Follow repository instructions and verify your work.",
35
- include_skills_usage_instructions: false,
36
- supports_reasoning_summaries: true,
37
- default_reasoning_summary: "none",
38
- support_verbosity: true,
39
- default_verbosity: "low",
40
- apply_patch_tool_type: "freeform",
41
- web_search_tool_type: "text_and_image",
42
- truncation_policy: { mode: "tokens", limit: 10_000 },
43
- context_window: slug === "gpt-5.3-codex-spark" ? 128_000 : 272_000,
44
- max_context_window: slug === "gpt-5.4" ? 1_000_000 : (slug === "gpt-5.3-codex-spark" ? 128_000 : 272_000),
45
- effective_context_window_percent: 95,
46
- experimental_supported_tools: [],
47
- input_modalities: ["text", "image"],
48
- supports_parallel_tool_calls: true,
49
- supports_image_detail_original: true,
50
- supports_search_tool: true,
51
- use_responses_lite: slug.startsWith("gpt-5.6-"),
52
- tool_mode: toolMode,
53
- multi_agent_version: multiAgentVersion,
54
- })),
55
- };
56
- }