@posthog/agent 2.3.675 → 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.
Files changed (33) hide show
  1. package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
  2. package/dist/adapters/claude/mcp/tool-metadata.d.ts +4 -1
  3. package/dist/adapters/claude/mcp/tool-metadata.js +4 -0
  4. package/dist/adapters/claude/mcp/tool-metadata.js.map +1 -1
  5. package/dist/adapters/claude/tools.js.map +1 -1
  6. package/dist/adapters/codex/local-tools-mcp-server.js +1 -1
  7. package/dist/adapters/codex/local-tools-mcp-server.js.map +1 -1
  8. package/dist/agent.js +270 -29
  9. package/dist/agent.js.map +1 -1
  10. package/dist/handoff-checkpoint.js +70 -0
  11. package/dist/handoff-checkpoint.js.map +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/posthog-api.js +1 -1
  14. package/dist/posthog-api.js.map +1 -1
  15. package/dist/resume.js +70 -0
  16. package/dist/resume.js.map +1 -1
  17. package/dist/server/agent-server.js +419 -180
  18. package/dist/server/agent-server.js.map +1 -1
  19. package/dist/server/bin.cjs +412 -173
  20. package/dist/server/bin.cjs.map +1 -1
  21. package/package.json +2 -2
  22. package/src/adapters/claude/claude-agent.ts +62 -1
  23. package/src/adapters/claude/context-breakdown.test.ts +130 -0
  24. package/src/adapters/claude/context-breakdown.ts +127 -0
  25. package/src/adapters/claude/conversion/acp-to-sdk.test.ts +39 -0
  26. package/src/adapters/claude/conversion/acp-to-sdk.ts +20 -24
  27. package/src/adapters/claude/mcp/local-tools.test.ts +1 -1
  28. package/src/adapters/claude/mcp/tool-metadata.ts +6 -0
  29. package/src/adapters/claude/types.ts +3 -0
  30. package/src/adapters/codex/codex-agent.ts +46 -5
  31. package/src/adapters/codex/codex-client.test.ts +49 -1
  32. package/src/adapters/codex/session-state.ts +36 -5
  33. package/src/adapters/local-tools/registry.ts +2 -2
@@ -9471,7 +9471,7 @@ var import_zod4 = require("zod");
9471
9471
  // package.json
9472
9472
  var package_default = {
9473
9473
  name: "@posthog/agent",
9474
- version: "2.3.675",
9474
+ version: "2.3.696",
9475
9475
  repository: "https://github.com/PostHog/code",
9476
9476
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
9477
9477
  exports: {
@@ -14584,7 +14584,7 @@ var BaseAcpAgent = class {
14584
14584
  var import_zod = require("zod");
14585
14585
 
14586
14586
  // src/adapters/local-tools/registry.ts
14587
- var LOCAL_TOOLS_MCP_NAME = "posthog-local";
14587
+ var LOCAL_TOOLS_MCP_NAME = "posthog-code-tools";
14588
14588
  function defineLocalTool(def) {
14589
14589
  return def;
14590
14590
  }
@@ -14668,22 +14668,314 @@ function resolveTaskId(meta) {
14668
14668
  return meta?.taskId ?? meta?.persistence?.taskId;
14669
14669
  }
14670
14670
 
14671
+ // src/adapters/claude/context-breakdown.ts
14672
+ var CLAUDE_PRESET_ESTIMATE_TOKENS = 4e3;
14673
+ var CHARS_PER_TOKEN = 4;
14674
+ function estimateTokens(text2) {
14675
+ if (!text2) return 0;
14676
+ return Math.max(0, Math.round(text2.length / CHARS_PER_TOKEN));
14677
+ }
14678
+ function estimateJsonTokens(value) {
14679
+ try {
14680
+ return estimateTokens(JSON.stringify(value));
14681
+ } catch {
14682
+ return 0;
14683
+ }
14684
+ }
14685
+ function estimateSkillsTokens(commands) {
14686
+ if (!commands.length) return 0;
14687
+ return estimateJsonTokens(
14688
+ commands.map((c) => ({
14689
+ name: c.name,
14690
+ description: c.description,
14691
+ hint: c.input?.hint
14692
+ }))
14693
+ );
14694
+ }
14695
+ function estimateMcpTokens(tools) {
14696
+ if (!tools.length) return 0;
14697
+ return estimateJsonTokens(
14698
+ tools.map((t) => ({ name: t.name, description: t.description }))
14699
+ );
14700
+ }
14701
+ function estimateRulesTokens(rules) {
14702
+ return estimateTokens(rules);
14703
+ }
14704
+ function emptyBaseline() {
14705
+ return {
14706
+ systemPrompt: 0,
14707
+ tools: 0,
14708
+ rules: 0,
14709
+ skills: 0,
14710
+ mcp: 0,
14711
+ subagents: 0
14712
+ };
14713
+ }
14714
+ function estimateSystemPrompt(systemPrompt) {
14715
+ if (!systemPrompt) return CLAUDE_PRESET_ESTIMATE_TOKENS;
14716
+ if (typeof systemPrompt === "string") return estimateTokens(systemPrompt);
14717
+ if (typeof systemPrompt === "object") {
14718
+ const obj = systemPrompt;
14719
+ const appendTokens = typeof obj.append === "string" ? estimateTokens(obj.append) : 0;
14720
+ if (obj.type === "preset") {
14721
+ return CLAUDE_PRESET_ESTIMATE_TOKENS + appendTokens;
14722
+ }
14723
+ return appendTokens;
14724
+ }
14725
+ return 0;
14726
+ }
14727
+ function buildBreakdown(baseline, currentInputTokens) {
14728
+ const stableSum = baseline.systemPrompt + baseline.tools + baseline.rules + baseline.skills + baseline.mcp + baseline.subagents;
14729
+ const conversation = Math.max(0, currentInputTokens - stableSum);
14730
+ return {
14731
+ systemPrompt: baseline.systemPrompt,
14732
+ tools: baseline.tools,
14733
+ rules: baseline.rules,
14734
+ skills: baseline.skills,
14735
+ mcp: baseline.mcp,
14736
+ subagents: baseline.subagents,
14737
+ conversation
14738
+ };
14739
+ }
14740
+
14671
14741
  // src/adapters/claude/conversion/acp-to-sdk.ts
14672
14742
  var path6 = __toESM(require("path"), 1);
14673
14743
  var import_node_url = require("url");
14674
- var PDF_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
14675
- var COMMON_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
14676
- "png",
14677
- "jpg",
14678
- "jpeg",
14679
- "gif",
14680
- "webp",
14681
- "bmp",
14682
- "svg",
14683
- "heic",
14684
- "tif",
14685
- "tiff"
14744
+
14745
+ // ../shared/dist/index.js
14746
+ var IMAGE_MIME_TYPES = {
14747
+ png: "image/png",
14748
+ jpg: "image/jpeg",
14749
+ jpeg: "image/jpeg",
14750
+ gif: "image/gif",
14751
+ webp: "image/webp",
14752
+ bmp: "image/bmp",
14753
+ ico: "image/x-icon",
14754
+ tiff: "image/tiff",
14755
+ tif: "image/tiff",
14756
+ svg: "image/svg+xml",
14757
+ heic: "image/heic",
14758
+ heif: "image/heif",
14759
+ avif: "image/avif"
14760
+ };
14761
+ var IMAGE_EXTENSIONS = new Set(
14762
+ Object.keys(IMAGE_MIME_TYPES)
14763
+ );
14764
+ var CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
14765
+ "image/jpeg",
14766
+ "image/png",
14767
+ "image/gif",
14768
+ "image/webp"
14769
+ ]);
14770
+ function isClaudeImageMimeType(mimeType) {
14771
+ return CLAUDE_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());
14772
+ }
14773
+ var MAX_DATA_URL_LENGTH = 20 * 1024 * 1024;
14774
+ var MAX_IMAGE_BASE64_LENGTH = 15 * 1024 * 1024;
14775
+ function extensionOf(filename) {
14776
+ const slash = Math.max(filename.lastIndexOf("/"), filename.lastIndexOf("\\"));
14777
+ const basename4 = slash >= 0 ? filename.slice(slash + 1) : filename;
14778
+ const cleanBasename = basename4.split(/[?#]/)[0];
14779
+ const dot = cleanBasename.lastIndexOf(".");
14780
+ return dot > 0 ? cleanBasename.slice(dot + 1).toLowerCase() : "";
14781
+ }
14782
+ function isImageFile(filename) {
14783
+ const ext = extensionOf(filename);
14784
+ return ext.length > 0 && IMAGE_EXTENSIONS.has(ext);
14785
+ }
14786
+ var AUDIO_VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
14787
+ "mp3",
14788
+ "mp4",
14789
+ "wav",
14790
+ "avi",
14791
+ "mov",
14792
+ "mkv",
14793
+ "webm",
14794
+ "mpg",
14795
+ "mpeg",
14796
+ "flac",
14797
+ "ogg",
14798
+ "m4a",
14799
+ "aac"
14686
14800
  ]);
14801
+ var ARCHIVE_EXTENSIONS = /* @__PURE__ */ new Set([
14802
+ "zip",
14803
+ "tar",
14804
+ "gz",
14805
+ "tgz",
14806
+ "bz2",
14807
+ "xz",
14808
+ "rar",
14809
+ "7z"
14810
+ ]);
14811
+ var EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
14812
+ "exe",
14813
+ "dll",
14814
+ "so",
14815
+ "dylib",
14816
+ "wasm",
14817
+ "bin",
14818
+ "o"
14819
+ ]);
14820
+ var FONT_EXTENSIONS = /* @__PURE__ */ new Set([
14821
+ "ttf",
14822
+ "otf",
14823
+ "woff",
14824
+ "woff2",
14825
+ "eot"
14826
+ ]);
14827
+ var DOCUMENT_BINARY_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
14828
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
14829
+ ...Object.keys(IMAGE_MIME_TYPES).filter((ext) => ext !== "svg"),
14830
+ ...AUDIO_VIDEO_EXTENSIONS,
14831
+ ...ARCHIVE_EXTENSIONS,
14832
+ ...EXECUTABLE_EXTENSIONS,
14833
+ ...FONT_EXTENSIONS,
14834
+ ...DOCUMENT_BINARY_EXTENSIONS
14835
+ ]);
14836
+ var CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:";
14837
+ function deserializeCloudPrompt(value) {
14838
+ const trimmed2 = value.trim();
14839
+ if (!trimmed2) {
14840
+ return [];
14841
+ }
14842
+ if (!trimmed2.startsWith(CLOUD_PROMPT_PREFIX)) {
14843
+ return [{ type: "text", text: trimmed2 }];
14844
+ }
14845
+ try {
14846
+ const parsed = JSON.parse(trimmed2.slice(CLOUD_PROMPT_PREFIX.length));
14847
+ if (Array.isArray(parsed.blocks) && parsed.blocks.length > 0) {
14848
+ return parsed.blocks;
14849
+ }
14850
+ } catch {
14851
+ }
14852
+ return [{ type: "text", text: trimmed2 }];
14853
+ }
14854
+ function promptBlocksToText(blocks) {
14855
+ return blocks.filter((b) => b.type === "text").map((block) => block.text).join("").trim();
14856
+ }
14857
+ var consoleLogger = {
14858
+ info: (_message, _data) => {
14859
+ },
14860
+ debug: (_message, _data) => {
14861
+ },
14862
+ error: (_message, _data) => {
14863
+ },
14864
+ warn: (_message, _data) => {
14865
+ }
14866
+ };
14867
+ var Saga = class {
14868
+ completedSteps = [];
14869
+ currentStepName = "unknown";
14870
+ stepTimings = [];
14871
+ log;
14872
+ constructor(logger) {
14873
+ this.log = logger ?? consoleLogger;
14874
+ }
14875
+ /**
14876
+ * Run the saga with the given input.
14877
+ * Returns a discriminated union result - either success with data or failure with error details.
14878
+ */
14879
+ async run(input) {
14880
+ this.completedSteps = [];
14881
+ this.currentStepName = "unknown";
14882
+ this.stepTimings = [];
14883
+ const sagaStart = performance.now();
14884
+ try {
14885
+ const result = await this.execute(input);
14886
+ const totalDuration = performance.now() - sagaStart;
14887
+ this.log.debug("Saga completed successfully", {
14888
+ sagaName: this.sagaName,
14889
+ stepsCompleted: this.completedSteps.length,
14890
+ totalDurationMs: Math.round(totalDuration),
14891
+ stepTimings: this.stepTimings
14892
+ });
14893
+ return { success: true, data: result };
14894
+ } catch (error) {
14895
+ this.log.error("Saga failed, initiating rollback", {
14896
+ sagaName: this.sagaName,
14897
+ failedStep: this.currentStepName,
14898
+ error: error instanceof Error ? error.message : String(error),
14899
+ completedStepTimings: this.stepTimings
14900
+ });
14901
+ await this.rollback();
14902
+ return {
14903
+ success: false,
14904
+ error: error instanceof Error ? error.message : String(error),
14905
+ failedStep: this.currentStepName
14906
+ };
14907
+ }
14908
+ }
14909
+ /**
14910
+ * Execute a step with its rollback action.
14911
+ * If the step succeeds, its rollback action is stored for potential rollback.
14912
+ * The step name is automatically tracked for error reporting.
14913
+ *
14914
+ * @param config - Step configuration with name, execute, and rollback functions
14915
+ * @returns The result of the execute function
14916
+ * @throws Re-throws any error from the execute function (triggers rollback)
14917
+ */
14918
+ async step(config) {
14919
+ this.currentStepName = config.name;
14920
+ const stepStart = performance.now();
14921
+ const result = await config.execute();
14922
+ const durationMs = Math.round(performance.now() - stepStart);
14923
+ this.stepTimings.push({ name: config.name, durationMs });
14924
+ this.completedSteps.push({
14925
+ name: config.name,
14926
+ rollback: () => config.rollback(result)
14927
+ });
14928
+ return result;
14929
+ }
14930
+ /**
14931
+ * Execute a step that doesn't need rollback.
14932
+ * Useful for read-only operations or operations that are idempotent.
14933
+ * The step name is automatically tracked for error reporting.
14934
+ *
14935
+ * @param name - Step name for logging and error tracking
14936
+ * @param execute - The action to execute
14937
+ * @returns The result of the execute function
14938
+ */
14939
+ async readOnlyStep(name2, execute) {
14940
+ this.currentStepName = name2;
14941
+ const stepStart = performance.now();
14942
+ const result = await execute();
14943
+ const durationMs = Math.round(performance.now() - stepStart);
14944
+ this.stepTimings.push({ name: name2, durationMs });
14945
+ return result;
14946
+ }
14947
+ /**
14948
+ * Roll back all completed steps in reverse order.
14949
+ * Rollback errors are logged but don't stop the rollback of other steps.
14950
+ */
14951
+ async rollback() {
14952
+ this.log.info("Rolling back saga", {
14953
+ stepsToRollback: this.completedSteps.length
14954
+ });
14955
+ const stepsReversed = [...this.completedSteps].reverse();
14956
+ for (const step of stepsReversed) {
14957
+ try {
14958
+ await step.rollback();
14959
+ } catch (error) {
14960
+ this.log.error(`Failed to rollback step: ${step.name}`, {
14961
+ error: error instanceof Error ? error.message : String(error)
14962
+ });
14963
+ }
14964
+ }
14965
+ this.log.info("Rollback completed", {
14966
+ stepsAttempted: this.completedSteps.length
14967
+ });
14968
+ }
14969
+ /**
14970
+ * Get the number of completed steps (useful for testing)
14971
+ */
14972
+ getCompletedStepCount() {
14973
+ return this.completedSteps.length;
14974
+ }
14975
+ };
14976
+
14977
+ // src/adapters/claude/conversion/acp-to-sdk.ts
14978
+ var PDF_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
14687
14979
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
14688
14980
  "mp4",
14689
14981
  "mov",
@@ -14712,7 +15004,7 @@ function readToolGuidanceForPath(filePath) {
14712
15004
  if (PDF_EXTENSIONS.has(ext)) {
14713
15005
  return 'Optional `pages` string (e.g. "1-5") per Read call instead of loading the entire PDF.';
14714
15006
  }
14715
- if (COMMON_IMAGE_EXTENSIONS.has(ext) || VIDEO_EXTENSIONS.has(ext)) {
15007
+ if (isImageFile(filePath) || VIDEO_EXTENSIONS.has(ext)) {
14716
15008
  return "Binary file \u2014 use Read with `file_path`; prefer bounded reads where supported.";
14717
15009
  }
14718
15010
  return "Large text \u2014 use multiple Read calls with optional `offset` and `limit`.";
@@ -14778,14 +15070,22 @@ ${chunk.resource.text}
14778
15070
  break;
14779
15071
  case "image":
14780
15072
  if (chunk.data) {
14781
- content.push({
14782
- type: "image",
14783
- source: {
14784
- type: "base64",
14785
- data: chunk.data,
14786
- media_type: chunk.mimeType
14787
- }
14788
- });
15073
+ if (isClaudeImageMimeType(chunk.mimeType)) {
15074
+ content.push({
15075
+ type: "image",
15076
+ source: {
15077
+ type: "base64",
15078
+ data: chunk.data,
15079
+ media_type: chunk.mimeType
15080
+ }
15081
+ });
15082
+ } else {
15083
+ content.push(
15084
+ sdkText(
15085
+ `[Unsupported image MIME type: ${chunk.mimeType}. Supported: image/jpeg, image/png, image/gif, image/webp.]`
15086
+ )
15087
+ );
15088
+ }
14789
15089
  } else if (chunk.uri?.startsWith("http")) {
14790
15090
  content.push({
14791
15091
  type: "image",
@@ -15254,6 +15554,9 @@ function getConnectedMcpServerNames() {
15254
15554
  }
15255
15555
  return [...names];
15256
15556
  }
15557
+ function getCachedMcpTools() {
15558
+ return [...mcpToolMetadataCache.values()];
15559
+ }
15257
15560
  function getMcpToolApprovalState(toolName) {
15258
15561
  return mcpToolMetadataCache.get(toolName)?.approvalState;
15259
15562
  }
@@ -18085,6 +18388,19 @@ var SettingsManager = class {
18085
18388
  var SESSION_VALIDATION_TIMEOUT_MS = 3e4;
18086
18389
  var MAX_TITLE_LENGTH = 256;
18087
18390
  var LOCAL_ONLY_COMMANDS = /* @__PURE__ */ new Set(["/context", "/heapdump", "/extra-usage"]);
18391
+ function readClaudeMdQuietly(cwd, logger) {
18392
+ try {
18393
+ return fs10.readFileSync(path12.join(cwd, "CLAUDE.md"), "utf-8");
18394
+ } catch (err2) {
18395
+ if (err2?.code !== "ENOENT") {
18396
+ logger.warn("Failed to read CLAUDE.md for context breakdown", {
18397
+ cwd,
18398
+ error: err2 instanceof Error ? err2.message : String(err2)
18399
+ });
18400
+ }
18401
+ return void 0;
18402
+ }
18403
+ }
18088
18404
  function sanitizeTitle(text2) {
18089
18405
  const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
18090
18406
  if (sanitized.length <= MAX_TITLE_LENGTH) {
@@ -18412,6 +18728,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18412
18728
  }
18413
18729
  });
18414
18730
  }
18731
+ const breakdownInputTokens = lastStreamUsage.input_tokens + lastStreamUsage.cache_read_input_tokens + lastStreamUsage.cache_creation_input_tokens;
18415
18732
  await this.client.extNotification(
18416
18733
  POSTHOG_NOTIFICATIONS.USAGE_UPDATE,
18417
18734
  {
@@ -18422,7 +18739,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18422
18739
  cachedReadTokens: message.usage.cache_read_input_tokens,
18423
18740
  cachedWriteTokens: message.usage.cache_creation_input_tokens
18424
18741
  },
18425
- cost: message.total_cost_usd
18742
+ cost: message.total_cost_usd,
18743
+ breakdown: buildBreakdown(
18744
+ this.session.contextBreakdownBaseline ?? emptyBaseline(),
18745
+ breakdownInputTokens
18746
+ )
18426
18747
  }
18427
18748
  );
18428
18749
  const usage = {
@@ -18873,6 +19194,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18873
19194
  pendingMessages: /* @__PURE__ */ new Map(),
18874
19195
  nextPendingOrder: 0,
18875
19196
  emitRawSDKMessages: meta?.claudeCode?.emitRawSDKMessages ?? false,
19197
+ contextBreakdownBaseline: {
19198
+ ...emptyBaseline(),
19199
+ systemPrompt: estimateSystemPrompt(systemPrompt),
19200
+ rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19201
+ },
18876
19202
  // Custom properties
18877
19203
  cwd,
18878
19204
  notificationHistory: [],
@@ -19130,13 +19456,26 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19130
19456
  }
19131
19457
  async sendAvailableCommandsUpdate() {
19132
19458
  const commands = await this.session.query.supportedCommands();
19459
+ const available = getAvailableSlashCommands(commands);
19133
19460
  await this.client.sessionUpdate({
19134
19461
  sessionId: this.sessionId,
19135
19462
  update: {
19136
19463
  sessionUpdate: "available_commands_update",
19137
- availableCommands: getAvailableSlashCommands(commands)
19464
+ availableCommands: available
19138
19465
  }
19139
19466
  });
19467
+ this.updateBreakdownCategory("skills", estimateSkillsTokens(available));
19468
+ }
19469
+ /** Update one category of the context-breakdown baseline so the next
19470
+ * `_posthog/usage_update` carries fresher numbers. No-op when the baseline
19471
+ * hasn't been initialized yet (e.g. in a unit-test session). */
19472
+ updateBreakdownCategory(key, tokens) {
19473
+ if (!this.session?.contextBreakdownBaseline) return;
19474
+ if (this.session.contextBreakdownBaseline[key] === tokens) return;
19475
+ this.session.contextBreakdownBaseline = {
19476
+ ...this.session.contextBreakdownBaseline,
19477
+ [key]: tokens
19478
+ };
19140
19479
  }
19141
19480
  async replaySessionHistory(sessionId) {
19142
19481
  try {
@@ -19185,6 +19524,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19185
19524
  () => this.sendAvailableCommandsUpdate()
19186
19525
  ),
19187
19526
  fetchMcpToolMetadata(q, this.logger).then(() => {
19527
+ this.updateBreakdownCategory(
19528
+ "mcp",
19529
+ estimateMcpTokens(getCachedMcpTools())
19530
+ );
19188
19531
  const serverNames = getConnectedMcpServerNames();
19189
19532
  if (serverNames.length > 0) {
19190
19533
  this.options?.onMcpServersReady?.(serverNames);
@@ -19411,6 +19754,25 @@ function createSessionState(sessionId, cwd, opts) {
19411
19754
  taskId: opts?.taskId
19412
19755
  };
19413
19756
  }
19757
+ function resetSessionState(state, sessionId, cwd, opts) {
19758
+ state.sessionId = sessionId;
19759
+ state.cwd = cwd;
19760
+ state.modeId = opts?.modeId ?? "auto";
19761
+ state.modelId = opts?.modelId;
19762
+ state.configOptions = [];
19763
+ state.accumulatedUsage = {
19764
+ inputTokens: 0,
19765
+ outputTokens: 0,
19766
+ cachedReadTokens: 0,
19767
+ cachedWriteTokens: 0
19768
+ };
19769
+ state.contextSize = void 0;
19770
+ state.contextUsed = void 0;
19771
+ state.contextBreakdownBaseline = void 0;
19772
+ state.permissionMode = opts?.permissionMode ?? "auto";
19773
+ state.taskRunId = opts?.taskRunId;
19774
+ state.taskId = opts?.taskId;
19775
+ }
19414
19776
  function resetUsage(state) {
19415
19777
  state.accumulatedUsage = {
19416
19778
  inputTokens: 0,
@@ -19633,6 +19995,12 @@ function classifyPromptError(error) {
19633
19995
  message
19634
19996
  );
19635
19997
  }
19998
+ var CODEX_BASELINE_TOKENS = 12e3;
19999
+ function buildCodexBaseline(meta) {
20000
+ const baseline = emptyBaseline();
20001
+ baseline.systemPrompt = CODEX_BASELINE_TOKENS + estimateTokens(meta?.systemPrompt);
20002
+ return baseline;
20003
+ }
19636
20004
  var CODEX_NATIVE_MODE = {
19637
20005
  auto: "auto",
19638
20006
  default: "auto",
@@ -19802,7 +20170,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19802
20170
  response.configOptions = normalizeCodexConfigOptions(
19803
20171
  response.configOptions
19804
20172
  );
19805
- this.sessionState = createSessionState(response.sessionId, params.cwd, {
20173
+ resetSessionState(this.sessionState, response.sessionId, params.cwd, {
19806
20174
  taskRunId: meta?.taskRunId,
19807
20175
  taskId: resolveTaskId(meta),
19808
20176
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19811,6 +20179,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19811
20179
  });
19812
20180
  this.sessionId = response.sessionId;
19813
20181
  this.sessionState.configOptions = response.configOptions ?? [];
20182
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19814
20183
  await this.applyInitialPermissionMode(
19815
20184
  response.sessionId,
19816
20185
  meta?.permissionMode,
@@ -19843,7 +20212,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19843
20212
  response.modes?.currentModeId,
19844
20213
  meta?.permissionMode
19845
20214
  );
19846
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20215
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19847
20216
  taskRunId: meta?.taskRunId,
19848
20217
  taskId: resolveTaskId(meta),
19849
20218
  modeId: response.modes?.currentModeId ?? "auto",
@@ -19851,6 +20220,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19851
20220
  });
19852
20221
  this.sessionId = params.sessionId;
19853
20222
  this.sessionState.configOptions = response.configOptions ?? [];
20223
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19854
20224
  if (meta?.taskRunId) {
19855
20225
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19856
20226
  taskRunId: meta.taskRunId,
@@ -19882,7 +20252,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19882
20252
  loadResponse.modes?.currentModeId,
19883
20253
  meta?.permissionMode
19884
20254
  );
19885
- this.sessionState = createSessionState(params.sessionId, params.cwd, {
20255
+ resetSessionState(this.sessionState, params.sessionId, params.cwd, {
19886
20256
  taskRunId: meta?.taskRunId,
19887
20257
  taskId: resolveTaskId(meta),
19888
20258
  modeId: loadResponse.modes?.currentModeId ?? "auto",
@@ -19890,6 +20260,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19890
20260
  });
19891
20261
  this.sessionId = params.sessionId;
19892
20262
  this.sessionState.configOptions = loadResponse.configOptions ?? [];
20263
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19893
20264
  if (meta?.taskRunId) {
19894
20265
  await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
19895
20266
  taskRunId: meta.taskRunId,
@@ -19921,7 +20292,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19921
20292
  newResponse.configOptions
19922
20293
  );
19923
20294
  const requestedPermissionMode = toCodexPermissionMode(meta?.permissionMode);
19924
- this.sessionState = createSessionState(newResponse.sessionId, params.cwd, {
20295
+ resetSessionState(this.sessionState, newResponse.sessionId, params.cwd, {
19925
20296
  taskRunId: meta?.taskRunId,
19926
20297
  taskId: resolveTaskId(meta),
19927
20298
  modeId: newResponse.modes?.currentModeId ?? "auto",
@@ -19929,6 +20300,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
19929
20300
  });
19930
20301
  this.sessionId = newResponse.sessionId;
19931
20302
  this.sessionState.configOptions = newResponse.configOptions ?? [];
20303
+ this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
19932
20304
  await this.applyInitialPermissionMode(
19933
20305
  newResponse.sessionId,
19934
20306
  meta?.permissionMode,
@@ -20065,6 +20437,15 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20065
20437
  });
20066
20438
  }
20067
20439
  }
20440
+ if (this.sessionState.contextUsed !== void 0) {
20441
+ await this.client.extNotification(POSTHOG_NOTIFICATIONS.USAGE_UPDATE, {
20442
+ sessionId: params.sessionId,
20443
+ breakdown: buildBreakdown(
20444
+ this.sessionState.contextBreakdownBaseline ?? emptyBaseline(),
20445
+ this.sessionState.contextUsed
20446
+ )
20447
+ });
20448
+ }
20068
20449
  return response;
20069
20450
  }
20070
20451
  async interrupt() {
@@ -20381,148 +20762,6 @@ var import_node_crypto2 = require("crypto");
20381
20762
  var fs12 = __toESM(require("fs/promises"), 1);
20382
20763
  var path14 = __toESM(require("path"), 1);
20383
20764
 
20384
- // ../shared/dist/index.js
20385
- var CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:";
20386
- function deserializeCloudPrompt(value) {
20387
- const trimmed2 = value.trim();
20388
- if (!trimmed2) {
20389
- return [];
20390
- }
20391
- if (!trimmed2.startsWith(CLOUD_PROMPT_PREFIX)) {
20392
- return [{ type: "text", text: trimmed2 }];
20393
- }
20394
- try {
20395
- const parsed = JSON.parse(trimmed2.slice(CLOUD_PROMPT_PREFIX.length));
20396
- if (Array.isArray(parsed.blocks) && parsed.blocks.length > 0) {
20397
- return parsed.blocks;
20398
- }
20399
- } catch {
20400
- }
20401
- return [{ type: "text", text: trimmed2 }];
20402
- }
20403
- function promptBlocksToText(blocks) {
20404
- return blocks.filter((b) => b.type === "text").map((block) => block.text).join("").trim();
20405
- }
20406
- var consoleLogger = {
20407
- info: (_message, _data) => {
20408
- },
20409
- debug: (_message, _data) => {
20410
- },
20411
- error: (_message, _data) => {
20412
- },
20413
- warn: (_message, _data) => {
20414
- }
20415
- };
20416
- var Saga = class {
20417
- completedSteps = [];
20418
- currentStepName = "unknown";
20419
- stepTimings = [];
20420
- log;
20421
- constructor(logger) {
20422
- this.log = logger ?? consoleLogger;
20423
- }
20424
- /**
20425
- * Run the saga with the given input.
20426
- * Returns a discriminated union result - either success with data or failure with error details.
20427
- */
20428
- async run(input) {
20429
- this.completedSteps = [];
20430
- this.currentStepName = "unknown";
20431
- this.stepTimings = [];
20432
- const sagaStart = performance.now();
20433
- try {
20434
- const result = await this.execute(input);
20435
- const totalDuration = performance.now() - sagaStart;
20436
- this.log.debug("Saga completed successfully", {
20437
- sagaName: this.sagaName,
20438
- stepsCompleted: this.completedSteps.length,
20439
- totalDurationMs: Math.round(totalDuration),
20440
- stepTimings: this.stepTimings
20441
- });
20442
- return { success: true, data: result };
20443
- } catch (error) {
20444
- this.log.error("Saga failed, initiating rollback", {
20445
- sagaName: this.sagaName,
20446
- failedStep: this.currentStepName,
20447
- error: error instanceof Error ? error.message : String(error),
20448
- completedStepTimings: this.stepTimings
20449
- });
20450
- await this.rollback();
20451
- return {
20452
- success: false,
20453
- error: error instanceof Error ? error.message : String(error),
20454
- failedStep: this.currentStepName
20455
- };
20456
- }
20457
- }
20458
- /**
20459
- * Execute a step with its rollback action.
20460
- * If the step succeeds, its rollback action is stored for potential rollback.
20461
- * The step name is automatically tracked for error reporting.
20462
- *
20463
- * @param config - Step configuration with name, execute, and rollback functions
20464
- * @returns The result of the execute function
20465
- * @throws Re-throws any error from the execute function (triggers rollback)
20466
- */
20467
- async step(config) {
20468
- this.currentStepName = config.name;
20469
- const stepStart = performance.now();
20470
- const result = await config.execute();
20471
- const durationMs = Math.round(performance.now() - stepStart);
20472
- this.stepTimings.push({ name: config.name, durationMs });
20473
- this.completedSteps.push({
20474
- name: config.name,
20475
- rollback: () => config.rollback(result)
20476
- });
20477
- return result;
20478
- }
20479
- /**
20480
- * Execute a step that doesn't need rollback.
20481
- * Useful for read-only operations or operations that are idempotent.
20482
- * The step name is automatically tracked for error reporting.
20483
- *
20484
- * @param name - Step name for logging and error tracking
20485
- * @param execute - The action to execute
20486
- * @returns The result of the execute function
20487
- */
20488
- async readOnlyStep(name2, execute) {
20489
- this.currentStepName = name2;
20490
- const stepStart = performance.now();
20491
- const result = await execute();
20492
- const durationMs = Math.round(performance.now() - stepStart);
20493
- this.stepTimings.push({ name: name2, durationMs });
20494
- return result;
20495
- }
20496
- /**
20497
- * Roll back all completed steps in reverse order.
20498
- * Rollback errors are logged but don't stop the rollback of other steps.
20499
- */
20500
- async rollback() {
20501
- this.log.info("Rolling back saga", {
20502
- stepsToRollback: this.completedSteps.length
20503
- });
20504
- const stepsReversed = [...this.completedSteps].reverse();
20505
- for (const step of stepsReversed) {
20506
- try {
20507
- await step.rollback();
20508
- } catch (error) {
20509
- this.log.error(`Failed to rollback step: ${step.name}`, {
20510
- error: error instanceof Error ? error.message : String(error)
20511
- });
20512
- }
20513
- }
20514
- this.log.info("Rollback completed", {
20515
- stepsAttempted: this.completedSteps.length
20516
- });
20517
- }
20518
- /**
20519
- * Get the number of completed steps (useful for testing)
20520
- */
20521
- getCompletedStepCount() {
20522
- return this.completedSteps.length;
20523
- }
20524
- };
20525
-
20526
20765
  // ../git/dist/git-saga.js
20527
20766
  var GitSaga = class extends Saga {
20528
20767
  _git = null;
@@ -21758,7 +21997,7 @@ var import_node_crypto3 = require("crypto");
21758
21997
  var fs13 = __toESM(require("fs/promises"), 1);
21759
21998
  var os8 = __toESM(require("os"), 1);
21760
21999
  var path16 = __toESM(require("path"), 1);
21761
- var CHARS_PER_TOKEN = 4;
22000
+ var CHARS_PER_TOKEN2 = 4;
21762
22001
  var DEFAULT_MAX_TOKENS = 15e4;
21763
22002
  function estimateTurnTokens(turn) {
21764
22003
  let chars = 0;
@@ -21775,7 +22014,7 @@ function estimateTurnTokens(turn) {
21775
22014
  }
21776
22015
  }
21777
22016
  }
21778
- return Math.ceil(chars / CHARS_PER_TOKEN);
22017
+ return Math.ceil(chars / CHARS_PER_TOKEN2);
21779
22018
  }
21780
22019
  function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
21781
22020
  let budget = maxTokens;