@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.
@@ -4429,7 +4429,7 @@ var require_lib5 = __commonJS({
4429
4429
 
4430
4430
  // src/server/agent-server.ts
4431
4431
  import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
4432
- import { basename as basename3, join as join13 } from "path";
4432
+ import { basename as basename3, join as join14 } from "path";
4433
4433
  import { pathToFileURL } from "url";
4434
4434
  import {
4435
4435
  ClientSideConnection as ClientSideConnection2,
@@ -9315,7 +9315,7 @@ import { z as z5 } from "zod";
9315
9315
  // package.json
9316
9316
  var package_default = {
9317
9317
  name: "@posthog/agent",
9318
- version: "2.3.678",
9318
+ version: "2.3.696",
9319
9319
  repository: "https://github.com/PostHog/code",
9320
9320
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
9321
9321
  exports: {
@@ -14517,22 +14517,314 @@ function resolveTaskId(meta) {
14517
14517
  return meta?.taskId ?? meta?.persistence?.taskId;
14518
14518
  }
14519
14519
 
14520
+ // src/adapters/claude/context-breakdown.ts
14521
+ var CLAUDE_PRESET_ESTIMATE_TOKENS = 4e3;
14522
+ var CHARS_PER_TOKEN = 4;
14523
+ function estimateTokens(text2) {
14524
+ if (!text2) return 0;
14525
+ return Math.max(0, Math.round(text2.length / CHARS_PER_TOKEN));
14526
+ }
14527
+ function estimateJsonTokens(value) {
14528
+ try {
14529
+ return estimateTokens(JSON.stringify(value));
14530
+ } catch {
14531
+ return 0;
14532
+ }
14533
+ }
14534
+ function estimateSkillsTokens(commands) {
14535
+ if (!commands.length) return 0;
14536
+ return estimateJsonTokens(
14537
+ commands.map((c) => ({
14538
+ name: c.name,
14539
+ description: c.description,
14540
+ hint: c.input?.hint
14541
+ }))
14542
+ );
14543
+ }
14544
+ function estimateMcpTokens(tools) {
14545
+ if (!tools.length) return 0;
14546
+ return estimateJsonTokens(
14547
+ tools.map((t) => ({ name: t.name, description: t.description }))
14548
+ );
14549
+ }
14550
+ function estimateRulesTokens(rules) {
14551
+ return estimateTokens(rules);
14552
+ }
14553
+ function emptyBaseline() {
14554
+ return {
14555
+ systemPrompt: 0,
14556
+ tools: 0,
14557
+ rules: 0,
14558
+ skills: 0,
14559
+ mcp: 0,
14560
+ subagents: 0
14561
+ };
14562
+ }
14563
+ function estimateSystemPrompt(systemPrompt) {
14564
+ if (!systemPrompt) return CLAUDE_PRESET_ESTIMATE_TOKENS;
14565
+ if (typeof systemPrompt === "string") return estimateTokens(systemPrompt);
14566
+ if (typeof systemPrompt === "object") {
14567
+ const obj = systemPrompt;
14568
+ const appendTokens = typeof obj.append === "string" ? estimateTokens(obj.append) : 0;
14569
+ if (obj.type === "preset") {
14570
+ return CLAUDE_PRESET_ESTIMATE_TOKENS + appendTokens;
14571
+ }
14572
+ return appendTokens;
14573
+ }
14574
+ return 0;
14575
+ }
14576
+ function buildBreakdown(baseline, currentInputTokens) {
14577
+ const stableSum = baseline.systemPrompt + baseline.tools + baseline.rules + baseline.skills + baseline.mcp + baseline.subagents;
14578
+ const conversation = Math.max(0, currentInputTokens - stableSum);
14579
+ return {
14580
+ systemPrompt: baseline.systemPrompt,
14581
+ tools: baseline.tools,
14582
+ rules: baseline.rules,
14583
+ skills: baseline.skills,
14584
+ mcp: baseline.mcp,
14585
+ subagents: baseline.subagents,
14586
+ conversation
14587
+ };
14588
+ }
14589
+
14520
14590
  // src/adapters/claude/conversion/acp-to-sdk.ts
14521
14591
  import * as path6 from "path";
14522
14592
  import { fileURLToPath as fileURLToPath2 } from "url";
14523
- var PDF_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
14524
- var COMMON_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
14525
- "png",
14526
- "jpg",
14527
- "jpeg",
14528
- "gif",
14529
- "webp",
14530
- "bmp",
14531
- "svg",
14532
- "heic",
14533
- "tif",
14534
- "tiff"
14593
+
14594
+ // ../shared/dist/index.js
14595
+ var IMAGE_MIME_TYPES = {
14596
+ png: "image/png",
14597
+ jpg: "image/jpeg",
14598
+ jpeg: "image/jpeg",
14599
+ gif: "image/gif",
14600
+ webp: "image/webp",
14601
+ bmp: "image/bmp",
14602
+ ico: "image/x-icon",
14603
+ tiff: "image/tiff",
14604
+ tif: "image/tiff",
14605
+ svg: "image/svg+xml",
14606
+ heic: "image/heic",
14607
+ heif: "image/heif",
14608
+ avif: "image/avif"
14609
+ };
14610
+ var IMAGE_EXTENSIONS = new Set(
14611
+ Object.keys(IMAGE_MIME_TYPES)
14612
+ );
14613
+ var CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
14614
+ "image/jpeg",
14615
+ "image/png",
14616
+ "image/gif",
14617
+ "image/webp"
14618
+ ]);
14619
+ function isClaudeImageMimeType(mimeType) {
14620
+ return CLAUDE_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());
14621
+ }
14622
+ var MAX_DATA_URL_LENGTH = 20 * 1024 * 1024;
14623
+ var MAX_IMAGE_BASE64_LENGTH = 15 * 1024 * 1024;
14624
+ function extensionOf(filename) {
14625
+ const slash = Math.max(filename.lastIndexOf("/"), filename.lastIndexOf("\\"));
14626
+ const basename4 = slash >= 0 ? filename.slice(slash + 1) : filename;
14627
+ const cleanBasename = basename4.split(/[?#]/)[0];
14628
+ const dot = cleanBasename.lastIndexOf(".");
14629
+ return dot > 0 ? cleanBasename.slice(dot + 1).toLowerCase() : "";
14630
+ }
14631
+ function isImageFile(filename) {
14632
+ const ext = extensionOf(filename);
14633
+ return ext.length > 0 && IMAGE_EXTENSIONS.has(ext);
14634
+ }
14635
+ var AUDIO_VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
14636
+ "mp3",
14637
+ "mp4",
14638
+ "wav",
14639
+ "avi",
14640
+ "mov",
14641
+ "mkv",
14642
+ "webm",
14643
+ "mpg",
14644
+ "mpeg",
14645
+ "flac",
14646
+ "ogg",
14647
+ "m4a",
14648
+ "aac"
14535
14649
  ]);
14650
+ var ARCHIVE_EXTENSIONS = /* @__PURE__ */ new Set([
14651
+ "zip",
14652
+ "tar",
14653
+ "gz",
14654
+ "tgz",
14655
+ "bz2",
14656
+ "xz",
14657
+ "rar",
14658
+ "7z"
14659
+ ]);
14660
+ var EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
14661
+ "exe",
14662
+ "dll",
14663
+ "so",
14664
+ "dylib",
14665
+ "wasm",
14666
+ "bin",
14667
+ "o"
14668
+ ]);
14669
+ var FONT_EXTENSIONS = /* @__PURE__ */ new Set([
14670
+ "ttf",
14671
+ "otf",
14672
+ "woff",
14673
+ "woff2",
14674
+ "eot"
14675
+ ]);
14676
+ var DOCUMENT_BINARY_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
14677
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
14678
+ ...Object.keys(IMAGE_MIME_TYPES).filter((ext) => ext !== "svg"),
14679
+ ...AUDIO_VIDEO_EXTENSIONS,
14680
+ ...ARCHIVE_EXTENSIONS,
14681
+ ...EXECUTABLE_EXTENSIONS,
14682
+ ...FONT_EXTENSIONS,
14683
+ ...DOCUMENT_BINARY_EXTENSIONS
14684
+ ]);
14685
+ var CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:";
14686
+ function deserializeCloudPrompt(value) {
14687
+ const trimmed2 = value.trim();
14688
+ if (!trimmed2) {
14689
+ return [];
14690
+ }
14691
+ if (!trimmed2.startsWith(CLOUD_PROMPT_PREFIX)) {
14692
+ return [{ type: "text", text: trimmed2 }];
14693
+ }
14694
+ try {
14695
+ const parsed = JSON.parse(trimmed2.slice(CLOUD_PROMPT_PREFIX.length));
14696
+ if (Array.isArray(parsed.blocks) && parsed.blocks.length > 0) {
14697
+ return parsed.blocks;
14698
+ }
14699
+ } catch {
14700
+ }
14701
+ return [{ type: "text", text: trimmed2 }];
14702
+ }
14703
+ function promptBlocksToText(blocks) {
14704
+ return blocks.filter((b) => b.type === "text").map((block) => block.text).join("").trim();
14705
+ }
14706
+ var consoleLogger = {
14707
+ info: (_message, _data) => {
14708
+ },
14709
+ debug: (_message, _data) => {
14710
+ },
14711
+ error: (_message, _data) => {
14712
+ },
14713
+ warn: (_message, _data) => {
14714
+ }
14715
+ };
14716
+ var Saga = class {
14717
+ completedSteps = [];
14718
+ currentStepName = "unknown";
14719
+ stepTimings = [];
14720
+ log;
14721
+ constructor(logger) {
14722
+ this.log = logger ?? consoleLogger;
14723
+ }
14724
+ /**
14725
+ * Run the saga with the given input.
14726
+ * Returns a discriminated union result - either success with data or failure with error details.
14727
+ */
14728
+ async run(input) {
14729
+ this.completedSteps = [];
14730
+ this.currentStepName = "unknown";
14731
+ this.stepTimings = [];
14732
+ const sagaStart = performance.now();
14733
+ try {
14734
+ const result = await this.execute(input);
14735
+ const totalDuration = performance.now() - sagaStart;
14736
+ this.log.debug("Saga completed successfully", {
14737
+ sagaName: this.sagaName,
14738
+ stepsCompleted: this.completedSteps.length,
14739
+ totalDurationMs: Math.round(totalDuration),
14740
+ stepTimings: this.stepTimings
14741
+ });
14742
+ return { success: true, data: result };
14743
+ } catch (error) {
14744
+ this.log.error("Saga failed, initiating rollback", {
14745
+ sagaName: this.sagaName,
14746
+ failedStep: this.currentStepName,
14747
+ error: error instanceof Error ? error.message : String(error),
14748
+ completedStepTimings: this.stepTimings
14749
+ });
14750
+ await this.rollback();
14751
+ return {
14752
+ success: false,
14753
+ error: error instanceof Error ? error.message : String(error),
14754
+ failedStep: this.currentStepName
14755
+ };
14756
+ }
14757
+ }
14758
+ /**
14759
+ * Execute a step with its rollback action.
14760
+ * If the step succeeds, its rollback action is stored for potential rollback.
14761
+ * The step name is automatically tracked for error reporting.
14762
+ *
14763
+ * @param config - Step configuration with name, execute, and rollback functions
14764
+ * @returns The result of the execute function
14765
+ * @throws Re-throws any error from the execute function (triggers rollback)
14766
+ */
14767
+ async step(config) {
14768
+ this.currentStepName = config.name;
14769
+ const stepStart = performance.now();
14770
+ const result = await config.execute();
14771
+ const durationMs = Math.round(performance.now() - stepStart);
14772
+ this.stepTimings.push({ name: config.name, durationMs });
14773
+ this.completedSteps.push({
14774
+ name: config.name,
14775
+ rollback: () => config.rollback(result)
14776
+ });
14777
+ return result;
14778
+ }
14779
+ /**
14780
+ * Execute a step that doesn't need rollback.
14781
+ * Useful for read-only operations or operations that are idempotent.
14782
+ * The step name is automatically tracked for error reporting.
14783
+ *
14784
+ * @param name - Step name for logging and error tracking
14785
+ * @param execute - The action to execute
14786
+ * @returns The result of the execute function
14787
+ */
14788
+ async readOnlyStep(name2, execute) {
14789
+ this.currentStepName = name2;
14790
+ const stepStart = performance.now();
14791
+ const result = await execute();
14792
+ const durationMs = Math.round(performance.now() - stepStart);
14793
+ this.stepTimings.push({ name: name2, durationMs });
14794
+ return result;
14795
+ }
14796
+ /**
14797
+ * Roll back all completed steps in reverse order.
14798
+ * Rollback errors are logged but don't stop the rollback of other steps.
14799
+ */
14800
+ async rollback() {
14801
+ this.log.info("Rolling back saga", {
14802
+ stepsToRollback: this.completedSteps.length
14803
+ });
14804
+ const stepsReversed = [...this.completedSteps].reverse();
14805
+ for (const step of stepsReversed) {
14806
+ try {
14807
+ await step.rollback();
14808
+ } catch (error) {
14809
+ this.log.error(`Failed to rollback step: ${step.name}`, {
14810
+ error: error instanceof Error ? error.message : String(error)
14811
+ });
14812
+ }
14813
+ }
14814
+ this.log.info("Rollback completed", {
14815
+ stepsAttempted: this.completedSteps.length
14816
+ });
14817
+ }
14818
+ /**
14819
+ * Get the number of completed steps (useful for testing)
14820
+ */
14821
+ getCompletedStepCount() {
14822
+ return this.completedSteps.length;
14823
+ }
14824
+ };
14825
+
14826
+ // src/adapters/claude/conversion/acp-to-sdk.ts
14827
+ var PDF_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
14536
14828
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
14537
14829
  "mp4",
14538
14830
  "mov",
@@ -14561,7 +14853,7 @@ function readToolGuidanceForPath(filePath) {
14561
14853
  if (PDF_EXTENSIONS.has(ext)) {
14562
14854
  return 'Optional `pages` string (e.g. "1-5") per Read call instead of loading the entire PDF.';
14563
14855
  }
14564
- if (COMMON_IMAGE_EXTENSIONS.has(ext) || VIDEO_EXTENSIONS.has(ext)) {
14856
+ if (isImageFile(filePath) || VIDEO_EXTENSIONS.has(ext)) {
14565
14857
  return "Binary file \u2014 use Read with `file_path`; prefer bounded reads where supported.";
14566
14858
  }
14567
14859
  return "Large text \u2014 use multiple Read calls with optional `offset` and `limit`.";
@@ -14627,14 +14919,22 @@ ${chunk.resource.text}
14627
14919
  break;
14628
14920
  case "image":
14629
14921
  if (chunk.data) {
14630
- content.push({
14631
- type: "image",
14632
- source: {
14633
- type: "base64",
14634
- data: chunk.data,
14635
- media_type: chunk.mimeType
14636
- }
14637
- });
14922
+ if (isClaudeImageMimeType(chunk.mimeType)) {
14923
+ content.push({
14924
+ type: "image",
14925
+ source: {
14926
+ type: "base64",
14927
+ data: chunk.data,
14928
+ media_type: chunk.mimeType
14929
+ }
14930
+ });
14931
+ } else {
14932
+ content.push(
14933
+ sdkText(
14934
+ `[Unsupported image MIME type: ${chunk.mimeType}. Supported: image/jpeg, image/png, image/gif, image/webp.]`
14935
+ )
14936
+ );
14937
+ }
14638
14938
  } else if (chunk.uri?.startsWith("http")) {
14639
14939
  content.push({
14640
14940
  type: "image",
@@ -15103,6 +15403,9 @@ function getConnectedMcpServerNames() {
15103
15403
  }
15104
15404
  return [...names];
15105
15405
  }
15406
+ function getCachedMcpTools() {
15407
+ return [...mcpToolMetadataCache.values()];
15408
+ }
15106
15409
  function getMcpToolApprovalState(toolName) {
15107
15410
  return mcpToolMetadataCache.get(toolName)?.approvalState;
15108
15411
  }
@@ -18044,6 +18347,19 @@ var SettingsManager = class {
18044
18347
  var SESSION_VALIDATION_TIMEOUT_MS = 3e4;
18045
18348
  var MAX_TITLE_LENGTH = 256;
18046
18349
  var LOCAL_ONLY_COMMANDS = /* @__PURE__ */ new Set(["/context", "/heapdump", "/extra-usage"]);
18350
+ function readClaudeMdQuietly(cwd, logger) {
18351
+ try {
18352
+ return fs10.readFileSync(path12.join(cwd, "CLAUDE.md"), "utf-8");
18353
+ } catch (err2) {
18354
+ if (err2?.code !== "ENOENT") {
18355
+ logger.warn("Failed to read CLAUDE.md for context breakdown", {
18356
+ cwd,
18357
+ error: err2 instanceof Error ? err2.message : String(err2)
18358
+ });
18359
+ }
18360
+ return void 0;
18361
+ }
18362
+ }
18047
18363
  function sanitizeTitle(text2) {
18048
18364
  const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
18049
18365
  if (sanitized.length <= MAX_TITLE_LENGTH) {
@@ -18371,6 +18687,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18371
18687
  }
18372
18688
  });
18373
18689
  }
18690
+ const breakdownInputTokens = lastStreamUsage.input_tokens + lastStreamUsage.cache_read_input_tokens + lastStreamUsage.cache_creation_input_tokens;
18374
18691
  await this.client.extNotification(
18375
18692
  POSTHOG_NOTIFICATIONS.USAGE_UPDATE,
18376
18693
  {
@@ -18381,7 +18698,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18381
18698
  cachedReadTokens: message.usage.cache_read_input_tokens,
18382
18699
  cachedWriteTokens: message.usage.cache_creation_input_tokens
18383
18700
  },
18384
- cost: message.total_cost_usd
18701
+ cost: message.total_cost_usd,
18702
+ breakdown: buildBreakdown(
18703
+ this.session.contextBreakdownBaseline ?? emptyBaseline(),
18704
+ breakdownInputTokens
18705
+ )
18385
18706
  }
18386
18707
  );
18387
18708
  const usage = {
@@ -18832,6 +19153,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18832
19153
  pendingMessages: /* @__PURE__ */ new Map(),
18833
19154
  nextPendingOrder: 0,
18834
19155
  emitRawSDKMessages: meta?.claudeCode?.emitRawSDKMessages ?? false,
19156
+ contextBreakdownBaseline: {
19157
+ ...emptyBaseline(),
19158
+ systemPrompt: estimateSystemPrompt(systemPrompt),
19159
+ rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19160
+ },
18835
19161
  // Custom properties
18836
19162
  cwd,
18837
19163
  notificationHistory: [],
@@ -19089,13 +19415,26 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19089
19415
  }
19090
19416
  async sendAvailableCommandsUpdate() {
19091
19417
  const commands = await this.session.query.supportedCommands();
19418
+ const available = getAvailableSlashCommands(commands);
19092
19419
  await this.client.sessionUpdate({
19093
19420
  sessionId: this.sessionId,
19094
19421
  update: {
19095
19422
  sessionUpdate: "available_commands_update",
19096
- availableCommands: getAvailableSlashCommands(commands)
19423
+ availableCommands: available
19097
19424
  }
19098
19425
  });
19426
+ this.updateBreakdownCategory("skills", estimateSkillsTokens(available));
19427
+ }
19428
+ /** Update one category of the context-breakdown baseline so the next
19429
+ * `_posthog/usage_update` carries fresher numbers. No-op when the baseline
19430
+ * hasn't been initialized yet (e.g. in a unit-test session). */
19431
+ updateBreakdownCategory(key, tokens) {
19432
+ if (!this.session?.contextBreakdownBaseline) return;
19433
+ if (this.session.contextBreakdownBaseline[key] === tokens) return;
19434
+ this.session.contextBreakdownBaseline = {
19435
+ ...this.session.contextBreakdownBaseline,
19436
+ [key]: tokens
19437
+ };
19099
19438
  }
19100
19439
  async replaySessionHistory(sessionId) {
19101
19440
  try {
@@ -19144,6 +19483,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19144
19483
  () => this.sendAvailableCommandsUpdate()
19145
19484
  ),
19146
19485
  fetchMcpToolMetadata(q, this.logger).then(() => {
19486
+ this.updateBreakdownCategory(
19487
+ "mcp",
19488
+ estimateMcpTokens(getCachedMcpTools())
19489
+ );
19147
19490
  const serverNames = getConnectedMcpServerNames();
19148
19491
  if (serverNames.length > 0) {
19149
19492
  this.options?.onMcpServersReady?.(serverNames);
@@ -19408,6 +19751,25 @@ function createSessionState(sessionId, cwd, opts) {
19408
19751
  taskId: opts?.taskId
19409
19752
  };
19410
19753
  }
19754
+ function resetSessionState(state, sessionId, cwd, opts) {
19755
+ state.sessionId = sessionId;
19756
+ state.cwd = cwd;
19757
+ state.modeId = opts?.modeId ?? "auto";
19758
+ state.modelId = opts?.modelId;
19759
+ state.configOptions = [];
19760
+ state.accumulatedUsage = {
19761
+ inputTokens: 0,
19762
+ outputTokens: 0,
19763
+ cachedReadTokens: 0,
19764
+ cachedWriteTokens: 0
19765
+ };
19766
+ state.contextSize = void 0;
19767
+ state.contextUsed = void 0;
19768
+ state.contextBreakdownBaseline = void 0;
19769
+ state.permissionMode = opts?.permissionMode ?? "auto";
19770
+ state.taskRunId = opts?.taskRunId;
19771
+ state.taskId = opts?.taskId;
19772
+ }
19411
19773
  function resetUsage(state) {
19412
19774
  state.accumulatedUsage = {
19413
19775
  inputTokens: 0,
@@ -19629,6 +19991,12 @@ function classifyPromptError(error) {
19629
19991
  message
19630
19992
  );
19631
19993
  }
19994
+ var CODEX_BASELINE_TOKENS = 12e3;
19995
+ function buildCodexBaseline(meta) {
19996
+ const baseline = emptyBaseline();
19997
+ baseline.systemPrompt = CODEX_BASELINE_TOKENS + estimateTokens(meta?.systemPrompt);
19998
+ return baseline;
19999
+ }
19632
20000
  var CODEX_NATIVE_MODE = {
19633
20001
  auto: "auto",
19634
20002
  default: "auto",
@@ -19798,7 +20166,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19798
20166
  response.configOptions = normalizeCodexConfigOptions(
19799
20167
  response.configOptions
19800
20168
  );
19801
- this.sessionState = createSessionState(response.sessionId, params.cwd, {
20169
+ resetSessionState(this.sessionState, response.sessionId, params.cwd, {
19802
20170
  taskRunId: meta?.taskRunId,
19803
20171
  taskId: resolveTaskId(meta),
19804
20172
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19807,6 +20175,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19807
20175
  });
19808
20176
  this.sessionId = response.sessionId;
19809
20177
  this.sessionState.configOptions = response.configOptions ?? [];
20178
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19810
20179
  await this.applyInitialPermissionMode(
19811
20180
  response.sessionId,
19812
20181
  meta?.permissionMode,
@@ -19839,7 +20208,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19839
20208
  response.modes?.currentModeId,
19840
20209
  meta?.permissionMode
19841
20210
  );
19842
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20211
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19843
20212
  taskRunId: meta?.taskRunId,
19844
20213
  taskId: resolveTaskId(meta),
19845
20214
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19847,6 +20216,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19847
20216
  });
19848
20217
  this.sessionId = params.sessionId;
19849
20218
  this.sessionState.configOptions = response.configOptions ?? [];
20219
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19850
20220
  if (meta?.taskRunId) {
19851
20221
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19852
20222
  taskRunId: meta.taskRunId,
@@ -19878,7 +20248,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19878
20248
  loadResponse.modes?.currentModeId,
19879
20249
  meta?.permissionMode
19880
20250
  );
19881
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20251
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19882
20252
  taskRunId: meta?.taskRunId,
19883
20253
  taskId: resolveTaskId(meta),
19884
20254
  modeId: loadResponse.modes?.currentModeId ?? "auto",
@@ -19886,6 +20256,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19886
20256
  });
19887
20257
  this.sessionId = params.sessionId;
19888
20258
  this.sessionState.configOptions = loadResponse.configOptions ?? [];
20259
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19889
20260
  if (meta?.taskRunId) {
19890
20261
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19891
20262
  taskRunId: meta.taskRunId,
@@ -19917,7 +20288,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19917
20288
  newResponse.configOptions
19918
20289
  );
19919
20290
  const requestedPermissionMode = toCodexPermissionMode(meta?.permissionMode);
19920
- this.sessionState = createSessionState(newResponse.sessionId, params.cwd, {
20291
+ resetSessionState(this.sessionState, newResponse.sessionId, params.cwd, {
19921
20292
  taskRunId: meta?.taskRunId,
19922
20293
  taskId: resolveTaskId(meta),
19923
20294
  modeId: newResponse.modes?.currentModeId ?? "auto",
@@ -19925,6 +20296,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19925
20296
  });
19926
20297
  this.sessionId = newResponse.sessionId;
19927
20298
  this.sessionState.configOptions = newResponse.configOptions ?? [];
20299
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19928
20300
  await this.applyInitialPermissionMode(
19929
20301
  newResponse.sessionId,
19930
20302
  meta?.permissionMode,
@@ -20061,6 +20433,15 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20061
20433
  });
20062
20434
  }
20063
20435
  }
20436
+ if (this.sessionState.contextUsed !== void 0) {
20437
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.USAGE_UPDATE, {
20438
+ sessionId: params.sessionId,
20439
+ breakdown: buildBreakdown(
20440
+ this.sessionState.contextBreakdownBaseline ?? emptyBaseline(),
20441
+ this.sessionState.contextUsed
20442
+ )
20443
+ });
20444
+ }
20064
20445
  return response;
20065
20446
  }
20066
20447
  async interrupt() {
@@ -20364,7 +20745,7 @@ function createCodexConnection(config) {
20364
20745
  // src/handoff-checkpoint.ts
20365
20746
  import { mkdtemp as mkdtemp2, readFile as readFile4, rm as rm5, writeFile as writeFile2 } from "fs/promises";
20366
20747
  import { tmpdir as tmpdir2 } from "os";
20367
- import { dirname as dirname7, join as join11 } from "path";
20748
+ import { dirname as dirname7, join as join12 } from "path";
20368
20749
 
20369
20750
  // ../git/dist/handoff.js
20370
20751
  import { spawn as spawn5 } from "child_process";
@@ -20377,148 +20758,6 @@ import { randomUUID as randomUUID2 } from "crypto";
20377
20758
  import * as fs12 from "fs/promises";
20378
20759
  import * as path14 from "path";
20379
20760
 
20380
- // ../shared/dist/index.js
20381
- var CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:";
20382
- function deserializeCloudPrompt(value) {
20383
- const trimmed2 = value.trim();
20384
- if (!trimmed2) {
20385
- return [];
20386
- }
20387
- if (!trimmed2.startsWith(CLOUD_PROMPT_PREFIX)) {
20388
- return [{ type: "text", text: trimmed2 }];
20389
- }
20390
- try {
20391
- const parsed = JSON.parse(trimmed2.slice(CLOUD_PROMPT_PREFIX.length));
20392
- if (Array.isArray(parsed.blocks) && parsed.blocks.length > 0) {
20393
- return parsed.blocks;
20394
- }
20395
- } catch {
20396
- }
20397
- return [{ type: "text", text: trimmed2 }];
20398
- }
20399
- function promptBlocksToText(blocks) {
20400
- return blocks.filter((b) => b.type === "text").map((block) => block.text).join("").trim();
20401
- }
20402
- var consoleLogger = {
20403
- info: (_message, _data) => {
20404
- },
20405
- debug: (_message, _data) => {
20406
- },
20407
- error: (_message, _data) => {
20408
- },
20409
- warn: (_message, _data) => {
20410
- }
20411
- };
20412
- var Saga = class {
20413
- completedSteps = [];
20414
- currentStepName = "unknown";
20415
- stepTimings = [];
20416
- log;
20417
- constructor(logger) {
20418
- this.log = logger ?? consoleLogger;
20419
- }
20420
- /**
20421
- * Run the saga with the given input.
20422
- * Returns a discriminated union result - either success with data or failure with error details.
20423
- */
20424
- async run(input) {
20425
- this.completedSteps = [];
20426
- this.currentStepName = "unknown";
20427
- this.stepTimings = [];
20428
- const sagaStart = performance.now();
20429
- try {
20430
- const result = await this.execute(input);
20431
- const totalDuration = performance.now() - sagaStart;
20432
- this.log.debug("Saga completed successfully", {
20433
- sagaName: this.sagaName,
20434
- stepsCompleted: this.completedSteps.length,
20435
- totalDurationMs: Math.round(totalDuration),
20436
- stepTimings: this.stepTimings
20437
- });
20438
- return { success: true, data: result };
20439
- } catch (error) {
20440
- this.log.error("Saga failed, initiating rollback", {
20441
- sagaName: this.sagaName,
20442
- failedStep: this.currentStepName,
20443
- error: error instanceof Error ? error.message : String(error),
20444
- completedStepTimings: this.stepTimings
20445
- });
20446
- await this.rollback();
20447
- return {
20448
- success: false,
20449
- error: error instanceof Error ? error.message : String(error),
20450
- failedStep: this.currentStepName
20451
- };
20452
- }
20453
- }
20454
- /**
20455
- * Execute a step with its rollback action.
20456
- * If the step succeeds, its rollback action is stored for potential rollback.
20457
- * The step name is automatically tracked for error reporting.
20458
- *
20459
- * @param config - Step configuration with name, execute, and rollback functions
20460
- * @returns The result of the execute function
20461
- * @throws Re-throws any error from the execute function (triggers rollback)
20462
- */
20463
- async step(config) {
20464
- this.currentStepName = config.name;
20465
- const stepStart = performance.now();
20466
- const result = await config.execute();
20467
- const durationMs = Math.round(performance.now() - stepStart);
20468
- this.stepTimings.push({ name: config.name, durationMs });
20469
- this.completedSteps.push({
20470
- name: config.name,
20471
- rollback: () => config.rollback(result)
20472
- });
20473
- return result;
20474
- }
20475
- /**
20476
- * Execute a step that doesn't need rollback.
20477
- * Useful for read-only operations or operations that are idempotent.
20478
- * The step name is automatically tracked for error reporting.
20479
- *
20480
- * @param name - Step name for logging and error tracking
20481
- * @param execute - The action to execute
20482
- * @returns The result of the execute function
20483
- */
20484
- async readOnlyStep(name2, execute) {
20485
- this.currentStepName = name2;
20486
- const stepStart = performance.now();
20487
- const result = await execute();
20488
- const durationMs = Math.round(performance.now() - stepStart);
20489
- this.stepTimings.push({ name: name2, durationMs });
20490
- return result;
20491
- }
20492
- /**
20493
- * Roll back all completed steps in reverse order.
20494
- * Rollback errors are logged but don't stop the rollback of other steps.
20495
- */
20496
- async rollback() {
20497
- this.log.info("Rolling back saga", {
20498
- stepsToRollback: this.completedSteps.length
20499
- });
20500
- const stepsReversed = [...this.completedSteps].reverse();
20501
- for (const step of stepsReversed) {
20502
- try {
20503
- await step.rollback();
20504
- } catch (error) {
20505
- this.log.error(`Failed to rollback step: ${step.name}`, {
20506
- error: error instanceof Error ? error.message : String(error)
20507
- });
20508
- }
20509
- }
20510
- this.log.info("Rollback completed", {
20511
- stepsAttempted: this.completedSteps.length
20512
- });
20513
- }
20514
- /**
20515
- * Get the number of completed steps (useful for testing)
20516
- */
20517
- getCompletedStepCount() {
20518
- return this.completedSteps.length;
20519
- }
20520
- };
20521
-
20522
20761
  // ../git/dist/git-saga.js
20523
20762
  var GitSaga = class extends Saga {
20524
20763
  _git = null;
@@ -21326,10 +21565,10 @@ var HandoffCheckpointTracker = class {
21326
21565
  }
21327
21566
  const gitTracker = this.createGitTracker();
21328
21567
  const tmpDir = await mkdtemp2(
21329
- join11(tmpdir2(), `posthog-code-handoff-${checkpoint.checkpointId}-`)
21568
+ join12(tmpdir2(), `posthog-code-handoff-${checkpoint.checkpointId}-`)
21330
21569
  );
21331
- const packPath = join11(tmpDir, `${checkpoint.checkpointId}.pack`);
21332
- const indexPath = join11(tmpDir, `${checkpoint.checkpointId}.index`);
21570
+ const packPath = join12(tmpDir, `${checkpoint.checkpointId}.pack`);
21571
+ const indexPath = join12(tmpDir, `${checkpoint.checkpointId}.index`);
21333
21572
  try {
21334
21573
  const downloads = await this.downloadArtifacts([
21335
21574
  {
@@ -21754,7 +21993,7 @@ import { randomUUID as randomUUID3 } from "crypto";
21754
21993
  import * as fs13 from "fs/promises";
21755
21994
  import * as os8 from "os";
21756
21995
  import * as path16 from "path";
21757
- var CHARS_PER_TOKEN = 4;
21996
+ var CHARS_PER_TOKEN2 = 4;
21758
21997
  var DEFAULT_MAX_TOKENS = 15e4;
21759
21998
  function estimateTurnTokens(turn) {
21760
21999
  let chars = 0;
@@ -21771,7 +22010,7 @@ function estimateTurnTokens(turn) {
21771
22010
  }
21772
22011
  }
21773
22012
  }
21774
- return Math.ceil(chars / CHARS_PER_TOKEN);
22013
+ return Math.ceil(chars / CHARS_PER_TOKEN2);
21775
22014
  }
21776
22015
  function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
21777
22016
  let budget = maxTokens;
@@ -24369,7 +24608,7 @@ Continue from where you left off. The user is waiting for your response.`
24369
24608
  throw new Error(`Failed to download artifact ${artifact.name}`);
24370
24609
  }
24371
24610
  const safeName = this.getSafeArtifactName(artifact.name);
24372
- const artifactDir = join13(
24611
+ const artifactDir = join14(
24373
24612
  this.config.repositoryPath ?? "/tmp/workspace",
24374
24613
  ".posthog",
24375
24614
  "attachments",
@@ -24377,7 +24616,7 @@ Continue from where you left off. The user is waiting for your response.`
24377
24616
  artifact.id ?? safeName
24378
24617
  );
24379
24618
  await mkdir5(artifactDir, { recursive: true });
24380
- const artifactPath = join13(artifactDir, safeName);
24619
+ const artifactPath = join14(artifactDir, safeName);
24381
24620
  await writeFile4(artifactPath, Buffer.from(data));
24382
24621
  return resourceLink(pathToFileURL(artifactPath).toString(), artifact.name, {
24383
24622
  ...artifact.content_type ? { mimeType: artifact.content_type } : {},