@posthog/agent 2.3.125 → 2.3.131

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.3.125",
3
+ "version": "2.3.131",
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": {
@@ -388,12 +388,14 @@ async function handleDefaultPermissionFlow(
388
388
  updatedInput: toolInput as Record<string, unknown>,
389
389
  };
390
390
  } else {
391
- const message = "User refused permission to run tool";
391
+ const feedback = (
392
+ response._meta?.customInput as string | undefined
393
+ )?.trim();
394
+ const message = feedback
395
+ ? `User refused permission to run tool with feedback: ${feedback}`
396
+ : "User refused permission to run tool";
392
397
  await emitToolDenial(context, message);
393
- return {
394
- behavior: "deny",
395
- message,
396
- };
398
+ return { behavior: "deny", message, interrupt: !feedback };
397
399
  }
398
400
  }
399
401
 
@@ -169,6 +169,12 @@ export class AgentServer {
169
169
  private initializationPromise: Promise<void> | null = null;
170
170
  private pendingEvents: Record<string, unknown>[] = [];
171
171
 
172
+ private detachSseController(controller: SseController): void {
173
+ if (this.session?.sseController === controller) {
174
+ this.session.sseController = null;
175
+ }
176
+ }
177
+
172
178
  private emitConsoleLog = (
173
179
  level: LogLevel,
174
180
  _scope: string,
@@ -250,18 +256,15 @@ export class AgentServer {
250
256
  controller.enqueue(
251
257
  new TextEncoder().encode(`data: ${JSON.stringify(data)}\n\n`),
252
258
  );
253
- } catch (error) {
254
- this.logger.debug(
255
- "SSE send failed (stream may be closed)",
256
- error,
257
- );
259
+ } catch {
260
+ this.detachSseController(sseController);
258
261
  }
259
262
  },
260
263
  close: () => {
261
264
  try {
262
265
  controller.close();
263
- } catch (error) {
264
- this.logger.debug("SSE close failed (already closed)", error);
266
+ } catch {
267
+ this.detachSseController(sseController);
265
268
  }
266
269
  },
267
270
  };
@@ -1577,6 +1580,10 @@ Important:
1577
1580
  }
1578
1581
 
1579
1582
  private sendSseEvent(controller: SseController, data: unknown): void {
1580
- controller.send(data);
1583
+ try {
1584
+ controller.send(data);
1585
+ } catch {
1586
+ this.detachSseController(controller);
1587
+ }
1581
1588
  }
1582
1589
  }
@@ -235,6 +235,71 @@ describe("Question relay", () => {
235
235
 
236
236
  expect(result.outcome.outcome).toBe("selected");
237
237
  });
238
+
239
+ it("keeps auto-approving permissions after SSE send failures", async () => {
240
+ const appendRawLine = vi.fn();
241
+ const brokenSseController = {
242
+ send: vi.fn(() => {
243
+ throw new Error("stream closed");
244
+ }),
245
+ close: vi.fn(),
246
+ };
247
+
248
+ const cloudPermissionServer = server as TestableAgentServer & {
249
+ emitConsoleLog: (
250
+ level: "debug" | "info" | "warn" | "error",
251
+ scope: string,
252
+ message: string,
253
+ data?: unknown,
254
+ ) => void;
255
+ logger: { debug: (message: string, data?: unknown) => void };
256
+ session: {
257
+ payload: typeof TEST_PAYLOAD;
258
+ sseController: typeof brokenSseController | null;
259
+ logWriter: {
260
+ appendRawLine: (runId: string, line: string) => void;
261
+ };
262
+ };
263
+ };
264
+
265
+ cloudPermissionServer.session = {
266
+ payload: TEST_PAYLOAD,
267
+ sseController: brokenSseController,
268
+ logWriter: {
269
+ appendRawLine,
270
+ },
271
+ };
272
+ cloudPermissionServer.logger = {
273
+ debug: (message: string, data?: unknown) => {
274
+ cloudPermissionServer.emitConsoleLog(
275
+ "debug",
276
+ "agent",
277
+ message,
278
+ data,
279
+ );
280
+ },
281
+ };
282
+
283
+ const client = cloudPermissionServer.createCloudClient(TEST_PAYLOAD);
284
+
285
+ const firstResult = await client.requestPermission({
286
+ options: ALLOW_OPTIONS,
287
+ toolCall: { _meta: { codeToolKind: "bash" } },
288
+ });
289
+
290
+ expect(firstResult.outcome.outcome).toBe("selected");
291
+ expect(brokenSseController.send).toHaveBeenCalledTimes(1);
292
+ expect(cloudPermissionServer.session.sseController).toBeNull();
293
+
294
+ const secondResult = await client.requestPermission({
295
+ options: ALLOW_OPTIONS,
296
+ toolCall: { _meta: { codeToolKind: "bash" } },
297
+ });
298
+
299
+ expect(secondResult.outcome.outcome).toBe("selected");
300
+ expect(brokenSseController.send).toHaveBeenCalledTimes(1);
301
+ expect(appendRawLine).toHaveBeenCalledTimes(2);
302
+ });
238
303
  });
239
304
  });
240
305