@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.19.1-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 (94) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/dist/src/agent-execution.js +114 -87
  3. package/dist/src/background-widget.d.ts +61 -0
  4. package/dist/src/background-widget.js +23 -16
  5. package/dist/src/decoders.js +28 -1
  6. package/dist/src/execution.d.ts +4 -2
  7. package/dist/src/execution.js +43 -2
  8. package/dist/src/host-delivery.js +1 -0
  9. package/dist/src/host-navigator.d.ts +18 -0
  10. package/dist/src/host-navigator.js +71 -13
  11. package/dist/src/host-recovery.d.ts +5 -2
  12. package/dist/src/host-recovery.js +3 -3
  13. package/dist/src/host-view.d.ts +21 -1
  14. package/dist/src/host-view.js +138 -47
  15. package/dist/src/host.d.ts +2 -2
  16. package/dist/src/host.js +246 -60
  17. package/dist/src/index.d.ts +3 -1
  18. package/dist/src/index.js +1346 -622
  19. package/dist/src/index.js.map +3 -3
  20. package/dist/src/paths.d.ts +2 -0
  21. package/dist/src/paths.js +2 -0
  22. package/dist/src/registry.js +3 -2
  23. package/dist/src/roles.d.ts +70 -0
  24. package/dist/src/roles.js +326 -0
  25. package/dist/src/script-tools.d.ts +27 -0
  26. package/dist/src/script-tools.js +87 -0
  27. package/dist/src/settings.d.ts +26 -0
  28. package/dist/src/settings.js +277 -0
  29. package/dist/src/trajectory.d.ts +39 -4
  30. package/dist/src/trajectory.js +82 -5
  31. package/dist/src/types.d.ts +30 -3
  32. package/dist/src/types.js +1 -1
  33. package/dist/src/utils.d.ts +21 -1
  34. package/dist/src/utils.js +40 -0
  35. package/dist/src/validation.d.ts +10 -17
  36. package/dist/src/validation.js +29 -458
  37. package/dist/starter/index.js +0 -2
  38. package/dist/starter/index.js.map +1 -1
  39. package/dist/starter/review-loop.d.ts +0 -13
  40. package/dist/starter/review-loop.js +0 -2
  41. package/dist/subagents/index.js +1344 -798
  42. package/dist/subagents/index.js.map +3 -3
  43. package/dist/subagents/src/contracts.d.ts +49 -29
  44. package/dist/subagents/src/contracts.js +18 -7
  45. package/dist/subagents/src/index.d.ts +1 -1
  46. package/dist/subagents/src/index.js +15 -7
  47. package/dist/subagents/src/manager.js +32 -13
  48. package/dist/subagents/src/navigator.js +396 -247
  49. package/dist/subagents/src/view.d.ts +24 -2
  50. package/dist/subagents/src/view.js +107 -80
  51. package/dist/trajectory/assets/index.html +370 -232
  52. package/dist/trajectory/assets/prism.min.js +13 -0
  53. package/dist/trajectory/index.js +445 -277
  54. package/dist/trajectory/index.js.map +3 -3
  55. package/dist/trajectory/src/assets/index.html +370 -232
  56. package/dist/trajectory/src/assets/prism.min.js +13 -0
  57. package/dist/trajectory/src/export.js +4 -2
  58. package/dist/trajectory/src/index.d.ts +2 -0
  59. package/dist/trajectory/src/index.js +151 -60
  60. package/dist/trajectory/src/server.js +138 -11
  61. package/dist/trajectory/src/server.js.map +2 -2
  62. package/package.json +7 -6
  63. package/skills/pi-extensible-workflows/SKILL.md +2 -0
  64. package/src/agent-execution.ts +110 -80
  65. package/src/background-widget.ts +26 -19
  66. package/src/decoders.ts +22 -1
  67. package/src/execution.ts +41 -4
  68. package/src/host-delivery.ts +1 -0
  69. package/src/host-navigator.ts +58 -14
  70. package/src/host-recovery.ts +8 -7
  71. package/src/host-view.ts +116 -42
  72. package/src/host.ts +223 -55
  73. package/src/index.ts +3 -1
  74. package/src/paths.ts +3 -0
  75. package/src/registry.ts +3 -2
  76. package/src/roles.ts +327 -0
  77. package/src/script-tools.ts +76 -0
  78. package/src/settings.ts +224 -0
  79. package/src/trajectory.ts +97 -9
  80. package/src/types.ts +12 -5
  81. package/src/utils.ts +36 -1
  82. package/src/validation.ts +30 -356
  83. package/starter/review-loop.ts +0 -2
  84. package/subagents/README.md +3 -3
  85. package/subagents/src/contracts.ts +22 -7
  86. package/subagents/src/index.ts +18 -7
  87. package/subagents/src/manager.ts +31 -13
  88. package/subagents/src/navigator.ts +336 -180
  89. package/subagents/src/view.ts +108 -76
  90. package/trajectory/src/assets/index.html +370 -232
  91. package/trajectory/src/assets/prism.min.js +13 -0
  92. package/trajectory/src/export.ts +4 -2
  93. package/trajectory/src/index.ts +124 -47
  94. package/trajectory/src/server.ts +114 -14
@@ -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";
@@ -8,7 +8,7 @@ import { getAgentDir } from "@earendil-works/pi-coding-agent";
8
8
  import { clearTrajectoryHost, setTrajectoryHost } from "../../src/trajectory-host-handle.js";
9
9
  import { processAlive } from "../../src/session-lease.js";
10
10
  import { errorText, isNodeError, object, positiveInteger } from "../../src/utils.js";
11
- import { isTrajectoryAction, isTrajectoryTarget, trajectoryActionError, TRAJECTORY_MAX_TRANSCRIPT_BYTES } from "../../src/trajectory.js";
11
+ import { isTimingTranscriptEntry, isTrajectoryAction, isTrajectoryTarget, trajectoryActionError, TRAJECTORY_MAX_TRANSCRIPT_BYTES } from "../../src/trajectory.js";
12
12
  import { shareTrajectoryRun } from "./export.js";
13
13
  const DEFAULT_TRAJECTORY_PORT = 7432;
14
14
  const TRAJECTORY_IDLE_EXIT_MS = 5 * 60 * 1000;
@@ -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,9 +197,11 @@ 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
- function isTimingEntry(value) { return object(value) && value.type === "custom" && value.customType === "pi-workflows:tool-timing"; }
187
205
  function boundedString(value, maxLength = 1024) {
188
206
  if (typeof value !== "string")
189
207
  return value;
@@ -194,54 +212,111 @@ const MAX_LIVE_STRING_BYTES = 64 * 1024;
194
212
  const MAX_LIVE_ARRAY_ENTRIES = 256;
195
213
  const MAX_LIVE_OBJECT_KEYS = 64;
196
214
  const LIVE_METADATA_ARRAY_KEYS = new Set(["agents", "runs", "subagents"]);
197
- const LIVE_TRUNCATABLE_ARRAY_KEYS = new Set(["events", "phaseHistory"]);
215
+ const LIVE_TRUNCATABLE_ARRAY_KEYS = new Set(["events", "phaseHistory", "scriptCalls"]);
198
216
  const LIVE_METADATA_OBJECT_KEYS = new Set(["transcripts"]);
217
+ function liveValueWillBeBounded(value, key = "", depth = 0) {
218
+ if (typeof value === "string")
219
+ return Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES;
220
+ if (typeof value !== "object" || value === null)
221
+ return false;
222
+ if (depth >= 12)
223
+ return true;
224
+ if (Array.isArray(value)) {
225
+ const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
226
+ if (key === "attemptDetails" && value.length > 8 || !LIVE_METADATA_ARRAY_KEYS.has(key) && value.length > maxEntries)
227
+ return true;
228
+ return value.some((entry) => liveValueWillBeBounded(entry, "", depth + 1));
229
+ }
230
+ if (!object(value))
231
+ return false;
232
+ const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value) : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
233
+ if (!LIVE_METADATA_OBJECT_KEYS.has(key) && Object.keys(value).length > MAX_LIVE_OBJECT_KEYS)
234
+ return true;
235
+ return properties.some((property) => liveValueWillBeBounded(value[property], property, depth + 1));
236
+ }
237
+ const MAX_LIVE_TIMING_BYTES = 64 * 1024;
238
+ /** Keeps the newest timing entries: dropping the oldest leaves a live agent's gantt growing instead of frozen. */
199
239
  function boundedTiming(value) {
200
- const entries = Array.isArray(value) ? value.filter(isTimingEntry) : [];
240
+ const entries = Array.isArray(value) ? value.filter(isTimingTranscriptEntry) : [];
201
241
  const retained = [];
202
242
  let bytes = 2;
203
- for (const entry of entries) {
243
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
204
244
  let serialized;
205
245
  try {
206
- serialized = JSON.stringify(entry);
246
+ serialized = JSON.stringify(entries[index]);
207
247
  }
208
248
  catch {
209
249
  continue;
210
250
  }
211
251
  const nextBytes = bytes + (retained.length ? 1 : 0) + Buffer.byteLength(serialized);
212
- if (nextBytes >= 64 * 1024)
252
+ if (nextBytes >= MAX_LIVE_TIMING_BYTES)
213
253
  break;
214
- retained.push(entry);
254
+ retained.push(entries[index]);
215
255
  bytes = nextBytes;
216
256
  }
217
- return retained;
257
+ return retained.reverse();
218
258
  }
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)
259
+ function boundedLiveValue(value, key = "", depth = 0, bounds) {
260
+ const pathLength = bounds?.path.length ?? 0;
261
+ if (bounds && key)
262
+ bounds.path.push(key);
263
+ const argsValue = bounds !== undefined && bounds.path.length >= 3 && bounds.path[0] === "runs" && bounds.path[1] === "snapshot" && bounds.path[2] === "args";
264
+ const markArgsTruncated = () => { if (argsValue && bounds.runIndex !== undefined)
265
+ bounds.argsTruncatedRuns.add(bounds.runIndex); };
266
+ try {
267
+ if (typeof value === "string") {
268
+ if (argsValue && Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES)
269
+ markArgsTruncated();
270
+ return boundedString(value, MAX_LIVE_STRING_BYTES);
271
+ }
272
+ if (typeof value === "number" || typeof value === "boolean" || value === null)
273
+ return value;
274
+ if (depth >= 12) {
275
+ markArgsTruncated();
276
+ return undefined;
277
+ }
278
+ if (key === "timing" && !argsValue)
279
+ return boundedTiming(value);
280
+ if (key === "output" && object(value) && value.status === "available") {
281
+ const boundedOutput = boundedLiveValue(value.value, "value", depth + 1, bounds);
282
+ return liveValueWillBeBounded(value.value, "value", depth + 1) ? { status: "truncated", kind: "result", bytes: value.bytes } : { ...value, value: boundedOutput };
283
+ }
284
+ if (Array.isArray(value)) {
285
+ const array = value;
286
+ const maxEntries = !argsValue && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
287
+ 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)];
288
+ if (entries.length !== array.length)
289
+ markArgsTruncated();
290
+ const publisherRuns = bounds?.path.length === 1 && bounds.path[0] === "runs";
291
+ const bounded = entries.map((entry, index) => {
292
+ const previousRunIndex = bounds?.runIndex;
293
+ if (publisherRuns)
294
+ bounds.runIndex = index;
295
+ const result = boundedLiveValue(entry, "", depth + 1, bounds);
296
+ if (bounds)
297
+ bounds.runIndex = previousRunIndex;
298
+ return result;
299
+ });
300
+ if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key))
301
+ bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
302
+ return bounded;
303
+ }
304
+ if (object(value)) {
305
+ const result = {};
306
+ const keys = Object.keys(value).sort();
307
+ const properties = !argsValue && LIVE_METADATA_OBJECT_KEYS.has(key) ? keys : keys.slice(0, MAX_LIVE_OBJECT_KEYS);
308
+ if (properties.length !== keys.length)
309
+ markArgsTruncated();
310
+ for (const property of properties)
311
+ result[property] = boundedLiveValue(value[property], property, depth + 1, bounds);
312
+ return result;
313
+ }
225
314
  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
315
  }
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;
316
+ finally {
317
+ if (bounds)
318
+ bounds.path.length = pathLength;
243
319
  }
244
- return undefined;
245
320
  }
246
321
  function transcriptRevisionProjection(value, depth = 0) {
247
322
  if (depth >= 8)
@@ -293,8 +368,21 @@ function projectPublisher(metadata, publisher, revisions) {
293
368
  const publisherId = typeof publisher.id === "string" ? publisher.id : "";
294
369
  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
370
  }
296
- function minimalStatePublisher(publisher) {
297
- return boundedLiveValue(publisher) ?? {};
371
+ export function minimalStatePublisher(publisher) {
372
+ const bounds = { argsTruncatedRuns: new Set(), path: [], runIndex: undefined };
373
+ const bounded = boundedLiveValue(publisher, "", 0, bounds) ?? {};
374
+ const sourceRuns = Array.isArray(publisher.runs) ? publisher.runs : [];
375
+ const boundedRuns = Array.isArray(bounded.runs) ? bounded.runs : [];
376
+ if (!sourceRuns.length || !boundedRuns.length)
377
+ return bounded;
378
+ const runs = boundedRuns.map((run, index) => {
379
+ const sourceRun = sourceRuns[index];
380
+ const sourceSnapshot = object(sourceRun) && object(sourceRun.snapshot) ? sourceRun.snapshot : undefined;
381
+ const boundedSnapshot = object(run) && object(run.snapshot) ? run.snapshot : undefined;
382
+ const argsUnavailable = sourceSnapshot !== undefined && sourceSnapshot.args !== undefined && sourceSnapshot.args !== null && (boundedSnapshot === undefined || !Object.prototype.hasOwnProperty.call(boundedSnapshot, "args"));
383
+ return (bounds.argsTruncatedRuns.has(index) || argsUnavailable) && object(run) ? { ...run, snapshotArgsTruncated: true } : run;
384
+ });
385
+ return { ...bounded, runs };
298
386
  }
299
387
  function publisherStateFrame(publisher, runs, subagents, truncated = false) {
300
388
  const publisherSummary = { ...publisher };
@@ -365,11 +453,12 @@ export function createTrajectoryController(agentDir) {
365
453
  } };
366
454
  const clearTranscriptRequests = () => { for (const request of pendingTranscripts.values())
367
455
  clearTimeout(request.timer); pendingTranscripts.clear(); };
456
+ let viewers = 0;
368
457
  const startPolling = () => { stopPolling(); pollTimer = setInterval(() => { void sendState().catch((error) => { if (!closing)
369
- console.error(`Trajectory state publish failed: ${errorText(error)}`); }); }, 1000); pollTimer.unref(); };
458
+ console.error(`Trajectory state publish failed: ${errorText(error)}`); }); }, viewers > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS); pollTimer.unref(); };
370
459
  let stateLoad;
371
460
  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 });
461
+ 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
462
  const sendState = async () => {
374
463
  const activeSocket = socket;
375
464
  if (activeSocket !== undefined && stateLoad?.socket === activeSocket)
@@ -423,23 +512,14 @@ export function createTrajectoryController(agentDir) {
423
512
  }
424
513
  };
425
514
  const transcriptFromFallback = async (input, request) => {
426
- const boundedTranscriptResult = (result) => {
427
- try {
428
- if (result.status === "available" && Buffer.byteLength(JSON.stringify(result.entries)) > TRAJECTORY_MAX_TRANSCRIPT_BYTES)
429
- return { status: "oversized", revision: result.revision, entries: [], error: "Transcript is too large" };
430
- }
431
- catch {
432
- return { status: "failed", revision: result.revision, entries: [], error: "Transcript failed" };
433
- }
434
- return result;
435
- };
436
- const boundedResult = (entries, revision) => { try {
437
- if (Buffer.byteLength(JSON.stringify(entries)) > TRAJECTORY_MAX_TRANSCRIPT_BYTES)
438
- return { status: "oversized", revision, entries: [], error: "Transcript is too large" };
515
+ const oversized = (entries, revision) => { try {
516
+ return Buffer.byteLength(JSON.stringify(entries)) > TRAJECTORY_MAX_TRANSCRIPT_BYTES ? { status: "oversized", revision, entries: [], error: "Transcript is too large" } : undefined;
439
517
  }
440
518
  catch {
441
519
  return { status: "failed", revision, entries: [], error: "Transcript failed" };
442
- } return { status: entries.length ? "available" : "empty", revision, entries }; };
520
+ } };
521
+ const boundedTranscriptResult = (result) => (result.status === "available" ? oversized(result.entries, result.revision) : undefined) ?? result;
522
+ const boundedResult = (entries, revision) => oversized(entries, revision) ?? { status: entries.length ? "available" : "empty", revision, entries };
443
523
  if (input.loadTranscript) {
444
524
  const result = await input.loadTranscript(request);
445
525
  const revisionChanged = request.revision !== undefined && result.revision !== request.revision && result.status !== "missing" && result.status !== "failed" && result.status !== "oversized" && result.status !== "disconnected";
@@ -547,6 +627,17 @@ export function createTrajectoryController(agentDir) {
547
627
  established = false;
548
628
  return;
549
629
  }
630
+ if (message.type === "publisher:viewers" && typeof message.count === "number") {
631
+ const watched = viewers > 0;
632
+ viewers = message.count;
633
+ if (watched === viewers > 0)
634
+ return;
635
+ if (socket === next && !closing)
636
+ startPolling();
637
+ if (viewers > 0)
638
+ void sendState().catch(() => undefined);
639
+ return;
640
+ }
550
641
  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
642
  handleTranscriptRequest(next, generation, message, input);
552
643
  return;