@posthog/agent 2.1.8 → 2.1.15

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.8",
3
+ "version": "2.1.15",
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": {
@@ -37,7 +37,7 @@ export interface BuildOptionsParams {
37
37
  const BRANCH_NAMING_INSTRUCTIONS = `
38
38
  # Branch Naming
39
39
 
40
- Before pushing a "workspace-*" branch to origin, rename it to something descriptive based on the work done. Do this automatically without asking the user.
40
+ When working in a detached HEAD state, create a descriptive branch name based on the work being done before committing. Do this automatically without asking the user.
41
41
  `;
42
42
 
43
43
  export function buildSystemPrompt(
@@ -518,7 +518,11 @@ export class AgentServer {
518
518
  await clientConnection.newSession({
519
519
  cwd: this.config.repositoryPath,
520
520
  mcpServers: [],
521
- _meta: { sessionId: payload.run_id },
521
+ _meta: {
522
+ sessionId: payload.run_id,
523
+ taskRunId: payload.run_id,
524
+ systemPrompt: { append: this.buildCloudSystemPrompt() },
525
+ },
522
526
  });
523
527
 
524
528
  this.session = {
@@ -533,6 +537,15 @@ export class AgentServer {
533
537
 
534
538
  this.logger.info("Session initialized successfully");
535
539
 
540
+ // Signal in_progress so the UI can start polling for updates
541
+ this.posthogAPI
542
+ .updateTaskRun(payload.task_id, payload.run_id, {
543
+ status: "in_progress",
544
+ })
545
+ .catch((err) =>
546
+ this.logger.warn("Failed to set task run to in_progress", err),
547
+ );
548
+
536
549
  await this.sendInitialTaskMessage(payload);
537
550
  }
538
551
 
@@ -579,6 +592,20 @@ export class AgentServer {
579
592
  }
580
593
  }
581
594
 
595
+ private buildCloudSystemPrompt(): string {
596
+ return `
597
+ # Cloud Task Execution
598
+
599
+ After completing the requested changes:
600
+ 1. Create a new branch with a descriptive name based on the work done
601
+ 2. Stage and commit all changes with a clear commit message
602
+ 3. Push the branch to origin
603
+ 4. Create a pull request using \`gh pr create\` with a descriptive title and body
604
+
605
+ Important: Always create the PR. Do not ask for confirmation.
606
+ `;
607
+ }
608
+
582
609
  private async signalTaskComplete(
583
610
  payload: JwtPayload,
584
611
  stopReason: string,
@@ -672,11 +699,97 @@ export class AgentServer {
672
699
  ) {
673
700
  await this.captureTreeState();
674
701
  }
702
+
703
+ if (
704
+ toolName &&
705
+ (toolName.includes("Bash") || toolName.includes("bash"))
706
+ ) {
707
+ this.detectAndAttachPrUrl(payload, params.update);
708
+ }
675
709
  }
676
710
  },
677
711
  };
678
712
  }
679
713
 
714
+ private detectAndAttachPrUrl(
715
+ payload: JwtPayload,
716
+ update: Record<string, unknown>,
717
+ ): void {
718
+ try {
719
+ const meta = (update?._meta as Record<string, unknown>)?.claudeCode as
720
+ | Record<string, unknown>
721
+ | undefined;
722
+ const toolResponse = meta?.toolResponse;
723
+
724
+ // Extract text content from tool response
725
+ let textToSearch = "";
726
+
727
+ if (toolResponse) {
728
+ if (typeof toolResponse === "string") {
729
+ textToSearch = toolResponse;
730
+ } else if (typeof toolResponse === "object" && toolResponse !== null) {
731
+ const respObj = toolResponse as Record<string, unknown>;
732
+ textToSearch =
733
+ String(respObj.stdout || "") + String(respObj.stderr || "");
734
+ if (!textToSearch && respObj.output) {
735
+ textToSearch = String(respObj.output);
736
+ }
737
+ }
738
+ }
739
+
740
+ // Also check content array
741
+ const content = update?.content;
742
+ if (Array.isArray(content)) {
743
+ for (const item of content) {
744
+ if (item.type === "text" && item.text) {
745
+ textToSearch += ` ${item.text}`;
746
+ }
747
+ }
748
+ }
749
+
750
+ if (!textToSearch) return;
751
+
752
+ // Match GitHub PR URLs
753
+ const prUrlMatch = textToSearch.match(
754
+ /https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/,
755
+ );
756
+ if (!prUrlMatch) return;
757
+
758
+ const prUrl = prUrlMatch[0];
759
+ this.logger.info("Detected PR URL in bash output", {
760
+ runId: payload.run_id,
761
+ prUrl,
762
+ });
763
+
764
+ // Fire-and-forget: attach PR URL to the task run
765
+ this.posthogAPI
766
+ .updateTaskRun(payload.task_id, payload.run_id, {
767
+ output: { pr_url: prUrl },
768
+ })
769
+ .then(() => {
770
+ this.logger.info("PR URL attached to task run", {
771
+ taskId: payload.task_id,
772
+ runId: payload.run_id,
773
+ prUrl,
774
+ });
775
+ })
776
+ .catch((err) => {
777
+ this.logger.error("Failed to attach PR URL to task run", {
778
+ taskId: payload.task_id,
779
+ runId: payload.run_id,
780
+ prUrl,
781
+ error: err,
782
+ });
783
+ });
784
+ } catch (err) {
785
+ // Never let detection errors break message flow
786
+ this.logger.debug("Error in PR URL detection", {
787
+ runId: payload.run_id,
788
+ error: err,
789
+ });
790
+ }
791
+ }
792
+
680
793
  private async cleanupSession(): Promise<void> {
681
794
  if (!this.session) return;
682
795