UNPKG

@posthog/agent

Version:

TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog

159 lines (156 loc) 4.71 kB
import 'node:fs'; import 'node:os'; import { WritableStream, ReadableStream } from 'node:stream/web'; // A pushable async iterable: allows you to push items and consume them with for-await. // Useful for bridging push-based and async-iterator-based code. class Pushable { queue = []; resolvers = []; done = false; push(item) { const resolve = this.resolvers.shift(); if (resolve) { resolve({ value: item, done: false }); } else { this.queue.push(item); } } end() { this.done = true; for (const resolve of this.resolvers) { resolve({ value: undefined, done: true }); } this.resolvers = []; } [Symbol.asyncIterator]() { return { next: () => { if (this.queue.length > 0) { const value = this.queue.shift(); return Promise.resolve({ value, done: false }); } if (this.done) { return Promise.resolve({ value: undefined, done: true, }); } return new Promise((resolve) => { this.resolvers.push(resolve); }); }, }; } } function unreachable(value, logger) { let valueAsString; try { valueAsString = JSON.stringify(value); } catch { valueAsString = value; } logger.error(`Unexpected case: ${valueAsString}`); } function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); } function pushableToReadableStream(pushable) { const iterator = pushable[Symbol.asyncIterator](); return new ReadableStream({ async pull(controller) { const { value, done } = await iterator.next(); if (done) { controller.close(); } else { controller.enqueue(value); } }, }); } function createBidirectionalStreams() { const clientToAgentPushable = new Pushable(); const agentToClientPushable = new Pushable(); const clientToAgentReadable = pushableToReadableStream(clientToAgentPushable); const agentToClientReadable = pushableToReadableStream(agentToClientPushable); const clientToAgentWritable = new WritableStream({ write(chunk) { clientToAgentPushable.push(chunk); }, close() { clientToAgentPushable.end(); }, }); const agentToClientWritable = new WritableStream({ write(chunk) { agentToClientPushable.push(chunk); }, close() { agentToClientPushable.end(); }, }); return { client: { readable: agentToClientReadable, writable: clientToAgentWritable, }, agent: { readable: clientToAgentReadable, writable: agentToClientWritable, }, }; } /** * Extracts lines from file content with byte limit enforcement. * * @param fullContent - The complete file content * @param maxContentLength - Maximum number of UTF-16 Code Units to return * @returns Object containing extracted content and metadata */ function extractLinesWithByteLimit(fullContent, maxContentLength) { if (fullContent === "") { return { content: "", wasLimited: false, linesRead: 1, }; } let linesSeen = 0; let index = 0; linesSeen = 0; let contentLength = 0; let wasLimited = false; while (true) { const nextIndex = fullContent.indexOf("\n", index); if (nextIndex < 0) { // Last line in file (no trailing newline) if (linesSeen > 0 && fullContent.length > maxContentLength) { wasLimited = true; break; } linesSeen += 1; contentLength = fullContent.length; break; } else { // Line with newline - include up to the newline const newContentLength = nextIndex + 1; if (linesSeen > 0 && newContentLength > maxContentLength) { wasLimited = true; break; } linesSeen += 1; contentLength = newContentLength; index = newContentLength; } } return { content: fullContent.slice(0, contentLength), wasLimited, linesRead: linesSeen, }; } export { Pushable, createBidirectionalStreams, extractLinesWithByteLimit, sleep, unreachable }; //# sourceMappingURL=utils.js.map