fix(attachments): hydrate opencode inbox payloads

This commit is contained in:
777genius 2026-05-09 01:38:58 +03:00
parent f400fddb8f
commit b3d5dbca08

View file

@ -72,7 +72,11 @@ import {
stripCrossTeamPrefix, stripCrossTeamPrefix,
} from '@shared/constants/crossTeam'; } from '@shared/constants/crossTeam';
import { getMemberColorByName } from '@shared/constants/memberColors'; import { getMemberColorByName } from '@shared/constants/memberColors';
import { DEFAULT_TOOL_APPROVAL_SETTINGS, type AttachmentPayload } from '@shared/types/team'; import {
DEFAULT_TOOL_APPROVAL_SETTINGS,
type AttachmentMeta,
type AttachmentPayload,
} from '@shared/types/team';
import { resolveLanguageName } from '@shared/utils/agentLanguage'; import { resolveLanguageName } from '@shared/utils/agentLanguage';
import { resolveAnthropicLaunchModel } from '@shared/utils/anthropicLaunchModel'; import { resolveAnthropicLaunchModel } from '@shared/utils/anthropicLaunchModel';
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults'; import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
@ -273,6 +277,7 @@ import {
readBootstrapRuntimeState, readBootstrapRuntimeState,
} from './TeamBootstrapStateReader'; } from './TeamBootstrapStateReader';
import { TeamConfigReader } from './TeamConfigReader'; import { TeamConfigReader } from './TeamConfigReader';
import { TeamAttachmentStore } from './TeamAttachmentStore';
import { TeamInboxReader } from './TeamInboxReader'; import { TeamInboxReader } from './TeamInboxReader';
import { TeamInboxWriter } from './TeamInboxWriter'; import { TeamInboxWriter } from './TeamInboxWriter';
import { import {
@ -5558,7 +5563,8 @@ export class TeamProvisioningService {
private readonly teamMetaStore: TeamMetaStore = new TeamMetaStore(), private readonly teamMetaStore: TeamMetaStore = new TeamMetaStore(),
private readonly inboxWriter: TeamInboxWriter = new TeamInboxWriter(), private readonly inboxWriter: TeamInboxWriter = new TeamInboxWriter(),
private readonly openCodeTaskLogAttributionStore: OpenCodeTaskLogAttributionStore = new OpenCodeTaskLogAttributionStore(), private readonly openCodeTaskLogAttributionStore: OpenCodeTaskLogAttributionStore = new OpenCodeTaskLogAttributionStore(),
private readonly memberWorktreeManager: TeamMemberWorktreeManager = new TeamMemberWorktreeManager() private readonly memberWorktreeManager: TeamMemberWorktreeManager = new TeamMemberWorktreeManager(),
private readonly attachmentStore: TeamAttachmentStore = new TeamAttachmentStore()
) { ) {
this.memberLogsFinder = new TeamMemberLogsFinder( this.memberLogsFinder = new TeamMemberLogsFinder(
this.configReader, this.configReader,
@ -19972,6 +19978,76 @@ export class TeamProvisioningService {
return { kind: 'native_member_noop', relayed: 0 }; return { kind: 'native_member_noop', relayed: 0 };
} }
private async resolveOpenCodeInboxAttachmentPayloads(input: {
teamName: string;
message: InboxMessage & { messageId: string };
}): Promise<
| { ok: true; attachments?: AttachmentPayload[] }
| { ok: false; reason: string; diagnostics: string[] }
> {
const metas = input.message.attachments ?? [];
if (metas.length === 0) {
return { ok: true };
}
let fileDataById: Map<string, { data: string; mimeType: string }> | null = null;
const payloads: AttachmentPayload[] = [];
const missingIds: string[] = [];
for (const meta of metas) {
const inlinePayload = this.asOpenCodeAttachmentPayload(meta);
if (inlinePayload) {
payloads.push(inlinePayload);
continue;
}
if (!fileDataById) {
let fileData: Awaited<ReturnType<TeamAttachmentStore['getAttachments']>>;
try {
fileData = await this.attachmentStore.getAttachments(
input.teamName,
input.message.messageId
);
} catch (error) {
const reason = `opencode_inbox_attachment_payload_read_failed: ${getErrorMessage(error)}`;
return { ok: false, reason, diagnostics: [reason] };
}
fileDataById = new Map(
fileData.map((attachment) => [
attachment.id,
{ data: attachment.data, mimeType: attachment.mimeType },
])
);
}
const data = fileDataById.get(meta.id);
if (!data) {
missingIds.push(meta.id);
continue;
}
payloads.push({
...meta,
mimeType: meta.mimeType || data.mimeType,
data: data.data,
});
}
if (missingIds.length > 0) {
const reason = `opencode_inbox_attachment_payload_unavailable: ${missingIds.join(', ')}`;
return { ok: false, reason, diagnostics: [reason] };
}
return { ok: true, attachments: payloads };
}
private asOpenCodeAttachmentPayload(meta: AttachmentMeta): AttachmentPayload | null {
const data = (meta as Partial<AttachmentPayload>).data;
return typeof data === 'string' && data.length > 0
? {
...meta,
data,
}
: null;
}
async relayOpenCodeMemberInboxMessages( async relayOpenCodeMemberInboxMessages(
teamName: string, teamName: string,
memberName: string, memberName: string,
@ -20208,6 +20284,20 @@ export class TeamProvisioningService {
const effectiveTaskRefs = const effectiveTaskRefs =
existingRecord?.taskRefs ?? options.deliveryMetadata?.taskRefs ?? message.taskRefs ?? []; existingRecord?.taskRefs ?? options.deliveryMetadata?.taskRefs ?? message.taskRefs ?? [];
const effectiveSource = existingRecord?.source ?? options.source ?? 'watcher'; const effectiveSource = existingRecord?.source ?? options.source ?? 'watcher';
const attachmentPayloads = await this.resolveOpenCodeInboxAttachmentPayloads({
teamName,
message,
});
if (!attachmentPayloads.ok) {
result.failed += 1;
result.diagnostics = [...(result.diagnostics ?? []), ...attachmentPayloads.diagnostics];
result.lastDelivery = {
delivered: false,
reason: attachmentPayloads.reason,
diagnostics: attachmentPayloads.diagnostics,
};
break;
}
result.attempted += 1; result.attempted += 1;
const delivery = await this.deliverOpenCodeMemberMessage(teamName, { const delivery = await this.deliverOpenCodeMemberMessage(teamName, {
memberName, memberName,
@ -20217,7 +20307,7 @@ export class TeamProvisioningService {
actionMode: effectiveActionMode ?? undefined, actionMode: effectiveActionMode ?? undefined,
messageKind: message.messageKind, messageKind: message.messageKind,
taskRefs: effectiveTaskRefs, taskRefs: effectiveTaskRefs,
attachments: message.attachments, attachments: attachmentPayloads.attachments,
source: effectiveSource, source: effectiveSource,
inboxTimestamp: message.timestamp, inboxTimestamp: message.timestamp,
}); });