@parall/codex-agent 1.31.0 → 1.32.1

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/src/index.ts CHANGED
@@ -1,8 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import * as os from "node:os";
4
- import { ParallAgentGateway, createPlatformConfigManager, createLogger, childLogger, isPlatformManagedProfile, parseShutdownDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource } from "@parall/agent-core";
5
- import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
3
+ import * as os from 'node:os';
4
+ import {
5
+ ParallAgentGateway,
6
+ createPlatformConfigManager,
7
+ createLogger,
8
+ createOtelLogger,
9
+ childLogger,
10
+ isPlatformManagedProfile,
11
+ isPlatformModelOverride,
12
+ resolveRuntimeModel,
13
+ parseShutdownDeadlineMs,
14
+ parseForkDeadlineMs,
15
+ parseDispatchDeadlineMs,
16
+ parseProviderConfig,
17
+ clearAllProviderCreds,
18
+ llmSource,
19
+ initAgentTelemetry,
20
+ } from '@parall/agent-core';
21
+ import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
6
22
  import {
7
23
  buildCodexRuntimeKey,
8
24
  contextFilePathForSession,
@@ -10,12 +26,17 @@ import {
10
26
  resolveWsUrl,
11
27
  sessionStateFilePathForRuntime,
12
28
  stepIdFilePathForSession,
13
- } from "./config.js";
14
- import { CodexAppServerAdapter } from "./dispatch.js";
15
- import { CodexSessionManager } from "./session-manager.js";
16
- import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode } from "./workspace.js";
29
+ } from './config.js';
30
+ import { CodexAppServerAdapter } from './dispatch.js';
31
+ import { CodexSessionManager } from './session-manager.js';
32
+ import {
33
+ ensureCodexWorkspace,
34
+ ensureParallProvider,
35
+ ensureWorkspaceTrusted,
36
+ isParallProxyMode,
37
+ } from './workspace.js';
17
38
 
18
- const log = createLogger("codex-agent");
39
+ const log = createLogger('codex-agent');
19
40
  let activeLog = log;
20
41
 
21
42
  async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string) {
@@ -35,136 +56,181 @@ function resolveProviderEnv(): void {
35
56
  if (!pc) return;
36
57
  clearAllProviderCreds(process.env);
37
58
  const source = llmSource(pc);
38
- if (source === "parall") {
59
+ if (source === 'parall') {
39
60
  process.env.OPENAI_API_KEY = process.env.PRLL_API_KEY;
40
61
  process.env.OPENAI_BASE_URL = `${process.env.PRLL_API_URL}/api/llm/v1`;
41
- } else if (source === "custom") {
62
+ } else if (source === 'custom') {
42
63
  if (pc.openai_api_key) process.env.OPENAI_API_KEY = pc.openai_api_key;
43
64
  if (pc.openai_base_url) process.env.OPENAI_BASE_URL = pc.openai_base_url;
44
65
  }
45
66
  }
46
67
 
47
68
  async function main() {
48
- resolveProviderEnv();
49
- const config = resolveCodexAgentConfig(process.env);
69
+ const telemetry = await initAgentTelemetry('parall-codex-agent', 'codex');
70
+ activeLog = createOtelLogger('agent', 'codex-agent');
71
+ try {
72
+ resolveProviderEnv();
73
+ const config = resolveCodexAgentConfig(process.env);
50
74
 
51
- const client = new ParallClient({
52
- baseUrl: config.apiUrl,
53
- token: config.apiKey,
54
- swimlaneName: config.swimlaneName,
55
- });
75
+ const client = new ParallClient({
76
+ baseUrl: config.apiUrl,
77
+ token: config.apiKey,
78
+ swimlaneName: config.swimlaneName,
79
+ });
56
80
 
57
- const me = await getAgentMeWithLegacyFallback(client, config.orgId);
58
- const agentUserId = me.id;
59
- const agentLog = childLogger(log, agentUserId);
60
- activeLog = agentLog;
61
- ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
62
- const useParallProvider = isParallProxyMode();
63
- if (useParallProvider) {
64
- ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
65
- agentLog.info("parall custom provider configured (Responses API HTTP/SSE mode)");
66
- }
67
- ensureCodexWorkspace(config.workspaceDir, agentLog, {
68
- userId: agentUserId,
69
- displayName: me.display_name,
70
- description: me.agent_profile?.description ?? undefined,
71
- });
72
- const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
73
- const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
74
- const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
75
- const sessionManager = new CodexSessionManager(
76
- runtimeKey,
77
- sessionStateFilePath,
78
- agentLog,
79
- );
81
+ const me = await getAgentMeWithLegacyFallback(client, config.orgId);
82
+ const agentUserId = me.id;
83
+ const agentLog = childLogger(activeLog, agentUserId);
84
+ activeLog = agentLog;
85
+ ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
86
+ const useParallProvider = isParallProxyMode();
87
+ if (useParallProvider) {
88
+ ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
89
+ agentLog.info('parall custom provider configured (Responses API HTTP/SSE mode)');
90
+ }
91
+ ensureCodexWorkspace(config.workspaceDir, agentLog, {
92
+ userId: agentUserId,
93
+ displayName: me.display_name,
94
+ description: me.agent_profile?.description ?? undefined,
95
+ });
96
+ const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
97
+ const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
98
+ const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
99
+ const sessionManager = new CodexSessionManager(runtimeKey, sessionStateFilePath, agentLog);
80
100
 
81
- const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
82
- const ws = new ParallWs({
83
- getTicket: () => client.getWsTicket(),
84
- wsUrl: resolvedWsUrl,
85
- });
101
+ const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
102
+ const ws = new ParallWs({
103
+ getTicket: () => client.getWsTicket(),
104
+ wsUrl: resolvedWsUrl,
105
+ });
86
106
 
87
- const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log: agentLog });
88
- const platformDefaults = await configMgr.fetch();
89
- let platformManaged = isPlatformManagedProfile(me.agent_profile);
90
- const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
91
- const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
92
- if (platformDefaults.model) agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
93
- if (platformDefaults.thinkingEffort) agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
107
+ const configMgr = createPlatformConfigManager({
108
+ client,
109
+ stateDir: config.stateDir,
110
+ runtimeType: 'codex',
111
+ log: agentLog,
112
+ });
113
+ const platformDefaults = await configMgr.fetch();
114
+ let platformManaged = isPlatformManagedProfile(me.agent_profile);
115
+ // Model precedence: operator override (platform-managed) > env > server floor.
116
+ // The server delivers a model floor for Parall-proxy routes even when
117
+ // model_management=self, so the CLI never falls back to a built-in default
118
+ // the catalog might reject — but that floor must lose to an explicit env
119
+ // override. A profile is an operator-override only when it is platform-managed
120
+ // AND not explicitly self-managed: this treats legacy hosted agents
121
+ // (model_management=null, resolved to platform server-side) as overrides,
122
+ // while excluding local self agents whose delivered model is a floor.
123
+ // reasoning_effort stays an operator override (platform).
124
+ let platformModelOverride = isPlatformModelOverride(me.agent_profile);
125
+ const resolvedModel = resolveRuntimeModel(
126
+ platformModelOverride,
127
+ platformDefaults.model,
128
+ config.model,
129
+ );
130
+ const resolvedEffort = platformManaged
131
+ ? (platformDefaults.thinkingEffort ?? config.reasoningEffort)
132
+ : config.reasoningEffort;
133
+ if (platformDefaults.model)
134
+ agentLog.info(
135
+ `platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'platform-managed' : 'floor/self'})`,
136
+ );
137
+ if (platformDefaults.thinkingEffort)
138
+ agentLog.info(
139
+ `platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformModelOverride ? 'applied' : 'ignored, model_management=self'})`,
140
+ );
94
141
 
95
- const adapter = new CodexAppServerAdapter({
96
- codexBin: config.codexBin,
97
- codexHome: config.codexHome,
98
- workspaceDir: config.workspaceDir,
99
- model: resolvedModel,
100
- reasoningEffort: resolvedEffort,
101
- sandbox: config.sandbox,
102
- approvalPolicy: config.approvalPolicy,
103
- sessionManager,
104
- log: agentLog,
105
- contextFilePath: mainContextFilePath,
106
- useParallProvider,
107
- });
142
+ const adapter = new CodexAppServerAdapter({
143
+ codexBin: config.codexBin,
144
+ codexHome: config.codexHome,
145
+ workspaceDir: config.workspaceDir,
146
+ model: resolvedModel,
147
+ reasoningEffort: resolvedEffort,
148
+ sandbox: config.sandbox,
149
+ approvalPolicy: config.approvalPolicy,
150
+ sessionManager,
151
+ log: agentLog,
152
+ contextFilePath: mainContextFilePath,
153
+ useParallProvider,
154
+ });
108
155
 
109
- const gateway = new ParallAgentGateway({
110
- accountId: agentUserId,
111
- client,
112
- ws,
113
- connectionLabel: resolvedWsUrl,
114
- config: {
115
- parall_url: config.apiUrl,
116
- api_key: config.apiKey,
117
- org_id: config.orgId,
118
- },
119
- agentUserId,
120
- runtimeType: "codex",
121
- runtimeKey,
122
- runtimeRef: {
123
- hostname: os.hostname(),
124
- pid: process.pid,
125
- workspace_dir: config.workspaceDir,
126
- codex_home: config.codexHome,
127
- driver: "app-server",
128
- },
129
- dispatchAdapter: adapter,
130
- log: agentLog,
131
- shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
132
- contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
133
- stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
134
- onConfigUpdate: async () => {
135
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
136
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
137
- const updated = await configMgr.fetch();
138
- adapter.updateConfig({
139
- model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
140
- reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
141
- });
142
- },
143
- onSessionReady: async () => {
144
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
145
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
146
- const updated = await configMgr.fetch();
147
- adapter.updateConfig({
148
- model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
149
- reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
150
- });
151
- },
152
- onNewSession: () => { sessionManager.clearMainThread(); },
153
- onSessionStale: () => { sessionManager.clearMainThread(); },
154
- });
156
+ const gateway = new ParallAgentGateway({
157
+ accountId: agentUserId,
158
+ client,
159
+ ws,
160
+ connectionLabel: resolvedWsUrl,
161
+ config: {
162
+ parall_url: config.apiUrl,
163
+ api_key: config.apiKey,
164
+ org_id: config.orgId,
165
+ },
166
+ agentUserId,
167
+ runtimeType: 'codex',
168
+ runtimeKey,
169
+ runtimeRef: {
170
+ hostname: os.hostname(),
171
+ pid: process.pid,
172
+ workspace_dir: config.workspaceDir,
173
+ codex_home: config.codexHome,
174
+ driver: 'app-server',
175
+ },
176
+ dispatchAdapter: adapter,
177
+ log: agentLog,
178
+ shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
179
+ forkDeadlineMs: parseForkDeadlineMs(process.env.PRLL_FORK_DEADLINE_MS),
180
+ dispatchDeadlineMs: parseDispatchDeadlineMs(process.env.PRLL_DISPATCH_DEADLINE_MS),
181
+ contextFilePathForSession: (sessionKey) =>
182
+ contextFilePathForSession(config.stateDir, sessionKey),
183
+ stepIdFilePathForSession: (sessionKey) =>
184
+ stepIdFilePathForSession(config.stateDir, sessionKey),
185
+ onConfigUpdate: async () => {
186
+ const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
187
+ platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
188
+ platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
189
+ const updated = await configMgr.fetch();
190
+ adapter.updateConfig({
191
+ model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
192
+ reasoningEffort: platformManaged
193
+ ? (updated.thinkingEffort ?? config.reasoningEffort)
194
+ : (config.reasoningEffort ?? null),
195
+ });
196
+ },
197
+ onSessionReady: async () => {
198
+ const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
199
+ platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
200
+ platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
201
+ const updated = await configMgr.fetch();
202
+ adapter.updateConfig({
203
+ model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
204
+ reasoningEffort: platformManaged
205
+ ? (updated.thinkingEffort ?? config.reasoningEffort)
206
+ : (config.reasoningEffort ?? null),
207
+ });
208
+ },
209
+ onNewSession: () => {
210
+ sessionManager.clearMainThread();
211
+ },
212
+ onSessionStale: () => {
213
+ sessionManager.clearMainThread();
214
+ },
215
+ });
155
216
 
156
- const abortController = new AbortController();
157
- const abort = () => abortController.abort();
158
- process.on("SIGINT", abort);
159
- process.on("SIGTERM", abort);
217
+ const abortController = new AbortController();
218
+ const abort = () => abortController.abort();
219
+ process.on('SIGINT', abort);
220
+ process.on('SIGTERM', abort);
160
221
 
161
- try {
162
- agentLog.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
163
- await gateway.run(abortController.signal);
222
+ try {
223
+ agentLog.info(
224
+ `starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`,
225
+ );
226
+ await gateway.run(abortController.signal);
227
+ } finally {
228
+ await adapter.stop();
229
+ process.off('SIGINT', abort);
230
+ process.off('SIGTERM', abort);
231
+ }
164
232
  } finally {
165
- await adapter.stop();
166
- process.off("SIGINT", abort);
167
- process.off("SIGTERM", abort);
233
+ await telemetry.shutdown();
168
234
  }
169
235
  }
170
236
 
@@ -1,22 +1,22 @@
1
- import type { ChildProcessWithoutNullStreams } from "node:child_process";
1
+ import type { ChildProcessWithoutNullStreams } from 'node:child_process';
2
2
 
3
3
  type JsonRpcId = number | string;
4
4
 
5
5
  export type JsonRpcRequest = {
6
- jsonrpc: "2.0";
6
+ jsonrpc: '2.0';
7
7
  id: JsonRpcId;
8
8
  method: string;
9
9
  params?: unknown;
10
10
  };
11
11
 
12
12
  export type JsonRpcNotification = {
13
- jsonrpc: "2.0";
13
+ jsonrpc: '2.0';
14
14
  method: string;
15
15
  params?: unknown;
16
16
  };
17
17
 
18
18
  export type JsonRpcResponse = {
19
- jsonrpc: "2.0";
19
+ jsonrpc: '2.0';
20
20
  id: JsonRpcId;
21
21
  result?: unknown;
22
22
  error?: { code: number; message: string; data?: unknown };
@@ -44,7 +44,7 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
44
44
  export class JsonRpcStdioClient {
45
45
  private nextId = 1;
46
46
  private readonly pending = new Map<JsonRpcId, Pending>();
47
- private buffer = "";
47
+ private buffer = '';
48
48
  private onNotification: NotificationHandler | null = null;
49
49
  private disposed = false;
50
50
 
@@ -53,11 +53,11 @@ export class JsonRpcStdioClient {
53
53
  private readonly requestTimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,
54
54
  private readonly killProcess?: (proc: ChildProcessWithoutNullStreams) => void,
55
55
  ) {
56
- proc.stdout.setEncoding("utf8");
57
- proc.stdout.on("data", (chunk: string) => this.ingest(chunk));
58
- proc.once("close", () => this.dispose(new Error("app-server subprocess closed")));
59
- proc.once("error", (err) => this.dispose(err));
60
- proc.stdin.on("error", (err) => this.killUnhealthy(err));
56
+ proc.stdout.setEncoding('utf8');
57
+ proc.stdout.on('data', (chunk: string) => this.ingest(chunk));
58
+ proc.once('close', () => this.dispose(new Error('app-server subprocess closed')));
59
+ proc.once('error', (err) => this.dispose(err));
60
+ proc.stdin.on('error', (err) => this.killUnhealthy(err));
61
61
  }
62
62
 
63
63
  setNotificationHandler(handler: NotificationHandler) {
@@ -66,16 +66,18 @@ export class JsonRpcStdioClient {
66
66
 
67
67
  sendRequest(method: string, params?: unknown): Promise<unknown> {
68
68
  if (this.disposed) {
69
- return Promise.reject(new Error("JSON-RPC client disposed"));
69
+ return Promise.reject(new Error('JSON-RPC client disposed'));
70
70
  }
71
71
  const id = this.nextId++;
72
- const request: JsonRpcRequest = { jsonrpc: "2.0", id, method, params };
72
+ const request: JsonRpcRequest = { jsonrpc: '2.0', id, method, params };
73
73
  const promise = new Promise<unknown>((resolve, reject) => {
74
74
  const timer = setTimeout(() => {
75
75
  const pending = this.pending.get(id);
76
76
  if (!pending) return;
77
77
  this.pending.delete(id);
78
- reject(new Error(`JSON-RPC request "${method}" timed out after ${this.requestTimeoutMs}ms`));
78
+ reject(
79
+ new Error(`JSON-RPC request "${method}" timed out after ${this.requestTimeoutMs}ms`),
80
+ );
79
81
  this.killUnhealthy(new Error(`request "${method}" timed out; subprocess assumed hung`));
80
82
  }, this.requestTimeoutMs);
81
83
  this.pending.set(id, { resolve, reject, timer });
@@ -86,7 +88,7 @@ export class JsonRpcStdioClient {
86
88
 
87
89
  sendNotification(method: string, params?: unknown) {
88
90
  if (this.disposed) return;
89
- const notification: JsonRpcNotification = { jsonrpc: "2.0", method, params };
91
+ const notification: JsonRpcNotification = { jsonrpc: '2.0', method, params };
90
92
  this.writeFrame(notification);
91
93
  }
92
94
 
@@ -118,19 +120,19 @@ export class JsonRpcStdioClient {
118
120
  if (this.killProcess) {
119
121
  this.killProcess(this.proc);
120
122
  } else {
121
- this.proc.kill("SIGTERM");
123
+ this.proc.kill('SIGTERM');
122
124
  }
123
125
  }
124
126
  }
125
127
 
126
128
  private ingest(chunk: string) {
127
129
  this.buffer += chunk;
128
- let newlineIndex = this.buffer.indexOf("\n");
130
+ let newlineIndex = this.buffer.indexOf('\n');
129
131
  while (newlineIndex >= 0) {
130
132
  const line = this.buffer.slice(0, newlineIndex).trim();
131
133
  this.buffer = this.buffer.slice(newlineIndex + 1);
132
134
  if (line) this.handleLine(line);
133
- newlineIndex = this.buffer.indexOf("\n");
135
+ newlineIndex = this.buffer.indexOf('\n');
134
136
  }
135
137
  }
136
138
 
@@ -148,7 +150,7 @@ export class JsonRpcStdioClient {
148
150
  this.pending.delete(message.id);
149
151
  clearTimeout(pending.timer);
150
152
  if (message.error) {
151
- pending.reject(new Error(message.error.message || "JSON-RPC error"));
153
+ pending.reject(new Error(message.error.message || 'JSON-RPC error'));
152
154
  } else {
153
155
  pending.resolve(message.result);
154
156
  }
@@ -162,9 +164,9 @@ export class JsonRpcStdioClient {
162
164
  }
163
165
 
164
166
  function isResponse(m: unknown): m is JsonRpcResponse {
165
- return !!m && typeof m === "object" && "id" in m && ("result" in m || "error" in m);
167
+ return !!m && typeof m === 'object' && 'id' in m && ('result' in m || 'error' in m);
166
168
  }
167
169
 
168
170
  function isNotification(m: unknown): m is JsonRpcNotification {
169
- return !!m && typeof m === "object" && "method" in m && !("id" in m);
171
+ return !!m && typeof m === 'object' && 'method' in m && !('id' in m);
170
172
  }
@@ -1,6 +1,6 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import type { ForkSessionHandle } from "@parall/agent-core";
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import type { ForkSessionHandle } from '@parall/agent-core';
4
4
 
5
5
  type PersistedThread = {
6
6
  runtimeKey: string;
@@ -66,13 +66,17 @@ export class CodexSessionManager {
66
66
 
67
67
  private restore() {
68
68
  try {
69
- const raw = fs.readFileSync(this.stateFilePath, "utf8");
69
+ const raw = fs.readFileSync(this.stateFilePath, 'utf8');
70
70
  const parsed = JSON.parse(raw) as Partial<PersistedThread>;
71
- if (parsed.runtimeKey === this.mainSessionKey && typeof parsed.threadId === "string" && parsed.threadId.trim()) {
71
+ if (
72
+ parsed.runtimeKey === this.mainSessionKey &&
73
+ typeof parsed.threadId === 'string' &&
74
+ parsed.threadId.trim()
75
+ ) {
72
76
  this.threadIds.set(this.mainSessionKey, parsed.threadId.trim());
73
77
  }
74
78
  } catch (error) {
75
- if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") {
79
+ if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') {
76
80
  this.logger?.warn(
77
81
  `codex-agent: could not restore main thread state from ${this.stateFilePath}: ${String(error)}`,
78
82
  );