@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.
package/dist/agent.js CHANGED
@@ -1,131 +1,5 @@
1
1
  // src/adapters/acp-connection.ts
2
- import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
3
-
4
- // src/acp-extensions.ts
5
- var POSTHOG_NOTIFICATIONS = {
6
- /** Git branch was created for a task */
7
- BRANCH_CREATED: "_posthog/branch_created",
8
- /** Task run has started execution */
9
- RUN_STARTED: "_posthog/run_started",
10
- /** Task has completed (success or failure) */
11
- TASK_COMPLETE: "_posthog/task_complete",
12
- /** Agent finished processing a turn (prompt returned, waiting for next input) */
13
- TURN_COMPLETE: "_posthog/turn_complete",
14
- /** Error occurred during task execution */
15
- ERROR: "_posthog/error",
16
- /** Console/log output from the agent */
17
- CONSOLE: "_posthog/console",
18
- /** Maps taskRunId to agent's sessionId and adapter type (for resumption) */
19
- SDK_SESSION: "_posthog/sdk_session",
20
- /** Tree state snapshot captured (git tree hash + file archive) */
21
- TREE_SNAPSHOT: "_posthog/tree_snapshot",
22
- /** Agent mode changed (interactive/background) */
23
- MODE_CHANGE: "_posthog/mode_change",
24
- /** Request to resume a session from previous state */
25
- SESSION_RESUME: "_posthog/session/resume",
26
- /** User message sent from client to agent */
27
- USER_MESSAGE: "_posthog/user_message",
28
- /** Request to cancel current operation */
29
- CANCEL: "_posthog/cancel",
30
- /** Request to close the session */
31
- CLOSE: "_posthog/close",
32
- /** Agent status update (thinking, working, etc.) */
33
- STATUS: "_posthog/status",
34
- /** Task-level notification (progress, milestones) */
35
- TASK_NOTIFICATION: "_posthog/task_notification",
36
- /** Marks a boundary for log compaction */
37
- COMPACT_BOUNDARY: "_posthog/compact_boundary"
38
- };
39
-
40
- // src/gateway-models.ts
41
- var DEFAULT_GATEWAY_MODEL = "claude-opus-4-6";
42
- var BLOCKED_MODELS = /* @__PURE__ */ new Set(["gpt-5-mini", "openai/gpt-5-mini"]);
43
- var CACHE_TTL = 10 * 60 * 1e3;
44
- var gatewayModelsCache = null;
45
- async function fetchGatewayModels(options) {
46
- const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
47
- if (!gatewayUrl) {
48
- return [];
49
- }
50
- if (gatewayModelsCache && gatewayModelsCache.url === gatewayUrl && Date.now() < gatewayModelsCache.expiry) {
51
- return gatewayModelsCache.models;
52
- }
53
- const modelsUrl = `${gatewayUrl}/v1/models`;
54
- try {
55
- const response = await fetch(modelsUrl);
56
- if (!response.ok) {
57
- return [];
58
- }
59
- const data = await response.json();
60
- const models = (data.data ?? []).filter((m) => !BLOCKED_MODELS.has(m.id));
61
- gatewayModelsCache = {
62
- models,
63
- expiry: Date.now() + CACHE_TTL,
64
- url: gatewayUrl
65
- };
66
- return models;
67
- } catch {
68
- return [];
69
- }
70
- }
71
- function isAnthropicModel(model) {
72
- if (model.owned_by) {
73
- return model.owned_by === "anthropic";
74
- }
75
- return model.id.startsWith("claude-") || model.id.startsWith("anthropic/");
76
- }
77
- var modelsListCache = null;
78
- async function fetchModelsList(options) {
79
- const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
80
- if (!gatewayUrl) {
81
- return [];
82
- }
83
- if (modelsListCache && modelsListCache.url === gatewayUrl && Date.now() < modelsListCache.expiry) {
84
- return modelsListCache.models;
85
- }
86
- try {
87
- const modelsUrl = `${gatewayUrl}/v1/models`;
88
- const response = await fetch(modelsUrl);
89
- if (!response.ok) {
90
- return [];
91
- }
92
- const data = await response.json();
93
- const models = Array.isArray(data) ? data : data.data ?? data.models ?? [];
94
- const results = [];
95
- for (const model of models) {
96
- const id = model?.id ? String(model.id) : "";
97
- if (!id) continue;
98
- results.push({ id, owned_by: model?.owned_by });
99
- }
100
- modelsListCache = {
101
- models: results,
102
- expiry: Date.now() + CACHE_TTL,
103
- url: gatewayUrl
104
- };
105
- return results;
106
- } catch {
107
- return [];
108
- }
109
- }
110
- var PROVIDER_PREFIXES = ["anthropic/", "openai/", "google-vertex/"];
111
- function formatGatewayModelName(model) {
112
- return formatModelId(model.id);
113
- }
114
- function formatModelId(modelId) {
115
- let cleanId = modelId;
116
- for (const prefix of PROVIDER_PREFIXES) {
117
- if (cleanId.startsWith(prefix)) {
118
- cleanId = cleanId.slice(prefix.length);
119
- break;
120
- }
121
- }
122
- cleanId = cleanId.replace(/(\d)-(\d)/g, "$1.$2");
123
- const words = cleanId.split(/[-_]/).map((word) => {
124
- if (word.match(/^[0-9.]+$/)) return word;
125
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
126
- });
127
- return words.join(" ");
128
- }
2
+ import { AgentSideConnection, ndJsonStream as ndJsonStream2 } from "@agentclientprotocol/sdk";
129
3
 
130
4
  // src/utils/logger.ts
131
5
  var Logger = class _Logger {
@@ -181,7 +55,7 @@ var Logger = class _Logger {
181
55
  };
182
56
 
183
57
  // src/utils/streams.ts
184
- import { ReadableStream, WritableStream as WritableStream2 } from "stream/web";
58
+ import { ReadableStream, WritableStream } from "stream/web";
185
59
  var Pushable = class {
186
60
  queue = [];
187
61
  resolvers = [];
@@ -239,7 +113,7 @@ function createBidirectionalStreams() {
239
113
  const agentToClientPushable = new Pushable();
240
114
  const clientToAgentReadable = pushableToReadableStream(clientToAgentPushable);
241
115
  const agentToClientReadable = pushableToReadableStream(agentToClientPushable);
242
- const clientToAgentWritable = new WritableStream2({
116
+ const clientToAgentWritable = new WritableStream({
243
117
  write(chunk) {
244
118
  clientToAgentPushable.push(chunk);
245
119
  },
@@ -247,7 +121,7 @@ function createBidirectionalStreams() {
247
121
  clientToAgentPushable.end();
248
122
  }
249
123
  });
250
- const agentToClientWritable = new WritableStream2({
124
+ const agentToClientWritable = new WritableStream({
251
125
  write(chunk) {
252
126
  agentToClientPushable.push(chunk);
253
127
  },
@@ -271,7 +145,7 @@ function createTappedWritableStream(underlying, options) {
271
145
  const decoder = new TextDecoder();
272
146
  let buffer = "";
273
147
  let _messageCount = 0;
274
- return new WritableStream2({
148
+ return new WritableStream({
275
149
  async write(chunk) {
276
150
  buffer += decoder.decode(chunk, { stream: true });
277
151
  const lines = buffer.split("\n");
@@ -327,7 +201,7 @@ function nodeReadableToWebReadable(nodeStream) {
327
201
  });
328
202
  }
329
203
  function nodeWritableToWebWritable(nodeStream) {
330
- return new WritableStream2({
204
+ return new WritableStream({
331
205
  write(chunk) {
332
206
  return new Promise((resolve3, reject) => {
333
207
  const ok = nodeStream.write(Buffer.from(chunk), (err) => {
@@ -371,7 +245,7 @@ import { v7 as uuidv7 } from "uuid";
371
245
  // package.json
372
246
  var package_default = {
373
247
  name: "@posthog/agent",
374
- version: "2.3.167",
248
+ version: "2.3.169",
375
249
  repository: "https://github.com/PostHog/code",
376
250
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
377
251
  exports: {
@@ -518,6 +392,97 @@ function unreachable(value, logger) {
518
392
  logger.error(`Unexpected case: ${valueAsString}`);
519
393
  }
520
394
 
395
+ // src/gateway-models.ts
396
+ var DEFAULT_GATEWAY_MODEL = "claude-opus-4-6";
397
+ var DEFAULT_CODEX_MODEL = "gpt-5.4";
398
+ var BLOCKED_MODELS = /* @__PURE__ */ new Set(["gpt-5-mini", "openai/gpt-5-mini"]);
399
+ var CACHE_TTL = 10 * 60 * 1e3;
400
+ var gatewayModelsCache = null;
401
+ async function fetchGatewayModels(options) {
402
+ const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
403
+ if (!gatewayUrl) {
404
+ return [];
405
+ }
406
+ if (gatewayModelsCache && gatewayModelsCache.url === gatewayUrl && Date.now() < gatewayModelsCache.expiry) {
407
+ return gatewayModelsCache.models;
408
+ }
409
+ const modelsUrl = `${gatewayUrl}/v1/models`;
410
+ try {
411
+ const response = await fetch(modelsUrl);
412
+ if (!response.ok) {
413
+ return [];
414
+ }
415
+ const data = await response.json();
416
+ const models = (data.data ?? []).filter((m) => !BLOCKED_MODELS.has(m.id));
417
+ gatewayModelsCache = {
418
+ models,
419
+ expiry: Date.now() + CACHE_TTL,
420
+ url: gatewayUrl
421
+ };
422
+ return models;
423
+ } catch {
424
+ return [];
425
+ }
426
+ }
427
+ function isAnthropicModel(model) {
428
+ if (model.owned_by) {
429
+ return model.owned_by === "anthropic";
430
+ }
431
+ return model.id.startsWith("claude-") || model.id.startsWith("anthropic/");
432
+ }
433
+ var modelsListCache = null;
434
+ async function fetchModelsList(options) {
435
+ const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
436
+ if (!gatewayUrl) {
437
+ return [];
438
+ }
439
+ if (modelsListCache && modelsListCache.url === gatewayUrl && Date.now() < modelsListCache.expiry) {
440
+ return modelsListCache.models;
441
+ }
442
+ try {
443
+ const modelsUrl = `${gatewayUrl}/v1/models`;
444
+ const response = await fetch(modelsUrl);
445
+ if (!response.ok) {
446
+ return [];
447
+ }
448
+ const data = await response.json();
449
+ const models = Array.isArray(data) ? data : data.data ?? data.models ?? [];
450
+ const results = [];
451
+ for (const model of models) {
452
+ const id = model?.id ? String(model.id) : "";
453
+ if (!id) continue;
454
+ results.push({ id, owned_by: model?.owned_by });
455
+ }
456
+ modelsListCache = {
457
+ models: results,
458
+ expiry: Date.now() + CACHE_TTL,
459
+ url: gatewayUrl
460
+ };
461
+ return results;
462
+ } catch {
463
+ return [];
464
+ }
465
+ }
466
+ var PROVIDER_PREFIXES = ["anthropic/", "openai/", "google-vertex/"];
467
+ function formatGatewayModelName(model) {
468
+ return formatModelId(model.id);
469
+ }
470
+ function formatModelId(modelId) {
471
+ let cleanId = modelId;
472
+ for (const prefix of PROVIDER_PREFIXES) {
473
+ if (cleanId.startsWith(prefix)) {
474
+ cleanId = cleanId.slice(prefix.length);
475
+ break;
476
+ }
477
+ }
478
+ cleanId = cleanId.replace(/(\d)-(\d)/g, "$1.$2");
479
+ const words = cleanId.split(/[-_]/).map((word) => {
480
+ if (word.match(/^[0-9.]+$/)) return word;
481
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
482
+ });
483
+ return words.join(" ");
484
+ }
485
+
521
486
  // src/adapters/base-acp-agent.ts
522
487
  var DEFAULT_CONTEXT_WINDOW = 2e5;
523
488
  var BaseAcpAgent = class {
@@ -725,8 +690,8 @@ var ToolContentBuilder = class {
725
690
  this.items.push({ type: "content", content: image(data, mimeType, uri) });
726
691
  return this;
727
692
  }
728
- diff(path8, oldText, newText) {
729
- this.items.push({ type: "diff", path: path8, oldText, newText });
693
+ diff(path9, oldText, newText) {
694
+ this.items.push({ type: "diff", path: path9, oldText, newText });
730
695
  return this;
731
696
  }
732
697
  build() {
@@ -4137,6 +4102,224 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4137
4102
  }
4138
4103
  };
4139
4104
 
4105
+ // src/adapters/codex/codex-agent.ts
4106
+ import {
4107
+ ClientSideConnection,
4108
+ ndJsonStream
4109
+ } from "@agentclientprotocol/sdk";
4110
+
4111
+ // src/acp-extensions.ts
4112
+ var POSTHOG_NOTIFICATIONS = {
4113
+ /** Git branch was created for a task */
4114
+ BRANCH_CREATED: "_posthog/branch_created",
4115
+ /** Task run has started execution */
4116
+ RUN_STARTED: "_posthog/run_started",
4117
+ /** Task has completed (success or failure) */
4118
+ TASK_COMPLETE: "_posthog/task_complete",
4119
+ /** Agent finished processing a turn (prompt returned, waiting for next input) */
4120
+ TURN_COMPLETE: "_posthog/turn_complete",
4121
+ /** Error occurred during task execution */
4122
+ ERROR: "_posthog/error",
4123
+ /** Console/log output from the agent */
4124
+ CONSOLE: "_posthog/console",
4125
+ /** Maps taskRunId to agent's sessionId and adapter type (for resumption) */
4126
+ SDK_SESSION: "_posthog/sdk_session",
4127
+ /** Tree state snapshot captured (git tree hash + file archive) */
4128
+ TREE_SNAPSHOT: "_posthog/tree_snapshot",
4129
+ /** Agent mode changed (interactive/background) */
4130
+ MODE_CHANGE: "_posthog/mode_change",
4131
+ /** Request to resume a session from previous state */
4132
+ SESSION_RESUME: "_posthog/session/resume",
4133
+ /** User message sent from client to agent */
4134
+ USER_MESSAGE: "_posthog/user_message",
4135
+ /** Request to cancel current operation */
4136
+ CANCEL: "_posthog/cancel",
4137
+ /** Request to close the session */
4138
+ CLOSE: "_posthog/close",
4139
+ /** Agent status update (thinking, working, etc.) */
4140
+ STATUS: "_posthog/status",
4141
+ /** Task-level notification (progress, milestones) */
4142
+ TASK_NOTIFICATION: "_posthog/task_notification",
4143
+ /** Marks a boundary for log compaction */
4144
+ COMPACT_BOUNDARY: "_posthog/compact_boundary"
4145
+ };
4146
+
4147
+ // src/adapters/codex/codex-client.ts
4148
+ function createCodexClient(upstreamClient, logger, sessionState, callbacks) {
4149
+ const terminalHandles = /* @__PURE__ */ new Map();
4150
+ return {
4151
+ async requestPermission(params) {
4152
+ logger.debug("Relaying permission request to upstream", {
4153
+ sessionId: params.sessionId
4154
+ });
4155
+ return upstreamClient.requestPermission(params);
4156
+ },
4157
+ async sessionUpdate(params) {
4158
+ const update = params.update;
4159
+ if (update?.sessionUpdate === "usage_update") {
4160
+ const used = update.used;
4161
+ const size = update.size;
4162
+ if (used !== void 0) sessionState.contextUsed = used;
4163
+ if (size !== void 0) sessionState.contextSize = size;
4164
+ callbacks?.onUsageUpdate?.(update);
4165
+ }
4166
+ await upstreamClient.sessionUpdate(params);
4167
+ },
4168
+ async readTextFile(params) {
4169
+ return upstreamClient.readTextFile(params);
4170
+ },
4171
+ async writeTextFile(params) {
4172
+ return upstreamClient.writeTextFile(params);
4173
+ },
4174
+ async createTerminal(params) {
4175
+ const handle = await upstreamClient.createTerminal(params);
4176
+ terminalHandles.set(handle.id, handle);
4177
+ return { terminalId: handle.id };
4178
+ },
4179
+ async terminalOutput(params) {
4180
+ const handle = terminalHandles.get(params.terminalId);
4181
+ if (!handle) {
4182
+ return { output: "", truncated: false };
4183
+ }
4184
+ return handle.currentOutput();
4185
+ },
4186
+ async releaseTerminal(params) {
4187
+ const handle = terminalHandles.get(params.terminalId);
4188
+ if (handle) {
4189
+ terminalHandles.delete(params.terminalId);
4190
+ const result = await handle.release();
4191
+ return result ?? void 0;
4192
+ }
4193
+ },
4194
+ async waitForTerminalExit(params) {
4195
+ const handle = terminalHandles.get(params.terminalId);
4196
+ if (!handle) {
4197
+ return { exitCode: 1 };
4198
+ }
4199
+ return handle.waitForExit();
4200
+ },
4201
+ async killTerminal(params) {
4202
+ const handle = terminalHandles.get(params.terminalId);
4203
+ if (handle) {
4204
+ return handle.kill();
4205
+ }
4206
+ },
4207
+ async extMethod(method, params) {
4208
+ return upstreamClient.extMethod(method, params);
4209
+ },
4210
+ async extNotification(method, params) {
4211
+ return upstreamClient.extNotification(method, params);
4212
+ }
4213
+ };
4214
+ }
4215
+
4216
+ // src/adapters/codex/session-state.ts
4217
+ function createSessionState(sessionId, cwd, opts) {
4218
+ return {
4219
+ sessionId,
4220
+ cwd,
4221
+ modeId: opts?.modeId ?? "default",
4222
+ modelId: opts?.modelId,
4223
+ configOptions: [],
4224
+ accumulatedUsage: {
4225
+ inputTokens: 0,
4226
+ outputTokens: 0,
4227
+ cachedReadTokens: 0,
4228
+ cachedWriteTokens: 0
4229
+ },
4230
+ cancelled: false,
4231
+ taskRunId: opts?.taskRunId,
4232
+ taskId: opts?.taskId
4233
+ };
4234
+ }
4235
+ function resetUsage(state) {
4236
+ state.accumulatedUsage = {
4237
+ inputTokens: 0,
4238
+ outputTokens: 0,
4239
+ cachedReadTokens: 0,
4240
+ cachedWriteTokens: 0
4241
+ };
4242
+ }
4243
+
4244
+ // src/adapters/codex/settings.ts
4245
+ import * as fs5 from "fs";
4246
+ import * as os5 from "os";
4247
+ import * as path7 from "path";
4248
+ var CodexSettingsManager = class {
4249
+ cwd;
4250
+ settings = {};
4251
+ initialized = false;
4252
+ constructor(cwd) {
4253
+ this.cwd = cwd;
4254
+ }
4255
+ async initialize() {
4256
+ if (this.initialized) {
4257
+ return;
4258
+ }
4259
+ await this.loadSettings();
4260
+ this.initialized = true;
4261
+ }
4262
+ getConfigPath() {
4263
+ return path7.join(os5.homedir(), ".codex", "config.toml");
4264
+ }
4265
+ async loadSettings() {
4266
+ const configPath = this.getConfigPath();
4267
+ try {
4268
+ const content = await fs5.promises.readFile(configPath, "utf-8");
4269
+ this.settings = parseCodexToml(content, this.cwd);
4270
+ } catch {
4271
+ this.settings = {};
4272
+ }
4273
+ }
4274
+ getSettings() {
4275
+ return this.settings;
4276
+ }
4277
+ getCwd() {
4278
+ return this.cwd;
4279
+ }
4280
+ async setCwd(cwd) {
4281
+ if (this.cwd === cwd) {
4282
+ return;
4283
+ }
4284
+ this.dispose();
4285
+ this.cwd = cwd;
4286
+ this.initialized = false;
4287
+ await this.initialize();
4288
+ }
4289
+ dispose() {
4290
+ this.initialized = false;
4291
+ }
4292
+ };
4293
+ function parseCodexToml(content, cwd) {
4294
+ const settings = {};
4295
+ let currentSection = "";
4296
+ for (const line of content.split("\n")) {
4297
+ const trimmed = line.trim();
4298
+ if (!trimmed || trimmed.startsWith("#")) continue;
4299
+ const sectionMatch = trimmed.match(/^\[(.+)\]$/);
4300
+ if (sectionMatch) {
4301
+ currentSection = sectionMatch[1] ?? "";
4302
+ continue;
4303
+ }
4304
+ const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
4305
+ if (!kvMatch) continue;
4306
+ const key = kvMatch[1];
4307
+ let value = kvMatch[2]?.trim() ?? "";
4308
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
4309
+ value = value.slice(1, -1);
4310
+ }
4311
+ if (!currentSection) {
4312
+ if (key === "model") settings.model = value;
4313
+ if (key === "personality") settings.personality = value;
4314
+ if (key === "model_reasoning_effort")
4315
+ settings.modelReasoningEffort = value;
4316
+ } else if (currentSection === `projects."${cwd}"`) {
4317
+ if (key === "trust_level") settings.trustLevel = value;
4318
+ }
4319
+ }
4320
+ return settings;
4321
+ }
4322
+
4140
4323
  // src/adapters/codex/spawn.ts
4141
4324
  import { spawn as spawn2 } from "child_process";
4142
4325
  import { existsSync as existsSync3 } from "fs";
@@ -4233,64 +4416,216 @@ function spawnCodexProcess(options) {
4233
4416
  };
4234
4417
  }
4235
4418
 
4236
- // src/adapters/acp-connection.ts
4237
- function isGroupedOptions(options) {
4238
- return options.length > 0 && "group" in options[0];
4239
- }
4240
- function formatOption(o) {
4241
- if (!o.value) return o;
4242
- return { ...o, name: formatModelId(o.value) };
4243
- }
4244
- function filterModelConfigOptions(msg, allowedModelIds) {
4245
- const payload = msg;
4246
- const configOptions = payload.result?.configOptions ?? payload.params?.update?.configOptions;
4247
- if (!configOptions) return null;
4248
- const filtered = configOptions.map((opt) => {
4249
- if (opt.category !== "model" || !opt.options) return opt;
4250
- const options = opt.options;
4251
- if (isGroupedOptions(options)) {
4252
- const filteredOptions2 = options.map((group) => ({
4253
- ...group,
4254
- options: (group.options ?? []).filter((o) => o?.value && allowedModelIds.has(o.value)).map(formatOption)
4255
- }));
4256
- const flat = filteredOptions2.flatMap((g) => g.options ?? []);
4257
- const currentAllowed2 = opt.currentValue && allowedModelIds.has(opt.currentValue);
4258
- const nextCurrent2 = currentAllowed2 || flat.length === 0 ? opt.currentValue : flat[0]?.value;
4259
- return {
4260
- ...opt,
4261
- currentValue: nextCurrent2,
4262
- options: filteredOptions2
4263
- };
4264
- }
4265
- const valueOptions = options;
4266
- const filteredOptions = valueOptions.filter((o) => o?.value && allowedModelIds.has(o.value)).map(formatOption);
4267
- const currentAllowed = opt.currentValue && allowedModelIds.has(opt.currentValue);
4268
- const nextCurrent = currentAllowed || filteredOptions.length === 0 ? opt.currentValue : filteredOptions[0]?.value;
4269
- return {
4270
- ...opt,
4271
- currentValue: nextCurrent,
4272
- options: filteredOptions
4419
+ // src/adapters/codex/codex-agent.ts
4420
+ var CodexAcpAgent = class extends BaseAcpAgent {
4421
+ adapterName = "codex";
4422
+ codexProcess;
4423
+ codexConnection;
4424
+ sessionState;
4425
+ constructor(client, options) {
4426
+ super(client);
4427
+ this.logger = new Logger({ debug: true, prefix: "[CodexAcpAgent]" });
4428
+ this.codexProcess = spawnCodexProcess({
4429
+ ...options.codexProcessOptions,
4430
+ logger: this.logger,
4431
+ processCallbacks: options.processCallbacks
4432
+ });
4433
+ const codexReadable = nodeReadableToWebReadable(this.codexProcess.stdout);
4434
+ const codexWritable = nodeWritableToWebWritable(this.codexProcess.stdin);
4435
+ const codexStream = ndJsonStream(codexWritable, codexReadable);
4436
+ const cwd = options.codexProcessOptions.cwd ?? process.cwd();
4437
+ const settingsManager = new CodexSettingsManager(cwd);
4438
+ const abortController = new AbortController();
4439
+ this.session = {
4440
+ abortController,
4441
+ settingsManager,
4442
+ notificationHistory: [],
4443
+ cancelled: false
4273
4444
  };
4274
- });
4275
- if (payload.result?.configOptions) {
4276
- return { ...msg, result: { ...payload.result, configOptions: filtered } };
4445
+ this.codexConnection = new ClientSideConnection(
4446
+ (_agent) => createCodexClient(
4447
+ this.client,
4448
+ this.logger,
4449
+ this.sessionState ?? {
4450
+ sessionId: "",
4451
+ cwd: "",
4452
+ modeId: "default",
4453
+ configOptions: [],
4454
+ accumulatedUsage: {
4455
+ inputTokens: 0,
4456
+ outputTokens: 0,
4457
+ cachedReadTokens: 0,
4458
+ cachedWriteTokens: 0
4459
+ },
4460
+ cancelled: false
4461
+ }
4462
+ ),
4463
+ codexStream
4464
+ );
4277
4465
  }
4278
- if (payload.params?.update?.configOptions) {
4466
+ async initialize(request) {
4467
+ await this.session.settingsManager.initialize();
4468
+ const response = await this.codexConnection.initialize(request);
4279
4469
  return {
4280
- ...msg,
4281
- params: {
4282
- ...payload.params,
4283
- update: { ...payload.params.update, configOptions: filtered }
4470
+ ...response,
4471
+ agentCapabilities: {
4472
+ ...response.agentCapabilities,
4473
+ sessionCapabilities: {
4474
+ ...response.agentCapabilities?.sessionCapabilities,
4475
+ resume: {},
4476
+ fork: {}
4477
+ },
4478
+ _meta: {
4479
+ posthog: {
4480
+ resumeSession: true
4481
+ }
4482
+ }
4483
+ },
4484
+ agentInfo: {
4485
+ name: package_default.name,
4486
+ title: "Codex Agent",
4487
+ version: package_default.version
4284
4488
  }
4285
4489
  };
4286
4490
  }
4287
- return null;
4288
- }
4289
- function extractReasoningEffort(configOptions) {
4290
- if (!configOptions) return void 0;
4291
- const option = configOptions.find((opt) => opt.id === "reasoning_effort");
4292
- return option?.currentValue ?? void 0;
4293
- }
4491
+ async newSession(params) {
4492
+ const meta = params._meta;
4493
+ const response = await this.codexConnection.newSession(params);
4494
+ this.sessionState = createSessionState(response.sessionId, params.cwd, {
4495
+ taskRunId: meta?.taskRunId,
4496
+ taskId: meta?.taskId ?? meta?.persistence?.taskId,
4497
+ modeId: response.modes?.currentModeId ?? "default",
4498
+ modelId: response.models?.currentModelId
4499
+ });
4500
+ this.sessionId = response.sessionId;
4501
+ this.sessionState.configOptions = response.configOptions ?? [];
4502
+ if (meta?.taskRunId) {
4503
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
4504
+ taskRunId: meta.taskRunId,
4505
+ sessionId: response.sessionId,
4506
+ adapter: "codex"
4507
+ });
4508
+ }
4509
+ this.logger.info("Codex session created", {
4510
+ sessionId: response.sessionId,
4511
+ taskRunId: meta?.taskRunId
4512
+ });
4513
+ return response;
4514
+ }
4515
+ async loadSession(params) {
4516
+ const response = await this.codexConnection.loadSession(params);
4517
+ this.sessionState = createSessionState(params.sessionId, params.cwd);
4518
+ this.sessionId = params.sessionId;
4519
+ this.sessionState.configOptions = response.configOptions ?? [];
4520
+ return response;
4521
+ }
4522
+ async unstable_resumeSession(params) {
4523
+ const loadResponse = await this.codexConnection.loadSession({
4524
+ sessionId: params.sessionId,
4525
+ cwd: params.cwd,
4526
+ mcpServers: params.mcpServers ?? []
4527
+ });
4528
+ this.sessionState = createSessionState(params.sessionId, params.cwd);
4529
+ this.sessionId = params.sessionId;
4530
+ this.sessionState.configOptions = loadResponse.configOptions ?? [];
4531
+ const meta = params._meta;
4532
+ if (meta?.taskRunId) {
4533
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
4534
+ taskRunId: meta.taskRunId,
4535
+ sessionId: params.sessionId,
4536
+ adapter: "codex"
4537
+ });
4538
+ }
4539
+ return {
4540
+ modes: loadResponse.modes,
4541
+ models: loadResponse.models,
4542
+ configOptions: loadResponse.configOptions
4543
+ };
4544
+ }
4545
+ async unstable_forkSession(params) {
4546
+ const newResponse = await this.codexConnection.newSession({
4547
+ cwd: params.cwd,
4548
+ mcpServers: params.mcpServers ?? [],
4549
+ _meta: params._meta
4550
+ });
4551
+ this.sessionState = createSessionState(newResponse.sessionId, params.cwd);
4552
+ this.sessionId = newResponse.sessionId;
4553
+ this.sessionState.configOptions = newResponse.configOptions ?? [];
4554
+ return newResponse;
4555
+ }
4556
+ async listSessions(params) {
4557
+ return this.codexConnection.listSessions(params);
4558
+ }
4559
+ async unstable_listSessions(params) {
4560
+ return this.codexConnection.listSessions(params);
4561
+ }
4562
+ async prompt(params) {
4563
+ if (this.sessionState) {
4564
+ this.sessionState.cancelled = false;
4565
+ this.sessionState.interruptReason = void 0;
4566
+ resetUsage(this.sessionState);
4567
+ }
4568
+ const response = await this.codexConnection.prompt(params);
4569
+ if (this.sessionState?.taskRunId && response.usage) {
4570
+ await this.client.extNotification("_posthog/usage_update", {
4571
+ sessionId: params.sessionId,
4572
+ used: {
4573
+ inputTokens: response.usage.inputTokens ?? 0,
4574
+ outputTokens: response.usage.outputTokens ?? 0,
4575
+ cachedReadTokens: response.usage.cachedReadTokens ?? 0,
4576
+ cachedWriteTokens: response.usage.cachedWriteTokens ?? 0
4577
+ },
4578
+ cost: null
4579
+ });
4580
+ }
4581
+ return response;
4582
+ }
4583
+ async interrupt() {
4584
+ if (this.sessionState) {
4585
+ this.sessionState.cancelled = true;
4586
+ }
4587
+ await this.codexConnection.cancel({
4588
+ sessionId: this.sessionId
4589
+ });
4590
+ }
4591
+ async cancel(params) {
4592
+ if (this.sessionState) {
4593
+ this.sessionState.cancelled = true;
4594
+ const meta = params._meta;
4595
+ if (meta?.interruptReason) {
4596
+ this.sessionState.interruptReason = meta.interruptReason;
4597
+ }
4598
+ }
4599
+ await this.codexConnection.cancel(params);
4600
+ }
4601
+ async setSessionMode(params) {
4602
+ const response = await this.codexConnection.setSessionMode(params);
4603
+ if (this.sessionState) {
4604
+ this.sessionState.modeId = params.modeId;
4605
+ }
4606
+ return response ?? {};
4607
+ }
4608
+ async setSessionConfigOption(params) {
4609
+ const response = await this.codexConnection.setSessionConfigOption(params);
4610
+ if (this.sessionState && response.configOptions) {
4611
+ this.sessionState.configOptions = response.configOptions;
4612
+ }
4613
+ return response;
4614
+ }
4615
+ async authenticate(_params) {
4616
+ }
4617
+ async closeSession() {
4618
+ this.logger.info("Closing Codex session", { sessionId: this.sessionId });
4619
+ this.session.settingsManager.dispose();
4620
+ try {
4621
+ this.codexProcess.kill();
4622
+ } catch (err) {
4623
+ this.logger.warn("Failed to kill codex-acp process", { error: err });
4624
+ }
4625
+ }
4626
+ };
4627
+
4628
+ // src/adapters/acp-connection.ts
4294
4629
  function createAcpConnection(config = {}) {
4295
4630
  const adapterType = config.adapter ?? "claude";
4296
4631
  if (adapterType === "codex") {
@@ -4331,7 +4666,7 @@ function createClaudeConnection(config) {
4331
4666
  hasLogWriter: !!logWriter
4332
4667
  });
4333
4668
  }
4334
- const agentStream = ndJsonStream(agentWritable, streams.agent.readable);
4669
+ const agentStream = ndJsonStream2(agentWritable, streams.agent.readable);
4335
4670
  let agent = null;
4336
4671
  const agentConnection = new AgentSideConnection((client) => {
4337
4672
  agent = new ClaudeAcpAgent(client, config.processCallbacks);
@@ -4363,214 +4698,63 @@ function createClaudeConnection(config) {
4363
4698
  function createCodexConnection(config) {
4364
4699
  const logger = config.logger?.child("CodexConnection") ?? new Logger({ debug: true, prefix: "[CodexConnection]" });
4365
4700
  const { logWriter } = config;
4366
- const allowedModelIds = config.allowedModelIds;
4367
- const codexProcess = spawnCodexProcess({
4368
- ...config.codexOptions,
4369
- logger,
4370
- processCallbacks: config.processCallbacks
4371
- });
4372
- let clientReadable = nodeReadableToWebReadable(codexProcess.stdout);
4373
- let clientWritable = nodeWritableToWebWritable(codexProcess.stdin);
4374
- let isLoadingSession = false;
4375
- let loadRequestId = null;
4376
- let newSessionRequestId = null;
4377
- let sdkSessionEmitted = false;
4378
- const reasoningEffortBySessionId = /* @__PURE__ */ new Map();
4379
- let injectedConfigId = 0;
4380
- const decoder = new TextDecoder();
4381
- const encoder = new TextEncoder();
4382
- let readBuffer = "";
4383
- const taskRunId = config.taskRunId;
4384
- const filteringReadable = clientReadable.pipeThrough(
4385
- new TransformStream({
4386
- transform(chunk, controller) {
4387
- readBuffer += decoder.decode(chunk, { stream: true });
4388
- const lines = readBuffer.split("\n");
4389
- readBuffer = lines.pop() ?? "";
4390
- const outputLines = [];
4391
- for (const line of lines) {
4392
- const trimmed = line.trim();
4393
- if (!trimmed) {
4394
- outputLines.push(line);
4395
- continue;
4396
- }
4397
- let shouldFilter = false;
4398
- try {
4399
- const msg = JSON.parse(trimmed);
4400
- const sessionId = msg?.params?.sessionId ?? msg?.result?.sessionId ?? null;
4401
- const configOptions = msg?.result?.configOptions ?? msg?.params?.update?.configOptions;
4402
- if (sessionId && configOptions) {
4403
- const effort = extractReasoningEffort(configOptions);
4404
- if (effort) {
4405
- reasoningEffortBySessionId.set(sessionId, effort);
4406
- }
4407
- }
4408
- if (!sdkSessionEmitted && newSessionRequestId !== null && msg.id === newSessionRequestId && "result" in msg) {
4409
- const sessionId2 = msg.result?.sessionId;
4410
- if (sessionId2 && taskRunId) {
4411
- const sdkSessionNotification = {
4412
- jsonrpc: "2.0",
4413
- method: POSTHOG_NOTIFICATIONS.SDK_SESSION,
4414
- params: {
4415
- taskRunId,
4416
- sessionId: sessionId2,
4417
- adapter: "codex"
4418
- }
4419
- };
4420
- outputLines.push(JSON.stringify(sdkSessionNotification));
4421
- sdkSessionEmitted = true;
4422
- }
4423
- newSessionRequestId = null;
4424
- }
4425
- if (isLoadingSession) {
4426
- if (msg.id === loadRequestId && "result" in msg) {
4427
- logger.debug("session/load complete, resuming stream");
4428
- isLoadingSession = false;
4429
- loadRequestId = null;
4430
- } else if (msg.method === "session/update") {
4431
- shouldFilter = true;
4432
- }
4433
- }
4434
- if (!shouldFilter && allowedModelIds && allowedModelIds.size > 0) {
4435
- const updated = filterModelConfigOptions(msg, allowedModelIds);
4436
- if (updated) {
4437
- outputLines.push(JSON.stringify(updated));
4438
- continue;
4439
- }
4440
- }
4441
- } catch {
4442
- }
4443
- if (!shouldFilter) {
4444
- outputLines.push(line);
4445
- const isChunkNoise = trimmed.includes('"sessionUpdate":"agent_message_chunk"') || trimmed.includes('"sessionUpdate":"agent_thought_chunk"');
4446
- if (!isChunkNoise) {
4447
- logger.debug("codex-acp stdout:", trimmed);
4448
- }
4449
- }
4450
- }
4451
- if (outputLines.length > 0) {
4452
- const output = `${outputLines.join("\n")}
4453
- `;
4454
- controller.enqueue(encoder.encode(output));
4455
- }
4456
- },
4457
- flush(controller) {
4458
- if (readBuffer.trim()) {
4459
- controller.enqueue(encoder.encode(readBuffer));
4460
- }
4461
- }
4462
- })
4463
- );
4464
- clientReadable = filteringReadable;
4465
- const originalWritable = clientWritable;
4466
- clientWritable = new WritableStream({
4467
- write(chunk) {
4468
- const text2 = decoder.decode(chunk, { stream: true });
4469
- const trimmed = text2.trim();
4470
- logger.debug("codex-acp stdin:", trimmed);
4471
- try {
4472
- const msg = JSON.parse(trimmed);
4473
- if (msg.method === "session/set_config_option" && msg.params?.configId === "reasoning_effort" && msg.params?.sessionId && msg.params?.value) {
4474
- reasoningEffortBySessionId.set(
4475
- msg.params.sessionId,
4476
- msg.params.value
4477
- );
4478
- }
4479
- if (msg.method === "session/prompt" && msg.params?.sessionId) {
4480
- const effort = reasoningEffortBySessionId.get(msg.params.sessionId);
4481
- if (effort) {
4482
- const injection = {
4483
- jsonrpc: "2.0",
4484
- id: `reasoning_effort_${Date.now()}_${injectedConfigId++}`,
4485
- method: "session/set_config_option",
4486
- params: {
4487
- sessionId: msg.params.sessionId,
4488
- configId: "reasoning_effort",
4489
- value: effort
4490
- }
4491
- };
4492
- const injectionLine = `${JSON.stringify(injection)}
4493
- `;
4494
- const writer2 = originalWritable.getWriter();
4495
- return writer2.write(encoder.encode(injectionLine)).then(() => writer2.releaseLock()).then(() => {
4496
- const nextWriter = originalWritable.getWriter();
4497
- return nextWriter.write(chunk).finally(() => nextWriter.releaseLock());
4498
- });
4499
- }
4500
- }
4501
- if (msg.method === "session/new" && msg.id) {
4502
- logger.debug("session/new detected, tracking request ID");
4503
- newSessionRequestId = msg.id;
4504
- } else if (msg.method === "session/load" && msg.id) {
4505
- logger.debug("session/load detected, pausing stream updates");
4506
- isLoadingSession = true;
4507
- loadRequestId = msg.id;
4508
- }
4509
- } catch {
4510
- }
4511
- const writer = originalWritable.getWriter();
4512
- return writer.write(chunk).finally(() => writer.releaseLock());
4513
- },
4514
- close() {
4515
- const writer = originalWritable.getWriter();
4516
- return writer.close().finally(() => writer.releaseLock());
4517
- }
4518
- });
4519
- const shouldTapLogs = config.taskRunId && logWriter;
4520
- if (shouldTapLogs && config.taskRunId) {
4521
- const taskRunId2 = config.taskRunId;
4522
- if (!logWriter.isRegistered(taskRunId2)) {
4523
- logWriter.register(taskRunId2, {
4524
- taskId: config.taskId ?? taskRunId2,
4525
- runId: taskRunId2
4701
+ const streams = createBidirectionalStreams();
4702
+ let agentWritable = streams.agent.writable;
4703
+ let clientWritable = streams.client.writable;
4704
+ if (config.taskRunId && logWriter) {
4705
+ if (!logWriter.isRegistered(config.taskRunId)) {
4706
+ logWriter.register(config.taskRunId, {
4707
+ taskId: config.taskId ?? config.taskRunId,
4708
+ runId: config.taskRunId,
4709
+ deviceType: config.deviceType
4526
4710
  });
4527
4711
  }
4528
- clientWritable = createTappedWritableStream(clientWritable, {
4712
+ const taskRunId = config.taskRunId;
4713
+ agentWritable = createTappedWritableStream(streams.agent.writable, {
4529
4714
  onMessage: (line) => {
4530
- logWriter.appendRawLine(taskRunId2, line);
4715
+ logWriter.appendRawLine(taskRunId, line);
4716
+ },
4717
+ logger
4718
+ });
4719
+ clientWritable = createTappedWritableStream(streams.client.writable, {
4720
+ onMessage: (line) => {
4721
+ logWriter.appendRawLine(taskRunId, line);
4531
4722
  },
4532
4723
  logger
4533
4724
  });
4534
- const originalReadable = clientReadable;
4535
- const logDecoder = new TextDecoder();
4536
- let logBuffer = "";
4537
- clientReadable = originalReadable.pipeThrough(
4538
- new TransformStream({
4539
- transform(chunk, controller) {
4540
- logBuffer += logDecoder.decode(chunk, { stream: true });
4541
- const lines = logBuffer.split("\n");
4542
- logBuffer = lines.pop() ?? "";
4543
- for (const line of lines) {
4544
- if (line.trim()) {
4545
- logWriter.appendRawLine(taskRunId2, line);
4546
- }
4547
- }
4548
- controller.enqueue(chunk);
4549
- },
4550
- flush() {
4551
- if (logBuffer.trim()) {
4552
- logWriter.appendRawLine(taskRunId2, logBuffer);
4553
- }
4554
- }
4555
- })
4556
- );
4557
4725
  } else {
4558
4726
  logger.info("Tapped streams NOT enabled for Codex", {
4559
4727
  hasTaskRunId: !!config.taskRunId,
4560
4728
  hasLogWriter: !!logWriter
4561
4729
  });
4562
4730
  }
4731
+ const agentStream = ndJsonStream2(agentWritable, streams.agent.readable);
4732
+ let agent = null;
4733
+ const agentConnection = new AgentSideConnection((client) => {
4734
+ agent = new CodexAcpAgent(client, {
4735
+ codexProcessOptions: config.codexOptions ?? {},
4736
+ processCallbacks: config.processCallbacks
4737
+ });
4738
+ logger.info(`Created ${agent.adapterName} agent`);
4739
+ return agent;
4740
+ }, agentStream);
4563
4741
  return {
4564
- agentConnection: void 0,
4742
+ agentConnection,
4565
4743
  clientStreams: {
4566
- readable: clientReadable,
4744
+ readable: streams.client.readable,
4567
4745
  writable: clientWritable
4568
4746
  },
4569
4747
  cleanup: async () => {
4570
4748
  logger.info("Cleaning up Codex connection");
4571
- codexProcess.kill();
4749
+ if (agent) {
4750
+ await agent.closeSession();
4751
+ }
4572
4752
  try {
4573
- await clientWritable.close();
4753
+ await streams.client.writable.close();
4754
+ } catch {
4755
+ }
4756
+ try {
4757
+ await streams.agent.writable.close();
4574
4758
  } catch {
4575
4759
  }
4576
4760
  }
@@ -4778,9 +4962,9 @@ var PostHogAPIClient = class {
4778
4962
  };
4779
4963
 
4780
4964
  // src/session-log-writer.ts
4781
- import fs5 from "fs";
4965
+ import fs6 from "fs";
4782
4966
  import fsp from "fs/promises";
4783
- import path7 from "path";
4967
+ import path8 from "path";
4784
4968
  var SessionLogWriter = class _SessionLogWriter {
4785
4969
  static FLUSH_DEBOUNCE_MS = 500;
4786
4970
  static FLUSH_MAX_INTERVAL_MS = 5e3;
@@ -4820,13 +5004,13 @@ var SessionLogWriter = class _SessionLogWriter {
4820
5004
  this.sessions.set(sessionId, { context, currentTurnMessages: [] });
4821
5005
  this.lastFlushAttemptTime.set(sessionId, Date.now());
4822
5006
  if (this.localCachePath) {
4823
- const sessionDir = path7.join(
5007
+ const sessionDir = path8.join(
4824
5008
  this.localCachePath,
4825
5009
  "sessions",
4826
5010
  context.runId
4827
5011
  );
4828
5012
  try {
4829
- fs5.mkdirSync(sessionDir, { recursive: true });
5013
+ fs6.mkdirSync(sessionDir, { recursive: true });
4830
5014
  } catch (error) {
4831
5015
  this.logger.warn("Failed to create local cache directory", {
4832
5016
  sessionDir,
@@ -5078,14 +5262,14 @@ var SessionLogWriter = class _SessionLogWriter {
5078
5262
  if (!this.localCachePath) return;
5079
5263
  const session = this.sessions.get(sessionId);
5080
5264
  if (!session) return;
5081
- const logPath = path7.join(
5265
+ const logPath = path8.join(
5082
5266
  this.localCachePath,
5083
5267
  "sessions",
5084
5268
  session.context.runId,
5085
5269
  "logs.ndjson"
5086
5270
  );
5087
5271
  try {
5088
- fs5.appendFileSync(logPath, `${JSON.stringify(entry)}
5272
+ fs6.appendFileSync(logPath, `${JSON.stringify(entry)}
5089
5273
  `);
5090
5274
  } catch (error) {
5091
5275
  this.logger.warn("Failed to write to local cache", {
@@ -5097,13 +5281,13 @@ var SessionLogWriter = class _SessionLogWriter {
5097
5281
  }
5098
5282
  }
5099
5283
  static async cleanupOldSessions(localCachePath) {
5100
- const sessionsDir = path7.join(localCachePath, "sessions");
5284
+ const sessionsDir = path8.join(localCachePath, "sessions");
5101
5285
  let deleted = 0;
5102
5286
  try {
5103
5287
  const entries = await fsp.readdir(sessionsDir);
5104
5288
  const now = Date.now();
5105
5289
  for (const entry of entries) {
5106
- const entryPath = path7.join(sessionsDir, entry);
5290
+ const entryPath = path8.join(sessionsDir, entry);
5107
5291
  try {
5108
5292
  const stats = await fsp.stat(entryPath);
5109
5293
  if (stats.isDirectory() && now - stats.birthtimeMs > _SessionLogWriter.SESSIONS_MAX_AGE_MS) {
@@ -5189,7 +5373,7 @@ var Agent = class {
5189
5373
  allowedModelIds = new Set(codexModelIds);
5190
5374
  }
5191
5375
  if (!sanitizedModel || !allowedModelIds?.has(sanitizedModel)) {
5192
- sanitizedModel = codexModelIds[0];
5376
+ sanitizedModel = codexModelIds.includes(DEFAULT_CODEX_MODEL) ? DEFAULT_CODEX_MODEL : codexModelIds[0];
5193
5377
  }
5194
5378
  }
5195
5379
  if (!sanitizedModel && options.adapter !== "codex") {