@posthog/agent 2.3.678 → 2.3.696

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.696",
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"
9701
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
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) {
@@ -18344,6 +18520,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18344
18520
  }
18345
18521
  });
18346
18522
  }
18523
+ const breakdownInputTokens = lastStreamUsage.input_tokens + lastStreamUsage.cache_read_input_tokens + lastStreamUsage.cache_creation_input_tokens;
18347
18524
  await this.client.extNotification(
18348
18525
  POSTHOG_NOTIFICATIONS.USAGE_UPDATE,
18349
18526
  {
@@ -18354,7 +18531,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18354
18531
  cachedReadTokens: message.usage.cache_read_input_tokens,
18355
18532
  cachedWriteTokens: message.usage.cache_creation_input_tokens
18356
18533
  },
18357
- cost: message.total_cost_usd
18534
+ cost: message.total_cost_usd,
18535
+ breakdown: buildBreakdown(
18536
+ this.session.contextBreakdownBaseline ?? emptyBaseline(),
18537
+ breakdownInputTokens
18538
+ )
18358
18539
  }
18359
18540
  );
18360
18541
  const usage = {
@@ -18805,6 +18986,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18805
18986
  pendingMessages: /* @__PURE__ */ new Map(),
18806
18987
  nextPendingOrder: 0,
18807
18988
  emitRawSDKMessages: meta?.claudeCode?.emitRawSDKMessages ?? false,
18989
+ contextBreakdownBaseline: {
18990
+ ...emptyBaseline(),
18991
+ systemPrompt: estimateSystemPrompt(systemPrompt),
18992
+ rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
18993
+ },
18808
18994
  // Custom properties
18809
18995
  cwd,
18810
18996
  notificationHistory: [],
@@ -19062,13 +19248,26 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19062
19248
  }
19063
19249
  async sendAvailableCommandsUpdate() {
19064
19250
  const commands = await this.session.query.supportedCommands();
19251
+ const available = getAvailableSlashCommands(commands);
19065
19252
  await this.client.sessionUpdate({
19066
19253
  sessionId: this.sessionId,
19067
19254
  update: {
19068
19255
  sessionUpdate: "available_commands_update",
19069
- availableCommands: getAvailableSlashCommands(commands)
19256
+ availableCommands: available
19070
19257
  }
19071
19258
  });
19259
+ this.updateBreakdownCategory("skills", estimateSkillsTokens(available));
19260
+ }
19261
+ /** Update one category of the context-breakdown baseline so the next
19262
+ * `_posthog/usage_update` carries fresher numbers. No-op when the baseline
19263
+ * hasn't been initialized yet (e.g. in a unit-test session). */
19264
+ updateBreakdownCategory(key, tokens) {
19265
+ if (!this.session?.contextBreakdownBaseline) return;
19266
+ if (this.session.contextBreakdownBaseline[key] === tokens) return;
19267
+ this.session.contextBreakdownBaseline = {
19268
+ ...this.session.contextBreakdownBaseline,
19269
+ [key]: tokens
19270
+ };
19072
19271
  }
19073
19272
  async replaySessionHistory(sessionId) {
19074
19273
  try {
@@ -19117,6 +19316,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19117
19316
  () => this.sendAvailableCommandsUpdate()
19118
19317
  ),
19119
19318
  fetchMcpToolMetadata(q, this.logger).then(() => {
19319
+ this.updateBreakdownCategory(
19320
+ "mcp",
19321
+ estimateMcpTokens(getCachedMcpTools())
19322
+ );
19120
19323
  const serverNames = getConnectedMcpServerNames();
19121
19324
  if (serverNames.length > 0) {
19122
19325
  this.options?.onMcpServersReady?.(serverNames);
@@ -19381,6 +19584,25 @@ function createSessionState(sessionId, cwd, opts) {
19381
19584
  taskId: opts?.taskId
19382
19585
  };
19383
19586
  }
19587
+ function resetSessionState(state, sessionId, cwd, opts) {
19588
+ state.sessionId = sessionId;
19589
+ state.cwd = cwd;
19590
+ state.modeId = opts?.modeId ?? "auto";
19591
+ state.modelId = opts?.modelId;
19592
+ state.configOptions = [];
19593
+ state.accumulatedUsage = {
19594
+ inputTokens: 0,
19595
+ outputTokens: 0,
19596
+ cachedReadTokens: 0,
19597
+ cachedWriteTokens: 0
19598
+ };
19599
+ state.contextSize = void 0;
19600
+ state.contextUsed = void 0;
19601
+ state.contextBreakdownBaseline = void 0;
19602
+ state.permissionMode = opts?.permissionMode ?? "auto";
19603
+ state.taskRunId = opts?.taskRunId;
19604
+ state.taskId = opts?.taskId;
19605
+ }
19384
19606
  function resetUsage(state) {
19385
19607
  state.accumulatedUsage = {
19386
19608
  inputTokens: 0,
@@ -19602,6 +19824,12 @@ function classifyPromptError(error) {
19602
19824
  message
19603
19825
  );
19604
19826
  }
19827
+ var CODEX_BASELINE_TOKENS = 12e3;
19828
+ function buildCodexBaseline(meta) {
19829
+ const baseline = emptyBaseline();
19830
+ baseline.systemPrompt = CODEX_BASELINE_TOKENS + estimateTokens(meta?.systemPrompt);
19831
+ return baseline;
19832
+ }
19605
19833
  var CODEX_NATIVE_MODE = {
19606
19834
  auto: "auto",
19607
19835
  default: "auto",
@@ -19771,7 +19999,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19771
19999
  response.configOptions = normalizeCodexConfigOptions(
19772
20000
  response.configOptions
19773
20001
  );
19774
- this.sessionState = createSessionState(response.sessionId, params.cwd, {
20002
+ resetSessionState(this.sessionState, response.sessionId, params.cwd, {
19775
20003
  taskRunId: meta?.taskRunId,
19776
20004
  taskId: resolveTaskId(meta),
19777
20005
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19780,6 +20008,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19780
20008
  });
19781
20009
  this.sessionId = response.sessionId;
19782
20010
  this.sessionState.configOptions = response.configOptions ?? [];
20011
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19783
20012
  await this.applyInitialPermissionMode(
19784
20013
  response.sessionId,
19785
20014
  meta?.permissionMode,
@@ -19812,7 +20041,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19812
20041
  response.modes?.currentModeId,
19813
20042
  meta?.permissionMode
19814
20043
  );
19815
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20044
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19816
20045
  taskRunId: meta?.taskRunId,
19817
20046
  taskId: resolveTaskId(meta),
19818
20047
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19820,6 +20049,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19820
20049
  });
19821
20050
  this.sessionId = params.sessionId;
19822
20051
  this.sessionState.configOptions = response.configOptions ?? [];
20052
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19823
20053
  if (meta?.taskRunId) {
19824
20054
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19825
20055
  taskRunId: meta.taskRunId,
@@ -19851,7 +20081,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19851
20081
  loadResponse.modes?.currentModeId,
19852
20082
  meta?.permissionMode
19853
20083
  );
19854
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20084
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19855
20085
  taskRunId: meta?.taskRunId,
19856
20086
  taskId: resolveTaskId(meta),
19857
20087
  modeId: loadResponse.modes?.currentModeId ?? "auto",
@@ -19859,6 +20089,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19859
20089
  });
19860
20090
  this.sessionId = params.sessionId;
19861
20091
  this.sessionState.configOptions = loadResponse.configOptions ?? [];
20092
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19862
20093
  if (meta?.taskRunId) {
19863
20094
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19864
20095
  taskRunId: meta.taskRunId,
@@ -19890,7 +20121,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19890
20121
  newResponse.configOptions
19891
20122
  );
19892
20123
  const requestedPermissionMode = toCodexPermissionMode(meta?.permissionMode);
19893
- this.sessionState = createSessionState(newResponse.sessionId, params.cwd, {
20124
+ resetSessionState(this.sessionState, newResponse.sessionId, params.cwd, {
19894
20125
  taskRunId: meta?.taskRunId,
19895
20126
  taskId: resolveTaskId(meta),
19896
20127
  modeId: newResponse.modes?.currentModeId ?? "auto",
@@ -19898,6 +20129,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19898
20129
  });
19899
20130
  this.sessionId = newResponse.sessionId;
19900
20131
  this.sessionState.configOptions = newResponse.configOptions ?? [];
20132
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19901
20133
  await this.applyInitialPermissionMode(
19902
20134
  newResponse.sessionId,
19903
20135
  meta?.permissionMode,
@@ -20034,6 +20266,15 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20034
20266
  });
20035
20267
  }
20036
20268
  }
20269
+ if (this.sessionState.contextUsed !== void 0) {
20270
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.USAGE_UPDATE, {
20271
+ sessionId: params.sessionId,
20272
+ breakdown: buildBreakdown(
20273
+ this.sessionState.contextBreakdownBaseline ?? emptyBaseline(),
20274
+ this.sessionState.contextUsed
20275
+ )
20276
+ });
20277
+ }
20037
20278
  return response;
20038
20279
  }
20039
20280
  async interrupt() {