@golba98/codexa 1.0.2 → 1.0.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/README.md +396 -100
- package/bin/codexa.js +62 -144
- package/package.json +14 -8
- package/src/app.tsx +596 -306
- package/src/commands/handler.ts +6 -6
- package/src/config/buildInfo.ts +2 -2
- package/src/config/layeredConfig.ts +1 -1
- package/src/config/persistence.ts +10 -0
- package/src/config/runtimeConfig.ts +1 -1
- package/src/config/settings.ts +8 -16
- package/src/config/trustStore.ts +1 -1
- package/src/config/updateCheckCache.ts +19 -1
- package/src/core/README.md +52 -0
- package/src/core/agent/loop.ts +282 -0
- package/src/core/agent/protocol.ts +211 -0
- package/src/core/agent/tools.ts +414 -0
- package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
- package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
- package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
- package/src/core/debug/modelStateDebug.ts +34 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/codexExecutable.ts +1 -0
- package/src/core/executables/executableResolver.ts +65 -43
- package/src/core/perf/renderDebug.ts +10 -6
- package/src/core/process/processValidation.ts +9 -5
- package/src/core/providerLauncher/launcher.ts +59 -42
- package/src/core/providerLauncher/registry.ts +30 -14
- package/src/core/providerLauncher/types.ts +11 -9
- package/src/core/providerLauncher/workspaceConfig.ts +41 -26
- package/src/core/providerRuntime/anthropic.ts +7 -1
- package/src/core/providerRuntime/antigravity.ts +305 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +268 -22
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/contextMetadata.ts +12 -24
- package/src/core/providerRuntime/local.ts +129 -51
- package/src/core/providerRuntime/models.ts +22 -11
- package/src/core/providerRuntime/registry.ts +58 -31
- package/src/core/providerRuntime/types.ts +19 -14
- package/src/core/providers/codexSubprocess.ts +2 -2
- package/src/core/providers/types.ts +1 -1
- package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
- package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
- package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
- package/src/core/terminal/clearFrameBoundary.ts +814 -0
- package/src/core/terminal/frameLock.ts +109 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/terminalControl.ts +22 -0
- package/src/core/terminal/terminalTitle.ts +16 -102
- package/src/core/version/channel.ts +23 -0
- package/src/core/version/updateCheck.ts +193 -0
- package/src/core/{launchContext.ts → workspace/launchContext.ts} +32 -39
- package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
- package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
- package/src/headless/execRunner.ts +2 -2
- package/src/index.tsx +43 -89
- package/src/session/appSession.ts +10 -7
- package/src/session/chatLifecycle.ts +1 -1
- package/src/session/liveRenderScheduler.ts +1 -1
- package/src/session/types.ts +3 -2
- package/src/ui/ActionRequiredBlock.tsx +5 -5
- package/src/ui/ActivityBars.tsx +3 -3
- package/src/ui/ActivityIndicator.tsx +6 -6
- package/src/ui/AgentBlock.tsx +6 -6
- package/src/ui/AnimatedStatusText.tsx +1 -1
- package/src/ui/AppShell.tsx +670 -719
- package/src/ui/AttachmentImportPanel.tsx +8 -8
- package/src/ui/AuthPanel.tsx +20 -20
- package/src/ui/BottomComposer.tsx +158 -118
- package/src/ui/DashCard.tsx +3 -3
- package/src/ui/Markdown.tsx +17 -17
- package/src/ui/ModelPickerScreen.tsx +222 -42
- package/src/ui/ModelReasoningPicker.tsx +15 -15
- package/src/ui/Panel.tsx +3 -3
- package/src/ui/PlanActionPicker.tsx +6 -6
- package/src/ui/PlanReviewPanel.tsx +9 -9
- package/src/ui/ProviderPicker.tsx +735 -321
- package/src/ui/RunFooter.tsx +3 -3
- package/src/ui/RuntimeStatusBar.tsx +108 -0
- package/src/ui/SelectionPanel.tsx +8 -4
- package/src/ui/SettingsPanel.tsx +9 -9
- package/src/ui/Spinner.tsx +1 -1
- package/src/ui/TextEntryPanel.tsx +11 -11
- package/src/ui/ThinkingBlock.tsx +8 -8
- package/src/ui/Timeline.tsx +1625 -1472
- package/src/ui/TopHeader.tsx +437 -293
- package/src/ui/TranscriptShell.tsx +322 -0
- package/src/ui/TurnGroup.tsx +33 -33
- package/src/ui/UpdateAvailableCard.tsx +41 -0
- package/src/ui/UpdatePromptPanel.tsx +197 -0
- package/src/ui/focus.ts +3 -0
- package/src/ui/layout.ts +299 -25
- package/src/ui/layoutListWindow.ts +145 -0
- package/src/ui/logoVariants.ts +103 -0
- package/src/ui/modeDisplay.ts +12 -12
- package/src/ui/runtimeDisplay.ts +112 -0
- package/src/ui/textLayout.ts +15 -4
- package/src/ui/theme.tsx +274 -395
- package/src/ui/timelineMeasure.ts +218 -136
- package/scripts/audit-codexa-capabilities.mjs +0 -466
- package/scripts/gen-build-info.mjs +0 -33
- package/scripts/smoke-terminal-bench.mjs +0 -35
- package/src/appRenderStability.test.ts +0 -131
- package/src/commands/handler.test.ts +0 -655
- package/src/config/launchArgs.test.ts +0 -189
- package/src/config/layeredConfig.test.ts +0 -143
- package/src/config/persistence.test.ts +0 -114
- package/src/config/runtimeConfig.test.ts +0 -218
- package/src/config/settings.test.ts +0 -155
- package/src/config/trustStore.test.ts +0 -29
- package/src/core/attachments.test.ts +0 -155
- package/src/core/auth/codexAuth.test.ts +0 -68
- package/src/core/cleanupFastFail.test.ts +0 -76
- package/src/core/codex.ts +0 -124
- package/src/core/codexExecArgs.test.ts +0 -195
- package/src/core/codexLaunch.test.ts +0 -205
- package/src/core/codexPrompt.test.ts +0 -252
- package/src/core/executables/codexExecutable.test.ts +0 -212
- package/src/core/executables/executableResolver.test.ts +0 -129
- package/src/core/executables/geminiExecutable.test.ts +0 -116
- package/src/core/executables/pathSanityScan.test.ts +0 -47
- package/src/core/githubDiagnostics.test.ts +0 -92
- package/src/core/hollowResponseFormat.test.ts +0 -58
- package/src/core/launchContext.test.ts +0 -157
- package/src/core/models/codexCapabilities.test.ts +0 -45
- package/src/core/models/codexModelCapabilities.test.ts +0 -246
- package/src/core/models/modelSpecs.test.ts +0 -283
- package/src/core/perf/renderDebug.test.ts +0 -230
- package/src/core/planStorage.test.ts +0 -143
- package/src/core/process/CommandRunner.test.ts +0 -105
- package/src/core/projectInstructions.test.ts +0 -50
- package/src/core/providerLauncher/launcher.test.ts +0 -238
- package/src/core/providerLauncher/registry.test.ts +0 -324
- package/src/core/providerLauncher/workspaceConfig.test.ts +0 -638
- package/src/core/providerRuntime/anthropic.test.ts +0 -1120
- package/src/core/providerRuntime/capabilityProfile.test.ts +0 -311
- package/src/core/providerRuntime/contextMetadata.test.ts +0 -468
- package/src/core/providerRuntime/gemini.test.ts +0 -437
- package/src/core/providerRuntime/lmstudio.test.ts +0 -168
- package/src/core/providerRuntime/local.test.ts +0 -787
- package/src/core/providerRuntime/registry.test.ts +0 -233
- package/src/core/providers/codexJsonStream.test.ts +0 -148
- package/src/core/providers/codexSubprocess.test.ts +0 -68
- package/src/core/providers/codexTranscript.test.ts +0 -284
- package/src/core/terminal/startupClear.test.ts +0 -55
- package/src/core/terminal/terminalCapabilities.test.ts +0 -93
- package/src/core/terminal/terminalControl.test.ts +0 -75
- package/src/core/terminal/terminalSanitize.test.ts +0 -22
- package/src/core/terminal/terminalSelection.test.ts +0 -42
- package/src/core/terminal/terminalTitle.test.ts +0 -328
- package/src/core/updateCheck.test.ts +0 -194
- package/src/core/updateCheck.ts +0 -172
- package/src/core/workspaceActivity.test.ts +0 -163
- package/src/core/workspaceGuard.test.ts +0 -151
- package/src/core/workspaceRoot.test.ts +0 -23
- package/src/exec.test.ts +0 -13
- package/src/headless/execArgs.test.ts +0 -147
- package/src/headless/execRunner.test.ts +0 -436
- package/src/index.test.tsx +0 -620
- package/src/session/appSession.test.ts +0 -897
- package/src/session/chatLifecycle.test.ts +0 -64
- package/src/session/liveRenderScheduler.test.ts +0 -201
- package/src/session/planFlow.test.ts +0 -103
- package/src/session/planTranscript.test.ts +0 -65
- package/src/session/promptRunSchedule.test.ts +0 -36
- package/src/ui/ActivityIndicator.test.tsx +0 -58
- package/src/ui/AgentBlock.test.ts +0 -6
- package/src/ui/AnimatedStatusText.test.ts +0 -16
- package/src/ui/AppShell.test.tsx +0 -1776
- package/src/ui/AttachmentImportPanel.test.tsx +0 -204
- package/src/ui/BottomComposer.test.ts +0 -674
- package/src/ui/CodexLogo.tsx +0 -55
- package/src/ui/Markdown.test.ts +0 -157
- package/src/ui/ModelPickerProviderScope.test.tsx +0 -411
- package/src/ui/ModelPickerScreen.test.tsx +0 -99
- package/src/ui/ModelPickerState.test.tsx +0 -151
- package/src/ui/ModelReasoningPicker.test.tsx +0 -447
- package/src/ui/PlanReviewPanel.test.tsx +0 -267
- package/src/ui/PromptCardBorder.test.tsx +0 -161
- package/src/ui/ProviderPicker.test.tsx +0 -289
- package/src/ui/ProviderShortcut.test.tsx +0 -143
- package/src/ui/SettingsPanel.test.tsx +0 -233
- package/src/ui/StaticTranscriptItem.tsx +0 -56
- package/src/ui/Timeline.test.ts +0 -2067
- package/src/ui/TimelineNavigation.test.tsx +0 -201
- package/src/ui/TopHeader.test.tsx +0 -254
- package/src/ui/TurnGroup.test.tsx +0 -365
- package/src/ui/busyStatusAnimation.test.ts +0 -30
- package/src/ui/commandNormalize.test.ts +0 -142
- package/src/ui/diffRenderer.test.ts +0 -102
- package/src/ui/focusFlow.test.tsx +0 -1098
- package/src/ui/inputBuffer.test.ts +0 -151
- package/src/ui/layout.test.ts +0 -146
- package/src/ui/modeDisplay.test.ts +0 -42
- package/src/ui/runActivityView.test.ts +0 -89
- package/src/ui/runLifecycleView.test.tsx +0 -237
- package/src/ui/statusRenderIsolation.test.tsx +0 -654
- package/src/ui/terminalAnswerFormat.test.ts +0 -19
- package/src/ui/textLayout.test.ts +0 -18
- package/src/ui/themeFlow.test.ts +0 -53
- package/src/ui/timelineMeasureCache.test.ts +0 -986
- /package/src/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
- /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
- /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
- /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
- /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
- /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
import {
|
|
7
|
-
buildWorkspaceStatusMessage,
|
|
8
|
-
createWorkspaceRelaunchPlan,
|
|
9
|
-
guardWorkspaceRelaunch,
|
|
10
|
-
resolveLaunchContext,
|
|
11
|
-
} from "./launchContext.js";
|
|
12
|
-
import { normalizeWorkspaceRoot } from "./workspaceRoot.js";
|
|
13
|
-
|
|
14
|
-
function createTempWorkspace(): string {
|
|
15
|
-
return mkdtempSync(join(tmpdir(), "codexa-launch-context-"));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
test("uses installed launcher metadata when available", () => {
|
|
19
|
-
const context = resolveLaunchContext({
|
|
20
|
-
workspaceRoot: "C:/target",
|
|
21
|
-
packageRoot: "C:/repo",
|
|
22
|
-
execPath: "C:/Program Files/nodejs/node.exe",
|
|
23
|
-
env: {
|
|
24
|
-
CODEXA_LAUNCH_KIND: "installed-bin",
|
|
25
|
-
CODEXA_PACKAGE_ROOT: "C:/repo",
|
|
26
|
-
CODEXA_LAUNCHER_SCRIPT: "C:/repo/bin/codexa.js",
|
|
27
|
-
CODEXA_RELAUNCH_EXECUTABLE: "C:/Program Files/nodejs/node.exe",
|
|
28
|
-
CODEXA_RELAUNCH_ARGS: JSON.stringify(["C:/repo/bin/codexa.js", "--profile", "review"]),
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
assert.equal(context.launchKind, "installed-bin");
|
|
33
|
-
assert.equal(context.packageRoot, "C:\\repo");
|
|
34
|
-
assert.equal(context.launcherScriptPath, "C:/repo/bin/codexa.js");
|
|
35
|
-
assert.equal(context.relaunchExecutable, "C:/Program Files/nodejs/node.exe");
|
|
36
|
-
assert.deepEqual(context.relaunchArgs, ["C:/repo/bin/codexa.js", "--profile", "review"]);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("falls back to bun repo launch metadata when no installed launcher env exists", () => {
|
|
40
|
-
const context = resolveLaunchContext({
|
|
41
|
-
workspaceRoot: "C:/repo",
|
|
42
|
-
packageRoot: "C:/repo",
|
|
43
|
-
execPath: "C:/tools/bun.exe",
|
|
44
|
-
hasBunRuntime: true,
|
|
45
|
-
forwardArgs: ["--profile", "review", "--config", "model=\"gpt-5.4\""],
|
|
46
|
-
env: {},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
assert.equal(context.launchKind, "dev-run");
|
|
50
|
-
assert.equal(context.relaunchExecutable, "C:/tools/bun.exe");
|
|
51
|
-
assert.deepEqual(context.relaunchArgs, [
|
|
52
|
-
"run",
|
|
53
|
-
"--silent",
|
|
54
|
-
join("C:\\repo", "src", "index.tsx"),
|
|
55
|
-
"--",
|
|
56
|
-
"--profile",
|
|
57
|
-
"review",
|
|
58
|
-
"--config",
|
|
59
|
-
"model=\"gpt-5.4\"",
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("creates an installed-bin relaunch plan with normalized target cwd and env", () => {
|
|
64
|
-
const root = createTempWorkspace();
|
|
65
|
-
try {
|
|
66
|
-
const nextWorkspace = join(root, "next");
|
|
67
|
-
mkdirSync(nextWorkspace);
|
|
68
|
-
|
|
69
|
-
const context = resolveLaunchContext({
|
|
70
|
-
workspaceRoot: root,
|
|
71
|
-
packageRoot: "C:/repo",
|
|
72
|
-
execPath: "C:/Program Files/nodejs/node.exe",
|
|
73
|
-
env: {
|
|
74
|
-
CODEXA_LAUNCH_KIND: "installed-bin",
|
|
75
|
-
CODEXA_PACKAGE_ROOT: "C:/repo",
|
|
76
|
-
CODEXA_LAUNCHER_SCRIPT: "C:/repo/bin/codexa.js",
|
|
77
|
-
CODEXA_RELAUNCH_EXECUTABLE: "C:/Program Files/nodejs/node.exe",
|
|
78
|
-
CODEXA_RELAUNCH_ARGS: JSON.stringify(["C:/repo/bin/codexa.js", "--profile", "review"]),
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const result = createWorkspaceRelaunchPlan(".\\next", context, {});
|
|
83
|
-
assert.equal(result.ok, true);
|
|
84
|
-
if (!result.ok) return;
|
|
85
|
-
|
|
86
|
-
assert.equal(result.plan.executable, "C:/Program Files/nodejs/node.exe");
|
|
87
|
-
assert.deepEqual(result.plan.args, ["C:/repo/bin/codexa.js", "--profile", "review"]);
|
|
88
|
-
assert.equal(result.plan.cwd, normalizeWorkspaceRoot(nextWorkspace));
|
|
89
|
-
assert.equal(result.plan.env.CODEX_WORKSPACE_ROOT, normalizeWorkspaceRoot(nextWorkspace));
|
|
90
|
-
assert.equal(result.plan.env.CODEXA_LAUNCH_KIND, "installed-bin");
|
|
91
|
-
} finally {
|
|
92
|
-
rmSync(root, { recursive: true, force: true });
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("rejects relaunch targets that do not exist", () => {
|
|
97
|
-
const root = createTempWorkspace();
|
|
98
|
-
try {
|
|
99
|
-
const context = resolveLaunchContext({
|
|
100
|
-
workspaceRoot: root,
|
|
101
|
-
packageRoot: root,
|
|
102
|
-
execPath: "C:/tools/bun.exe",
|
|
103
|
-
hasBunRuntime: true,
|
|
104
|
-
env: {},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const result = createWorkspaceRelaunchPlan(".\\missing", context, {});
|
|
108
|
-
assert.equal(result.ok, false);
|
|
109
|
-
if (result.ok) return;
|
|
110
|
-
assert.match(result.message, /does not exist/i);
|
|
111
|
-
} finally {
|
|
112
|
-
rmSync(root, { recursive: true, force: true });
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("rejects relaunch targets that are files instead of directories", () => {
|
|
117
|
-
const root = createTempWorkspace();
|
|
118
|
-
try {
|
|
119
|
-
const fileTarget = join(root, "notes.txt");
|
|
120
|
-
writeFileSync(fileTarget, "hello", "utf8");
|
|
121
|
-
const context = resolveLaunchContext({
|
|
122
|
-
workspaceRoot: root,
|
|
123
|
-
packageRoot: root,
|
|
124
|
-
execPath: "C:/tools/bun.exe",
|
|
125
|
-
hasBunRuntime: true,
|
|
126
|
-
env: {},
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const result = createWorkspaceRelaunchPlan(".\\notes.txt", context, {});
|
|
130
|
-
assert.equal(result.ok, false);
|
|
131
|
-
if (result.ok) return;
|
|
132
|
-
assert.match(result.message, /not a directory/i);
|
|
133
|
-
} finally {
|
|
134
|
-
rmSync(root, { recursive: true, force: true });
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test("blocks workspace relaunch while busy", () => {
|
|
139
|
-
assert.deepEqual(guardWorkspaceRelaunch(false), { allowed: true });
|
|
140
|
-
assert.equal(guardWorkspaceRelaunch(true).allowed, false);
|
|
141
|
-
assert.match(guardWorkspaceRelaunch(true).message ?? "", /finish the current run/i);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test("describes dev launch mode with install guidance", () => {
|
|
145
|
-
const message = buildWorkspaceStatusMessage(resolveLaunchContext({
|
|
146
|
-
workspaceRoot: "C:/repo",
|
|
147
|
-
packageRoot: "C:/repo",
|
|
148
|
-
execPath: "C:/tools/bun.exe",
|
|
149
|
-
hasBunRuntime: true,
|
|
150
|
-
env: {},
|
|
151
|
-
}));
|
|
152
|
-
|
|
153
|
-
assert.match(message, /Launch mode: dev\/repo launch/i);
|
|
154
|
-
assert.match(message, /npm link/i);
|
|
155
|
-
assert.match(message, /where codexa/i);
|
|
156
|
-
assert.match(message, /\/workspace relaunch <path>/i);
|
|
157
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { parseCodexCliCapabilities } from "./codexCapabilities.js";
|
|
4
|
-
|
|
5
|
-
test("detects exec help flags and strips ANSI noise", () => {
|
|
6
|
-
const capabilities = parseCodexCliCapabilities(
|
|
7
|
-
"\u001B[32mOptions:\u001B[0m\n --ask-for-approval <policy>\n --sandbox <mode>\n -c, --config <key=value>\n",
|
|
8
|
-
"Global options:\n --ask-for-approval <policy>\n",
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
assert.deepEqual(capabilities, {
|
|
12
|
-
askForApproval: true,
|
|
13
|
-
sandbox: true,
|
|
14
|
-
config: true,
|
|
15
|
-
fullAuto: false,
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("does not promote top-level approval flags into exec capabilities", () => {
|
|
20
|
-
const capabilities = parseCodexCliCapabilities(
|
|
21
|
-
"Usage: codex exec [OPTIONS]\n --sandbox <mode>\n --full-auto\n",
|
|
22
|
-
"Global options:\n --ask-for-approval <policy>\n --sandbox <mode>\n --full-auto\n",
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
assert.deepEqual(capabilities, {
|
|
26
|
-
askForApproval: false,
|
|
27
|
-
sandbox: true,
|
|
28
|
-
config: false,
|
|
29
|
-
fullAuto: true,
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("requires exact option tokens instead of prose guesses", () => {
|
|
34
|
-
const capabilities = parseCodexCliCapabilities(
|
|
35
|
-
"This build documents ask-for-approval compatibility in prose only.\nIt also mentions sandbox_mode in config docs.\n",
|
|
36
|
-
"Use full auto mode for trusted environments.\n",
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
assert.deepEqual(capabilities, {
|
|
40
|
-
askForApproval: false,
|
|
41
|
-
sandbox: false,
|
|
42
|
-
config: false,
|
|
43
|
-
fullAuto: false,
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
clearCodexModelCapabilityCache,
|
|
5
|
-
createFallbackModelCapabilities,
|
|
6
|
-
findModelCapability,
|
|
7
|
-
formatModelCapabilitiesList,
|
|
8
|
-
getCodexModelCapabilities,
|
|
9
|
-
getPreferredModelFromCapabilities,
|
|
10
|
-
normalizeCodexModelListResponses,
|
|
11
|
-
normalizeReasoningForModelCapabilities,
|
|
12
|
-
} from "./codexModelCapabilities.js";
|
|
13
|
-
|
|
14
|
-
const SAMPLE_RESPONSE = {
|
|
15
|
-
data: [
|
|
16
|
-
{
|
|
17
|
-
id: "gpt-5.4",
|
|
18
|
-
model: "gpt-5.4",
|
|
19
|
-
displayName: "gpt-5.4",
|
|
20
|
-
description: "Frontier model",
|
|
21
|
-
hidden: false,
|
|
22
|
-
isDefault: true,
|
|
23
|
-
defaultReasoningEffort: "medium",
|
|
24
|
-
supportedReasoningEfforts: [
|
|
25
|
-
{ reasoningEffort: "low", description: "Fast" },
|
|
26
|
-
{ reasoningEffort: "medium", description: "Balanced" },
|
|
27
|
-
{ reasoningEffort: "high", description: "Deep" },
|
|
28
|
-
{ reasoningEffort: "xhigh", description: "Deepest" },
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: "gpt-5.1-codex-mini",
|
|
33
|
-
model: "gpt-5.1-codex-mini",
|
|
34
|
-
displayName: "Codex Mini",
|
|
35
|
-
description: "Small model",
|
|
36
|
-
hidden: false,
|
|
37
|
-
isDefault: false,
|
|
38
|
-
defaultReasoningEffort: "medium",
|
|
39
|
-
supportedReasoningEfforts: [
|
|
40
|
-
{ reasoningEffort: "medium", description: "Dynamic" },
|
|
41
|
-
{ reasoningEffort: "high", description: "Deep" },
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
nextCursor: null,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
test("normalizes model/list responses with per-model reasoning metadata", () => {
|
|
49
|
-
const capabilities = normalizeCodexModelListResponses([SAMPLE_RESPONSE], {
|
|
50
|
-
discoveredAt: 123,
|
|
51
|
-
executable: "codex.cmd",
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
assert.equal(capabilities.status, "ready");
|
|
55
|
-
assert.equal(capabilities.source, "runtime");
|
|
56
|
-
assert.equal(capabilities.executable, "codex.cmd");
|
|
57
|
-
assert.equal(capabilities.models.length, 2);
|
|
58
|
-
assert.equal(capabilities.models[0]?.model, "gpt-5.4");
|
|
59
|
-
assert.equal(capabilities.models[0]?.reasoningLevelCount, 4);
|
|
60
|
-
assert.deepEqual(capabilities.models[0]?.supportedReasoningLevels?.map((item) => item.id), [
|
|
61
|
-
"low",
|
|
62
|
-
"medium",
|
|
63
|
-
"high",
|
|
64
|
-
"xhigh",
|
|
65
|
-
]);
|
|
66
|
-
assert.equal(capabilities.models[1]?.label, "Codex Mini");
|
|
67
|
-
assert.equal(capabilities.models[1]?.reasoningLevelCount, 2);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("normalization rejects empty or malformed discovery output", () => {
|
|
71
|
-
assert.throws(
|
|
72
|
-
() => normalizeCodexModelListResponses([{ data: [] }]),
|
|
73
|
-
/no usable models/i,
|
|
74
|
-
);
|
|
75
|
-
assert.throws(
|
|
76
|
-
() => normalizeCodexModelListResponses([{ data: [{ id: "", model: "" }] }]),
|
|
77
|
-
/no usable models/i,
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("fallback keeps models but does not invent reasoning support", () => {
|
|
82
|
-
const capabilities = createFallbackModelCapabilities(new Error("offline"), {
|
|
83
|
-
discoveredAt: 456,
|
|
84
|
-
executable: "codex.cmd",
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
assert.equal(capabilities.status, "fallback");
|
|
88
|
-
assert.equal(capabilities.error, "offline");
|
|
89
|
-
assert.ok(capabilities.models.length > 0);
|
|
90
|
-
assert.equal(capabilities.models[0]?.available, false);
|
|
91
|
-
assert.equal(capabilities.models[0]?.supportedReasoningLevels, null);
|
|
92
|
-
assert.equal(capabilities.models[0]?.reasoningLevelCount, null);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("normalizes reasoning by keeping valid values and clamping invalid ones", () => {
|
|
96
|
-
const capabilities = normalizeCodexModelListResponses([SAMPLE_RESPONSE]);
|
|
97
|
-
|
|
98
|
-
assert.equal(
|
|
99
|
-
normalizeReasoningForModelCapabilities("gpt-5.4", "high", capabilities),
|
|
100
|
-
"high",
|
|
101
|
-
);
|
|
102
|
-
assert.equal(
|
|
103
|
-
normalizeReasoningForModelCapabilities("gpt-5.1-codex-mini", "xhigh", capabilities),
|
|
104
|
-
"medium",
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("handles unavailable selected model with runtime default", () => {
|
|
109
|
-
const capabilities = normalizeCodexModelListResponses([SAMPLE_RESPONSE]);
|
|
110
|
-
|
|
111
|
-
assert.equal(getPreferredModelFromCapabilities(capabilities, "missing-model"), "gpt-5.4");
|
|
112
|
-
assert.equal(findModelCapability(capabilities, "Codex Mini"), null);
|
|
113
|
-
assert.equal(findModelCapability(capabilities, "gpt-5.1-codex-mini")?.model, "gpt-5.1-codex-mini");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("formats model list with dynamic reasoning counts", () => {
|
|
117
|
-
const capabilities = normalizeCodexModelListResponses([SAMPLE_RESPONSE]);
|
|
118
|
-
const message = formatModelCapabilitiesList(capabilities, "gpt-5.1-codex-mini");
|
|
119
|
-
|
|
120
|
-
assert.match(message, /Detected from Codex runtime/i);
|
|
121
|
-
assert.match(message, /gpt-5\.4.*4 reasoning levels/i);
|
|
122
|
-
assert.match(message, /gpt-5\.1-codex-mini.*2 reasoning levels.*\*/i);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("caches successful discovery and refreshes when forced", async () => {
|
|
126
|
-
clearCodexModelCapabilityCache();
|
|
127
|
-
let calls = 0;
|
|
128
|
-
|
|
129
|
-
const discover = async () => {
|
|
130
|
-
calls += 1;
|
|
131
|
-
return normalizeCodexModelListResponses([
|
|
132
|
-
{
|
|
133
|
-
data: [
|
|
134
|
-
{
|
|
135
|
-
id: `model-${calls}`,
|
|
136
|
-
model: `model-${calls}`,
|
|
137
|
-
displayName: `Model ${calls}`,
|
|
138
|
-
hidden: false,
|
|
139
|
-
isDefault: true,
|
|
140
|
-
defaultReasoningEffort: "medium",
|
|
141
|
-
supportedReasoningEfforts: [{ reasoningEffort: "medium", description: "Default" }],
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
},
|
|
145
|
-
], { discoveredAt: calls, executable: "codex" });
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const first = await getCodexModelCapabilities({
|
|
149
|
-
executable: "codex",
|
|
150
|
-
discover,
|
|
151
|
-
now: () => 100,
|
|
152
|
-
ttlMs: 1000,
|
|
153
|
-
});
|
|
154
|
-
const cached = await getCodexModelCapabilities({
|
|
155
|
-
executable: "codex",
|
|
156
|
-
discover,
|
|
157
|
-
now: () => 200,
|
|
158
|
-
ttlMs: 1000,
|
|
159
|
-
});
|
|
160
|
-
const refreshed = await getCodexModelCapabilities({
|
|
161
|
-
executable: "codex",
|
|
162
|
-
discover,
|
|
163
|
-
forceRefresh: true,
|
|
164
|
-
now: () => 300,
|
|
165
|
-
ttlMs: 1000,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
assert.equal(calls, 2);
|
|
169
|
-
assert.equal(first.models[0]?.model, "model-1");
|
|
170
|
-
assert.equal(cached.models[0]?.model, "model-1");
|
|
171
|
-
assert.equal(refreshed.models[0]?.model, "model-2");
|
|
172
|
-
clearCodexModelCapabilityCache();
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test("concurrent callers share a single in-flight discovery promise", async () => {
|
|
176
|
-
clearCodexModelCapabilityCache();
|
|
177
|
-
let calls = 0;
|
|
178
|
-
let resolveDiscover: (value: unknown) => void = () => {};
|
|
179
|
-
const gate = new Promise<unknown>((resolve) => {
|
|
180
|
-
resolveDiscover = resolve;
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const discover = async () => {
|
|
184
|
-
calls += 1;
|
|
185
|
-
await gate;
|
|
186
|
-
return normalizeCodexModelListResponses([
|
|
187
|
-
{
|
|
188
|
-
data: [
|
|
189
|
-
{
|
|
190
|
-
id: "model-a",
|
|
191
|
-
model: "model-a",
|
|
192
|
-
displayName: "Model A",
|
|
193
|
-
hidden: false,
|
|
194
|
-
isDefault: true,
|
|
195
|
-
defaultReasoningEffort: "medium",
|
|
196
|
-
supportedReasoningEfforts: [{ reasoningEffort: "medium", description: "Default" }],
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
},
|
|
200
|
-
], { discoveredAt: 1, executable: "codex" });
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const firstPromise = getCodexModelCapabilities({
|
|
204
|
-
executable: "codex",
|
|
205
|
-
discover,
|
|
206
|
-
now: () => 100,
|
|
207
|
-
ttlMs: 1000,
|
|
208
|
-
});
|
|
209
|
-
const secondPromise = getCodexModelCapabilities({
|
|
210
|
-
executable: "codex",
|
|
211
|
-
discover,
|
|
212
|
-
now: () => 100,
|
|
213
|
-
ttlMs: 1000,
|
|
214
|
-
});
|
|
215
|
-
const thirdPromise = getCodexModelCapabilities({
|
|
216
|
-
executable: "codex",
|
|
217
|
-
discover,
|
|
218
|
-
now: () => 100,
|
|
219
|
-
ttlMs: 1000,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
resolveDiscover(undefined);
|
|
223
|
-
const [first, second, third] = await Promise.all([firstPromise, secondPromise, thirdPromise]);
|
|
224
|
-
|
|
225
|
-
assert.equal(calls, 1, "discovery should run exactly once when called concurrently");
|
|
226
|
-
assert.equal(first.models[0]?.model, "model-a");
|
|
227
|
-
assert.equal(second.models[0]?.model, "model-a");
|
|
228
|
-
assert.equal(third.models[0]?.model, "model-a");
|
|
229
|
-
clearCodexModelCapabilityCache();
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test("falls back safely when discovery fails", async () => {
|
|
233
|
-
clearCodexModelCapabilityCache();
|
|
234
|
-
const capabilities = await getCodexModelCapabilities({
|
|
235
|
-
executable: "codex",
|
|
236
|
-
discover: async () => {
|
|
237
|
-
throw new Error("broken json");
|
|
238
|
-
},
|
|
239
|
-
now: () => 100,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
assert.equal(capabilities.status, "fallback");
|
|
243
|
-
assert.match(capabilities.error ?? "", /broken json/);
|
|
244
|
-
assert.equal(capabilities.models[0]?.supportedReasoningLevels, null);
|
|
245
|
-
clearCodexModelCapabilityCache();
|
|
246
|
-
});
|