@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.17.0-fork.1

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/src/agent-execution.js +36 -51
  3. package/dist/src/background-widget.d.ts +56 -0
  4. package/dist/src/background-widget.js +22 -15
  5. package/dist/src/decoders.js +2 -1
  6. package/dist/src/host-navigator.d.ts +18 -0
  7. package/dist/src/host-navigator.js +71 -13
  8. package/dist/src/host-view.d.ts +16 -1
  9. package/dist/src/host-view.js +109 -42
  10. package/dist/src/host.js +57 -24
  11. package/dist/src/index.d.ts +3 -1
  12. package/dist/src/index.js +867 -545
  13. package/dist/src/index.js.map +3 -3
  14. package/dist/src/registry.js +2 -1
  15. package/dist/src/roles.d.ts +70 -0
  16. package/dist/src/roles.js +325 -0
  17. package/dist/src/settings.d.ts +13 -0
  18. package/dist/src/settings.js +253 -0
  19. package/dist/src/trajectory.d.ts +29 -4
  20. package/dist/src/trajectory.js +58 -3
  21. package/dist/src/types.d.ts +0 -1
  22. package/dist/src/utils.d.ts +2 -1
  23. package/dist/src/utils.js +10 -0
  24. package/dist/src/validation.d.ts +5 -17
  25. package/dist/src/validation.js +9 -458
  26. package/dist/subagents/index.js +1132 -694
  27. package/dist/subagents/index.js.map +3 -3
  28. package/dist/subagents/src/contracts.d.ts +4 -0
  29. package/dist/subagents/src/index.d.ts +1 -1
  30. package/dist/subagents/src/index.js +2 -1
  31. package/dist/subagents/src/manager.js +29 -10
  32. package/dist/subagents/src/navigator.js +396 -247
  33. package/dist/subagents/src/view.d.ts +23 -2
  34. package/dist/subagents/src/view.js +96 -30
  35. package/dist/trajectory/assets/index.html +347 -231
  36. package/dist/trajectory/assets/prism.min.js +13 -0
  37. package/dist/trajectory/index.js +377 -257
  38. package/dist/trajectory/index.js.map +3 -3
  39. package/dist/trajectory/src/assets/index.html +347 -231
  40. package/dist/trajectory/src/assets/prism.min.js +13 -0
  41. package/dist/trajectory/src/export.js +4 -2
  42. package/dist/trajectory/src/index.d.ts +2 -0
  43. package/dist/trajectory/src/index.js +143 -42
  44. package/dist/trajectory/src/server.js +134 -9
  45. package/dist/trajectory/src/server.js.map +1 -1
  46. package/package.json +2 -1
  47. package/src/agent-execution.ts +36 -42
  48. package/src/background-widget.ts +25 -18
  49. package/src/decoders.ts +2 -1
  50. package/src/host-navigator.ts +58 -14
  51. package/src/host-view.ts +93 -37
  52. package/src/host.ts +51 -21
  53. package/src/index.ts +3 -1
  54. package/src/registry.ts +2 -1
  55. package/src/roles.ts +326 -0
  56. package/src/settings.ts +206 -0
  57. package/src/trajectory.ts +76 -7
  58. package/src/types.ts +1 -1
  59. package/src/utils.ts +8 -1
  60. package/src/validation.ts +12 -356
  61. package/subagents/README.md +1 -1
  62. package/subagents/src/contracts.ts +4 -0
  63. package/subagents/src/index.ts +3 -2
  64. package/subagents/src/manager.ts +28 -10
  65. package/subagents/src/navigator.ts +336 -180
  66. package/subagents/src/view.ts +97 -28
  67. package/trajectory/src/assets/index.html +347 -231
  68. package/trajectory/src/assets/prism.min.js +13 -0
  69. package/trajectory/src/export.ts +4 -2
  70. package/trajectory/src/index.ts +118 -38
  71. package/trajectory/src/server.ts +112 -12
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Prism v1.30.0 (core, clike, javascript, typescript, json, bash, diff, python) - https://prismjs.com
3
+ * Copyright (c) 2012 Lea Verou. (MIT Licensed)
4
+ */
5
+ window.Prism = window.Prism || {}; window.Prism.manual = true;
6
+ var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if(document.currentScript&&"SCRIPT"===document.currentScript.tagName)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var P=w.value;if(n.length>e.length)return;if(!(P instanceof i)){var E,S=1;if(y){if(!(E=l(b,A,e,m))||E.index>=e.length)break;var L=E.index,O=E.index+E[0].length,C=A;for(C+=w.value.length;L>=C;)C+=(w=w.next).value.length;if(A=C-=w.value.length,w.value instanceof i)continue;for(var j=w;j!==n.tail&&(C<O||"string"==typeof j.value);j=j.next)S++,C+=j.value.length;S--,P=e.slice(A,C),E.index-=A}else if(!(E=l(b,0,P,m)))continue;L=E.index;var N=E[0],_=P.slice(0,L),M=P.slice(L+N.length),W=A+P.length;g&&W>g.reach&&(g.reach=W);var I=w.prev;if(_&&(I=u(n,I,_),A+=_.length),c(n,I,S),w=u(n,I,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),S>1){var T={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,T),g&&T.reach>g.reach&&(g.reach=T.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,"&quot;")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
7
+ Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
8
+ Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
9
+ !function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var s=e.languages.extend("typescript",{});delete s["class-name"],e.languages.typescript["class-name"].inside=s,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism);
10
+ Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
11
+ !function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i<s.length;i++)o[s[i]]=e.languages.bash[s[i]];e.languages.sh=e.languages.bash,e.languages.shell=e.languages.bash}(Prism);
12
+ !function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var n={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(n).forEach((function(a){var i=n[a],r=[];/^\w+$/.test(a)||r.push(/\w+/.exec(a)[0]),"diff"===a&&r.push("bold"),e.languages.diff[a]={pattern:RegExp("^(?:["+i+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:r,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:n})}(Prism);
13
+ Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;
@@ -22,15 +22,16 @@ export async function exportTrajectoryRunHtml(options) {
22
22
  if (!run)
23
23
  throw new Error(`Workflow run ${options.runId} was not found for session ${options.sessionId}`);
24
24
  const assets = new URL("./assets/", import.meta.url);
25
- const [html, marked, morphdom, favicon] = await Promise.all([
25
+ const [html, marked, morphdom, prism, favicon] = await Promise.all([
26
26
  readFile(new URL("index.html", assets), "utf8"),
27
27
  readFile(new URL("marked.min.js", assets)),
28
28
  readFile(new URL("morphdom.min.js", assets)),
29
+ readFile(new URL("prism.min.js", assets)),
29
30
  readFile(new URL("favicon.png", assets)),
30
31
  ]);
31
32
  const state = {
32
33
  type: "state",
33
- publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId, themes: true, connected: true, runs: [run], subagents: [] }],
34
+ publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId, connected: true, runs: [run], subagents: [] }],
34
35
  updatedAt: Date.now(),
35
36
  };
36
37
  // Base64 data URLs avoid </script> escaping issues inside inlined sources; JSON gets the standard \u003c escape.
@@ -39,6 +40,7 @@ export async function exportTrajectoryRunHtml(options) {
39
40
  output = inlineAsset(output, '<link rel="icon" type="image/png" href="./favicon.png" />', `<link rel="icon" type="image/png" href="data:image/png;base64,${favicon.toString("base64")}" />`);
40
41
  output = inlineAsset(output, '<script src="./marked.min.js"></script>', script(marked));
41
42
  output = inlineAsset(output, '<script src="./morphdom.min.js"></script>', script(morphdom));
43
+ output = inlineAsset(output, '<script src="./prism.min.js"></script>', script(prism));
42
44
  output = inlineAsset(output, '<body data-view="run">', `<body data-view="run">\n <script>window.__PIEWF_STATIC__ = ${JSON.stringify(state).replace(/</g, "\\u003c")};</script>`);
43
45
  return output;
44
46
  }
@@ -13,6 +13,8 @@ export type TrajectoryController = {
13
13
  export declare function trajectoryServerPath(moduleDirectory?: string): string;
14
14
  export declare function trajectoryUrl(port: number): string;
15
15
  export declare function openTrajectoryUrl(url: string): void;
16
+ type LiveStateRecord = Record<string, unknown>;
17
+ export declare function minimalStatePublisher(publisher: LiveStateRecord): LiveStateRecord;
16
18
  export declare function createTrajectoryController(agentDir: string): TrajectoryController;
17
19
  export { DEFAULT_TRAJECTORY_PORT, TRAJECTORY_IDLE_EXIT_MS, TRAJECTORY_LOCK_NAME };
18
20
  export { exportTrajectoryRunHtml, shareTrajectoryRun, type TrajectoryExportOptions, type TrajectoryShareOptions, type TrajectoryShareResult } from "./export.js";
@@ -29,15 +29,22 @@ async function trajectoryFingerprint(serverPath) {
29
29
  function publisherId(cwd, sessionId) { return createHash("sha256").update(`${cwd}\n${sessionId}`).digest("hex").slice(0, 16); }
30
30
  function trajectoryPort(value) { return positiveInteger(value) && value <= 65535 ? value : DEFAULT_TRAJECTORY_PORT; }
31
31
  function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }
32
- async function serverHealthy(port) {
32
+ // Servers older than the identity fields answer `{ ok: true }` alone, so every field is optional.
33
+ async function serverHealth(port) {
33
34
  try {
34
35
  const response = await fetch(`http://127.0.0.1:${String(port)}/health`, { signal: AbortSignal.timeout(300) });
35
- return response.ok;
36
+ if (!response.ok)
37
+ return undefined;
38
+ const body = await response.json().catch(() => undefined);
39
+ if (!object(body))
40
+ return {};
41
+ return { ...(positiveInteger(body.pid) ? { pid: body.pid } : {}), ...(typeof body.fingerprint === "string" ? { fingerprint: body.fingerprint } : {}), ...(positiveInteger(body.startedAt) ? { startedAt: body.startedAt } : {}) };
36
42
  }
37
43
  catch {
38
- return false;
44
+ return undefined;
39
45
  }
40
46
  }
47
+ async function serverHealthy(port) { return await serverHealth(port) !== undefined; }
41
48
  function signalProcess(pid, signal) {
42
49
  try {
43
50
  process.kill(pid, signal);
@@ -82,17 +89,22 @@ async function readLock(path) {
82
89
  return undefined;
83
90
  }
84
91
  }
85
- async function waitForServer(port) {
92
+ // Only a server reporting this fingerprint counts: an older one still bound to the port would otherwise be adopted forever.
93
+ async function waitForServer(port, fingerprint) {
94
+ let foreign = false;
86
95
  for (let attempt = 0; attempt < 60; attempt += 1) {
87
- if (await serverHealthy(port))
96
+ const health = await serverHealth(port);
97
+ if (health?.fingerprint === fingerprint)
88
98
  return;
99
+ foreign = health !== undefined;
89
100
  await delay(50);
90
101
  }
91
- throw new Error(`Trajectory server did not start on port ${String(port)}`);
102
+ throw new Error(foreign ? `Trajectory port ${String(port)} is held by another Trajectory server; stop it and retry` : `Trajectory server did not start on port ${String(port)}`);
92
103
  }
93
104
  async function resolveExistingServer(lockPath, existing, fingerprint) {
94
- if (await serverHealthy(existing.port)) {
95
- if (existing.fingerprint === fingerprint)
105
+ const health = await serverHealth(existing.port);
106
+ if (health) {
107
+ if (existing.fingerprint === fingerprint && health.fingerprint === fingerprint)
96
108
  return existing;
97
109
  await stopStaleServer(existing);
98
110
  await rm(lockPath, { force: true });
@@ -100,7 +112,7 @@ async function resolveExistingServer(lockPath, existing, fingerprint) {
100
112
  }
101
113
  if (await processAlive(existing.pid, existing.startedAt)) {
102
114
  try {
103
- await waitForServer(existing.port);
115
+ await waitForServer(existing.port, fingerprint);
104
116
  return existing;
105
117
  }
106
118
  catch {
@@ -148,12 +160,16 @@ async function ensureTrajectoryServer(agentDir, configuredPort) {
148
160
  finally {
149
161
  await lockHandle?.close();
150
162
  }
163
+ // A server can answer on the port without owning the lock, e.g. one whose lock was lost; it reports its pid, so replace it before spawning.
164
+ const occupant = await serverHealth(configuredPort);
165
+ if (occupant?.pid !== undefined && occupant.fingerprint !== fingerprint)
166
+ await stopStaleServer({ pid: occupant.pid, port: configuredPort, ...(occupant.startedAt === undefined ? {} : { startedAt: occupant.startedAt }) });
151
167
  try {
152
168
  // NOTE: under a Bun-compiled pi binary process.execPath is the pi CLI, and Bun's node:http never writes the WebSocket 101 upgrade (oven-sh/bun#28157), so the server must run on a real node from PATH.
153
169
  const child = spawn(process.versions.bun ? "node" : process.execPath, [serverPath, "--port", String(configuredPort), "--lock", lockPath, "--fingerprint", fingerprint], { detached: true, stdio: "ignore" });
154
170
  const startupError = new Promise((_resolve, reject) => { child.once("error", reject); });
155
171
  child.unref();
156
- await Promise.race([waitForServer(configuredPort), startupError]);
172
+ await Promise.race([waitForServer(configuredPort, fingerprint), startupError]);
157
173
  return { port: configuredPort };
158
174
  }
159
175
  catch (error) {
@@ -181,6 +197,9 @@ const MAX_FRAME_BYTES = 32 * 1024 * 1024;
181
197
  const MAX_LIVE_STATE_BYTES = MAX_FRAME_BYTES - 1024;
182
198
  const MAX_TRANSCRIPT_REQUESTS = 64;
183
199
  const TRANSCRIPT_REQUEST_TIMEOUT_MS = 10_000;
200
+ const ACTIVE_POLL_MS = 1_000;
201
+ // Nobody is watching, so the publisher stops reading run state every second.
202
+ const IDLE_POLL_MS = 10_000;
184
203
  const RECONNECT_INITIAL_DELAY_MS = 100;
185
204
  const RECONNECT_MAX_DELAY_MS = 5_000;
186
205
  function isTimingEntry(value) { return object(value) && value.type === "custom" && value.customType === "pi-workflows:tool-timing"; }
@@ -196,52 +215,109 @@ const MAX_LIVE_OBJECT_KEYS = 64;
196
215
  const LIVE_METADATA_ARRAY_KEYS = new Set(["agents", "runs", "subagents"]);
197
216
  const LIVE_TRUNCATABLE_ARRAY_KEYS = new Set(["events", "phaseHistory"]);
198
217
  const LIVE_METADATA_OBJECT_KEYS = new Set(["transcripts"]);
218
+ function liveValueWillBeBounded(value, key = "", depth = 0) {
219
+ if (typeof value === "string")
220
+ return Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES;
221
+ if (typeof value !== "object" || value === null)
222
+ return false;
223
+ if (depth >= 12)
224
+ return true;
225
+ if (Array.isArray(value)) {
226
+ const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
227
+ if (key === "attemptDetails" && value.length > 8 || !LIVE_METADATA_ARRAY_KEYS.has(key) && value.length > maxEntries)
228
+ return true;
229
+ return value.some((entry) => liveValueWillBeBounded(entry, "", depth + 1));
230
+ }
231
+ if (!object(value))
232
+ return false;
233
+ const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value) : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
234
+ if (!LIVE_METADATA_OBJECT_KEYS.has(key) && Object.keys(value).length > MAX_LIVE_OBJECT_KEYS)
235
+ return true;
236
+ return properties.some((property) => liveValueWillBeBounded(value[property], property, depth + 1));
237
+ }
238
+ const MAX_LIVE_TIMING_BYTES = 64 * 1024;
239
+ /** Keeps the newest timing entries: dropping the oldest leaves a live agent's gantt growing instead of frozen. */
199
240
  function boundedTiming(value) {
200
241
  const entries = Array.isArray(value) ? value.filter(isTimingEntry) : [];
201
242
  const retained = [];
202
243
  let bytes = 2;
203
- for (const entry of entries) {
244
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
204
245
  let serialized;
205
246
  try {
206
- serialized = JSON.stringify(entry);
247
+ serialized = JSON.stringify(entries[index]);
207
248
  }
208
249
  catch {
209
250
  continue;
210
251
  }
211
252
  const nextBytes = bytes + (retained.length ? 1 : 0) + Buffer.byteLength(serialized);
212
- if (nextBytes >= 64 * 1024)
253
+ if (nextBytes >= MAX_LIVE_TIMING_BYTES)
213
254
  break;
214
- retained.push(entry);
255
+ retained.push(entries[index]);
215
256
  bytes = nextBytes;
216
257
  }
217
- return retained;
258
+ return retained.reverse();
218
259
  }
219
- function boundedLiveValue(value, key = "", depth = 0) {
220
- if (typeof value === "string")
221
- return boundedString(value, MAX_LIVE_STRING_BYTES);
222
- if (typeof value === "number" || typeof value === "boolean" || value === null)
223
- return value;
224
- if (depth >= 12)
260
+ function boundedLiveValue(value, key = "", depth = 0, bounds) {
261
+ const pathLength = bounds?.path.length ?? 0;
262
+ if (bounds && key)
263
+ bounds.path.push(key);
264
+ const argsValue = bounds !== undefined && bounds.path.length >= 3 && bounds.path[0] === "runs" && bounds.path[1] === "snapshot" && bounds.path[2] === "args";
265
+ const markArgsTruncated = () => { if (argsValue && bounds.runIndex !== undefined)
266
+ bounds.argsTruncatedRuns.add(bounds.runIndex); };
267
+ try {
268
+ if (typeof value === "string") {
269
+ if (argsValue && Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES)
270
+ markArgsTruncated();
271
+ return boundedString(value, MAX_LIVE_STRING_BYTES);
272
+ }
273
+ if (typeof value === "number" || typeof value === "boolean" || value === null)
274
+ return value;
275
+ if (depth >= 12) {
276
+ markArgsTruncated();
277
+ return undefined;
278
+ }
279
+ if (key === "timing" && !argsValue)
280
+ return boundedTiming(value);
281
+ if (key === "output" && object(value) && value.status === "available") {
282
+ const boundedOutput = boundedLiveValue(value.value, "value", depth + 1, bounds);
283
+ return liveValueWillBeBounded(value.value, "value", depth + 1) ? { status: "truncated", kind: "result", bytes: value.bytes } : { ...value, value: boundedOutput };
284
+ }
285
+ if (Array.isArray(value)) {
286
+ const array = value;
287
+ const maxEntries = !argsValue && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
288
+ const entries = !argsValue && key === "attemptDetails" ? array.slice(-8) : !argsValue && LIVE_METADATA_ARRAY_KEYS.has(key) ? array : array.length <= maxEntries ? array : [...array.slice(0, 32), ...array.slice(-maxEntries + 32)];
289
+ if (entries.length !== array.length)
290
+ markArgsTruncated();
291
+ const publisherRuns = bounds?.path.length === 1 && bounds.path[0] === "runs";
292
+ const bounded = entries.map((entry, index) => {
293
+ const previousRunIndex = bounds?.runIndex;
294
+ if (publisherRuns)
295
+ bounds.runIndex = index;
296
+ const result = boundedLiveValue(entry, "", depth + 1, bounds);
297
+ if (bounds)
298
+ bounds.runIndex = previousRunIndex;
299
+ return result;
300
+ });
301
+ if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key))
302
+ bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
303
+ return bounded;
304
+ }
305
+ if (object(value)) {
306
+ const result = {};
307
+ const keys = Object.keys(value).sort();
308
+ const properties = !argsValue && LIVE_METADATA_OBJECT_KEYS.has(key) ? keys : keys.slice(0, MAX_LIVE_OBJECT_KEYS);
309
+ if (properties.length !== keys.length)
310
+ markArgsTruncated();
311
+ for (const property of properties)
312
+ result[property] = boundedLiveValue(value[property], property, depth + 1, bounds);
313
+ return result;
314
+ }
225
315
  return undefined;
226
- if (key === "timing")
227
- return boundedTiming(value);
228
- if (Array.isArray(value)) {
229
- const array = value;
230
- const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
231
- const entries = key === "attemptDetails" ? array.slice(-8) : LIVE_METADATA_ARRAY_KEYS.has(key) ? array : array.length <= maxEntries ? array : [...array.slice(0, 32), ...array.slice(-maxEntries + 32)];
232
- const bounded = entries.map((entry) => boundedLiveValue(entry, "", depth + 1));
233
- if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key))
234
- bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
235
- return bounded;
236
316
  }
237
- if (object(value)) {
238
- const result = {};
239
- const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value).sort() : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
240
- for (const property of properties)
241
- result[property] = boundedLiveValue(value[property], property, depth + 1);
242
- return result;
317
+ finally {
318
+ if (bounds)
319
+ bounds.path.length = pathLength;
243
320
  }
244
- return undefined;
245
321
  }
246
322
  function transcriptRevisionProjection(value, depth = 0) {
247
323
  if (depth >= 8)
@@ -293,8 +369,21 @@ function projectPublisher(metadata, publisher, revisions) {
293
369
  const publisherId = typeof publisher.id === "string" ? publisher.id : "";
294
370
  return { ...publisher, runs: metadata.runs.map((run) => projectRun(run, `${publisherId}\t${run.run.id}`, revisions)), subagents: metadata.subagents.map((subagent) => projectSubagent(subagent, `${publisherId}\tsubagent\t${subagent.id}`, revisions)) };
295
371
  }
296
- function minimalStatePublisher(publisher) {
297
- return boundedLiveValue(publisher) ?? {};
372
+ export function minimalStatePublisher(publisher) {
373
+ const bounds = { argsTruncatedRuns: new Set(), path: [], runIndex: undefined };
374
+ const bounded = boundedLiveValue(publisher, "", 0, bounds) ?? {};
375
+ const sourceRuns = Array.isArray(publisher.runs) ? publisher.runs : [];
376
+ const boundedRuns = Array.isArray(bounded.runs) ? bounded.runs : [];
377
+ if (!sourceRuns.length || !boundedRuns.length)
378
+ return bounded;
379
+ const runs = boundedRuns.map((run, index) => {
380
+ const sourceRun = sourceRuns[index];
381
+ const sourceSnapshot = object(sourceRun) && object(sourceRun.snapshot) ? sourceRun.snapshot : undefined;
382
+ const boundedSnapshot = object(run) && object(run.snapshot) ? run.snapshot : undefined;
383
+ const argsUnavailable = sourceSnapshot !== undefined && sourceSnapshot.args !== undefined && sourceSnapshot.args !== null && (boundedSnapshot === undefined || !Object.prototype.hasOwnProperty.call(boundedSnapshot, "args"));
384
+ return (bounds.argsTruncatedRuns.has(index) || argsUnavailable) && object(run) ? { ...run, snapshotArgsTruncated: true } : run;
385
+ });
386
+ return { ...bounded, runs };
298
387
  }
299
388
  function publisherStateFrame(publisher, runs, subagents, truncated = false) {
300
389
  const publisherSummary = { ...publisher };
@@ -365,11 +454,12 @@ export function createTrajectoryController(agentDir) {
365
454
  } };
366
455
  const clearTranscriptRequests = () => { for (const request of pendingTranscripts.values())
367
456
  clearTimeout(request.timer); pendingTranscripts.clear(); };
457
+ let viewers = 0;
368
458
  const startPolling = () => { stopPolling(); pollTimer = setInterval(() => { void sendState().catch((error) => { if (!closing)
369
- console.error(`Trajectory state publish failed: ${errorText(error)}`); }); }, 1000); pollTimer.unref(); };
459
+ console.error(`Trajectory state publish failed: ${errorText(error)}`); }); }, viewers > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS); pollTimer.unref(); };
370
460
  let stateLoad;
371
461
  let lastState;
372
- const publisherValue = (input) => ({ id: publisherId(input.cwd, input.sessionId), title: `session ${input.sessionId.slice(0, 8)}`, cwd: input.cwd, sessionId: input.sessionId, themes: input.themes, connected: true });
462
+ const publisherValue = (input) => ({ id: publisherId(input.cwd, input.sessionId), title: `session ${input.sessionId.slice(0, 8)}`, cwd: input.cwd, sessionId: input.sessionId, connected: true });
373
463
  const sendState = async () => {
374
464
  const activeSocket = socket;
375
465
  if (activeSocket !== undefined && stateLoad?.socket === activeSocket)
@@ -547,6 +637,17 @@ export function createTrajectoryController(agentDir) {
547
637
  established = false;
548
638
  return;
549
639
  }
640
+ if (message.type === "publisher:viewers" && typeof message.count === "number") {
641
+ const watched = viewers > 0;
642
+ viewers = message.count;
643
+ if (watched === viewers > 0)
644
+ return;
645
+ if (socket === next && !closing)
646
+ startPolling();
647
+ if (viewers > 0)
648
+ void sendState().catch(() => undefined);
649
+ return;
650
+ }
550
651
  if (message.type === "publisher:transcript" && typeof message.requestId === "string" && typeof message.publisherId === "string" && (typeof message.runId === "string" && typeof message.agentId === "string" || typeof message.subagentId === "string")) {
551
652
  handleTranscriptRequest(next, generation, message, input);
552
653
  return;
@@ -1943,6 +1943,68 @@ function compactSubagent(subagent) {
1943
1943
  const record = subagent;
1944
1944
  return { ...record, transcript: compactTranscript(record.transcript) };
1945
1945
  }
1946
+ function withoutTiming(run) {
1947
+ if (!run || typeof run !== "object" || Array.isArray(run)) return run;
1948
+ const record = run;
1949
+ if (!record.transcripts || typeof record.transcripts !== "object" || Array.isArray(record.transcripts)) return run;
1950
+ const transcripts = {};
1951
+ for (const [id, entry] of Object.entries(record.transcripts)) transcripts[id] = entry && typeof entry === "object" && !Array.isArray(entry) ? { ...entry, timing: [] } : entry;
1952
+ return { ...record, transcripts };
1953
+ }
1954
+ function knownTiming(run, prefix, sent, mark) {
1955
+ if (!run || typeof run !== "object" || Array.isArray(run)) return run;
1956
+ const record = run;
1957
+ if (!record.transcripts || typeof record.transcripts !== "object" || Array.isArray(record.transcripts)) return run;
1958
+ const transcripts = {};
1959
+ for (const [id, entry] of Object.entries(record.transcripts)) {
1960
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
1961
+ transcripts[id] = entry;
1962
+ continue;
1963
+ }
1964
+ const value = entry;
1965
+ if (typeof value.revision !== "number" || !Array.isArray(value.timing)) {
1966
+ transcripts[id] = entry;
1967
+ continue;
1968
+ }
1969
+ if (sent.get(`${prefix} ${id}`) === value.revision) {
1970
+ const rest = { ...value };
1971
+ delete rest.timing;
1972
+ transcripts[id] = rest;
1973
+ continue;
1974
+ }
1975
+ mark(`${prefix} ${id}`, value.revision);
1976
+ transcripts[id] = entry;
1977
+ }
1978
+ return { ...record, transcripts };
1979
+ }
1980
+ function forgetTiming(run, prefix, sent) {
1981
+ if (!run || typeof run !== "object" || Array.isArray(run)) return;
1982
+ const record = run;
1983
+ if (!record.transcripts || typeof record.transcripts !== "object" || Array.isArray(record.transcripts)) return;
1984
+ for (const id of Object.keys(record.transcripts)) sent.delete(`${prefix} ${id}`);
1985
+ }
1986
+ function focusedState(state, focus, sent, mark = () => {
1987
+ }) {
1988
+ return {
1989
+ ...state,
1990
+ publishers: state.publishers.map((publisher) => {
1991
+ if (!publisher || typeof publisher !== "object" || Array.isArray(publisher)) return publisher;
1992
+ const value = publisher;
1993
+ const runs = Array.isArray(value.runs) ? value.runs : void 0;
1994
+ if (!runs) return publisher;
1995
+ const focusedPublisher = focus !== void 0 && value.id === focus.publisherId;
1996
+ return { ...value, runs: runs.map((run) => {
1997
+ const id = run && typeof run === "object" && !Array.isArray(run) ? run.run?.id : void 0;
1998
+ const prefix = `${String(value.id)} ${String(id)}`;
1999
+ if (!focusedPublisher || id !== focus.runId) {
2000
+ if (sent) forgetTiming(run, prefix, sent);
2001
+ return withoutTiming(run);
2002
+ }
2003
+ return sent ? knownTiming(run, prefix, sent, mark) : run;
2004
+ }) };
2005
+ })
2006
+ };
2007
+ }
1946
2008
  function compactPublishers(publishers) {
1947
2009
  return publishers.map((publisher) => {
1948
2010
  if (!publisher || typeof publisher !== "object" || Array.isArray(publisher)) return publisher;
@@ -1981,7 +2043,7 @@ function minimalPublisher(publisher) {
1981
2043
  function tinyPublisher(publisher) {
1982
2044
  if (!publisher || typeof publisher !== "object" || Array.isArray(publisher)) return { id: "publisher" };
1983
2045
  const value = publisher;
1984
- return { id: typeof value.id === "string" ? value.id.slice(0, 200) : "publisher", title: typeof value.title === "string" ? value.title.slice(0, 200) : void 0, cwd: typeof value.cwd === "string" ? value.cwd.slice(0, 1024) : void 0, sessionId: typeof value.sessionId === "string" ? value.sessionId.slice(0, 200) : void 0, themes: value.themes, connected: value.connected === true };
2046
+ return { id: typeof value.id === "string" ? value.id.slice(0, 200) : "publisher", title: typeof value.title === "string" ? value.title.slice(0, 200) : void 0, cwd: typeof value.cwd === "string" ? value.cwd.slice(0, 1024) : void 0, sessionId: typeof value.sessionId === "string" ? value.sessionId.slice(0, 200) : void 0, connected: value.connected === true };
1985
2047
  }
1986
2048
  function encodePublisherList(publishers, updatedAt, maxBytes, initial, truncated, projector, fallback) {
1987
2049
  const prefix = `{"type":"state","publishers":[`;
@@ -2062,14 +2124,19 @@ function frame(payload, maxBytes) {
2062
2124
  function isState(value) {
2063
2125
  return Boolean(value && typeof value === "object" && !Array.isArray(value) && value.type === "state");
2064
2126
  }
2065
- function writeFrame(client, packet, state) {
2127
+ function writeFrame(client, packet, state, onDelivered) {
2066
2128
  if (state && client.backpressured) {
2067
2129
  client.pendingState = packet;
2130
+ client.pendingDeliver = onDelivered;
2068
2131
  return;
2069
2132
  }
2070
2133
  try {
2071
- if (state) client.pendingState = void 0;
2134
+ if (state) {
2135
+ client.pendingState = void 0;
2136
+ client.pendingDeliver = void 0;
2137
+ }
2072
2138
  if (!client.socket.write(packet)) client.backpressured = true;
2139
+ onDelivered?.();
2073
2140
  } catch {
2074
2141
  client.socket.destroy();
2075
2142
  }
@@ -2145,6 +2212,7 @@ function authorized(request, port) {
2145
2212
  function createTrajectoryServer(port, lockPath, options = {}) {
2146
2213
  const maxFrameBytes = options.maxFrameBytes ?? MAX_FRAME_BYTES;
2147
2214
  const serverFingerprint = options.fingerprint ?? "";
2215
+ let startedAt = 0;
2148
2216
  const clients = /* @__PURE__ */ new Set();
2149
2217
  const publishers = /* @__PURE__ */ new Map();
2150
2218
  let latest = { type: "state", publishers: [], updatedAt: Date.now(), initial: true };
@@ -2216,8 +2284,21 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2216
2284
  const broadcast = (value) => {
2217
2285
  if (isState(value)) {
2218
2286
  try {
2219
- const stateFrame = frame(encodeState(value, maxFrameBytes), maxFrameBytes);
2220
- for (const client of clients) if (client.kind === "browser") writeFrame(client, stateFrame, true);
2287
+ let shared;
2288
+ for (const client of clients) {
2289
+ if (client.kind !== "browser") continue;
2290
+ if (client.focusAware !== true) {
2291
+ shared ??= frame(encodeState(value, maxFrameBytes), maxFrameBytes);
2292
+ writeFrame(client, shared, true);
2293
+ continue;
2294
+ }
2295
+ const sent = client.sentTiming ??= /* @__PURE__ */ new Map();
2296
+ const marks = [];
2297
+ const stateFrame = frame(encodeState(focusedState(value, client.focus, sent, (key, revision) => marks.push([key, revision])), maxFrameBytes), maxFrameBytes);
2298
+ writeFrame(client, stateFrame, true, () => {
2299
+ for (const [key, revision] of marks) sent.set(key, revision);
2300
+ });
2301
+ }
2221
2302
  } catch {
2222
2303
  for (const client of clients) if (client.kind === "browser") client.socket.destroy();
2223
2304
  }
@@ -2225,6 +2306,13 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2225
2306
  }
2226
2307
  for (const client of clients) if (client.kind === "browser") emit(client, value);
2227
2308
  };
2309
+ let viewers = 0;
2310
+ const notifyViewers = () => {
2311
+ const count = [...clients].filter((candidate) => candidate.kind === "browser").length;
2312
+ if (count === viewers) return;
2313
+ viewers = count;
2314
+ for (const candidate of clients) if (candidate.kind === "publisher") emit(candidate, { type: "publisher:viewers", count });
2315
+ };
2228
2316
  const publishState = () => {
2229
2317
  const values = [...publishers.values()].map(({ value, generation }) => ({ ...value, generation }));
2230
2318
  latest = { type: "state", publishers: values, updatedAt: Date.now() };
@@ -2233,6 +2321,7 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2233
2321
  const disconnect = (client) => {
2234
2322
  clearPending(client, client.kind === "publisher" ? "Publisher is disconnected" : "Trajectory browser disconnected");
2235
2323
  clients.delete(client);
2324
+ if (client.kind === "browser") notifyViewers();
2236
2325
  if (client.kind === "publisher" && client.publisherId && publishers.get(client.publisherId)?.client === client) {
2237
2326
  publishers.delete(client.publisherId);
2238
2327
  publishState();
@@ -2259,6 +2348,26 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2259
2348
  if (message.type === "ui:attach") {
2260
2349
  client.kind = "browser";
2261
2350
  emit(client, latest);
2351
+ notifyViewers();
2352
+ return;
2353
+ }
2354
+ if (message.type === "ui:focus" && client.kind === "browser") {
2355
+ const publisherId = typeof message.publisherId === "string" ? message.publisherId : void 0;
2356
+ const runId = typeof message.runId === "string" ? message.runId : void 0;
2357
+ client.focusAware = true;
2358
+ client.sentTiming = /* @__PURE__ */ new Map();
2359
+ if (publisherId !== void 0 && runId !== void 0) client.focus = { publisherId, runId };
2360
+ else delete client.focus;
2361
+ const sent = client.sentTiming;
2362
+ const marks = [];
2363
+ const focused = focusedState(latest, client.focus, sent, (key, revision) => marks.push([key, revision]));
2364
+ try {
2365
+ writeFrame(client, frame(encodeState(focused, maxFrameBytes), maxFrameBytes), true, () => {
2366
+ for (const [key, revision] of marks) sent.set(key, revision);
2367
+ });
2368
+ } catch {
2369
+ client.socket.destroy();
2370
+ }
2262
2371
  return;
2263
2372
  }
2264
2373
  if (client.kind === "publisher" && message.type === "publisher:attach" && typeof message.publisherId === "string") {
@@ -2273,6 +2382,7 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2273
2382
  }
2274
2383
  const generation = (previous?.generation ?? 0) + 1;
2275
2384
  publishers.set(message.publisherId, { client, value: { ...previous?.value ?? { id: message.publisherId }, id: message.publisherId, connected: true }, generation });
2385
+ emit(client, { type: "publisher:viewers", count: viewers });
2276
2386
  publishState();
2277
2387
  return;
2278
2388
  }
@@ -2409,7 +2519,7 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2409
2519
  }
2410
2520
  const path2 = url.pathname;
2411
2521
  if (request.method === "GET" && path2 === "/health") {
2412
- writeJson(response, 200, { ok: true });
2522
+ writeJson(response, 200, { ok: true, pid: process.pid, fingerprint: serverFingerprint, startedAt });
2413
2523
  return;
2414
2524
  }
2415
2525
  if (request.method === "GET" && (path2 === "/" || path2 === "/index.html")) {
@@ -2439,6 +2549,15 @@ function createTrajectoryServer(port, lockPath, options = {}) {
2439
2549
  });
2440
2550
  return;
2441
2551
  }
2552
+ if (request.method === "GET" && path2 === "/prism.min.js") {
2553
+ void readFile(new URL("./assets/prism.min.js", import.meta.url)).then((script) => {
2554
+ response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "content-length": script.byteLength, "cache-control": "no-store" });
2555
+ response.end(script);
2556
+ }).catch(() => {
2557
+ writeJson(response, 500, { error: "Trajectory syntax highlighting library is unavailable" });
2558
+ });
2559
+ return;
2560
+ }
2442
2561
  if (request.method === "GET" && (path2 === "/favicon.png" || path2 === "/favicon.ico")) {
2443
2562
  void readFile(new URL("./assets/favicon.png", import.meta.url)).then((icon) => {
2444
2563
  response.writeHead(200, { "content-type": "image/png", "content-length": icon.byteLength, "cache-control": "no-store" });
@@ -2470,12 +2589,14 @@ Connection: Upgrade\r
2470
2589
  Sec-WebSocket-Accept: ${accept}\r
2471
2590
  \r
2472
2591
  `);
2473
- const client = { socket, kind: "publisher", buffer: Buffer.alloc(0), pendingState: void 0, backpressured: false, superseded: false };
2592
+ const client = { socket, kind: "publisher", buffer: Buffer.alloc(0), pendingState: void 0, pendingDeliver: void 0, backpressured: false, superseded: false };
2474
2593
  socket.on("drain", () => {
2475
2594
  const pendingState = client.pendingState;
2595
+ const pendingDeliver = client.pendingDeliver;
2476
2596
  client.pendingState = void 0;
2597
+ client.pendingDeliver = void 0;
2477
2598
  client.backpressured = false;
2478
- if (pendingState !== void 0) writeFrame(client, pendingState, true);
2599
+ if (pendingState !== void 0) writeFrame(client, pendingState, true, pendingDeliver);
2479
2600
  });
2480
2601
  clients.add(client);
2481
2602
  socket.on("data", (chunk) => {
@@ -2492,9 +2613,13 @@ Sec-WebSocket-Accept: ${accept}\r
2492
2613
  socket.on("error", () => {
2493
2614
  disconnect(client);
2494
2615
  });
2616
+ socket.on("end", () => {
2617
+ socket.destroy();
2618
+ });
2495
2619
  });
2496
2620
  server.once("listening", () => {
2497
- void writeFile(lockPath, `${JSON.stringify({ pid: process.pid, port, fingerprint: serverFingerprint, startedAt: Date.now() })}
2621
+ startedAt = Date.now();
2622
+ void writeFile(lockPath, `${JSON.stringify({ pid: process.pid, port, fingerprint: serverFingerprint, startedAt })}
2498
2623
  `, { mode: 384 }).catch(() => {
2499
2624
  process.exitCode = 1;
2500
2625
  });