@posthog/agent 2.3.657 → 2.3.658

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