grix-connector 3.26.3 → 3.26.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- import{isRequestTimeoutError as E}from"../core/aibot/errors.js";import{log as u}from"../core/log/index.js";import{splitTextForAibotProtocol as g,AIBOT_PROTOCOL_MAX_RUNES as C}from"../core/protocol/protocol-text.js";import{isThinkingEventDropped as k,isToolEventDropped as y,normalizeRuntimeConfigSnapshot as T}from"./runtime-config.js";const v={claude:!1,acp:!1,codex:!0,codewhale:!0,pi:!0,openhuman:!1,cursor:!1,opencode:!0,agy:!0};class p{handle;connectorRuntimeConfig;eventRuntimeConfigs=new Map;eventQuotedMessageIds=new Map;defaultQuoteTrigger=!1;thinkingSeqMap=new Map;toolCardSeq=0;bufferedEventTexts=new Map;singleMessageDeliveries=new Map;chunkSeqMap=new Map;rejectedEvents=new Set;terminalEvents=new Set;static MAX_TERMINAL_EVENTS=1e3;stoppedEvents=new Set;streamCoalesce=new Map;streamCoalesceMs=S();static STREAM_COALESCE_MAX_RUNES=2048;recentToolCards=new Map;static TOOL_CARD_DEDUP_MS=100;constructor(e){this.connectorRuntimeConfig={...e}}bind(e){this.handle=e}markEventRejected(e){if(!e)return;this.rejectedEvents.add(e);const t=`${e}:`;for(const s of[...this.streamCoalesce.keys()])if(s.startsWith(t)){const n=this.streamCoalesce.get(s);n.timer&&clearTimeout(n.timer),this.streamCoalesce.delete(s)}u.info("send-ctrl",`event marked as stream-rejected, future chunks will be dropped event=${e}`)}markEventTerminal(e){if(!(!e||this.terminalEvents.has(e))&&(this.terminalEvents.add(e),this.terminalEvents.size>p.MAX_TERMINAL_EVENTS)){const t=this.terminalEvents.values().next().value;t!==void 0&&this.terminalEvents.delete(t)}}isEventClosed(e){return!!e&&(this.rejectedEvents.has(e)||this.terminalEvents.has(e))}markEventStopped(e){e&&(this.stoppedEvents.add(e),u.info("send-ctrl",`[stop-trace] event marked stopped; subsequent outbound will be flagged event=${e}`))}flagOutboundAfterStop(e,t,s,n,o,a){!e||!this.stoppedEvents.has(e)||u.warn("send-ctrl",`[stop-trace] OUTBOUND AFTER STOP ${s} event=${e} session=${t} seq=${n} is_finish=${o} len=${a}`)}getGlobalRuntimeConfig(){return this.connectorRuntimeConfig}setStreamCoalesceMs(e){this.streamCoalesceMs=Number.isFinite(e)&&e>0?Math.floor(e):0}captureEventRuntimeConfig(e){this.terminalEvents.delete(e.event_id);const t=T(e.connector_runtime_config);if(t||!this.eventRuntimeConfigs.has(e.event_id)){const s=t??this.connectorRuntimeConfig;this.eventRuntimeConfigs.set(e.event_id,{responseDelivery:s.responseDelivery,toolEvents:s.toolEvents,thinkingEvents:s.thinkingEvents})}e.msg_id&&!this.eventQuotedMessageIds.has(e.event_id)&&this.eventQuotedMessageIds.set(e.event_id,e.msg_id)}discardEventState(e){e&&(this.markEventTerminal(e),this.clearEventTrackingState(e))}clearEventTrackingState(e){this.eventRuntimeConfigs.delete(e),this.eventQuotedMessageIds.delete(e),this.rejectedEvents.delete(e),this.singleMessageDeliveries.delete(e),this.bufferedEventTexts.delete(e);for(const t of[...this.streamCoalesce.keys()])if(t.startsWith(`${e}:`)){const s=this.streamCoalesce.get(t);s.timer&&clearTimeout(s.timer),this.streamCoalesce.delete(t)}this.stoppedEvents.delete(e)&&u.info("send-ctrl",`[stop-trace] event tracking cleared stop flag event=${e}`);for(const t of this.chunkSeqMap.keys())t.startsWith(`${e}:`)&&this.chunkSeqMap.delete(t);for(const t of this.recentToolCards.keys())t.startsWith(`${e}:`)&&this.recentToolCards.delete(t)}setDefaultQuoteTrigger(e){this.defaultQuoteTrigger=e}getDefaultQuotedMessageId(e){if(!(!this.defaultQuoteTrigger||!e))return this.eventQuotedMessageIds.get(e)}resolveEventRuntimeConfig(e){if(e){const t=this.eventRuntimeConfigs.get(e);if(t)return t}return this.connectorRuntimeConfig}shouldDropToolDisplayEvent(e){return y(this.resolveEventRuntimeConfig(e))}shouldDropThinkingDisplayEvent(e){return k(this.resolveEventRuntimeConfig(e))}shouldDropCodexDisplayEvent(e,t){return this.shouldDropToolDisplayEvent(e)?t==="item/completed":!1}shouldDropAcpRawDisplayEvent(e,t){return!!(this.shouldDropToolDisplayEvent(e)&&(t==="tool_use"||t==="tool_result")||this.shouldDropThinkingDisplayEvent(e)&&t==="thinking")}sendReply(e,t,s,n,o,a){if(!a?.skipToolFilter&&this.shouldDropToolDisplayEvent(e)&&D(s,o)||this.isEventClosed(e))return;if(this.flagOutboundAfterStop(e,t,"reply",0,!1,(s||"").length),this.resolveEventRuntimeConfig(e).responseDelivery==="stream"&&!o){const i=g(s),l=e?`reply_stream_${e}`:`reply_stream_${Date.now()}`;if(i.length===0){this.handle.sendMsg({event_id:e,session_id:t,msg_type:1,content:s||" ",quoted_message_id:n||void 0});return}for(let h=0;h<i.length;h++){const f=h===i.length-1;this.sendStreamChunk(e,t,i[h],h+1,f,l,n)}return}this.handle.sendMsg({event_id:e,session_id:t,msg_type:1,content:s,quoted_message_id:n||void 0,...o?{extra:o}:{}})}sendStreamChunk(e,t,s,n,o,a,r){if(this.isEventClosed(e))return;if(r=r??this.getDefaultQuotedMessageId(e),this.flagOutboundAfterStop(e,t,"stream_chunk",n,o,(s||"").length),this.finalizeThinking(e,t),this.resolveEventRuntimeConfig(e).responseDelivery==="single_message"&&e){this.bufferStreamChunk(e,t,s,o,r);return}if(this.streamCoalesceMs<=0){this.emitStreamChunkParts(e,t,s,n,o,a,r);return}this.coalesceStreamChunk(e,t,s,o,a,r)}async sendFinalStreamChunkReliable(e,t,s,n,o=!1){if(this.isEventClosed(e))throw new Error(`required agent output was already rejected or the event is terminal: event=${e}`);this.finalizeThinking(e,t);const a=this.resolveEventRuntimeConfig(e);if(!o&&a.responseDelivery==="single_message"&&e){if(this.flushCoalesceForEvent(e),await this.ensureSingleMessageDelivery(e,n),this.isEventClosed(e))throw new Error(`required agent output was rejected before terminal: event=${e}`);return}const r=`${e}:${s??"default"}`;this.flushCoalesce(r,!1),this.streamCoalesce.delete(r);const i=(this.chunkSeqMap.get(r)??0)+1;this.chunkSeqMap.set(r,i);const l=this.getDefaultQuotedMessageId(e),h={event_id:e,session_id:t,delta_content:"",chunk_seq:i,is_finish:!0,...s?{client_msg_id:s}:{},...l?{quoted_message_id:l}:{}};this.flagOutboundAfterStop(e,t,"stream_chunk_final",i,!0,0);const f=async()=>{const c=await this.handle.sendStreamChunkRequest(h,n);if(c.cmd!=="send_ack"){const m=c.payload;throw new Error(`final chunk rejected: cmd=${c.cmd} code=${m?.code??""} msg=${m?.msg??""}`)}};try{await f()}catch(c){if(E(c))u.warn("send-ctrl",`final chunk ACK timed out, retry request with same chunk_seq=${i} event=${e}`),await f();else throw u.warn("send-ctrl",`final chunk send failed (no resend) event=${e}: ${c}`),c}if(this.isEventClosed(e))throw new Error(`required agent output was rejected before terminal: event=${e}`)}async deliverAuxiliaryLargeText(e,t,s,n){if(!s||this.isEventClosed(e))return!1;if(this.resolveEventRuntimeConfig(e).responseDelivery==="single_message"&&e){const r=this.getDefaultQuotedMessageId(e),i=g(s);for(let l=0;l<i.length;l++)this.flagOutboundAfterStop(e,t,"aux_detail_msg",l+1,!1,i[l].length),this.handle.sendMsg({event_id:e,session_id:t,msg_type:1,content:i[l],...l===0&&r?{quoted_message_id:r}:{},client_msg_id:i.length>1?`${n}_p${l+1}`:n});return!0}const a=g(s);for(const r of a)this.sendStreamChunk(e,t,r,0,!1,n);return await this.sendFinalStreamChunkReliable(e,t,n),!0}coalesceStreamChunk(e,t,s,n,o,a){if(!s&&!n)return;const r=`${e}:${o??"default"}`;let i=this.streamCoalesce.get(r);if(i||(i={eventId:e,sessionId:t,clientMsgId:o,quotedMessageId:a,text:"",flushedOnce:!1},this.streamCoalesce.set(r,i)),s&&(i.text+=s),i.sessionId=t,o!==void 0&&(i.clientMsgId=o),!i.quotedMessageId&&a&&(i.quotedMessageId=a),n){this.flushCoalesce(r,!0);return}if(!i.flushedOnce||i.text.length>=p.STREAM_COALESCE_MAX_RUNES){this.flushCoalesce(r,!1);return}i.timer||(i.timer=setTimeout(()=>this.flushCoalesce(r,!1),this.streamCoalesceMs))}flushCoalesce(e,t){const s=this.streamCoalesce.get(e);if(!s)return;s.timer&&(clearTimeout(s.timer),s.timer=void 0);const n=s.text;s.text="",s.flushedOnce=!0,t&&this.streamCoalesce.delete(e),!(!n&&!t)&&this.emitStreamChunkParts(s.eventId,s.sessionId,n,0,t,s.clientMsgId,s.quotedMessageId)}flushCoalesceForEvent(e){const t=`${e}:`;for(const s of[...this.streamCoalesce.keys()])s.startsWith(t)&&(this.flushCoalesce(s,!1),this.streamCoalesce.delete(s))}emitStreamChunkParts(e,t,s,n,o,a,r){const i=s||(o?`
2
- `:"");if(!i&&!o)return;const l=g(i,C);l.length===0&&l.push(i||`
3
- `);const h=`${e}:${a??"default"}`;for(let f=0;f<l.length;f++){const c=f===l.length-1,m=this.chunkSeqMap.get(h)??0,_=f===0&&n>m?n:m+1;this.chunkSeqMap.set(h,_),this.handle.sendStreamChunk({event_id:e,session_id:t,delta_content:l[f],chunk_seq:_,is_finish:c&&o,...a?{client_msg_id:a}:{},...r?{quoted_message_id:r}:{}})}}sendEventResult(e,t,s,n){this.finalizeThinking(e,""),this.flushCoalesceForEvent(e);const o=this.ensureSingleMessageDelivery(e),a=(r,i=s,l=n)=>{r==="responded"&&this.rejectedEvents.has(e)&&(r="failed",l=l||"agent_output_rejected",i=i||"required agent output was rejected by the server"),this.markEventTerminal(e),this.handle.sendEventResult({event_id:e,status:r,...i?{msg:i}:{},...l?{code:l}:{},updated_at:Date.now()}),this.clearEventTrackingState(e)};if(!o){a(t);return}o.then(()=>a(t),r=>{const i=`final output delivery failed: ${r instanceof Error?r.message:String(r)}`;a(t==="responded"?"failed":t,t==="responded"?i:s,t==="responded"?"agent_output_delivery_failed":n)})}sendThinking(e,t,s){if(this.shouldDropThinkingDisplayEvent(e)||this.isEventClosed(e))return;this.flagOutboundAfterStop(e,t,"thinking",this.thinkingSeqMap.get(e)??0,!1,(s||"").length);const n=(this.thinkingSeqMap.get(e)??0)+1;this.thinkingSeqMap.set(e,n),this.handle.sendStreamChunk({event_id:e,session_id:t,delta_content:s,chunk_seq:n,is_finish:!1,client_msg_id:`${e}_thinking`,is_thinking:!0})}sendToolExecutionCard(e,t,s,n){if(this.shouldDropToolDisplayEvent(e)){u.info("send-ctrl",`[tool-card] DROP event=${e} session=${t} summary=${s.summaryText}`);return}const a=Date.now(),r=`${e}:${s.summaryText}:${s.detailText??""}`,i=this.recentToolCards.get(r);if(i!==void 0&&a-i<p.TOOL_CARD_DEDUP_MS){u.info("send-ctrl",`[tool-card] DEDUP event=${e} session=${t} summary=${s.summaryText}`);return}this.recentToolCards.set(r,a);const l={summary_text:s.summaryText};s.detailText&&(l.detail_text=s.detailText),u.info("send-ctrl",`[tool-card] SEND event=${e} session=${t} summary=${s.summaryText}`),this.handle.sendMsg({event_id:e,session_id:t,client_msg_id:n??`tool_${++this.toolCardSeq}_${Date.now()}`,msg_type:1,content:"",extra:{channel_data:{grix:{toolExecution:l}},agent_api_origin:!0}})}finalizeThinking(e,t){const s=this.thinkingSeqMap.get(e);s!==void 0&&(this.thinkingSeqMap.delete(e),t&&(this.isEventClosed(e)||this.handle.sendStreamChunk({event_id:e,session_id:t,delta_content:"",chunk_seq:s+1,is_finish:!0,client_msg_id:`${e}_thinking`,is_thinking:!0})))}bufferOnly(e,t,s,n,o){o=o??this.getDefaultQuotedMessageId(e),this.bufferStreamChunk(e,t,s,n,o)}bufferStreamChunk(e,t,s,n,o){const a=this.bufferedEventTexts.get(e);a?(s&&(a.text+=s),!a.quotedMessageId&&o&&(a.quotedMessageId=o)):this.bufferedEventTexts.set(e,{sessionId:t,text:s??"",quotedMessageId:o||void 0}),n&&this.ensureSingleMessageDelivery(e)?.catch(i=>{u.warn("send-ctrl",`single-message output delivery failed event=${e}: ${i instanceof Error?i.message:String(i)}`)})}ensureSingleMessageDelivery(e,t){const s=this.singleMessageDeliveries.get(e);if(s)return s;const n=this.bufferedEventTexts.get(e);if(!n||(this.bufferedEventTexts.delete(e),!n.text))return;const o=(async()=>{const a={event_id:e,session_id:n.sessionId,content:n.text,client_msg_id:`agent_single_${e}`,...n.quotedMessageId?{quoted_message_id:n.quotedMessageId}:{}},r=async()=>{const i=t===void 0?await this.handle.sendText(a):await this.handle.sendText(a,t);if(i.cmd!=="send_ack"){const l=i.payload;throw new Error(`single-message output rejected: cmd=${i.cmd} code=${l?.code??""} msg=${l?.msg??""}`)}};try{await r()}catch(i){if(!E(i))throw i;u.warn("send-ctrl",`single-message ACK timed out, retry request with stable client_msg_id=${a.client_msg_id} event=${e}`),await r()}})();return this.singleMessageDeliveries.set(e,o),o}}function S(){const d=process.env.GRIX_STREAM_COALESCE_MS;if(d===void 0||d==="")return 500;const e=Number(d);return Number.isFinite(e)&&e>=0?Math.floor(e):500}function D(d,e){return e&&typeof e=="object"&&e.channel_data?.grix?.toolExecution?!0:/^\[tool\]/.test(d)||/^\[tool result\]/.test(d)}export{v as DEFAULT_QUOTE_TRIGGER_BY_ADAPTER,p as SendController};
1
+ import{isRequestTimeoutError as E}from"../core/aibot/errors.js";import{log as f}from"../core/log/index.js";import{splitTextForAibotProtocol as p,AIBOT_PROTOCOL_MAX_RUNES as C}from"../core/protocol/protocol-text.js";import{isThinkingEventDropped as k,isToolEventDropped as y,normalizeRuntimeConfigSnapshot as S}from"./runtime-config.js";const q={claude:!1,acp:!1,codex:!0,codewhale:!0,pi:!0,openhuman:!1,cursor:!1,opencode:!0,agy:!0};class _{handle;connectorRuntimeConfig;eventRuntimeConfigs=new Map;eventQuotedMessageIds=new Map;defaultQuoteTrigger=!1;thinkingSeqMap=new Map;thinkingSessionMap=new Map;toolCardSeq=0;bufferedEventTexts=new Map;singleMessageDeliveries=new Map;chunkSeqMap=new Map;rejectedEvents=new Set;terminalEvents=new Set;static MAX_TERMINAL_EVENTS=1e3;stoppedEvents=new Set;streamCoalesce=new Map;streamCoalesceMs=T();static STREAM_COALESCE_MAX_RUNES=2048;recentToolCards=new Map;static TOOL_CARD_DEDUP_MS=100;constructor(e){this.connectorRuntimeConfig={...e}}bind(e){this.handle=e}markEventRejected(e){if(!e)return;this.rejectedEvents.add(e);const t=`${e}:`;for(const s of[...this.streamCoalesce.keys()])if(s.startsWith(t)){const n=this.streamCoalesce.get(s);n.timer&&clearTimeout(n.timer),this.streamCoalesce.delete(s)}f.info("send-ctrl",`event marked as stream-rejected, future chunks will be dropped event=${e}`)}markEventTerminal(e){if(!(!e||this.terminalEvents.has(e))&&(this.terminalEvents.add(e),this.terminalEvents.size>_.MAX_TERMINAL_EVENTS)){const t=this.terminalEvents.values().next().value;t!==void 0&&this.terminalEvents.delete(t)}}isEventClosed(e){return!!e&&(this.rejectedEvents.has(e)||this.terminalEvents.has(e))}markEventStopped(e){e&&(this.stoppedEvents.add(e),f.info("send-ctrl",`[stop-trace] event marked stopped; subsequent outbound will be flagged event=${e}`))}flagOutboundAfterStop(e,t,s,n,r,a){!e||!this.stoppedEvents.has(e)||f.warn("send-ctrl",`[stop-trace] OUTBOUND AFTER STOP ${s} event=${e} session=${t} seq=${n} is_finish=${r} len=${a}`)}getGlobalRuntimeConfig(){return this.connectorRuntimeConfig}setStreamCoalesceMs(e){this.streamCoalesceMs=Number.isFinite(e)&&e>0?Math.floor(e):0}captureEventRuntimeConfig(e){this.terminalEvents.delete(e.event_id);const t=S(e.connector_runtime_config);if(t||!this.eventRuntimeConfigs.has(e.event_id)){const s=t??this.connectorRuntimeConfig;this.eventRuntimeConfigs.set(e.event_id,{responseDelivery:s.responseDelivery,toolEvents:s.toolEvents,thinkingEvents:s.thinkingEvents})}e.msg_id&&!this.eventQuotedMessageIds.has(e.event_id)&&this.eventQuotedMessageIds.set(e.event_id,e.msg_id)}discardEventState(e){e&&(this.markEventTerminal(e),this.clearEventTrackingState(e))}clearEventTrackingState(e){this.eventRuntimeConfigs.delete(e),this.eventQuotedMessageIds.delete(e),this.rejectedEvents.delete(e),this.singleMessageDeliveries.delete(e),this.thinkingSeqMap.delete(e),this.thinkingSessionMap.delete(e),this.bufferedEventTexts.delete(e);for(const t of[...this.streamCoalesce.keys()])if(t.startsWith(`${e}:`)){const s=this.streamCoalesce.get(t);s.timer&&clearTimeout(s.timer),this.streamCoalesce.delete(t)}this.stoppedEvents.delete(e)&&f.info("send-ctrl",`[stop-trace] event tracking cleared stop flag event=${e}`);for(const t of this.chunkSeqMap.keys())t.startsWith(`${e}:`)&&this.chunkSeqMap.delete(t);for(const t of this.recentToolCards.keys())t.startsWith(`${e}:`)&&this.recentToolCards.delete(t)}setDefaultQuoteTrigger(e){this.defaultQuoteTrigger=e}getDefaultQuotedMessageId(e){if(!(!this.defaultQuoteTrigger||!e))return this.eventQuotedMessageIds.get(e)}resolveEventRuntimeConfig(e){if(e){const t=this.eventRuntimeConfigs.get(e);if(t)return t}return this.connectorRuntimeConfig}shouldDropToolDisplayEvent(e){return y(this.resolveEventRuntimeConfig(e))}shouldDropThinkingDisplayEvent(e){return k(this.resolveEventRuntimeConfig(e))}shouldDropCodexDisplayEvent(e,t){return this.shouldDropToolDisplayEvent(e)?t==="item/completed":!1}shouldDropAcpRawDisplayEvent(e,t){return!!(this.shouldDropToolDisplayEvent(e)&&(t==="tool_use"||t==="tool_result")||this.shouldDropThinkingDisplayEvent(e)&&t==="thinking")}sendReply(e,t,s,n,r,a){if(!a?.skipToolFilter&&this.shouldDropToolDisplayEvent(e)&&D(s,r)||this.isEventClosed(e))return;if(this.flagOutboundAfterStop(e,t,"reply",0,!1,(s||"").length),this.resolveEventRuntimeConfig(e).responseDelivery==="stream"&&!r){const i=p(s),l=e?`reply_stream_${e}`:`reply_stream_${Date.now()}`;if(i.length===0){this.handle.sendMsg({event_id:e,session_id:t,msg_type:1,content:s||" ",quoted_message_id:n||void 0});return}for(let u=0;u<i.length;u++){const c=u===i.length-1;this.sendStreamChunk(e,t,i[u],u+1,c,l,n)}return}this.handle.sendMsg({event_id:e,session_id:t,msg_type:1,content:s,quoted_message_id:n||void 0,...r?{extra:r}:{}})}sendStreamChunk(e,t,s,n,r,a,o){if(this.isEventClosed(e))return;if(o=o??this.getDefaultQuotedMessageId(e),this.flagOutboundAfterStop(e,t,"stream_chunk",n,r,(s||"").length),this.resolveEventRuntimeConfig(e).responseDelivery==="single_message"&&e){this.bufferStreamChunk(e,t,s,r,o);return}if(this.streamCoalesceMs<=0){this.emitStreamChunkParts(e,t,s,n,r,a,o);return}this.coalesceStreamChunk(e,t,s,r,a,o)}async sendFinalStreamChunkReliable(e,t,s,n,r=!1,a=!1){if(this.isEventClosed(e))throw new Error(`required agent output was already rejected or the event is terminal: event=${e}`);a||this.finalizeThinking(e,t);const o=this.resolveEventRuntimeConfig(e);if(!r&&o.responseDelivery==="single_message"&&e){if(this.flushCoalesceForEvent(e),await this.ensureSingleMessageDelivery(e,n),this.isEventClosed(e))throw new Error(`required agent output was rejected before terminal: event=${e}`);return}const i=`${e}:${s??"default"}`;this.flushCoalesce(i,!1),this.streamCoalesce.delete(i);const l=(this.chunkSeqMap.get(i)??0)+1;this.chunkSeqMap.set(i,l);const u=this.getDefaultQuotedMessageId(e),c={event_id:e,session_id:t,delta_content:"",chunk_seq:l,is_finish:!0,...s?{client_msg_id:s}:{},...u?{quoted_message_id:u}:{}};this.flagOutboundAfterStop(e,t,"stream_chunk_final",l,!0,0);const m=async()=>{const h=await this.handle.sendStreamChunkRequest(c,n);if(h.cmd!=="send_ack"){const g=h.payload;throw new Error(`final chunk rejected: cmd=${h.cmd} code=${g?.code??""} msg=${g?.msg??""}`)}};try{await m()}catch(h){if(E(h))f.warn("send-ctrl",`final chunk ACK timed out, retry request with same chunk_seq=${l} event=${e}`),await m();else throw f.warn("send-ctrl",`final chunk send failed (no resend) event=${e}: ${h}`),h}if(this.isEventClosed(e))throw new Error(`required agent output was rejected before terminal: event=${e}`)}async deliverAuxiliaryLargeText(e,t,s,n){if(!s||this.isEventClosed(e))return!1;if(this.resolveEventRuntimeConfig(e).responseDelivery==="single_message"&&e){const o=this.getDefaultQuotedMessageId(e),i=p(s);for(let l=0;l<i.length;l++)this.flagOutboundAfterStop(e,t,"aux_detail_msg",l+1,!1,i[l].length),this.handle.sendMsg({event_id:e,session_id:t,msg_type:1,content:i[l],...l===0&&o?{quoted_message_id:o}:{},client_msg_id:i.length>1?`${n}_p${l+1}`:n});return!0}const a=p(s);for(const o of a)this.sendStreamChunk(e,t,o,0,!1,n);return await this.sendFinalStreamChunkReliable(e,t,n,void 0,!1,!0),!0}coalesceStreamChunk(e,t,s,n,r,a){if(!s&&!n)return;const o=`${e}:${r??"default"}`;let i=this.streamCoalesce.get(o);if(i||(i={eventId:e,sessionId:t,clientMsgId:r,quotedMessageId:a,text:"",flushedOnce:!1},this.streamCoalesce.set(o,i)),s&&(i.text+=s),i.sessionId=t,r!==void 0&&(i.clientMsgId=r),!i.quotedMessageId&&a&&(i.quotedMessageId=a),n){this.flushCoalesce(o,!0);return}if(!i.flushedOnce||i.text.length>=_.STREAM_COALESCE_MAX_RUNES){this.flushCoalesce(o,!1);return}i.timer||(i.timer=setTimeout(()=>this.flushCoalesce(o,!1),this.streamCoalesceMs))}flushCoalesce(e,t){const s=this.streamCoalesce.get(e);if(!s)return;s.timer&&(clearTimeout(s.timer),s.timer=void 0);const n=s.text;s.text="",s.flushedOnce=!0,t&&this.streamCoalesce.delete(e),!(!n&&!t)&&this.emitStreamChunkParts(s.eventId,s.sessionId,n,0,t,s.clientMsgId,s.quotedMessageId)}flushCoalesceForEvent(e){const t=`${e}:`;for(const s of[...this.streamCoalesce.keys()])s.startsWith(t)&&(this.flushCoalesce(s,!1),this.streamCoalesce.delete(s))}emitStreamChunkParts(e,t,s,n,r,a,o){const i=s||(r?`
2
+ `:"");if(!i&&!r)return;const l=p(i,C);l.length===0&&l.push(i||`
3
+ `);const u=`${e}:${a??"default"}`;for(let c=0;c<l.length;c++){const m=c===l.length-1,h=this.chunkSeqMap.get(u)??0,g=c===0&&n>h?n:h+1;this.chunkSeqMap.set(u,g),this.handle.sendStreamChunk({event_id:e,session_id:t,delta_content:l[c],chunk_seq:g,is_finish:m&&r,...a?{client_msg_id:a}:{},...o?{quoted_message_id:o}:{}})}}sendEventResult(e,t,s,n){this.finalizeThinking(e,""),this.flushCoalesceForEvent(e);const r=this.ensureSingleMessageDelivery(e),a=(o,i=s,l=n)=>{o==="responded"&&this.rejectedEvents.has(e)&&(o="failed",l=l||"agent_output_rejected",i=i||"required agent output was rejected by the server"),this.markEventTerminal(e),this.handle.sendEventResult({event_id:e,status:o,...i?{msg:i}:{},...l?{code:l}:{},updated_at:Date.now()}),this.clearEventTrackingState(e)};if(!r){a(t);return}r.then(()=>a(t),o=>{const i=`final output delivery failed: ${o instanceof Error?o.message:String(o)}`;a(t==="responded"?"failed":t,t==="responded"?i:s,t==="responded"?"agent_output_delivery_failed":n)})}sendThinking(e,t,s){if(this.shouldDropThinkingDisplayEvent(e)||this.isEventClosed(e))return;this.flagOutboundAfterStop(e,t,"thinking",this.thinkingSeqMap.get(e)??0,!1,(s||"").length);const n=(this.thinkingSeqMap.get(e)??0)+1;this.thinkingSeqMap.set(e,n),this.thinkingSessionMap.set(e,t),this.handle.sendStreamChunk({event_id:e,session_id:t,delta_content:s,chunk_seq:n,is_finish:!1,client_msg_id:`${e}_thinking`,is_thinking:!0})}sendToolExecutionCard(e,t,s,n){if(this.shouldDropToolDisplayEvent(e)){f.info("send-ctrl",`[tool-card] DROP event=${e} session=${t} summary=${s.summaryText}`);return}const a=Date.now(),o=`${e}:${s.summaryText}:${s.detailText??""}`,i=this.recentToolCards.get(o);if(i!==void 0&&a-i<_.TOOL_CARD_DEDUP_MS){f.info("send-ctrl",`[tool-card] DEDUP event=${e} session=${t} summary=${s.summaryText}`);return}this.recentToolCards.set(o,a);const l={summary_text:s.summaryText};s.detailText&&(l.detail_text=s.detailText),f.info("send-ctrl",`[tool-card] SEND event=${e} session=${t} summary=${s.summaryText}`),this.handle.sendMsg({event_id:e,session_id:t,client_msg_id:n??`tool_${++this.toolCardSeq}_${Date.now()}`,msg_type:1,content:"",extra:{channel_data:{grix:{toolExecution:l}},agent_api_origin:!0}})}finalizeThinking(e,t){const s=this.thinkingSeqMap.get(e);s!==void 0&&(this.thinkingSeqMap.delete(e),t=t||this.thinkingSessionMap.get(e)||"",this.thinkingSessionMap.delete(e),t&&(this.isEventClosed(e)||this.handle.sendStreamChunk({event_id:e,session_id:t,delta_content:"",chunk_seq:s+1,is_finish:!0,client_msg_id:`${e}_thinking`,is_thinking:!0})))}bufferOnly(e,t,s,n,r){r=r??this.getDefaultQuotedMessageId(e),this.bufferStreamChunk(e,t,s,n,r)}bufferStreamChunk(e,t,s,n,r){const a=this.bufferedEventTexts.get(e);a?(s&&(a.text+=s),!a.quotedMessageId&&r&&(a.quotedMessageId=r)):this.bufferedEventTexts.set(e,{sessionId:t,text:s??"",quotedMessageId:r||void 0}),n&&this.ensureSingleMessageDelivery(e)?.catch(i=>{f.warn("send-ctrl",`single-message output delivery failed event=${e}: ${i instanceof Error?i.message:String(i)}`)})}ensureSingleMessageDelivery(e,t){const s=this.singleMessageDeliveries.get(e);if(s)return s;const n=this.bufferedEventTexts.get(e);if(!n||(this.bufferedEventTexts.delete(e),!n.text))return;const r=(async()=>{const a={event_id:e,session_id:n.sessionId,content:n.text,client_msg_id:`agent_single_${e}`,...n.quotedMessageId?{quoted_message_id:n.quotedMessageId}:{}},o=async()=>{const i=t===void 0?await this.handle.sendText(a):await this.handle.sendText(a,t);if(i.cmd!=="send_ack"){const l=i.payload;throw new Error(`single-message output rejected: cmd=${i.cmd} code=${l?.code??""} msg=${l?.msg??""}`)}};try{await o()}catch(i){if(!E(i))throw i;f.warn("send-ctrl",`single-message ACK timed out, retry request with stable client_msg_id=${a.client_msg_id} event=${e}`),await o()}})();return this.singleMessageDeliveries.set(e,r),r}}function T(){const d=process.env.GRIX_STREAM_COALESCE_MS;if(d===void 0||d==="")return 500;const e=Number(d);return Number.isFinite(e)&&e>=0?Math.floor(e):500}function D(d,e){return e&&typeof e=="object"&&e.channel_data?.grix?.toolExecution?!0:/^\[tool\]/.test(d)||/^\[tool result\]/.test(d)}export{q as DEFAULT_QUOTE_TRIGGER_BY_ADAPTER,_ as SendController};
@@ -1 +1 @@
1
- import{TOOLS as A,EVENT_TOOLS as T,TOOL_ALIASES as S,ALL_TOOLS as e,EXPOSED_TOOLS as E,TOOL_MAP as _,PHASE1_TOOL_NAMES as r,PHASE2_TOOL_NAMES as L,PHASE1_INVOKE_ACTIONS as l,ACCESS_CONTROL_ACTION_MAP as t,toolCallToInvoke as a,mapToolAlias as N,isAlias as i}from"./tools.js";import{validateToolArgs as C}from"./tool-schemas.js";import{executeEventTool as P,isEventTool as m}from"./event-tool-executor.js";import{InternalApiServer as s}from"./internal-api-server.js";export{t as ACCESS_CONTROL_ACTION_MAP,e as ALL_TOOLS,T as EVENT_TOOLS,E as EXPOSED_TOOLS,s as InternalApiServer,l as PHASE1_INVOKE_ACTIONS,r as PHASE1_TOOL_NAMES,L as PHASE2_TOOL_NAMES,A as TOOLS,S as TOOL_ALIASES,_ as TOOL_MAP,P as executeEventTool,i as isAlias,m as isEventTool,N as mapToolAlias,a as toolCallToInvoke,C as validateToolArgs};
1
+ import{TOOLS as A,EVENT_TOOLS as E,TOOL_ALIASES as O,ALL_TOOLS as r,EXPOSED_TOOLS as S,TOOL_MAP as _,PHASE1_TOOL_NAMES as T,PHASE2_TOOL_NAMES as L,PHASE1_INVOKE_ACTIONS as l,ACCESS_CONTROL_ACTION_MAP as t,toolCallToInvoke as p,mapToolAlias as C,isAlias as M}from"./tools.js";import{validateToolArgs as a}from"./tool-schemas.js";import{executeEventTool as P,isEventTool as i}from"./event-tool-executor.js";import{InternalApiServer as n}from"./internal-api-server.js";import{LEGACY_MCP_SERVER_NAMES as x,FALLBACK_MCP_SERVER_NAME as I,resolveMcpServerName as f,pruneLegacyMcpServerEntries as R}from"./server-name.js";export{t as ACCESS_CONTROL_ACTION_MAP,r as ALL_TOOLS,E as EVENT_TOOLS,S as EXPOSED_TOOLS,I as FALLBACK_MCP_SERVER_NAME,n as InternalApiServer,x as LEGACY_MCP_SERVER_NAMES,l as PHASE1_INVOKE_ACTIONS,T as PHASE1_TOOL_NAMES,L as PHASE2_TOOL_NAMES,A as TOOLS,O as TOOL_ALIASES,_ as TOOL_MAP,P as executeEventTool,M as isAlias,i as isEventTool,C as mapToolAlias,R as pruneLegacyMcpServerEntries,f as resolveMcpServerName,p as toolCallToInvoke,a as validateToolArgs};
@@ -0,0 +1 @@
1
+ const i=["grix","grix-connector-tools"],c="grix-connector-tools";function s(e){if(!e)return c;const r=e.replace(/[^A-Za-z0-9_-]/g,"");return r?`grix-${r}`:c}function u(e,r){const t=[];for(const n of i){const o=e[n];if(o!==void 0)try{JSON.stringify(o).includes(r)&&(delete e[n],t.push(n))}catch{}}return t}export{c as FALLBACK_MCP_SERVER_NAME,i as LEGACY_MCP_SERVER_NAMES,u as pruneLegacyMcpServerEntries,s as resolveMcpServerName};
@@ -1 +1 @@
1
- import{mkdir as y,writeFile as v,rm as w}from"node:fs/promises";import{join as d}from"node:path";import{createHash as _}from"node:crypto";import{log as l}from"../log/index.js";import{readSkillManifest as O,writeSkillManifest as I}from"./manifest.js";const D=6e4,p=15e3;class R{configs;skillsDir;timer=null;running=!1;rerunPending=!1;stopped=!1;fetchImpl;intervalMs;onSyncSuccessHandler=null;constructor(t,r,i={}){this.configs=t,this.skillsDir=r,this.intervalMs=i.intervalMs??D,this.fetchImpl=i.fetchImpl??fetch}async start(){this.stopped=!1,await this.syncOnce().catch(t=>l.warn("skill-sync",`initial sync failed: ${c(t)}`)),this.timer=setInterval(()=>{this.syncOnce().catch(t=>l.warn("skill-sync",`sync failed: ${c(t)}`))},this.intervalMs),this.timer.unref?.()}stop(){this.stopped=!0,this.rerunPending=!1,this.timer&&(clearInterval(this.timer),this.timer=null)}setOnSyncSuccess(t){this.onSyncSuccessHandler=t}triggerSync(){if(this.running){this.rerunPending=!0;return}this.syncOnce().catch(t=>l.warn("skill-sync",`triggered sync failed: ${c(t)}`))}async syncOnce(){if(!this.running){this.running=!0;try{const t=await this.pickReachable();if(!t)return;const{cfg:r,remote:i}=t;await y(this.skillsDir,{recursive:!0});const n=await O(this.skillsDir),o=JSON.stringify(n),g=new Set(i.map(e=>e.name));for(const e of i){const s=n.skills[e.name];if(s&&s.digest===e.digest){const f=e.owner_id!==void 0?e.owner_id:s.owner_id,$=f==="0",h={...s,id:e.id,version:e.version,...f!==void 0?{owner_id:f}:{},system:$};(s.id!==h.id||s.version!==h.version||s.owner_id!==h.owner_id||s.system!==h.system)&&(n.skills[e.name]=h);continue}const S=e.owner_id==="0",u=M(e.name);if(!u){l.warn("skill-sync",`skip skill with unsafe name: ${e.name}`);continue}const m=await this.fetchContent(r,e.id);if(m!==null)try{await y(d(this.skillsDir,u),{recursive:!0}),await v(d(this.skillsDir,u,"SKILL.md"),m,"utf8"),n.skills[e.name]={id:e.id,version:e.version,digest:e.digest,dir:u,...e.owner_id!==void 0?{owner_id:e.owner_id}:{},system:S},s&&s.dir!==u&&!this.dirSharedByOthers(n,e.name,s.dir)&&await w(d(this.skillsDir,s.dir),{recursive:!0,force:!0}).catch(()=>{})}catch(f){l.warn("skill-sync",`write skill "${e.name}" failed: ${c(f)}`)}}for(const e of Object.keys(n.skills)){if(g.has(e))continue;const s=n.skills[e];this.dirSharedByOthers(n,e,s.dir)||await w(d(this.skillsDir,s.dir),{recursive:!0,force:!0}).catch(()=>{}),delete n.skills[e]}if(!(JSON.stringify(n)!==o))return;await I(this.skillsDir,n);try{this.onSyncSuccessHandler?.()}catch(e){l.warn("skill-sync",`onSyncSuccess handler failed: ${c(e)}`)}}finally{this.running=!1,this.rerunPending&&(this.rerunPending=!1,(this.timer!==null||!this.stopped)&&this.syncOnce().catch(t=>l.warn("skill-sync",`rerun sync failed: ${c(t)}`)))}}}dirSharedByOthers(t,r,i){return Object.entries(t.skills).some(([n,o])=>n!==r&&o.dir===i)}async pickReachable(){for(const t of this.configs){if(!t.apiKey||!t.wsUrl)continue;const r=await this.fetchList(t);if(r!==null)return{cfg:t,remote:r}}return null}async fetchList(t){const r=`${k(t.wsUrl)}/v1/agent-api/skills`;try{const i=await this.fetchImpl(r,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(p)});if(!i.ok)return l.warn("skill-sync",`list returned ${i.status}`),null;const n=await i.json();return n.code!==0?null:n.data?.items??[]}catch(i){return l.warn("skill-sync",`list error: ${c(i)}`),null}}async fetchContent(t,r){const i=`${k(t.wsUrl)}/v1/agent-api/skills/${encodeURIComponent(r)}/content`;try{const n=await this.fetchImpl(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(p)});if(!n.ok)return l.warn("skill-sync",`content ${r} returned ${n.status}`),null;const o=await n.json();return o.code!==0?null:typeof o.data?.content=="string"?o.data.content:null}catch(n){return l.warn("skill-sync",`content ${r} error: ${c(n)}`),null}}}function k(a){return new URL(a.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function M(a){const t=a.trim().replace(/[/\\]/g,"_").replace(/[<>:"|?*\u0000-\u001f]/g,"_").replace(/^\.+/,"").replace(/[. ]+$/,"");return!t||t.includes("..")||/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(t)?null:t!==a?`${t}-${_("sha1").update(a).digest("hex").slice(0,8)}`:t}function c(a){return a instanceof Error?a.message:String(a)}export{R as SkillSyncer};
1
+ import{mkdir as y,writeFile as v,rm as w}from"node:fs/promises";import{join as d}from"node:path";import{createHash as _}from"node:crypto";import{log as l}from"../log/index.js";import{readSkillManifest as O,writeSkillManifest as I}from"./manifest.js";const D=6*36e5,p=15e3;class R{configs;skillsDir;timer=null;running=!1;rerunPending=!1;stopped=!1;fetchImpl;intervalMs;onSyncSuccessHandler=null;constructor(t,r,i={}){this.configs=t,this.skillsDir=r,this.intervalMs=i.intervalMs??D,this.fetchImpl=i.fetchImpl??fetch}async start(){this.stopped=!1,await this.syncOnce().catch(t=>l.warn("skill-sync",`initial sync failed: ${c(t)}`)),this.timer=setInterval(()=>{this.syncOnce().catch(t=>l.warn("skill-sync",`sync failed: ${c(t)}`))},this.intervalMs),this.timer.unref?.()}stop(){this.stopped=!0,this.rerunPending=!1,this.timer&&(clearInterval(this.timer),this.timer=null)}setOnSyncSuccess(t){this.onSyncSuccessHandler=t}triggerSync(){if(this.running){this.rerunPending=!0;return}this.syncOnce().catch(t=>l.warn("skill-sync",`triggered sync failed: ${c(t)}`))}async syncOnce(){if(!this.running){this.running=!0;try{const t=await this.pickReachable();if(!t)return;const{cfg:r,remote:i}=t;await y(this.skillsDir,{recursive:!0});const n=await O(this.skillsDir),o=JSON.stringify(n),g=new Set(i.map(e=>e.name));for(const e of i){const s=n.skills[e.name];if(s&&s.digest===e.digest){const f=e.owner_id!==void 0?e.owner_id:s.owner_id,$=f==="0",h={...s,id:e.id,version:e.version,...f!==void 0?{owner_id:f}:{},system:$};(s.id!==h.id||s.version!==h.version||s.owner_id!==h.owner_id||s.system!==h.system)&&(n.skills[e.name]=h);continue}const S=e.owner_id==="0",u=M(e.name);if(!u){l.warn("skill-sync",`skip skill with unsafe name: ${e.name}`);continue}const m=await this.fetchContent(r,e.id);if(m!==null)try{await y(d(this.skillsDir,u),{recursive:!0}),await v(d(this.skillsDir,u,"SKILL.md"),m,"utf8"),n.skills[e.name]={id:e.id,version:e.version,digest:e.digest,dir:u,...e.owner_id!==void 0?{owner_id:e.owner_id}:{},system:S},s&&s.dir!==u&&!this.dirSharedByOthers(n,e.name,s.dir)&&await w(d(this.skillsDir,s.dir),{recursive:!0,force:!0}).catch(()=>{})}catch(f){l.warn("skill-sync",`write skill "${e.name}" failed: ${c(f)}`)}}for(const e of Object.keys(n.skills)){if(g.has(e))continue;const s=n.skills[e];this.dirSharedByOthers(n,e,s.dir)||await w(d(this.skillsDir,s.dir),{recursive:!0,force:!0}).catch(()=>{}),delete n.skills[e]}if(!(JSON.stringify(n)!==o))return;await I(this.skillsDir,n);try{this.onSyncSuccessHandler?.()}catch(e){l.warn("skill-sync",`onSyncSuccess handler failed: ${c(e)}`)}}finally{this.running=!1,this.rerunPending&&(this.rerunPending=!1,(this.timer!==null||!this.stopped)&&this.syncOnce().catch(t=>l.warn("skill-sync",`rerun sync failed: ${c(t)}`)))}}}dirSharedByOthers(t,r,i){return Object.entries(t.skills).some(([n,o])=>n!==r&&o.dir===i)}async pickReachable(){for(const t of this.configs){if(!t.apiKey||!t.wsUrl)continue;const r=await this.fetchList(t);if(r!==null)return{cfg:t,remote:r}}return null}async fetchList(t){const r=`${k(t.wsUrl)}/v1/agent-api/skills`;try{const i=await this.fetchImpl(r,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(p)});if(!i.ok)return l.warn("skill-sync",`list returned ${i.status}`),null;const n=await i.json();return n.code!==0?null:n.data?.items??[]}catch(i){return l.warn("skill-sync",`list error: ${c(i)}`),null}}async fetchContent(t,r){const i=`${k(t.wsUrl)}/v1/agent-api/skills/${encodeURIComponent(r)}/content`;try{const n=await this.fetchImpl(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(p)});if(!n.ok)return l.warn("skill-sync",`content ${r} returned ${n.status}`),null;const o=await n.json();return o.code!==0?null:typeof o.data?.content=="string"?o.data.content:null}catch(n){return l.warn("skill-sync",`content ${r} error: ${c(n)}`),null}}}function k(a){return new URL(a.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function M(a){const t=a.trim().replace(/[/\\]/g,"_").replace(/[<>:"|?*\u0000-\u001f]/g,"_").replace(/^\.+/,"").replace(/[. ]+$/,"");return!t||t.includes("..")||/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(t)?null:t!==a?`${t}-${_("sha1").update(a).digest("hex").slice(0,8)}`:t}function c(a){return a instanceof Error?a.message:String(a)}export{D as DEFAULT_INTERVAL_MS,R as SkillSyncer};
@@ -1 +1 @@
1
- function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
1
+ function a(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "3.26.3",
3
+ "version": "3.26.5",
4
4
  "description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",