@posthog/agent 2.3.655 → 2.3.657

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/types.d.ts CHANGED
@@ -26,7 +26,7 @@ interface Task {
26
26
  slug?: string;
27
27
  title: string;
28
28
  description: string;
29
- origin_product: "error_tracking" | "eval_clusters" | "user_created" | "support_queue" | "session_summaries";
29
+ origin_product: "error_tracking" | "eval_clusters" | "user_created" | "support_queue" | "session_summaries" | "signal_report";
30
30
  github_integration?: number | null;
31
31
  repository: string;
32
32
  json_schema?: Record<string, unknown> | null;
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n GitHandoffCheckpoint,\n HandoffLocalGitState as GitHandoffLocalGitState,\n} from \"@posthog/git/handoff\";\n\n/**\n * Stored custom notification following ACP extensibility model.\n * Custom notifications use underscore-prefixed methods (e.g., `_posthog/phase_start`).\n * See: https://agentclientprotocol.com/docs/extensibility\n */\nexport interface StoredNotification {\n type: \"notification\";\n /** When this notification was stored */\n timestamp: string;\n /** JSON-RPC 2.0 notification (no id field = notification, not request) */\n notification: {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n };\n}\n\n/**\n * Type alias for stored log entries.\n */\nexport type StoredEntry = StoredNotification;\n\n// PostHog Task model (matches PostHog Code's OpenAPI schema)\nexport interface Task {\n id: string;\n task_number?: number;\n slug?: string;\n title: string;\n description: string;\n origin_product:\n | \"error_tracking\"\n | \"eval_clusters\"\n | \"user_created\"\n | \"support_queue\"\n | \"session_summaries\";\n github_integration?: number | null;\n repository: string; // Format: \"organization/repository\" (e.g., \"posthog/posthog-js\")\n json_schema?: Record<string, unknown> | null; // JSON schema for task output validation\n internal?: boolean;\n created_at: string;\n updated_at: string;\n created_by?: {\n id: number;\n uuid: string;\n distinct_id: string;\n first_name: string;\n email: string;\n };\n latest_run?: TaskRun;\n}\n\n// Log entry structure for TaskRun.log\n\nexport type ArtifactType =\n | \"plan\"\n | \"context\"\n | \"reference\"\n | \"output\"\n | \"artifact\"\n | \"user_attachment\";\n\nexport interface TaskRunArtifact {\n id?: string;\n name: string;\n type: ArtifactType;\n source?: string;\n size?: number;\n content_type?: string;\n storage_path?: string;\n uploaded_at?: string;\n}\n\nexport type TaskRunStatus =\n | \"not_started\"\n | \"queued\"\n | \"in_progress\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\";\n\nexport type TaskRunEnvironment = \"local\" | \"cloud\";\n\n// TaskRun model - represents individual execution runs of tasks\nexport interface TaskRun {\n id: string;\n task: string; // Task ID\n team: number;\n branch: string | null;\n stage: string | null; // Current stage (e.g., 'research', 'plan', 'build')\n environment: TaskRunEnvironment;\n status: TaskRunStatus;\n log_url: string;\n error_message: string | null;\n output: Record<string, unknown> | null; // Structured output (PR URL, commit SHA, etc.)\n state: Record<string, unknown>; // Intermediate run state (defaults to {}, never null)\n artifacts?: TaskRunArtifact[];\n created_at: string;\n updated_at: string;\n completed_at: string | null;\n}\n\nexport interface ProcessSpawnedCallback {\n onProcessSpawned?: (info: {\n pid: number;\n command: string;\n sessionId?: string;\n }) => void;\n onProcessExited?: (pid: number) => void;\n onMcpServersReady?: (serverNames: string[]) => void;\n}\n\nexport interface TaskExecutionOptions {\n repositoryPath?: string;\n adapter?: \"claude\" | \"codex\";\n model?: string;\n gatewayUrl?: string;\n codexBinaryPath?: string;\n instructions?: string;\n processCallbacks?: ProcessSpawnedCallback;\n /** Callback invoked when the agent calls the create_output tool for structured output */\n onStructuredOutput?: (output: Record<string, unknown>) => Promise<void>;\n}\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport type OnLogCallback = (\n level: LogLevel,\n scope: string,\n message: string,\n data?: unknown,\n) => void;\n\nexport interface PostHogAPIConfig {\n apiUrl: string;\n getApiKey: () => string | Promise<string>;\n refreshApiKey?: () => string | Promise<string>;\n projectId: number;\n userAgent?: string;\n}\n\nexport interface OtelTransportConfig {\n /** PostHog ingest host, e.g., \"https://us.i.posthog.com\" */\n host: string;\n /** Project API key */\n apiKey: string;\n /** Override the logs endpoint path (default: /i/v1/logs) */\n logsPath?: string;\n}\n\nexport interface AgentConfig {\n posthog?: PostHogAPIConfig;\n /** OTEL transport config for shipping logs to PostHog Logs */\n otelTransport?: OtelTransportConfig;\n /** Skip session log persistence (e.g. for preview sessions with no real task) */\n skipLogPersistence?: boolean;\n /** Local cache path for instant log loading (e.g., ~/.posthog-code) */\n localCachePath?: string;\n /**\n * Annotate files the agent reads with PostHog enrichment (event volume,\n * flag rollout/staleness, experiment links). Defaults to enabled when\n * `posthog` config is present; set `{ enabled: false }` to opt out.\n */\n enricher?: { enabled?: boolean };\n debug?: boolean;\n onLog?: OnLogCallback;\n}\n\n// Device info for tracking where work happens\nexport interface DeviceInfo {\n type: \"local\" | \"cloud\";\n name?: string;\n}\n\n// Agent execution mode - for tracking interactive vs background runs, when backgrounded an agent will continue working without asking questions\nexport type AgentMode = \"interactive\" | \"background\";\n\n// Git file status codes\nexport type FileStatus = \"A\" | \"M\" | \"D\";\n\nexport interface FileChange {\n path: string;\n status: FileStatus;\n}\n\nexport type HandoffLocalGitState = GitHandoffLocalGitState;\n\nexport interface GitCheckpoint extends GitHandoffCheckpoint {\n artifactPath?: string;\n indexArtifactPath?: string;\n}\n\nexport interface GitCheckpointEvent extends GitCheckpoint {\n device?: DeviceInfo;\n}\n\n/**\n * Keeps the emitted `@posthog/agent/types` entrypoint as a runtime ESM module.\n *\n * `export {}` is stripped by tsup in this package, which leaves `dist/types.js`\n * empty and breaks downstream type resolution for the exported subpath.\n */\nexport const AGENT_TYPES_MODULE = true;\n"],"mappings":";AA8MO,IAAM,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n GitHandoffCheckpoint,\n HandoffLocalGitState as GitHandoffLocalGitState,\n} from \"@posthog/git/handoff\";\n\n/**\n * Stored custom notification following ACP extensibility model.\n * Custom notifications use underscore-prefixed methods (e.g., `_posthog/phase_start`).\n * See: https://agentclientprotocol.com/docs/extensibility\n */\nexport interface StoredNotification {\n type: \"notification\";\n /** When this notification was stored */\n timestamp: string;\n /** JSON-RPC 2.0 notification (no id field = notification, not request) */\n notification: {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n };\n}\n\n/**\n * Type alias for stored log entries.\n */\nexport type StoredEntry = StoredNotification;\n\n// PostHog Task model (matches PostHog Code's OpenAPI schema)\nexport interface Task {\n id: string;\n task_number?: number;\n slug?: string;\n title: string;\n description: string;\n origin_product:\n | \"error_tracking\"\n | \"eval_clusters\"\n | \"user_created\"\n | \"support_queue\"\n | \"session_summaries\"\n | \"signal_report\";\n github_integration?: number | null;\n repository: string; // Format: \"organization/repository\" (e.g., \"posthog/posthog-js\")\n json_schema?: Record<string, unknown> | null; // JSON schema for task output validation\n internal?: boolean;\n created_at: string;\n updated_at: string;\n created_by?: {\n id: number;\n uuid: string;\n distinct_id: string;\n first_name: string;\n email: string;\n };\n latest_run?: TaskRun;\n}\n\n// Log entry structure for TaskRun.log\n\nexport type ArtifactType =\n | \"plan\"\n | \"context\"\n | \"reference\"\n | \"output\"\n | \"artifact\"\n | \"user_attachment\";\n\nexport interface TaskRunArtifact {\n id?: string;\n name: string;\n type: ArtifactType;\n source?: string;\n size?: number;\n content_type?: string;\n storage_path?: string;\n uploaded_at?: string;\n}\n\nexport type TaskRunStatus =\n | \"not_started\"\n | \"queued\"\n | \"in_progress\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\";\n\nexport type TaskRunEnvironment = \"local\" | \"cloud\";\n\n// TaskRun model - represents individual execution runs of tasks\nexport interface TaskRun {\n id: string;\n task: string; // Task ID\n team: number;\n branch: string | null;\n stage: string | null; // Current stage (e.g., 'research', 'plan', 'build')\n environment: TaskRunEnvironment;\n status: TaskRunStatus;\n log_url: string;\n error_message: string | null;\n output: Record<string, unknown> | null; // Structured output (PR URL, commit SHA, etc.)\n state: Record<string, unknown>; // Intermediate run state (defaults to {}, never null)\n artifacts?: TaskRunArtifact[];\n created_at: string;\n updated_at: string;\n completed_at: string | null;\n}\n\nexport interface ProcessSpawnedCallback {\n onProcessSpawned?: (info: {\n pid: number;\n command: string;\n sessionId?: string;\n }) => void;\n onProcessExited?: (pid: number) => void;\n onMcpServersReady?: (serverNames: string[]) => void;\n}\n\nexport interface TaskExecutionOptions {\n repositoryPath?: string;\n adapter?: \"claude\" | \"codex\";\n model?: string;\n gatewayUrl?: string;\n codexBinaryPath?: string;\n instructions?: string;\n processCallbacks?: ProcessSpawnedCallback;\n /** Callback invoked when the agent calls the create_output tool for structured output */\n onStructuredOutput?: (output: Record<string, unknown>) => Promise<void>;\n}\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport type OnLogCallback = (\n level: LogLevel,\n scope: string,\n message: string,\n data?: unknown,\n) => void;\n\nexport interface PostHogAPIConfig {\n apiUrl: string;\n getApiKey: () => string | Promise<string>;\n refreshApiKey?: () => string | Promise<string>;\n projectId: number;\n userAgent?: string;\n}\n\nexport interface OtelTransportConfig {\n /** PostHog ingest host, e.g., \"https://us.i.posthog.com\" */\n host: string;\n /** Project API key */\n apiKey: string;\n /** Override the logs endpoint path (default: /i/v1/logs) */\n logsPath?: string;\n}\n\nexport interface AgentConfig {\n posthog?: PostHogAPIConfig;\n /** OTEL transport config for shipping logs to PostHog Logs */\n otelTransport?: OtelTransportConfig;\n /** Skip session log persistence (e.g. for preview sessions with no real task) */\n skipLogPersistence?: boolean;\n /** Local cache path for instant log loading (e.g., ~/.posthog-code) */\n localCachePath?: string;\n /**\n * Annotate files the agent reads with PostHog enrichment (event volume,\n * flag rollout/staleness, experiment links). Defaults to enabled when\n * `posthog` config is present; set `{ enabled: false }` to opt out.\n */\n enricher?: { enabled?: boolean };\n debug?: boolean;\n onLog?: OnLogCallback;\n}\n\n// Device info for tracking where work happens\nexport interface DeviceInfo {\n type: \"local\" | \"cloud\";\n name?: string;\n}\n\n// Agent execution mode - for tracking interactive vs background runs, when backgrounded an agent will continue working without asking questions\nexport type AgentMode = \"interactive\" | \"background\";\n\n// Git file status codes\nexport type FileStatus = \"A\" | \"M\" | \"D\";\n\nexport interface FileChange {\n path: string;\n status: FileStatus;\n}\n\nexport type HandoffLocalGitState = GitHandoffLocalGitState;\n\nexport interface GitCheckpoint extends GitHandoffCheckpoint {\n artifactPath?: string;\n indexArtifactPath?: string;\n}\n\nexport interface GitCheckpointEvent extends GitCheckpoint {\n device?: DeviceInfo;\n}\n\n/**\n * Keeps the emitted `@posthog/agent/types` entrypoint as a runtime ESM module.\n *\n * `export {}` is stripped by tsup in this package, which leaves `dist/types.js`\n * empty and breaks downstream type resolution for the exported subpath.\n */\nexport const AGENT_TYPES_MODULE = true;\n"],"mappings":";AA+MO,IAAM,qBAAqB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.3.655",
3
+ "version": "2.3.657",
4
4
  "repository": "https://github.com/PostHog/code",
5
5
  "description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
6
6
  "exports": {
@@ -107,8 +107,8 @@
107
107
  "typescript": "^5.5.0",
108
108
  "vitest": "^2.1.8",
109
109
  "@posthog/shared": "1.0.0",
110
- "@posthog/enricher": "1.0.0",
111
- "@posthog/git": "1.0.0"
110
+ "@posthog/git": "1.0.0",
111
+ "@posthog/enricher": "1.0.0"
112
112
  },
113
113
  "dependencies": {
114
114
  "@agentclientprotocol/sdk": "0.19.0",
@@ -2,13 +2,20 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { AgentServer } from "./agent-server";
3
3
 
4
4
  interface TestableServer {
5
- configureEnvironment(args?: { isInternal?: boolean }): void;
5
+ configureEnvironment(args?: {
6
+ isInternal?: boolean;
7
+ originProduct?: string | null;
8
+ taskId?: string | null;
9
+ taskRunId?: string | null;
10
+ taskUserId?: number | null;
11
+ }): void;
6
12
  }
7
13
 
8
14
  const ENV_KEYS_UNDER_TEST = [
9
15
  "LLM_GATEWAY_URL",
10
16
  "ANTHROPIC_BASE_URL",
11
17
  "OPENAI_BASE_URL",
18
+ "ANTHROPIC_CUSTOM_HEADERS",
12
19
  ] as const;
13
20
 
14
21
  describe("AgentServer.configureEnvironment", () => {
@@ -85,6 +92,62 @@ describe("AgentServer.configureEnvironment", () => {
85
92
  expect(fromBackground).toBe("https://gateway.us.posthog.com/posthog_code");
86
93
  });
87
94
 
95
+ it("tags as signals when an internal task has origin_product 'signal_report'", () => {
96
+ buildServer("background").configureEnvironment({
97
+ isInternal: true,
98
+ originProduct: "signal_report",
99
+ });
100
+
101
+ expect(process.env.LLM_GATEWAY_URL).toBe(
102
+ "https://gateway.us.posthog.com/signals",
103
+ );
104
+ expect(process.env.ANTHROPIC_BASE_URL).toBe(
105
+ "https://gateway.us.posthog.com/signals",
106
+ );
107
+ expect(process.env.OPENAI_BASE_URL).toBe(
108
+ "https://gateway.us.posthog.com/signals/v1",
109
+ );
110
+ });
111
+
112
+ it("does not tag as signals when origin_product is 'signal_report' but the task is not internal", () => {
113
+ buildServer("background").configureEnvironment({
114
+ isInternal: false,
115
+ originProduct: "signal_report",
116
+ });
117
+
118
+ expect(process.env.LLM_GATEWAY_URL).toBe(
119
+ "https://gateway.us.posthog.com/posthog_code",
120
+ );
121
+ });
122
+
123
+ it("forwards task metadata as ANTHROPIC_CUSTOM_HEADERS", () => {
124
+ buildServer("background").configureEnvironment({
125
+ isInternal: true,
126
+ originProduct: "signal_report",
127
+ taskId: "task-abc",
128
+ taskRunId: "run-xyz",
129
+ taskUserId: 42,
130
+ });
131
+
132
+ expect(process.env.ANTHROPIC_CUSTOM_HEADERS).toBe(
133
+ [
134
+ "x-posthog-property-task_origin_product: signal_report",
135
+ "x-posthog-property-task_internal: true",
136
+ "x-posthog-property-task_id: task-abc",
137
+ "x-posthog-property-task_run_id: run-xyz",
138
+ "x-posthog-property-task_user_id: 42",
139
+ ].join("\n"),
140
+ );
141
+ });
142
+
143
+ it("omits optional task metadata from ANTHROPIC_CUSTOM_HEADERS when not provided", () => {
144
+ buildServer("background").configureEnvironment({ isInternal: false });
145
+
146
+ expect(process.env.ANTHROPIC_CUSTOM_HEADERS).toBe(
147
+ "x-posthog-property-task_internal: false",
148
+ );
149
+ });
150
+
88
151
  it("respects the LLM_GATEWAY_URL override regardless of internal flag", () => {
89
152
  process.env.LLM_GATEWAY_URL = "http://ngrok.test/proxy";
90
153
 
@@ -48,7 +48,11 @@ import type {
48
48
  } from "../types";
49
49
  import { resourceLink } from "../utils/acp-content";
50
50
  import { AsyncMutex } from "../utils/async-mutex";
51
- import { type GatewayProduct, getLlmGatewayUrl } from "../utils/gateway";
51
+ import {
52
+ buildGatewayPropertyHeaders,
53
+ getLlmGatewayUrl,
54
+ resolveGatewayProduct,
55
+ } from "../utils/gateway";
52
56
  import { Logger } from "../utils/logger";
53
57
  import { logAgentshRuntimeInfo } from "./agentsh-runtime";
54
58
  import {
@@ -845,7 +849,13 @@ export class AgentServer {
845
849
  }),
846
850
  ]);
847
851
 
848
- this.configureEnvironment({ isInternal: preTask?.internal === true });
852
+ this.configureEnvironment({
853
+ isInternal: preTask?.internal === true,
854
+ originProduct: preTask?.origin_product,
855
+ taskId: payload.task_id,
856
+ taskRunId: payload.run_id,
857
+ taskUserId: payload.user_id,
858
+ });
849
859
 
850
860
  const prUrl = getTaskRunStateString(preTaskRun, "slack_notified_pr_url");
851
861
 
@@ -1801,18 +1811,35 @@ ${signedCommitInstructions}
1801
1811
 
1802
1812
  private configureEnvironment({
1803
1813
  isInternal = false,
1814
+ originProduct,
1815
+ taskId,
1816
+ taskRunId,
1817
+ taskUserId,
1804
1818
  }: {
1805
1819
  isInternal?: boolean;
1820
+ originProduct?: string | null;
1821
+ taskId?: string | null;
1822
+ taskRunId?: string | null;
1823
+ taskUserId?: number | null;
1806
1824
  } = {}): void {
1807
1825
  const { apiKey, apiUrl, projectId } = this.config;
1808
- const product: GatewayProduct = isInternal
1809
- ? "background_agents"
1810
- : "posthog_code";
1826
+ const product = resolveGatewayProduct({ isInternal, originProduct });
1811
1827
  const gatewayUrl =
1812
1828
  process.env.LLM_GATEWAY_URL || getLlmGatewayUrl(apiUrl, product);
1813
1829
  const openaiBaseUrl = gatewayUrl.endsWith("/v1")
1814
1830
  ? gatewayUrl
1815
1831
  : `${gatewayUrl}/v1`;
1832
+ // Forward task metadata as `x-posthog-property-*` headers so the gateway
1833
+ // lifts them onto the $ai_generation event. Routes through the Anthropic
1834
+ // SDK's ANTHROPIC_CUSTOM_HEADERS env var; the OpenAI/codex path has no
1835
+ // equivalent today.
1836
+ const customHeaders = buildGatewayPropertyHeaders({
1837
+ task_origin_product: originProduct,
1838
+ task_internal: isInternal,
1839
+ task_id: taskId,
1840
+ task_run_id: taskRunId,
1841
+ task_user_id: taskUserId,
1842
+ });
1816
1843
 
1817
1844
  Object.assign(process.env, {
1818
1845
  // PostHog
@@ -1825,6 +1852,7 @@ ${signedCommitInstructions}
1825
1852
  ANTHROPIC_API_KEY: apiKey,
1826
1853
  ANTHROPIC_AUTH_TOKEN: apiKey,
1827
1854
  ANTHROPIC_BASE_URL: gatewayUrl,
1855
+ ANTHROPIC_CUSTOM_HEADERS: customHeaders,
1828
1856
  // OpenAI (for models like GPT-4, o1, etc.)
1829
1857
  OPENAI_API_KEY: apiKey,
1830
1858
  OPENAI_BASE_URL: openaiBaseUrl,