@posthog/agent 2.3.256 → 2.3.261

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.
@@ -5683,7 +5683,7 @@ var import_hono = require("hono");
5683
5683
  // package.json
5684
5684
  var package_default = {
5685
5685
  name: "@posthog/agent",
5686
- version: "2.3.256",
5686
+ version: "2.3.261",
5687
5687
  repository: "https://github.com/PostHog/code",
5688
5688
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
5689
5689
  exports: {
@@ -5779,6 +5779,7 @@ var package_default = {
5779
5779
  },
5780
5780
  dependencies: {
5781
5781
  "@agentclientprotocol/sdk": "0.16.1",
5782
+ ajv: "^8.17.1",
5782
5783
  "@anthropic-ai/claude-agent-sdk": "0.2.76",
5783
5784
  "@anthropic-ai/sdk": "^0.78.0",
5784
5785
  "@hono/node-server": "^1.19.9",
@@ -8630,6 +8631,7 @@ function buildSessionOptions(params) {
8630
8631
  params.settingsManager,
8631
8632
  params.logger
8632
8633
  ),
8634
+ outputFormat: params.outputFormat,
8633
8635
  abortController: getAbortController(
8634
8636
  params.userProvidedOptions?.abortController
8635
8637
  ),
@@ -9217,6 +9219,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
9217
9219
  };
9218
9220
  const result = handleResultMessage(message);
9219
9221
  if (result.error) throw result.error;
9222
+ if (message.subtype === "success" && message.structured_output != null && this.options?.onStructuredOutput) {
9223
+ await this.options.onStructuredOutput(
9224
+ message.structured_output
9225
+ );
9226
+ }
9220
9227
  if (isLocalOnlyCommand && message.subtype === "success" && message.result) {
9221
9228
  await this.client.sessionUpdate({
9222
9229
  sessionId: params.sessionId,
@@ -9442,6 +9449,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
9442
9449
  const earlyModelId = settingsManager.getSettings().model || meta?.model || "";
9443
9450
  const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params) : {};
9444
9451
  const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
9452
+ const outputFormat = meta?.jsonSchema && this.options?.onStructuredOutput ? { type: "json_schema", schema: meta.jsonSchema } : void 0;
9445
9453
  this.logger.info(isResume ? "Resuming session" : "Creating new session", {
9446
9454
  sessionId,
9447
9455
  taskId,
@@ -9465,6 +9473,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
9465
9473
  ...meta?.additionalRoots ?? []
9466
9474
  ],
9467
9475
  disableBuiltInTools: meta?.disableBuiltInTools,
9476
+ outputFormat,
9468
9477
  settingsManager,
9469
9478
  onModeChange: this.createOnModeChange(),
9470
9479
  onProcessSpawned: this.options?.onProcessSpawned,
@@ -10424,7 +10433,10 @@ function createClaudeConnection(config) {
10424
10433
  const agentStream = (0, import_sdk4.ndJsonStream)(agentWritable, streams.agent.readable);
10425
10434
  let agent = null;
10426
10435
  const agentConnection = new import_sdk4.AgentSideConnection((client) => {
10427
- agent = new ClaudeAcpAgent(client, config.processCallbacks);
10436
+ agent = new ClaudeAcpAgent(client, {
10437
+ ...config.processCallbacks,
10438
+ onStructuredOutput: config.onStructuredOutput
10439
+ });
10428
10440
  logger.info(`Created ${agent.adapterName} agent`);
10429
10441
  return agent;
10430
10442
  }, agentStream);
@@ -10656,6 +10668,15 @@ var PostHogAPIClient = class {
10656
10668
  }
10657
10669
  );
10658
10670
  }
10671
+ async setTaskRunOutput(taskId, runId, output) {
10672
+ return this.apiRequest(
10673
+ `/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,
10674
+ {
10675
+ method: "PATCH",
10676
+ body: JSON.stringify(output)
10677
+ }
10678
+ );
10679
+ }
10659
10680
  async appendTaskRunLog(taskId, runId, entries) {
10660
10681
  const teamId = this.getTeamId();
10661
10682
  return this.apiRequest(
@@ -12591,12 +12612,9 @@ var AgentServer = class _AgentServer {
12591
12612
  prompt,
12592
12613
  ...this.detectedPrUrl && {
12593
12614
  _meta: {
12594
- prContext: `IMPORTANT \u2014 OVERRIDE PREVIOUS INSTRUCTIONS ABOUT CREATING BRANCHES/PRs.
12595
- You already have an open pull request: ${this.detectedPrUrl}
12596
- You MUST:
12597
- 1. Check out the existing PR branch with \`gh pr checkout ${this.detectedPrUrl}\`
12598
- 2. Make changes, commit, and push to that branch
12599
- You MUST NOT create a new branch, close the existing PR, or create a new PR.`
12615
+ // Keep the live-session PR override aligned with the startup
12616
+ // prompt policy so non-Slack runs remain review-first.
12617
+ prContext: this.buildDetectedPrContext(this.detectedPrUrl)
12600
12618
  }
12601
12619
  }
12602
12620
  });
@@ -12704,7 +12722,16 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
12704
12722
  taskRunId: payload.run_id,
12705
12723
  taskId: payload.task_id,
12706
12724
  deviceType: deviceInfo.type,
12707
- logWriter
12725
+ logWriter,
12726
+ onStructuredOutput: async (output) => {
12727
+ await this.posthogAPI.setTaskRunOutput(
12728
+ payload.task_id,
12729
+ payload.run_id,
12730
+ {
12731
+ output
12732
+ }
12733
+ );
12734
+ }
12708
12735
  });
12709
12736
  const onAcpMessage = (message) => {
12710
12737
  this.broadcastEvent({
@@ -12732,18 +12759,23 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
12732
12759
  protocolVersion: import_sdk5.PROTOCOL_VERSION,
12733
12760
  clientCapabilities: {}
12734
12761
  });
12735
- let preTaskRun = null;
12736
- try {
12737
- preTaskRun = await this.posthogAPI.getTaskRun(
12738
- payload.task_id,
12739
- payload.run_id
12740
- );
12741
- } catch {
12742
- this.logger.warn("Failed to fetch task run for session context", {
12743
- taskId: payload.task_id,
12744
- runId: payload.run_id
12745
- });
12746
- }
12762
+ const [preTaskRun, preTask] = await Promise.all([
12763
+ this.posthogAPI.getTaskRun(payload.task_id, payload.run_id).catch((err) => {
12764
+ this.logger.warn("Failed to fetch task run for session context", {
12765
+ taskId: payload.task_id,
12766
+ runId: payload.run_id,
12767
+ error: err
12768
+ });
12769
+ return null;
12770
+ }),
12771
+ this.posthogAPI.getTask(payload.task_id).catch((err) => {
12772
+ this.logger.warn("Failed to fetch task for session context", {
12773
+ taskId: payload.task_id,
12774
+ error: err
12775
+ });
12776
+ return null;
12777
+ })
12778
+ ]);
12747
12779
  const prUrl = typeof preTaskRun?.state?.slack_notified_pr_url === "string" ? (preTaskRun?.state).slack_notified_pr_url : null;
12748
12780
  if (prUrl) {
12749
12781
  this.detectedPrUrl = prUrl;
@@ -12756,6 +12788,7 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
12756
12788
  taskRunId: payload.run_id,
12757
12789
  systemPrompt: this.buildSessionSystemPrompt(prUrl),
12758
12790
  allowedDomains: this.config.allowedDomains,
12791
+ jsonSchema: preTask?.json_schema ?? null,
12759
12792
  ...this.config.claudeCode?.plugins?.length && {
12760
12793
  claudeCode: {
12761
12794
  options: {
@@ -13041,13 +13074,38 @@ ${toolSummary}`);
13041
13074
  }
13042
13075
  return { append: cloudAppend };
13043
13076
  }
13077
+ getCloudInteractionOrigin() {
13078
+ return process.env.POSTHOG_CODE_INTERACTION_ORIGIN ?? process.env.CODE_INTERACTION_ORIGIN ?? process.env.TWIG_INTERACTION_ORIGIN;
13079
+ }
13080
+ /**
13081
+ * Slack-origin cloud runs auto-publish by default. Every other origin is
13082
+ * review-first unless the user explicitly asks, and createPr=false always
13083
+ * disables publishing.
13084
+ */
13085
+ shouldAutoPublishCloudChanges() {
13086
+ return this.getCloudInteractionOrigin() === "slack" && this.config.createPr !== false;
13087
+ }
13088
+ buildDetectedPrContext(prUrl) {
13089
+ if (!this.shouldAutoPublishCloudChanges()) {
13090
+ return `An open pull request already exists: ${prUrl}
13091
+ Use that PR as context if it is helpful, but stop with local changes ready for review.
13092
+ Do NOT create commits, push to the PR branch, update the pull request, create a new branch, or create a new pull request unless the user explicitly asks.`;
13093
+ }
13094
+ return `IMPORTANT \u2014 OVERRIDE PREVIOUS INSTRUCTIONS ABOUT CREATING BRANCHES/PRs.
13095
+ You already have an open pull request: ${prUrl}
13096
+ You MUST:
13097
+ 1. Check out the existing PR branch with \`gh pr checkout ${prUrl}\`
13098
+ 2. Make changes, commit, and push to that branch
13099
+ You MUST NOT create a new branch, close the existing PR, or create a new PR.`;
13100
+ }
13044
13101
  buildCloudSystemPrompt(prUrl) {
13045
13102
  const taskId = this.config.taskId;
13103
+ const shouldAutoCreatePr = this.shouldAutoPublishCloudChanges();
13046
13104
  const attributionInstructions = `
13047
13105
  ## Attribution
13048
13106
  Do NOT use Claude Code's default attribution (no "Co-Authored-By" trailers, no "Generated with [Claude Code]" lines).
13049
13107
 
13050
- Instead, add the following trailers to EVERY commit message (after a blank line at the end):
13108
+ If you create a commit, add the following trailers to the commit message (after a blank line at the end):
13051
13109
  Generated-By: PostHog Code
13052
13110
  Task-Id: ${taskId}
13053
13111
 
@@ -13062,6 +13120,20 @@ EOF
13062
13120
  )"
13063
13121
  \`\`\``;
13064
13122
  if (prUrl) {
13123
+ if (!shouldAutoCreatePr) {
13124
+ return `
13125
+ # Cloud Task Execution
13126
+
13127
+ This task already has an open pull request: ${prUrl}
13128
+
13129
+ Do the requested work, but stop with local changes ready for review.
13130
+
13131
+ Important:
13132
+ - Do NOT create new commits, push to the branch, or update the pull request unless the user explicitly asks.
13133
+ - Do NOT create a new branch or a new pull request.
13134
+ ${attributionInstructions}
13135
+ `;
13136
+ }
13065
13137
  return `
13066
13138
  # Cloud Task Execution
13067
13139
 
@@ -13095,6 +13167,17 @@ When the user asks for code changes or software engineering tasks:
13095
13167
  Important:
13096
13168
  - Do NOT create branches, commits, or pull requests in this mode.
13097
13169
  - Prefer using MCP tools to answer questions with real data over giving generic advice.
13170
+ `;
13171
+ }
13172
+ if (!shouldAutoCreatePr) {
13173
+ return `
13174
+ # Cloud Task Execution
13175
+
13176
+ Do the requested work, but stop with local changes ready for review.
13177
+
13178
+ Important:
13179
+ - Do NOT create a branch, commit, push, or open a pull request unless the user explicitly asks.
13180
+ ${attributionInstructions}
13098
13181
  `;
13099
13182
  }
13100
13183
  return `
@@ -13203,9 +13286,30 @@ ${attributionInstructions}
13203
13286
  LLM_GATEWAY_URL: gatewayUrl
13204
13287
  });
13205
13288
  }
13289
+ buildSlackQuestionRelayResponse(payload, toolMeta2) {
13290
+ this.relaySlackQuestion(payload, toolMeta2);
13291
+ return {
13292
+ outcome: { outcome: "cancelled" },
13293
+ _meta: {
13294
+ message: "This question has been relayed to the Slack thread where this task originated. The user will reply there. Do NOT re-ask the question or pick an answer yourself. Simply let the user know you are waiting for their reply."
13295
+ }
13296
+ };
13297
+ }
13298
+ shouldBlockPublishPermission(params) {
13299
+ if (this.config.createPr !== false) {
13300
+ return false;
13301
+ }
13302
+ const meta = params.toolCall?._meta && typeof params.toolCall._meta === "object" && !Array.isArray(params.toolCall._meta) ? params.toolCall._meta : null;
13303
+ const rawInput = params.toolCall?.rawInput && typeof params.toolCall.rawInput === "object" && !Array.isArray(params.toolCall.rawInput) ? params.toolCall.rawInput : null;
13304
+ const toolName = typeof meta?.toolName === "string" ? meta.toolName : null;
13305
+ const command = typeof rawInput?.command === "string" ? rawInput.command : null;
13306
+ return Boolean(
13307
+ toolName && (toolName === "Bash" || toolName.includes("bash")) && command && /\bgit\s+push\b|\bgh\s+pr\s+(create|edit|ready|merge)\b/.test(command)
13308
+ );
13309
+ }
13206
13310
  createCloudClient(payload) {
13207
13311
  const mode = this.getEffectiveMode(payload);
13208
- const interactionOrigin = process.env.CODE_INTERACTION_ORIGIN ?? process.env.TWIG_INTERACTION_ORIGIN;
13312
+ const interactionOrigin = process.env.POSTHOG_CODE_INTERACTION_ORIGIN ?? process.env.CODE_INTERACTION_ORIGIN ?? process.env.TWIG_INTERACTION_ORIGIN;
13209
13313
  return {
13210
13314
  requestPermission: async (params) => {
13211
13315
  this.logger.debug("Permission request", {
@@ -13220,15 +13324,20 @@ ${attributionInstructions}
13220
13324
  if (interactionOrigin === "slack") {
13221
13325
  const codeToolKind = params.toolCall?._meta?.codeToolKind;
13222
13326
  if (codeToolKind === "question") {
13223
- this.relaySlackQuestion(payload, params.toolCall?._meta);
13224
- return {
13225
- outcome: { outcome: "cancelled" },
13226
- _meta: {
13227
- message: "This question has been relayed to the Slack thread where this task originated. The user will reply there. Do NOT re-ask the question or pick an answer yourself. Simply let the user know you are waiting for their reply."
13228
- }
13229
- };
13327
+ return this.buildSlackQuestionRelayResponse(
13328
+ payload,
13329
+ params.toolCall?._meta
13330
+ );
13230
13331
  }
13231
13332
  }
13333
+ if (this.shouldBlockPublishPermission(params)) {
13334
+ return {
13335
+ outcome: { outcome: "cancelled" },
13336
+ _meta: {
13337
+ message: "This run is configured to stop before publishing. Do not push commits or create/update pull requests unless the user explicitly asks."
13338
+ }
13339
+ };
13340
+ }
13232
13341
  return {
13233
13342
  outcome: {
13234
13343
  outcome: "selected",
@@ -13513,6 +13622,12 @@ var envSchema = import_v42.z.object({
13513
13622
  }).regex(/^\d+$/, "POSTHOG_PROJECT_ID must be a numeric string").transform((val) => parseInt(val, 10))
13514
13623
  });
13515
13624
  var program = new import_commander.Command();
13625
+ function parseBooleanOption(raw, flag) {
13626
+ if (raw === void 0) return void 0;
13627
+ if (raw === "true") return true;
13628
+ if (raw === "false") return false;
13629
+ program.error(`${flag} must be either "true" or "false"`);
13630
+ }
13516
13631
  function parseJsonOption(raw, schema, flag) {
13517
13632
  if (!raw) return void 0;
13518
13633
  let parsed;
@@ -13536,7 +13651,7 @@ program.name("agent-server").description("PostHog cloud agent server - runs in s
13536
13651
  ).option("--repositoryPath <path>", "Path to the repository").requiredOption("--taskId <id>", "Task ID").requiredOption("--runId <id>", "Task run ID").option(
13537
13652
  "--mcpServers <json>",
13538
13653
  "MCP servers config as JSON array (ACP McpServer[] format)"
13539
- ).option("--baseBranch <branch>", "Base branch for PR creation").option(
13654
+ ).option("--createPr <boolean>", "Whether this run may publish changes").option("--baseBranch <branch>", "Base branch for PR creation").option(
13540
13655
  "--claudeCodeConfig <json>",
13541
13656
  "Claude Code config as JSON (systemPrompt, systemPromptAppend, plugins)"
13542
13657
  ).option(
@@ -13552,6 +13667,7 @@ ${errors}`);
13552
13667
  }
13553
13668
  const env = envResult.data;
13554
13669
  const mode = options.mode === "background" ? "background" : "interactive";
13670
+ const createPr = parseBooleanOption(options.createPr, "--createPr");
13555
13671
  const mcpServers = parseJsonOption(
13556
13672
  options.mcpServers,
13557
13673
  mcpServersSchema,
@@ -13573,6 +13689,7 @@ ${errors}`);
13573
13689
  mode,
13574
13690
  taskId: options.taskId,
13575
13691
  runId: options.runId,
13692
+ createPr,
13576
13693
  mcpServers,
13577
13694
  baseBranch: options.baseBranch,
13578
13695
  claudeCode,