@posthog/agent 2.3.167 → 2.3.169

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,355 @@
1
+ /**
2
+ * In-process ACP proxy agent for Codex.
3
+ *
4
+ * Implements the ACP Agent interface and delegates to the codex-acp binary
5
+ * via a ClientSideConnection. This gives us interception points for:
6
+ * - PostHog-specific notifications (sdk_session, usage_update, turn_complete)
7
+ * - Session resume/fork (not natively supported by codex-acp)
8
+ * - Usage accumulation
9
+ * - System prompt injection
10
+ */
11
+
12
+ import {
13
+ type AgentSideConnection,
14
+ type AuthenticateRequest,
15
+ type CancelNotification,
16
+ ClientSideConnection,
17
+ type ForkSessionRequest,
18
+ type ForkSessionResponse,
19
+ type InitializeRequest,
20
+ type InitializeResponse,
21
+ type ListSessionsRequest,
22
+ type ListSessionsResponse,
23
+ type LoadSessionRequest,
24
+ type LoadSessionResponse,
25
+ type NewSessionRequest,
26
+ type NewSessionResponse,
27
+ ndJsonStream,
28
+ type PromptRequest,
29
+ type PromptResponse,
30
+ type ResumeSessionRequest,
31
+ type ResumeSessionResponse,
32
+ type SetSessionConfigOptionRequest,
33
+ type SetSessionConfigOptionResponse,
34
+ type SetSessionModeRequest,
35
+ type SetSessionModeResponse,
36
+ } from "@agentclientprotocol/sdk";
37
+ import packageJson from "../../../package.json" with { type: "json" };
38
+ import { POSTHOG_NOTIFICATIONS } from "../../acp-extensions";
39
+ import type { ProcessSpawnedCallback } from "../../types";
40
+ import { Logger } from "../../utils/logger";
41
+ import {
42
+ nodeReadableToWebReadable,
43
+ nodeWritableToWebWritable,
44
+ } from "../../utils/streams";
45
+ import { BaseAcpAgent, type BaseSession } from "../base-acp-agent";
46
+ import { createCodexClient } from "./codex-client";
47
+ import {
48
+ type CodexSessionState,
49
+ createSessionState,
50
+ resetUsage,
51
+ } from "./session-state";
52
+ import { CodexSettingsManager } from "./settings";
53
+ import {
54
+ type CodexProcess,
55
+ type CodexProcessOptions,
56
+ spawnCodexProcess,
57
+ } from "./spawn";
58
+
59
+ interface NewSessionMeta {
60
+ taskRunId?: string;
61
+ taskId?: string;
62
+ systemPrompt?: string;
63
+ permissionMode?: string;
64
+ model?: string;
65
+ persistence?: { taskId?: string; runId?: string; logUrl?: string };
66
+ claudeCode?: {
67
+ options?: Record<string, unknown>;
68
+ };
69
+ additionalRoots?: string[];
70
+ disableBuiltInTools?: boolean;
71
+ allowedDomains?: string[];
72
+ }
73
+
74
+ export interface CodexAcpAgentOptions {
75
+ codexProcessOptions: CodexProcessOptions;
76
+ processCallbacks?: ProcessSpawnedCallback;
77
+ }
78
+
79
+ type CodexSession = BaseSession & {
80
+ settingsManager: CodexSettingsManager;
81
+ };
82
+
83
+ export class CodexAcpAgent extends BaseAcpAgent {
84
+ readonly adapterName = "codex";
85
+ declare session: CodexSession;
86
+ private codexProcess: CodexProcess;
87
+ private codexConnection!: ClientSideConnection;
88
+ private sessionState!: CodexSessionState;
89
+
90
+ constructor(client: AgentSideConnection, options: CodexAcpAgentOptions) {
91
+ super(client);
92
+ this.logger = new Logger({ debug: true, prefix: "[CodexAcpAgent]" });
93
+
94
+ // Spawn the codex-acp subprocess
95
+ this.codexProcess = spawnCodexProcess({
96
+ ...options.codexProcessOptions,
97
+ logger: this.logger,
98
+ processCallbacks: options.processCallbacks,
99
+ });
100
+
101
+ // Create ACP connection to codex-acp over stdin/stdout
102
+ const codexReadable = nodeReadableToWebReadable(this.codexProcess.stdout);
103
+ const codexWritable = nodeWritableToWebWritable(this.codexProcess.stdin);
104
+ const codexStream = ndJsonStream(codexWritable, codexReadable);
105
+
106
+ // Set up session with CodexSettingsManager
107
+ const cwd = options.codexProcessOptions.cwd ?? process.cwd();
108
+ const settingsManager = new CodexSettingsManager(cwd);
109
+ const abortController = new AbortController();
110
+ this.session = {
111
+ abortController,
112
+ settingsManager,
113
+ notificationHistory: [],
114
+ cancelled: false,
115
+ };
116
+
117
+ // Create the ClientSideConnection to codex-acp.
118
+ // The Client handler delegates all requests from codex-acp to the upstream
119
+ // PostHog Code client via our AgentSideConnection.
120
+ this.codexConnection = new ClientSideConnection(
121
+ (_agent) =>
122
+ createCodexClient(
123
+ this.client,
124
+ this.logger,
125
+ this.sessionState ?? {
126
+ sessionId: "",
127
+ cwd: "",
128
+ modeId: "default",
129
+ configOptions: [],
130
+ accumulatedUsage: {
131
+ inputTokens: 0,
132
+ outputTokens: 0,
133
+ cachedReadTokens: 0,
134
+ cachedWriteTokens: 0,
135
+ },
136
+ cancelled: false,
137
+ },
138
+ ),
139
+ codexStream,
140
+ );
141
+ }
142
+
143
+ async initialize(request: InitializeRequest): Promise<InitializeResponse> {
144
+ // Initialize settings
145
+ await this.session.settingsManager.initialize();
146
+
147
+ // Forward to codex-acp
148
+ const response = await this.codexConnection.initialize(request);
149
+
150
+ // Merge our enhanced capabilities
151
+ return {
152
+ ...response,
153
+ agentCapabilities: {
154
+ ...response.agentCapabilities,
155
+ sessionCapabilities: {
156
+ ...response.agentCapabilities?.sessionCapabilities,
157
+ resume: {},
158
+ fork: {},
159
+ },
160
+ _meta: {
161
+ posthog: {
162
+ resumeSession: true,
163
+ },
164
+ },
165
+ },
166
+ agentInfo: {
167
+ name: packageJson.name,
168
+ title: "Codex Agent",
169
+ version: packageJson.version,
170
+ },
171
+ };
172
+ }
173
+
174
+ async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
175
+ const meta = params._meta as NewSessionMeta | undefined;
176
+
177
+ const response = await this.codexConnection.newSession(params);
178
+
179
+ // Initialize session state
180
+ this.sessionState = createSessionState(response.sessionId, params.cwd, {
181
+ taskRunId: meta?.taskRunId,
182
+ taskId: meta?.taskId ?? meta?.persistence?.taskId,
183
+ modeId: response.modes?.currentModeId ?? "default",
184
+ modelId: response.models?.currentModelId,
185
+ });
186
+ this.sessionId = response.sessionId;
187
+ this.sessionState.configOptions = response.configOptions ?? [];
188
+
189
+ // Emit _posthog/sdk_session so the app can track the session
190
+ if (meta?.taskRunId) {
191
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
192
+ taskRunId: meta.taskRunId,
193
+ sessionId: response.sessionId,
194
+ adapter: "codex",
195
+ });
196
+ }
197
+
198
+ this.logger.info("Codex session created", {
199
+ sessionId: response.sessionId,
200
+ taskRunId: meta?.taskRunId,
201
+ });
202
+
203
+ return response;
204
+ }
205
+
206
+ async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
207
+ const response = await this.codexConnection.loadSession(params);
208
+
209
+ // Update session state
210
+ this.sessionState = createSessionState(params.sessionId, params.cwd);
211
+ this.sessionId = params.sessionId;
212
+ this.sessionState.configOptions = response.configOptions ?? [];
213
+
214
+ return response;
215
+ }
216
+
217
+ async unstable_resumeSession(
218
+ params: ResumeSessionRequest,
219
+ ): Promise<ResumeSessionResponse> {
220
+ // codex-acp doesn't support resume natively, use loadSession instead
221
+ const loadResponse = await this.codexConnection.loadSession({
222
+ sessionId: params.sessionId,
223
+ cwd: params.cwd,
224
+ mcpServers: params.mcpServers ?? [],
225
+ });
226
+
227
+ this.sessionState = createSessionState(params.sessionId, params.cwd);
228
+ this.sessionId = params.sessionId;
229
+ this.sessionState.configOptions = loadResponse.configOptions ?? [];
230
+
231
+ const meta = params._meta as NewSessionMeta | undefined;
232
+ if (meta?.taskRunId) {
233
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
234
+ taskRunId: meta.taskRunId,
235
+ sessionId: params.sessionId,
236
+ adapter: "codex",
237
+ });
238
+ }
239
+
240
+ return {
241
+ modes: loadResponse.modes,
242
+ models: loadResponse.models,
243
+ configOptions: loadResponse.configOptions,
244
+ };
245
+ }
246
+
247
+ async unstable_forkSession(
248
+ params: ForkSessionRequest,
249
+ ): Promise<ForkSessionResponse> {
250
+ // Create a new session via codex-acp (fork isn't natively supported)
251
+ const newResponse = await this.codexConnection.newSession({
252
+ cwd: params.cwd,
253
+ mcpServers: params.mcpServers ?? [],
254
+ _meta: params._meta,
255
+ });
256
+
257
+ this.sessionState = createSessionState(newResponse.sessionId, params.cwd);
258
+ this.sessionId = newResponse.sessionId;
259
+ this.sessionState.configOptions = newResponse.configOptions ?? [];
260
+
261
+ return newResponse;
262
+ }
263
+
264
+ async listSessions(
265
+ params: ListSessionsRequest,
266
+ ): Promise<ListSessionsResponse> {
267
+ return this.codexConnection.listSessions(params);
268
+ }
269
+
270
+ async unstable_listSessions(
271
+ params: ListSessionsRequest,
272
+ ): Promise<ListSessionsResponse> {
273
+ return this.codexConnection.listSessions(params);
274
+ }
275
+
276
+ async prompt(params: PromptRequest): Promise<PromptResponse> {
277
+ if (this.sessionState) {
278
+ this.sessionState.cancelled = false;
279
+ this.sessionState.interruptReason = undefined;
280
+ resetUsage(this.sessionState);
281
+ }
282
+
283
+ const response = await this.codexConnection.prompt(params);
284
+
285
+ // Emit PostHog usage notification
286
+ if (this.sessionState?.taskRunId && response.usage) {
287
+ await this.client.extNotification("_posthog/usage_update", {
288
+ sessionId: params.sessionId,
289
+ used: {
290
+ inputTokens: response.usage.inputTokens ?? 0,
291
+ outputTokens: response.usage.outputTokens ?? 0,
292
+ cachedReadTokens: response.usage.cachedReadTokens ?? 0,
293
+ cachedWriteTokens: response.usage.cachedWriteTokens ?? 0,
294
+ },
295
+ cost: null,
296
+ });
297
+ }
298
+
299
+ return response;
300
+ }
301
+
302
+ protected async interrupt(): Promise<void> {
303
+ if (this.sessionState) {
304
+ this.sessionState.cancelled = true;
305
+ }
306
+ await this.codexConnection.cancel({
307
+ sessionId: this.sessionId,
308
+ });
309
+ }
310
+
311
+ async cancel(params: CancelNotification): Promise<void> {
312
+ if (this.sessionState) {
313
+ this.sessionState.cancelled = true;
314
+ const meta = params._meta as { interruptReason?: string } | undefined;
315
+ if (meta?.interruptReason) {
316
+ this.sessionState.interruptReason = meta.interruptReason;
317
+ }
318
+ }
319
+ await this.codexConnection.cancel(params);
320
+ }
321
+
322
+ async setSessionMode(
323
+ params: SetSessionModeRequest,
324
+ ): Promise<SetSessionModeResponse> {
325
+ const response = await this.codexConnection.setSessionMode(params);
326
+ if (this.sessionState) {
327
+ this.sessionState.modeId = params.modeId;
328
+ }
329
+ return response ?? {};
330
+ }
331
+
332
+ async setSessionConfigOption(
333
+ params: SetSessionConfigOptionRequest,
334
+ ): Promise<SetSessionConfigOptionResponse> {
335
+ const response = await this.codexConnection.setSessionConfigOption(params);
336
+ if (this.sessionState && response.configOptions) {
337
+ this.sessionState.configOptions = response.configOptions;
338
+ }
339
+ return response;
340
+ }
341
+
342
+ async authenticate(_params: AuthenticateRequest): Promise<void> {
343
+ // Auth handled externally
344
+ }
345
+
346
+ async closeSession(): Promise<void> {
347
+ this.logger.info("Closing Codex session", { sessionId: this.sessionId });
348
+ this.session.settingsManager.dispose();
349
+ try {
350
+ this.codexProcess.kill();
351
+ } catch (err) {
352
+ this.logger.warn("Failed to kill codex-acp process", { error: err });
353
+ }
354
+ }
355
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * ACP Client implementation for communicating with codex-acp subprocess.
3
+ *
4
+ * This acts as the "client" from codex-acp's perspective: it receives
5
+ * permission requests, session updates, file I/O, and terminal operations
6
+ * from codex-acp and delegates them to the upstream PostHog Code client.
7
+ */
8
+
9
+ import type {
10
+ AgentSideConnection,
11
+ Client,
12
+ CreateTerminalRequest,
13
+ CreateTerminalResponse,
14
+ KillTerminalRequest,
15
+ KillTerminalResponse,
16
+ ReadTextFileRequest,
17
+ ReadTextFileResponse,
18
+ ReleaseTerminalRequest,
19
+ ReleaseTerminalResponse,
20
+ RequestPermissionRequest,
21
+ RequestPermissionResponse,
22
+ SessionNotification,
23
+ TerminalHandle,
24
+ TerminalOutputRequest,
25
+ TerminalOutputResponse,
26
+ WaitForTerminalExitRequest,
27
+ WaitForTerminalExitResponse,
28
+ WriteTextFileRequest,
29
+ WriteTextFileResponse,
30
+ } from "@agentclientprotocol/sdk";
31
+ import type { Logger } from "../../utils/logger";
32
+ import type { CodexSessionState } from "./session-state";
33
+
34
+ export interface CodexClientCallbacks {
35
+ /** Called when a usage_update session notification is received */
36
+ onUsageUpdate?: (update: Record<string, unknown>) => void;
37
+ }
38
+
39
+ /**
40
+ * Creates an ACP Client that delegates all requests from codex-acp
41
+ * to the upstream PostHog Code client (via AgentSideConnection).
42
+ */
43
+ export function createCodexClient(
44
+ upstreamClient: AgentSideConnection,
45
+ logger: Logger,
46
+ sessionState: CodexSessionState,
47
+ callbacks?: CodexClientCallbacks,
48
+ ): Client {
49
+ // Track terminal handles for delegation
50
+ const terminalHandles = new Map<string, TerminalHandle>();
51
+
52
+ return {
53
+ async requestPermission(
54
+ params: RequestPermissionRequest,
55
+ ): Promise<RequestPermissionResponse> {
56
+ logger.debug("Relaying permission request to upstream", {
57
+ sessionId: params.sessionId,
58
+ });
59
+ return upstreamClient.requestPermission(params);
60
+ },
61
+
62
+ async sessionUpdate(params: SessionNotification): Promise<void> {
63
+ // Parse usage data from session updates
64
+ const update = params.update as Record<string, unknown> | undefined;
65
+ if (update?.sessionUpdate === "usage_update") {
66
+ const used = update.used as number | undefined;
67
+ const size = update.size as number | undefined;
68
+ if (used !== undefined) sessionState.contextUsed = used;
69
+ if (size !== undefined) sessionState.contextSize = size;
70
+ callbacks?.onUsageUpdate?.(update);
71
+ }
72
+
73
+ // Forward to upstream client
74
+ await upstreamClient.sessionUpdate(params);
75
+ },
76
+
77
+ async readTextFile(
78
+ params: ReadTextFileRequest,
79
+ ): Promise<ReadTextFileResponse> {
80
+ return upstreamClient.readTextFile(params);
81
+ },
82
+
83
+ async writeTextFile(
84
+ params: WriteTextFileRequest,
85
+ ): Promise<WriteTextFileResponse> {
86
+ return upstreamClient.writeTextFile(params);
87
+ },
88
+
89
+ async createTerminal(
90
+ params: CreateTerminalRequest,
91
+ ): Promise<CreateTerminalResponse> {
92
+ const handle = await upstreamClient.createTerminal(params);
93
+ terminalHandles.set(handle.id, handle);
94
+ return { terminalId: handle.id };
95
+ },
96
+
97
+ async terminalOutput(
98
+ params: TerminalOutputRequest,
99
+ ): Promise<TerminalOutputResponse> {
100
+ const handle = terminalHandles.get(params.terminalId);
101
+ if (!handle) {
102
+ return { output: "", truncated: false };
103
+ }
104
+ return handle.currentOutput();
105
+ },
106
+
107
+ async releaseTerminal(
108
+ params: ReleaseTerminalRequest,
109
+ ): Promise<ReleaseTerminalResponse | undefined> {
110
+ const handle = terminalHandles.get(params.terminalId);
111
+ if (handle) {
112
+ terminalHandles.delete(params.terminalId);
113
+ const result = await handle.release();
114
+ return result ?? undefined;
115
+ }
116
+ },
117
+
118
+ async waitForTerminalExit(
119
+ params: WaitForTerminalExitRequest,
120
+ ): Promise<WaitForTerminalExitResponse> {
121
+ const handle = terminalHandles.get(params.terminalId);
122
+ if (!handle) {
123
+ return { exitCode: 1 };
124
+ }
125
+ return handle.waitForExit();
126
+ },
127
+
128
+ async killTerminal(
129
+ params: KillTerminalRequest,
130
+ ): Promise<KillTerminalResponse | undefined> {
131
+ const handle = terminalHandles.get(params.terminalId);
132
+ if (handle) {
133
+ return handle.kill();
134
+ }
135
+ },
136
+
137
+ async extMethod(
138
+ method: string,
139
+ params: Record<string, unknown>,
140
+ ): Promise<Record<string, unknown>> {
141
+ return upstreamClient.extMethod(method, params);
142
+ },
143
+
144
+ async extNotification(
145
+ method: string,
146
+ params: Record<string, unknown>,
147
+ ): Promise<void> {
148
+ return upstreamClient.extNotification(method, params);
149
+ },
150
+ };
151
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Session state tracking for Codex proxy agent.
3
+ * Tracks usage accumulation, model/mode state, and config options.
4
+ */
5
+
6
+ import type { SessionConfigOption } from "@agentclientprotocol/sdk";
7
+
8
+ export interface CodexUsage {
9
+ inputTokens: number;
10
+ outputTokens: number;
11
+ cachedReadTokens: number;
12
+ cachedWriteTokens: number;
13
+ }
14
+
15
+ export interface CodexSessionState {
16
+ sessionId: string;
17
+ cwd: string;
18
+ modelId?: string;
19
+ modeId: string;
20
+ configOptions: SessionConfigOption[];
21
+ accumulatedUsage: CodexUsage;
22
+ contextSize?: number;
23
+ contextUsed?: number;
24
+ cancelled: boolean;
25
+ interruptReason?: string;
26
+ taskRunId?: string;
27
+ taskId?: string;
28
+ }
29
+
30
+ export function createSessionState(
31
+ sessionId: string,
32
+ cwd: string,
33
+ opts?: {
34
+ taskRunId?: string;
35
+ taskId?: string;
36
+ modeId?: string;
37
+ modelId?: string;
38
+ },
39
+ ): CodexSessionState {
40
+ return {
41
+ sessionId,
42
+ cwd,
43
+ modeId: opts?.modeId ?? "default",
44
+ modelId: opts?.modelId,
45
+ configOptions: [],
46
+ accumulatedUsage: {
47
+ inputTokens: 0,
48
+ outputTokens: 0,
49
+ cachedReadTokens: 0,
50
+ cachedWriteTokens: 0,
51
+ },
52
+ cancelled: false,
53
+ taskRunId: opts?.taskRunId,
54
+ taskId: opts?.taskId,
55
+ };
56
+ }
57
+
58
+ export function resetUsage(state: CodexSessionState): void {
59
+ state.accumulatedUsage = {
60
+ inputTokens: 0,
61
+ outputTokens: 0,
62
+ cachedReadTokens: 0,
63
+ cachedWriteTokens: 0,
64
+ };
65
+ }
@@ -0,0 +1,127 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+
5
+ /**
6
+ * Codex settings parsed from ~/.codex/config.toml and project-level config.
7
+ *
8
+ * Mirrors the shape of ClaudeCodeSettings so both adapters have a
9
+ * consistent settings interface.
10
+ */
11
+ export interface CodexSettings {
12
+ model?: string;
13
+ personality?: string;
14
+ modelReasoningEffort?: string;
15
+ trustLevel?: string;
16
+ }
17
+
18
+ /**
19
+ * SettingsManager for Codex sessions.
20
+ *
21
+ * Reads from ~/.codex/config.toml (user-level) and respects
22
+ * per-project trust configuration. Has the same public interface
23
+ * as Claude's SettingsManager so both can satisfy BaseSession.
24
+ */
25
+ export class CodexSettingsManager {
26
+ private cwd: string;
27
+ private settings: CodexSettings = {};
28
+ private initialized = false;
29
+
30
+ constructor(cwd: string) {
31
+ this.cwd = cwd;
32
+ }
33
+
34
+ async initialize(): Promise<void> {
35
+ if (this.initialized) {
36
+ return;
37
+ }
38
+ await this.loadSettings();
39
+ this.initialized = true;
40
+ }
41
+
42
+ private getConfigPath(): string {
43
+ return path.join(os.homedir(), ".codex", "config.toml");
44
+ }
45
+
46
+ private async loadSettings(): Promise<void> {
47
+ const configPath = this.getConfigPath();
48
+ try {
49
+ const content = await fs.promises.readFile(configPath, "utf-8");
50
+ this.settings = parseCodexToml(content, this.cwd);
51
+ } catch {
52
+ this.settings = {};
53
+ }
54
+ }
55
+
56
+ getSettings(): CodexSettings {
57
+ return this.settings;
58
+ }
59
+
60
+ getCwd(): string {
61
+ return this.cwd;
62
+ }
63
+
64
+ async setCwd(cwd: string): Promise<void> {
65
+ if (this.cwd === cwd) {
66
+ return;
67
+ }
68
+ this.dispose();
69
+ this.cwd = cwd;
70
+ this.initialized = false;
71
+ await this.initialize();
72
+ }
73
+
74
+ dispose(): void {
75
+ this.initialized = false;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Minimal TOML parser for codex config.toml.
81
+ * Handles flat key=value pairs and [projects."path"] sections.
82
+ * Does NOT handle full TOML spec — only what codex config uses.
83
+ */
84
+ function parseCodexToml(content: string, cwd: string): CodexSettings {
85
+ const settings: CodexSettings = {};
86
+ let currentSection = "";
87
+
88
+ for (const line of content.split("\n")) {
89
+ const trimmed = line.trim();
90
+ if (!trimmed || trimmed.startsWith("#")) continue;
91
+
92
+ // Section header: [projects."/some/path"] or [section]
93
+ const sectionMatch = trimmed.match(/^\[(.+)\]$/);
94
+ if (sectionMatch) {
95
+ currentSection = sectionMatch[1] ?? "";
96
+ continue;
97
+ }
98
+
99
+ // Key = value
100
+ const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
101
+ if (!kvMatch) continue;
102
+
103
+ const key = kvMatch[1];
104
+ let value = kvMatch[2]?.trim() ?? "";
105
+
106
+ // Strip quotes
107
+ if (
108
+ (value.startsWith('"') && value.endsWith('"')) ||
109
+ (value.startsWith("'") && value.endsWith("'"))
110
+ ) {
111
+ value = value.slice(1, -1);
112
+ }
113
+
114
+ if (!currentSection) {
115
+ // Top-level keys
116
+ if (key === "model") settings.model = value;
117
+ if (key === "personality") settings.personality = value;
118
+ if (key === "model_reasoning_effort")
119
+ settings.modelReasoningEffort = value;
120
+ } else if (currentSection === `projects."${cwd}"`) {
121
+ // Project-specific keys
122
+ if (key === "trust_level") settings.trustLevel = value;
123
+ }
124
+ }
125
+
126
+ return settings;
127
+ }
package/src/agent.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  } from "./adapters/acp-connection";
5
5
  import {
6
6
  BLOCKED_MODELS,
7
+ DEFAULT_CODEX_MODEL,
7
8
  DEFAULT_GATEWAY_MODEL,
8
9
  fetchModelsList,
9
10
  } from "./gateway-models";
@@ -104,7 +105,9 @@ export class Agent {
104
105
  }
105
106
 
106
107
  if (!sanitizedModel || !allowedModelIds?.has(sanitizedModel)) {
107
- sanitizedModel = codexModelIds[0];
108
+ sanitizedModel = codexModelIds.includes(DEFAULT_CODEX_MODEL)
109
+ ? DEFAULT_CODEX_MODEL
110
+ : codexModelIds[0];
108
111
  }
109
112
  }
110
113
  if (!sanitizedModel && options.adapter !== "codex") {