@posthog/agent 2.3.168 → 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,132 +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 DEFAULT_CODEX_MODEL = "gpt-5.4";
43
- var BLOCKED_MODELS = /* @__PURE__ */ new Set(["gpt-5-mini", "openai/gpt-5-mini"]);
44
- var CACHE_TTL = 10 * 60 * 1e3;
45
- var gatewayModelsCache = null;
46
- async function fetchGatewayModels(options) {
47
- const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
48
- if (!gatewayUrl) {
49
- return [];
50
- }
51
- if (gatewayModelsCache && gatewayModelsCache.url === gatewayUrl && Date.now() < gatewayModelsCache.expiry) {
52
- return gatewayModelsCache.models;
53
- }
54
- const modelsUrl = `${gatewayUrl}/v1/models`;
55
- try {
56
- const response = await fetch(modelsUrl);
57
- if (!response.ok) {
58
- return [];
59
- }
60
- const data = await response.json();
61
- const models = (data.data ?? []).filter((m) => !BLOCKED_MODELS.has(m.id));
62
- gatewayModelsCache = {
63
- models,
64
- expiry: Date.now() + CACHE_TTL,
65
- url: gatewayUrl
66
- };
67
- return models;
68
- } catch {
69
- return [];
70
- }
71
- }
72
- function isAnthropicModel(model) {
73
- if (model.owned_by) {
74
- return model.owned_by === "anthropic";
75
- }
76
- return model.id.startsWith("claude-") || model.id.startsWith("anthropic/");
77
- }
78
- var modelsListCache = null;
79
- async function fetchModelsList(options) {
80
- const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
81
- if (!gatewayUrl) {
82
- return [];
83
- }
84
- if (modelsListCache && modelsListCache.url === gatewayUrl && Date.now() < modelsListCache.expiry) {
85
- return modelsListCache.models;
86
- }
87
- try {
88
- const modelsUrl = `${gatewayUrl}/v1/models`;
89
- const response = await fetch(modelsUrl);
90
- if (!response.ok) {
91
- return [];
92
- }
93
- const data = await response.json();
94
- const models = Array.isArray(data) ? data : data.data ?? data.models ?? [];
95
- const results = [];
96
- for (const model of models) {
97
- const id = model?.id ? String(model.id) : "";
98
- if (!id) continue;
99
- results.push({ id, owned_by: model?.owned_by });
100
- }
101
- modelsListCache = {
102
- models: results,
103
- expiry: Date.now() + CACHE_TTL,
104
- url: gatewayUrl
105
- };
106
- return results;
107
- } catch {
108
- return [];
109
- }
110
- }
111
- var PROVIDER_PREFIXES = ["anthropic/", "openai/", "google-vertex/"];
112
- function formatGatewayModelName(model) {
113
- return formatModelId(model.id);
114
- }
115
- function formatModelId(modelId) {
116
- let cleanId = modelId;
117
- for (const prefix of PROVIDER_PREFIXES) {
118
- if (cleanId.startsWith(prefix)) {
119
- cleanId = cleanId.slice(prefix.length);
120
- break;
121
- }
122
- }
123
- cleanId = cleanId.replace(/(\d)-(\d)/g, "$1.$2");
124
- const words = cleanId.split(/[-_]/).map((word) => {
125
- if (word.match(/^[0-9.]+$/)) return word;
126
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
127
- });
128
- return words.join(" ");
129
- }
2
+ import { AgentSideConnection, ndJsonStream as ndJsonStream2 } from "@agentclientprotocol/sdk";
130
3
 
131
4
  // src/utils/logger.ts
132
5
  var Logger = class _Logger {
@@ -182,7 +55,7 @@ var Logger = class _Logger {
182
55
  };
183
56
 
184
57
  // src/utils/streams.ts
185
- import { ReadableStream, WritableStream as WritableStream2 } from "stream/web";
58
+ import { ReadableStream, WritableStream } from "stream/web";
186
59
  var Pushable = class {
187
60
  queue = [];
188
61
  resolvers = [];
@@ -240,7 +113,7 @@ function createBidirectionalStreams() {
240
113
  const agentToClientPushable = new Pushable();
241
114
  const clientToAgentReadable = pushableToReadableStream(clientToAgentPushable);
242
115
  const agentToClientReadable = pushableToReadableStream(agentToClientPushable);
243
- const clientToAgentWritable = new WritableStream2({
116
+ const clientToAgentWritable = new WritableStream({
244
117
  write(chunk) {
245
118
  clientToAgentPushable.push(chunk);
246
119
  },
@@ -248,7 +121,7 @@ function createBidirectionalStreams() {
248
121
  clientToAgentPushable.end();
249
122
  }
250
123
  });
251
- const agentToClientWritable = new WritableStream2({
124
+ const agentToClientWritable = new WritableStream({
252
125
  write(chunk) {
253
126
  agentToClientPushable.push(chunk);
254
127
  },
@@ -272,7 +145,7 @@ function createTappedWritableStream(underlying, options) {
272
145
  const decoder = new TextDecoder();
273
146
  let buffer = "";
274
147
  let _messageCount = 0;
275
- return new WritableStream2({
148
+ return new WritableStream({
276
149
  async write(chunk) {
277
150
  buffer += decoder.decode(chunk, { stream: true });
278
151
  const lines = buffer.split("\n");
@@ -328,7 +201,7 @@ function nodeReadableToWebReadable(nodeStream) {
328
201
  });
329
202
  }
330
203
  function nodeWritableToWebWritable(nodeStream) {
331
- return new WritableStream2({
204
+ return new WritableStream({
332
205
  write(chunk) {
333
206
  return new Promise((resolve3, reject) => {
334
207
  const ok = nodeStream.write(Buffer.from(chunk), (err) => {
@@ -372,7 +245,7 @@ import { v7 as uuidv7 } from "uuid";
372
245
  // package.json
373
246
  var package_default = {
374
247
  name: "@posthog/agent",
375
- version: "2.3.168",
248
+ version: "2.3.169",
376
249
  repository: "https://github.com/PostHog/code",
377
250
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
378
251
  exports: {
@@ -519,6 +392,97 @@ function unreachable(value, logger) {
519
392
  logger.error(`Unexpected case: ${valueAsString}`);
520
393
  }
521
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
+
522
486
  // src/adapters/base-acp-agent.ts
523
487
  var DEFAULT_CONTEXT_WINDOW = 2e5;
524
488
  var BaseAcpAgent = class {
@@ -726,8 +690,8 @@ var ToolContentBuilder = class {
726
690
  this.items.push({ type: "content", content: image(data, mimeType, uri) });
727
691
  return this;
728
692
  }
729
- diff(path8, oldText, newText) {
730
- this.items.push({ type: "diff", path: path8, oldText, newText });
693
+ diff(path9, oldText, newText) {
694
+ this.items.push({ type: "diff", path: path9, oldText, newText });
731
695
  return this;
732
696
  }
733
697
  build() {
@@ -4138,6 +4102,224 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4138
4102
  }
4139
4103
  };
4140
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
+
4141
4323
  // src/adapters/codex/spawn.ts
4142
4324
  import { spawn as spawn2 } from "child_process";
4143
4325
  import { existsSync as existsSync3 } from "fs";
@@ -4234,64 +4416,216 @@ function spawnCodexProcess(options) {
4234
4416
  };
4235
4417
  }
4236
4418
 
4237
- // src/adapters/acp-connection.ts
4238
- function isGroupedOptions(options) {
4239
- return options.length > 0 && "group" in options[0];
4240
- }
4241
- function formatOption(o) {
4242
- if (!o.value) return o;
4243
- return { ...o, name: formatModelId(o.value) };
4244
- }
4245
- function filterModelConfigOptions(msg, allowedModelIds) {
4246
- const payload = msg;
4247
- const configOptions = payload.result?.configOptions ?? payload.params?.update?.configOptions;
4248
- if (!configOptions) return null;
4249
- const filtered = configOptions.map((opt) => {
4250
- if (opt.category !== "model" || !opt.options) return opt;
4251
- const options = opt.options;
4252
- if (isGroupedOptions(options)) {
4253
- const filteredOptions2 = options.map((group) => ({
4254
- ...group,
4255
- options: (group.options ?? []).filter((o) => o?.value && allowedModelIds.has(o.value)).map(formatOption)
4256
- }));
4257
- const flat = filteredOptions2.flatMap((g) => g.options ?? []);
4258
- const currentAllowed2 = opt.currentValue && allowedModelIds.has(opt.currentValue);
4259
- const nextCurrent2 = currentAllowed2 || flat.length === 0 ? opt.currentValue : flat[0]?.value;
4260
- return {
4261
- ...opt,
4262
- currentValue: nextCurrent2,
4263
- options: filteredOptions2
4264
- };
4265
- }
4266
- const valueOptions = options;
4267
- const filteredOptions = valueOptions.filter((o) => o?.value && allowedModelIds.has(o.value)).map(formatOption);
4268
- const currentAllowed = opt.currentValue && allowedModelIds.has(opt.currentValue);
4269
- const nextCurrent = currentAllowed || filteredOptions.length === 0 ? opt.currentValue : filteredOptions[0]?.value;
4270
- return {
4271
- ...opt,
4272
- currentValue: nextCurrent,
4273
- 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
4274
4444
  };
4275
- });
4276
- if (payload.result?.configOptions) {
4277
- 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
+ );
4278
4465
  }
4279
- if (payload.params?.update?.configOptions) {
4466
+ async initialize(request) {
4467
+ await this.session.settingsManager.initialize();
4468
+ const response = await this.codexConnection.initialize(request);
4280
4469
  return {
4281
- ...msg,
4282
- params: {
4283
- ...payload.params,
4284
- 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
4285
4488
  }
4286
4489
  };
4287
4490
  }
4288
- return null;
4289
- }
4290
- function extractReasoningEffort(configOptions) {
4291
- if (!configOptions) return void 0;
4292
- const option = configOptions.find((opt) => opt.id === "reasoning_effort");
4293
- return option?.currentValue ?? void 0;
4294
- }
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
4295
4629
  function createAcpConnection(config = {}) {
4296
4630
  const adapterType = config.adapter ?? "claude";
4297
4631
  if (adapterType === "codex") {
@@ -4332,7 +4666,7 @@ function createClaudeConnection(config) {
4332
4666
  hasLogWriter: !!logWriter
4333
4667
  });
4334
4668
  }
4335
- const agentStream = ndJsonStream(agentWritable, streams.agent.readable);
4669
+ const agentStream = ndJsonStream2(agentWritable, streams.agent.readable);
4336
4670
  let agent = null;
4337
4671
  const agentConnection = new AgentSideConnection((client) => {
4338
4672
  agent = new ClaudeAcpAgent(client, config.processCallbacks);
@@ -4364,214 +4698,63 @@ function createClaudeConnection(config) {
4364
4698
  function createCodexConnection(config) {
4365
4699
  const logger = config.logger?.child("CodexConnection") ?? new Logger({ debug: true, prefix: "[CodexConnection]" });
4366
4700
  const { logWriter } = config;
4367
- const allowedModelIds = config.allowedModelIds;
4368
- const codexProcess = spawnCodexProcess({
4369
- ...config.codexOptions,
4370
- logger,
4371
- processCallbacks: config.processCallbacks
4372
- });
4373
- let clientReadable = nodeReadableToWebReadable(codexProcess.stdout);
4374
- let clientWritable = nodeWritableToWebWritable(codexProcess.stdin);
4375
- let isLoadingSession = false;
4376
- let loadRequestId = null;
4377
- let newSessionRequestId = null;
4378
- let sdkSessionEmitted = false;
4379
- const reasoningEffortBySessionId = /* @__PURE__ */ new Map();
4380
- let injectedConfigId = 0;
4381
- const decoder = new TextDecoder();
4382
- const encoder = new TextEncoder();
4383
- let readBuffer = "";
4384
- const taskRunId = config.taskRunId;
4385
- const filteringReadable = clientReadable.pipeThrough(
4386
- new TransformStream({
4387
- transform(chunk, controller) {
4388
- readBuffer += decoder.decode(chunk, { stream: true });
4389
- const lines = readBuffer.split("\n");
4390
- readBuffer = lines.pop() ?? "";
4391
- const outputLines = [];
4392
- for (const line of lines) {
4393
- const trimmed = line.trim();
4394
- if (!trimmed) {
4395
- outputLines.push(line);
4396
- continue;
4397
- }
4398
- let shouldFilter = false;
4399
- try {
4400
- const msg = JSON.parse(trimmed);
4401
- const sessionId = msg?.params?.sessionId ?? msg?.result?.sessionId ?? null;
4402
- const configOptions = msg?.result?.configOptions ?? msg?.params?.update?.configOptions;
4403
- if (sessionId && configOptions) {
4404
- const effort = extractReasoningEffort(configOptions);
4405
- if (effort) {
4406
- reasoningEffortBySessionId.set(sessionId, effort);
4407
- }
4408
- }
4409
- if (!sdkSessionEmitted && newSessionRequestId !== null && msg.id === newSessionRequestId && "result" in msg) {
4410
- const sessionId2 = msg.result?.sessionId;
4411
- if (sessionId2 && taskRunId) {
4412
- const sdkSessionNotification = {
4413
- jsonrpc: "2.0",
4414
- method: POSTHOG_NOTIFICATIONS.SDK_SESSION,
4415
- params: {
4416
- taskRunId,
4417
- sessionId: sessionId2,
4418
- adapter: "codex"
4419
- }
4420
- };
4421
- outputLines.push(JSON.stringify(sdkSessionNotification));
4422
- sdkSessionEmitted = true;
4423
- }
4424
- newSessionRequestId = null;
4425
- }
4426
- if (isLoadingSession) {
4427
- if (msg.id === loadRequestId && "result" in msg) {
4428
- logger.debug("session/load complete, resuming stream");
4429
- isLoadingSession = false;
4430
- loadRequestId = null;
4431
- } else if (msg.method === "session/update") {
4432
- shouldFilter = true;
4433
- }
4434
- }
4435
- if (!shouldFilter && allowedModelIds && allowedModelIds.size > 0) {
4436
- const updated = filterModelConfigOptions(msg, allowedModelIds);
4437
- if (updated) {
4438
- outputLines.push(JSON.stringify(updated));
4439
- continue;
4440
- }
4441
- }
4442
- } catch {
4443
- }
4444
- if (!shouldFilter) {
4445
- outputLines.push(line);
4446
- const isChunkNoise = trimmed.includes('"sessionUpdate":"agent_message_chunk"') || trimmed.includes('"sessionUpdate":"agent_thought_chunk"');
4447
- if (!isChunkNoise) {
4448
- logger.debug("codex-acp stdout:", trimmed);
4449
- }
4450
- }
4451
- }
4452
- if (outputLines.length > 0) {
4453
- const output = `${outputLines.join("\n")}
4454
- `;
4455
- controller.enqueue(encoder.encode(output));
4456
- }
4457
- },
4458
- flush(controller) {
4459
- if (readBuffer.trim()) {
4460
- controller.enqueue(encoder.encode(readBuffer));
4461
- }
4462
- }
4463
- })
4464
- );
4465
- clientReadable = filteringReadable;
4466
- const originalWritable = clientWritable;
4467
- clientWritable = new WritableStream({
4468
- write(chunk) {
4469
- const text2 = decoder.decode(chunk, { stream: true });
4470
- const trimmed = text2.trim();
4471
- logger.debug("codex-acp stdin:", trimmed);
4472
- try {
4473
- const msg = JSON.parse(trimmed);
4474
- if (msg.method === "session/set_config_option" && msg.params?.configId === "reasoning_effort" && msg.params?.sessionId && msg.params?.value) {
4475
- reasoningEffortBySessionId.set(
4476
- msg.params.sessionId,
4477
- msg.params.value
4478
- );
4479
- }
4480
- if (msg.method === "session/prompt" && msg.params?.sessionId) {
4481
- const effort = reasoningEffortBySessionId.get(msg.params.sessionId);
4482
- if (effort) {
4483
- const injection = {
4484
- jsonrpc: "2.0",
4485
- id: `reasoning_effort_${Date.now()}_${injectedConfigId++}`,
4486
- method: "session/set_config_option",
4487
- params: {
4488
- sessionId: msg.params.sessionId,
4489
- configId: "reasoning_effort",
4490
- value: effort
4491
- }
4492
- };
4493
- const injectionLine = `${JSON.stringify(injection)}
4494
- `;
4495
- const writer2 = originalWritable.getWriter();
4496
- return writer2.write(encoder.encode(injectionLine)).then(() => writer2.releaseLock()).then(() => {
4497
- const nextWriter = originalWritable.getWriter();
4498
- return nextWriter.write(chunk).finally(() => nextWriter.releaseLock());
4499
- });
4500
- }
4501
- }
4502
- if (msg.method === "session/new" && msg.id) {
4503
- logger.debug("session/new detected, tracking request ID");
4504
- newSessionRequestId = msg.id;
4505
- } else if (msg.method === "session/load" && msg.id) {
4506
- logger.debug("session/load detected, pausing stream updates");
4507
- isLoadingSession = true;
4508
- loadRequestId = msg.id;
4509
- }
4510
- } catch {
4511
- }
4512
- const writer = originalWritable.getWriter();
4513
- return writer.write(chunk).finally(() => writer.releaseLock());
4514
- },
4515
- close() {
4516
- const writer = originalWritable.getWriter();
4517
- return writer.close().finally(() => writer.releaseLock());
4518
- }
4519
- });
4520
- const shouldTapLogs = config.taskRunId && logWriter;
4521
- if (shouldTapLogs && config.taskRunId) {
4522
- const taskRunId2 = config.taskRunId;
4523
- if (!logWriter.isRegistered(taskRunId2)) {
4524
- logWriter.register(taskRunId2, {
4525
- taskId: config.taskId ?? taskRunId2,
4526
- 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
4527
4710
  });
4528
4711
  }
4529
- clientWritable = createTappedWritableStream(clientWritable, {
4712
+ const taskRunId = config.taskRunId;
4713
+ agentWritable = createTappedWritableStream(streams.agent.writable, {
4530
4714
  onMessage: (line) => {
4531
- 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);
4532
4722
  },
4533
4723
  logger
4534
4724
  });
4535
- const originalReadable = clientReadable;
4536
- const logDecoder = new TextDecoder();
4537
- let logBuffer = "";
4538
- clientReadable = originalReadable.pipeThrough(
4539
- new TransformStream({
4540
- transform(chunk, controller) {
4541
- logBuffer += logDecoder.decode(chunk, { stream: true });
4542
- const lines = logBuffer.split("\n");
4543
- logBuffer = lines.pop() ?? "";
4544
- for (const line of lines) {
4545
- if (line.trim()) {
4546
- logWriter.appendRawLine(taskRunId2, line);
4547
- }
4548
- }
4549
- controller.enqueue(chunk);
4550
- },
4551
- flush() {
4552
- if (logBuffer.trim()) {
4553
- logWriter.appendRawLine(taskRunId2, logBuffer);
4554
- }
4555
- }
4556
- })
4557
- );
4558
4725
  } else {
4559
4726
  logger.info("Tapped streams NOT enabled for Codex", {
4560
4727
  hasTaskRunId: !!config.taskRunId,
4561
4728
  hasLogWriter: !!logWriter
4562
4729
  });
4563
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);
4564
4741
  return {
4565
- agentConnection: void 0,
4742
+ agentConnection,
4566
4743
  clientStreams: {
4567
- readable: clientReadable,
4744
+ readable: streams.client.readable,
4568
4745
  writable: clientWritable
4569
4746
  },
4570
4747
  cleanup: async () => {
4571
4748
  logger.info("Cleaning up Codex connection");
4572
- codexProcess.kill();
4749
+ if (agent) {
4750
+ await agent.closeSession();
4751
+ }
4573
4752
  try {
4574
- await clientWritable.close();
4753
+ await streams.client.writable.close();
4754
+ } catch {
4755
+ }
4756
+ try {
4757
+ await streams.agent.writable.close();
4575
4758
  } catch {
4576
4759
  }
4577
4760
  }
@@ -4779,9 +4962,9 @@ var PostHogAPIClient = class {
4779
4962
  };
4780
4963
 
4781
4964
  // src/session-log-writer.ts
4782
- import fs5 from "fs";
4965
+ import fs6 from "fs";
4783
4966
  import fsp from "fs/promises";
4784
- import path7 from "path";
4967
+ import path8 from "path";
4785
4968
  var SessionLogWriter = class _SessionLogWriter {
4786
4969
  static FLUSH_DEBOUNCE_MS = 500;
4787
4970
  static FLUSH_MAX_INTERVAL_MS = 5e3;
@@ -4821,13 +5004,13 @@ var SessionLogWriter = class _SessionLogWriter {
4821
5004
  this.sessions.set(sessionId, { context, currentTurnMessages: [] });
4822
5005
  this.lastFlushAttemptTime.set(sessionId, Date.now());
4823
5006
  if (this.localCachePath) {
4824
- const sessionDir = path7.join(
5007
+ const sessionDir = path8.join(
4825
5008
  this.localCachePath,
4826
5009
  "sessions",
4827
5010
  context.runId
4828
5011
  );
4829
5012
  try {
4830
- fs5.mkdirSync(sessionDir, { recursive: true });
5013
+ fs6.mkdirSync(sessionDir, { recursive: true });
4831
5014
  } catch (error) {
4832
5015
  this.logger.warn("Failed to create local cache directory", {
4833
5016
  sessionDir,
@@ -5079,14 +5262,14 @@ var SessionLogWriter = class _SessionLogWriter {
5079
5262
  if (!this.localCachePath) return;
5080
5263
  const session = this.sessions.get(sessionId);
5081
5264
  if (!session) return;
5082
- const logPath = path7.join(
5265
+ const logPath = path8.join(
5083
5266
  this.localCachePath,
5084
5267
  "sessions",
5085
5268
  session.context.runId,
5086
5269
  "logs.ndjson"
5087
5270
  );
5088
5271
  try {
5089
- fs5.appendFileSync(logPath, `${JSON.stringify(entry)}
5272
+ fs6.appendFileSync(logPath, `${JSON.stringify(entry)}
5090
5273
  `);
5091
5274
  } catch (error) {
5092
5275
  this.logger.warn("Failed to write to local cache", {
@@ -5098,13 +5281,13 @@ var SessionLogWriter = class _SessionLogWriter {
5098
5281
  }
5099
5282
  }
5100
5283
  static async cleanupOldSessions(localCachePath) {
5101
- const sessionsDir = path7.join(localCachePath, "sessions");
5284
+ const sessionsDir = path8.join(localCachePath, "sessions");
5102
5285
  let deleted = 0;
5103
5286
  try {
5104
5287
  const entries = await fsp.readdir(sessionsDir);
5105
5288
  const now = Date.now();
5106
5289
  for (const entry of entries) {
5107
- const entryPath = path7.join(sessionsDir, entry);
5290
+ const entryPath = path8.join(sessionsDir, entry);
5108
5291
  try {
5109
5292
  const stats = await fsp.stat(entryPath);
5110
5293
  if (stats.isDirectory() && now - stats.birthtimeMs > _SessionLogWriter.SESSIONS_MAX_AGE_MS) {