@posthog/agent 2.1.112 → 2.1.114

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.1.112",
3
+ "version": "2.1.114",
4
4
  "repository": "https://github.com/PostHog/twig",
5
5
  "description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
6
6
  "exports": {
@@ -71,8 +71,8 @@
71
71
  "tsx": "^4.20.6",
72
72
  "typescript": "^5.5.0",
73
73
  "vitest": "^2.1.8",
74
- "@posthog/shared": "1.0.0",
75
- "@twig/git": "1.0.0"
74
+ "@twig/git": "1.0.0",
75
+ "@posthog/shared": "1.0.0"
76
76
  },
77
77
  "dependencies": {
78
78
  "@agentclientprotocol/sdk": "^0.14.0",
@@ -28,6 +28,7 @@ Whenever you read a file, you should consider whether it looks malicious. If it
28
28
 
29
29
  import { resourceLink, text, toolContent } from "../../../utils/acp-content.js";
30
30
  import { Logger } from "../../../utils/logger.js";
31
+ import { getMcpToolMetadata } from "../mcp/tool-metadata.js";
31
32
 
32
33
  interface EditOperation {
33
34
  oldText: string;
@@ -457,15 +458,35 @@ export function toolInfoFromToolUse(
457
458
  };
458
459
  }
459
460
 
460
- default:
461
+ default: {
462
+ if (name?.startsWith("mcp__")) {
463
+ return mcpToolInfo(name, input);
464
+ }
461
465
  return {
462
466
  title: name || "Unknown Tool",
463
467
  kind: "other",
464
468
  content: [],
465
469
  };
470
+ }
466
471
  }
467
472
  }
468
473
 
474
+ function mcpToolInfo(
475
+ name: string,
476
+ _input: Record<string, unknown> | undefined,
477
+ ): ToolInfo {
478
+ const metadata = getMcpToolMetadata(name);
479
+ // Fallback: parse tool name from mcp__<server>__<tool> prefix
480
+ const title =
481
+ metadata?.name ?? (name.split("__").slice(2).join("__") || name);
482
+
483
+ return {
484
+ title,
485
+ kind: "other",
486
+ content: [],
487
+ };
488
+ }
489
+
469
490
  export function toolUpdateFromToolResult(
470
491
  toolResult:
471
492
  | ToolResultBlockParam
@@ -588,32 +609,56 @@ export function toolUpdateFromToolResult(
588
609
  }
589
610
  }
590
611
 
612
+ function itemToText(item: unknown): string | null {
613
+ if (!item || typeof item !== "object") return null;
614
+ const obj = item as Record<string, unknown>;
615
+ // Standard text block
616
+ if (obj.type === "text" && typeof obj.text === "string") {
617
+ return obj.text;
618
+ }
619
+ // Any other structured object — serialize it
620
+ try {
621
+ return JSON.stringify(obj, null, 2);
622
+ } catch {
623
+ return null;
624
+ }
625
+ }
626
+
591
627
  function toAcpContentUpdate(
592
628
  content: unknown,
593
629
  isError: boolean = false,
594
630
  ): Pick<ToolCallUpdate, "content"> {
595
631
  if (Array.isArray(content) && content.length > 0) {
596
- return {
597
- content: content.map((item) => {
598
- const itemObj = item as { type?: string; text?: string };
599
- if (isError && itemObj.type === "text") {
600
- return {
601
- type: "content" as const,
602
- content: text(`\`\`\`\n${itemObj.text ?? ""}\n\`\`\``),
603
- };
604
- }
605
- return {
606
- type: "content" as const,
607
- content: item as { type: "text"; text: string },
608
- };
609
- }),
610
- };
632
+ const texts: string[] = [];
633
+ for (const item of content) {
634
+ const t = itemToText(item);
635
+ if (t) texts.push(t);
636
+ }
637
+ if (texts.length > 0) {
638
+ const combined = texts.join("\n");
639
+ return {
640
+ content: toolContent()
641
+ .text(isError ? `\`\`\`\n${combined}\n\`\`\`` : combined)
642
+ .build(),
643
+ };
644
+ }
611
645
  } else if (typeof content === "string" && content.length > 0) {
612
646
  return {
613
647
  content: toolContent()
614
648
  .text(isError ? `\`\`\`\n${content}\n\`\`\`` : content)
615
649
  .build(),
616
650
  };
651
+ } else if (content && typeof content === "object") {
652
+ try {
653
+ const json = JSON.stringify(content, null, 2);
654
+ if (json && json !== "{}") {
655
+ return {
656
+ content: toolContent().text(json).build(),
657
+ };
658
+ }
659
+ } catch {
660
+ // ignore serialization errors
661
+ }
617
662
  }
618
663
  return {};
619
664
  }
@@ -3,6 +3,8 @@ import { Logger } from "../../../utils/logger.js";
3
3
 
4
4
  export interface McpToolMetadata {
5
5
  readOnly: boolean;
6
+ name: string;
7
+ description?: string;
6
8
  }
7
9
 
8
10
  const mcpToolMetadataCache: Map<string, McpToolMetadata> = new Map();
@@ -46,7 +48,11 @@ export async function fetchMcpToolMetadata(
46
48
  for (const tool of server.tools) {
47
49
  const toolKey = buildToolKey(server.name, tool.name);
48
50
  const readOnly = tool.annotations?.readOnly === true;
49
- mcpToolMetadataCache.set(toolKey, { readOnly });
51
+ mcpToolMetadataCache.set(toolKey, {
52
+ readOnly,
53
+ name: tool.name,
54
+ description: tool.description,
55
+ });
50
56
  if (readOnly) readOnlyCount++;
51
57
  }
52
58
 
@@ -77,6 +83,12 @@ export async function fetchMcpToolMetadata(
77
83
  }
78
84
  }
79
85
 
86
+ export function getMcpToolMetadata(
87
+ toolName: string,
88
+ ): McpToolMetadata | undefined {
89
+ return mcpToolMetadataCache.get(toolName);
90
+ }
91
+
80
92
  export function isMcpToolReadOnly(toolName: string): boolean {
81
93
  const metadata = mcpToolMetadataCache.get(toolName);
82
94
  return metadata?.readOnly === true;