@posthog/agent 2.3.46 → 2.3.53

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.3.46",
907
+ version: "2.3.53",
908
908
  repository: "https://github.com/PostHog/code",
909
909
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
910
910
  exports: {
@@ -11108,7 +11108,7 @@ var SessionLogWriter = class _SessionLogWriter {
11108
11108
  taskId: context.taskId,
11109
11109
  runId: context.runId
11110
11110
  });
11111
- this.sessions.set(sessionId, { context });
11111
+ this.sessions.set(sessionId, { context, currentTurnMessages: [] });
11112
11112
  this.lastFlushAttemptTime.set(sessionId, Date.now());
11113
11113
  if (this.localCachePath) {
11114
11114
  const sessionDir = import_node_path7.default.join(
@@ -11155,6 +11155,7 @@ var SessionLogWriter = class _SessionLogWriter {
11155
11155
  const nonChunkAgentText = this.extractAgentMessageText(message);
11156
11156
  if (nonChunkAgentText) {
11157
11157
  session.lastAgentMessage = nonChunkAgentText;
11158
+ session.currentTurnMessages.push(nonChunkAgentText);
11158
11159
  }
11159
11160
  const entry = {
11160
11161
  type: "notification",
@@ -11251,6 +11252,7 @@ var SessionLogWriter = class _SessionLogWriter {
11251
11252
  const { text: text2, firstTimestamp } = session.chunkBuffer;
11252
11253
  session.chunkBuffer = void 0;
11253
11254
  session.lastAgentMessage = text2;
11255
+ session.currentTurnMessages.push(text2);
11254
11256
  const entry = {
11255
11257
  type: "notification",
11256
11258
  timestamp: firstTimestamp,
@@ -11276,6 +11278,17 @@ var SessionLogWriter = class _SessionLogWriter {
11276
11278
  getLastAgentMessage(sessionId) {
11277
11279
  return this.sessions.get(sessionId)?.lastAgentMessage;
11278
11280
  }
11281
+ getFullAgentResponse(sessionId) {
11282
+ const session = this.sessions.get(sessionId);
11283
+ if (!session || session.currentTurnMessages.length === 0) return void 0;
11284
+ return session.currentTurnMessages.join("\n\n");
11285
+ }
11286
+ resetTurnMessages(sessionId) {
11287
+ const session = this.sessions.get(sessionId);
11288
+ if (session) {
11289
+ session.currentTurnMessages = [];
11290
+ }
11291
+ }
11279
11292
  extractAgentMessageText(message) {
11280
11293
  if (message.method !== "session/update") {
11281
11294
  return null;
@@ -11591,6 +11604,12 @@ var AgentServer = class _AgentServer {
11591
11604
  questionRelayedToSlack = false;
11592
11605
  detectedPrUrl = null;
11593
11606
  resumeState = null;
11607
+ // Guards against concurrent session initialization. autoInitializeSession() and
11608
+ // the GET /events SSE handler can both call initializeSession() — the SSE connection
11609
+ // often arrives while newSession() is still awaited (this.session is still null),
11610
+ // causing a second session to be created and duplicate Slack messages to be sent.
11611
+ initializationPromise = null;
11612
+ pendingEvents = [];
11594
11613
  emitConsoleLog = (level, _scope, message, data) => {
11595
11614
  if (!this.session) return;
11596
11615
  const formatted = data !== void 0 ? `${message} ${JSON.stringify(data)}` : message;
@@ -11670,6 +11689,7 @@ var AgentServer = class _AgentServer {
11670
11689
  await this.initializeSession(payload, sseController);
11671
11690
  } else {
11672
11691
  this.session.sseController = sseController;
11692
+ this.replayPendingEvents();
11673
11693
  }
11674
11694
  this.sendSseEvent(sseController, {
11675
11695
  type: "connected",
@@ -11849,6 +11869,7 @@ var AgentServer = class _AgentServer {
11849
11869
  this.logger.info(
11850
11870
  `Processing user message (detectedPrUrl=${this.detectedPrUrl ?? "none"}): ${content.substring(0, 100)}...`
11851
11871
  );
11872
+ this.session.logWriter.resetTurnMessages(this.session.payload.run_id);
11852
11873
  const result = await this.session.clientConnection.prompt({
11853
11874
  sessionId: this.session.acpSessionId,
11854
11875
  prompt: [{ type: "text", text: content }],
@@ -11864,7 +11885,24 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
11864
11885
  }
11865
11886
  });
11866
11887
  this.broadcastTurnComplete(result.stopReason);
11867
- return { stopReason: result.stopReason };
11888
+ if (result.stopReason === "end_turn") {
11889
+ this.relayAgentResponse(this.session.payload).catch(
11890
+ (err) => this.logger.warn("Failed to relay follow-up response", err)
11891
+ );
11892
+ }
11893
+ let assistantMessage;
11894
+ try {
11895
+ await this.session.logWriter.flush(this.session.payload.run_id);
11896
+ assistantMessage = this.session.logWriter.getFullAgentResponse(
11897
+ this.session.payload.run_id
11898
+ );
11899
+ } catch {
11900
+ this.logger.warn("Failed to extract assistant message from logs");
11901
+ }
11902
+ return {
11903
+ stopReason: result.stopReason,
11904
+ ...assistantMessage && { assistant_message: assistantMessage }
11905
+ };
11868
11906
  }
11869
11907
  case POSTHOG_NOTIFICATIONS.CANCEL:
11870
11908
  case "cancel": {
@@ -11887,6 +11925,28 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
11887
11925
  }
11888
11926
  }
11889
11927
  async initializeSession(payload, sseController) {
11928
+ if (this.initializationPromise) {
11929
+ this.logger.info("Waiting for in-progress initialization", {
11930
+ runId: payload.run_id
11931
+ });
11932
+ await this.initializationPromise;
11933
+ if (this.session && sseController) {
11934
+ this.session.sseController = sseController;
11935
+ this.replayPendingEvents();
11936
+ }
11937
+ return;
11938
+ }
11939
+ this.initializationPromise = this._doInitializeSession(
11940
+ payload,
11941
+ sseController
11942
+ );
11943
+ try {
11944
+ await this.initializationPromise;
11945
+ } finally {
11946
+ this.initializationPromise = null;
11947
+ }
11948
+ }
11949
+ async _doInitializeSession(payload, sseController) {
11890
11950
  if (this.session) {
11891
11951
  await this.cleanupSession();
11892
11952
  }
@@ -12073,6 +12133,7 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
12073
12133
  descriptionLength: initialPrompt.length,
12074
12134
  usedInitialPromptOverride: !!initialPromptOverride
12075
12135
  });
12136
+ this.session.logWriter.resetTurnMessages(payload.run_id);
12076
12137
  const result = await this.session.clientConnection.prompt({
12077
12138
  sessionId: this.session.acpSessionId,
12078
12139
  prompt: [{ type: "text", text: initialPrompt }]
@@ -12099,7 +12160,7 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
12099
12160
  this.resumeState.conversation
12100
12161
  );
12101
12162
  const pendingUserMessage = this.getPendingUserMessage(taskRun);
12102
- const sandboxContext = this.resumeState.snapshotApplied ? `The sandbox environment (all files, packages, and code changes) has been fully restored from a snapshot.` : `The sandbox could not be restored from a snapshot (it may have expired). You are starting with a fresh environment but have the full conversation history below.`;
12163
+ const sandboxContext = this.resumeState.snapshotApplied ? `The workspace environment (all files, packages, and code changes) has been fully restored from where you left off.` : `The workspace files from the previous session were not restored (the file snapshot may have expired), so you are starting with a fresh environment. Your conversation history is fully preserved below.`;
12103
12164
  let resumePrompt;
12104
12165
  if (pendingUserMessage) {
12105
12166
  resumePrompt = `You are resuming a previous conversation. ${sandboxContext}
@@ -12130,6 +12191,7 @@ Continue from where you left off. The user is waiting for your response.`;
12130
12191
  snapshotApplied: this.resumeState.snapshotApplied
12131
12192
  });
12132
12193
  this.resumeState = null;
12194
+ this.session.logWriter.resetTurnMessages(payload.run_id);
12133
12195
  const result = await this.session.clientConnection.prompt({
12134
12196
  sessionId: this.session.acpSessionId,
12135
12197
  prompt: [{ type: "text", text: resumePrompt }]
@@ -12138,6 +12200,9 @@ Continue from where you left off. The user is waiting for your response.`;
12138
12200
  stopReason: result.stopReason
12139
12201
  });
12140
12202
  this.broadcastTurnComplete(result.stopReason);
12203
+ if (result.stopReason === "end_turn") {
12204
+ await this.relayAgentResponse(payload);
12205
+ }
12141
12206
  } catch (error) {
12142
12207
  this.logger.error("Failed to send resume message", error);
12143
12208
  if (this.session) {
@@ -12236,6 +12301,26 @@ Important:
12236
12301
  - Do NOT create a new branch or a new pull request.
12237
12302
  - Do NOT add "Co-Authored-By" trailers to commit messages.
12238
12303
  - Do NOT add "Generated with [Claude Code]" or similar attribution lines to PR descriptions.
12304
+ `;
12305
+ }
12306
+ if (!this.config.repositoryPath) {
12307
+ return `
12308
+ # Cloud Task Execution \u2014 No Repository Mode
12309
+
12310
+ You are a helpful assistant with access to PostHog via MCP tools. You can help with both code tasks and data/analytics questions.
12311
+
12312
+ When the user asks about analytics, data, metrics, events, funnels, dashboards, feature flags, experiments, or anything PostHog-related:
12313
+ - Use your PostHog MCP tools to query data, search insights, and provide real answers
12314
+ - Do NOT tell the user to check an external analytics platform \u2014 you ARE the analytics platform
12315
+ - Use tools like insight-query, query-run, event-definitions-list, and others to answer questions directly
12316
+
12317
+ When the user asks for code changes or software engineering tasks:
12318
+ - Let them know you can help but don't have a repository connected for this session
12319
+ - Offer to write code snippets, scripts, or provide guidance
12320
+
12321
+ Important:
12322
+ - Do NOT create branches, commits, or pull requests in this mode.
12323
+ - Prefer using MCP tools to answer questions with real data over giving generic advice.
12239
12324
  `;
12240
12325
  }
12241
12326
  return `
@@ -12338,6 +12423,9 @@ Important:
12338
12423
  }
12339
12424
  };
12340
12425
  },
12426
+ extNotification: async (method, params) => {
12427
+ this.logger.debug("Extension notification", { method, params });
12428
+ },
12341
12429
  sessionUpdate: async (params) => {
12342
12430
  if (params.update?.sessionUpdate === "tool_call_update") {
12343
12431
  const meta = params.update?._meta?.claudeCode;
@@ -12370,7 +12458,7 @@ Important:
12370
12458
  error
12371
12459
  });
12372
12460
  }
12373
- const message = this.session.logWriter.getLastAgentMessage(payload.run_id);
12461
+ const message = this.session.logWriter.getFullAgentResponse(payload.run_id);
12374
12462
  if (!message) {
12375
12463
  this.logger.warn("No agent message found for Slack relay", {
12376
12464
  taskId: payload.task_id,
@@ -12523,6 +12611,7 @@ Important:
12523
12611
  if (this.session.sseController) {
12524
12612
  this.session.sseController.close();
12525
12613
  }
12614
+ this.pendingEvents = [];
12526
12615
  this.session = null;
12527
12616
  }
12528
12617
  async captureTreeState() {
@@ -12571,6 +12660,16 @@ Important:
12571
12660
  broadcastEvent(event) {
12572
12661
  if (this.session?.sseController) {
12573
12662
  this.sendSseEvent(this.session.sseController, event);
12663
+ } else if (this.session) {
12664
+ this.pendingEvents.push(event);
12665
+ }
12666
+ }
12667
+ replayPendingEvents() {
12668
+ if (!this.session?.sseController || this.pendingEvents.length === 0) return;
12669
+ const events = this.pendingEvents;
12670
+ this.pendingEvents = [];
12671
+ for (const event of events) {
12672
+ this.sendSseEvent(this.session.sseController, event);
12574
12673
  }
12575
12674
  }
12576
12675
  sendSseEvent(controller, data) {