import { createHash } from "node:crypto"; import type { FeedbackReference, PlanDiscussionData, PlanReviewData, PullReviewData, } from "./features/contracts.js"; export type { FeedbackReference, ImplementationFixReply, PlanConversationReply, PlanDiscussionData, PlanReviewData, PullReviewData, PullReviewFinding, PullReviewReply, } from "./features/contracts.js"; export { assertImplementationFixReply, assertImplementationSummary, assertPlanConversationReply, assertPlanDraft, assertPullReviewReply, assertReviewDecision, } from "./features/validation.js"; export const protocolVersion = 1; export const planLabel = "agent:plan"; export const implementLabel = "agent:implement"; export const generatedLabel = "agent:generated"; export const blockedLabel = "agent:blocked"; export const planReadyLabel = "agent:plan-ready"; export const reviewFixLabel = "agent:fix-review"; export function log( level: string, message: string, fields: Record, ): string { return JSON.stringify({ level, message, ...fields, time: new Date().toISOString(), }); } export type Mode = "plan" | "implement"; export interface PlanData { issueDigest: string; baseSha: string; planDigest: string; markdown: string; summary: string; iterations: number; } export interface ImplementationData { issueDigest: string; planDigest: string; branch: string; baseBranch: string; baseSha: string; startingRemoteSha: string | null; gitSafetyDigest: string; diffDigest: string; changedFiles: string[]; pendingFiles: string[]; summary: string; iterations: number; review?: ReviewDecision; pullRequestNumber?: number; response?: string; feedback?: FeedbackReference[]; } export interface Result { version: number; mode: Mode; status: "success" | "no-changes" | "review-failed" | "failed"; message: string; plan?: PlanData; implementation?: ImplementationData; discussion?: PlanDiscussionData; review?: PlanReviewData | PullReviewData; } export interface Marker { v: number; kind: | "status" | "plan" | "implementation" | "pull-request" | "conversation" | "review"; issue: number; mode?: Mode; request?: string; status?: string; issueDigest?: string; baseSha?: string; planDigest?: string; branch?: string; pullRequest?: number; } export interface IssueSnapshot { digest: string; title: string; body: string; comments: Array<{ id: number; author: string; createdAt: string; body: string; }>; } export interface PlanDraft { planMarkdown: string; summary: string; } export interface ReviewDecision { verdict: "accept" | "revise"; findings: string[]; rationale: string; } export interface ImplementationSummary { summary: string; files: string[]; } export function requireEnv(name: string): string { const value = process.env[name]?.trim(); if (!value) throw new Error(`Required environment variable ${name} is not set`); return value; } export function sha256(value: string): string { return createHash("sha256").update(value).digest("hex"); } export function marker(value: Marker): string { return ``; } export function parseMarker( body: string, kind?: Marker["kind"], ): Marker | undefined { const match = body.match(//); if (!match?.[1]) return undefined; try { const value = JSON.parse(match[1]) as Marker; if ( value.v !== protocolVersion || !value.kind || !Number.isInteger(value.issue) ) return undefined; if (kind && value.kind !== kind) return undefined; return value; } catch { return undefined; } } export const statusMarker = (issue: number, mode: Mode): Marker => ({ v: protocolVersion, kind: "status", issue, mode, }); export function formatError(error: unknown): string { if (error instanceof Error) { if (error.cause === undefined) return error.message; const cause = formatError(error.cause); return cause && cause !== error.message ? `${error.message}: ${cause}` : error.message; } return String(error); }