beeos-claw 0.6.76 → 0.6.78
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.
package/README.md
CHANGED
|
@@ -234,7 +234,7 @@ Directory listing accepts `ls` with `l`/`a`/`h` flags and at most one path.
|
|
|
234
234
|
`convert -version` and the complete fixed check
|
|
235
235
|
`python3 -c "import PIL; print(PIL.__version__)"` show `check_rendering`;
|
|
236
236
|
other inline Python remains `command`.
|
|
237
|
-
Headless `google-chrome` screenshots of local HTML to PNG use `generate_image`
|
|
237
|
+
Headless `google-chrome` screenshots of local HTML or SVG to PNG use `generate_image`
|
|
238
238
|
with the output basename. Only known rendering flags are accepted; filenames
|
|
239
239
|
never imply `generate_poster`.
|
|
240
240
|
`identify` with one ordinary local PNG/JPEG/WebP/GIF path shows `read` and its basename.
|
|
@@ -290,6 +290,14 @@ The plugin records office/PDF paths produced by `write` / `exec` and uploads any
|
|
|
290
290
|
that were not already delivered. Tool-result enrichment attaches those files
|
|
291
291
|
during the turn; `before_agent_finalize` uploads leftovers without requesting
|
|
292
292
|
another model pass. Temporary files, source, and credentials are not scanned.
|
|
293
|
+
Native `tool_call` uploads use the canonical call's upload ID and the matching
|
|
294
|
+
`rawResult.details.tool` identity (`openclaw:beeos-claw:beeos_upload_file`,
|
|
295
|
+
`beeos_upload_file`, source `openclaw`). Successful structured results supply
|
|
296
|
+
existing `beeos-file://` links; display text is never parsed for attachments.
|
|
297
|
+
The server emits the existing tool result plus top-level `resource_link` parts
|
|
298
|
+
and matching progress `media_urls`, so live delivery and history use the same
|
|
299
|
+
attachment contract. Early results retain their exact outer call ID; repeated
|
|
300
|
+
observations and repeated links do not duplicate attachments or upload files again.
|
|
293
301
|
Non-bundled installations still need
|
|
294
302
|
`plugins.entries.beeos-claw.hooks.allowConversationAccess` (see Quick Start)
|
|
295
303
|
for the finalization hook to register.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{toMessagePart}from"../message-client-types.js";import{isUploadArtifactToolName,resolveToolArtifactLinks}from"../upload/tool-artifact-enrichment.js";import{sessionWorkspaceDir,takeFinalizeAttachments}from"../upload/delivery-ledger.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}}function asToolParams(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e}const summarizeObservedToolError=e=>{if(null==e)return;if("string"==typeof e)return e.slice(0,180);if("object"!=typeof e)return String(e).slice(0,180);const t=e;for(const e of["error","message","blockReason","details","text"]){const r=t[e];if("string"==typeof r&&r.trim())return r.slice(0,180);if(isPlainRecord(r)&&"string"==typeof r.message&&r.message.trim())return r.message.slice(0,180)}try{const t=JSON.stringify(e);return t?t.slice(0,180):void 0}catch{return}},observedToolResultText=e=>{if(!isPlainRecord(e)||!Array.isArray(e.content))return;const t=e.content.filter(e=>isPlainRecord(e)&&"text"===e.type&&"string"==typeof e.text).map(e=>e.text.trim()).filter(Boolean);return t.length>0?t.join("\n"):void 0};export function createImReplySink(e){const t=e.startedAtMs??Date.now();let r=!1;const o=new Map,s=new Set,n={text:""};let a="",i="",l=!1,d=!1;const u=new Set;let c;const m={receivedSnapshotCount:0,streamingPublishCount:0,terminalPublishCount:0},g=[];let p;const f=new Map,h=[],b=new Map,y=new Map;let v,I=0,_=1;const S=(e,t={})=>({schema_version:1,seq:e,...t}),x=new Set,k=new Set,q=t=>{t!==n.text&&(t.startsWith(n.text)?e.stream.appendBody(t.slice(n.text.length)):e.stream.setBody(t),n.text=t)},w=e=>{void 0===e.publishTimer&&(e.publishTimer=setTimeout(()=>{e.publishTimer=void 0,te("body_preview_flush",()=>{c===e&&(e=>{n.text!==e.targetBody&&(q(e.targetBody),e.streamingPublishCount+=1)})(e)})},200))},$=e=>{const t=c;t?(void 0!==t.publishTimer&&(clearTimeout(t.publishTimer),t.publishTimer=void 0),t.targetBody=e,n.text!==e&&(q(e),t.terminalPublishCount+=1),m.receivedSnapshotCount+=t.receivedSnapshotCount,m.streamingPublishCount+=t.streamingPublishCount,m.terminalPublishCount+=t.terminalPublishCount,c=void 0):q(e)},P=t=>{if(void 0!==t.partIndex)return;const r=I,o=_;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:o,data:S(o)})),t.partIndex=r,I+=1,_+=1},T=()=>{if(!d){d=!0;for(const e of g)P(e)}},C=e=>{const t={text:e};g.push(t),d&&P(t)},R=(t,r)=>{for(const o of t){const t=y.get(o.url);if(t){const r=mergeSearchSource(t,o);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:S(s.displaySeq)})),y.set(o.url,s);continue}const s={...o,partIndex:I,displaySeq:_,toolUseId:r};e.stream.addPart(toMessagePart({type:"source",url:o.url,...o.title?{title:o.title}:{},...o.snippet?{snippet:o.snippet}:{},tool_use_id:r,_seq:s.displaySeq,data:S(s.displaySeq)})),y.set(o.url,s),I+=1,_+=1,Y.source+=1}},E=(e,t,r,o)=>toMessagePart({type:"thinking",text:t,state:r,_seq:e.displaySeq,_started_at_ms:e.startedAtMs,...void 0!==o?{_duration_ms:o}:{},data:S(e.displaySeq,{started_at_ms:e.startedAtMs,...void 0!==o?{duration_ms:o}:{}})}),M=t=>{T();const r={partIndex:I,displaySeq:_,text:t,publishedText:t,receivedSnapshotCount:1,streamingPublishCount:1,startedAtMs:Date.now(),startedAtMonotonicMs:performance.now()};return e.stream.addPart(E(r,t,"streaming")),I+=1,_+=1,p=r,r},A=t=>{void 0===t.publishTimer&&(t.publishTimer=setTimeout(()=>{t.publishTimer=void 0,te("reasoning_snapshot_flush",()=>{p===t&&(t=>{t.publishedText!==t.text&&(e.stream.replacePart(t.partIndex,E(t,t.text,"streaming")),t.publishedText=t.text,t.streamingPublishCount+=1)})(t)})},200))},F=()=>{const r=p;if(!r)return;void 0!==r.publishTimer&&(clearTimeout(r.publishTimer),r.publishTimer=void 0);const o=Math.max(0,Math.round(performance.now()-r.startedAtMonotonicMs));e.stream.replacePart(r.partIndex,E(r,r.text,"done",o)),p=void 0,logChatLatency(e.logger,"claw.reasoning.settled",e.requestId,t,{reasoning_duration_ms:o,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})},O=e=>{const t=h.indexOf(e);t>=0&&h.splice(t,1)},N=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),U=e=>{let t=b.get(e);return t||(t={},b.set(e,t)),t},j=(t,r,o)=>{const s="tool"===t.name&&"tool"!==r?r:t.name,n=!N(t.arguments)&&N(o)?o:t.arguments;s===t.name&&n===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:s,arguments:n,state:t.state,_seq:t.displaySeq,data:S(t.displaySeq)})),t.name=s,t.arguments=n)},L=(t,r,o,s)=>{const n=f.get(t);if(n)return j(n,r,o),s&&!h.includes(t)&&"streaming"===n.state&&h.push(t),n;T();const a=b.get(t),i="tool"!==r?r:a?.name&&"tool"!==a.name?a.name:r,l=N(o)?o:a?.arguments??o,d=((t,r,o)=>{const s=I,n=_;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:o,state:"streaming",_seq:n,data:S(n)})),I+=1,_+=1,{partIndex:s,displaySeq:n}})(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 f.set(t,u),(s||u.toolStartObserved||u.itemEventObserved)&&(h.includes(t)||"streaming"!==u.state||h.push(t)),a&&W(u,a),b.delete(t),u},B=(e,t)=>{const r=(e=>f.get(e))(e);if(r)return void 0===t.name&&void 0===t.arguments||j(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 o=U(e);void 0!==t.name&&(o.name=t.name),void 0!==t.arguments&&(o.arguments=t.arguments),t.toolStartObserved&&(o.toolStartObserved=!0),t.itemEventObserved&&(o.itemEventObserved=!0)},z=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:S(t.displaySeq)})),t.state="done")},D=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(D).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${D(t)}`).join(",")}}`},Q=(t,r,o)=>{const s=`${o?"1":"0"}:${D(r)}`;if(t.progressSignature===s)return;const n=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:t.id,content:r,...o?{is_error:!0}:{}}});if(void 0===t.progressPartIndex){const r=I;return e.stream.addPart(n),t.progressPartIndex=r,t.progressSignature=s,void(I+=1)}e.stream.replacePart(t.progressPartIndex,n),t.progressSignature=s},W=(e,t)=>{if(t.progress?.length)for(const r of t.progress)Q(e,r.content,r.isError);t.observedResult&&(Q(e,t.observedResult.content,t.observedResult.isError),e.observedResult=t.observedResult),(t.progress?.length||t.observedResult)&&(z(e),O(e.id)),t.sources?.length&&R(t.sources,e.id)},J=e=>[...new Set([e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)).map(e=>e.trim()).filter(Boolean))],H=(e,t=[])=>{const r=[...new Set([...J(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.":""},K=t=>{for(const r of t)e.stream.addPart(toMessagePart({type:"resource_link",uri:r.uri,name:r.name,...r.mimeType?{mimeType:r.mimeType}:{}})),I+=1},G=t=>{x.add(t);const r=()=>{x.delete(t)};e.onCallbackFailure?t.then(r,r):t.finally(r)},V=(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 o=pickRawResultFromPayload(r),s=(async()=>{let s=[];try{s=await resolveToolArtifactLinks({toolCallId:t.id,toolName:t.name,rawResult:o,rawText:"string"==typeof r.text?r.text:void 0,params:asToolParams(t.arguments),cwd:sessionWorkspaceDir(e.sessionKey),payloadMediaUrls:J(r)})}catch(r){const o=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] resolveToolArtifactLinks failed requestId=${e.requestId} toolUseId=${t.id} error=${o}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r;return}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((o,n)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const o=s.map(e=>e.uri);t.artifactMediaUrls=o;const n=t.observedResult;Q(t,H(n?{text:n.content,isError:n.isError}:r,o),n?.isError??!0===r.isError),((t,r,o)=>{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}:{}})),o),I+=1,K(r))})(t,s,n?.isError??!0===r.isError)}catch(r){const o=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment apply failed requestId=${e.requestId} toolUseId=${t.id} error=${o}`),e.onCallbackFailure?.(r),e.onCallbackFailure&&n(r)}finally{o()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(o,n)})):e.logger.debug?.(`[im-sink] tool artifact enrichment found no links requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`)})().catch(r=>{const o=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] tool artifact enrichment failed requestId=${e.requestId} toolUseId=${t.id} error=${o}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r});G(s)},X=(e,t,r)=>B(e,{name:t,arguments:r}),Y={tool:0,block:0,final:0,source:0};let Z,ee=!1;const te=(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):!ee||(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 o=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=${o}`),e.onCallbackFailure?.(r)}})},re={sendToolResult:t=>(te("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),o=resolveRuntimeToolName(t),s=resolveRuntimeToolArguments(t),n=H(t),a=!0===t.isError;let i;if(r){if(i=X(r,o,s),!i){const e=U(r);return o&&(e.name=o),N(s)&&(e.arguments=s),e.progress=[...e.progress??[],{content:n,isError:a}],void(Y.tool+=1)}i&&(Y.tool+=1,i.observedResult||Q(i,n,a),z(i),O(i.id),V(i,t))}else e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${o??"unknown"} pending=${h.length}`)}),!0),sendBlockReply:e=>(te("block_reply",()=>{Y.block+=1,isNonEmptyString(e.text)&&(Z=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:t=>(te("final_reply",()=>{var r;(Y.final+=1,!0!==t.isError)?((t=>{const r=J(t).filter(e=>!k.has(e));if(0===r.length)return;for(const e of r)k.add(e);const o=(async()=>{let t=[];try{t=await resolveToolArtifactLinks({toolCallId:`final:${e.runId??e.requestId}`,toolName:"final_reply_media",payloadMediaUrls:r})}catch(t){for(const e of r)k.delete(e);if(e.logger.warn?.(`[im-sink] final media enrichment failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure)throw t;return}if(0!==t.length)await new Promise((r,o)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;K(t)}catch(t){e.logger.warn?.(`[im-sink] final media enrichment apply failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure&&o(t)}finally{r()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(r,o)});else for(const e of r)k.delete(e)})();G(o)})(t),isNonEmptyString(t.text)&&(Z=void 0,l||(r=t.text,i="",a+=r,$(a),C(r),l=!0))):Z=t.text}),!0),waitForIdle:async()=>{for(let t=0;(e.onCallbackFailure||t<16)&&(await e.callbackQueue.waitForIdle(),0!==x.size);t++)e.onCallbackFailure?await Promise.all([...x]):await Promise.allSettled([...x]);await e.callbackQueue.waitForIdle();const t=takeFinalizeAttachments(e.sessionKey);t.length>0&&!e.abortSignal.aborted&&!e.stream.isTerminated&&K(t)},getQueuedCounts:()=>({tool:Y.tool,block:Y.block,final:Y.final}),getAccumulatedBody:()=>n.text,getTerminalErrorText:()=>Z,markComplete:r=>{F(),b.size>0&&e.logger.debug?.(`[im-sink] dropping uncommitted lifecycle tool signals requestId=${e.requestId} buffered=${b.size}`),i="",$(a),m.receivedSnapshotCount>0&&logChatLatency(e.logger,"claw.text_preview.settled",e.requestId,t,{text_preview_snapshot_count:m.receivedSnapshotCount,text_preview_streaming_publish_count:m.streamingPublishCount,text_preview_terminal_publish_count:m.terminalPublishCount,text_preview_coalesced_snapshot_count:Math.max(0,m.receivedSnapshotCount-m.streamingPublishCount-m.terminalPublishCount),text_preview_publish_interval_ms:200});for(const t of f.values())try{"success"===r&&isUploadArtifactToolName(t.name)&&!t.durableUploadFilesEmitted&&V(t,{toolCallId:t.id,toolName:t.name}),"success"===r&&z(t),O(t.id)}catch(r){const o=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool finalization failed requestId=${e.requestId} toolUseId=${t.id} error=${o}`)}b.clear(),ee=!0}},oe={...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:o=>{te("partial",()=>{"string"==typeof o.text&&(!r&&o.text.length>0&&(r=!0,logChatLatency(e.logger,"claw.model.first_delta",e.requestId,t)),i=o.text,(e=>{const t=c;if(!t){if(n.text===e)return;return q(e),void(c={targetBody:e,receivedSnapshotCount:1,streamingPublishCount:1,terminalPublishCount:0})}t.targetBody!==e&&(t.targetBody=e,t.receivedSnapshotCount+=1,w(t))})(a+i))})},onReasoningStream:e=>{te("reasoning",()=>{if(!isNonEmptyString(e.text))return;const t=e.text,r=p??M(t);t!==r.text&&(r.text.startsWith(t)||(t.startsWith(r.text)?(r.text=t,r.receivedSnapshotCount+=1,A(r)):(F(),M(t))))})},onReasoningEnd:()=>{te("reasoning_end",()=>{F()})},onAssistantMessageStart:()=>{te("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=>{te("tool_start",()=>{const s="string"==typeof r.phase?r.phase:"start",n=resolveRuntimeToolName(r)??"tool",a=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);"update"!==s?(a&&o.set(a,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:n}),a&&B(a,{name:n,arguments:i,toolStartObserved:!0,pending:!0})):a&&B(a,{name:n,arguments:i,pending:!0})})},onItemEvent:e=>{te("item_event",()=>{const t="string"==typeof e.kind?e.kind.toLowerCase():"";if(t&&"tool"!==t)return;"string"==typeof e.phase&&e.phase.toLowerCase();const r=resolveRuntimeToolCallId(e)??("string"==typeof e.itemId&&e.itemId.trim()?e.itemId.trim():void 0),o=resolveRuntimeToolName(e)??("string"==typeof e.title&&e.title.trim()?e.title.trim():void 0);r&&o&&B(r,{name:o,itemEventObserved:!0,pending:!0})})}},se={add(t){if(e.stream.isTerminated)return;const r=I;return e.stream.addPart(t),I+=1,r}},ne={add:e=>se.add(e),replace:(t,r)=>!e.stream.isTerminated&&(e.stream.replacePart(t,r),!0)};return{dispatcher:re,replyOptions:oe,commitAssistantMessage:t=>{te("assistant_message_commit",()=>{if(u.has(t.key))return;u.add(t.key),l=!0,Z=void 0,i="";const r=t.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");a+=r,$(a);for(const e of t.segments)"text"!==e.type?L(e.id,e.name,e.arguments,!0):C(e.text);if(void 0!==t.executedModel||void 0!==v){const r=toMessagePart({type:"custom",kind:"execution_model",data:{executed_model:t.executedModel??""}});void 0===v?(e.stream.addPart(r),v=I++):e.stream.replacePart(v,r)}})},observeToolResult:r=>{te("tool_result_observation",()=>{const n=r.toolCallId.trim(),a=r.toolName.trim();if(!n||!a)return;const i=!s.has(n);if(i){s.add(n);const i=o.get(n);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",...r.isError?{error:summarizeObservedToolError(r.rawResult)??"error"}:{}})}if(e.persistObservedToolResults&&i){const e=observedToolResultText(r.rawResult);if(void 0!==e){const t=X(n,a);if(t)Q(t,H({text:e,isError:r.isError},t.artifactMediaUrls),r.isError),t.observedResult={content:e,isError:r.isError},z(t),O(t.id);else{const t=U(n);t.name??=a,t.observedResult={content:e,isError:r.isError}}}}const l=searchSourceProvider(r.rawResult)??"unknown";let d=0;try{const t=extractSearchSources({toolName:a,rawResult:r.rawResult,isError:r.isError});if(d=t.length,0===d)return;const o=X(n,a);if(o)R(t,o.id);else{const e=U(n);e.name??=a,e.sources=((e,t)=>{if(!e?.length&&!t?.length)return;const r=[],o=new Map;for(const s of[...e??[],...t??[]]){const e=o.get(s.url);void 0===e?(o.set(s.url,r.length),r.push(s)):r[e]=mergeSearchSource(r[e],s)}return r})(e.sources,t)}e.logger.debug?.(`[im-sink] search source projection requestId=${e.requestId} tool=${a} provider=${l} source_count=${d}`)}catch{e.logger.warn?.(`[im-sink] search source projection ignored requestId=${e.requestId} tool=${a} provider=${l} source_count=${d}`)}})},getSourceCount:()=>Y.source,approvalParts:se,questionParts:ne}}
|
|
1
|
+
import{toMessagePart}from"../message-client-types.js";import{isUploadArtifactToolName,resolveToolArtifactLinks}from"../upload/tool-artifact-enrichment.js";import{sessionWorkspaceDir,takeFinalizeAttachments}from"../upload/delivery-ledger.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}}function asToolParams(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e}const summarizeObservedToolError=e=>{if(null==e)return;if("string"==typeof e)return e.slice(0,180);if("object"!=typeof e)return String(e).slice(0,180);const t=e;for(const e of["error","message","blockReason","details","text"]){const r=t[e];if("string"==typeof r&&r.trim())return r.slice(0,180);if(isPlainRecord(r)&&"string"==typeof r.message&&r.message.trim())return r.message.slice(0,180)}try{const t=JSON.stringify(e);return t?t.slice(0,180):void 0}catch{return}},observedToolResultText=e=>{if(!isPlainRecord(e)||!Array.isArray(e.content))return;const t=e.content.filter(e=>isPlainRecord(e)&&"text"===e.type&&"string"==typeof e.text).map(e=>e.text.trim()).filter(Boolean);return t.length>0?t.join("\n"):void 0};export function createImReplySink(e){const t=e.startedAtMs??Date.now();let r=!1;const s=new Map,o=new Set,a={text:""};let n="",i="",l=!1,d=!1;const u=new Set;let c;const m={receivedSnapshotCount:0,streamingPublishCount:0,terminalPublishCount:0},g=[];let p;const f=new Map,h=[],b=new Map,v=new Map;let y,_=0,I=1;const S=(e,t={})=>({schema_version:1,seq:e,...t}),x=new Set,w=new Set,k=t=>{t!==a.text&&(t.startsWith(a.text)?e.stream.appendBody(t.slice(a.text.length)):e.stream.setBody(t),a.text=t)},q=e=>{void 0===e.publishTimer&&(e.publishTimer=setTimeout(()=>{e.publishTimer=void 0,te("body_preview_flush",()=>{c===e&&(e=>{a.text!==e.targetBody&&(k(e.targetBody),e.streamingPublishCount+=1)})(e)})},200))},R=e=>{const t=c;t?(void 0!==t.publishTimer&&(clearTimeout(t.publishTimer),t.publishTimer=void 0),t.targetBody=e,a.text!==e&&(k(e),t.terminalPublishCount+=1),m.receivedSnapshotCount+=t.receivedSnapshotCount,m.streamingPublishCount+=t.streamingPublishCount,m.terminalPublishCount+=t.terminalPublishCount,c=void 0):k(e)},$=t=>{if(void 0!==t.partIndex)return;const r=_,s=I;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:s,data:S(s)})),t.partIndex=r,_+=1,I+=1},P=()=>{if(!d){d=!0;for(const e of g)$(e)}},T=e=>{const t={text:e};g.push(t),d&&$(t)},C=(t,r)=>{for(const s of t){const t=v.get(s.url);if(t){const r=mergeSearchSource(t,s);if(r.title===t.title&&r.snippet===t.snippet)continue;const o={...r,partIndex:t.partIndex,displaySeq:t.displaySeq,toolUseId:t.toolUseId};e.stream.replacePart(t.partIndex,toMessagePart({type:"source",url:o.url,...o.title?{title:o.title}:{},...o.snippet?{snippet:o.snippet}:{},tool_use_id:o.toolUseId,_seq:o.displaySeq,data:S(o.displaySeq)})),v.set(s.url,o);continue}const o={...s,partIndex:_,displaySeq:I,toolUseId:r};e.stream.addPart(toMessagePart({type:"source",url:s.url,...s.title?{title:s.title}:{},...s.snippet?{snippet:s.snippet}:{},tool_use_id:r,_seq:o.displaySeq,data:S(o.displaySeq)})),v.set(s.url,o),_+=1,I+=1,Y.source+=1}},E=(e,t,r,s)=>toMessagePart({type:"thinking",text:t,state:r,_seq:e.displaySeq,_started_at_ms:e.startedAtMs,...void 0!==s?{_duration_ms:s}:{},data:S(e.displaySeq,{started_at_ms:e.startedAtMs,...void 0!==s?{duration_ms:s}:{}})}),M=t=>{P();const r={partIndex:_,displaySeq:I,text:t,publishedText:t,receivedSnapshotCount:1,streamingPublishCount:1,startedAtMs:Date.now(),startedAtMonotonicMs:performance.now()};return e.stream.addPart(E(r,t,"streaming")),_+=1,I+=1,p=r,r},A=t=>{void 0===t.publishTimer&&(t.publishTimer=setTimeout(()=>{t.publishTimer=void 0,te("reasoning_snapshot_flush",()=>{p===t&&(t=>{t.publishedText!==t.text&&(e.stream.replacePart(t.partIndex,E(t,t.text,"streaming")),t.publishedText=t.text,t.streamingPublishCount+=1)})(t)})},200))},F=()=>{const r=p;if(!r)return;void 0!==r.publishTimer&&(clearTimeout(r.publishTimer),r.publishTimer=void 0);const s=Math.max(0,Math.round(performance.now()-r.startedAtMonotonicMs));e.stream.replacePart(r.partIndex,E(r,r.text,"done",s)),p=void 0,logChatLatency(e.logger,"claw.reasoning.settled",e.requestId,t,{reasoning_duration_ms:s,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})},O=e=>{const t=h.indexOf(e);t>=0&&h.splice(t,1)},N=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),U=e=>{let t=b.get(e);return t||(t={},b.set(e,t)),t},j=(t,r,s)=>{const o="tool"===t.name&&"tool"!==r?r:t.name,a=!N(t.arguments)&&N(s)?s:t.arguments;o===t.name&&a===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:o,arguments:a,state:t.state,_seq:t.displaySeq,data:S(t.displaySeq)})),t.name=o,t.arguments=a)},L=(t,r,s,o)=>{const a=f.get(t);if(a)return j(a,r,s),o&&!h.includes(t)&&"streaming"===a.state&&h.push(t),a;P();const n=b.get(t),i="tool"!==r?r:n?.name&&"tool"!==n.name?n.name:r,l=N(s)?s:n?.arguments??s,d=((t,r,s)=>{const o=_,a=I;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:s,state:"streaming",_seq:a,data:S(a)})),_+=1,I+=1,{partIndex:o,displaySeq:a}})(t,i,l),u={id:t,name:i,arguments:l,partIndex:d.partIndex,displaySeq:d.displaySeq,state:"streaming",itemEventObserved:!0===n?.itemEventObserved,toolStartObserved:!0===n?.toolStartObserved};return f.set(t,u),(o||u.toolStartObserved||u.itemEventObserved)&&(h.includes(t)||"streaming"!==u.state||h.push(t)),n&&W(u,n),b.delete(t),u},B=(e,t)=>{const r=(e=>f.get(e))(e);if(r)return void 0===t.name&&void 0===t.arguments||j(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 s=U(e);void 0!==t.name&&(s.name=t.name),void 0!==t.arguments&&(s.arguments=t.arguments),t.toolStartObserved&&(s.toolStartObserved=!0),t.itemEventObserved&&(s.itemEventObserved=!0)},z=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:S(t.displaySeq)})),t.state="done")},D=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(D).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${D(t)}`).join(",")}}`},Q=(t,r,s)=>{const o=`${s?"1":"0"}:${D(r)}`;if(t.progressSignature===o)return;const a=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:t.id,content:r,...s?{is_error:!0}:{}}});if(void 0===t.progressPartIndex){const r=_;return e.stream.addPart(a),t.progressPartIndex=r,t.progressSignature=o,void(_+=1)}e.stream.replacePart(t.progressPartIndex,a),t.progressSignature=o},W=(e,t)=>{if(t.progress?.length)for(const r of t.progress)Q(e,r.content,r.isError);t.observedResult&&(Q(e,t.observedResult.content,t.observedResult.isError),e.observedResult=t.observedResult,"tool_call"!==e.name||t.observedResult.isError||V(e,{text:t.observedResult.content,isError:t.observedResult.isError,rawResult:t.observedResult.rawResult})),(t.progress?.length||t.observedResult)&&(z(e),O(e.id)),t.sources?.length&&C(t.sources,e.id)},J=e=>[...new Set([e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)).map(e=>e.trim()).filter(Boolean))],H=(e,t=[])=>{const r=[...new Set([...J(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.":""},K=t=>{for(const r of t)e.stream.addPart(toMessagePart({type:"resource_link",uri:r.uri,name:r.name,...r.mimeType?{mimeType:r.mimeType}:{}})),_+=1},G=t=>{x.add(t);const r=()=>{x.delete(t)};e.onCallbackFailure?t.then(r,r):t.finally(r)},V=(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 s=pickRawResultFromPayload(r),o=(async()=>{let o=[];try{o=await resolveToolArtifactLinks({toolCallId:t.id,toolName:t.name,rawResult:s,rawText:"string"==typeof r.text?r.text:void 0,params:asToolParams(t.arguments),cwd:sessionWorkspaceDir(e.sessionKey),payloadMediaUrls:J(r)})}catch(r){const s=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] resolveToolArtifactLinks failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r;return}0!==o.length?(e.logger.info?.(`[im-sink] tool artifact enrichment resolved ${o.length} link(s) requestId=${e.requestId} toolUseId=${t.id}`),await new Promise((s,a)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const s=o.map(e=>e.uri);t.artifactMediaUrls=s;const a=t.observedResult;Q(t,H(a?{text:a.content,isError:a.isError}:r,s),a?.isError??!0===r.isError),((t,r,s)=>{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}:{}})),s),_+=1,K(r))})(t,o,a?.isError??!0===r.isError)}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment apply failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure&&a(r)}finally{s()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(s,a)})):e.logger.debug?.(`[im-sink] tool artifact enrichment found no links requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`)})().catch(r=>{const s=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] tool artifact enrichment failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r});G(o)},X=(e,t,r)=>B(e,{name:t,arguments:r}),Y={tool:0,block:0,final:0,source:0};let Z,ee=!1;const te=(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):!ee||(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 s=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=${s}`),e.onCallbackFailure?.(r)}})},re={sendToolResult:t=>(te("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),s=resolveRuntimeToolName(t),o=resolveRuntimeToolArguments(t),a=H(t),n=!0===t.isError;let i;if(r){if(i=X(r,s,o),!i){const e=U(r);return s&&(e.name=s),N(o)&&(e.arguments=o),e.progress=[...e.progress??[],{content:a,isError:n}],void(Y.tool+=1)}i&&(Y.tool+=1,i.observedResult||Q(i,a,n),z(i),O(i.id),V(i,t))}else e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${s??"unknown"} pending=${h.length}`)}),!0),sendBlockReply:e=>(te("block_reply",()=>{Y.block+=1,isNonEmptyString(e.text)&&(Z=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:t=>(te("final_reply",()=>{var r;(Y.final+=1,!0!==t.isError)?((t=>{const r=J(t).filter(e=>!w.has(e));if(0===r.length)return;for(const e of r)w.add(e);const s=(async()=>{let t=[];try{t=await resolveToolArtifactLinks({toolCallId:`final:${e.runId??e.requestId}`,toolName:"final_reply_media",payloadMediaUrls:r})}catch(t){for(const e of r)w.delete(e);if(e.logger.warn?.(`[im-sink] final media enrichment failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure)throw t;return}if(0!==t.length)await new Promise((r,s)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;K(t)}catch(t){e.logger.warn?.(`[im-sink] final media enrichment apply failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure&&s(t)}finally{r()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(r,s)});else for(const e of r)w.delete(e)})();G(s)})(t),isNonEmptyString(t.text)&&(Z=void 0,l||(r=t.text,i="",n+=r,R(n),T(r),l=!0))):Z=t.text}),!0),waitForIdle:async()=>{for(let t=0;(e.onCallbackFailure||t<16)&&(await e.callbackQueue.waitForIdle(),0!==x.size);t++)e.onCallbackFailure?await Promise.all([...x]):await Promise.allSettled([...x]);await e.callbackQueue.waitForIdle();const t=takeFinalizeAttachments(e.sessionKey);t.length>0&&!e.abortSignal.aborted&&!e.stream.isTerminated&&K(t)},getQueuedCounts:()=>({tool:Y.tool,block:Y.block,final:Y.final}),getAccumulatedBody:()=>a.text,getTerminalErrorText:()=>Z,markComplete:r=>{F(),b.size>0&&e.logger.debug?.(`[im-sink] dropping uncommitted lifecycle tool signals requestId=${e.requestId} buffered=${b.size}`),i="",R(n),m.receivedSnapshotCount>0&&logChatLatency(e.logger,"claw.text_preview.settled",e.requestId,t,{text_preview_snapshot_count:m.receivedSnapshotCount,text_preview_streaming_publish_count:m.streamingPublishCount,text_preview_terminal_publish_count:m.terminalPublishCount,text_preview_coalesced_snapshot_count:Math.max(0,m.receivedSnapshotCount-m.streamingPublishCount-m.terminalPublishCount),text_preview_publish_interval_ms:200});for(const t of f.values())try{"success"===r&&isUploadArtifactToolName(t.name)&&!t.durableUploadFilesEmitted&&V(t,{toolCallId:t.id,toolName:t.name}),"success"===r&&z(t),O(t.id)}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool finalization failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`)}b.clear(),ee=!0}},se={...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:s=>{te("partial",()=>{"string"==typeof s.text&&(!r&&s.text.length>0&&(r=!0,logChatLatency(e.logger,"claw.model.first_delta",e.requestId,t)),i=s.text,(e=>{const t=c;if(!t){if(a.text===e)return;return k(e),void(c={targetBody:e,receivedSnapshotCount:1,streamingPublishCount:1,terminalPublishCount:0})}t.targetBody!==e&&(t.targetBody=e,t.receivedSnapshotCount+=1,q(t))})(n+i))})},onReasoningStream:e=>{te("reasoning",()=>{if(!isNonEmptyString(e.text))return;const t=e.text,r=p??M(t);t!==r.text&&(r.text.startsWith(t)||(t.startsWith(r.text)?(r.text=t,r.receivedSnapshotCount+=1,A(r)):(F(),M(t))))})},onReasoningEnd:()=>{te("reasoning_end",()=>{F()})},onAssistantMessageStart:()=>{te("assistant_message_start",()=>{i="",l=!1,R(n),e.logger.debug?.(`[im-sink] assistant_message_start requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`)})},onToolStart:r=>{te("tool_start",()=>{const o="string"==typeof r.phase?r.phase:"start",a=resolveRuntimeToolName(r)??"tool",n=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);"update"!==o?(n&&s.set(n,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:a}),n&&B(n,{name:a,arguments:i,toolStartObserved:!0,pending:!0})):n&&B(n,{name:a,arguments:i,pending:!0})})},onItemEvent:e=>{te("item_event",()=>{const t="string"==typeof e.kind?e.kind.toLowerCase():"";if(t&&"tool"!==t)return;"string"==typeof e.phase&&e.phase.toLowerCase();const r=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);r&&s&&B(r,{name:s,itemEventObserved:!0,pending:!0})})}},oe={add(t){if(e.stream.isTerminated)return;const r=_;return e.stream.addPart(t),_+=1,r}},ae={add:e=>oe.add(e),replace:(t,r)=>!e.stream.isTerminated&&(e.stream.replacePart(t,r),!0)};return{dispatcher:re,replyOptions:se,commitAssistantMessage:t=>{te("assistant_message_commit",()=>{if(u.has(t.key))return;u.add(t.key),l=!0,Z=void 0,i="";const r=t.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");n+=r,R(n);for(const e of t.segments)"text"!==e.type?L(e.id,e.name,e.arguments,!0):T(e.text);if(void 0!==t.executedModel||void 0!==y){const r=toMessagePart({type:"custom",kind:"execution_model",data:{executed_model:t.executedModel??""}});void 0===y?(e.stream.addPart(r),y=_++):e.stream.replacePart(y,r)}})},observeToolResult:r=>{te("tool_result_observation",()=>{const a=r.toolCallId.trim(),n=r.toolName.trim();if(!a||!n)return;const i=!o.has(a);if(i){o.add(a);const i=s.get(a);logChatLatency(e.logger,"claw.tool.result",e.requestId,t,{tool:n,tool_elapsed_ms:void 0===i?void 0:Math.max(0,Date.now()-i),outcome:r.isError?"error":"success",...r.isError?{error:summarizeObservedToolError(r.rawResult)??"error"}:{}})}if(e.persistObservedToolResults&&i){const e=observedToolResultText(r.rawResult)??("tool_call"===n?"":void 0);if(void 0!==e){const t=X(a,n);if(t)Q(t,H({text:e,isError:r.isError},t.artifactMediaUrls),r.isError),t.observedResult={content:e,isError:r.isError,rawResult:r.rawResult},z(t),O(t.id),"tool_call"!==t.name||r.isError||V(t,{text:e,isError:r.isError,rawResult:r.rawResult});else{const t=U(a);t.name??=n,t.observedResult={content:e,isError:r.isError,rawResult:r.rawResult}}}}const l=searchSourceProvider(r.rawResult)??"unknown";let d=0;try{const t=extractSearchSources({toolName:n,rawResult:r.rawResult,isError:r.isError});if(d=t.length,0===d)return;const s=X(a,n);if(s)C(t,s.id);else{const e=U(a);e.name??=n,e.sources=((e,t)=>{if(!e?.length&&!t?.length)return;const r=[],s=new Map;for(const o of[...e??[],...t??[]]){const e=s.get(o.url);void 0===e?(s.set(o.url,r.length),r.push(o)):r[e]=mergeSearchSource(r[e],o)}return r})(e.sources,t)}e.logger.debug?.(`[im-sink] search source projection requestId=${e.requestId} tool=${n} provider=${l} source_count=${d}`)}catch{e.logger.warn?.(`[im-sink] search source projection ignored requestId=${e.requestId} tool=${n} provider=${l} source_count=${d}`)}})},getSourceCount:()=>Y.source,approvalParts:oe,questionParts:ae}}
|
package/dist/tool-activity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const record=e=>{if("string"==typeof e&&e.length<=65536)try{e=JSON.parse(e)}catch{return{}}return null===e||"object"!=typeof e||Array.isArray(e)?{}:e},text=e=>"string"==typeof e?e:"",short=e=>{const t=e.replace(/[\u0000-\u001f\u007f]/g," ").trim(),n=Array.from(t);return n.length<=180?t:`${n.slice(0,180).join("")}…`},action=(e,t="")=>{const n=short(t);return n?{kind:e,subject:n}:{kind:e}},filename=e=>text(e).replaceAll("\\","/").split("/").at(-1)??"",toolName=e=>text(e).split(":").at(-1)??"";export function toolActivity(e,t){let n,s=toolName(e),i=record(t);for(let e=0;e<3&&"tool_call"===s;e++)s=toolName(i.id??i.name),i=record(i.args??i.arguments??i.input);switch(s){case"exec":case"exec_command":n=commandActions(text(i.command??i.cmd));break;case"beeos_search":case"web_search":case"search_web":n=[action("search",text(i.query))];break;case"beeos_fetch":case"web_fetch":{let e="";try{const t=new URL(text(i.url));"https:"!==t.protocol&&"http:"!==t.protocol||(e=t.hostname)}catch{}n=[action("page",e)];break}case"read":case"read_file":n=[action("read",filename(i.fileName??i.file_path??i.path))];break;case"write":case"edit":case"write_file":case"edit_file":case"beeos_upload_file":n=Array.isArray(i.paths)&&i.paths.length>0?i.paths.slice(0,5).map(e=>action("save",filename(e))):[action("save",filename(i.fileName??i.file_path??i.path))];break;case"tool_search":{const e=Array.isArray(i.queries)?i.queries:[],t=e.length>0&&e.every(e=>{const t=text(record(e).query).toLowerCase();return/\b(image|poster|graphic)\b/.test(t)&&/\b(generate|create|design)\b/.test(t)&&!/\b(not|never|without)\b/.test(t)});n=[action(t?"find_design_tools":"find_tools")];break}case"image_generate":{const e=text(i.prompt).trim(),t=/^(?:制作|生成|设计)[^不无非。!?\n]{0,60}海报[。!!]?$/i.test(e)||/^(?:create|generate|design) (?:a |an )?poster[.!]?$/i.test(e);n=[action(t?"generate_poster":"generate_image")];break}default:n=[action("other")]}return{schema_version:1,source:"tool_arguments",actions:n}}function commandActions(e){const t=[action("command")];if(!e||e.length>8192)return t;if(/^(?:\/(?:usr\/(?:local\/)?)?bin\/)?python3[ \t]+-c[ \t]+(["'])import PIL; print\(PIL\.__version__\)\1$/.test(e.trim()))return[action("check_rendering")];if(/[$`\\(){}<>]/.test(e))return t;const n=/"[^"\n]*"|'[^'\n]*'|&&|\|\||[;|\n]|[^\s"';|&]+/g,s=[],i=[];let r=[],a=0,c="";for(const o of e.matchAll(n)){const n=e.slice(a,o.index);if(n.trim())return t;const l=o[0],h=[";","|","&&","||","\n"].includes(l);if(!h&&r.length>0&&0===n.length)return t;if(a=o.index+l.length,h){if(!r.length)return t;s.push(r),i.push(c),r=[],c=l}else r.push(l.startsWith('"')||l.startsWith("'")?l.slice(1,-1):l)}if(e.slice(a).trim()||!r.length&&["&&","||","|"].includes(c))return t;r.length&&(s.push(r),i.push(c));const o=[],l=new Set(["rsvg-convert","inkscape","chromium","chromium-browser","google-chrome","convert","magick","python3"]);let h=!1;for(const[e,n]of s.entries()){const s=h&&"|"===i[e];h=!1;const[r,...a]=n,c=r.replace(/^\/(?:usr\/(?:local\/)?)?bin\/([A-Za-z0-9_.-]+)$/,"$1");let u;if("echo"!==c||1!==a.length||!["---","DONE"].includes(a[0])){if("which"===c&&a.length>0&&a.every(e=>l.has(e))||"command"===c&&2===a.length&&"-v"===a[0]&&l.has(a[1])||"convert"===c&&1===a.length&&"-version"===a[0])u=action("check_rendering");else if("google-chrome"===c&&chromeScreenshot(a))u=action("generate_image",filename(a.find(e=>e.startsWith("--screenshot=")).slice(13)));else if("identify"===c&&1===a.length&&/^(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:png|jpe?g|webp|gif)$/.test(a[0]))u=action("read",filename(a[0]));else if("fc-list"===c&&(0===a.length||":lang=zh"===a[0]&&a.slice(1).every(e=>["family","style","fullname"].includes(e))))u=action(0===a.length?"list_fonts":"list_chinese_fonts"),h=!0;else{if("sort"===c&&s&&1===a.length&&"-u"===a[0]){h=!0;continue}if("ls"===c&&a.filter(e=>!e.startsWith("-")).length<=1&&a.every(e=>!e.startsWith("-")||/^-[lah]+$/.test(e)))u=action("list_files");else{if("head"===c&&1===a.length&&/^-\d+$/.test(a[0])&&o.length>0&&"|"===i[e]){h=s;continue}{const e="python3"===c&&"-m"===a[0]&&"pip"===a[1]?["pip",...a.slice(2)]:"uv"===c&&"pip"===a[0]?a:[c,...a];if("pip"!==e[0]&&"pip3"!==e[0]||"install"!==e[1]){if(!["python","python3","node"].includes(c)||1!==a.length||!/^(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:py|js|mjs)$/.test(a[0]))return t;u=action("run_script",filename(a[0]))}else{const n=e.slice(2).filter(e=>!["--quiet","-q","--upgrade","-U","--user","--no-cache-dir"].includes(e));if(!n.length||n.length>6||n.some(e=>!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}(?:==[A-Za-z0-9_.-]{1,40})?$/.test(e)))return t;u=action("install",n.join(", "))}}}}o.some(e=>e.kind===u.kind&&e.subject===u.subject)||o.push(u)}}return o.length?o:t}function chromeScreenshot(e){const t=e.map(e=>e.startsWith("--")?e.split("=",1)[0]:"input");return new Set(t).size===t.length&&e.includes("--headless=new")&&t.includes("--screenshot")&&t.includes("input")&&e.every(e=>["--headless=new","--no-sandbox","--disable-gpu","--hide-scrollbars"].includes(e)||/^--force-device-scale-factor=[1-4]$/.test(e)||/^--window-size=[1-9]\d{0,3},[1-9]\d{0,3}$/.test(e)||/^--screenshot=(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.png$/.test(e)||/^file:\/\/\/[A-Za-z0-9_./-]+\.html
|
|
1
|
+
const record=e=>{if("string"==typeof e&&e.length<=65536)try{e=JSON.parse(e)}catch{return{}}return null===e||"object"!=typeof e||Array.isArray(e)?{}:e},text=e=>"string"==typeof e?e:"",short=e=>{const t=e.replace(/[\u0000-\u001f\u007f]/g," ").trim(),n=Array.from(t);return n.length<=180?t:`${n.slice(0,180).join("")}…`},action=(e,t="")=>{const n=short(t);return n?{kind:e,subject:n}:{kind:e}},filename=e=>text(e).replaceAll("\\","/").split("/").at(-1)??"",toolName=e=>text(e).split(":").at(-1)??"";export function toolActivity(e,t){let n,s=toolName(e),i=record(t);for(let e=0;e<3&&"tool_call"===s;e++)s=toolName(i.id??i.name),i=record(i.args??i.arguments??i.input);switch(s){case"exec":case"exec_command":n=commandActions(text(i.command??i.cmd));break;case"beeos_search":case"web_search":case"search_web":n=[action("search",text(i.query))];break;case"beeos_fetch":case"web_fetch":{let e="";try{const t=new URL(text(i.url));"https:"!==t.protocol&&"http:"!==t.protocol||(e=t.hostname)}catch{}n=[action("page",e)];break}case"read":case"read_file":n=[action("read",filename(i.fileName??i.file_path??i.path))];break;case"write":case"edit":case"write_file":case"edit_file":case"beeos_upload_file":n=Array.isArray(i.paths)&&i.paths.length>0?i.paths.slice(0,5).map(e=>action("save",filename(e))):[action("save",filename(i.fileName??i.file_path??i.path))];break;case"tool_search":{const e=Array.isArray(i.queries)?i.queries:[],t=e.length>0&&e.every(e=>{const t=text(record(e).query).toLowerCase();return/\b(image|poster|graphic)\b/.test(t)&&/\b(generate|create|design)\b/.test(t)&&!/\b(not|never|without)\b/.test(t)});n=[action(t?"find_design_tools":"find_tools")];break}case"image_generate":{const e=text(i.prompt).trim(),t=/^(?:制作|生成|设计)[^不无非。!?\n]{0,60}海报[。!!]?$/i.test(e)||/^(?:create|generate|design) (?:a |an )?poster[.!]?$/i.test(e);n=[action(t?"generate_poster":"generate_image")];break}default:n=[action("other")]}return{schema_version:1,source:"tool_arguments",actions:n}}function commandActions(e){const t=[action("command")];if(!e||e.length>8192)return t;if(/^(?:\/(?:usr\/(?:local\/)?)?bin\/)?python3[ \t]+-c[ \t]+(["'])import PIL; print\(PIL\.__version__\)\1$/.test(e.trim()))return[action("check_rendering")];if(/[$`\\(){}<>]/.test(e))return t;const n=/"[^"\n]*"|'[^'\n]*'|&&|\|\||[;|\n]|[^\s"';|&]+/g,s=[],i=[];let r=[],a=0,c="";for(const o of e.matchAll(n)){const n=e.slice(a,o.index);if(n.trim())return t;const l=o[0],h=[";","|","&&","||","\n"].includes(l);if(!h&&r.length>0&&0===n.length)return t;if(a=o.index+l.length,h){if(!r.length)return t;s.push(r),i.push(c),r=[],c=l}else r.push(l.startsWith('"')||l.startsWith("'")?l.slice(1,-1):l)}if(e.slice(a).trim()||!r.length&&["&&","||","|"].includes(c))return t;r.length&&(s.push(r),i.push(c));const o=[],l=new Set(["rsvg-convert","inkscape","chromium","chromium-browser","google-chrome","convert","magick","python3"]);let h=!1;for(const[e,n]of s.entries()){const s=h&&"|"===i[e];h=!1;const[r,...a]=n,c=r.replace(/^\/(?:usr\/(?:local\/)?)?bin\/([A-Za-z0-9_.-]+)$/,"$1");let u;if("echo"!==c||1!==a.length||!["---","DONE"].includes(a[0])){if("which"===c&&a.length>0&&a.every(e=>l.has(e))||"command"===c&&2===a.length&&"-v"===a[0]&&l.has(a[1])||"convert"===c&&1===a.length&&"-version"===a[0])u=action("check_rendering");else if("google-chrome"===c&&chromeScreenshot(a))u=action("generate_image",filename(a.find(e=>e.startsWith("--screenshot=")).slice(13)));else if("identify"===c&&1===a.length&&/^(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:png|jpe?g|webp|gif)$/.test(a[0]))u=action("read",filename(a[0]));else if("fc-list"===c&&(0===a.length||":lang=zh"===a[0]&&a.slice(1).every(e=>["family","style","fullname"].includes(e))))u=action(0===a.length?"list_fonts":"list_chinese_fonts"),h=!0;else{if("sort"===c&&s&&1===a.length&&"-u"===a[0]){h=!0;continue}if("ls"===c&&a.filter(e=>!e.startsWith("-")).length<=1&&a.every(e=>!e.startsWith("-")||/^-[lah]+$/.test(e)))u=action("list_files");else{if("head"===c&&1===a.length&&/^-\d+$/.test(a[0])&&o.length>0&&"|"===i[e]){h=s;continue}{const e="python3"===c&&"-m"===a[0]&&"pip"===a[1]?["pip",...a.slice(2)]:"uv"===c&&"pip"===a[0]?a:[c,...a];if("pip"!==e[0]&&"pip3"!==e[0]||"install"!==e[1]){if(!["python","python3","node"].includes(c)||1!==a.length||!/^(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:py|js|mjs)$/.test(a[0]))return t;u=action("run_script",filename(a[0]))}else{const n=e.slice(2).filter(e=>!["--quiet","-q","--upgrade","-U","--user","--no-cache-dir"].includes(e));if(!n.length||n.length>6||n.some(e=>!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}(?:==[A-Za-z0-9_.-]{1,40})?$/.test(e)))return t;u=action("install",n.join(", "))}}}}o.some(e=>e.kind===u.kind&&e.subject===u.subject)||o.push(u)}}return o.length?o:t}function chromeScreenshot(e){const t=e.map(e=>e.startsWith("--")?e.split("=",1)[0]:"input");return new Set(t).size===t.length&&e.includes("--headless=new")&&t.includes("--screenshot")&&t.includes("input")&&e.every(e=>["--headless=new","--no-sandbox","--disable-gpu","--hide-scrollbars"].includes(e)||/^--force-device-scale-factor=[1-4]$/.test(e)||/^--window-size=[1-9]\d{0,3},[1-9]\d{0,3}$/.test(e)||/^--screenshot=(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.png$/.test(e)||/^file:\/\/\/[A-Za-z0-9_./-]+\.(?:html?|svg)$/.test(e))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{basename}from"node:path";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}from"./desktop-screenshot-tool.js";import{extractUploadResourceLinks}from"./extract-upload-uris.js";import{resolveUploadToolResultForEnrichment,writeEnrichmentCache}from"./result-cache.js";import{tryExtractLocalFiles}from"./local-file-extractors.js";import{getUploadDeps,uploadLocalFile}from"./upload-file.js";const UPLOAD_TOOL_NAMES=new Set([BEEOS_UPLOAD_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME]);export function isUploadArtifactToolName(e){if(!e)return!1;if(UPLOAD_TOOL_NAMES.has(e))return!0;const
|
|
1
|
+
import{basename}from"node:path";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}from"./desktop-screenshot-tool.js";import{extractUploadResourceLinks}from"./extract-upload-uris.js";import{resolveUploadToolResultForEnrichment,writeEnrichmentCache}from"./result-cache.js";import{tryExtractLocalFiles}from"./local-file-extractors.js";import{getUploadDeps,uploadLocalFile}from"./upload-file.js";import{isPlainRecord}from"../utils/text.js";const UPLOAD_TOOL_NAMES=new Set([BEEOS_UPLOAD_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME]);export function isUploadArtifactToolName(e){if(!e)return!1;if(UPLOAD_TOOL_NAMES.has(e))return!0;const o=e.toLowerCase();return o.includes("beeos_upload")||o.includes("beeos upload file")||"beeos file upload"===o||o.includes("beeos_desktop_screenshot")||o.includes("desktop screenshot")}export function isRemoteOrPlatformMediaUri(e){const o=e.trim();return!!o&&(!!o.startsWith("beeos-file://")||(!(!o.startsWith("http://")&&!o.startsWith("https://"))||!!o.startsWith("data:")))}export function isLocalMediaPath(e){const o=e.trim();return!(!o||isRemoteOrPlatformMediaUri(o))&&(!!o.startsWith("file://")||!!o.startsWith("/"))}function filePathFromMediaUri(e){const o=e.trim();if(o.startsWith("file://"))try{return decodeURIComponent(new URL(o).pathname)}catch{return o.slice(7)}return o}function linkNameFromUri(e){if(e.startsWith("beeos-file://"))return e.slice(13)||"file";try{const o=e.includes("://")?new URL(e).pathname:e;return basename(o)||"file"}catch{return"file"}}function pushUnique(e,o,t){t.uri&&!o.has(t.uri)&&(o.add(t.uri),e.push(t))}function wrappedUploadResult(e){const o="openclaw:beeos-claw:beeos_upload_file",t=e.params?.id;if("tool_call"!==e.toolName||t!==BEEOS_UPLOAD_TOOL_NAME&&t!==o||!isPlainRecord(e.params?.args)||!Array.isArray(e.params.args.paths)||0===e.params.args.paths.length||!e.params.args.paths.every(e=>"string"==typeof e&&e.trim())||!isPlainRecord(e.rawResult)||!0===e.rawResult.isError)return;const a=e.rawResult.details;return isPlainRecord(a)&&isPlainRecord(a.tool)&&isPlainRecord(a.result)&&a.tool.id===o&&a.tool.name===BEEOS_UPLOAD_TOOL_NAME&&"openclaw"===a.tool.source&&!0===a.result.ok&&!0!==a.result.isError&&(void 0===a.status||"completed"===a.status)?a.result:void 0}export async function resolveToolArtifactLinks(e){const o=[],t=new Set,a=getUploadDeps(),r=a?.logger;let i=!1;const l=wrappedUploadResult(e);if(void 0!==l)for(const e of extractUploadResourceLinks(l))/^beeos-file:\/\/[A-Za-z0-9][A-Za-z0-9_-]*$/.test(e.uri)&&pushUnique(o,t,e);if(isUploadArtifactToolName(e.toolName))try{const a=await resolveUploadToolResultForEnrichment(e.toolCallId,e.toolName===BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME?BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME:BEEOS_UPLOAD_TOOL_NAME);if(null!=a)for(const e of extractUploadResourceLinks(a))pushUnique(o,t,e)}catch(o){r?.warn?.(`[enrich] resolveUploadToolResultForEnrichment failed toolCallId=${e.toolCallId}: ${o instanceof Error?o.message:String(o)}`)}if(a){const l=new Set,s=tryExtractLocalFiles({toolCallId:e.toolCallId,toolName:e.toolName,rawResult:e.rawResult,rawText:e.rawText,params:e.params,cwd:e.cwd});for(const n of s)if(!l.has(n.filePath))try{r?.debug?.(`[enrich] Uploading local file ${n.filePath} for toolCallId=${e.toolCallId}`);const s=await uploadLocalFile(n.filePath,a.gateway,{fileNameOverride:n.suggestedName});r?.info?.(`[enrich] Uploaded: fileId=${s.fileId} toolCallId=${e.toolCallId}`),l.add(n.filePath),pushUnique(o,t,{uri:s.uri,name:s.name,...s.mimeType?{mimeType:s.mimeType}:{}}),i=!0}catch(e){r?.warn?.(`[enrich] Upload failed for ${n.filePath}: ${e instanceof Error?e.message:String(e)}, skipping`)}for(const s of e.payloadMediaUrls??[]){if(!isLocalMediaPath(s))continue;const n=filePathFromMediaUri(s);if(!l.has(n))try{r?.debug?.(`[enrich] Uploading payload media path ${n} for toolCallId=${e.toolCallId}`);const s=await uploadLocalFile(n,a.gateway);r?.info?.(`[enrich] Uploaded payload media: fileId=${s.fileId} toolCallId=${e.toolCallId}`),l.add(n),pushUnique(o,t,{uri:s.uri,name:s.name,...s.mimeType?{mimeType:s.mimeType}:{}}),i=!0}catch(e){r?.warn?.(`[enrich] Payload media upload failed for ${n}: ${e instanceof Error?e.message:String(e)}, skipping`)}}}for(const a of e.payloadMediaUrls??[])isRemoteOrPlatformMediaUri(a)&&pushUnique(o,t,{uri:a.trim(),name:linkNameFromUri(a.trim())});if(o.length>0&&(i||o.some(e=>e.uri.startsWith("beeos-file://")))){const t=o.map(e=>({uri:e.uri,name:e.name,...e.mimeType?{mimeType:e.mimeType}:{}}));writeEnrichmentCache(e.toolCallId,t).catch(o=>{r?.warn?.(`[enrich] Cache write failed for toolCallId=${e.toolCallId}: ${o instanceof Error?o.message:String(o)}`)})}return o}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeos-claw",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.78",
|
|
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>",
|
|
@@ -90,5 +90,5 @@
|
|
|
90
90
|
"brace-expansion": ">=5.0.9",
|
|
91
91
|
"@beeos-ai/beeos-types": "$@beeos-ai/beeos-types"
|
|
92
92
|
},
|
|
93
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "3978246cec569daab71716c136960a527317d1ac"
|
|
94
94
|
}
|