beeos-claw 0.6.74 → 0.6.75
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
|
@@ -195,24 +195,80 @@ Connects local OpenClaw agents to the BeeOS platform via the Agent Communication
|
|
|
195
195
|
|
|
196
196
|
beeos-claw is **only the ACP→v3 protocol translator** for outbound agent replies. All Message Service (MS) Envelope v3 streaming machinery — PATCH batching, snapshot semantics, lazy `MessageClient` connect, open-failure `sendV3` fallback, `stopReason` whitelisting — lives in `@beeos-ai/message-sdk` (`MessageStream` + `MessageClient`).
|
|
197
197
|
|
|
198
|
-
`src/
|
|
198
|
+
Interactive replies use `src/runtime/im-reply-sink.ts`; adopted background runs use
|
|
199
|
+
`src/runtime/message-service-run-output-channel.ts`. Both create and replace
|
|
200
|
+
`tool_use` parts through `toMessagePart` in `src/message-client-types.ts`.
|
|
201
|
+
The same resulting parts are streamed and persisted, so history and reconnect
|
|
202
|
+
use the server's recorded action information.
|
|
199
203
|
|
|
200
|
-
|
|
201
|
-
|---|---|
|
|
202
|
-
| `agent_message_chunk` | `stream.appendBody(text)` |
|
|
203
|
-
| `agent_thought_chunk` | `stream.appendThinking(text)` |
|
|
204
|
-
| `tool_call` | `stream.appendToolUse(id, name, args, "streaming")` |
|
|
205
|
-
| `tool_call_update` (completed / failed) | `stream.appendToolResult(id, content, isError)` |
|
|
206
|
-
| `tool_call_update` content blocks (`resource_link` / `file`) | `stream.addPart({type: "file", ...})` |
|
|
204
|
+
Each `tool_use` includes a server-generated activity:
|
|
207
205
|
|
|
208
|
-
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"type": "tool_use",
|
|
209
|
+
"id": "call-fonts",
|
|
210
|
+
"name": "exec",
|
|
211
|
+
"arguments": { "command": "fc-list :lang=zh" },
|
|
212
|
+
"state": "streaming",
|
|
213
|
+
"activity": {
|
|
214
|
+
"schema_version": 1,
|
|
215
|
+
"source": "tool_arguments",
|
|
216
|
+
"actions": [{ "kind": "list_chinese_fonts" }]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
209
220
|
|
|
210
|
-
|
|
221
|
+
Clients translate `kind` into localized wording and use `subject` only when
|
|
222
|
+
present. Supported kinds are `search`, `page`, `read`, `save`, `command`, `other`,
|
|
223
|
+
`find_design_tools`, `find_tools`, `check_rendering`, `list_chinese_fonts`,
|
|
224
|
+
`list_fonts`, `list_files`, `install`, `run_script`, `generate_poster`, and
|
|
225
|
+
`generate_image`. Actions describe the invocation, not its success. A compound
|
|
226
|
+
command can contain multiple actions; unknown shell syntax falls back to
|
|
227
|
+
`command` for the whole invocation. Standard `/bin`, `/usr/bin`, and
|
|
228
|
+
`/usr/local/bin` executable paths use the same rules as bare names. Script
|
|
229
|
+
execution shows the script basename without guessing what it produces.
|
|
230
|
+
`write`/`edit` calls show `save` with the file basename. Unknown tools use `other`.
|
|
231
|
+
|
|
232
|
+
`src/tool-activity.ts` reads actual tool names and arguments, including bounded
|
|
233
|
+
`tool_call` wrappers. It ignores `title`, `tasktitle`, and incoming `activity`.
|
|
234
|
+
Subjects are limited to search queries, URL hostnames, file basenames, and
|
|
235
|
+
allowlisted package names. Raw commands, inline code, URL credentials/query
|
|
236
|
+
strings, and full local paths are not activity subjects. The classifier never
|
|
237
|
+
executes commands and does not alter approval requirements.
|
|
238
|
+
|
|
239
|
+
Tool results and lifecycle events correlate by the explicit canonical call ID.
|
|
240
|
+
Early results wait for that exact card; anonymous summaries are ignored even
|
|
241
|
+
when just one tool is pending. A delayed duplicate cannot finish a different
|
|
242
|
+
call with the same name. Native retained replies persist host-observed results
|
|
243
|
+
by their call ID. Background `pending`/`in_progress` updates keep the tool
|
|
244
|
+
streaming; only `completed`/`failed` updates settle it. Late progress cannot
|
|
245
|
+
replace a terminal result. Error information remains in the matching
|
|
246
|
+
`tool_progress.data.is_error`, separate from activity.
|
|
247
|
+
|
|
248
|
+
Acceptance coverage lives in `message-client-types.test.ts`,
|
|
249
|
+
`runtime/im-reply-sink.test.ts`, and
|
|
250
|
+
`runtime/message-service-run-output-channel.test.ts`: compound command actions,
|
|
251
|
+
misleading titles and prompts, secret-bearing shell/URL arguments, wrapped
|
|
252
|
+
calls, delayed anonymous approval errors, early results for a different ID,
|
|
253
|
+
and approval waiting versus terminal updates.
|
|
254
|
+
|
|
255
|
+
MS transport lifecycle, PATCH retries, fallback writes, and stop reasons remain
|
|
256
|
+
the SDK's responsibility. See the
|
|
257
|
+
[`@beeos-ai/message-sdk` README](../../sdks/message-sdk-node/README.md#streaming-agent-replies--envelope-v3).
|
|
211
258
|
|
|
212
259
|
### File Transfer
|
|
213
260
|
|
|
214
261
|
Provides the `beeos_upload_file` tool for agents to upload files from the sandbox to BeeOS platform storage. Also supports file staging via the `POST /beeos/files/stage` HTTP endpoint for downloading files from the platform into the agent sandbox.
|
|
215
262
|
|
|
263
|
+
Artifact generation guidance uses one executable per `exec` call so native
|
|
264
|
+
approval can bind the command and its file operands. Discovery uses `ls` / `read`
|
|
265
|
+
or a standalone `which`; shell builtins such as `command -v`, mixed commands,
|
|
266
|
+
redirections and pipelines are not reliable approval requests. A generated script
|
|
267
|
+
is written to a workspace file before a separate interpreter invocation. The
|
|
268
|
+
agent must wait for the real result, verify the artifact, and upload it before
|
|
269
|
+
claiming delivery. Binding errors and pending approvals are never completion,
|
|
270
|
+
and the guidance does not relax the host's approval policy.
|
|
271
|
+
|
|
216
272
|
The plugin records office/PDF paths produced by `write` / `exec` and uploads any
|
|
217
273
|
that were not already delivered. Tool-result enrichment attaches those files
|
|
218
274
|
during the turn; `before_agent_finalize` uploads leftovers without requesting
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const toMessagePart=e
|
|
1
|
+
import{toolActivity}from"./tool-activity.js";export const toMessagePart=t=>{const e=normalizeJson(t,new WeakSet);return null===e||"object"!=typeof e||Array.isArray(e)||"tool_use"!==e.type||(e.activity=toolActivity(e.name,e.arguments??e.input)),e};const normalizeJson=(t,e)=>{if(null===t||"string"==typeof t||"boolean"==typeof t)return t;if("number"==typeof t)return Number.isFinite(t)?t:null;if("bigint"==typeof t)return t.toString();if(Array.isArray(t)){if(e.has(t))return"[Circular]";e.add(t);const o=t.map(t=>normalizeJson(t,e));return e.delete(t),o}if("object"==typeof t){if(e.has(t))return"[Circular]";e.add(t);const o={};for(const[r,n]of Object.entries(t))void 0!==n&&"function"!=typeof n&&"symbol"!=typeof n&&(o[r]=normalizeJson(n,e));return e.delete(t),o}return String(t)};export const projectedMessages=t=>Object.values(t.getSnapshot().messages);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const buildToolRoutingHint=(e,t)=>`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS of web pages, interact with elements (click, type), or visually inspect pages.\n- ${t}: Use to capture the agent desktop (XFCE/VNC DISPLAY). Prefer this over shell/python X11 dumps; never invent raw Xlib/PIL capture.\n- beeos_search: Use ONLY for text-based information retrieval (fact-finding, research queries). Never for page interaction or screenshots.\n- beeos_fetch: Use ONLY to extract article text from a known URL. Never for visual content or screenshots.\n- ${e}\nWhen the user says "open", "visit", "go to", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool. When they ask for a desktop / screen / VNC capture of the agent workspace, use ${t}.`;const THIRD_PARTY_CONTEXT='This conversation is in a third-party channel (Lark/Telegram/Discord/Slack); Canvas surfaces are not visible here. Do NOT use render_ui, close_ui, or render_form. Use built-in tools directly (e.g. the `cron` tool with `action:"add"` for scheduled tasks) and respond with plain text.';export const buildAppendSystemContext=e=>e.thirdParty?THIRD_PARTY_CONTEXT:`${e.canvasSystemPrompt}${e.toolRoutingHint}`;export function canvasToolBlockReason(e){return e.thirdParty?"Canvas tools are not available in this channel. Respond with plain text only.":e.hasSession?void 0:"Canvas session unknown. Respond with plain text instead."}
|
|
1
|
+
export const buildToolRoutingHint=(e,t)=>`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS of web pages, interact with elements (click, type), or visually inspect pages.\n- ${t}: Use to capture the agent desktop (XFCE/VNC DISPLAY). Prefer this over shell/python X11 dumps; never invent raw Xlib/PIL capture.\n- beeos_search: Use ONLY for text-based information retrieval (fact-finding, research queries). Never for page interaction or screenshots.\n- beeos_fetch: Use ONLY to extract article text from a known URL. Never for visual content or screenshots.\n- exec: Keep each call to one executable and its arguments. Native approval must bind the exact executable and any script before it can run. Prefer ls/read for inspecting files; for executable discovery use a standalone which call, not the shell builtin command -v. Do not combine discovery, redirections, pipelines, or several commands into one approval request.\n- To generate an artifact with a script, write the script to a workspace file first, then invoke the discovered interpreter with that file as a separate exec call. Wait for the actual execution result, check the output file, and upload it with beeos_upload_file before claiming delivery. Do not claim a local path is a downloadable attachment.\n- An approval binding error means the command was not executed. Explain the failure and use a supported command shape without changing its purpose. Never disable approval, change the execution host to evade it, or treat a pending approval as success. A denied request must stay denied.\n- ${e}\nWhen the user says "open", "visit", "go to", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool. When they ask for a desktop / screen / VNC capture of the agent workspace, use ${t}.`;const THIRD_PARTY_CONTEXT='This conversation is in a third-party channel (Lark/Telegram/Discord/Slack); Canvas surfaces are not visible here. Do NOT use render_ui, close_ui, or render_form. Use built-in tools directly (e.g. the `cron` tool with `action:"add"` for scheduled tasks) and respond with plain text.';export const buildAppendSystemContext=e=>e.thirdParty?THIRD_PARTY_CONTEXT:`${e.canvasSystemPrompt}${e.toolRoutingHint}`;export function canvasToolBlockReason(e){return e.thirdParty?"Canvas tools are not available in this channel. Respond with plain text only.":e.hasSession?void 0:"Canvas session unknown. Respond with plain text instead."}
|
|
@@ -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 s=new Map,n=new Set,o={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=new Map,v=[],b=[],y=new Map,I=new Map;let _,S=0,x=1;const k=(e,t={})=>({schema_version:1,seq:e,...t}),q=new Set,$=new Set,w=t=>{t!==o.text&&(t.startsWith(o.text)?e.stream.appendBody(t.slice(o.text.length)):e.stream.setBody(t),o.text=t)},P=e=>{void 0===e.publishTimer&&(e.publishTimer=setTimeout(()=>{e.publishTimer=void 0,ue("body_preview_flush",()=>{c===e&&(e=>{o.text!==e.targetBody&&(w(e.targetBody),e.streamingPublishCount+=1)})(e)})},200))},T=e=>{const t=c;t?(void 0!==t.publishTimer&&(clearTimeout(t.publishTimer),t.publishTimer=void 0),t.targetBody=e,o.text!==e&&(w(e),t.terminalPublishCount+=1),m.receivedSnapshotCount+=t.receivedSnapshotCount,m.streamingPublishCount+=t.streamingPublishCount,m.terminalPublishCount+=t.terminalPublishCount,c=void 0):w(e)},C=t=>{if(void 0!==t.partIndex)return;const r=S,s=x;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:s,data:k(s)})),t.partIndex=r,S+=1,x+=1},E=()=>{if(!d){d=!0;for(const e of g)C(e)}},R=e=>{const t={text:e};g.push(t),d&&C(t)},M=(t,r)=>{for(const s of t){const t=I.get(s.url);if(t){const r=mergeSearchSource(t,s);if(r.title===t.title&&r.snippet===t.snippet)continue;const n={...r,partIndex:t.partIndex,displaySeq:t.displaySeq,toolUseId:t.toolUseId};e.stream.replacePart(t.partIndex,toMessagePart({type:"source",url:n.url,...n.title?{title:n.title}:{},...n.snippet?{snippet:n.snippet}:{},tool_use_id:n.toolUseId,_seq:n.displaySeq,data:k(n.displaySeq)})),I.set(s.url,n);continue}const n={...s,partIndex:S,displaySeq:x,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:n.displaySeq,data:k(n.displaySeq)})),I.set(s.url,n),S+=1,x+=1,ie.source+=1}},O=(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:k(e.displaySeq,{started_at_ms:e.startedAtMs,...void 0!==s?{duration_ms:s}:{}})}),A=t=>{E();const r={partIndex:S,displaySeq:x,text:t,publishedText:t,receivedSnapshotCount:1,streamingPublishCount:1,startedAtMs:Date.now(),startedAtMonotonicMs:performance.now()};return e.stream.addPart(O(r,t,"streaming")),S+=1,x+=1,p=r,r},F=t=>{void 0===t.publishTimer&&(t.publishTimer=setTimeout(()=>{t.publishTimer=void 0,ue("reasoning_snapshot_flush",()=>{p===t&&(t=>{t.publishedText!==t.text&&(e.stream.replacePart(t.partIndex,O(t,t.text,"streaming")),t.publishedText=t.text,t.streamingPublishCount+=1)})(t)})},200))},N=()=>{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,O(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})},U=e=>{const t=v.indexOf(e);t>=0&&v.splice(t,1)},j=e=>{const t=e?v.filter(t=>f.get(t)?.name===e):v;return 1===t.length?t[0]:void 0},L=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:s}]=t;return b.splice(s,1),r},B=(e,t)=>{if(h.set(e,t),e===t)return;const r=y.get(e);if(!r)return;const s=Q(t);J(s,r),y.delete(e);const n=f.get(t);n&&(Z(n,s),y.delete(t))},z=e=>h.get(e)??e,D=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),Q=e=>{let t=y.get(e);return t||(t={},y.set(e,t)),t},W=(e,t)=>{if(!e?.length&&!t?.length)return;const r=[],s=new Map;for(const n of[...e??[],...t??[]]){const e=s.get(n.url);void 0===e?(s.set(n.url,r.length),r.push(n)):r[e]=mergeSearchSource(r[e],n)}return r},J=(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.observedResult??=t.observedResult,e.sources=W(e.sources,t.sources)},H=(t,r,s)=>{const n="tool"===t.name&&"tool"!==r?r:t.name,o=!D(t.arguments)&&D(s)?s:t.arguments;n===t.name&&o===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:n,arguments:o,state:t.state,_seq:t.displaySeq,data:k(t.displaySeq)})),t.name=n,t.arguments=o)},K=(t,r,s,n)=>{const o=f.get(t);if(o)return H(o,r,s),n&&!v.includes(t)&&"streaming"===o.state&&v.push(t),o;E();const a=y.get(t),i="tool"!==r?r:a?.name&&"tool"!==a.name?a.name:r,l=D(s)?s:a?.arguments??s,d=((t,r,s)=>{const n=S,o=x;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:s,state:"streaming",_seq:o,data:k(o)})),S+=1,x+=1,{partIndex:n,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 f.set(t,u),(n||u.toolStartObserved||u.itemEventObserved)&&(v.includes(t)||"streaming"!==u.state||v.push(t)),a&&Z(u,a),y.delete(t),u},G=(e,t)=>{const r=(e=>{const t=h.get(e);return t?f.get(t):f.get(e)})(e);if(r)return void 0===t.name&&void 0===t.arguments||H(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)&&!v.includes(r.id)&&"streaming"===r.state&&v.push(r.id),r;const s=Q(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)},V=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:k(t.displaySeq)})),t.state="done")},X=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(X).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${X(t)}`).join(",")}}`},Y=(t,r,s)=>{const n=`${s?"1":"0"}:${X(r)}`;if(t.progressSignature===n)return;const o=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=S;return e.stream.addPart(o),t.progressPartIndex=r,t.progressSignature=n,void(S+=1)}e.stream.replacePart(t.progressPartIndex,o),t.progressSignature=n},Z=(e,t)=>{if(t.progress?.length)for(const r of t.progress)Y(e,r.content,r.isError);t.observedResult&&(Y(e,t.observedResult.content,t.observedResult.isError),e.observedResult=t.observedResult),(t.progress?.length||t.observedResult)&&(V(e),U(e.id)),t.sources?.length&&M(t.sources,e.id)},ee=e=>[...new Set([e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)).map(e=>e.trim()).filter(Boolean))],te=(e,t=[])=>{const r=[...new Set([...ee(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.":""},re=t=>{for(const r of t)e.stream.addPart(toMessagePart({type:"resource_link",uri:r.uri,name:r.name,...r.mimeType?{mimeType:r.mimeType}:{}})),S+=1},se=t=>{q.add(t);const r=()=>{q.delete(t)};e.onCallbackFailure?t.then(r,r):t.finally(r)},ne=(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),n=(async()=>{let n=[];try{n=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:ee(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!==n.length?(e.logger.info?.(`[im-sink] tool artifact enrichment resolved ${n.length} link(s) requestId=${e.requestId} toolUseId=${t.id}`),await new Promise((s,o)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const s=n.map(e=>e.uri);t.artifactMediaUrls=s;const o=t.observedResult;Y(t,te(o?{text:o.content,isError:o.isError}:r,s),o?.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),S+=1,re(r))})(t,n,o?.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&&o(r)}finally{s()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(s,o)})):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});se(n)},oe=(e,t,r)=>{const s=h.get(e);if(s)return G(s,{name:t,arguments:r});const n=G(e,{name:t,arguments:r});if(n)return n;const o=j(t);if(o)return B(e,o),G(o,{name:t,arguments:r});G(e,{name:t,arguments:r})},ae=(e,t)=>{const r=h.get(e);if(r)return G(r,{name:t,itemEventObserved:!0,pending:!0});const s=G(e,{name:t,itemEventObserved:!0,pending:!0});if(s)return s;const n=(e=>{const t=v.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(n)return B(e,n),G(n,{name:t,itemEventObserved:!0,pending:!0});const o=L(t);G(e,{name:t,arguments:o?.arguments,itemEventObserved:!0,pending:!0})},ie={tool:0,block:0,final:0,source:0};let le,de=!1;const ue=(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):!de||(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)}})},ce={sendToolResult:t=>(ue("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),s=resolveRuntimeToolName(t),n=resolveRuntimeToolArguments(t),o=te(t),a=!0===t.isError;let i;if(r){if(i=oe(r,s,n),!i){const e=Q(r);return s&&(e.name=s),D(n)&&(e.arguments=n),e.progress=[...e.progress??[],{content:o,isError:a}],void(ie.tool+=1)}}else{const t=s?j(s):j();if(!t)return L(s),void e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${s??"unknown"} pending=${v.length}`);i=G(t,{name:s,arguments:n})}i&&(ie.tool+=1,i.observedResult||Y(i,o,a),V(i),U(i.id),ne(i,t))}),!0),sendBlockReply:e=>(ue("block_reply",()=>{ie.block+=1,isNonEmptyString(e.text)&&(le=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:t=>(ue("final_reply",()=>{var r;(ie.final+=1,!0!==t.isError)?((t=>{const r=ee(t).filter(e=>!$.has(e));if(0===r.length)return;for(const e of r)$.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)$.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;re(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)$.delete(e)})();se(s)})(t),isNonEmptyString(t.text)&&(le=void 0,l||(r=t.text,i="",a+=r,T(a),R(r),l=!0))):le=t.text}),!0),waitForIdle:async()=>{for(let t=0;(e.onCallbackFailure||t<16)&&(await e.callbackQueue.waitForIdle(),0!==q.size);t++)e.onCallbackFailure?await Promise.all([...q]):await Promise.allSettled([...q]);await e.callbackQueue.waitForIdle();const t=takeFinalizeAttachments(e.sessionKey);t.length>0&&!e.abortSignal.aborted&&!e.stream.isTerminated&&re(t)},getQueuedCounts:()=>({tool:ie.tool,block:ie.block,final:ie.final}),getAccumulatedBody:()=>o.text,getTerminalErrorText:()=>le,markComplete:r=>{N(),(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="",T(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&&ne(t,{toolCallId:t.id,toolName:t.name}),"success"===r&&V(t),U(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.length=0,y.clear(),de=!0}},me={...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=>{ue("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(o.text===e)return;return w(e),void(c={targetBody:e,receivedSnapshotCount:1,streamingPublishCount:1,terminalPublishCount:0})}t.targetBody!==e&&(t.targetBody=e,t.receivedSnapshotCount+=1,P(t))})(a+i))})},onReasoningStream:e=>{ue("reasoning",()=>{if(!isNonEmptyString(e.text))return;const t=e.text,r=p??A(t);t!==r.text&&(r.text.startsWith(t)||(t.startsWith(r.text)?(r.text=t,r.receivedSnapshotCount+=1,F(r)):(N(),A(t))))})},onReasoningEnd:()=>{ue("reasoning_end",()=>{N()})},onAssistantMessageStart:()=>{ue("assistant_message_start",()=>{i="",l=!1,T(a),e.logger.debug?.(`[im-sink] assistant_message_start requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`)})},onToolStart:r=>{ue("tool_start",()=>{const n="string"==typeof r.phase?r.phase:"start",o=resolveRuntimeToolName(r)??"tool",a=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);if("update"===n)return void(a&&G(z(a),{name:o,arguments:i,pending:!0}));if(a&&s.set(a,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:o}),a){G(a,{name:o,arguments:i,toolStartObserved:!0,pending:!0});const e=j(o);return void(e&&e!==a&&!h.has(a)&&(B(a,e),G(e,{name:o,arguments:i,toolStartObserved:!0,pending:!0})))}const l=(d=o,v.map(e=>f.get(e)).filter(e=>void 0!==e&&(!d||e.name===d)&&e.itemEventObserved));var d;if(1===l.length)return void G(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}):G(u[0][0],{name:o,arguments:i,toolStartObserved:!0,pending:!0})})},onItemEvent:e=>{ue("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",s=resolveRuntimeToolCallId(e)??("string"==typeof e.itemId&&e.itemId.trim()?e.itemId.trim():void 0),n=resolveRuntimeToolName(e)??("string"==typeof e.title&&e.title.trim()?e.title.trim():void 0);s&&n&&("update"!==r?ae(s,n):G(z(s),{name:n,itemEventObserved:!0,pending:!0}))})}},ge={add(t){if(e.stream.isTerminated)return;const r=S;return e.stream.addPart(t),S+=1,r}},pe={add:e=>ge.add(e),replace:(t,r)=>!e.stream.isTerminated&&(e.stream.replacePart(t,r),!0)};return{dispatcher:ce,replyOptions:me,commitAssistantMessage:t=>{ue("assistant_message_commit",()=>{if(u.has(t.key))return;u.add(t.key),l=!0,le=void 0,i="";const r=t.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");a+=r,T(a);for(const e of t.segments)"text"!==e.type?K(e.id,e.name,e.arguments,!0):R(e.text);if(void 0!==t.executedModel||void 0!==_){const r=toMessagePart({type:"custom",kind:"execution_model",data:{executed_model:t.executedModel??""}});void 0===_?(e.stream.addPart(r),_=S++):e.stream.replacePart(_,r)}})},observeToolResult:r=>{ue("tool_result_observation",()=>{const o=r.toolCallId.trim(),a=r.toolName.trim();if(!o||!a)return;const i=!n.has(o);if(i){n.add(o);const i=s.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",...r.isError?{error:summarizeObservedToolError(r.rawResult)??"error"}:{}})}if(e.persistObservedToolResults&&i){const e=observedToolResultText(r.rawResult);if(void 0!==e){const t=oe(o,a);if(t)Y(t,te({text:e,isError:r.isError},t.artifactMediaUrls),r.isError),t.observedResult={content:e,isError:r.isError},V(t),U(t.id);else{const t=Q(z(o));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 s=oe(o,a);if(s)M(t,s.id);else{const e=Q(z(o));e.name??=a,e.sources=W(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:()=>ie.source,approvalParts:ge,questionParts:pe}}
|
|
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 +1 @@
|
|
|
1
|
-
import{dedupeMessageServiceArtifacts,extractMessageToolCaption,messageServiceFileParts,messageServiceRunMessageId,sanitizeMessageServiceText}from"./message-service-output-format.js";import{toMessagePart}from"../message-client-types.js";import{extractSearchSources,mergeSearchSource,searchSourceProvider}from"./search-source-extractor.js";import{toMessageServiceCompletedStopReason}from"./message-service-stop-reason.js";import{messageStopReasonForRuntimeFailure}from"./im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./write-runtime-failure.js";export class MessageServiceRunOutputChannel{deps;id="message_service";runs=new Map;constructor(e){this.deps=e}open(e){if(!e.conversationId)throw new Error(`Message Service run has no conversation: ${e.runId}`);this.runs.set(e.runId,{assistantText:"",reasoningText:"",artifacts:[],artifactUris:new Set,tools:[],toolsById:new Map,sourcesByUrl:new Map})}project(e,t){const r=this.state(e.runId);try{switch(t.type){case"assistant_delta":this.projectAssistantDelta(e,r,t.content);break;case"thought_chunk":this.projectThoughtDelta(e,r,t.content);break;case"tool_call":this.projectToolStart(e,r,t);break;case"tool_call_update":this.projectToolUpdate(e,r,t)}}catch(t){throw this.reportProjectionFailure(e,t,"project"),t}}async terminal(e,t,r){const s=this.state(e.runId);try{const o=dedupeMessageServiceArtifacts(s.artifacts);let n,a=sanitizeMessageServiceText(s.assistantText)||sanitizeMessageServiceText(s.caption);r.expectedUploadArtifacts&&0===o.length?(a="The generated attachment could not be delivered.",n="attachment_delivery_failed"):"failed"===t.kind&&(a||=t.message,n="interrupted"===t.mode?t.errorCode:"error");const i="failed"===r.persistenceReconciliation||"timed_out"===r.persistenceReconciliation;!i||a||0!==o.length||0!==s.tools.length||s.reasoningText||"complete"!==t.kind||(a="The response could not be delivered.",n=`persistence_reconciliation_${r.persistenceReconciliation}`),i&&this.deps.writeObs({component:"message-service-run-output",domain:"lifecycle",name:"timed_out"===r.persistenceReconciliation?"run_output.reconciliation_timeout":"run_output.reconciliation_failed",severity:"warn",payload:{conversationId:e.conversationId,runId:e.runId,status:r.persistenceReconciliation}});if(!(Boolean(a)||o.length>0||s.tools.length>0||Boolean(s.reasoningText))&&"complete"===t.kind)return;const c=this.stream(e,s);if(a!==s.assistantText&&c.setBody(a),this.finishReasoning(s),this.finishTools(s),"cancelled"===t.kind)await c.cancel({stopReason:"user_stop"});else if("attachment_delivery_failed"===n||n?.startsWith("persistence_reconciliation_"))await c.fail({stopReason:n});else if("failed"===t.kind)await writeRuntimeFailure(c,{body:a??"",stopReason:t.classification?messageStopReasonForRuntimeFailure(t.classification):"error",businessErrorCode:t.classification?.businessErrorCode});else{const e="complete"===t.kind?toMessageServiceCompletedStopReason(t.stopReason):"end_turn";await c.finalize({stopReason:e})}this.deps.writeObs({component:"message-service-run-output",domain:"lifecycle",name:n?"run_output.failed":"run_output.completed",severity:n?"warn":"info",payload:{conversationId:e.conversationId,runId:e.runId,textLen:a.length,artifactCount:o.length,toolCount:s.tools.length,sourceCount:s.sourcesByUrl.size}})}catch(t){throw this.reportProjectionFailure(e,t,"terminal"),t}finally{this.runs.delete(e.runId)}}projectAssistantDelta(e,t,r){const s=asRecord(r);if(!s)return;if("text"===s.type&&"string"==typeof s.text)return t.assistantText+=s.text,void this.stream(e,t).appendBody(s.text);const o=artifactFromBlock(s);o&&this.projectArtifact(e,t,o)}projectThoughtDelta(e,t,r){const s=asRecord(r);if("text"!==s?.type||"string"!=typeof s.text)return;t.reasoningText+=s.text;const o=this.stream(e,t),n=toMessagePart({type:"thinking",text:t.reasoningText,state:"streaming"});void 0===t.reasoningPartIndex?(t.reasoningPartIndex=o.parts.length,o.addPart(n)):o.replacePart(t.reasoningPartIndex,n)}projectToolStart(e,t,r){const s={id:String(r.toolCallId),name:r.name??r.title,arguments:r.rawInput??parseArguments(r.arguments),isError:!1,completed:!1};t.tools.push(s),t.toolsById.set(s.id,s),this.upsertToolUse(e,t,s);const o=extractMessageToolCaption(s.name,s.arguments);o&&(t.caption=o)}projectToolUpdate(e,t,r){const s=String(r.toolCallId);let o=t.toolsById.get(s);o||(o={id:s,name:r.name??r.title??"tool",arguments:{},isError:!1,completed:!1},t.tools.push(o),t.toolsById.set(s,o));const n=r.name??r.title;"tool"===o.name&&n&&(o.name=n),o.result=r.result??r.content??r.rawOutput??r.output??r.text,o.isError="failed"===r.status,o.completed
|
|
1
|
+
import{dedupeMessageServiceArtifacts,extractMessageToolCaption,messageServiceFileParts,messageServiceRunMessageId,sanitizeMessageServiceText}from"./message-service-output-format.js";import{toMessagePart}from"../message-client-types.js";import{extractSearchSources,mergeSearchSource,searchSourceProvider}from"./search-source-extractor.js";import{toMessageServiceCompletedStopReason}from"./message-service-stop-reason.js";import{messageStopReasonForRuntimeFailure}from"./im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./write-runtime-failure.js";export class MessageServiceRunOutputChannel{deps;id="message_service";runs=new Map;constructor(e){this.deps=e}open(e){if(!e.conversationId)throw new Error(`Message Service run has no conversation: ${e.runId}`);this.runs.set(e.runId,{assistantText:"",reasoningText:"",artifacts:[],artifactUris:new Set,tools:[],toolsById:new Map,sourcesByUrl:new Map})}project(e,t){const r=this.state(e.runId);try{switch(t.type){case"assistant_delta":this.projectAssistantDelta(e,r,t.content);break;case"thought_chunk":this.projectThoughtDelta(e,r,t.content);break;case"tool_call":this.projectToolStart(e,r,t);break;case"tool_call_update":this.projectToolUpdate(e,r,t)}}catch(t){throw this.reportProjectionFailure(e,t,"project"),t}}async terminal(e,t,r){const s=this.state(e.runId);try{const o=dedupeMessageServiceArtifacts(s.artifacts);let n,a=sanitizeMessageServiceText(s.assistantText)||sanitizeMessageServiceText(s.caption);r.expectedUploadArtifacts&&0===o.length?(a="The generated attachment could not be delivered.",n="attachment_delivery_failed"):"failed"===t.kind&&(a||=t.message,n="interrupted"===t.mode?t.errorCode:"error");const i="failed"===r.persistenceReconciliation||"timed_out"===r.persistenceReconciliation;!i||a||0!==o.length||0!==s.tools.length||s.reasoningText||"complete"!==t.kind||(a="The response could not be delivered.",n=`persistence_reconciliation_${r.persistenceReconciliation}`),i&&this.deps.writeObs({component:"message-service-run-output",domain:"lifecycle",name:"timed_out"===r.persistenceReconciliation?"run_output.reconciliation_timeout":"run_output.reconciliation_failed",severity:"warn",payload:{conversationId:e.conversationId,runId:e.runId,status:r.persistenceReconciliation}});if(!(Boolean(a)||o.length>0||s.tools.length>0||Boolean(s.reasoningText))&&"complete"===t.kind)return;const c=this.stream(e,s);if(a!==s.assistantText&&c.setBody(a),this.finishReasoning(s),this.finishTools(s),"cancelled"===t.kind)await c.cancel({stopReason:"user_stop"});else if("attachment_delivery_failed"===n||n?.startsWith("persistence_reconciliation_"))await c.fail({stopReason:n});else if("failed"===t.kind)await writeRuntimeFailure(c,{body:a??"",stopReason:t.classification?messageStopReasonForRuntimeFailure(t.classification):"error",businessErrorCode:t.classification?.businessErrorCode});else{const e="complete"===t.kind?toMessageServiceCompletedStopReason(t.stopReason):"end_turn";await c.finalize({stopReason:e})}this.deps.writeObs({component:"message-service-run-output",domain:"lifecycle",name:n?"run_output.failed":"run_output.completed",severity:n?"warn":"info",payload:{conversationId:e.conversationId,runId:e.runId,textLen:a.length,artifactCount:o.length,toolCount:s.tools.length,sourceCount:s.sourcesByUrl.size}})}catch(t){throw this.reportProjectionFailure(e,t,"terminal"),t}finally{this.runs.delete(e.runId)}}projectAssistantDelta(e,t,r){const s=asRecord(r);if(!s)return;if("text"===s.type&&"string"==typeof s.text)return t.assistantText+=s.text,void this.stream(e,t).appendBody(s.text);const o=artifactFromBlock(s);o&&this.projectArtifact(e,t,o)}projectThoughtDelta(e,t,r){const s=asRecord(r);if("text"!==s?.type||"string"!=typeof s.text)return;t.reasoningText+=s.text;const o=this.stream(e,t),n=toMessagePart({type:"thinking",text:t.reasoningText,state:"streaming"});void 0===t.reasoningPartIndex?(t.reasoningPartIndex=o.parts.length,o.addPart(n)):o.replacePart(t.reasoningPartIndex,n)}projectToolStart(e,t,r){const s={id:String(r.toolCallId),name:r.name??r.title,arguments:r.rawInput??parseArguments(r.arguments),isError:!1,completed:!1};t.tools.push(s),t.toolsById.set(s.id,s),this.upsertToolUse(e,t,s);const o=extractMessageToolCaption(s.name,s.arguments);o&&(t.caption=o)}projectToolUpdate(e,t,r){const s=String(r.toolCallId);let o=t.toolsById.get(s);if(o||(o={id:s,name:r.name??r.title??"tool",arguments:{},isError:!1,completed:!1},t.tools.push(o),t.toolsById.set(s,o)),o.completed&&"completed"!==r.status&&"failed"!==r.status)return;const n=r.name??r.title;"tool"===o.name&&n&&(o.name=n),o.result=r.result??r.content??r.rawOutput??r.output??r.text,o.isError="failed"===r.status,o.completed="completed"===r.status||"failed"===r.status,this.upsertToolUse(e,t,o),this.upsertToolProgress(e,t,o),this.projectSearchSources(e,t,o.id,o.name,r.rawOutput,r.result,o.isError);for(const s of r.content??[]){const r=asRecord(asRecord(s)?.content),o=r?artifactFromBlock(r):void 0;o&&this.projectArtifact(e,t,o)}}upsertToolUse(e,t,r){const s=this.stream(e,t),o=toMessagePart({type:"tool_use",id:r.id,name:r.name,arguments:r.arguments,state:r.completed?"done":"streaming"});void 0===r.usePartIndex?(r.usePartIndex=s.parts.length,s.addPart(o)):s.replacePart(r.usePartIndex,o)}upsertToolProgress(e,t,r){if(void 0===r.result)return;const s=this.stream(e,t),o=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:r.id,content:r.result,...r.isError?{is_error:!0}:{}}});void 0===r.progressPartIndex?(r.progressPartIndex=s.parts.length,s.addPart(o)):s.replacePart(r.progressPartIndex,o)}projectArtifact(e,t,r){const s=dedupeMessageServiceArtifacts([r])[0];if(!s||t.artifactUris.has(s.uri))return;t.artifactUris.add(s.uri),t.artifacts.push(s);const[o]=messageServiceFileParts([s]);o&&this.stream(e,t).addPart(o)}projectSearchSources(e,t,r,s,o,n,a){let i=o,c=0;try{let u=extractSearchSources({toolName:s,rawResult:i,isError:a});0===u.length&&void 0!==n&&n!==o&&(i=n,u=extractSearchSources({toolName:s,rawResult:i,isError:a}));const l=searchSourceProvider(i)??"unknown";if(c=u.length,0===c)return;const p=this.stream(e,t);for(const e of u){const s=t.sourcesByUrl.get(e.url);if(s){const r=mergeSearchSource(s.source,e);if(r.title===s.source.title&&r.snippet===s.source.snippet)continue;p.replacePart(s.partIndex,toMessagePart({type:"source",url:r.url,...r.title?{title:r.title}:{},...r.snippet?{snippet:r.snippet}:{},tool_use_id:s.toolUseId})),t.sourcesByUrl.set(e.url,{source:r,partIndex:s.partIndex,toolUseId:s.toolUseId});continue}const o=p.parts.length;p.addPart(toMessagePart({type:"source",url:e.url,...e.title?{title:e.title}:{},...e.snippet?{snippet:e.snippet}:{},tool_use_id:r})),t.sourcesByUrl.set(e.url,{source:e,partIndex:o,toolUseId:r})}this.deps.logger.debug?.(`[message-service-run-output] search source projection run=${e.runId} tool=${s} provider=${l} source_count=${c}`)}catch{const t=searchSourceProvider(i)??"unknown";this.deps.logger.warn?.(`[message-service-run-output] search source projection ignored run=${e.runId} tool=${s} provider=${t} source_count=${c}`)}}finishReasoning(e){e.stream&&void 0!==e.reasoningPartIndex&&e.stream.replacePart(e.reasoningPartIndex,toMessagePart({type:"thinking",text:e.reasoningText,state:"done"}))}finishTools(e){if(e.stream)for(const t of e.tools)void 0!==t.usePartIndex&&(t.completed||e.stream.replacePart(t.usePartIndex,toMessagePart({type:"tool_use",id:t.id,name:t.name,arguments:t.arguments,state:"done"})))}stream(e,t){if(t.stream)return t.stream;const r=this.deps.getClient();if(!r)throw new Error(`Message Service client unavailable: ${e.runId}`);const s=messageServiceRunMessageId(e.runId);return t.stream=r.messages.startStream({conversationId:e.conversationId,clientMessageId:s,idempotencyKey:s,type:"agent_reply",content:{run_id:e.runId},...e.replyTo?{replyTo:e.replyTo}:{}}),t.stream}reportProjectionFailure(e,t,r){const s=t instanceof Error?t.message:String(t);this.deps.logger.warn?.(`[message-service-run-output] ${r} failed run=${e.runId}: ${s}`),this.deps.writeObs({component:"message-service-run-output",domain:"lifecycle",name:"run_output.failed",severity:"warn",summary:s,payload:{conversationId:e.conversationId,runId:e.runId,phase:r}})}state(e){const t=this.runs.get(e);if(!t)throw new Error(`Message Service run output is not open: ${e}`);return t}}function asRecord(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:void 0}function parseArguments(e){if(!e)return{};try{return JSON.parse(e)}catch{return e}}function artifactFromBlock(e){if("resource_link"!==e.type&&"file"!==e.type)return;const t="string"==typeof e.uri?e.uri:"string"==typeof e.url?e.url:void 0;return t?{uri:t,..."string"==typeof e.name?{name:e.name}:{},..."string"==typeof e.mimeType?{mimeType:e.mimeType}:"string"==typeof e.mime_type?{mimeType:e.mime_type}:{}}:void 0}
|
|
@@ -0,0 +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),r=record(t);for(let e=0;e<3&&"tool_call"===s;e++)s=toolName(r.id??r.name),r=record(r.args??r.arguments??r.input);switch(s){case"exec":case"exec_command":n=commandActions(text(r.command??r.cmd));break;case"beeos_search":case"web_search":case"search_web":n=[action("search",text(r.query))];break;case"beeos_fetch":case"web_fetch":{let e="";try{const t=new URL(text(r.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(r.fileName??r.file_path??r.path))];break;case"write":case"edit":case"write_file":case"edit_file":case"beeos_upload_file":n=Array.isArray(r.paths)&&r.paths.length>0?r.paths.slice(0,5).map(e=>action("save",filename(e))):[action("save",filename(r.fileName??r.file_path??r.path))];break;case"tool_search":{const e=Array.isArray(r.queries)?r.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(r.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||/[$`\\(){}<>]/.test(e))return t;const n=/"[^"\n]*"|'[^'\n]*'|&&|\|\||[;|\n]|[^\s"';|&]+/g,s=[],r=[];let a=[],i=0,c="";for(const o of e.matchAll(n)){const n=e.slice(i,o.index);if(n.trim())return t;const l=o[0],h=[";","|","&&","||","\n"].includes(l);if(!h&&a.length>0&&0===n.length)return t;if(i=o.index+l.length,h){if(!a.length)return t;s.push(a),r.push(c),a=[],c=l}else a.push(l.startsWith('"')||l.startsWith("'")?l.slice(1,-1):l)}if(e.slice(i).trim()||!a.length&&["&&","||","|"].includes(c))return t;a.length&&(s.push(a),r.push(c));const o=[],l=new Set(["rsvg-convert","inkscape","chromium","chromium-browser","google-chrome","convert","magick","python3"]);for(const[e,n]of s.entries()){const[s,...a]=n,i=s.replace(/^\/(?:usr\/(?:local\/)?)?bin\/([A-Za-z0-9_.-]+)$/,"$1");let c;if("echo"!==i||1!==a.length||!["---","DONE"].includes(a[0])){if("which"===i&&a.length>0&&a.every(e=>l.has(e))||"command"===i&&2===a.length&&"-v"===a[0]&&l.has(a[1]))c=action("check_rendering");else if("fc-list"===i&&(0===a.length||1===a.length&&":lang=zh"===a[0]))c=action(0===a.length?"list_fonts":"list_chinese_fonts");else if("ls"!==i||0!==a.length&&(1!==a.length||a[0].startsWith("-"))){if("head"===i&&1===a.length&&/^-\d+$/.test(a[0])&&o.length>0&&"|"===r[e])continue;{const e="python3"===i&&"-m"===a[0]&&"pip"===a[1]?["pip",...a.slice(2)]:"uv"===i&&"pip"===a[0]?a:[i,...a];if("pip"!==e[0]&&"pip3"!==e[0]||"install"!==e[1]){if(!["python","python3","node"].includes(i)||1!==a.length||!/^(?:[A-Za-z0-9_./-]+\/)?[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:py|js|mjs)$/.test(a[0]))return t;c=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;c=action("install",n.join(", "))}}}else c=action("list_files");o.some(e=>e.kind===c.kind&&e.subject===c.subject)||o.push(c)}}return o.length?o:t}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeos-claw",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.75",
|
|
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": "8a30ad4aa510b0ed608942881b4a9c3cd5ed2285"
|
|
94
94
|
}
|