@north-light/crouter 0.3.327 → 0.3.328
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.
|
@@ -19,10 +19,18 @@ export declare class NodeEventRing {
|
|
|
19
19
|
};
|
|
20
20
|
get empty(): boolean;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
/** Per-node state retained while broker frames become public stream events. */
|
|
23
|
+
export declare class NodeEventTranslationState {
|
|
24
|
+
readonly toolSummaries: Map<string, string>;
|
|
25
|
+
private emittedAssistantText;
|
|
26
|
+
resetAssistantText(): void;
|
|
27
|
+
emitAssistantTextDelta(message: unknown): string;
|
|
28
|
+
seedAssistantText(message: unknown): string;
|
|
29
|
+
}
|
|
30
|
+
/** Translate only the application-safe subset of broker frames. Tool argument
|
|
31
|
+
* summaries and emitted assistant text stay per node hub; raw arguments and
|
|
32
|
+
* tool output never cross this boundary. */
|
|
33
|
+
export declare function translateBrokerFrame(nodeId: string, frame: BrokerToClient, state?: NodeEventTranslationState): StreamEventPayload[];
|
|
26
34
|
declare class NodeEventStreams {
|
|
27
35
|
private readonly hubs;
|
|
28
36
|
subscribe(nodeId: string, after: number | undefined, subscriber: Subscriber): {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
1
|
+
var O=Object.defineProperty;var o=(n,e)=>O(n,"name",{value:e,configurable:!0});import{randomUUID as q}from"node:crypto";import{ApiError as E}from"../../../api/errors.js";import{BrokerClient as C}from"../../../core/broker-client/client.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{isPidAlive as B}from"../../../core/canvas/pid.js";import{BROKER_VIEW_SOCKET_WAIT_MS as k}from"../../../core/runtime/view-socket.js";import{toNodeOutcomeDTO as M}from"../map.js";import{reportsForNode as L}from"./reports.js";const D=15e3,$=6e4,x=250,P=512,T=256*1024,j=1e3,G=32*1024*1024;class V{static{o(this,"NodeEventRing")}events=[];bytes=0;nextSequence=1;earliestAvailable=1;append(e){const t={...e,sequence_number:this.nextSequence++},s=Buffer.byteLength(JSON.stringify(t),"utf8");if(s>T)return this.events=[],this.bytes=0,this.earliestAvailable=this.nextSequence,t;for(this.events.push(t),this.bytes+=s;this.events.length>P||this.bytes>T;){const r=this.events.shift();if(r===void 0)break;this.bytes-=Buffer.byteLength(JSON.stringify(r),"utf8"),this.earliestAvailable=r.sequence_number+1}return t}replay(e){if(e===void 0)return{gap:null,events:this.events};const t=e<this.earliestAvailable-1||e>=this.nextSequence?A(this.earliestAvailable):null;return{gap:t,events:t===null?this.events.filter(s=>s.sequence_number>e):this.events}}get empty(){return this.events.length===0}}function A(n){return{type:"error",error:{code:"stream_gap",message:"the requested event cursor is older than this daemon can resume",details:{earliest_sequence:n}}}}o(A,"streamGap");function H(n){return n===null?"arguments: null":Array.isArray(n)?`arguments: array (${n.length} items)`:typeof n=="object"?`arguments: object (${Object.keys(n).length} fields)`:`arguments: ${typeof n}`}o(H,"toolSummary");function m(n){if(typeof n!="object"||n===null)return"";const e=n.content;return typeof e=="string"?e:Array.isArray(e)?e.filter(t=>typeof t=="object"&&t!==null).filter(t=>t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(""):""}o(m,"assistantText");class w{static{o(this,"NodeEventTranslationState")}toolSummaries=new Map;emittedAssistantText="";resetAssistantText(){this.emittedAssistantText=""}emitAssistantTextDelta(e){const t=m(e),s=t.startsWith(this.emittedAssistantText)?t.slice(this.emittedAssistantText.length):t;return this.emittedAssistantText=t,s}seedAssistantText(e){const t=m(e);return this.emittedAssistantText=t,t}}function z(n,e,t=new w){switch(e.type){case"message_start":return e.message.role==="assistant"&&t.resetAssistantText(),[];case"message_update":{if(e.message.role!=="assistant")return[];const s=t.emitAssistantTextDelta(e.message);return s===""?[]:[{type:"node.output_text.delta",node_id:n,delta:s}]}case"message_end":{if(e.message.role!=="assistant")return[];const s=m(e.message);return t.resetAssistantText(),[{type:"node.output_text.done",node_id:n,text:s}]}case"tool_execution_start":{const s=H(e.args);return t.toolSummaries.set(e.toolCallId,s),[{type:"node.tool_call.started",node_id:n,tool_call_id:e.toolCallId,tool:e.toolName,summary:s}]}case"tool_execution_end":{const s=t.toolSummaries.get(e.toolCallId)??"[arguments unavailable]";return t.toolSummaries.delete(e.toolCallId),[{type:"node.tool_call.completed",node_id:n,tool_call_id:e.toolCallId,tool:e.toolName,status:e.isError?"error":"ok",summary:s}]}case"turn_start":return[{type:"node.turn.started",node_id:n}];case"turn_end":return[{type:"node.turn.completed",node_id:n}];default:return[]}}o(z,"translateBrokerFrame");class J{static{o(this,"NodeEventHub")}nodeId;remove;ring=new V;subscribers=new Set;observer;observerErrorAt;poll;reconnect;idleClose;status;settledRevision;settledOutcome;settledSequence;snapshotSeeded=!1;reportsSeeded=!1;translationState=new w;fresh=!0;constructor(e,t){this.nodeId=e,this.remove=t}subscribe(e,t){const s=this.fresh&&e!==void 0;this.fresh=!1,this.start();const r=this.settledOutcome!==void 0,d=s?{gap:A(1),events:this.ring.replay(void 0).events}:this.ring.replay(e);r||(this.subscribers.add(t),this.status!=="dormant"&&this.openObserver());let h=!1;return queueMicrotask(()=>{if(!h){d.gap!==null&&t(d.gap);for(const l of d.events)t(l)}}),r&&this.scheduleIdleClose(),{unsubscribe:o(()=>{h=!0,r||this.unsubscribe(t)},"unsubscribe"),terminal:r}}report(e){this.settledRevision===void 0&&this.publish({type:"node.report.pushed",node_id:this.nodeId,report:e})}start(){this.idleClose!==void 0&&(clearTimeout(this.idleClose),this.idleClose=void 0),this.poll===void 0&&(this.check(),this.settledRevision===void 0&&(this.seedReports(),this.poll=setInterval(()=>this.check(),x)))}seedReports(){if(!this.reportsSeeded){this.reportsSeeded=!0;for(const e of L(this.nodeId).reverse())this.report(e)}}check(){const e=b(this.nodeId);if(e===null){this.fail("stream_error",`node ${this.nodeId} no longer exists`);return}const t=M(e);if(t!==null){this.settledRevision!==t.revision&&(this.settledRevision=t.revision,this.settledOutcome=t,this.settledSequence=this.publish({type:"node.settled",node_id:this.nodeId,outcome:t}).sequence_number,this.stopObserver(),this.stopPoll());return}const s=e.pi_pid===null?"dormant":e.status;if(s==="dormant"){this.status!=="dormant"&&(this.status=s,this.publish({type:"node.status.changed",node_id:this.nodeId,status:s}));return}this.status===void 0?this.status=s:this.status!=="dormant"&&this.status!==s&&(this.status=s,this.publish({type:"node.status.changed",node_id:this.nodeId,status:s})),this.openObserver()}openObserver(){if(this.observer!==void 0||this.subscribers.size===0)return;const e=new C(this.nodeId);this.observer=e,e.on("connect",()=>{this.observerErrorAt=void 0;const t=b(this.nodeId);this.status==="dormant"&&t!==null&&t.pi_pid!==null&&(this.status=t.status,this.publish({type:"node.status.changed",node_id:this.nodeId,status:t.status})),e.send({type:"hello",role:"observer",client_id:q(),attends:!1})}),e.on("frame",t=>{try{t.type==="welcome"&&this.seedSnapshot(t.snapshot);for(const s of z(this.nodeId,t,this.translationState))this.publish(s)}catch(s){this.fail("stream_error",s instanceof Error?s.message:String(s))}}),e.on("error",()=>{const t=b(this.nodeId),s=Date.now();t!==null&&t.pi_pid!==null&&B(t.pi_pid)&&(this.observerErrorAt??=s,s-this.observerErrorAt<k)||this.fail("stream_error","observer connection failed")}),e.on("close",()=>{this.observer===e&&(this.observer=void 0,!(this.subscribers.size===0||this.settledRevision!==void 0)&&(this.status!=="dormant"&&(this.publish({type:"node.status.changed",node_id:this.nodeId,status:"dormant"}),this.status="dormant"),this.scheduleReconnect()))}),e.connect()}seedSnapshot(e){if(this.snapshotSeeded)return;this.snapshotSeeded=!0;const t=e.messages.filter(r=>r.role==="assistant"),s=e.state.isStreaming?t.pop():void 0;for(const r of t)this.publish({type:"node.output_text.done",node_id:this.nodeId,text:m(r)});s!==void 0&&this.publish({type:"node.output_text.delta",node_id:this.nodeId,delta:this.translationState.seedAssistantText(s)})}scheduleReconnect(){this.reconnect===void 0&&(this.reconnect=setTimeout(()=>{this.reconnect=void 0,this.check()},x))}publish(e){const t=this.ring.append(e);for(const s of[...this.subscribers])s(t);return t}fail(e,t){const s={type:"error",error:{code:e,message:t}};for(const r of[...this.subscribers])r(s);this.stopObserver(),this.stopPoll()}unsubscribe(e){this.subscribers.delete(e),this.subscribers.size===0&&this.scheduleIdleClose()}scheduleIdleClose(){this.idleClose===void 0&&(this.idleClose=setTimeout(()=>{this.stopObserver(),this.stopPoll(),this.remove()},$))}stopObserver(){this.reconnect!==void 0&&(clearTimeout(this.reconnect),this.reconnect=void 0);const e=this.observer;this.observer=void 0,e?.close()}stopPoll(){this.poll!==void 0&&(clearInterval(this.poll),this.poll=void 0)}}class W{static{o(this,"NodeEventStreams")}hubs=new Map;subscribe(e,t,s){let r=this.hubs.get(e);return r===void 0&&(r=new J(e,()=>this.hubs.delete(e)),this.hubs.set(e,r)),r.subscribe(t,s)}report(e,t){this.hubs.get(e)?.report(t)}}const F=new W;function K(n){const e=n.get("after");if(e===null||e==="")return{};if(!/^(?:0|[1-9]\d*)$/.test(e))throw new E(400,"invalid_request","`after` must be a non-negative integer");const t=Number(e);if(!Number.isSafeInteger(t))throw new E(400,"invalid_request","`after` must be a safe integer");return{after:t}}o(K,"parseQuery");function I(n){const{type:e,...t}=n;return`event: ${e}
|
|
2
2
|
data: ${JSON.stringify(t)}
|
|
3
3
|
|
|
4
|
-
`}o(
|
|
4
|
+
`}o(I,"sseRecord");async function U(n){const e=n.params.id??"";if(b(e)===null)throw new E(404,"node_not_found",`no node ${e}`);const{after:t}=K(n.query),{req:s,res:r}=n;r.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache",connection:"keep-alive"});let d=!1,h=!1,l=!1,c=0;const u=[];let v,f;const _=o(()=>{r.writableEnded||r.end()},"finish"),p=o(()=>{if(!d){if(d=!0,f!==void 0&&clearInterval(f),v?.(),s.destroyed||r.destroyed){u.length=0,c=0,_();return}if(l||u.length>0){h=!0;return}_()}},"close"),g=o(()=>{for(l=!1;u.length>0;){const i=u.shift();if(c-=Buffer.byteLength(i,"utf8"),!r.write(i)){l=!0,r.once("drain",g);return}}h&&_()},"flush"),N=o(()=>{if(d)return;u.length=0,c=0;const i=I({type:"error",error:{code:"stream_dropped",message:"stream reader is too slow"}});u.push(i),c=Buffer.byteLength(i,"utf8"),d=!0,h=!0,f!==void 0&&clearInterval(f),v?.(),l||g()},"dropSlowReader"),y=o(i=>{if(d)return!1;if(l||u.length>0){const a=Buffer.byteLength(i,"utf8");return u.length>=j||c+a>G?(N(),!1):(u.push(i),c+=a,!0)}return r.write(i)||(l=!0,r.once("drain",g)),!0},"write"),S=o((i,a)=>{d||y(I({type:"error",error:{code:i,message:a}})),p()},"fail"),R=o(i=>{try{if(!y(I(i)))return;(i.type==="node.settled"||i.type==="error"&&i.error.code!=="stream_gap")&&p()}catch(a){S("stream_error",a instanceof Error?a.message:String(a))}},"onEvent");f=setInterval(()=>{try{d||y(`: keepalive
|
|
5
5
|
|
|
6
|
-
`)}catch(i){
|
|
6
|
+
`)}catch(i){S("stream_error",i instanceof Error?i.message:String(i))}},D),s.once("close",p);try{const i=F.subscribe(e,t,R);v=i.unsubscribe,i.terminal&&queueMicrotask(p)}catch(i){S("stream_error",i instanceof Error?i.message:String(i))}return{status:200}}o(U,"handleEvents");const ie=[{method:"GET",pattern:"/v1/nodes/:id/events",handler:U}];export{V as NodeEventRing,w as NodeEventTranslationState,ie as nodeEventRoutes,F as nodeEventStreams,z as translateBrokerFrame};
|
package/package.json
CHANGED
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.328",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.328",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "GPL-3.0-only",
|
|
12
12
|
"workspaces": [
|
|
@@ -4795,25 +4795,25 @@
|
|
|
4795
4795
|
},
|
|
4796
4796
|
"packages/crouter-api": {
|
|
4797
4797
|
"name": "@north-light/crouter-api",
|
|
4798
|
-
"version": "0.3.
|
|
4798
|
+
"version": "0.3.328",
|
|
4799
4799
|
"license": "GPL-3.0-only"
|
|
4800
4800
|
},
|
|
4801
4801
|
"packages/crouter-env-docker": {
|
|
4802
4802
|
"name": "@north-light/crouter-env-docker",
|
|
4803
|
-
"version": "0.3.
|
|
4803
|
+
"version": "0.3.328",
|
|
4804
4804
|
"license": "GPL-3.0-only"
|
|
4805
4805
|
},
|
|
4806
4806
|
"packages/crouter-plugin": {
|
|
4807
4807
|
"name": "@north-light/crouter-plugin",
|
|
4808
|
-
"version": "0.3.
|
|
4808
|
+
"version": "0.3.328",
|
|
4809
4809
|
"license": "GPL-3.0-only",
|
|
4810
4810
|
"dependencies": {
|
|
4811
|
-
"@north-light/crouter-api": "^0.3.
|
|
4811
|
+
"@north-light/crouter-api": "^0.3.328"
|
|
4812
4812
|
}
|
|
4813
4813
|
},
|
|
4814
4814
|
"packages/crouter-sdk": {
|
|
4815
4815
|
"name": "@north-light/crouter-sdk",
|
|
4816
|
-
"version": "0.3.
|
|
4816
|
+
"version": "0.3.328",
|
|
4817
4817
|
"license": "GPL-3.0-only",
|
|
4818
4818
|
"dependencies": {
|
|
4819
4819
|
"@north-light/crouter-api": "^0.3.322"
|