1- import type { AcpTurnAttachment } from "../../acp/control-plane/manager.types.js" ;
21import { resolveAcpAgentPolicyError , resolveAcpDispatchPolicyError } from "../../acp/policy.js" ;
32import { formatAcpRuntimeErrorText } from "../../acp/runtime/error-text.js" ;
43import { toAcpRuntimeError } from "../../acp/runtime/errors.js" ;
@@ -18,15 +17,13 @@ import { resolveStatusTtsSnapshot } from "../../tts/status-config.js";
1817import { resolveConfiguredTtsMode } from "../../tts/tts-config.js" ;
1918import type { FinalizedMsgContext } from "../templating.js" ;
2019import { createAcpReplyProjector } from "./acp-projector.js" ;
20+ import { loadDispatchAcpMediaRuntime , resolveAcpAttachments } from "./dispatch-acp-attachments.js" ;
2121import {
2222 createAcpDispatchDeliveryCoordinator ,
2323 type AcpDispatchDeliveryCoordinator ,
2424} from "./dispatch-acp-delivery.js" ;
2525import type { ReplyDispatcher , ReplyDispatchKind } from "./reply-dispatcher.js" ;
2626
27- let dispatchAcpMediaRuntimePromise : Promise <
28- typeof import ( "./dispatch-acp-media.runtime.js" )
29- > | null = null ;
3027let dispatchAcpManagerRuntimePromise : Promise <
3128 typeof import ( "./dispatch-acp-manager.runtime.js" )
3229> | null = null ;
@@ -36,11 +33,6 @@ let dispatchAcpSessionRuntimePromise: Promise<
3633let dispatchAcpTtsRuntimePromise : Promise < typeof import ( "./dispatch-acp-tts.runtime.js" ) > | null =
3734 null ;
3835
39- function loadDispatchAcpMediaRuntime ( ) {
40- dispatchAcpMediaRuntimePromise ??= import ( "./dispatch-acp-media.runtime.js" ) ;
41- return dispatchAcpMediaRuntimePromise ;
42- }
43-
4436function loadDispatchAcpManagerRuntime ( ) {
4537 dispatchAcpManagerRuntimePromise ??= import ( "./dispatch-acp-manager.runtime.js" ) ;
4638 return dispatchAcpManagerRuntimePromise ;
@@ -99,62 +91,6 @@ function hasInboundMediaForAcp(ctx: FinalizedMsgContext): boolean {
9991 ) ;
10092}
10193
102- const ACP_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024 ;
103- const ACP_ATTACHMENT_TIMEOUT_MS = 1_000 ;
104-
105- async function resolveAcpAttachments (
106- ctx : FinalizedMsgContext ,
107- cfg : OpenClawConfig ,
108- ) : Promise < AcpTurnAttachment [ ] > {
109- if ( ! hasInboundMediaForAcp ( ctx ) ) {
110- return [ ] ;
111- }
112- const {
113- MediaAttachmentCache,
114- isMediaUnderstandingSkipError,
115- normalizeAttachments,
116- resolveMediaAttachmentLocalRoots,
117- } = await loadDispatchAcpMediaRuntime ( ) ;
118- const mediaAttachments = normalizeAttachments ( ctx ) . map ( ( attachment ) =>
119- attachment . path ?. trim ( ) ? { ...attachment , url : undefined } : attachment ,
120- ) ;
121- const cache = new MediaAttachmentCache ( mediaAttachments , {
122- localPathRoots : resolveMediaAttachmentLocalRoots ( { cfg, ctx } ) ,
123- } ) ;
124- const results : AcpTurnAttachment [ ] = [ ] ;
125- for ( const attachment of mediaAttachments ) {
126- const mediaType = attachment . mime ?? "application/octet-stream" ;
127- if ( ! mediaType . startsWith ( "image/" ) ) {
128- continue ;
129- }
130- if ( ! attachment . path ?. trim ( ) ) {
131- continue ;
132- }
133- try {
134- const { buffer } = await cache . getBuffer ( {
135- attachmentIndex : attachment . index ,
136- maxBytes : ACP_ATTACHMENT_MAX_BYTES ,
137- timeoutMs : ACP_ATTACHMENT_TIMEOUT_MS ,
138- } ) ;
139- results . push ( {
140- mediaType,
141- data : buffer . toString ( "base64" ) ,
142- } ) ;
143- } catch ( error ) {
144- if ( isMediaUnderstandingSkipError ( error ) ) {
145- logVerbose ( `dispatch-acp: skipping attachment #${ attachment . index + 1 } (${ error . reason } )` ) ;
146- } else {
147- const errorName = error instanceof Error ? error . name : typeof error ;
148- logVerbose (
149- `dispatch-acp: failed to read attachment #${ attachment . index + 1 } (${ errorName } )` ,
150- ) ;
151- }
152- // Skip unreadable files. Text content should still be delivered.
153- }
154- }
155- return results ;
156- }
157-
15894function resolveAcpRequestId ( ctx : FinalizedMsgContext ) : string {
15995 const id = ctx . MessageSidFull ?? ctx . MessageSid ?? ctx . MessageSidFirst ?? ctx . MessageSidLast ;
16096 if ( typeof id === "string" && id . trim ( ) ) {
@@ -492,7 +428,9 @@ export async function tryDispatchAcpReply(params: {
492428 }
493429
494430 const promptText = resolveAcpPromptText ( params . ctx ) ;
495- const attachments = await resolveAcpAttachments ( params . ctx , params . cfg ) ;
431+ const attachments = hasInboundMediaForAcp ( params . ctx )
432+ ? await resolveAcpAttachments ( { ctx : params . ctx , cfg : params . cfg } )
433+ : [ ] ;
496434 if ( ! promptText && attachments . length === 0 ) {
497435 const counts = params . dispatcher . getQueuedCounts ( ) ;
498436 delivery . applyRoutedCounts ( counts ) ;
0 commit comments