@posthog/agent 2.3.657 → 2.3.663

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.
@@ -9315,7 +9315,7 @@ import { z as z5 } from "zod";
9315
9315
  // package.json
9316
9316
  var package_default = {
9317
9317
  name: "@posthog/agent",
9318
- version: "2.3.657",
9318
+ version: "2.3.663",
9319
9319
  repository: "https://github.com/PostHog/code",
9320
9320
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
9321
9321
  exports: {
@@ -19500,6 +19500,10 @@ function buildConfigArgs(options) {
19500
19500
  if (options.reasoningEffort) {
19501
19501
  args2.push("-c", `model_reasoning_effort="${options.reasoningEffort}"`);
19502
19502
  }
19503
+ if (options.additionalDirectories?.length) {
19504
+ const escaped = options.additionalDirectories.map((p) => `"${p.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`).join(",");
19505
+ args2.push("-c", `sandbox_workspace_write.writable_roots=[${escaped}]`);
19506
+ }
19503
19507
  if (options.instructions) {
19504
19508
  const escaped = options.instructions.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/"/g, '\\"');
19505
19509
  args2.push("-c", `instructions="${escaped}"`);
@@ -22450,6 +22454,671 @@ function normalizeCloudPromptContent(content) {
22450
22454
  return content;
22451
22455
  }
22452
22456
 
22457
+ // src/server/event-stream-sender.ts
22458
+ import { Buffer as Buffer4 } from "buffer";
22459
+
22460
+ // src/server/streaming-upload.ts
22461
+ import { Buffer as Buffer3 } from "buffer";
22462
+ import {
22463
+ request as httpRequest
22464
+ } from "http";
22465
+ import { request as httpsRequest } from "https";
22466
+ import { URL as URL2 } from "url";
22467
+ function headersFromIncoming(headers) {
22468
+ const result = new Headers();
22469
+ for (const [name2, value] of Object.entries(headers)) {
22470
+ if (value === void 0) {
22471
+ continue;
22472
+ }
22473
+ if (Array.isArray(value)) {
22474
+ for (const item of value) {
22475
+ result.append(name2, item);
22476
+ }
22477
+ } else {
22478
+ result.set(name2, String(value));
22479
+ }
22480
+ }
22481
+ return result;
22482
+ }
22483
+ function abortError() {
22484
+ const error = new Error("aborted");
22485
+ error.name = "AbortError";
22486
+ return error;
22487
+ }
22488
+ function writeRequestChunk(request, chunk) {
22489
+ return new Promise((resolve8, reject) => {
22490
+ const onError2 = (error) => {
22491
+ request.off("error", onError2);
22492
+ reject(error);
22493
+ };
22494
+ request.once("error", onError2);
22495
+ request.write(Buffer3.from(chunk), (error) => {
22496
+ request.off("error", onError2);
22497
+ if (error) {
22498
+ reject(error);
22499
+ return;
22500
+ }
22501
+ resolve8();
22502
+ });
22503
+ });
22504
+ }
22505
+ function closeRequest(request) {
22506
+ return new Promise((resolve8, reject) => {
22507
+ const onError2 = (error) => {
22508
+ request.off("error", onError2);
22509
+ reject(error);
22510
+ };
22511
+ request.once("error", onError2);
22512
+ request.end(() => {
22513
+ request.off("error", onError2);
22514
+ resolve8();
22515
+ });
22516
+ });
22517
+ }
22518
+ function createNodeStreamingUpload({
22519
+ url,
22520
+ headers,
22521
+ abortController
22522
+ }) {
22523
+ const parsedUrl = new URL2(url);
22524
+ const requestFactory = parsedUrl.protocol === "https:" ? httpsRequest : parsedUrl.protocol === "http:" ? httpRequest : void 0;
22525
+ if (!requestFactory) {
22526
+ throw new Error(`Unsupported event ingest protocol: ${parsedUrl.protocol}`);
22527
+ }
22528
+ const request = requestFactory(parsedUrl, {
22529
+ method: "POST",
22530
+ headers
22531
+ });
22532
+ let closed = false;
22533
+ const responsePromise = new Promise((resolve8, reject) => {
22534
+ request.on("response", (response) => {
22535
+ const chunks = [];
22536
+ response.on("data", (chunk) => {
22537
+ chunks.push(Buffer3.isBuffer(chunk) ? chunk : Buffer3.from(chunk));
22538
+ });
22539
+ response.on("end", () => {
22540
+ resolve8(
22541
+ new Response(Buffer3.concat(chunks), {
22542
+ status: response.statusCode ?? 0,
22543
+ statusText: response.statusMessage,
22544
+ headers: headersFromIncoming(response.headers)
22545
+ })
22546
+ );
22547
+ });
22548
+ response.on("error", reject);
22549
+ });
22550
+ request.on("error", reject);
22551
+ });
22552
+ const abortRequest = () => {
22553
+ closed = true;
22554
+ if (!request.destroyed) {
22555
+ request.destroy(abortError());
22556
+ }
22557
+ };
22558
+ abortController.signal.addEventListener("abort", abortRequest, {
22559
+ once: true
22560
+ });
22561
+ void responsePromise.finally(() => {
22562
+ abortController.signal.removeEventListener("abort", abortRequest);
22563
+ }).catch(() => void 0);
22564
+ return {
22565
+ async write(chunk) {
22566
+ if (closed) {
22567
+ throw new Error("Cannot write to closed event ingest stream");
22568
+ }
22569
+ await writeRequestChunk(request, chunk);
22570
+ },
22571
+ async close() {
22572
+ if (closed) {
22573
+ return;
22574
+ }
22575
+ closed = true;
22576
+ await closeRequest(request);
22577
+ },
22578
+ async abort() {
22579
+ abortRequest();
22580
+ },
22581
+ responsePromise
22582
+ };
22583
+ }
22584
+
22585
+ // src/server/event-stream-sender.ts
22586
+ var DEFAULT_MAX_BUFFERED_EVENTS = 2e4;
22587
+ var DEFAULT_MAX_STREAM_EVENTS = 900;
22588
+ var DEFAULT_MAX_STREAM_BYTES = 4e6;
22589
+ var DEFAULT_MAX_EVENT_BYTES = 9e5;
22590
+ var DEFAULT_FLUSH_DELAY_MS = 0;
22591
+ var DEFAULT_RETRY_DELAY_MS = 1e3;
22592
+ var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
22593
+ var DEFAULT_STOP_TIMEOUT_MS = 3e4;
22594
+ var DEFAULT_STREAM_WINDOW_MS = 5 * 60 * 1e3;
22595
+ var STREAM_COMPLETE_CONTROL_TYPE = "_posthog/stream_complete";
22596
+ var TaskRunEventStreamSender = class {
22597
+ constructor(config) {
22598
+ this.config = config;
22599
+ const apiUrl = config.apiUrl.replace(/\/$/, "");
22600
+ this.ingestUrl = `${apiUrl}/api/projects/${config.projectId}/tasks/${encodeURIComponent(
22601
+ config.taskId
22602
+ )}/runs/${encodeURIComponent(config.runId)}/event_stream/`;
22603
+ this.maxBufferedEvents = config.maxBufferedEvents ?? DEFAULT_MAX_BUFFERED_EVENTS;
22604
+ this.maxStreamEvents = config.maxStreamEvents ?? DEFAULT_MAX_STREAM_EVENTS;
22605
+ this.maxStreamBytes = config.maxStreamBytes ?? DEFAULT_MAX_STREAM_BYTES;
22606
+ this.maxEventBytes = config.maxEventBytes ?? DEFAULT_MAX_EVENT_BYTES;
22607
+ this.flushDelayMs = config.flushDelayMs ?? DEFAULT_FLUSH_DELAY_MS;
22608
+ this.retryDelayMs = config.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
22609
+ this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
22610
+ this.stopTimeoutMs = config.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
22611
+ this.streamWindowMs = config.streamWindowMs ?? DEFAULT_STREAM_WINDOW_MS;
22612
+ this.createStreamingUpload = config.createStreamingUpload ?? createNodeStreamingUpload;
22613
+ }
22614
+ ingestUrl;
22615
+ maxBufferedEvents;
22616
+ maxStreamEvents;
22617
+ maxStreamBytes;
22618
+ maxEventBytes;
22619
+ flushDelayMs;
22620
+ retryDelayMs;
22621
+ requestTimeoutMs;
22622
+ stopTimeoutMs;
22623
+ streamWindowMs;
22624
+ createStreamingUpload;
22625
+ encoder = new TextEncoder();
22626
+ sequence = 0;
22627
+ lastKnownAcceptedSeq = 0;
22628
+ bufferedEvents = [];
22629
+ flushTimer = null;
22630
+ flushPromise = null;
22631
+ streamClosePromise = null;
22632
+ activeStream = null;
22633
+ stopPromise = null;
22634
+ stopped = false;
22635
+ sequenceSynced = false;
22636
+ sequenceInitialized = false;
22637
+ transportCompleted = false;
22638
+ droppedBeforeSequenceCount = 0;
22639
+ bufferRevision = 0;
22640
+ enqueue(event) {
22641
+ if (this.stopped) return;
22642
+ if (!this.canAcceptEvent(event)) {
22643
+ return;
22644
+ }
22645
+ const envelope = {
22646
+ seq: ++this.sequence,
22647
+ event
22648
+ };
22649
+ this.bufferedEvents.push(envelope);
22650
+ this.scheduleFlush();
22651
+ }
22652
+ async stop() {
22653
+ if (this.stopPromise) {
22654
+ await this.stopPromise;
22655
+ return;
22656
+ }
22657
+ this.stopped = true;
22658
+ if (this.flushTimer) {
22659
+ clearTimeout(this.flushTimer);
22660
+ this.flushTimer = null;
22661
+ }
22662
+ this.stopPromise = this.drainForStop();
22663
+ await this.stopPromise;
22664
+ }
22665
+ scheduleFlush(delayMs = this.flushDelayMs) {
22666
+ if (this.flushTimer || this.flushPromise || this.stopped) return;
22667
+ this.flushTimer = setTimeout(() => {
22668
+ this.flushTimer = null;
22669
+ void this.flush();
22670
+ }, delayMs);
22671
+ }
22672
+ async drainForStop() {
22673
+ const startedAtMs = Date.now();
22674
+ const deadlineAtMs = startedAtMs + this.stopTimeoutMs;
22675
+ while (!this.transportCompleted) {
22676
+ const previousLength = this.bufferedEvents.length;
22677
+ const previousRevision = this.bufferRevision;
22678
+ try {
22679
+ await this.flush();
22680
+ await this.writeCompletionLine();
22681
+ await this.closeActiveStream();
22682
+ this.transportCompleted = true;
22683
+ return;
22684
+ } catch (error) {
22685
+ this.config.logger.warn(
22686
+ "Task run event ingest stop request failed",
22687
+ this.describeError(error)
22688
+ );
22689
+ }
22690
+ const madeProgress = this.bufferedEvents.length < previousLength || this.bufferRevision !== previousRevision;
22691
+ if (!madeProgress && !await this.waitBeforeStopRetry(deadlineAtMs)) {
22692
+ this.warnStopDeadlineReached(startedAtMs);
22693
+ return;
22694
+ }
22695
+ if (Date.now() >= deadlineAtMs && !this.transportCompleted) {
22696
+ this.warnStopDeadlineReached(startedAtMs);
22697
+ return;
22698
+ }
22699
+ }
22700
+ }
22701
+ async flush() {
22702
+ if (this.flushPromise) {
22703
+ await this.flushPromise.catch(() => void 0);
22704
+ }
22705
+ if (this.bufferedEvents.length === 0) {
22706
+ return true;
22707
+ }
22708
+ const previousBufferLength = this.bufferedEvents.length;
22709
+ const flushPromise = this.flushBufferedEvents();
22710
+ this.flushPromise = flushPromise;
22711
+ try {
22712
+ await flushPromise;
22713
+ return this.bufferedEvents.length < previousBufferLength;
22714
+ } catch (error) {
22715
+ this.config.logger.warn(
22716
+ "Task run event ingest stream write failed",
22717
+ this.describeError(error)
22718
+ );
22719
+ await this.abortActiveStream();
22720
+ if (!this.stopped) {
22721
+ this.scheduleFlush(this.retryDelayMs);
22722
+ }
22723
+ return false;
22724
+ } finally {
22725
+ if (this.flushPromise === flushPromise) {
22726
+ this.flushPromise = null;
22727
+ }
22728
+ if (!this.stopped && this.hasUnwrittenBufferedEvents()) {
22729
+ this.scheduleFlush(0);
22730
+ }
22731
+ }
22732
+ }
22733
+ async flushBufferedEvents() {
22734
+ while (true) {
22735
+ const stream = await this.ensureActiveStream();
22736
+ const nextEvent = this.bufferedEvents.find(
22737
+ (event) => event.seq > stream.sentThroughSeq
22738
+ );
22739
+ if (!nextEvent) {
22740
+ return;
22741
+ }
22742
+ const line = `${this.serializeEnvelope(nextEvent)}
22743
+ `;
22744
+ const lineBytes = Buffer4.byteLength(line, "utf8");
22745
+ if (this.shouldRollStreamBeforeWriting(stream, lineBytes)) {
22746
+ await this.closeActiveStream();
22747
+ continue;
22748
+ }
22749
+ await stream.upload.write(this.encoder.encode(line));
22750
+ stream.sentThroughSeq = nextEvent.seq;
22751
+ stream.sentEvents += 1;
22752
+ stream.sentBytes += lineBytes;
22753
+ }
22754
+ }
22755
+ hasUnwrittenBufferedEvents() {
22756
+ const sentThroughSeq = this.activeStream?.sentThroughSeq ?? this.lastKnownAcceptedSeq;
22757
+ return this.bufferedEvents.some((event) => event.seq > sentThroughSeq);
22758
+ }
22759
+ async writeCompletionLine() {
22760
+ await this.syncSequenceWithServer();
22761
+ while (true) {
22762
+ const stream = await this.ensureActiveStream();
22763
+ const hasUnwrittenEvents = this.bufferedEvents.some(
22764
+ (event) => event.seq > stream.sentThroughSeq
22765
+ );
22766
+ if (hasUnwrittenEvents) {
22767
+ await this.flushBufferedEvents();
22768
+ continue;
22769
+ }
22770
+ const line = `${JSON.stringify({
22771
+ type: STREAM_COMPLETE_CONTROL_TYPE,
22772
+ final_seq: this.sequence
22773
+ })}
22774
+ `;
22775
+ const lineBytes = Buffer4.byteLength(line, "utf8");
22776
+ if (this.shouldRollStreamBeforeWriting(stream, lineBytes, {
22777
+ ignoreEventCount: true
22778
+ })) {
22779
+ await this.closeActiveStream();
22780
+ continue;
22781
+ }
22782
+ await stream.upload.write(this.encoder.encode(line));
22783
+ stream.sentBytes += lineBytes;
22784
+ return;
22785
+ }
22786
+ }
22787
+ shouldRollStreamBeforeWriting(stream, lineBytes, options = {}) {
22788
+ if (!options.ignoreEventCount && stream.sentEvents > 0 && stream.sentEvents >= this.maxStreamEvents) {
22789
+ return true;
22790
+ }
22791
+ if (stream.sentBytes > 0 && stream.sentBytes + lineBytes > this.maxStreamBytes) {
22792
+ return true;
22793
+ }
22794
+ return Date.now() - stream.startedAtMs >= this.streamWindowMs;
22795
+ }
22796
+ async ensureActiveStream() {
22797
+ if (this.streamClosePromise) {
22798
+ await this.streamClosePromise.catch(() => void 0);
22799
+ }
22800
+ if (this.activeStream) {
22801
+ return this.activeStream;
22802
+ }
22803
+ await this.syncSequenceWithServer();
22804
+ const abortController = new AbortController();
22805
+ const upload = this.createStreamingUpload({
22806
+ url: this.ingestUrl,
22807
+ headers: this.buildHeaders(),
22808
+ abortController
22809
+ });
22810
+ const activeStream = {
22811
+ abortController,
22812
+ upload,
22813
+ responsePromise: upload.responsePromise,
22814
+ startedAtMs: Date.now(),
22815
+ sentThroughSeq: this.lastKnownAcceptedSeq,
22816
+ sentEvents: 0,
22817
+ sentBytes: 0,
22818
+ windowTimer: null
22819
+ };
22820
+ this.activeStream = activeStream;
22821
+ this.scheduleStreamWindowClose(activeStream);
22822
+ upload.responsePromise.catch((error) => {
22823
+ void this.handleActiveStreamResponseFailure(activeStream, error);
22824
+ });
22825
+ return activeStream;
22826
+ }
22827
+ scheduleStreamWindowClose(stream, delayOverrideMs) {
22828
+ this.clearStreamWindowClose(stream);
22829
+ const delayMs = delayOverrideMs ?? Math.max(0, stream.startedAtMs + this.streamWindowMs - Date.now());
22830
+ stream.windowTimer = setTimeout(() => {
22831
+ stream.windowTimer = null;
22832
+ void this.closeExpiredStream(stream);
22833
+ }, delayMs);
22834
+ }
22835
+ clearStreamWindowClose(stream) {
22836
+ if (!stream.windowTimer) {
22837
+ return;
22838
+ }
22839
+ clearTimeout(stream.windowTimer);
22840
+ stream.windowTimer = null;
22841
+ }
22842
+ async closeExpiredStream(stream) {
22843
+ if (this.activeStream !== stream || this.stopped) {
22844
+ return;
22845
+ }
22846
+ if (this.flushPromise) {
22847
+ this.scheduleStreamWindowClose(stream, 50);
22848
+ return;
22849
+ }
22850
+ try {
22851
+ await this.closeActiveStream();
22852
+ } catch (error) {
22853
+ this.config.logger.warn(
22854
+ "Task run event ingest stream window close failed",
22855
+ this.describeError(error)
22856
+ );
22857
+ if (!this.stopped && this.bufferedEvents.length > 0) {
22858
+ this.scheduleFlush(this.retryDelayMs);
22859
+ }
22860
+ }
22861
+ }
22862
+ async handleActiveStreamResponseFailure(stream, error) {
22863
+ if (this.activeStream !== stream) {
22864
+ return;
22865
+ }
22866
+ this.config.logger.warn(
22867
+ "Task run event ingest stream request failed",
22868
+ this.describeError(error)
22869
+ );
22870
+ try {
22871
+ await this.abortActiveStream();
22872
+ } catch (abortError2) {
22873
+ this.config.logger.warn(
22874
+ "Task run event ingest stream abort failed",
22875
+ this.describeError(abortError2)
22876
+ );
22877
+ }
22878
+ if (!this.stopped && this.bufferedEvents.length > 0) {
22879
+ this.scheduleFlush(this.retryDelayMs);
22880
+ }
22881
+ }
22882
+ async closeActiveStream() {
22883
+ if (this.streamClosePromise) {
22884
+ await this.streamClosePromise;
22885
+ return;
22886
+ }
22887
+ const stream = this.activeStream;
22888
+ if (!stream) {
22889
+ return;
22890
+ }
22891
+ const closePromise = this.closeStream(stream);
22892
+ this.streamClosePromise = closePromise;
22893
+ try {
22894
+ await closePromise;
22895
+ } finally {
22896
+ this.clearStreamWindowClose(stream);
22897
+ if (this.activeStream === stream) {
22898
+ this.activeStream = null;
22899
+ }
22900
+ if (this.streamClosePromise === closePromise) {
22901
+ this.streamClosePromise = null;
22902
+ }
22903
+ }
22904
+ }
22905
+ async closeStream(stream) {
22906
+ try {
22907
+ await stream.upload.close();
22908
+ } catch (error) {
22909
+ stream.abortController.abort();
22910
+ this.sequenceSynced = false;
22911
+ throw error;
22912
+ }
22913
+ let response;
22914
+ try {
22915
+ response = await this.waitForResponseWithTimeout(
22916
+ stream.responsePromise,
22917
+ stream.abortController
22918
+ );
22919
+ } catch (error) {
22920
+ stream.abortController.abort();
22921
+ this.sequenceSynced = false;
22922
+ throw error;
22923
+ }
22924
+ await this.applyIngestResponse(response, "Event ingest stream");
22925
+ this.sequenceSynced = true;
22926
+ }
22927
+ async abortActiveStream() {
22928
+ const stream = this.activeStream;
22929
+ if (!stream) {
22930
+ return;
22931
+ }
22932
+ stream.abortController.abort();
22933
+ this.clearStreamWindowClose(stream);
22934
+ try {
22935
+ await stream.upload.abort();
22936
+ } catch {
22937
+ } finally {
22938
+ if (this.activeStream === stream) {
22939
+ this.activeStream = null;
22940
+ }
22941
+ this.sequenceSynced = false;
22942
+ }
22943
+ }
22944
+ async waitBeforeStopRetry(deadlineAtMs) {
22945
+ const remainingMs = deadlineAtMs - Date.now();
22946
+ if (remainingMs <= 0) {
22947
+ return false;
22948
+ }
22949
+ await new Promise(
22950
+ (resolve8) => setTimeout(resolve8, Math.min(this.retryDelayMs, remainingMs))
22951
+ );
22952
+ return Date.now() < deadlineAtMs;
22953
+ }
22954
+ warnStopDeadlineReached(startedAtMs) {
22955
+ this.config.logger.warn(
22956
+ "Task run event ingest stop deadline reached before fully completing transport",
22957
+ {
22958
+ remaining: this.bufferedEvents.length,
22959
+ stopTimeoutMs: this.stopTimeoutMs,
22960
+ elapsedMs: Date.now() - startedAtMs
22961
+ }
22962
+ );
22963
+ }
22964
+ async syncSequenceWithServer() {
22965
+ if (this.sequenceSynced) return;
22966
+ const response = await this.fetchWithTimeout({
22967
+ method: "POST",
22968
+ headers: this.buildHeaders(),
22969
+ body: ""
22970
+ });
22971
+ const responseBody = await this.parseResponse(response);
22972
+ if (!response.ok) {
22973
+ throw new Error(
22974
+ `Event ingest sequence sync returned HTTP ${response.status}: ${responseBody.text.slice(0, 300)}`
22975
+ );
22976
+ }
22977
+ const lastAcceptedSeq = responseBody.parsed?.last_accepted_seq;
22978
+ if (typeof lastAcceptedSeq === "number" && lastAcceptedSeq > 0) {
22979
+ if (!this.sequenceInitialized) {
22980
+ this.bufferedEvents = this.bufferedEvents.map((event) => ({
22981
+ ...event,
22982
+ seq: event.seq + lastAcceptedSeq
22983
+ }));
22984
+ this.sequence += lastAcceptedSeq;
22985
+ this.bufferRevision += 1;
22986
+ } else {
22987
+ this.acceptThrough(lastAcceptedSeq);
22988
+ if (lastAcceptedSeq > this.sequence) {
22989
+ this.sequence = lastAcceptedSeq;
22990
+ }
22991
+ }
22992
+ this.lastKnownAcceptedSeq = lastAcceptedSeq;
22993
+ }
22994
+ this.sequenceSynced = true;
22995
+ this.sequenceInitialized = true;
22996
+ }
22997
+ async fetchWithTimeout(init2) {
22998
+ const abortController = new AbortController();
22999
+ const timeout = setTimeout(() => {
23000
+ abortController.abort();
23001
+ }, this.requestTimeoutMs);
23002
+ try {
23003
+ return await fetch(this.ingestUrl, {
23004
+ ...init2,
23005
+ signal: abortController.signal
23006
+ });
23007
+ } finally {
23008
+ clearTimeout(timeout);
23009
+ }
23010
+ }
23011
+ async waitForResponseWithTimeout(responsePromise, abortController) {
23012
+ const timeout = setTimeout(() => {
23013
+ abortController.abort();
23014
+ }, this.requestTimeoutMs);
23015
+ try {
23016
+ return await responsePromise;
23017
+ } finally {
23018
+ clearTimeout(timeout);
23019
+ }
23020
+ }
23021
+ async applyIngestResponse(response, label) {
23022
+ const responseBody = await this.parseResponse(response);
23023
+ const lastAcceptedSeq = responseBody.parsed?.last_accepted_seq;
23024
+ if (typeof lastAcceptedSeq === "number") {
23025
+ this.acceptThrough(lastAcceptedSeq);
23026
+ if (lastAcceptedSeq > this.sequence) {
23027
+ this.sequence = lastAcceptedSeq;
23028
+ }
23029
+ this.lastKnownAcceptedSeq = lastAcceptedSeq;
23030
+ if (response.status === 409) {
23031
+ this.rebaseBufferedEvents(lastAcceptedSeq);
23032
+ }
23033
+ }
23034
+ if (!response.ok) {
23035
+ throw new Error(
23036
+ `${label} returned HTTP ${response.status}: ${responseBody.text.slice(0, 300)}`
23037
+ );
23038
+ }
23039
+ }
23040
+ acceptThrough(lastAcceptedSeq) {
23041
+ const previousLength = this.bufferedEvents.length;
23042
+ this.bufferedEvents = this.bufferedEvents.filter(
23043
+ (event) => event.seq > lastAcceptedSeq
23044
+ );
23045
+ if (this.bufferedEvents.length !== previousLength) {
23046
+ this.bufferRevision += 1;
23047
+ }
23048
+ }
23049
+ buildHeaders() {
23050
+ return {
23051
+ Authorization: `Bearer ${this.config.token}`,
23052
+ "Content-Type": "application/x-ndjson"
23053
+ };
23054
+ }
23055
+ rebaseBufferedEvents(lastAcceptedSeq) {
23056
+ let nextSeq = lastAcceptedSeq + 1;
23057
+ this.bufferedEvents = this.bufferedEvents.map((event) => ({
23058
+ ...event,
23059
+ seq: nextSeq++
23060
+ }));
23061
+ this.sequence = nextSeq - 1;
23062
+ this.sequenceSynced = true;
23063
+ this.sequenceInitialized = true;
23064
+ this.lastKnownAcceptedSeq = lastAcceptedSeq;
23065
+ this.bufferRevision += 1;
23066
+ }
23067
+ async parseResponse(response) {
23068
+ const text2 = await response.text();
23069
+ if (!text2) {
23070
+ return { parsed: null, text: text2 };
23071
+ }
23072
+ try {
23073
+ return { parsed: JSON.parse(text2), text: text2 };
23074
+ } catch {
23075
+ return { parsed: null, text: text2 };
23076
+ }
23077
+ }
23078
+ canAcceptEvent(event) {
23079
+ const eventBytes = Buffer4.byteLength(
23080
+ this.serializeEnvelope({ seq: this.sequence + 1, event }),
23081
+ "utf8"
23082
+ );
23083
+ if (eventBytes > this.maxEventBytes) {
23084
+ this.config.logger.warn("Dropped oversized task run event", {
23085
+ eventBytes,
23086
+ maxEventBytes: this.maxEventBytes
23087
+ });
23088
+ return false;
23089
+ }
23090
+ if (this.bufferedEvents.length >= this.maxBufferedEvents) {
23091
+ this.droppedBeforeSequenceCount += 1;
23092
+ if (this.droppedBeforeSequenceCount === 1 || this.droppedBeforeSequenceCount % 100 === 0) {
23093
+ this.config.logger.warn(
23094
+ "Dropped task run event before assigning sequence due to backpressure",
23095
+ {
23096
+ dropped: this.droppedBeforeSequenceCount,
23097
+ maxBufferedEvents: this.maxBufferedEvents
23098
+ }
23099
+ );
23100
+ }
23101
+ return false;
23102
+ }
23103
+ if (this.droppedBeforeSequenceCount > 0) {
23104
+ this.config.logger.warn("Task run event ingest recovered after drops", {
23105
+ dropped: this.droppedBeforeSequenceCount
23106
+ });
23107
+ this.droppedBeforeSequenceCount = 0;
23108
+ }
23109
+ return true;
23110
+ }
23111
+ serializeEnvelope(envelope) {
23112
+ return JSON.stringify({ seq: envelope.seq, event: envelope.event });
23113
+ }
23114
+ describeError(error) {
23115
+ if (error instanceof Error) {
23116
+ return { message: error.message, stack: error.stack };
23117
+ }
23118
+ return error;
23119
+ }
23120
+ };
23121
+
22453
23122
  // src/server/jwt.ts
22454
23123
  import jwt from "jsonwebtoken";
22455
23124
  import { z as z3 } from "zod";
@@ -22714,6 +23383,7 @@ var AgentServer = class {
22714
23383
  session = null;
22715
23384
  app;
22716
23385
  posthogAPI;
23386
+ eventStreamSender = null;
22717
23387
  questionRelayedToSlack = false;
22718
23388
  detectedPrUrl = null;
22719
23389
  lastReportedBranch = null;
@@ -22757,6 +23427,17 @@ var AgentServer = class {
22757
23427
  getApiKey: () => config.apiKey,
22758
23428
  userAgent: `posthog/cloud.hog.dev; version: ${config.version ?? package_default.version}`
22759
23429
  });
23430
+ if (config.eventIngestToken) {
23431
+ this.eventStreamSender = new TaskRunEventStreamSender({
23432
+ apiUrl: config.apiUrl,
23433
+ projectId: config.projectId,
23434
+ taskId: config.taskId,
23435
+ runId: config.runId,
23436
+ token: config.eventIngestToken,
23437
+ logger: this.logger.child("EventIngest"),
23438
+ streamWindowMs: config.eventIngestStreamWindowMs
23439
+ });
23440
+ }
22760
23441
  this.app = this.createApp();
22761
23442
  }
22762
23443
  getRuntimeAdapter() {
@@ -22974,7 +23655,9 @@ var AgentServer = class {
22974
23655
  async stop() {
22975
23656
  this.logger.debug("Stopping agent server...");
22976
23657
  if (this.session) {
22977
- await this.cleanupSession();
23658
+ await this.cleanupSession({ completeEventStream: true });
23659
+ } else {
23660
+ await this.eventStreamSender?.stop();
22978
23661
  }
22979
23662
  if (this.server) {
22980
23663
  this.server.close();
@@ -23924,6 +24607,11 @@ ${signedCommitInstructions}
23924
24607
  return;
23925
24608
  }
23926
24609
  const status = "failed";
24610
+ this.enqueueTaskTerminalEvent(POSTHOG_NOTIFICATIONS.ERROR, {
24611
+ source: "agent_server",
24612
+ stopReason,
24613
+ error: errorMessage2 ?? "Agent error"
24614
+ });
23927
24615
  try {
23928
24616
  await this.posthogAPI.updateTaskRun(payload.task_id, payload.run_id, {
23929
24617
  status,
@@ -23932,8 +24620,21 @@ ${signedCommitInstructions}
23932
24620
  this.logger.debug("Task completion signaled", { status, stopReason });
23933
24621
  } catch (error) {
23934
24622
  this.logger.error("Failed to signal task completion", error);
24623
+ } finally {
24624
+ await this.eventStreamSender?.stop();
23935
24625
  }
23936
24626
  }
24627
+ enqueueTaskTerminalEvent(method, params) {
24628
+ this.eventStreamSender?.enqueue({
24629
+ type: "notification",
24630
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
24631
+ notification: {
24632
+ jsonrpc: "2.0",
24633
+ method,
24634
+ params
24635
+ }
24636
+ });
24637
+ }
23937
24638
  configureEnvironment({
23938
24639
  isInternal = false,
23939
24640
  originProduct,
@@ -24200,7 +24901,9 @@ ${signedCommitInstructions}
24200
24901
  });
24201
24902
  }
24202
24903
  }
24203
- async cleanupSession() {
24904
+ async cleanupSession({
24905
+ completeEventStream = false
24906
+ } = {}) {
24204
24907
  if (!this.session) return;
24205
24908
  this.logger.debug("Cleaning up session");
24206
24909
  try {
@@ -24230,6 +24933,9 @@ ${signedCommitInstructions}
24230
24933
  if (this.session.sseController) {
24231
24934
  this.session.sseController.close();
24232
24935
  }
24936
+ if (completeEventStream) {
24937
+ await this.eventStreamSender?.stop();
24938
+ }
24233
24939
  this.pendingEvents = [];
24234
24940
  this.lastReportedBranch = null;
24235
24941
  this.session = null;
@@ -24297,9 +25003,11 @@ ${signedCommitInstructions}
24297
25003
  );
24298
25004
  }
24299
25005
  broadcastEvent(event) {
25006
+ if (!this.session) return;
25007
+ this.eventStreamSender?.enqueue(event);
24300
25008
  if (this.session?.sseController) {
24301
25009
  this.sendSseEvent(this.session.sseController, event);
24302
- } else if (this.session) {
25010
+ } else {
24303
25011
  this.pendingEvents.push(event);
24304
25012
  }
24305
25013
  }