@parall/codex-agent 1.30.0 → 1.32.0

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 } 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) {
@@ -30,125 +51,186 @@ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string)
30
51
  }
31
52
  }
32
53
 
54
+ function resolveProviderEnv(): void {
55
+ const pc = parseProviderConfig(process.env);
56
+ if (!pc) return;
57
+ clearAllProviderCreds(process.env);
58
+ const source = llmSource(pc);
59
+ if (source === 'parall') {
60
+ process.env.OPENAI_API_KEY = process.env.PRLL_API_KEY;
61
+ process.env.OPENAI_BASE_URL = `${process.env.PRLL_API_URL}/api/llm/v1`;
62
+ } else if (source === 'custom') {
63
+ if (pc.openai_api_key) process.env.OPENAI_API_KEY = pc.openai_api_key;
64
+ if (pc.openai_base_url) process.env.OPENAI_BASE_URL = pc.openai_base_url;
65
+ }
66
+ }
67
+
33
68
  async function main() {
34
- 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);
35
74
 
36
- const client = new ParallClient({
37
- baseUrl: config.apiUrl,
38
- token: config.apiKey,
39
- swimlaneName: config.swimlaneName,
40
- });
75
+ const client = new ParallClient({
76
+ baseUrl: config.apiUrl,
77
+ token: config.apiKey,
78
+ swimlaneName: config.swimlaneName,
79
+ });
41
80
 
42
- const me = await getAgentMeWithLegacyFallback(client, config.orgId);
43
- const agentUserId = me.id;
44
- const agentLog = childLogger(log, agentUserId);
45
- activeLog = agentLog;
46
- ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
47
- const useParallProvider = isParallProxyMode();
48
- if (useParallProvider) {
49
- ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
50
- agentLog.info("parall custom provider configured (Responses API HTTP/SSE mode)");
51
- }
52
- ensureCodexWorkspace(config.workspaceDir, agentLog, {
53
- userId: agentUserId,
54
- displayName: me.display_name,
55
- description: me.agent_profile?.description ?? undefined,
56
- });
57
- const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
58
- const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
59
- const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
60
- const sessionManager = new CodexSessionManager(
61
- runtimeKey,
62
- sessionStateFilePath,
63
- agentLog,
64
- );
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);
65
100
 
66
- const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
67
- const ws = new ParallWs({
68
- getTicket: () => client.getWsTicket(),
69
- wsUrl: resolvedWsUrl,
70
- });
101
+ const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
102
+ const ws = new ParallWs({
103
+ getTicket: () => client.getWsTicket(),
104
+ wsUrl: resolvedWsUrl,
105
+ });
71
106
 
72
- const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log: agentLog });
73
- const platformDefaults = await configMgr.fetch();
74
- let platformManaged = isPlatformManagedProfile(me.agent_profile);
75
- const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
76
- const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
77
- if (platformDefaults.model) agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
78
- 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
+ );
79
141
 
80
- const adapter = new CodexAppServerAdapter({
81
- codexBin: config.codexBin,
82
- codexHome: config.codexHome,
83
- workspaceDir: config.workspaceDir,
84
- model: resolvedModel,
85
- reasoningEffort: resolvedEffort,
86
- sandbox: config.sandbox,
87
- approvalPolicy: config.approvalPolicy,
88
- sessionManager,
89
- log: agentLog,
90
- contextFilePath: mainContextFilePath,
91
- useParallProvider,
92
- });
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
+ });
93
155
 
94
- const gateway = new ParallAgentGateway({
95
- accountId: agentUserId,
96
- client,
97
- ws,
98
- connectionLabel: resolvedWsUrl,
99
- config: {
100
- parall_url: config.apiUrl,
101
- api_key: config.apiKey,
102
- org_id: config.orgId,
103
- },
104
- agentUserId,
105
- runtimeType: "codex",
106
- runtimeKey,
107
- runtimeRef: {
108
- hostname: os.hostname(),
109
- pid: process.pid,
110
- workspace_dir: config.workspaceDir,
111
- codex_home: config.codexHome,
112
- driver: "app-server",
113
- },
114
- dispatchAdapter: adapter,
115
- log: agentLog,
116
- shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
117
- contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
118
- stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
119
- onConfigUpdate: async () => {
120
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
121
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
122
- const updated = await configMgr.fetch();
123
- adapter.updateConfig({
124
- model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
125
- reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
126
- });
127
- },
128
- onSessionReady: async () => {
129
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
130
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
131
- const updated = await configMgr.fetch();
132
- adapter.updateConfig({
133
- model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
134
- reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
135
- });
136
- },
137
- onNewSession: () => { sessionManager.clearMainThread(); },
138
- });
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
+ });
139
216
 
140
- const abortController = new AbortController();
141
- const abort = () => abortController.abort();
142
- process.on("SIGINT", abort);
143
- process.on("SIGTERM", abort);
217
+ const abortController = new AbortController();
218
+ const abort = () => abortController.abort();
219
+ process.on('SIGINT', abort);
220
+ process.on('SIGTERM', abort);
144
221
 
145
- try {
146
- agentLog.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
147
- 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
+ }
148
232
  } finally {
149
- await adapter.stop();
150
- process.off("SIGINT", abort);
151
- process.off("SIGTERM", abort);
233
+ await telemetry.shutdown();
152
234
  }
153
235
  }
154
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
  );