beeos-claw 0.6.54-staging.9 → 0.6.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{logChatLatency}from"./chat-latency.js";const normalizePromptTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,a)=>`${e}_${createHash("sha256").update(`${n}:${t}:${a}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:a,logger:r,promptRunStore:i,assistantMessageCommitRouter:s,toolResultObservationRouter:o,modelCallObservationRouter:c,sessionMutationCoordinator:d,abortTransport:l,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return r.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=randomUUID(),I=Date.now();logChatLatency(r,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=normalizePromptTimeoutMs(e.promptTimeoutMs),_=async(n,a)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,a)}catch(e){r.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},f=promptRunKey(t.channelId,t.inboundMessageId),w=t.sessionKey??"agent:main",R=new AbortController;let b,E,M;const $=new Promise((e,n)=>{M=n});let S;const v=()=>{void 0!==S&&(clearTimeout(S),S=void 0)},C=async e=>{if(E??=e,R.abort(e),!b)return{ok:!0};const n=await d.requestCancel(b,l);return n.ok?n.terminalObservation.then(e=>{e.ok||M(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):M(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}};if(!i.register(f,{requestId:m,channelId:t.channelId,requestCancel:C,startedAt:I,sessionKey:w}))return r.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const q=deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),k=a.messages.startStream({conversationId:t.channelId,clientMessageId:q,idempotencyKey:q,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",content:{}});let O,T,L=!1;try{const e=await k.opened();L="duplicate"===e.outcome,logChatLatency(r,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let s="unconfirmed";try{s=(await a.messages.getEnvelope(t.channelId,q)).state}catch{}return r.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${q} reconciled_state=${s}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(f),!1}if(L)return r.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(f),!1;const A=new SerializedCallbackQueue,D=createImReplySink({stream:k,openReasoningStream:()=>{const n=deterministicReplyMessageId("rsn",t.channelId,t.inboundMessageId,t.selfPrincipalId);return O=a.messages.startStream({conversationId:t.channelId,clientMessageId:n,idempotencyKey:n,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reasoning",content:{}}),O},abortSignal:R.signal,callbackQueue:A,logger:r,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,onAgentRunStart:e=>{d.bindRun(b,e),void 0!==T&&logChatLatency(r,"claw.runtime.pre_run_completed",m,T)}}),U=interceptAgentRuntimeFailures(D,"im_runtime",e=>r.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),N=U.sink,K=async(e,n)=>{if(!O)return!1;try{return"cancel"===e?await O.cancel({stopReason:"user_stop"}):"fail"===e?await O.fail(n?{stopReason:n}:void 0):await O.finalize({stopReason:"end_turn"}),!1}catch(e){return r.warn?.(`[im] runtime reasoning stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`),!0}};let P,F,G=!1;const Q=async e=>{N.dispatcher.markComplete(e);try{await N.dispatcher.waitForIdle()}catch{}},Y=()=>{v();const n=normalizePromptTimeoutMs(e.abortTerminalGraceMs)??1e4;S=setTimeout(()=>{S=void 0,(async()=>{if(k.isTerminated)return;const e=R.signal.reason??E,n=void 0!==F,a=!n&&"string"==typeof e&&e.startsWith("chat_cancel");if(await Q("failure"),!k.isTerminated)try{a?(await K("cancel"),await k.cancel({stopReason:"user_stop"})):n?(await K("fail","timeout"),await failMessageStream(k,{body:F?.message??"IM runtime dispatch timed out",stopReason:"timeout"})):(await K("fail"),await failMessageStream(k,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"})),r.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${a} timeout=${n}`)}catch(e){r.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};R.signal.aborted?Y():R.signal.addEventListener("abort",Y,{once:!0});const j=()=>{if(!R.signal.aborted)return;const e=R.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let z,B,V,x,W,J;const H=async e=>{if(!b)throw new Error("session_mutation_not_acquired");const n=await d.converge(b,e);return n.ok?(z=n.permit,!0):(r.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${w} reason=${n.recovery.reason}`),B={code:n.recovery.code,reason:n.recovery.reason,sessionKey:n.recovery.sessionKey,operationId:n.recovery.operationId,surface:"message_service",...n.recovery.terminalKind?{terminalKind:n.recovery.terminalKind}:{}},!1)};try{j();const l=d.tryStartInteractive({sessionKey:w,label:"im_chat_message"},"message_service");if(!l.ok){try{await failMessageStream(k,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){r.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return r.warn?.(`[im] session write gate busy sessionKey=${w} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${l.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(b=l.token,logChatLatency(r,"claw.session_gate.acquired",m,I),d.bindRun(b,p),x=s.register(w,D),W=o.register(w,D),J=c.register(p,{requestId:m,startedAtMs:I,logger:r}),u){const e=d.snapshot(w);V={runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${e?.operationId??p}`},u.activeRuns.register(V,async()=>{const e=`chat_cancel:activeRun:${V.activeRunRegistrationId}`,n=await i.abort(f,e);return n.ok||"already_aborted"===n.reason?"accepted":"terminal_committing"===n.reason||"not_found"===n.reason?"already_terminal":"recovery_required"})}j(),r.info?.(`[im] runtime dispatching requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId}),T=Date.now(),logChatLatency(r,"claw.runtime.invoke",m,I);const R=setTimeout(()=>{const e=Date.now()-T-100;e>=500&&logChatLatency(r,"claw.runtime.event_loop_lag",m,T,{lag_ms:e,phase:"pre_run"})},100);R.unref?.();const E=dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:N},onDispatchInvoked:()=>{G=!0,logChatLatency(r,"claw.runtime.dispatch_returned",m,T)}});void 0!==y&&(P=setTimeout(()=>{F=new Error(`IM runtime dispatch timed out after ${y}ms`),r.warn?.(`[im] runtime dispatch timeout requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} timeout_ms=${y}`),C(F)},y));const M=Promise.race([E,$]),S=await M.finally(()=>{P&&(clearTimeout(P),P=void 0)}),v=U.detectedFailure();if(v)throw new Error(v.diagnosticMessage);if(j(),await Q("success"),j(),!i.claimTerminal(f,"completion")){if(j(),"cancelling"===i.phase(f))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await H("completed"))return await K("fail"),await failMessageStream(k,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...B?{recovery:B}:{}}),!1;const q=N.dispatcher.getTerminalErrorText?.();if(void 0!==q){await K("fail");let n=!1;try{await failMessageStream(k,{body:q,stopReason:"error"})}catch(e){n=!0,r.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&V&&u.activeRuns.markTerminal(V,"failed"),r.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(S.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await _("",q),!1}const O=await K("complete");let L=!1;try{await k.finalize({stopReason:"end_turn"})}catch(e){L=!0,r.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&V&&u.activeRuns.markTerminal(V,"completed");const A={...S.counts,source:D.getSourceCount()};r.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(A)} elapsed_ms=${Date.now()-I}`),logChatLatency(r,"claw.turn.terminal",m,I,{outcome:L?"outcome_unknown":"completed",tool_count:A.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:A,...L?{terminalWriteFailed:!0}:{},...O?{reasoningTerminalWriteFailed:!0}:{}});const Y=N.dispatcher.getAccumulatedBody?.()??"";L||await _(Y,"");const z="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";return maybeRenameImConversationTitle({client:a,conversationId:t.channelId,question:z,answer:Y,logger:r}),!0}catch(n){await Q("failure");const a=R.signal.aborted,s=a?R.signal.reason:void 0,o="cancelling"===i.phase(f),c=void 0!==F,d=s??E,l=!c&&"string"==typeof d&&d.startsWith("chat_cancel");let p="runtime_error";c?p="timeout":(a||o)&&(p="abort");const g=o||l||c||a?"cancellation":"completion";if(!i.claimTerminal(f,g)){if(r.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!k.isTerminated)try{l?(await K("cancel"),await k.cancel({stopReason:"user_stop"})):c?(await K("fail","timeout"),await failMessageStream(k,{body:F?.message??formatErrMessage(n),stopReason:"timeout"})):(await K("fail"),await failMessageStream(k,{body:formatErrMessage(n),stopReason:"error"}))}catch(e){r.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const h=l?"cancelled":c?"timeout":"error";if(u&&V&&u.activeRuns.markTerminal(V,"cancelled"===h?"cancelled":"failed"),b&&!await H(h))throw l?(await K("cancel"),await k.cancel({stopReason:"user_stop"}).catch(()=>{})):(await K("fail"),await failMessageStream(k,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{})),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:p,...B?{recovery:B}:{}}),n;const y=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:l}),w=l?y:U.detectedFailure()??y;l?await K("cancel"):c?await K("fail","timeout"):await K("fail");let M=!1;try{l?await k.cancel({stopReason:"user_stop"}):await failMessageStream(k,{body:w.publicMessage,stopReason:messageStopReasonForRuntimeFailure(w)})}catch(e){M=!0,r.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw r.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${p} dispatchInvoked=${G} error_code=${w.errorCode}`),logChatLatency(r,"claw.turn.terminal",m,I,{outcome:p,error_code:w.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:w.errorCode,cause:p}),l||M||await _("",w.publicMessage),n}finally{v(),J?.(),W?.(),x?.(),i.unregister(f),G?z&&d.release(z):d.releaseBeforeRuntime(b)}};
1
+ import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{logChatLatency}from"./chat-latency.js";const normalizePromptTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,r)=>`${e}_${createHash("sha256").update(`${n}:${t}:${r}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:r,logger:a,promptRunStore:i,assistantMessageCommitRouter:s,toolResultObservationRouter:o,modelCallObservationRouter:c,sessionMutationCoordinator:d,abortTransport:l,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=randomUUID(),I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=normalizePromptTimeoutMs(e.promptTimeoutMs),_=async(n,r)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,r)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},f=promptRunKey(t.channelId,t.inboundMessageId),R=t.sessionKey??"agent:main",E=new AbortController;let b,w,M;const $=new Promise((e,n)=>{M=n});let S;const v=()=>{void 0!==S&&(clearTimeout(S),S=void 0)},C=async e=>{if(w??=e,E.abort(e),!b)return{ok:!0};const n=await d.requestCancel(b,l);return n.ok?n.terminalObservation.then(e=>{e.ok||M(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):M(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}};if(!i.register(f,{requestId:m,channelId:t.channelId,requestCancel:C,startedAt:I,sessionKey:R}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const q=deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),k={conversationId:t.channelId,clientMessageId:q,idempotencyKey:q,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",content:{}},O=r.messages.startStream(k);let L,T=!1;try{const e=await O.opened();T="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let s="unconfirmed";try{s=(await r.messages.getEnvelope(t.channelId,q)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${q} reconciled_state=${s}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(f),!1}if(T)return a.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(f),!1;const A=new SerializedCallbackQueue,D=createImReplySink({stream:O,abortSignal:E.signal,callbackQueue:A,logger:a,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,onAgentRunStart:e=>{d.bindRun(b,e),void 0!==L&&logChatLatency(a,"claw.runtime.pre_run_completed",m,L)}}),U=interceptAgentRuntimeFailures(D,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),N=U.sink;let K,P,F=!1;const G=async e=>{N.dispatcher.markComplete(e);try{await N.dispatcher.waitForIdle()}catch{}},Q=()=>{v();const n=normalizePromptTimeoutMs(e.abortTerminalGraceMs)??1e4;S=setTimeout(()=>{S=void 0,(async()=>{if(O.isTerminated)return;const e=E.signal.reason??w,n=void 0!==P,r=!n&&"string"==typeof e&&e.startsWith("chat_cancel");if(await G("failure"),!O.isTerminated)try{r?await O.cancel({stopReason:"user_stop"}):n?await failMessageStream(O,{body:P?.message??"IM runtime dispatch timed out",stopReason:"timeout"}):await failMessageStream(O,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${r} timeout=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};E.signal.aborted?Q():E.signal.addEventListener("abort",Q,{once:!0});const Y=()=>{if(!E.signal.aborted)return;const e=E.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let j,B,V,z,x,W;const J=async e=>{if(!b)throw new Error("session_mutation_not_acquired");const n=await d.converge(b,e);return n.ok?(j=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${R} reason=${n.recovery.reason}`),B={code:n.recovery.code,reason:n.recovery.reason,sessionKey:n.recovery.sessionKey,operationId:n.recovery.operationId,surface:"message_service",...n.recovery.terminalKind?{terminalKind:n.recovery.terminalKind}:{}},!1)};try{Y();const l=d.tryStartInteractive({sessionKey:R,label:"im_chat_message"},"message_service");if(!l.ok){try{await failMessageStream(O,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${R} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${l.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(b=l.token,logChatLatency(a,"claw.session_gate.acquired",m,I),d.bindRun(b,p),z=s.register(R,D),x=o.register(R,D),W=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const e=d.snapshot(R);V={runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${e?.operationId??p}`},u.activeRuns.register(V,async()=>{const e=`chat_cancel:activeRun:${V.activeRunRegistrationId}`,n=await i.abort(f,e);return n.ok||"already_aborted"===n.reason?"accepted":"terminal_committing"===n.reason||"not_found"===n.reason?"already_terminal":"recovery_required"})}Y(),a.info?.(`[im] runtime dispatching requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId}),L=Date.now(),logChatLatency(a,"claw.runtime.invoke",m,I);const E=setTimeout(()=>{const e=Date.now()-L-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,L,{lag_ms:e,phase:"pre_run"})},100);E.unref?.();const w=dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:N},onDispatchInvoked:()=>{F=!0,logChatLatency(a,"claw.runtime.dispatch_returned",m,L)}});void 0!==y&&(K=setTimeout(()=>{P=new Error(`IM runtime dispatch timed out after ${y}ms`),a.warn?.(`[im] runtime dispatch timeout requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} timeout_ms=${y}`),C(P)},y));const M=Promise.race([w,$]),S=await M.finally(()=>{K&&(clearTimeout(K),K=void 0)}),v=U.detectedFailure();if(v)throw new Error(v.diagnosticMessage);if(Y(),await G("success"),Y(),!i.claimTerminal(f,"completion")){if(Y(),"cancelling"===i.phase(f))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await J("completed"))return await failMessageStream(O,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...B?{recovery:B}:{}}),!1;const q=N.dispatcher.getTerminalErrorText?.();if(void 0!==q){let n=!1;try{await failMessageStream(O,{body:q,stopReason:"error"})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&V&&u.activeRuns.markTerminal(V,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(S.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await _("",q),!1}let k=!1;try{await O.finalize({stopReason:"end_turn"})}catch(e){k=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&V&&u.activeRuns.markTerminal(V,"completed");const T={...S.counts,source:D.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(T)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:k?"outcome_unknown":"completed",tool_count:T.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:T,...k?{terminalWriteFailed:!0}:{}});const A=N.dispatcher.getAccumulatedBody?.()??"";k||await _(A,"");const Q="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";return maybeRenameImConversationTitle({client:r,conversationId:t.channelId,question:Q,answer:A,logger:a}),!0}catch(n){await G("failure");const r=E.signal.aborted,s=r?E.signal.reason:void 0,o="cancelling"===i.phase(f),c=void 0!==P,d=s??w,l=!c&&"string"==typeof d&&d.startsWith("chat_cancel");let p="runtime_error";c?p="timeout":(r||o)&&(p="abort");const g=o||l||c||r?"cancellation":"completion";if(!i.claimTerminal(f,g)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!O.isTerminated)try{l?await O.cancel({stopReason:"user_stop"}):c?await failMessageStream(O,{body:P?.message??formatErrMessage(n),stopReason:"timeout"}):await failMessageStream(O,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const h=l?"cancelled":c?"timeout":"error";if(u&&V&&u.activeRuns.markTerminal(V,"cancelled"===h?"cancelled":"failed"),b&&!await J(h))throw l?await O.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(O,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:p,...B?{recovery:B}:{}}),n;const y=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:l}),R=l?y:U.detectedFailure()??y;let M=!1;try{l?await O.cancel({stopReason:"user_stop"}):await failMessageStream(O,{body:R.publicMessage,stopReason:messageStopReasonForRuntimeFailure(R)})}catch(e){M=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${p} dispatchInvoked=${F} error_code=${R.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:p,error_code:R.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:R.errorCode,cause:p}),l||M||await _("",R.publicMessage),n}finally{v(),W?.(),x?.(),z?.(),i.unregister(f),F?j&&d.release(j):d.releaseBeforeRuntime(b)}};
@@ -1 +1 @@
1
- const AROUTER_PROVIDER_ID="arouter";export const AROUTER_LATENCY_PREFERRED_MODEL="deepseek/deepseek-v4-flash";export const AROUTER_PREFERRED_P99_LATENCY_SECONDS=3;function isRecord(e){return Boolean(e)&&"object"==typeof e&&!Array.isArray(e)}export function applyArouterLatencyPreference(e){if(!isRecord(e))return e;const r=isRecord(e.provider)?e.provider:{},t=isRecord(r.preferred_max_latency)?r.preferred_max_latency:{};return{...e,provider:{...r,preferred_max_latency:{...t,p99:3}}}}export function createArouterLatencyPreferredStream(e){return(r,t,o)=>{const a=o?.onPayload;return e(r,t,{...o,onPayload:async(e,r)=>applyArouterLatencyPreference((a?await a(e,r):void 0)??e)})}}export function registerArouterLatencyRouting(e){"function"==typeof e.registerProvider?(e.registerProvider({id:"arouter",label:"ARouter",auth:[],wrapStreamFn:({modelId:e,streamFn:r})=>r&&"deepseek/deepseek-v4-flash"===e?createArouterLatencyPreferredStream(r):r}),e.logger?.info?.("[beeos-claw] Registered ARouter latency preference for deepseek/deepseek-v4-flash")):e.logger?.warn?.("[beeos-claw] registerProvider unavailable; skipping ARouter latency preference")}
1
+ const AROUTER_PROVIDER_ID="arouter";export const AROUTER_LATENCY_ROUTED_MODEL="deepseek/deepseek-v4-flash";export const AROUTER_PROVIDER_SORT="latency";function isRecord(e){return Boolean(e)&&"object"==typeof e&&!Array.isArray(e)}export function applyArouterLatencyRouting(e){if(!isRecord(e))return e;const r=isRecord(e.provider)?e.provider:{};return{...e,provider:{...r,sort:"latency"}}}export function createArouterLatencyRoutedStream(e){return(r,t,o)=>{const n=o?.onPayload;return e(r,t,{...o,onPayload:async(e,r)=>applyArouterLatencyRouting((n?await n(e,r):void 0)??e)})}}export function registerArouterLatencyRouting(e){"function"==typeof e.registerProvider?(e.registerProvider({id:"arouter",label:"ARouter",auth:[],wrapStreamFn:({modelId:e,streamFn:r})=>r&&"deepseek/deepseek-v4-flash"===e?createArouterLatencyRoutedStream(r):r}),e.logger?.info?.("[beeos-claw] Registered ARouter latency routing for deepseek/deepseek-v4-flash")):e.logger?.warn?.("[beeos-claw] registerProvider unavailable; skipping ARouter latency routing")}
@@ -0,0 +1 @@
1
+ const AROUTER_PROVIDER_ID="arouter";export const DEEPSEEK_REASONING_MODEL_ID="deepseek/deepseek-v4-flash";export const DEEPSEEK_REASONING_MODEL_KEY=`arouter/${DEEPSEEK_REASONING_MODEL_ID}`;function isRecord(e){return Boolean(e)&&"object"==typeof e&&!Array.isArray(e)}export function enableDeepseekReasoningStream(e){if(!e)return;const r=isRecord(e.models)?e.models:void 0,o=isRecord(r?.providers)?r.providers:void 0,s=isRecord(o?.arouter)?o.arouter:void 0,E=Array.isArray(s?.models)?s.models:void 0;if(!E)return e;let t=!1;const i=E.map(e=>isRecord(e)&&e.id===DEEPSEEK_REASONING_MODEL_ID?(t=!0,{...e,reasoning:!0}):e);if(!t)return e;const n=isRecord(e.agents)?e.agents:{},a=isRecord(n.defaults)?n.defaults:{},d=isRecord(a.models)?a.models:{},R=isRecord(d[DEEPSEEK_REASONING_MODEL_KEY])?d[DEEPSEEK_REASONING_MODEL_KEY]:{},D=isRecord(R.params)?R.params:{},_="string"==typeof D.thinking,c="string"==typeof a.reasoningDefault;return{...e,agents:{...n,defaults:{...a,reasoningDefault:c?a.reasoningDefault:"stream",models:{...d,[DEEPSEEK_REASONING_MODEL_KEY]:{...R,params:{...D,..._?{}:{thinking:"high"}}}}}},models:{...r,providers:{...o,[AROUTER_PROVIDER_ID]:{...s,models:i}}}}}
@@ -1 +1 @@
1
- import{toMessagePart}from"../message-client-types.js";import{resolveToolArtifactLinks}from"../upload/tool-artifact-enrichment.js";import{resolveRuntimeToolArguments,resolveRuntimeToolCallId,resolveRuntimeToolName}from"./tool-envelope-metadata.js";import{extractSearchSources,mergeSearchSource,searchSourceProvider}from"./search-source-extractor.js";import{logChatLatency}from"../chat-latency.js";const isNonEmptyString=e=>"string"==typeof e&&e.length>0,isPlainRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e);function pickRawResultFromPayload(e){if(void 0!==e.rawResult)return e.rawResult;if(void 0!==e.result)return e.result;if(void 0!==e.details)return e.details;if(void 0!==e.toolResult)return e.toolResult;if(isPlainRecord(e.channelData)){const t=e.channelData;if(void 0!==t.rawResult)return t.rawResult;if(void 0!==t.result)return t.result;if(void 0!==t.details)return t.details}}export function createImReplySink(e){const t=e.startedAtMs??Date.now();let r=!1;const n=new Map,s=new Set,o={text:""};let a="",i="",l=!1,d=!1;const u=new Set,m=[];let g,c="",p="";const f=new Map,v=new Map,h=[],y=[],I=new Map,b=new Map;let S=0,$=1;const q=new Set,_=t=>{t!==o.text&&(t.startsWith(o.text)?e.stream.appendBody(t.slice(o.text.length)):e.stream.setBody(t),o.text=t)},x=t=>{if(void 0!==t.partIndex)return;const r=S,n=$;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:n})),t.partIndex=r,S+=1,$+=1},k=e=>{const t={text:e};m.push(t),d&&x(t)},w=(t,r)=>{for(const n of t){const t=b.get(n.url);if(t){const r=mergeSearchSource(t,n);if(r.title===t.title&&r.snippet===t.snippet)continue;const s={...r,partIndex:t.partIndex,displaySeq:t.displaySeq,toolUseId:t.toolUseId};e.stream.replacePart(t.partIndex,toMessagePart({type:"source",url:s.url,...s.title?{title:s.title}:{},...s.snippet?{snippet:s.snippet}:{},tool_use_id:s.toolUseId,_seq:s.displaySeq})),b.set(n.url,s);continue}const s={...n,partIndex:S,displaySeq:$,toolUseId:r};e.stream.addPart(toMessagePart({type:"source",url:n.url,...n.title?{title:n.title}:{},...n.snippet?{snippet:n.snippet}:{},tool_use_id:r,_seq:s.displaySeq})),b.set(n.url,s),S+=1,$+=1,K.source+=1}},E=e=>{const t=h.indexOf(e);t>=0&&h.splice(t,1)},R=e=>{const t=e?h.filter(t=>f.get(t)?.name===e):h;return 1===t.length?t[0]:void 0},O=e=>{const t=y.map((e,t)=>({hint:e,index:t})).filter(({hint:t})=>!e||t.name===e);if(1!==t.length)return;const[{hint:r,index:n}]=t;return y.splice(n,1),r},T=(e,t)=>{if(v.set(e,t),e===t)return;const r=I.get(e);if(!r)return;const n=A(t);U(n,r),I.delete(e);const s=f.get(t);s&&(Q(s,n),I.delete(t))},P=e=>v.get(e)??e,M=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),A=e=>{let t=I.get(e);return t||(t={},I.set(e,t)),t},C=(e,t)=>{if(!e?.length&&!t?.length)return;const r=[],n=new Map;for(const s of[...e??[],...t??[]]){const e=n.get(s.url);void 0===e?(n.set(s.url,r.length),r.push(s)):r[e]=mergeSearchSource(r[e],s)}return r},U=(e,t)=>{e.name??=t.name,e.arguments??=t.arguments,t.toolStartObserved&&(e.toolStartObserved=!0),t.itemEventObserved&&(e.itemEventObserved=!0),t.progress?.length&&(e.progress=[...e.progress??[],...t.progress]),e.sources=C(e.sources,t.sources)},N=(t,r,n)=>{const s="tool"===t.name&&"tool"!==r?r:t.name,o=!M(t.arguments)&&M(n)?n:t.arguments;s===t.name&&o===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:s,arguments:o,state:t.state,_seq:t.displaySeq})),t.name=s,t.arguments=o)},j=(t,r,n,s)=>{const o=f.get(t);if(o)return N(o,r,n),s&&!h.includes(t)&&"streaming"===o.state&&h.push(t),o;(()=>{if(!d){d=!0;for(const e of m)x(e)}})();const a=I.get(t),i="tool"!==r?r:a?.name&&"tool"!==a.name?a.name:r,l=M(n)?n:a?.arguments??n,u=((t,r,n)=>{const s=S,o=$;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:n,state:"streaming",_seq:o})),S+=1,$+=1,{partIndex:s,displaySeq:o}})(t,i,l),g={id:t,name:i,arguments:l,partIndex:u.partIndex,displaySeq:u.displaySeq,state:"streaming",itemEventObserved:!0===a?.itemEventObserved,toolStartObserved:!0===a?.toolStartObserved};return f.set(t,g),(s||g.toolStartObserved||g.itemEventObserved)&&(h.includes(t)||"streaming"!==g.state||h.push(t)),a&&Q(g,a),I.delete(t),g},L=(e,t)=>{const r=(e=>{const t=v.get(e);return t?f.get(t):f.get(e)})(e);if(r)return void 0===t.name&&void 0===t.arguments||N(r,t.name??r.name,t.arguments??r.arguments),t.toolStartObserved&&(r.toolStartObserved=!0),t.itemEventObserved&&(r.itemEventObserved=!0),(t.pending||t.toolStartObserved||t.itemEventObserved)&&!h.includes(r.id)&&"streaming"===r.state&&h.push(r.id),r;const n=A(e);void 0!==t.name&&(n.name=t.name),void 0!==t.arguments&&(n.arguments=t.arguments),t.toolStartObserved&&(n.toolStartObserved=!0),t.itemEventObserved&&(n.itemEventObserved=!0)},F=t=>{"done"!==t.state&&(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:t.name,arguments:t.arguments,state:"done",_seq:t.displaySeq})),t.state="done")},B=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(B).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${B(t)}`).join(",")}}`},D=(t,r,n)=>{const s=`${n?"1":"0"}:${B(r)}`;if(t.progressSignature===s)return;const o=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:t.id,content:r,...n?{is_error:!0}:{}}});if(void 0===t.progressPartIndex){const r=S;return e.stream.addPart(o),t.progressPartIndex=r,t.progressSignature=s,void(S+=1)}e.stream.replacePart(t.progressPartIndex,o),t.progressSignature=s},Q=(e,t)=>{if(t.progress?.length){for(const r of t.progress)D(e,r.content,r.isError);F(e),E(e.id)}t.sources?.length&&w(t.sources,e.id)},z=e=>[e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)),W=(e,t=[])=>{const r=[...new Set([...z(e),...t])];return isNonEmptyString(e.text)&&r.length>0?{text:e.text,media_urls:r}:isNonEmptyString(e.text)?e.text:1===r.length?r[0]:r.length>1?{media_urls:r}:!0===e.isError?"Tool execution failed.":""},J=(t,r)=>{if(t.durableUploadFilesEmitted)return;e.logger.info?.(`[im-sink] schedule tool artifact enrichment requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`);const n=pickRawResultFromPayload(r),s=(async()=>{let s=[];try{s=await resolveToolArtifactLinks({toolCallId:t.id,toolName:t.name,rawResult:n,rawText:"string"==typeof r.text?r.text:void 0,payloadMediaUrls:z(r)})}catch(r){const n=r instanceof Error?r.message:String(r);return void e.logger.warn?.(`[im-sink] resolveToolArtifactLinks failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)}0!==s.length?(e.logger.info?.(`[im-sink] tool artifact enrichment resolved ${s.length} link(s) requestId=${e.requestId} toolUseId=${t.id}`),await new Promise(n=>{e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const n=s.map(e=>e.uri);D(t,W(r,n),!0===r.isError),((t,r,n)=>{if(0!==r.length&&!t.durableUploadFilesEmitted){t.durableUploadFilesEmitted=!0,e.stream.appendToolResult(t.id,r.map(e=>({type:"resource_link",uri:e.uri,name:e.name,...e.mimeType?{mimeType:e.mimeType}:{}})),n),S+=1;for(const t of r)e.stream.addPart(toMessagePart({type:"resource_link",uri:t.uri,name:t.name,...t.mimeType?{mimeType:t.mimeType}:{}})),S+=1}})(t,s,!0===r.isError)}catch(r){const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment apply failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)}finally{n()}})})):e.logger.debug?.(`[im-sink] tool artifact enrichment found no links requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`)})().catch(r=>{const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)});q.add(s),s.finally(()=>{q.delete(s)})},G=(e,t,r)=>{const n=v.get(e);if(n)return L(n,{name:t,arguments:r});const s=L(e,{name:t,arguments:r});if(s)return s;const o=R(t);if(o)return T(e,o),L(o,{name:t,arguments:r});L(e,{name:t,arguments:r})},H=(e,t)=>{const r=v.get(e);if(r)return L(r,{name:t,itemEventObserved:!0,pending:!0});const n=L(e,{name:t,itemEventObserved:!0,pending:!0});if(n)return n;const s=(e=>{const t=h.filter(t=>{const r=f.get(t);return!(void 0===r||e&&r.name!==e||r.itemEventObserved)});return 1===t.length?t[0]:void 0})(t);if(s)return T(e,s),L(s,{name:t,itemEventObserved:!0,pending:!0});const o=O(t);L(e,{name:t,arguments:o?.arguments,itemEventObserved:!0,pending:!0})},K={tool:0,block:0,final:0,source:0};let V,X=!1;const Y=(t,r)=>{e.callbackQueue.enqueueSync(()=>{if((t=>e.abortSignal.aborted?(e.logger.debug?.(`[im-sink] ${t} ignored after abort requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):e.stream.isTerminated?(e.logger.debug?.(`[im-sink] ${t} ignored after stream_terminated requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):!X||(e.logger.debug?.(`[im-sink] ${t} ignored after mark_complete requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1))(t))try{r()}catch(r){const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] ${t} mapping failed requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId} error=${n}`)}})};return{dispatcher:{sendToolResult:t=>(Y("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),n=resolveRuntimeToolName(t),s=resolveRuntimeToolArguments(t),o=W(t),a=!0===t.isError;let i;if(r){if(i=G(r,n,s),!i){const e=A(r);return n&&(e.name=n),M(s)&&(e.arguments=s),e.progress=[...e.progress??[],{content:o,isError:a}],void(K.tool+=1)}}else{const t=n?R(n):R();if(!t)return O(n),void e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${n??"unknown"} pending=${h.length}`);i=L(t,{name:n,arguments:s})}i&&(K.tool+=1,D(i,o,a),F(i),E(i.id),J(i,t))}),!0),sendBlockReply:e=>(Y("block_reply",()=>{K.block+=1,isNonEmptyString(e.text)&&(V=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:e=>(Y("final_reply",()=>{var t;(K.final+=1,isNonEmptyString(e.text))&&(!0!==e.isError?(V=void 0,l||(t=e.text,i="",a+=t,_(a),k(t),l=!0)):V=e.text)}),!0),waitForIdle:async()=>{for(let t=0;t<16;t++){if(await e.callbackQueue.waitForIdle(),0===q.size)return;await Promise.allSettled([...q])}await e.callbackQueue.waitForIdle()},getQueuedCounts:()=>({tool:K.tool,block:K.block,final:K.final}),getAccumulatedBody:()=>o.text,getTerminalErrorText:()=>V,markComplete:t=>{(y.length>0||I.size>0)&&e.logger.debug?.(`[im-sink] dropping uncommitted lifecycle tool signals requestId=${e.requestId} legacy_hints=${y.length} buffered=${I.size}`),i="",_(a);for(const r of f.values())try{"success"===t&&F(r),E(r.id)}catch(t){const n=t instanceof Error?t.message:String(t);e.logger.warn?.(`[im-sink] tool finalization failed requestId=${e.requestId} toolUseId=${r.id} error=${n}`)}y.length=0,I.clear(),X=!0}},replyOptions:{...e.runId?{runId:e.runId}:{},abortSignal:e.abortSignal,disableBlockStreaming:!0,onAgentRunStart:r=>{e.onAgentRunStart?.(r),logChatLatency(e.logger,"claw.runtime.run_started",e.requestId,t),e.logger.info?.(`[im-sink] run_start requestId=${e.requestId} runId=${r} channel=${e.channelId} in_reply_to=${e.inboundMessageId} origin=${e.origin??"im"}`)},onPartialReply:n=>{Y("partial",()=>{"string"==typeof n.text&&(!r&&n.text.length>0&&(r=!0,logChatLatency(e.logger,"claw.model.first_delta",e.requestId,t)),i=n.text,_(a+i))})},onReasoningStream:t=>{Y("reasoning",()=>{if(!isNonEmptyString(t.text))return;if(!g&&(g=e.openReasoningStream?.(),!g))return;const r=t.text;""===p&&""!==c&&(g.appendBody("\n"),c+="\n"),r!==p&&(r.startsWith(p)?(g.appendBody(r.slice(p.length)),p=r):p.startsWith(r)||(g.setBody(c+r),p=r))})},onReasoningEnd:()=>{Y("reasoning_end",()=>{var t;""!==p&&(t=p,e.stream.appendThinking(t,"done"),S+=1,c+=p,p="")})},onAssistantMessageStart:()=>{Y("assistant_message_start",()=>{i="",l=!1,_(a),e.logger.debug?.(`[im-sink] assistant_message_start requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`)})},onToolStart:r=>{Y("tool_start",()=>{const s="string"==typeof r.phase?r.phase:"start",o=resolveRuntimeToolName(r)??"tool",a=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);if("update"===s)return void(a&&L(P(a),{name:o,arguments:i,pending:!0}));if(a&&n.set(a,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:o}),a){L(a,{name:o,arguments:i,toolStartObserved:!0,pending:!0});const e=R(o);return void(e&&e!==a&&!v.has(a)&&(T(a,e),L(e,{name:o,arguments:i,toolStartObserved:!0,pending:!0})))}const l=(d=o,h.map(e=>f.get(e)).filter(e=>void 0!==e&&(!d||e.name===d)&&e.itemEventObserved));var d;if(1===l.length)return void L(l[0].id,{name:o,arguments:i,toolStartObserved:!0,pending:!0});if(l.length>1)return void e.logger.debug?.(`[im-sink] ambiguous id-less tool start ignored requestId=${e.requestId} channel=${e.channelId} tool=${o} candidates=${l.length}`);const u=[...I.entries()].filter(([,e])=>!e.name||e.name===o||"tool"===e.name);1!==u.length?y.push({name:o,arguments:i}):L(u[0][0],{name:o,arguments:i,toolStartObserved:!0,pending:!0})})},onItemEvent:e=>{Y("item_event",()=>{const t="string"==typeof e.kind?e.kind.toLowerCase():"";if(t&&"tool"!==t)return;const r="string"==typeof e.phase?e.phase.toLowerCase():"update",n=resolveRuntimeToolCallId(e)??("string"==typeof e.itemId&&e.itemId.trim()?e.itemId.trim():void 0),s=resolveRuntimeToolName(e)??("string"==typeof e.title&&e.title.trim()?e.title.trim():void 0);n&&s&&("update"!==r?H(n,s):L(P(n),{name:s,itemEventObserved:!0,pending:!0}))})}},commitAssistantMessage:e=>{Y("assistant_message_commit",()=>{if(u.has(e.key))return;u.add(e.key),l=!0,V=void 0,i="";const t=e.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");a+=t,_(a);for(const t of e.segments)"text"!==t.type?j(t.id,t.name,t.arguments,!0):k(t.text)})},observeToolResult:r=>{Y("tool_result_observation",()=>{const o=r.toolCallId.trim(),a=r.toolName.trim();if(!o||!a)return;if(!s.has(o)){s.add(o);const i=n.get(o);logChatLatency(e.logger,"claw.tool.result",e.requestId,t,{tool:a,tool_elapsed_ms:void 0===i?void 0:Math.max(0,Date.now()-i),outcome:r.isError?"error":"success"})}const i=searchSourceProvider(r.rawResult)??"unknown";let l=0;try{const t=extractSearchSources({toolName:a,rawResult:r.rawResult,isError:r.isError});if(l=t.length,0===l)return;const n=G(o,a);if(n)w(t,n.id);else{const e=A(P(o));e.name??=a,e.sources=C(e.sources,t)}e.logger.debug?.(`[im-sink] search source projection requestId=${e.requestId} tool=${a} provider=${i} source_count=${l}`)}catch{e.logger.warn?.(`[im-sink] search source projection ignored requestId=${e.requestId} tool=${a} provider=${i} source_count=${l}`)}})},getSourceCount:()=>K.source}}
1
+ import{toMessagePart}from"../message-client-types.js";import{resolveToolArtifactLinks}from"../upload/tool-artifact-enrichment.js";import{resolveRuntimeToolArguments,resolveRuntimeToolCallId,resolveRuntimeToolName}from"./tool-envelope-metadata.js";import{extractSearchSources,mergeSearchSource,searchSourceProvider}from"./search-source-extractor.js";import{logChatLatency}from"../chat-latency.js";const STREAMING_SNAPSHOT_PUBLISH_INTERVAL_MS=200,isNonEmptyString=e=>"string"==typeof e&&e.length>0,isPlainRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e);function pickRawResultFromPayload(e){if(void 0!==e.rawResult)return e.rawResult;if(void 0!==e.result)return e.result;if(void 0!==e.details)return e.details;if(void 0!==e.toolResult)return e.toolResult;if(isPlainRecord(e.channelData)){const t=e.channelData;if(void 0!==t.rawResult)return t.rawResult;if(void 0!==t.result)return t.result;if(void 0!==t.details)return t.details}}export function createImReplySink(e){const t=e.startedAtMs??Date.now();let r=!1;const n=new Map,s=new Set,o={text:""};let a="",i="",l=!1,d=!1;const u=new Set;let m;const g={receivedSnapshotCount:0,streamingPublishCount:0,terminalPublishCount:0},c=[];let p;const h=new Map,v=new Map,f=[],b=[],y=new Map,_=new Map;let I=0,S=1;const x=(e,t={})=>({schema_version:1,seq:e,...t}),$=new Set,q=t=>{t!==o.text&&(t.startsWith(o.text)?e.stream.appendBody(t.slice(o.text.length)):e.stream.setBody(t),o.text=t)},w=e=>{void 0===e.publishTimer&&(e.publishTimer=setTimeout(()=>{e.publishTimer=void 0,oe("body_preview_flush",()=>{m===e&&(e=>{o.text!==e.targetBody&&(q(e.targetBody),e.streamingPublishCount+=1)})(e)})},200))},P=e=>{const t=m;t?(void 0!==t.publishTimer&&(clearTimeout(t.publishTimer),t.publishTimer=void 0),t.targetBody=e,o.text!==e&&(q(e),t.terminalPublishCount+=1),g.receivedSnapshotCount+=t.receivedSnapshotCount,g.streamingPublishCount+=t.streamingPublishCount,g.terminalPublishCount+=t.terminalPublishCount,m=void 0):q(e)},T=t=>{if(void 0!==t.partIndex)return;const r=I,n=S;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:n,data:x(n)})),t.partIndex=r,I+=1,S+=1},k=()=>{if(!d){d=!0;for(const e of c)T(e)}},C=e=>{const t={text:e};c.push(t),d&&T(t)},E=(t,r)=>{for(const n of t){const t=_.get(n.url);if(t){const r=mergeSearchSource(t,n);if(r.title===t.title&&r.snippet===t.snippet)continue;const s={...r,partIndex:t.partIndex,displaySeq:t.displaySeq,toolUseId:t.toolUseId};e.stream.replacePart(t.partIndex,toMessagePart({type:"source",url:s.url,...s.title?{title:s.title}:{},...s.snippet?{snippet:s.snippet}:{},tool_use_id:s.toolUseId,_seq:s.displaySeq,data:x(s.displaySeq)})),_.set(n.url,s);continue}const s={...n,partIndex:I,displaySeq:S,toolUseId:r};e.stream.addPart(toMessagePart({type:"source",url:n.url,...n.title?{title:n.title}:{},...n.snippet?{snippet:n.snippet}:{},tool_use_id:r,_seq:s.displaySeq,data:x(s.displaySeq)})),_.set(n.url,s),I+=1,S+=1,re.source+=1}},M=(e,t,r,n)=>toMessagePart({type:"thinking",text:t,state:r,_seq:e.displaySeq,_started_at_ms:e.startedAtMs,...void 0!==n?{_duration_ms:n}:{},data:x(e.displaySeq,{started_at_ms:e.startedAtMs,...void 0!==n?{duration_ms:n}:{}})}),R=t=>{void 0===t.publishTimer&&(t.publishTimer=setTimeout(()=>{t.publishTimer=void 0,oe("reasoning_snapshot_flush",()=>{p===t&&(t=>{t.publishedText!==t.text&&(e.stream.replacePart(t.partIndex,M(t,t.text,"streaming")),t.publishedText=t.text,t.streamingPublishCount+=1)})(t)})},200))},O=()=>{const r=p;if(!r)return;void 0!==r.publishTimer&&(clearTimeout(r.publishTimer),r.publishTimer=void 0);const n=Math.max(0,Math.round(performance.now()-r.startedAtMonotonicMs));e.stream.replacePart(r.partIndex,M(r,r.text,"done",n)),p=void 0,logChatLatency(e.logger,"claw.reasoning.settled",e.requestId,t,{reasoning_duration_ms:n,reasoning_chars:r.text.length,reasoning_snapshot_count:r.receivedSnapshotCount,reasoning_streaming_publish_count:r.streamingPublishCount,reasoning_coalesced_snapshot_count:Math.max(0,r.receivedSnapshotCount-r.streamingPublishCount),reasoning_publish_interval_ms:200})},A=e=>{const t=f.indexOf(e);t>=0&&f.splice(t,1)},N=e=>{const t=e?f.filter(t=>h.get(t)?.name===e):f;return 1===t.length?t[0]:void 0},U=e=>{const t=b.map((e,t)=>({hint:e,index:t})).filter(({hint:t})=>!e||t.name===e);if(1!==t.length)return;const[{hint:r,index:n}]=t;return b.splice(n,1),r},j=(e,t)=>{if(v.set(e,t),e===t)return;const r=y.get(e);if(!r)return;const n=F(t);Q(n,r),y.delete(e);const s=h.get(t);s&&(K(s,n),y.delete(t))},L=e=>v.get(e)??e,B=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),F=e=>{let t=y.get(e);return t||(t={},y.set(e,t)),t},D=(e,t)=>{if(!e?.length&&!t?.length)return;const r=[],n=new Map;for(const s of[...e??[],...t??[]]){const e=n.get(s.url);void 0===e?(n.set(s.url,r.length),r.push(s)):r[e]=mergeSearchSource(r[e],s)}return r},Q=(e,t)=>{e.name??=t.name,e.arguments??=t.arguments,t.toolStartObserved&&(e.toolStartObserved=!0),t.itemEventObserved&&(e.itemEventObserved=!0),t.progress?.length&&(e.progress=[...e.progress??[],...t.progress]),e.sources=D(e.sources,t.sources)},z=(t,r,n)=>{const s="tool"===t.name&&"tool"!==r?r:t.name,o=!B(t.arguments)&&B(n)?n:t.arguments;s===t.name&&o===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:s,arguments:o,state:t.state,_seq:t.displaySeq,data:x(t.displaySeq)})),t.name=s,t.arguments=o)},H=(t,r,n,s)=>{const o=h.get(t);if(o)return z(o,r,n),s&&!f.includes(t)&&"streaming"===o.state&&f.push(t),o;k();const a=y.get(t),i="tool"!==r?r:a?.name&&"tool"!==a.name?a.name:r,l=B(n)?n:a?.arguments??n,d=((t,r,n)=>{const s=I,o=S;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:n,state:"streaming",_seq:o,data:x(o)})),I+=1,S+=1,{partIndex:s,displaySeq:o}})(t,i,l),u={id:t,name:i,arguments:l,partIndex:d.partIndex,displaySeq:d.displaySeq,state:"streaming",itemEventObserved:!0===a?.itemEventObserved,toolStartObserved:!0===a?.toolStartObserved};return h.set(t,u),(s||u.toolStartObserved||u.itemEventObserved)&&(f.includes(t)||"streaming"!==u.state||f.push(t)),a&&K(u,a),y.delete(t),u},J=(e,t)=>{const r=(e=>{const t=v.get(e);return t?h.get(t):h.get(e)})(e);if(r)return void 0===t.name&&void 0===t.arguments||z(r,t.name??r.name,t.arguments??r.arguments),t.toolStartObserved&&(r.toolStartObserved=!0),t.itemEventObserved&&(r.itemEventObserved=!0),(t.pending||t.toolStartObserved||t.itemEventObserved)&&!f.includes(r.id)&&"streaming"===r.state&&f.push(r.id),r;const n=F(e);void 0!==t.name&&(n.name=t.name),void 0!==t.arguments&&(n.arguments=t.arguments),t.toolStartObserved&&(n.toolStartObserved=!0),t.itemEventObserved&&(n.itemEventObserved=!0)},W=t=>{"done"!==t.state&&(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:t.name,arguments:t.arguments,state:"done",_seq:t.displaySeq,data:x(t.displaySeq)})),t.state="done")},G=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(G).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${G(t)}`).join(",")}}`},V=(t,r,n)=>{const s=`${n?"1":"0"}:${G(r)}`;if(t.progressSignature===s)return;const o=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:t.id,content:r,...n?{is_error:!0}:{}}});if(void 0===t.progressPartIndex){const r=I;return e.stream.addPart(o),t.progressPartIndex=r,t.progressSignature=s,void(I+=1)}e.stream.replacePart(t.progressPartIndex,o),t.progressSignature=s},K=(e,t)=>{if(t.progress?.length){for(const r of t.progress)V(e,r.content,r.isError);W(e),A(e.id)}t.sources?.length&&E(t.sources,e.id)},X=e=>[e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)),Y=(e,t=[])=>{const r=[...new Set([...X(e),...t])];return isNonEmptyString(e.text)&&r.length>0?{text:e.text,media_urls:r}:isNonEmptyString(e.text)?e.text:1===r.length?r[0]:r.length>1?{media_urls:r}:!0===e.isError?"Tool execution failed.":""},Z=(t,r)=>{if(t.durableUploadFilesEmitted)return;e.logger.info?.(`[im-sink] schedule tool artifact enrichment requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`);const n=pickRawResultFromPayload(r),s=(async()=>{let s=[];try{s=await resolveToolArtifactLinks({toolCallId:t.id,toolName:t.name,rawResult:n,rawText:"string"==typeof r.text?r.text:void 0,payloadMediaUrls:X(r)})}catch(r){const n=r instanceof Error?r.message:String(r);return void e.logger.warn?.(`[im-sink] resolveToolArtifactLinks failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)}0!==s.length?(e.logger.info?.(`[im-sink] tool artifact enrichment resolved ${s.length} link(s) requestId=${e.requestId} toolUseId=${t.id}`),await new Promise(n=>{e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const n=s.map(e=>e.uri);V(t,Y(r,n),!0===r.isError),((t,r,n)=>{if(0!==r.length&&!t.durableUploadFilesEmitted){t.durableUploadFilesEmitted=!0,e.stream.appendToolResult(t.id,r.map(e=>({type:"resource_link",uri:e.uri,name:e.name,...e.mimeType?{mimeType:e.mimeType}:{}})),n),I+=1;for(const t of r)e.stream.addPart(toMessagePart({type:"resource_link",uri:t.uri,name:t.name,...t.mimeType?{mimeType:t.mimeType}:{}})),I+=1}})(t,s,!0===r.isError)}catch(r){const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment apply failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)}finally{n()}})})):e.logger.debug?.(`[im-sink] tool artifact enrichment found no links requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`)})().catch(r=>{const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)});$.add(s),s.finally(()=>{$.delete(s)})},ee=(e,t,r)=>{const n=v.get(e);if(n)return J(n,{name:t,arguments:r});const s=J(e,{name:t,arguments:r});if(s)return s;const o=N(t);if(o)return j(e,o),J(o,{name:t,arguments:r});J(e,{name:t,arguments:r})},te=(e,t)=>{const r=v.get(e);if(r)return J(r,{name:t,itemEventObserved:!0,pending:!0});const n=J(e,{name:t,itemEventObserved:!0,pending:!0});if(n)return n;const s=(e=>{const t=f.filter(t=>{const r=h.get(t);return!(void 0===r||e&&r.name!==e||r.itemEventObserved)});return 1===t.length?t[0]:void 0})(t);if(s)return j(e,s),J(s,{name:t,itemEventObserved:!0,pending:!0});const o=U(t);J(e,{name:t,arguments:o?.arguments,itemEventObserved:!0,pending:!0})},re={tool:0,block:0,final:0,source:0};let ne,se=!1;const oe=(t,r)=>{e.callbackQueue.enqueueSync(()=>{if((t=>e.abortSignal.aborted?(e.logger.debug?.(`[im-sink] ${t} ignored after abort requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):e.stream.isTerminated?(e.logger.debug?.(`[im-sink] ${t} ignored after stream_terminated requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):!se||(e.logger.debug?.(`[im-sink] ${t} ignored after mark_complete requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1))(t))try{r()}catch(r){const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] ${t} mapping failed requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId} error=${n}`)}})};return{dispatcher:{sendToolResult:t=>(oe("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),n=resolveRuntimeToolName(t),s=resolveRuntimeToolArguments(t),o=Y(t),a=!0===t.isError;let i;if(r){if(i=ee(r,n,s),!i){const e=F(r);return n&&(e.name=n),B(s)&&(e.arguments=s),e.progress=[...e.progress??[],{content:o,isError:a}],void(re.tool+=1)}}else{const t=n?N(n):N();if(!t)return U(n),void e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${n??"unknown"} pending=${f.length}`);i=J(t,{name:n,arguments:s})}i&&(re.tool+=1,V(i,o,a),W(i),A(i.id),Z(i,t))}),!0),sendBlockReply:e=>(oe("block_reply",()=>{re.block+=1,isNonEmptyString(e.text)&&(ne=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:e=>(oe("final_reply",()=>{var t;(re.final+=1,isNonEmptyString(e.text))&&(!0!==e.isError?(ne=void 0,l||(t=e.text,i="",a+=t,P(a),C(t),l=!0)):ne=e.text)}),!0),waitForIdle:async()=>{for(let t=0;t<16;t++){if(await e.callbackQueue.waitForIdle(),0===$.size)return;await Promise.allSettled([...$])}await e.callbackQueue.waitForIdle()},getQueuedCounts:()=>({tool:re.tool,block:re.block,final:re.final}),getAccumulatedBody:()=>o.text,getTerminalErrorText:()=>ne,markComplete:r=>{O(),(b.length>0||y.size>0)&&e.logger.debug?.(`[im-sink] dropping uncommitted lifecycle tool signals requestId=${e.requestId} legacy_hints=${b.length} buffered=${y.size}`),i="",P(a),g.receivedSnapshotCount>0&&logChatLatency(e.logger,"claw.text_preview.settled",e.requestId,t,{text_preview_snapshot_count:g.receivedSnapshotCount,text_preview_streaming_publish_count:g.streamingPublishCount,text_preview_terminal_publish_count:g.terminalPublishCount,text_preview_coalesced_snapshot_count:Math.max(0,g.receivedSnapshotCount-g.streamingPublishCount-g.terminalPublishCount),text_preview_publish_interval_ms:200});for(const t of h.values())try{"success"===r&&W(t),A(t.id)}catch(r){const n=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool finalization failed requestId=${e.requestId} toolUseId=${t.id} error=${n}`)}b.length=0,y.clear(),se=!0}},replyOptions:{...e.runId?{runId:e.runId}:{},abortSignal:e.abortSignal,disableBlockStreaming:!0,onAgentRunStart:r=>{e.onAgentRunStart?.(r),logChatLatency(e.logger,"claw.runtime.run_started",e.requestId,t),e.logger.info?.(`[im-sink] run_start requestId=${e.requestId} runId=${r} channel=${e.channelId} in_reply_to=${e.inboundMessageId} origin=${e.origin??"im"}`)},onPartialReply:n=>{oe("partial",()=>{"string"==typeof n.text&&(!r&&n.text.length>0&&(r=!0,logChatLatency(e.logger,"claw.model.first_delta",e.requestId,t)),i=n.text,(e=>{const t=m;if(!t){if(o.text===e)return;return q(e),void(m={targetBody:e,receivedSnapshotCount:1,streamingPublishCount:1,terminalPublishCount:0})}t.targetBody!==e&&(t.targetBody=e,t.receivedSnapshotCount+=1,w(t))})(a+i))})},onReasoningStream:t=>{oe("reasoning",()=>{if(!isNonEmptyString(t.text))return;const r=t.text,n=p??(t=>{k();const r={partIndex:I,displaySeq:S,text:t,publishedText:t,receivedSnapshotCount:1,streamingPublishCount:1,startedAtMs:Date.now(),startedAtMonotonicMs:performance.now()};return e.stream.addPart(M(r,t,"streaming")),I+=1,S+=1,p=r,r})(r);r!==n.text&&(n.text.startsWith(r)||(n.text=r,n.receivedSnapshotCount+=1,R(n)))})},onReasoningEnd:()=>{oe("reasoning_end",()=>{O()})},onAssistantMessageStart:()=>{oe("assistant_message_start",()=>{i="",l=!1,P(a),e.logger.debug?.(`[im-sink] assistant_message_start requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`)})},onToolStart:r=>{oe("tool_start",()=>{const s="string"==typeof r.phase?r.phase:"start",o=resolveRuntimeToolName(r)??"tool",a=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);if("update"===s)return void(a&&J(L(a),{name:o,arguments:i,pending:!0}));if(a&&n.set(a,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:o}),a){J(a,{name:o,arguments:i,toolStartObserved:!0,pending:!0});const e=N(o);return void(e&&e!==a&&!v.has(a)&&(j(a,e),J(e,{name:o,arguments:i,toolStartObserved:!0,pending:!0})))}const l=(d=o,f.map(e=>h.get(e)).filter(e=>void 0!==e&&(!d||e.name===d)&&e.itemEventObserved));var d;if(1===l.length)return void J(l[0].id,{name:o,arguments:i,toolStartObserved:!0,pending:!0});if(l.length>1)return void e.logger.debug?.(`[im-sink] ambiguous id-less tool start ignored requestId=${e.requestId} channel=${e.channelId} tool=${o} candidates=${l.length}`);const u=[...y.entries()].filter(([,e])=>!e.name||e.name===o||"tool"===e.name);1!==u.length?b.push({name:o,arguments:i}):J(u[0][0],{name:o,arguments:i,toolStartObserved:!0,pending:!0})})},onItemEvent:e=>{oe("item_event",()=>{const t="string"==typeof e.kind?e.kind.toLowerCase():"";if(t&&"tool"!==t)return;const r="string"==typeof e.phase?e.phase.toLowerCase():"update",n=resolveRuntimeToolCallId(e)??("string"==typeof e.itemId&&e.itemId.trim()?e.itemId.trim():void 0),s=resolveRuntimeToolName(e)??("string"==typeof e.title&&e.title.trim()?e.title.trim():void 0);n&&s&&("update"!==r?te(n,s):J(L(n),{name:s,itemEventObserved:!0,pending:!0}))})}},commitAssistantMessage:e=>{oe("assistant_message_commit",()=>{if(u.has(e.key))return;u.add(e.key),l=!0,ne=void 0,i="";const t=e.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");a+=t,P(a);for(const t of e.segments)"text"!==t.type?H(t.id,t.name,t.arguments,!0):C(t.text)})},observeToolResult:r=>{oe("tool_result_observation",()=>{const o=r.toolCallId.trim(),a=r.toolName.trim();if(!o||!a)return;if(!s.has(o)){s.add(o);const i=n.get(o);logChatLatency(e.logger,"claw.tool.result",e.requestId,t,{tool:a,tool_elapsed_ms:void 0===i?void 0:Math.max(0,Date.now()-i),outcome:r.isError?"error":"success"})}const i=searchSourceProvider(r.rawResult)??"unknown";let l=0;try{const t=extractSearchSources({toolName:a,rawResult:r.rawResult,isError:r.isError});if(l=t.length,0===l)return;const n=ee(o,a);if(n)E(t,n.id);else{const e=F(L(o));e.name??=a,e.sources=D(e.sources,t)}e.logger.debug?.(`[im-sink] search source projection requestId=${e.requestId} tool=${a} provider=${i} source_count=${l}`)}catch{e.logger.warn?.(`[im-sink] search source projection ignored requestId=${e.requestId} tool=${a} provider=${i} source_count=${l}`)}})},getSourceCount:()=>re.source}}
@@ -1 +1 @@
1
- const DEFAULT_ORIGINATING_CHANNEL="beeos-claw",DEFAULT_ORIGINATING_TARGET="main",DEFAULT_ORIGINATING_ACCOUNT_ID="main",isRecord=o=>"object"==typeof o&&null!==o&&!Array.isArray(o),assertKnownReplyProtocol=o=>{if("acp"!==o&&"im"!==o)throw new Error(`unsupported reply route protocol=${o}`)},normalizeMediaUrls=o=>{if(!Array.isArray(o))return;const e=o.filter(o=>"string"==typeof o&&o.length>0);return e.length>0?e:void 0};export const hasRuntimeReplyApi=o=>"function"==typeof o?.channel?.reply?.dispatchReplyFromConfig;export const buildFinalizedMsgContext=(o,e)=>{assertKnownReplyProtocol(e.protocol);const r=e.originatingChannel??"beeos-claw",t=e.originatingTo??"main",n=e.accountId??"main",i=normalizeMediaUrls(o.attachments);return{Body:o.message,BodyForAgent:o.agentMessage,From:`${r}:${t}`,To:t,SessionKey:e.sessionId,Surface:r,Provider:r,OriginatingChannel:r,OriginatingTo:t,AccountId:n,CommandAuthorized:!0,...i?{MediaUrls:i}:{}}};export const resolveReplySinkForRoute=(o,e)=>{assertKnownReplyProtocol(o.protocol);const r=e[o.protocol];if(!r)throw new Error(`runtime reply sink unavailable protocol=${o.protocol} requestId=${o.requestId}`);return r};const resolveRuntimeDispatchConfig=(o,e)=>{try{const e=o.config?.loadConfig?.();if(isRecord(e))return e}catch{}return e};export const dispatchRuntimeReplyWithRoute=async o=>{const e=o.runtime.channel?.reply?.dispatchReplyFromConfig;if("function"!=typeof e)throw new Error(`runtime reply API unavailable protocol=${o.replyRoute.protocol} requestId=${o.replyRoute.requestId}`);const r=resolveReplySinkForRoute(o.replyRoute,o.replySinks),t=e({ctx:buildFinalizedMsgContext(o.preparedPrompt,o.replyRoute),cfg:resolveRuntimeDispatchConfig(o.runtime,o.pluginConfig),dispatcher:r.dispatcher,replyOptions:r.replyOptions});return o.onDispatchInvoked?.(),t};
1
+ import{enableDeepseekReasoningStream}from"./deepseek-reasoning-stream.js";const DEFAULT_ORIGINATING_CHANNEL="beeos-claw",DEFAULT_ORIGINATING_TARGET="main",DEFAULT_ORIGINATING_ACCOUNT_ID="main",isRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e),assertKnownReplyProtocol=e=>{if("acp"!==e&&"im"!==e)throw new Error(`unsupported reply route protocol=${e}`)},normalizeMediaUrls=e=>{if(!Array.isArray(e))return;const o=e.filter(e=>"string"==typeof e&&e.length>0);return o.length>0?o:void 0};export const hasRuntimeReplyApi=e=>"function"==typeof e?.channel?.reply?.dispatchReplyFromConfig;export const buildFinalizedMsgContext=(e,o)=>{assertKnownReplyProtocol(o.protocol);const r=o.originatingChannel??"beeos-claw",t=o.originatingTo??"main",n=o.accountId??"main",i=normalizeMediaUrls(e.attachments);return{Body:e.message,BodyForAgent:e.agentMessage,From:`${r}:${t}`,To:t,SessionKey:o.sessionId,Surface:r,Provider:r,OriginatingChannel:r,OriginatingTo:t,AccountId:n,CommandAuthorized:!0,...i?{MediaUrls:i}:{}}};export const resolveReplySinkForRoute=(e,o)=>{assertKnownReplyProtocol(e.protocol);const r=o[e.protocol];if(!r)throw new Error(`runtime reply sink unavailable protocol=${e.protocol} requestId=${e.requestId}`);return r};const resolveRuntimeDispatchConfig=(e,o)=>{try{const o=e.config?.loadConfig?.();if(isRecord(o))return o}catch{}return o};export const dispatchRuntimeReplyWithRoute=async e=>{const o=e.runtime.channel?.reply?.dispatchReplyFromConfig;if("function"!=typeof o)throw new Error(`runtime reply API unavailable protocol=${e.replyRoute.protocol} requestId=${e.replyRoute.requestId}`);const r=resolveReplySinkForRoute(e.replyRoute,e.replySinks),t=enableDeepseekReasoningStream(resolveRuntimeDispatchConfig(e.runtime,e.pluginConfig)),n=o({ctx:buildFinalizedMsgContext(e.preparedPrompt,e.replyRoute),cfg:t,dispatcher:r.dispatcher,replyOptions:r.replyOptions});return e.onDispatchInvoked?.(),n};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.54-staging.9",
3
+ "version": "0.6.55",
4
4
  "description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
5
5
  "license": "MIT",
6
6
  "author": "BeeOS <dev@beeos.ai>",
@@ -87,5 +87,5 @@
87
87
  "fast-uri": ">=3.1.4",
88
88
  "brace-expansion": ">=5.0.9"
89
89
  },
90
- "gitHead": "f9f0951c6bc3679989ea9ee100d98a6db6b5a954"
90
+ "gitHead": "6108feadd424081fa4fa5ee658f7764624c9d884"
91
91
  }