agent-ecosystem/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts

260 lines
10 KiB
TypeScript

import type { SendMessageResult } from '@shared/types';
export interface OpenCodeRuntimeDeliveryDebugDetails {
messageId: string;
statusMessageId?: string;
providerId: string;
delivered: boolean | null;
responsePending: boolean | null;
responseState: string | null;
ledgerStatus: string | null;
visibleReplyMessageId?: string | null;
visibleReplyCorrelation?: string | null;
queuedBehindMessageId?: string | null;
acceptanceUnknown: boolean | null;
reason: string | null;
diagnostics: string[];
userVisibleState?: string | null;
userVisibleReasonCode?: string | null;
userVisibleMessage?: string | null;
userVisibleNextReviewAt?: string | null;
}
interface OpenCodeRuntimeDeliveryDiagnostics {
warning: string | null;
debugDetails: OpenCodeRuntimeDeliveryDebugDetails | null;
}
const PENDING_WARNING =
'OpenCode delivery is still being checked. Message was saved and will be observed before retry if needed.';
const PROOF_WARNING =
'OpenCode reply could not be verified. Message was saved to inbox, but no visible reply or task progress proof was found.';
const FAILED_WARNING =
'OpenCode runtime delivery failed. Message was saved to inbox, but live delivery did not complete.';
const ATTACHMENT_FAILED_WARNING =
'OpenCode attachment was not sent. Message was saved to inbox, but live delivery cannot include this attachment.';
function isOpenCodeAttachmentDeliveryFailureReason(reason: string | null | undefined): boolean {
const normalized = reason?.trim().toLowerCase();
return (
normalized === 'opencode_attachment_delivery_prepare_failed' ||
normalized?.startsWith('attachment_') === true ||
normalized?.startsWith('opencode_attachment_delivery_prepare_failed:') === true
);
}
function formatOpenCodeRuntimeDeliveryFailureReason(reason: string | null | undefined): string {
const normalized = reason?.trim();
if (!normalized) {
return '';
}
const normalizedLower = normalized.toLowerCase();
if (normalizedLower === 'empty_assistant_turn') {
return 'OpenCode returned an empty assistant turn.';
}
if (normalizedLower === 'prompt_delivered_no_assistant_message') {
return 'OpenCode accepted the prompt, but no assistant turn was recorded.';
}
if (
normalizedLower === 'visible_reply_still_required' ||
normalizedLower === 'visible_reply_ack_only_still_requires_answer' ||
normalizedLower === 'plain_text_ack_only_still_requires_answer'
) {
return 'OpenCode responded, but did not create a visible message_send reply.';
}
if (
normalizedLower === 'visible_reply_destination_not_found_yet' ||
normalizedLower === 'visible_reply_missing_relayofmessageid'
) {
return 'OpenCode created a reply without the required relayOfMessageId correlation.';
}
if (normalizedLower === 'visible_reply_missing_task_refs') {
return 'OpenCode created a reply without the required taskRefs metadata.';
}
if (normalizedLower === 'visible_reply_missing_task_refs_after_merge') {
return 'OpenCode created a reply without the required taskRefs metadata.';
}
if (normalizedLower === 'visible_reply_task_refs_merge_failed') {
return 'OpenCode created a reply without the required taskRefs metadata, and the app could not attach it automatically.';
}
if (normalizedLower === 'non_visible_tool_without_task_progress') {
return 'OpenCode used tools, but did not create a visible reply or task progress proof.';
}
if (normalizedLower === 'attachment_model_unsupported') {
return 'This OpenCode model is not verified for image attachments. Choose a vision-capable model or remove the image.';
}
if (normalizedLower === 'attachment_type_unsupported') {
return 'This OpenCode model cannot receive this attachment type. Remove the attachment or choose a supported image model.';
}
if (normalizedLower === 'attachment_too_large') {
return 'The attachment is too large for live OpenCode delivery. Reduce the image size or remove the attachment.';
}
if (
normalizedLower === 'attachment_artifact_missing' ||
normalizedLower === 'attachment_artifact_path_unsafe'
) {
return 'The attachment file is not available for live OpenCode delivery. Reattach the file and try again.';
}
if (normalizedLower === 'attachment_optimization_failed') {
return 'The attachment could not be optimized for live OpenCode delivery. Try a smaller image or remove the attachment.';
}
if (normalizedLower === 'attachment_provider_rejected') {
return 'The OpenCode provider rejected the attachment. Choose a different model or remove the attachment.';
}
if (normalizedLower === 'attachment_runtime_transport_failed') {
return 'OpenCode could not transport the attachment to the runtime. Try again or remove the attachment.';
}
if (normalizedLower.startsWith('opencode_attachment_delivery_prepare_failed:')) {
return normalized.slice('opencode_attachment_delivery_prepare_failed:'.length).trim();
}
return '';
}
export function buildOpenCodeRuntimeDeliveryDiagnostics(
result: SendMessageResult
): OpenCodeRuntimeDeliveryDiagnostics {
const runtimeDelivery = result.runtimeDelivery;
if (runtimeDelivery?.attempted !== true) {
return { warning: null, debugDetails: null };
}
const userVisibleState = runtimeDelivery.userVisibleImpact?.state;
const isFailed =
userVisibleState === 'error' || (!userVisibleState && runtimeDelivery.delivered === false);
const isWarning = userVisibleState === 'warning';
const isPending =
userVisibleState === 'checking' ||
(!userVisibleState && runtimeDelivery.responsePending === true);
if (!isFailed && !isPending) {
if (!isWarning) {
return { warning: null, debugDetails: null };
}
}
const userVisibleMessage = runtimeDelivery.userVisibleImpact?.message?.trim();
const candidateFailureReason =
userVisibleMessage ?? runtimeDelivery.reason ?? runtimeDelivery.diagnostics?.[0];
const mappedFailureReason =
isFailed || isWarning ? formatOpenCodeRuntimeDeliveryFailureReason(candidateFailureReason) : '';
const failureReason = mappedFailureReason || (isFailed || isWarning ? userVisibleMessage : '');
const isAttachmentFailure =
isFailed &&
(isOpenCodeAttachmentDeliveryFailureReason(runtimeDelivery.reason) ||
isOpenCodeAttachmentDeliveryFailureReason(runtimeDelivery.diagnostics?.[0]) ||
isOpenCodeAttachmentDeliveryFailureReason(candidateFailureReason));
const statusMessageId = runtimeDelivery.queuedBehindMessageId ?? result.messageId;
return {
warning:
isWarning && failureReason
? `${PROOF_WARNING} Reason: ${failureReason}`
: isWarning
? PROOF_WARNING
: isAttachmentFailure && failureReason
? `${ATTACHMENT_FAILED_WARNING} Reason: ${failureReason}`
: isAttachmentFailure
? ATTACHMENT_FAILED_WARNING
: isFailed && failureReason
? `${FAILED_WARNING} Reason: ${failureReason}`
: isFailed
? FAILED_WARNING
: PENDING_WARNING,
debugDetails: {
messageId: result.messageId,
statusMessageId,
providerId: runtimeDelivery.providerId,
delivered: typeof runtimeDelivery.delivered === 'boolean' ? runtimeDelivery.delivered : null,
responsePending:
typeof runtimeDelivery.responsePending === 'boolean'
? runtimeDelivery.responsePending
: null,
responseState: runtimeDelivery.responseState ?? null,
ledgerStatus: runtimeDelivery.ledgerStatus ?? null,
visibleReplyMessageId: runtimeDelivery.visibleReplyMessageId ?? null,
visibleReplyCorrelation: runtimeDelivery.visibleReplyCorrelation ?? null,
queuedBehindMessageId: runtimeDelivery.queuedBehindMessageId ?? null,
acceptanceUnknown:
typeof runtimeDelivery.acceptanceUnknown === 'boolean'
? runtimeDelivery.acceptanceUnknown
: null,
reason: runtimeDelivery.reason ?? null,
diagnostics: runtimeDelivery.diagnostics ?? [],
userVisibleState: runtimeDelivery.userVisibleImpact?.state ?? null,
userVisibleReasonCode: runtimeDelivery.userVisibleImpact?.reasonCode ?? null,
userVisibleMessage: runtimeDelivery.userVisibleImpact?.message ?? null,
userVisibleNextReviewAt: runtimeDelivery.userVisibleImpact?.nextReviewAt ?? null,
},
};
}
export function isOpenCodeRuntimeDeliveryHardUxFailure(
runtimeDelivery: SendMessageResult['runtimeDelivery'] | null | undefined
): boolean {
if (runtimeDelivery?.attempted !== true) {
return false;
}
const userVisibleState = runtimeDelivery.userVisibleImpact?.state;
if (userVisibleState) {
return userVisibleState === 'error';
}
return runtimeDelivery.delivered === false;
}
export function isOpenCodeRuntimeDeliveryHardUxFailureFromDebugDetails(
details: OpenCodeRuntimeDeliveryDebugDetails | null | undefined
): boolean {
if (!details) {
return false;
}
if (details.userVisibleState) {
return details.userVisibleState === 'error';
}
return details.delivered === false;
}
export function shouldClearPendingReplyForOpenCodeRuntimeDelivery(
runtimeDelivery: SendMessageResult['runtimeDelivery'] | null | undefined
): boolean {
if (runtimeDelivery?.attempted !== true) {
return false;
}
const userVisibleState = runtimeDelivery.userVisibleImpact?.state;
if (userVisibleState === 'none') {
return true;
}
if (userVisibleState === 'warning' || userVisibleState === 'error') {
return true;
}
if (userVisibleState === 'checking') {
return false;
}
return runtimeDelivery.responsePending !== true;
}
export function formatOpenCodeRuntimeDeliveryDebugDetails(
details: OpenCodeRuntimeDeliveryDebugDetails
): string {
return JSON.stringify(
{
messageId: details.messageId,
statusMessageId: details.statusMessageId,
providerId: details.providerId,
delivered: details.delivered,
responsePending: details.responsePending,
responseState: details.responseState,
ledgerStatus: details.ledgerStatus,
visibleReplyMessageId: details.visibleReplyMessageId,
visibleReplyCorrelation: details.visibleReplyCorrelation,
queuedBehindMessageId: details.queuedBehindMessageId,
acceptanceUnknown: details.acceptanceUnknown,
reason: details.reason,
diagnostics: details.diagnostics,
userVisibleState: details.userVisibleState,
userVisibleReasonCode: details.userVisibleReasonCode,
userVisibleMessage: details.userVisibleMessage,
userVisibleNextReviewAt: details.userVisibleNextReviewAt,
},
null,
2
);
}