import { Schema } from "effect" export const PROTOCOL_VERSION = 1 as const const ErrorFields = Schema.Record(Schema.String, Schema.Unknown) export const SuccessEnvelope = Schema.Struct({ version: Schema.Literal(PROTOCOL_VERSION), ok: Schema.Literal(true), result: Schema.Unknown, }) export const ErrorDetail = Schema.Struct({ code: Schema.String, message: Schema.String, fields: ErrorFields, retryable: Schema.Boolean, remediation: Schema.optional(Schema.String), }) export const ErrorEnvelope = Schema.Struct({ version: Schema.Literal(PROTOCOL_VERSION), ok: Schema.Literal(false), error: ErrorDetail, }) export const AgencyEnvelope = Schema.Union([SuccessEnvelope, ErrorEnvelope]) export type SuccessEnvelope = Schema.Schema.Type export type ErrorEnvelope = Schema.Schema.Type export type AgencyEnvelope = Schema.Schema.Type interface ErrorMetadata { readonly code: string readonly retryable: boolean readonly remediation?: string } const errorMetadata: Readonly> = { CliUsageError: { code: "CLI_USAGE", retryable: false, remediation: "Correct the arguments using the usage value in error.fields.", }, WorkbaseNotFoundError: { code: "WORKBASE_NOT_FOUND", retryable: false, remediation: "Run the command from an Agency workbase or provide an explicit workbase path.", }, WorkbaseConfigError: { code: "WORKBASE_CONFIG_INVALID", retryable: false, remediation: "Correct the workbase configuration and retry the command.", }, WorkbaseRegistryError: { code: "WORKBASE_REGISTRY_ERROR", retryable: false, remediation: "Correct the registered workbase entry and retry the command.", }, FileNotFoundError: { code: "FILE_NOT_FOUND", retryable: false, remediation: "Restore the required file or correct the supplied path.", }, FileSystemError: { code: "FILESYSTEM_ERROR", retryable: false }, FrontmatterParseError: { code: "FRONTMATTER_INVALID", retryable: false, remediation: "Correct the document frontmatter and retry the command.", }, ValidationFailedError: { code: "VALIDATION_FAILED", retryable: false, remediation: "Resolve the validation issues in error.fields and retry.", }, RepositoryError: { code: "REPOSITORY_ERROR", retryable: false }, EpicError: { code: "EPIC_ERROR", retryable: false }, TaskError: { code: "TASK_ERROR", retryable: false }, PhaseError: { code: "PHASE_ERROR", retryable: false }, RevisionConflictError: { code: "REVISION_CONFLICT", retryable: true, remediation: "Read the current document revision and retry intentionally.", }, ArchiveError: { code: "ARCHIVE_ERROR", retryable: false }, WorktreeError: { code: "WORKTREE_ERROR", retryable: false }, PushError: { code: "PUSH_ERROR", retryable: false }, RebaseError: { code: "REBASE_ERROR", retryable: false }, RebaseConflictError: { code: "REBASE_CONFLICT", retryable: false, remediation: "Resolve and stage the conflicts in error.fields.checkoutPath, then run error.fields.continueCommand or error.fields.abortCommand.", }, PullRequestError: { code: "PULL_REQUEST_ERROR", retryable: false }, ReviewError: { code: "REVIEW_ERROR", retryable: false }, ContextError: { code: "CONTEXT_ERROR", retryable: false, remediation: "Run the command from an Agency entity or provide a valid target.", }, ItemSelectorError: { code: "ITEM_SELECTOR_INVALID", retryable: false, remediation: "Provide an item ID or a path inside the intended epic, task, or phase, or run the command from inside it.", }, GraphError: { code: "GRAPH_ERROR", retryable: false, remediation: "Correct the workbase graph data or filters and retry.", }, ExecutionGuardError: { code: "EXECUTION_BLOCKED", retryable: false, remediation: "Resolve the reported blockers or retry intentionally with --force.", }, SyncError: { code: "SYNC_ERROR", retryable: false, remediation: "Resolve workbase validation errors before reconciling.", }, BranchNameCommandError: { code: "BRANCH_NAME_COMMAND_FAILED", retryable: false, remediation: "Fix the workbase branchNameCommand or pass an explicit --branch.", }, ProcessError: { code: "PROCESS_ERROR", retryable: true }, RuntimeError: { code: "RUNTIME_ERROR", retryable: true, remediation: "Check that the runtime (Herdr) is running and retry.", }, TriageError: { code: "TRIAGE_ERROR", retryable: false, remediation: "Correct the item selector or triage prompt configuration and retry.", }, SupervisionStateLockError: { code: "SUPERVISION_STATE_LOCKED", retryable: true, remediation: "Retry; remove the reported lock file only if no Agency process is using it.", }, ProtocolOutputError: { code: "PROTOCOL_OUTPUT_ERROR", retryable: false, remediation: "Report this Agency protocol violation.", }, } class ProtocolOutputError extends Error { readonly _tag = "ProtocolOutputError" } let resultCollector: ((value: unknown) => void) | undefined const parseCommandResult = (value: unknown): unknown => { if (typeof value !== "string") return value try { return JSON.parse(value) } catch { return value } } export const emitCommandResult = (value: unknown): void => { if (resultCollector) { resultCollector(parseCommandResult(value)) return } console.log(value) } export const collectCommandResult = async ( run: () => Promise, ): Promise => { if (resultCollector) { throw new ProtocolOutputError( "A machine result collector is already active.", ) } let emitted = false let result: unknown = null const originalLog = console.log resultCollector = (value) => { if (emitted) { throw new ProtocolOutputError( "A machine command emitted more than one result.", ) } emitted = true result = value } console.log = (...values) => { resultCollector?.( parseCommandResult(values.length === 1 ? values[0] : values.join(" ")), ) } try { await run() return result } finally { console.log = originalLog resultCollector = undefined } } const errorTag = (error: unknown): string | undefined => { if (typeof error !== "object" || error === null) return undefined if ("_tag" in error && typeof error._tag === "string") return error._tag if (error instanceof Error && error.name !== "Error") return error.name return undefined } const unwrapEffectError = (error: unknown): unknown => { const seen = new Set() const visit = (value: unknown): unknown => { if (typeof value !== "object" || value === null || seen.has(value)) return null seen.add(value) if ("protocolCode" in value && typeof value.protocolCode === "string") { return value } for (const nested of [ ...Object.values(value), ...Object.getOwnPropertySymbols(value).map( (symbol) => (value as Record)[symbol], ), ]) { const found = visit(nested) if (found) return found } if ( "_tag" in value && typeof value._tag === "string" && value._tag.endsWith("Error") ) { return value } return null } return visit(error) ?? error } const errorMessage = (error: unknown): string => { if ( typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ) { return error.message } return String(error) } const errorFields = (error: unknown): Record => { if (typeof error !== "object" || error === null) return {} return Object.fromEntries( Object.entries(error).filter( ([key, value]) => !key.startsWith("_") && key !== "name" && key !== "message" && key !== "cause" && key !== "protocolCode" && key !== "retryable" && key !== "remediation" && value !== undefined, ), ) } export const successEnvelope = (result: unknown): SuccessEnvelope => ({ version: PROTOCOL_VERSION, ok: true, result: result === undefined ? null : result, }) export const errorEnvelope = (error: unknown): ErrorEnvelope => { const normalized = unwrapEffectError(error) const defaults = errorMetadata[errorTag(normalized) ?? ""] ?? { code: "COMMAND_FAILED", retryable: false, } const dynamic = typeof normalized === "object" && normalized !== null ? { ...("protocolCode" in normalized && typeof normalized.protocolCode === "string" ? { code: normalized.protocolCode } : {}), ...("retryable" in normalized && typeof normalized.retryable === "boolean" ? { retryable: normalized.retryable } : {}), ...("remediation" in normalized && typeof normalized.remediation === "string" ? { remediation: normalized.remediation } : {}), } : {} return { version: PROTOCOL_VERSION, ok: false, error: { ...defaults, ...dynamic, message: errorMessage(normalized), fields: errorFields(normalized), }, } } export const writeEnvelope = (envelope: AgencyEnvelope): void => { process.stdout.write(`${JSON.stringify(envelope)}\n`) }