feat: polish issue thread markdown and references

This commit is contained in:
Dotta 2026-04-10 22:26:21 -05:00
parent 548721248e
commit 958c11699e
16 changed files with 659 additions and 44 deletions

View file

@ -3,6 +3,7 @@ import type { TranscriptEntry, StdoutLineParser, TranscriptParserSource } from "
export type RunLogChunk = { ts: string; stream: "stdout" | "stderr" | "system"; chunk: string };
type TranscriptBuildOptions = { censorUsernameInLogs?: boolean };
type RedactionOptions = { enabled: boolean };
function resolveStdoutParser(source: StdoutLineParser | TranscriptParserSource) {
if (typeof source === "function") {
@ -33,6 +34,66 @@ export function appendTranscriptEntries(entries: TranscriptEntry[], incoming: Tr
}
}
function truncateTranscriptLine(line: string, maxLength = 160) {
if (line.length <= maxLength) return line;
return `${line.slice(0, maxLength - 3)}...`;
}
function formatTranscriptParserError(error: unknown) {
if (error instanceof Error && error.message) return error.message;
if (typeof error === "string" && error) return error;
try {
return JSON.stringify(error);
} catch {
return String(error);
}
}
function createTranscriptParseErrorEntry(
line: string,
ts: string,
error: unknown,
redactionOptions: RedactionOptions,
): TranscriptEntry {
const errorText = formatTranscriptParserError(error) || "unknown parser error";
const preview = truncateTranscriptLine(line);
return {
kind: "result",
ts,
text: redactHomePathUserSegments(
`Chat transcript error: ${errorText}. Falling back for line: ${preview}`,
redactionOptions,
),
inputTokens: 0,
outputTokens: 0,
cachedTokens: 0,
costUsd: 0,
subtype: "transcript_parse_error",
isError: true,
errors: [],
};
}
function appendParsedTranscriptLine(args: {
entries: TranscriptEntry[];
line: string;
ts: string;
parseLine: (line: string, ts: string) => TranscriptEntry[];
reset: (() => void) | null;
redactionOptions: RedactionOptions;
}) {
const { entries, line, ts, parseLine, reset, redactionOptions } = args;
try {
appendTranscriptEntries(
entries,
parseLine(line, ts).map((entry) => redactTranscriptEntryPaths(entry, redactionOptions)),
);
} catch (error) {
reset?.();
appendTranscriptEntry(entries, createTranscriptParseErrorEntry(line, ts, error, redactionOptions));
}
}
export function buildTranscript(
chunks: RunLogChunk[],
parserSource: StdoutLineParser | TranscriptParserSource,
@ -59,14 +120,28 @@ export function buildTranscript(
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
appendTranscriptEntries(entries, parseLine(trimmed, chunk.ts).map((entry) => redactTranscriptEntryPaths(entry, redactionOptions)));
appendParsedTranscriptLine({
entries,
line: trimmed,
ts: chunk.ts,
parseLine,
reset,
redactionOptions,
});
}
}
const trailing = stdoutBuffer.trim();
if (trailing) {
const ts = chunks.length > 0 ? chunks[chunks.length - 1]!.ts : new Date().toISOString();
appendTranscriptEntries(entries, parseLine(trailing, ts).map((entry) => redactTranscriptEntryPaths(entry, redactionOptions)));
appendParsedTranscriptLine({
entries,
line: trailing,
ts,
parseLine,
reset,
redactionOptions,
});
}
reset?.();