@posthog/agent 2.3.678 → 2.3.703

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
@@ -4674,7 +4674,7 @@ import { v7 as uuidv7 } from "uuid";
4674
4674
  // package.json
4675
4675
  var package_default = {
4676
4676
  name: "@posthog/agent",
4677
- version: "2.3.678",
4677
+ version: "2.3.703",
4678
4678
  repository: "https://github.com/PostHog/code",
4679
4679
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
4680
4680
  exports: {
@@ -9683,22 +9683,174 @@ function resolveTaskId(meta) {
9683
9683
  return meta?.taskId ?? meta?.persistence?.taskId;
9684
9684
  }
9685
9685
 
9686
+ // src/adapters/claude/context-breakdown.ts
9687
+ var CLAUDE_PRESET_ESTIMATE_TOKENS = 4e3;
9688
+ var CHARS_PER_TOKEN = 4;
9689
+ function estimateTokens(text2) {
9690
+ if (!text2) return 0;
9691
+ return Math.max(0, Math.round(text2.length / CHARS_PER_TOKEN));
9692
+ }
9693
+ function estimateJsonTokens(value) {
9694
+ try {
9695
+ return estimateTokens(JSON.stringify(value));
9696
+ } catch {
9697
+ return 0;
9698
+ }
9699
+ }
9700
+ function estimateSkillsTokens(commands) {
9701
+ if (!commands.length) return 0;
9702
+ return estimateJsonTokens(
9703
+ commands.map((c) => ({
9704
+ name: c.name,
9705
+ description: c.description,
9706
+ hint: c.input?.hint
9707
+ }))
9708
+ );
9709
+ }
9710
+ function estimateMcpTokens(tools) {
9711
+ if (!tools.length) return 0;
9712
+ return estimateJsonTokens(
9713
+ tools.map((t) => ({ name: t.name, description: t.description }))
9714
+ );
9715
+ }
9716
+ function estimateRulesTokens(rules) {
9717
+ return estimateTokens(rules);
9718
+ }
9719
+ function emptyBaseline() {
9720
+ return {
9721
+ systemPrompt: 0,
9722
+ tools: 0,
9723
+ rules: 0,
9724
+ skills: 0,
9725
+ mcp: 0,
9726
+ subagents: 0
9727
+ };
9728
+ }
9729
+ function estimateSystemPrompt(systemPrompt) {
9730
+ if (!systemPrompt) return CLAUDE_PRESET_ESTIMATE_TOKENS;
9731
+ if (typeof systemPrompt === "string") return estimateTokens(systemPrompt);
9732
+ if (typeof systemPrompt === "object") {
9733
+ const obj = systemPrompt;
9734
+ const appendTokens = typeof obj.append === "string" ? estimateTokens(obj.append) : 0;
9735
+ if (obj.type === "preset") {
9736
+ return CLAUDE_PRESET_ESTIMATE_TOKENS + appendTokens;
9737
+ }
9738
+ return appendTokens;
9739
+ }
9740
+ return 0;
9741
+ }
9742
+ function buildBreakdown(baseline, currentInputTokens) {
9743
+ const stableSum = baseline.systemPrompt + baseline.tools + baseline.rules + baseline.skills + baseline.mcp + baseline.subagents;
9744
+ const conversation = Math.max(0, currentInputTokens - stableSum);
9745
+ return {
9746
+ systemPrompt: baseline.systemPrompt,
9747
+ tools: baseline.tools,
9748
+ rules: baseline.rules,
9749
+ skills: baseline.skills,
9750
+ mcp: baseline.mcp,
9751
+ subagents: baseline.subagents,
9752
+ conversation
9753
+ };
9754
+ }
9755
+
9686
9756
  // src/adapters/claude/conversion/acp-to-sdk.ts
9687
9757
  import * as path6 from "path";
9688
9758
  import { fileURLToPath as fileURLToPath2 } from "url";
9689
- var PDF_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
9690
- var COMMON_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
9691
- "png",
9692
- "jpg",
9693
- "jpeg",
9694
- "gif",
9695
- "webp",
9696
- "bmp",
9697
- "svg",
9698
- "heic",
9699
- "tif",
9700
- "tiff"
9759
+
9760
+ // ../shared/dist/index.js
9761
+ var IMAGE_MIME_TYPES = {
9762
+ png: "image/png",
9763
+ jpg: "image/jpeg",
9764
+ jpeg: "image/jpeg",
9765
+ gif: "image/gif",
9766
+ webp: "image/webp",
9767
+ bmp: "image/bmp",
9768
+ ico: "image/x-icon",
9769
+ tiff: "image/tiff",
9770
+ tif: "image/tiff",
9771
+ svg: "image/svg+xml",
9772
+ heic: "image/heic",
9773
+ heif: "image/heif",
9774
+ avif: "image/avif"
9775
+ };
9776
+ var IMAGE_EXTENSIONS = new Set(
9777
+ Object.keys(IMAGE_MIME_TYPES)
9778
+ );
9779
+ var CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
9780
+ "image/jpeg",
9781
+ "image/png",
9782
+ "image/gif",
9783
+ "image/webp"
9784
+ ]);
9785
+ function isClaudeImageMimeType(mimeType) {
9786
+ return CLAUDE_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());
9787
+ }
9788
+ var MAX_DATA_URL_LENGTH = 20 * 1024 * 1024;
9789
+ var MAX_IMAGE_BASE64_LENGTH = 15 * 1024 * 1024;
9790
+ function extensionOf(filename) {
9791
+ const slash = Math.max(filename.lastIndexOf("/"), filename.lastIndexOf("\\"));
9792
+ const basename3 = slash >= 0 ? filename.slice(slash + 1) : filename;
9793
+ const cleanBasename = basename3.split(/[?#]/)[0];
9794
+ const dot = cleanBasename.lastIndexOf(".");
9795
+ return dot > 0 ? cleanBasename.slice(dot + 1).toLowerCase() : "";
9796
+ }
9797
+ function isImageFile(filename) {
9798
+ const ext = extensionOf(filename);
9799
+ return ext.length > 0 && IMAGE_EXTENSIONS.has(ext);
9800
+ }
9801
+ var AUDIO_VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
9802
+ "mp3",
9803
+ "mp4",
9804
+ "wav",
9805
+ "avi",
9806
+ "mov",
9807
+ "mkv",
9808
+ "webm",
9809
+ "mpg",
9810
+ "mpeg",
9811
+ "flac",
9812
+ "ogg",
9813
+ "m4a",
9814
+ "aac"
9815
+ ]);
9816
+ var ARCHIVE_EXTENSIONS = /* @__PURE__ */ new Set([
9817
+ "zip",
9818
+ "tar",
9819
+ "gz",
9820
+ "tgz",
9821
+ "bz2",
9822
+ "xz",
9823
+ "rar",
9824
+ "7z"
9825
+ ]);
9826
+ var EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
9827
+ "exe",
9828
+ "dll",
9829
+ "so",
9830
+ "dylib",
9831
+ "wasm",
9832
+ "bin",
9833
+ "o"
9834
+ ]);
9835
+ var FONT_EXTENSIONS = /* @__PURE__ */ new Set([
9836
+ "ttf",
9837
+ "otf",
9838
+ "woff",
9839
+ "woff2",
9840
+ "eot"
9841
+ ]);
9842
+ var DOCUMENT_BINARY_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
9843
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
9844
+ ...Object.keys(IMAGE_MIME_TYPES).filter((ext) => ext !== "svg"),
9845
+ ...AUDIO_VIDEO_EXTENSIONS,
9846
+ ...ARCHIVE_EXTENSIONS,
9847
+ ...EXECUTABLE_EXTENSIONS,
9848
+ ...FONT_EXTENSIONS,
9849
+ ...DOCUMENT_BINARY_EXTENSIONS
9701
9850
  ]);
9851
+
9852
+ // src/adapters/claude/conversion/acp-to-sdk.ts
9853
+ var PDF_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
9702
9854
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
9703
9855
  "mp4",
9704
9856
  "mov",
@@ -9727,7 +9879,7 @@ function readToolGuidanceForPath(filePath) {
9727
9879
  if (PDF_EXTENSIONS.has(ext)) {
9728
9880
  return 'Optional `pages` string (e.g. "1-5") per Read call instead of loading the entire PDF.';
9729
9881
  }
9730
- if (COMMON_IMAGE_EXTENSIONS.has(ext) || VIDEO_EXTENSIONS.has(ext)) {
9882
+ if (isImageFile(filePath) || VIDEO_EXTENSIONS.has(ext)) {
9731
9883
  return "Binary file \u2014 use Read with `file_path`; prefer bounded reads where supported.";
9732
9884
  }
9733
9885
  return "Large text \u2014 use multiple Read calls with optional `offset` and `limit`.";
@@ -9793,14 +9945,22 @@ ${chunk.resource.text}
9793
9945
  break;
9794
9946
  case "image":
9795
9947
  if (chunk.data) {
9796
- content.push({
9797
- type: "image",
9798
- source: {
9799
- type: "base64",
9800
- data: chunk.data,
9801
- media_type: chunk.mimeType
9802
- }
9803
- });
9948
+ if (isClaudeImageMimeType(chunk.mimeType)) {
9949
+ content.push({
9950
+ type: "image",
9951
+ source: {
9952
+ type: "base64",
9953
+ data: chunk.data,
9954
+ media_type: chunk.mimeType
9955
+ }
9956
+ });
9957
+ } else {
9958
+ content.push(
9959
+ sdkText(
9960
+ `[Unsupported image MIME type: ${chunk.mimeType}. Supported: image/jpeg, image/png, image/gif, image/webp.]`
9961
+ )
9962
+ );
9963
+ }
9804
9964
  } else if (chunk.uri?.startsWith("http")) {
9805
9965
  content.push({
9806
9966
  type: "image",
@@ -10269,6 +10429,9 @@ function getConnectedMcpServerNames() {
10269
10429
  }
10270
10430
  return [...names];
10271
10431
  }
10432
+ function getCachedMcpTools() {
10433
+ return [...mcpToolMetadataCache.values()];
10434
+ }
10272
10435
  function getMcpToolApprovalState(toolName) {
10273
10436
  return mcpToolMetadataCache.get(toolName)?.approvalState;
10274
10437
  }
@@ -18017,6 +18180,19 @@ var SettingsManager = class {
18017
18180
  var SESSION_VALIDATION_TIMEOUT_MS = 3e4;
18018
18181
  var MAX_TITLE_LENGTH = 256;
18019
18182
  var LOCAL_ONLY_COMMANDS = /* @__PURE__ */ new Set(["/context", "/heapdump", "/extra-usage"]);
18183
+ function readClaudeMdQuietly(cwd, logger) {
18184
+ try {
18185
+ return fs10.readFileSync(path14.join(cwd, "CLAUDE.md"), "utf-8");
18186
+ } catch (err2) {
18187
+ if (err2?.code !== "ENOENT") {
18188
+ logger.warn("Failed to read CLAUDE.md for context breakdown", {
18189
+ cwd,
18190
+ error: err2 instanceof Error ? err2.message : String(err2)
18191
+ });
18192
+ }
18193
+ return void 0;
18194
+ }
18195
+ }
18020
18196
  function sanitizeTitle(text2) {
18021
18197
  const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
18022
18198
  if (sanitized.length <= MAX_TITLE_LENGTH) {
@@ -18282,6 +18458,24 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18282
18458
  }
18283
18459
  if (message.subtype === "session_state_changed" && message.state === "idle") {
18284
18460
  if (!promptReplayed) {
18461
+ if (commandMatch) {
18462
+ const cmd = commandMatch[1];
18463
+ this.logger.warn(
18464
+ "Slash command produced no output; treating as unsupported",
18465
+ { sessionId: params.sessionId, command: cmd }
18466
+ );
18467
+ await this.client.sessionUpdate({
18468
+ sessionId: params.sessionId,
18469
+ update: {
18470
+ sessionUpdate: "agent_message_chunk",
18471
+ content: {
18472
+ type: "text",
18473
+ text: `Unsupported slash command: \`${cmd}\`. PostHog Code does not implement this command.`
18474
+ }
18475
+ }
18476
+ });
18477
+ return { stopReason: "end_turn" };
18478
+ }
18285
18479
  this.logger.debug("Skipping idle state before prompt replay", {
18286
18480
  sessionId: params.sessionId
18287
18481
  });
@@ -18344,6 +18538,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18344
18538
  }
18345
18539
  });
18346
18540
  }
18541
+ const breakdownInputTokens = lastStreamUsage.input_tokens + lastStreamUsage.cache_read_input_tokens + lastStreamUsage.cache_creation_input_tokens;
18347
18542
  await this.client.extNotification(
18348
18543
  POSTHOG_NOTIFICATIONS.USAGE_UPDATE,
18349
18544
  {
@@ -18354,7 +18549,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18354
18549
  cachedReadTokens: message.usage.cache_read_input_tokens,
18355
18550
  cachedWriteTokens: message.usage.cache_creation_input_tokens
18356
18551
  },
18357
- cost: message.total_cost_usd
18552
+ cost: message.total_cost_usd,
18553
+ breakdown: buildBreakdown(
18554
+ this.session.contextBreakdownBaseline ?? emptyBaseline(),
18555
+ breakdownInputTokens
18556
+ )
18358
18557
  }
18359
18558
  );
18360
18559
  const usage = {
@@ -18805,6 +19004,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18805
19004
  pendingMessages: /* @__PURE__ */ new Map(),
18806
19005
  nextPendingOrder: 0,
18807
19006
  emitRawSDKMessages: meta?.claudeCode?.emitRawSDKMessages ?? false,
19007
+ contextBreakdownBaseline: {
19008
+ ...emptyBaseline(),
19009
+ systemPrompt: estimateSystemPrompt(systemPrompt),
19010
+ rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19011
+ },
18808
19012
  // Custom properties
18809
19013
  cwd,
18810
19014
  notificationHistory: [],
@@ -19062,13 +19266,26 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19062
19266
  }
19063
19267
  async sendAvailableCommandsUpdate() {
19064
19268
  const commands = await this.session.query.supportedCommands();
19269
+ const available = getAvailableSlashCommands(commands);
19065
19270
  await this.client.sessionUpdate({
19066
19271
  sessionId: this.sessionId,
19067
19272
  update: {
19068
19273
  sessionUpdate: "available_commands_update",
19069
- availableCommands: getAvailableSlashCommands(commands)
19274
+ availableCommands: available
19070
19275
  }
19071
19276
  });
19277
+ this.updateBreakdownCategory("skills", estimateSkillsTokens(available));
19278
+ }
19279
+ /** Update one category of the context-breakdown baseline so the next
19280
+ * `_posthog/usage_update` carries fresher numbers. No-op when the baseline
19281
+ * hasn't been initialized yet (e.g. in a unit-test session). */
19282
+ updateBreakdownCategory(key, tokens) {
19283
+ if (!this.session?.contextBreakdownBaseline) return;
19284
+ if (this.session.contextBreakdownBaseline[key] === tokens) return;
19285
+ this.session.contextBreakdownBaseline = {
19286
+ ...this.session.contextBreakdownBaseline,
19287
+ [key]: tokens
19288
+ };
19072
19289
  }
19073
19290
  async replaySessionHistory(sessionId) {
19074
19291
  try {
@@ -19117,6 +19334,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19117
19334
  () => this.sendAvailableCommandsUpdate()
19118
19335
  ),
19119
19336
  fetchMcpToolMetadata(q, this.logger).then(() => {
19337
+ this.updateBreakdownCategory(
19338
+ "mcp",
19339
+ estimateMcpTokens(getCachedMcpTools())
19340
+ );
19120
19341
  const serverNames = getConnectedMcpServerNames();
19121
19342
  if (serverNames.length > 0) {
19122
19343
  this.options?.onMcpServersReady?.(serverNames);
@@ -19381,6 +19602,25 @@ function createSessionState(sessionId, cwd, opts) {
19381
19602
  taskId: opts?.taskId
19382
19603
  };
19383
19604
  }
19605
+ function resetSessionState(state, sessionId, cwd, opts) {
19606
+ state.sessionId = sessionId;
19607
+ state.cwd = cwd;
19608
+ state.modeId = opts?.modeId ?? "auto";
19609
+ state.modelId = opts?.modelId;
19610
+ state.configOptions = [];
19611
+ state.accumulatedUsage = {
19612
+ inputTokens: 0,
19613
+ outputTokens: 0,
19614
+ cachedReadTokens: 0,
19615
+ cachedWriteTokens: 0
19616
+ };
19617
+ state.contextSize = void 0;
19618
+ state.contextUsed = void 0;
19619
+ state.contextBreakdownBaseline = void 0;
19620
+ state.permissionMode = opts?.permissionMode ?? "auto";
19621
+ state.taskRunId = opts?.taskRunId;
19622
+ state.taskId = opts?.taskId;
19623
+ }
19384
19624
  function resetUsage(state) {
19385
19625
  state.accumulatedUsage = {
19386
19626
  inputTokens: 0,
@@ -19602,6 +19842,12 @@ function classifyPromptError(error) {
19602
19842
  message
19603
19843
  );
19604
19844
  }
19845
+ var CODEX_BASELINE_TOKENS = 12e3;
19846
+ function buildCodexBaseline(meta) {
19847
+ const baseline = emptyBaseline();
19848
+ baseline.systemPrompt = CODEX_BASELINE_TOKENS + estimateTokens(meta?.systemPrompt);
19849
+ return baseline;
19850
+ }
19605
19851
  var CODEX_NATIVE_MODE = {
19606
19852
  auto: "auto",
19607
19853
  default: "auto",
@@ -19771,7 +20017,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19771
20017
  response.configOptions = normalizeCodexConfigOptions(
19772
20018
  response.configOptions
19773
20019
  );
19774
- this.sessionState = createSessionState(response.sessionId, params.cwd, {
20020
+ resetSessionState(this.sessionState, response.sessionId, params.cwd, {
19775
20021
  taskRunId: meta?.taskRunId,
19776
20022
  taskId: resolveTaskId(meta),
19777
20023
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19780,6 +20026,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19780
20026
  });
19781
20027
  this.sessionId = response.sessionId;
19782
20028
  this.sessionState.configOptions = response.configOptions ?? [];
20029
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19783
20030
  await this.applyInitialPermissionMode(
19784
20031
  response.sessionId,
19785
20032
  meta?.permissionMode,
@@ -19812,7 +20059,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19812
20059
  response.modes?.currentModeId,
19813
20060
  meta?.permissionMode
19814
20061
  );
19815
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20062
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19816
20063
  taskRunId: meta?.taskRunId,
19817
20064
  taskId: resolveTaskId(meta),
19818
20065
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19820,6 +20067,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19820
20067
  });
19821
20068
  this.sessionId = params.sessionId;
19822
20069
  this.sessionState.configOptions = response.configOptions ?? [];
20070
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19823
20071
  if (meta?.taskRunId) {
19824
20072
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19825
20073
  taskRunId: meta.taskRunId,
@@ -19851,7 +20099,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19851
20099
  loadResponse.modes?.currentModeId,
19852
20100
  meta?.permissionMode
19853
20101
  );
19854
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20102
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19855
20103
  taskRunId: meta?.taskRunId,
19856
20104
  taskId: resolveTaskId(meta),
19857
20105
  modeId: loadResponse.modes?.currentModeId ?? "auto",
@@ -19859,6 +20107,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19859
20107
  });
19860
20108
  this.sessionId = params.sessionId;
19861
20109
  this.sessionState.configOptions = loadResponse.configOptions ?? [];
20110
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19862
20111
  if (meta?.taskRunId) {
19863
20112
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19864
20113
  taskRunId: meta.taskRunId,
@@ -19890,7 +20139,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19890
20139
  newResponse.configOptions
19891
20140
  );
19892
20141
  const requestedPermissionMode = toCodexPermissionMode(meta?.permissionMode);
19893
- this.sessionState = createSessionState(newResponse.sessionId, params.cwd, {
20142
+ resetSessionState(this.sessionState, newResponse.sessionId, params.cwd, {
19894
20143
  taskRunId: meta?.taskRunId,
19895
20144
  taskId: resolveTaskId(meta),
19896
20145
  modeId: newResponse.modes?.currentModeId ?? "auto",
@@ -19898,6 +20147,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19898
20147
  });
19899
20148
  this.sessionId = newResponse.sessionId;
19900
20149
  this.sessionState.configOptions = newResponse.configOptions ?? [];
20150
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19901
20151
  await this.applyInitialPermissionMode(
19902
20152
  newResponse.sessionId,
19903
20153
  meta?.permissionMode,
@@ -20034,6 +20284,15 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20034
20284
  });
20035
20285
  }
20036
20286
  }
20287
+ if (this.sessionState.contextUsed !== void 0) {
20288
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.USAGE_UPDATE, {
20289
+ sessionId: params.sessionId,
20290
+ breakdown: buildBreakdown(
20291
+ this.sessionState.contextBreakdownBaseline ?? emptyBaseline(),
20292
+ this.sessionState.contextUsed
20293
+ )
20294
+ });
20295
+ }
20037
20296
  return response;
20038
20297
  }
20039
20298
  async interrupt() {