import type { CanonicalMessage } from "../../model/index.js";
import type { AgentTurnResult } from "../../agent/protocol/result.js";
export type AgentTranscriptEntryType =
| "accepted_input"
| "assistant_message"
| "tool_result_message"
| "durable_message"
| "turn_result"
| "control_boundary"
| "session_metadata"
| "subagent_started"
| "subagent_completed";
export type AgentTranscriptEntryBase = {
type: AgentTranscriptEntryType;
sessionId: string;
turnId: string;
sequence: number;
createdAt: string;
entryId?: string;
parentEntryId?: string | null;
};
export type AgentAcceptedInputTranscriptEntry = AgentTranscriptEntryBase & {
type: "accepted_input";
messages: CanonicalMessage[];
};
export type AgentMessageTranscriptEntry = AgentTranscriptEntryBase & {
type: "assistant_message" | "tool_result_message" | "durable_message";
message: CanonicalMessage;
};
export type AgentTurnResultTranscriptEntry = AgentTranscriptEntryBase & {
type: "turn_result";
result: AgentTurnResult;
};
export type CompactBoundaryMetadata = {
trigger: "manual" | "auto" | "reactive";
preTokens: number;
postTokens?: number;
messagesSummarized?: number;
logicalParentUuid?: string;
preservedSegment?: {
fromIndex: number;
toIndex: number;
};
* Tools that were available before compact; used by replay to detect missing
* tool references after compact.
*/
preCompactDiscoveredTools?: string[];
extra?: Record<string, unknown>;
};
export type MicroCompactBoundaryMetadata = {
trigger: "time_based" | "cached";
toolCallIds: string[];
rewrittenBytes?: number;
};
export type AgentControlBoundaryTranscriptEntry = AgentTranscriptEntryBase & {
type: "control_boundary";
boundary:
| {
kind: "compact";
subtype: "compact_boundary";
compactMetadata: CompactBoundaryMetadata;
}
| {
kind: "compact";
subtype: "microcompact_boundary";
microCompactMetadata: MicroCompactBoundaryMetadata;
}
| {
kind: "resume" | "manual";
metadata?: Record<string, unknown>;
};
};
export type SessionMetadataValue = {
title?: string;
aiTitle?: string;
tag?: string;
firstPrompt?: string;
lastPrompt?: string;
gitBranch?: string;
mode?: "normal" | "coordinator";
linkedPullRequest?: {
number: number;
url: string;
repository: string;
};
updatedAt?: string;
};
export type AgentSessionMetadataTranscriptEntry = AgentTranscriptEntryBase & {
type: "session_metadata";
metadata: SessionMetadataValue;
};
* Soft caps for sidechain reference fields. The full directive / final report
* lives in the sidechain transcript; the parent records only a truncated
* preview so the parent transcript stays bounded.
*/
export const SUBAGENT_PROMPT_PREVIEW_BYTES = 1024;
export const SUBAGENT_SUMMARY_PREVIEW_BYTES = 4 * 1024;
export type AgentSubagentStartedTranscriptEntry = AgentTranscriptEntryBase & {
type: "subagent_started";
subagentId: string;
subagentType: string;
* Truncated parent directive — capped at {@link SUBAGENT_PROMPT_PREVIEW_BYTES}
* to keep main-transcript size bounded. Full directive is the first user
* message in the sidechain.
*/
promptPreview: string;
promptTruncated: boolean;
transcriptRelativePath: string;
subagentSessionId?: string;
};
export type AgentSubagentCompletedTranscriptEntry = AgentTranscriptEntryBase & {
type: "subagent_completed";
subagentId: string;
subagentType: string;
summaryPreview: string;
summaryTruncated: boolean;
usage?: {
inputTokens?: number;
outputTokens?: number;
cacheReadTokens?: number;
cacheWriteTokens?: number;
totalTokens?: number;
};
turns: number;
durationMs: number;
errored?: boolean;
};
export type AgentTranscriptEntry =
| AgentAcceptedInputTranscriptEntry
| AgentMessageTranscriptEntry
| AgentTurnResultTranscriptEntry
| AgentControlBoundaryTranscriptEntry
| AgentSessionMetadataTranscriptEntry
| AgentSubagentStartedTranscriptEntry
| AgentSubagentCompletedTranscriptEntry;
export function truncatePreview(input: string, byteCap: number): { preview: string; truncated: boolean } {
const total = Buffer.byteLength(input, "utf8");
if (total <= byteCap) return { preview: input, truncated: false };
let bytes = 0;
let out = "";
for (const ch of input) {
const chBytes = Buffer.byteLength(ch, "utf8");
if (bytes + chBytes > byteCap) break;
bytes += chBytes;
out += ch;
}
return { preview: out, truncated: true };
}
export type AgentTranscriptDiagnostic = {
code: "transcript_missing" | "transcript_too_large" | "transcript_line_invalid" | "transcript_entry_invalid";
severity: "warning" | "error";
message: string;
line?: number;
};
export function classifyDurableMessageEntry(message: CanonicalMessage): AgentMessageTranscriptEntry["type"] {
if (message.role === "assistant") {
return "assistant_message";
}
if (message.content.some((block) => block.type === "tool_result")) {
return "tool_result_message";
}
return "durable_message";
}