@golba98/codexa 1.0.2 → 1.0.3

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 (150) hide show
  1. package/README.md +391 -94
  2. package/package.json +12 -7
  3. package/src/app.tsx +149 -117
  4. package/src/commands/handler.ts +4 -4
  5. package/src/config/buildInfo.ts +2 -2
  6. package/src/config/persistence.ts +10 -0
  7. package/src/config/settings.ts +7 -15
  8. package/src/config/updateCheckCache.ts +19 -1
  9. package/src/core/channel.ts +23 -0
  10. package/src/core/executables/codexExecutable.ts +1 -0
  11. package/src/core/executables/executableResolver.ts +61 -43
  12. package/src/core/launchContext.ts +33 -33
  13. package/src/core/process/processValidation.ts +9 -5
  14. package/src/core/providerLauncher/launcher.ts +59 -42
  15. package/src/core/updateCheck.ts +111 -95
  16. package/src/session/types.ts +2 -1
  17. package/src/ui/ActionRequiredBlock.tsx +5 -5
  18. package/src/ui/ActivityBars.tsx +3 -3
  19. package/src/ui/ActivityIndicator.tsx +6 -6
  20. package/src/ui/AgentBlock.tsx +6 -6
  21. package/src/ui/AnimatedStatusText.tsx +1 -1
  22. package/src/ui/AppShell.tsx +9 -24
  23. package/src/ui/AttachmentImportPanel.tsx +8 -8
  24. package/src/ui/AuthPanel.tsx +20 -20
  25. package/src/ui/BottomComposer.tsx +117 -78
  26. package/src/ui/DashCard.tsx +3 -3
  27. package/src/ui/Markdown.tsx +17 -17
  28. package/src/ui/ModelPickerScreen.tsx +10 -10
  29. package/src/ui/ModelReasoningPicker.tsx +14 -14
  30. package/src/ui/Panel.tsx +3 -3
  31. package/src/ui/PlanActionPicker.tsx +6 -6
  32. package/src/ui/PlanReviewPanel.tsx +8 -8
  33. package/src/ui/ProviderPicker.tsx +18 -18
  34. package/src/ui/RunFooter.tsx +3 -3
  35. package/src/ui/SelectionPanel.tsx +4 -4
  36. package/src/ui/SettingsPanel.tsx +8 -8
  37. package/src/ui/Spinner.tsx +1 -1
  38. package/src/ui/TextEntryPanel.tsx +10 -10
  39. package/src/ui/ThinkingBlock.tsx +8 -8
  40. package/src/ui/Timeline.tsx +21 -17
  41. package/src/ui/TopHeader.tsx +421 -293
  42. package/src/ui/TurnGroup.tsx +31 -31
  43. package/src/ui/UpdateAvailableCard.tsx +41 -0
  44. package/src/ui/UpdatePromptPanel.tsx +203 -0
  45. package/src/ui/focus.ts +3 -0
  46. package/src/ui/layout.ts +1 -1
  47. package/src/ui/logoVariants.ts +107 -0
  48. package/src/ui/modeDisplay.ts +12 -12
  49. package/src/ui/runtimeDisplay.ts +100 -0
  50. package/src/ui/theme.tsx +274 -395
  51. package/src/ui/timelineMeasure.ts +29 -19
  52. package/scripts/audit-codexa-capabilities.mjs +0 -466
  53. package/scripts/gen-build-info.mjs +0 -33
  54. package/scripts/smoke-terminal-bench.mjs +0 -35
  55. package/src/appRenderStability.test.ts +0 -131
  56. package/src/commands/handler.test.ts +0 -655
  57. package/src/config/launchArgs.test.ts +0 -189
  58. package/src/config/layeredConfig.test.ts +0 -143
  59. package/src/config/persistence.test.ts +0 -114
  60. package/src/config/runtimeConfig.test.ts +0 -218
  61. package/src/config/settings.test.ts +0 -155
  62. package/src/config/trustStore.test.ts +0 -29
  63. package/src/core/attachments.test.ts +0 -155
  64. package/src/core/auth/codexAuth.test.ts +0 -68
  65. package/src/core/cleanupFastFail.test.ts +0 -76
  66. package/src/core/codexExecArgs.test.ts +0 -195
  67. package/src/core/codexLaunch.test.ts +0 -205
  68. package/src/core/codexPrompt.test.ts +0 -252
  69. package/src/core/executables/codexExecutable.test.ts +0 -212
  70. package/src/core/executables/executableResolver.test.ts +0 -129
  71. package/src/core/executables/geminiExecutable.test.ts +0 -116
  72. package/src/core/executables/pathSanityScan.test.ts +0 -47
  73. package/src/core/githubDiagnostics.test.ts +0 -92
  74. package/src/core/hollowResponseFormat.test.ts +0 -58
  75. package/src/core/launchContext.test.ts +0 -157
  76. package/src/core/models/codexCapabilities.test.ts +0 -45
  77. package/src/core/models/codexModelCapabilities.test.ts +0 -246
  78. package/src/core/models/modelSpecs.test.ts +0 -283
  79. package/src/core/perf/renderDebug.test.ts +0 -230
  80. package/src/core/planStorage.test.ts +0 -143
  81. package/src/core/process/CommandRunner.test.ts +0 -105
  82. package/src/core/projectInstructions.test.ts +0 -50
  83. package/src/core/providerLauncher/launcher.test.ts +0 -238
  84. package/src/core/providerLauncher/registry.test.ts +0 -324
  85. package/src/core/providerLauncher/workspaceConfig.test.ts +0 -638
  86. package/src/core/providerRuntime/anthropic.test.ts +0 -1120
  87. package/src/core/providerRuntime/capabilityProfile.test.ts +0 -311
  88. package/src/core/providerRuntime/contextMetadata.test.ts +0 -468
  89. package/src/core/providerRuntime/gemini.test.ts +0 -437
  90. package/src/core/providerRuntime/lmstudio.test.ts +0 -168
  91. package/src/core/providerRuntime/local.test.ts +0 -787
  92. package/src/core/providerRuntime/registry.test.ts +0 -233
  93. package/src/core/providers/codexJsonStream.test.ts +0 -148
  94. package/src/core/providers/codexSubprocess.test.ts +0 -68
  95. package/src/core/providers/codexTranscript.test.ts +0 -284
  96. package/src/core/terminal/startupClear.test.ts +0 -55
  97. package/src/core/terminal/terminalCapabilities.test.ts +0 -93
  98. package/src/core/terminal/terminalControl.test.ts +0 -75
  99. package/src/core/terminal/terminalSanitize.test.ts +0 -22
  100. package/src/core/terminal/terminalSelection.test.ts +0 -42
  101. package/src/core/terminal/terminalTitle.test.ts +0 -328
  102. package/src/core/updateCheck.test.ts +0 -194
  103. package/src/core/workspaceActivity.test.ts +0 -163
  104. package/src/core/workspaceGuard.test.ts +0 -151
  105. package/src/core/workspaceRoot.test.ts +0 -23
  106. package/src/exec.test.ts +0 -13
  107. package/src/headless/execArgs.test.ts +0 -147
  108. package/src/headless/execRunner.test.ts +0 -436
  109. package/src/index.test.tsx +0 -620
  110. package/src/session/appSession.test.ts +0 -897
  111. package/src/session/chatLifecycle.test.ts +0 -64
  112. package/src/session/liveRenderScheduler.test.ts +0 -201
  113. package/src/session/planFlow.test.ts +0 -103
  114. package/src/session/planTranscript.test.ts +0 -65
  115. package/src/session/promptRunSchedule.test.ts +0 -36
  116. package/src/ui/ActivityIndicator.test.tsx +0 -58
  117. package/src/ui/AgentBlock.test.ts +0 -6
  118. package/src/ui/AnimatedStatusText.test.ts +0 -16
  119. package/src/ui/AppShell.test.tsx +0 -1776
  120. package/src/ui/AttachmentImportPanel.test.tsx +0 -204
  121. package/src/ui/BottomComposer.test.ts +0 -674
  122. package/src/ui/CodexLogo.tsx +0 -55
  123. package/src/ui/Markdown.test.ts +0 -157
  124. package/src/ui/ModelPickerProviderScope.test.tsx +0 -411
  125. package/src/ui/ModelPickerScreen.test.tsx +0 -99
  126. package/src/ui/ModelPickerState.test.tsx +0 -151
  127. package/src/ui/ModelReasoningPicker.test.tsx +0 -447
  128. package/src/ui/PlanReviewPanel.test.tsx +0 -267
  129. package/src/ui/PromptCardBorder.test.tsx +0 -161
  130. package/src/ui/ProviderPicker.test.tsx +0 -289
  131. package/src/ui/ProviderShortcut.test.tsx +0 -143
  132. package/src/ui/SettingsPanel.test.tsx +0 -233
  133. package/src/ui/Timeline.test.ts +0 -2067
  134. package/src/ui/TimelineNavigation.test.tsx +0 -201
  135. package/src/ui/TopHeader.test.tsx +0 -254
  136. package/src/ui/TurnGroup.test.tsx +0 -365
  137. package/src/ui/busyStatusAnimation.test.ts +0 -30
  138. package/src/ui/commandNormalize.test.ts +0 -142
  139. package/src/ui/diffRenderer.test.ts +0 -102
  140. package/src/ui/focusFlow.test.tsx +0 -1098
  141. package/src/ui/inputBuffer.test.ts +0 -151
  142. package/src/ui/layout.test.ts +0 -146
  143. package/src/ui/modeDisplay.test.ts +0 -42
  144. package/src/ui/runActivityView.test.ts +0 -89
  145. package/src/ui/runLifecycleView.test.tsx +0 -237
  146. package/src/ui/statusRenderIsolation.test.tsx +0 -654
  147. package/src/ui/terminalAnswerFormat.test.ts +0 -19
  148. package/src/ui/textLayout.test.ts +0 -18
  149. package/src/ui/themeFlow.test.ts +0 -53
  150. package/src/ui/timelineMeasureCache.test.ts +0 -986
@@ -1,233 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import {
4
- discoverProviderModels,
5
- getProviderRouteSetupMessage,
6
- getProviderRuntime,
7
- isProviderRouteConfigured,
8
- resolveActiveProviderRoute,
9
- } from "./registry.js";
10
- import { resetGeminiRouteValidationCacheForTests } from "./gemini.js";
11
- import { resetAnthropicRouteValidationCacheForTests } from "./anthropic.js";
12
- import { checkLocalProvider, resetLocalProviderStateForTests } from "./local.js";
13
-
14
- test("google runtime exposes configured Gemini models for in-Codexa routing", () => {
15
- const runtime = getProviderRuntime("google");
16
- const discovery = discoverProviderModels("google");
17
-
18
- assert.equal(runtime.routeAvailable, true);
19
- assert.equal(runtime.backendKind, "gemini-cli-auth");
20
- assert.equal(discovery.status, "ready");
21
- assert.ok(discovery.models.length > 0);
22
- });
23
-
24
- test("anthropic runtime exposes configured Claude models for in-Codexa routing", () => {
25
- const runtime = getProviderRuntime("anthropic");
26
- const discovery = discoverProviderModels("anthropic");
27
-
28
- assert.equal(runtime.routeAvailable, true);
29
- assert.equal(runtime.backendKind, "claude-code-auth");
30
- assert.equal(discovery.status, "ready");
31
- assert.ok(discovery.models.length > 0);
32
- });
33
-
34
- test("active route resolution preserves routable google routes", () => {
35
- const route = resolveActiveProviderRoute({
36
- workspaceConfigActiveRoute: {
37
- providerId: "google",
38
- modelId: "gemini-2.5-pro",
39
- backendKind: "gemini-cli-auth",
40
- reasoning: "medium",
41
- },
42
- currentModel: "gpt-5.4",
43
- currentReasoning: "high",
44
- });
45
-
46
- assert.deepEqual(route, {
47
- providerId: "google",
48
- modelId: "gemini-2.5-pro",
49
- backendKind: "gemini-cli-auth",
50
- reasoning: "medium",
51
- });
52
- });
53
-
54
- test("active route resolution normalizes legacy Gemini 3 Flash routes to preview", () => {
55
- const route = resolveActiveProviderRoute({
56
- workspaceConfigActiveRoute: {
57
- providerId: "google",
58
- modelId: "gemini-3-flash",
59
- backendKind: "gemini-cli-auth",
60
- reasoning: "high",
61
- },
62
- currentModel: "gpt-5.4",
63
- currentReasoning: "medium",
64
- });
65
-
66
- assert.deepEqual(route, {
67
- providerId: "google",
68
- modelId: "gemini-3-flash-preview",
69
- backendKind: "gemini-cli-auth",
70
- reasoning: "high",
71
- });
72
- });
73
-
74
- test("active route resolution preserves routable anthropic routes", () => {
75
- const route = resolveActiveProviderRoute({
76
- workspaceConfigActiveRoute: {
77
- providerId: "anthropic",
78
- modelId: "claude-sonnet-4-20250514",
79
- backendKind: "anthropic-api-key",
80
- reasoning: "high",
81
- },
82
- currentModel: "gpt-5.4",
83
- currentReasoning: "medium",
84
- });
85
-
86
- assert.deepEqual(route, {
87
- providerId: "anthropic",
88
- modelId: "claude-sonnet-4-20250514",
89
- backendKind: "anthropic-api-key",
90
- reasoning: "high",
91
- });
92
- });
93
-
94
- test("active route resolution preserves routable local routes", async () => {
95
- resetLocalProviderStateForTests();
96
- await checkLocalProvider({
97
- fetchImpl: (async () => new Response(JSON.stringify({
98
- data: [{ id: "llama-local" }],
99
- }), { status: 200 })) as typeof fetch,
100
- });
101
- const route = resolveActiveProviderRoute({
102
- workspaceConfigActiveRoute: {
103
- providerId: "local",
104
- modelId: "llama-local",
105
- backendKind: "local-openai-compatible",
106
- },
107
- currentModel: "gpt-5.4",
108
- currentReasoning: "high",
109
- });
110
-
111
- assert.deepEqual(route, {
112
- providerId: "local",
113
- modelId: "llama-local",
114
- backendKind: "local-openai-compatible",
115
- });
116
- resetLocalProviderStateForTests();
117
- });
118
-
119
- // ─── CLI --model override precedence ─────────────────────────────────────────
120
- // These tests mirror the app-level logic: when --model is given on the CLI,
121
- // the caller constructs effectiveRoute = { ...savedRoute, modelId: cliModel }
122
- // before passing it to resolveActiveProviderRoute. The tests verify the resolver
123
- // honours that override correctly.
124
-
125
- test("CLI model override wins over persisted OpenAI activeRoute model", () => {
126
- // Simulate: providers.json has gpt-5.4-mini, but user ran --model gpt-5.5
127
- const cliModel = "gpt-5.5";
128
- const savedRoute = {
129
- providerId: "openai" as const,
130
- modelId: "gpt-5.4-mini",
131
- backendKind: "codex-cli-auth" as const,
132
- reasoning: "low",
133
- };
134
- const effectiveRoute = { ...savedRoute, modelId: cliModel };
135
-
136
- const route = resolveActiveProviderRoute({
137
- workspaceConfigActiveRoute: effectiveRoute,
138
- currentModel: "gpt-5.5",
139
- currentReasoning: "low",
140
- });
141
-
142
- assert.equal(route.modelId, "gpt-5.5", "CLI model must be used for the run, not providers.json model");
143
- assert.equal(route.providerId, "openai");
144
- });
145
-
146
- test("without CLI model override the persisted providers.json activeRoute model is used", () => {
147
- const savedRoute = {
148
- providerId: "openai" as const,
149
- modelId: "gpt-5.4-mini",
150
- backendKind: "codex-cli-auth" as const,
151
- reasoning: "low",
152
- };
153
-
154
- const route = resolveActiveProviderRoute({
155
- workspaceConfigActiveRoute: savedRoute,
156
- currentModel: "gpt-5.4",
157
- currentReasoning: "high",
158
- });
159
-
160
- assert.equal(route.modelId, "gpt-5.4-mini", "Without CLI override, persisted model must win over layered-config default");
161
- });
162
-
163
- test("CLI model override preserves provider from providers.json activeRoute", () => {
164
- // --model on the CLI should not change the provider, only the modelId
165
- const cliModel = "gpt-5.5";
166
- const savedRoute = {
167
- providerId: "openai" as const,
168
- modelId: "gpt-5.4-mini",
169
- backendKind: "codex-cli-auth" as const,
170
- reasoning: "low",
171
- };
172
- const effectiveRoute = { ...savedRoute, modelId: cliModel };
173
-
174
- const route = resolveActiveProviderRoute({
175
- workspaceConfigActiveRoute: effectiveRoute,
176
- currentModel: cliModel,
177
- currentReasoning: "low",
178
- });
179
-
180
- assert.equal(route.providerId, "openai", "Provider from providers.json must be preserved");
181
- assert.equal(route.modelId, cliModel);
182
- assert.equal(route.reasoning, "low", "Reasoning from providers.json must be preserved");
183
- });
184
-
185
- // ─── Authentication and setup gates ──────────────────────────────────────────
186
-
187
- test("anthropic route configuration is gated by ANTHROPIC_API_KEY or Claude Code", () => {
188
- const original = process.env.ANTHROPIC_API_KEY;
189
-
190
- try {
191
- delete process.env.ANTHROPIC_API_KEY;
192
- resetAnthropicRouteValidationCacheForTests();
193
- assert.equal(isProviderRouteConfigured("anthropic"), false);
194
- assert.match(getProviderRouteSetupMessage("anthropic"), /set ANTHROPIC_API_KEY/);
195
- } finally {
196
- if (original) process.env.ANTHROPIC_API_KEY = original;
197
- }
198
- });
199
-
200
- test("google route configuration is gated by Gemini API key or validated headless CLI", () => {
201
- const originalGemini = process.env.GEMINI_API_KEY;
202
- const originalGoogle = process.env.GOOGLE_API_KEY;
203
-
204
- try {
205
- delete process.env.GEMINI_API_KEY;
206
- delete process.env.GOOGLE_API_KEY;
207
- resetGeminiRouteValidationCacheForTests();
208
- assert.equal(isProviderRouteConfigured("google"), false);
209
- assert.match(getProviderRouteSetupMessage("google"), /GEMINI_API_KEY \/ GOOGLE_API_KEY/);
210
- } finally {
211
- if (originalGemini) process.env.GEMINI_API_KEY = originalGemini;
212
- if (originalGoogle) process.env.GOOGLE_API_KEY = originalGoogle;
213
- }
214
- });
215
-
216
- test("local route configuration is gated by endpoint model discovery", async () => {
217
- resetLocalProviderStateForTests();
218
- assert.equal(isProviderRouteConfigured("local"), false);
219
-
220
- await checkLocalProvider({
221
- fetchImpl: (async (input) => {
222
- if (String(input).includes("/api/v0/")) {
223
- return new Response(null, { status: 404 });
224
- }
225
- return new Response(JSON.stringify({
226
- data: [{ id: "google/gemma-4-26b-a4b" }],
227
- }), { status: 200 });
228
- }) as typeof fetch,
229
- });
230
-
231
- assert.equal(isProviderRouteConfigured("local"), true);
232
- resetLocalProviderStateForTests();
233
- });
@@ -1,148 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import type { BackendProgressUpdate } from "./types.js";
4
- import type { RunToolActivity } from "../../session/types.js";
5
- import { createCodexJsonStreamParser } from "./codexJsonStream.js";
6
-
7
- test("streams incremental agent_message growth as assistant deltas", () => {
8
- const assistant: string[] = [];
9
- const observedFinalAnswers: string[] = [];
10
- const parser = createCodexJsonStreamParser({
11
- onAssistantDelta: (chunk) => assistant.push(chunk),
12
- onFinalAnswerObserved: (response) => observedFinalAnswers.push(response),
13
- });
14
-
15
- assert.equal(parser.feedLine(JSON.stringify({
16
- type: "item.started",
17
- item: { id: "msg-1", type: "agent_message", text: "Hello" },
18
- })), true);
19
- assert.equal(parser.feedLine(JSON.stringify({
20
- type: "item.updated",
21
- item: { id: "msg-1", type: "agent_message", text: "Hello world" },
22
- })), true);
23
- assert.equal(parser.feedLine(JSON.stringify({
24
- type: "item.completed",
25
- item: { id: "msg-1", type: "agent_message", text: "Hello world!" },
26
- })), true);
27
- assert.equal(parser.feedLine(JSON.stringify({
28
- type: "turn.completed",
29
- })), true);
30
-
31
- assert.deepEqual(assistant, ["Hello", " world", "!"]);
32
- assert.deepEqual(observedFinalAnswers, ["Hello world!"]);
33
- assert.equal(parser.getFinalResponse(), "Hello world!");
34
- });
35
-
36
- test("surfaces command execution lifecycle as tool activity", () => {
37
- const toolActivities: RunToolActivity[] = [];
38
- const parser = createCodexJsonStreamParser({
39
- onToolActivity: (activity) => toolActivities.push(activity),
40
- });
41
-
42
- parser.feedLine(JSON.stringify({
43
- type: "item.started",
44
- item: {
45
- id: "cmd-1",
46
- type: "command_execution",
47
- command: "rg --files",
48
- status: "in_progress",
49
- aggregated_output: "",
50
- },
51
- }));
52
- parser.feedLine(JSON.stringify({
53
- type: "item.completed",
54
- item: {
55
- id: "cmd-1",
56
- type: "command_execution",
57
- command: "rg --files",
58
- status: "completed",
59
- aggregated_output: "src/app.tsx\nsrc/ui/Timeline.tsx\n",
60
- exit_code: 0,
61
- },
62
- }));
63
-
64
- assert.equal(toolActivities.length, 2);
65
- assert.equal(toolActivities[0]?.status, "running");
66
- assert.equal(toolActivities[0]?.command, "rg --files");
67
- assert.equal(toolActivities[1]?.status, "completed");
68
- assert.match(toolActivities[1]?.summary ?? "", /src\/app\.tsx/i);
69
- });
70
-
71
- test("emits progress updates for todo_list and reasoning items", () => {
72
- const progress: BackendProgressUpdate[] = [];
73
- const parser = createCodexJsonStreamParser({
74
- onProgress: (update) => progress.push(update),
75
- });
76
-
77
- parser.feedLine(JSON.stringify({
78
- type: "item.updated",
79
- item: {
80
- id: "todo-1",
81
- type: "todo_list",
82
- items: [
83
- { text: "Inspect workspace", completed: true },
84
- { text: "Write file", completed: false },
85
- ],
86
- },
87
- }));
88
- parser.feedLine(JSON.stringify({
89
- type: "item.updated",
90
- item: {
91
- id: "reason-1",
92
- type: "reasoning",
93
- text: "Verifying the generated output\n\nChecking edge cases",
94
- },
95
- }));
96
-
97
- assert.deepEqual(progress, [
98
- { id: "todo-1", source: "todo", text: "Todo 1/2: Write file" },
99
- { id: "reason-1", source: "reasoning", text: "Verifying the generated output\n\nChecking edge cases" },
100
- ]);
101
- });
102
-
103
- test("reuses the same progress id when a structured update grows", () => {
104
- const progress: BackendProgressUpdate[] = [];
105
- const parser = createCodexJsonStreamParser({
106
- onProgress: (update) => progress.push(update),
107
- });
108
-
109
- parser.feedLine(JSON.stringify({
110
- type: "item.updated",
111
- item: {
112
- id: "reason-7",
113
- type: "reasoning",
114
- text: "Inspecting the config",
115
- },
116
- }));
117
- parser.feedLine(JSON.stringify({
118
- type: "item.updated",
119
- item: {
120
- id: "reason-7",
121
- type: "reasoning",
122
- text: "Inspecting the config\n\nComparing runtime defaults",
123
- },
124
- }));
125
-
126
- assert.deepEqual(progress, [
127
- { id: "reason-7", source: "reasoning", text: "Inspecting the config" },
128
- { id: "reason-7", source: "reasoning", text: "Inspecting the config\n\nComparing runtime defaults" },
129
- ]);
130
- });
131
-
132
- test("captures turn failures from structured events", () => {
133
- const parser = createCodexJsonStreamParser({});
134
-
135
- parser.feedLine(JSON.stringify({
136
- type: "turn.failed",
137
- error: { message: "Permission denied" },
138
- }));
139
-
140
- assert.equal(parser.getFailureMessage(), "Permission denied");
141
- });
142
-
143
- test("returns false for non-json lines so callers can fall back to transcript parsing", () => {
144
- const parser = createCodexJsonStreamParser({});
145
-
146
- assert.equal(parser.feedLine("assistant"), false);
147
- assert.equal(parser.hasStructuredEvents(), false);
148
- });
@@ -1,68 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import test from "node:test";
5
-
6
- test("codex subprocess attaches output handlers before writing stdin", () => {
7
- const source = readFileSync(fileURLToPath(new URL("./codexSubprocess.ts", import.meta.url)), "utf8");
8
- const spawnIndex = source.indexOf("proc = spawnCodexProcess");
9
- const stdoutIndex = source.indexOf('proc.stdout?.on("data"', spawnIndex);
10
- const stderrIndex = source.indexOf('proc.stderr?.on("data"', spawnIndex);
11
- const closeIndex = source.indexOf('proc.on("close"', spawnIndex);
12
- const errorIndex = source.indexOf('proc.on("error"', spawnIndex);
13
- const stdinWriteIndex = source.indexOf("proc.stdin?.write", spawnIndex);
14
-
15
- assert.notEqual(spawnIndex, -1);
16
- assert.notEqual(stdoutIndex, -1);
17
- assert.notEqual(stderrIndex, -1);
18
- assert.notEqual(closeIndex, -1);
19
- assert.notEqual(errorIndex, -1);
20
- assert.notEqual(stdinWriteIndex, -1);
21
- assert.ok(stdoutIndex < stdinWriteIndex);
22
- assert.ok(stderrIndex < stdinWriteIndex);
23
- assert.ok(closeIndex < stdinWriteIndex);
24
- assert.ok(errorIndex < stdinWriteIndex);
25
- });
26
-
27
- test("codex subprocess supports raw prompt passthrough before wrapped prompt fallback", () => {
28
- const source = readFileSync(fileURLToPath(new URL("./codexSubprocess.ts", import.meta.url)), "utf8");
29
-
30
- assert.match(source, /const promptPolicy = options\.promptPolicy \?\? "wrapped"/);
31
- assert.match(source, /promptPolicy === "raw"\s+\?\s+prompt\s+:\s+buildCodexPrompt/s);
32
- });
33
-
34
- test("codex subprocess cleanup skips kill after process close", () => {
35
- const source = readFileSync(fileURLToPath(new URL("./codexSubprocess.ts", import.meta.url)), "utf8");
36
- const closeIndex = source.indexOf('proc.on("close"', source.indexOf("proc = spawnCodexProcess"));
37
- const exitedIndex = source.indexOf("procExited = true", closeIndex);
38
- const cleanupIndex = source.indexOf("return () =>", exitedIndex);
39
- const skipIndex = source.indexOf("!proc || procExited || proc.killed", cleanupIndex);
40
- const killIndex = source.indexOf("proc.kill()", cleanupIndex);
41
-
42
- assert.notEqual(closeIndex, -1);
43
- assert.notEqual(exitedIndex, -1);
44
- assert.notEqual(cleanupIndex, -1);
45
- assert.notEqual(skipIndex, -1);
46
- assert.notEqual(killIndex, -1);
47
- assert.ok(exitedIndex < cleanupIndex);
48
- assert.ok(skipIndex < killIndex);
49
- });
50
-
51
- test("codex subprocess reports lifecycle boundaries for terminal title reassertion", () => {
52
- const source = readFileSync(fileURLToPath(new URL("./codexSubprocess.ts", import.meta.url)), "utf8");
53
- const beforeSpawnIndex = source.indexOf('handlers.onProcessLifecycle?.("before-spawn")');
54
- const spawnIndex = source.indexOf("proc = spawnCodexProcess");
55
- const spawnedIndex = source.indexOf('handlers.onProcessLifecycle?.("spawned")', spawnIndex);
56
- const closeIndex = source.indexOf('proc.on("close"', spawnIndex);
57
- const exitIndex = source.indexOf('handlers.onProcessLifecycle?.("exit")', closeIndex);
58
- const errorIndex = source.indexOf('proc.on("error"', spawnIndex);
59
- const lifecycleErrorIndex = source.indexOf('handlers.onProcessLifecycle?.("error")', errorIndex);
60
- const cleanupIndex = source.indexOf("return () =>", spawnedIndex);
61
- const lifecycleCleanupIndex = source.indexOf('handlers.onProcessLifecycle?.("cleanup")', cleanupIndex);
62
-
63
- assert.ok(beforeSpawnIndex >= 0 && beforeSpawnIndex < spawnIndex);
64
- assert.ok(spawnedIndex > spawnIndex);
65
- assert.ok(exitIndex > closeIndex);
66
- assert.ok(lifecycleErrorIndex > errorIndex);
67
- assert.ok(lifecycleCleanupIndex > cleanupIndex);
68
- });