@posthog/agent 2.1.150 → 2.1.156

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.
@@ -904,7 +904,7 @@ var import_hono = require("hono");
904
904
  // package.json
905
905
  var package_default = {
906
906
  name: "@posthog/agent",
907
- version: "2.1.150",
907
+ version: "2.1.156",
908
908
  repository: "https://github.com/PostHog/twig",
909
909
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
910
910
  exports: {
@@ -1810,8 +1810,14 @@ function toolInfoFromToolUse(toolUse, options) {
1810
1810
  };
1811
1811
  case "Edit": {
1812
1812
  const path9 = input?.file_path ? String(input.file_path) : void 0;
1813
- const oldText = input?.old_string ? String(input.old_string) : null;
1814
- const newText = input?.new_string ? String(input.new_string) : "";
1813
+ let oldText = input?.old_string ? String(input.old_string) : null;
1814
+ let newText = input?.new_string ? String(input.new_string) : "";
1815
+ if (path9 && options?.cachedFileContent && path9 in options.cachedFileContent) {
1816
+ const oldContent = options.cachedFileContent[path9];
1817
+ const newContent = input?.replace_all ? oldContent.replaceAll(oldText ?? "", newText) : oldContent.replace(oldText ?? "", newText);
1818
+ oldText = oldContent;
1819
+ newText = newContent;
1820
+ }
1815
1821
  return {
1816
1822
  title: path9 ? `Edit \`${path9}\`` : "Edit",
1817
1823
  kind: "edit",
@@ -1831,7 +1837,8 @@ function toolInfoFromToolUse(toolUse, options) {
1831
1837
  const filePath = input?.file_path ? String(input.file_path) : void 0;
1832
1838
  const contentStr = input?.content ? String(input.content) : void 0;
1833
1839
  if (filePath) {
1834
- contentResult = toolContent().diff(filePath, null, contentStr ?? "").build();
1840
+ const oldContent = options?.cachedFileContent && filePath in options.cachedFileContent ? options.cachedFileContent[filePath] : null;
1841
+ contentResult = toolContent().diff(filePath, oldContent, contentStr ?? "").build();
1835
1842
  } else if (contentStr) {
1836
1843
  contentResult = toolContent().text(contentStr).build();
1837
1844
  }
@@ -2325,7 +2332,8 @@ function handleToolUseChunk(chunk, ctx) {
2325
2332
  }
2326
2333
  const toolInfo = toolInfoFromToolUse(chunk, {
2327
2334
  supportsTerminalOutput: ctx.supportsTerminalOutput,
2328
- toolUseId: chunk.id
2335
+ toolUseId: chunk.id,
2336
+ cachedFileContent: ctx.fileContentCache
2329
2337
  });
2330
2338
  const meta = {
2331
2339
  ...toolMeta(chunk.name, void 0, ctx.parentToolCallId)
@@ -2351,6 +2359,47 @@ function handleToolUseChunk(chunk, ctx) {
2351
2359
  ...toolInfo
2352
2360
  };
2353
2361
  }
2362
+ function extractTextFromContent(content) {
2363
+ if (Array.isArray(content)) {
2364
+ const parts = [];
2365
+ for (const item of content) {
2366
+ if (typeof item === "object" && item !== null && "text" in item && typeof item.text === "string") {
2367
+ parts.push(item.text);
2368
+ }
2369
+ }
2370
+ return parts.length > 0 ? parts.join("") : null;
2371
+ }
2372
+ if (typeof content === "string") {
2373
+ return content;
2374
+ }
2375
+ return null;
2376
+ }
2377
+ function stripCatLineNumbers(text2) {
2378
+ return text2.replace(/^ *\d+[\t→]/gm, "");
2379
+ }
2380
+ function updateFileContentCache(toolUse, chunk, ctx) {
2381
+ const input = toolUse.input;
2382
+ const filePath = input?.file_path ? String(input.file_path) : void 0;
2383
+ if (!filePath) return;
2384
+ if (toolUse.name === "Read" && !input?.limit && !input?.offset) {
2385
+ const fileText = extractTextFromContent(chunk.content);
2386
+ if (fileText !== null) {
2387
+ ctx.fileContentCache[filePath] = stripCatLineNumbers(fileText);
2388
+ }
2389
+ } else if (toolUse.name === "Write") {
2390
+ const content = input?.content;
2391
+ if (typeof content === "string") {
2392
+ ctx.fileContentCache[filePath] = content;
2393
+ }
2394
+ } else if (toolUse.name === "Edit") {
2395
+ const oldString = input?.old_string;
2396
+ const newString = input?.new_string;
2397
+ if (typeof oldString === "string" && typeof newString === "string" && filePath in ctx.fileContentCache) {
2398
+ const current = ctx.fileContentCache[filePath];
2399
+ ctx.fileContentCache[filePath] = input?.replace_all ? current.replaceAll(oldString, newString) : current.replace(oldString, newString);
2400
+ }
2401
+ }
2402
+ }
2354
2403
  function handleToolResultChunk(chunk, ctx) {
2355
2404
  const toolUse = ctx.toolUseCache[chunk.tool_use_id];
2356
2405
  if (!toolUse) {
@@ -2362,12 +2411,16 @@ function handleToolResultChunk(chunk, ctx) {
2362
2411
  if (toolUse.name === "TodoWrite") {
2363
2412
  return [];
2364
2413
  }
2414
+ if (!chunk.is_error) {
2415
+ updateFileContentCache(toolUse, chunk, ctx);
2416
+ }
2365
2417
  const { _meta: resultMeta, ...toolUpdate } = toolUpdateFromToolResult(
2366
2418
  chunk,
2367
2419
  toolUse,
2368
2420
  {
2369
2421
  supportsTerminalOutput: ctx.supportsTerminalOutput,
2370
- toolUseId: chunk.tool_use_id
2422
+ toolUseId: chunk.tool_use_id,
2423
+ cachedFileContent: ctx.fileContentCache
2371
2424
  }
2372
2425
  );
2373
2426
  const updates = [];
@@ -10894,6 +10947,17 @@ function validateJwt(token, publicKey) {
10894
10947
 
10895
10948
  // src/server/schemas.ts
10896
10949
  var import_zod3 = require("zod");
10950
+ var httpHeaderSchema = import_zod3.z.object({
10951
+ name: import_zod3.z.string(),
10952
+ value: import_zod3.z.string()
10953
+ });
10954
+ var remoteMcpServerSchema = import_zod3.z.object({
10955
+ type: import_zod3.z.enum(["http", "sse"]),
10956
+ name: import_zod3.z.string().min(1, "MCP server name is required"),
10957
+ url: import_zod3.z.string().url("MCP server url must be a valid URL"),
10958
+ headers: import_zod3.z.array(httpHeaderSchema).default([])
10959
+ });
10960
+ var mcpServersSchema = import_zod3.z.array(remoteMcpServerSchema);
10897
10961
  var jsonRpcRequestSchema = import_zod3.z.object({
10898
10962
  jsonrpc: import_zod3.z.literal("2.0"),
10899
10963
  method: import_zod3.z.string(),
@@ -11018,6 +11082,24 @@ var AgentServer = class {
11018
11082
  posthogAPI;
11019
11083
  questionRelayedToSlack = false;
11020
11084
  detectedPrUrl = null;
11085
+ emitConsoleLog = (level, _scope, message, data) => {
11086
+ if (!this.session) return;
11087
+ const formatted = data !== void 0 ? `${message} ${JSON.stringify(data)}` : message;
11088
+ const notification = {
11089
+ jsonrpc: "2.0",
11090
+ method: POSTHOG_NOTIFICATIONS.CONSOLE,
11091
+ params: { level, message: formatted }
11092
+ };
11093
+ this.broadcastEvent({
11094
+ type: "notification",
11095
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11096
+ notification
11097
+ });
11098
+ this.session.logWriter.appendRawLine(
11099
+ this.session.payload.run_id,
11100
+ JSON.stringify(notification)
11101
+ );
11102
+ };
11021
11103
  constructor(config) {
11022
11104
  this.config = config;
11023
11105
  this.logger = new Logger({ debug: true, prefix: "[AgentServer]" });
@@ -11347,7 +11429,7 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
11347
11429
  }
11348
11430
  const sessionResponse = await clientConnection.newSession({
11349
11431
  cwd: this.config.repositoryPath,
11350
- mcpServers: [],
11432
+ mcpServers: this.config.mcpServers ?? [],
11351
11433
  _meta: {
11352
11434
  sessionId: payload.run_id,
11353
11435
  taskRunId: payload.run_id,
@@ -11369,6 +11451,14 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
11369
11451
  deviceInfo,
11370
11452
  logWriter
11371
11453
  };
11454
+ this.logger = new Logger({
11455
+ debug: true,
11456
+ prefix: "[AgentServer]",
11457
+ onLog: (level, scope, message, data) => {
11458
+ const _formatted = data !== void 0 ? `${message} ${JSON.stringify(data)}` : message;
11459
+ this.emitConsoleLog(level, scope, message, data);
11460
+ }
11461
+ });
11372
11462
  this.logger.info("Session initialized successfully");
11373
11463
  this.posthogAPI.updateTaskRun(payload.task_id, payload.run_id, {
11374
11464
  status: "in_progress"
@@ -11751,15 +11841,25 @@ Important:
11751
11841
  ...snapshot,
11752
11842
  device: this.session.deviceInfo
11753
11843
  };
11844
+ const notification = {
11845
+ jsonrpc: "2.0",
11846
+ method: POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT,
11847
+ params: snapshotWithDevice
11848
+ };
11754
11849
  this.broadcastEvent({
11755
11850
  type: "notification",
11756
11851
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11757
- notification: {
11758
- jsonrpc: "2.0",
11759
- method: POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT,
11760
- params: snapshotWithDevice
11761
- }
11852
+ notification
11762
11853
  });
11854
+ const { archiveUrl: _, ...paramsWithoutArchive } = snapshotWithDevice;
11855
+ const logNotification = {
11856
+ ...notification,
11857
+ params: paramsWithoutArchive
11858
+ };
11859
+ this.session.logWriter.appendRawLine(
11860
+ this.session.payload.run_id,
11861
+ JSON.stringify(logNotification)
11862
+ );
11763
11863
  }
11764
11864
  } catch (error) {
11765
11865
  this.logger.error("Failed to capture tree state", error);
@@ -11795,7 +11895,10 @@ program.name("agent-server").description("PostHog cloud agent server - runs in s
11795
11895
  "--mode <mode>",
11796
11896
  "Execution mode: interactive or background",
11797
11897
  "interactive"
11798
- ).requiredOption("--repositoryPath <path>", "Path to the repository").requiredOption("--taskId <id>", "Task ID").requiredOption("--runId <id>", "Task run ID").action(async (options) => {
11898
+ ).requiredOption("--repositoryPath <path>", "Path to the repository").requiredOption("--taskId <id>", "Task ID").requiredOption("--runId <id>", "Task run ID").option(
11899
+ "--mcpServers <json>",
11900
+ "MCP servers config as JSON array (ACP McpServer[] format)"
11901
+ ).action(async (options) => {
11799
11902
  const envResult = envSchema.safeParse(process.env);
11800
11903
  if (!envResult.success) {
11801
11904
  const errors = envResult.error.issues.map((issue) => ` - ${issue.message}`).join("\n");
@@ -11805,6 +11908,26 @@ ${errors}`);
11805
11908
  }
11806
11909
  const env = envResult.data;
11807
11910
  const mode = options.mode === "background" ? "background" : "interactive";
11911
+ let mcpServers;
11912
+ if (options.mcpServers) {
11913
+ let parsed;
11914
+ try {
11915
+ parsed = JSON.parse(options.mcpServers);
11916
+ } catch {
11917
+ program.error("--mcpServers must be valid JSON");
11918
+ return;
11919
+ }
11920
+ const result = mcpServersSchema.safeParse(parsed);
11921
+ if (!result.success) {
11922
+ const errors = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
11923
+ program.error(
11924
+ `--mcpServers validation failed (only remote http/sse servers are supported):
11925
+ ${errors}`
11926
+ );
11927
+ return;
11928
+ }
11929
+ mcpServers = result.data;
11930
+ }
11808
11931
  const server = new AgentServer({
11809
11932
  port: parseInt(options.port, 10),
11810
11933
  jwtPublicKey: env.JWT_PUBLIC_KEY,
@@ -11814,7 +11937,8 @@ ${errors}`);
11814
11937
  projectId: env.POSTHOG_PROJECT_ID,
11815
11938
  mode,
11816
11939
  taskId: options.taskId,
11817
- runId: options.runId
11940
+ runId: options.runId,
11941
+ mcpServers
11818
11942
  });
11819
11943
  process.on("SIGINT", async () => {
11820
11944
  await server.stop();