sootsim 0.1.145 → 0.1.147

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 (154) hide show
  1. package/dist-cli/bin.js +3 -3
  2. package/dist-cli/chunks/{agent-YX5V2KV7.js → agent-COSQ2VSX.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-3X53DI4P.js → agent-wrapper-J6LA36AT.js} +2 -2
  4. package/dist-cli/chunks/{app-fonts-5I6J7HMM.js → app-fonts-H4CXDIF6.js} +2 -2
  5. package/dist-cli/chunks/{assert-BDSCYJ3N.js → assert-THSUXYWU.js} +2 -2
  6. package/dist-cli/chunks/auto-bootstrap-SPALBOGS.js +2 -0
  7. package/dist-cli/chunks/beta-U3V7RSRI.js +2 -0
  8. package/dist-cli/chunks/{camera-HXVXO2XW.js → camera-4NINTJSC.js} +2 -2
  9. package/dist-cli/chunks/{chunk-36RF3WRB.js → chunk-4CLJIXM3.js} +1 -1
  10. package/dist-cli/chunks/{chunk-26P6DJRO.js → chunk-54R6LIOF.js} +2 -2
  11. package/dist-cli/chunks/{chunk-2GAVMQGW.js → chunk-5CTOWZY4.js} +2 -2
  12. package/dist-cli/chunks/{chunk-KSNQFEBZ.js → chunk-5MX2B2EO.js} +2 -2
  13. package/dist-cli/chunks/{chunk-QZAJOB4N.js → chunk-5PKIU2SG.js} +1 -1
  14. package/dist-cli/chunks/{chunk-CUPKQONE.js → chunk-B46RT5KY.js} +1 -1
  15. package/dist-cli/chunks/{chunk-DNM2AOHD.js → chunk-BDLFGAKU.js} +1 -1
  16. package/dist-cli/chunks/{chunk-HVRSV4QO.js → chunk-BOU5OTEA.js} +1 -1
  17. package/dist-cli/chunks/{chunk-NX6V5VAV.js → chunk-DO2Z5CNM.js} +3 -3
  18. package/dist-cli/chunks/{chunk-4QJCDB6B.js → chunk-DXL23RFP.js} +2 -2
  19. package/dist-cli/chunks/chunk-EJIK4ANI.js +2 -0
  20. package/dist-cli/chunks/chunk-EWAA5DKR.js +1 -0
  21. package/dist-cli/chunks/{chunk-TRA4IJCY.js → chunk-FOLNNKZG.js} +2 -2
  22. package/dist-cli/chunks/{chunk-YWUBSH2K.js → chunk-FY4TSSMQ.js} +1 -1
  23. package/dist-cli/chunks/{chunk-F6UIBH3K.js → chunk-HENMFGLU.js} +1 -1
  24. package/dist-cli/chunks/{chunk-YENFSYAG.js → chunk-HN2Q2ZWT.js} +1 -1
  25. package/dist-cli/chunks/{chunk-7GBAY2BX.js → chunk-HYVQAUD4.js} +2 -2
  26. package/dist-cli/chunks/{chunk-QS37KABB.js → chunk-KAAYKAUG.js} +2 -2
  27. package/dist-cli/chunks/chunk-KTCY7WJ2.js +2 -0
  28. package/dist-cli/chunks/{chunk-I5FEWPWO.js → chunk-L7SG7TDU.js} +1 -1
  29. package/dist-cli/chunks/{chunk-F6YOWJAA.js → chunk-LWC2FLTG.js} +2 -2
  30. package/dist-cli/chunks/{chunk-ANTCWWXN.js → chunk-MCQIOGWG.js} +1 -1
  31. package/dist-cli/chunks/{chunk-MNMNX4O7.js → chunk-OL7HTJVC.js} +1 -1
  32. package/dist-cli/chunks/{chunk-RV3WN4FQ.js → chunk-PWGYVHEW.js} +2 -2
  33. package/dist-cli/chunks/{chunk-6MAW2X3J.js → chunk-PZL2H5SD.js} +1 -1
  34. package/dist-cli/chunks/{chunk-C6XQHJFR.js → chunk-QCPYYZGR.js} +2 -2
  35. package/dist-cli/chunks/{chunk-R76YX6KD.js → chunk-QEC2WZAU.js} +2 -2
  36. package/dist-cli/chunks/{chunk-7ODTKW6U.js → chunk-QGQRB6D2.js} +2 -2
  37. package/dist-cli/chunks/chunk-QGWWRXSG.js +1 -0
  38. package/dist-cli/chunks/{chunk-NRBPTQVR.js → chunk-SAV6NL5J.js} +3 -3
  39. package/dist-cli/chunks/{chunk-RGJAUL7P.js → chunk-SIGAKBUY.js} +2 -2
  40. package/dist-cli/chunks/{chunk-T35Q6ZGG.js → chunk-SK3UIUQL.js} +1 -1
  41. package/dist-cli/chunks/{chunk-3AALQI5D.js → chunk-SQBPTIVB.js} +2 -2
  42. package/dist-cli/chunks/{chunk-AXNDFONG.js → chunk-T7ERR3Y2.js} +1 -1
  43. package/dist-cli/chunks/{chunk-3Q7M3ATN.js → chunk-TM6NU7CC.js} +2 -2
  44. package/dist-cli/chunks/{chunk-5MF7XOPR.js → chunk-TNCYQFCC.js} +1 -1
  45. package/dist-cli/chunks/{chunk-5HCF2FQK.js → chunk-UODOGO3P.js} +2 -2
  46. package/dist-cli/chunks/{chunk-2R6ALEI7.js → chunk-UPDZS7GX.js} +3 -3
  47. package/dist-cli/chunks/{chunk-KDNOWAFU.js → chunk-UQGDJNV2.js} +2 -2
  48. package/dist-cli/chunks/{chunk-7FL2TKGB.js → chunk-V5TDYT4H.js} +2 -2
  49. package/dist-cli/chunks/{chunk-Q3UIMXJY.js → chunk-VC7MRZW7.js} +2 -2
  50. package/dist-cli/chunks/{chunk-RHHCV6M4.js → chunk-VZ7VB7HX.js} +2 -2
  51. package/dist-cli/chunks/{chunk-A27TUQQZ.js → chunk-ZO7E7WE5.js} +1 -1
  52. package/dist-cli/chunks/chunk-ZPDQ3S7P.js +1 -0
  53. package/dist-cli/chunks/{chunk-AUMBVWWD.js → chunk-ZU5S3Z7A.js} +2 -2
  54. package/dist-cli/chunks/cli-version-TJZ4JXUC.js +2 -0
  55. package/dist-cli/chunks/{compat-OH3WCY73.js → compat-E5CN4JWN.js} +3 -3
  56. package/dist-cli/chunks/{config-75MZT5JS.js → config-H75A6KBX.js} +2 -2
  57. package/dist-cli/chunks/{control-KSGYRJQK.js → control-CYDNB7NS.js} +2 -2
  58. package/dist-cli/chunks/{cpu-profile-2EHAVP5P.js → cpu-profile-QUFYSFW2.js} +2 -2
  59. package/dist-cli/chunks/{daemon-KRESTNSO.js → daemon-7GPY5B63.js} +2 -2
  60. package/dist-cli/chunks/{debug-XJ2XIBGI.js → debug-5WWA5QA3.js} +3 -3
  61. package/dist-cli/chunks/{detox-D3P2BFHN.js → detox-O55BWRH7.js} +2 -2
  62. package/dist-cli/chunks/{device-TMFWZCML.js → device-AQRY7VKI.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-67SOCM6I.js → diagnose-FDKSEQJ5.js} +2 -2
  64. package/dist-cli/chunks/drivers-QDHVKRYQ.js +2 -0
  65. package/dist-cli/chunks/{electron-FLPDCIDO.js → electron-ZQBTU3QE.js} +3 -3
  66. package/dist-cli/chunks/flow-H6MLEX2L.js +2 -0
  67. package/dist-cli/chunks/help-THQ2V5SE.js +2 -0
  68. package/dist-cli/chunks/{hints-ODFXO5MG.js → hints-7UYBREXC.js} +2 -2
  69. package/dist-cli/chunks/{home-paths-NIEX6VIW.js → home-paths-2FGC73RX.js} +2 -2
  70. package/dist-cli/chunks/{inspect-C5R2IXQP.js → inspect-DIBHPUVR.js} +3 -3
  71. package/dist-cli/chunks/install-R63X4FZO.js +2 -0
  72. package/dist-cli/chunks/{install-desktop-TGI4DCY3.js → install-desktop-JGMQYTE4.js} +3 -3
  73. package/dist-cli/chunks/{keys-2QJCGBZU.js → keys-76ZYEU3L.js} +2 -2
  74. package/dist-cli/chunks/{launch-A2NXENLP.js → launch-73NGQKXM.js} +3 -3
  75. package/dist-cli/chunks/{login-TQLNNRFL.js → login-6EFONAEU.js} +4 -4
  76. package/dist-cli/chunks/{logout-N6R66AFU.js → logout-C3W5XSSV.js} +2 -2
  77. package/dist-cli/chunks/{maestro-DMNUWMEX.js → maestro-HVAORFSN.js} +2 -2
  78. package/dist-cli/chunks/optional-demo-registry-AJQV4MPG.js +2 -0
  79. package/dist-cli/chunks/{preview-O45WIZT7.js → preview-EC3BTC6H.js} +2 -2
  80. package/dist-cli/chunks/{profile-HSL4JQ2B.js → profile-UAZ2OUJ4.js} +2 -2
  81. package/dist-cli/chunks/{react-STTXJK2P.js → react-XQCVY4U2.js} +2 -2
  82. package/dist-cli/chunks/{record-6X7TU345.js → record-IGGTUUYU.js} +2 -2
  83. package/dist-cli/chunks/runtime-ATNAKUCZ.js +2 -0
  84. package/dist-cli/chunks/{runtime-delivery-D76JBTRS.js → runtime-delivery-NXMBF4CK.js} +2 -2
  85. package/dist-cli/chunks/{screenshot-WLKERV6O.js → screenshot-JTZJZCCX.js} +2 -2
  86. package/dist-cli/chunks/{screenshot-mode-QGT6WGQ6.js → screenshot-mode-WZ6ZO5CD.js} +2 -2
  87. package/dist-cli/chunks/screenshots-P5VB65PJ.js +74 -0
  88. package/dist-cli/chunks/{server-NNJNTLTV.js → server-2BYQFJUM.js} +12 -12
  89. package/dist-cli/chunks/setup-repo-7BUMPQZM.js +2 -0
  90. package/dist-cli/chunks/{skills-FTYSXEVA.js → skills-A2I5K4UQ.js} +2 -2
  91. package/dist-cli/chunks/{slides-FTSJM4AC.js → slides-O25XV2IB.js} +2 -2
  92. package/dist-cli/chunks/{start-Q37DNJPY.js → start-HY4ZGAE2.js} +4 -4
  93. package/dist-cli/chunks/store-VXGE7SLD.js +2 -0
  94. package/dist-cli/chunks/telemetry-HGRYUILN.js +2 -0
  95. package/dist-cli/chunks/{test-7WGTTJFK.js → test-IVVOPKT5.js} +3 -3
  96. package/dist-cli/chunks/{three-mode-T6V3MWE7.js → three-mode-TYAB3FVD.js} +2 -2
  97. package/dist-cli/chunks/{timeline-T36NNMIW.js → timeline-R6M6EJQA.js} +2 -2
  98. package/dist-cli/chunks/{upgrade-D5LA6F7T.js → upgrade-CZQ6GK7Y.js} +2 -2
  99. package/dist-cli/chunks/upload-NR4J3LM2.js +2 -0
  100. package/dist-cli/chunks/{version-NEMFK44K.js → version-XVAS3GOH.js} +2 -2
  101. package/dist-cli/chunks/{web-EBBS27LE.js → web-35EQF7J6.js} +2 -2
  102. package/dist-cli/chunks/{what-happened-LWSM2L7E.js → what-happened-MYXKT7EW.js} +2 -2
  103. package/dist-cli/chunks/{whoami-AXLQJ7NW.js → whoami-J7EHW2UD.js} +2 -2
  104. package/dist-lib/agent-daemon-client.cjs +1 -1
  105. package/dist-lib/agent-events.cjs +1 -1
  106. package/dist-lib/agent-sessions.cjs +1 -1
  107. package/dist-lib/attached-projects.cjs +1 -1
  108. package/dist-lib/auth/shared-session.cjs +1 -1
  109. package/dist-lib/backend-origin.cjs +1 -1
  110. package/dist-lib/beta.cjs +1 -1
  111. package/dist-lib/beta.mjs +1 -1
  112. package/dist-lib/bridge-constants.cjs +1 -1
  113. package/dist-lib/cli-constants.cjs +1 -1
  114. package/dist-lib/config.cjs +1 -1
  115. package/dist-lib/detox/index.cjs +1 -1
  116. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  117. package/dist-lib/home-paths.cjs +1 -1
  118. package/dist-lib/host/bridge-host.cjs +47 -3
  119. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  121. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  122. package/dist-lib/host/websocket-proxy.cjs +1 -1
  123. package/dist-lib/index.cjs +1 -1
  124. package/dist-lib/metro.cjs +1 -1
  125. package/dist-lib/profiles.cjs +1 -1
  126. package/dist-lib/render-mode.cjs +1 -1
  127. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  128. package/dist-lib/sdk.cjs +1 -1
  129. package/dist-lib/sdk.mjs +1 -1
  130. package/dist-lib/skills.cjs +47 -3
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +1 -1
  133. package/src/host/bridge-host.ts +70 -4
  134. package/src/screenshots/orchestrate.ts +7 -0
  135. package/src/screenshots/org-deck.ts +91 -0
  136. package/dist-cli/chunks/auto-bootstrap-JVI75WWN.js +0 -2
  137. package/dist-cli/chunks/beta-IXSKJTBA.js +0 -2
  138. package/dist-cli/chunks/chunk-HONMR5LW.js +0 -1
  139. package/dist-cli/chunks/chunk-OW2ZBOB3.js +0 -2
  140. package/dist-cli/chunks/chunk-QNJYE5OY.js +0 -2
  141. package/dist-cli/chunks/chunk-SMMTE4QY.js +0 -1
  142. package/dist-cli/chunks/chunk-WJA4MM4L.js +0 -1
  143. package/dist-cli/chunks/cli-version-C56OR6JV.js +0 -2
  144. package/dist-cli/chunks/drivers-OYNUTXKG.js +0 -2
  145. package/dist-cli/chunks/flow-Q2Z4UAPG.js +0 -2
  146. package/dist-cli/chunks/help-EVEPPKOR.js +0 -2
  147. package/dist-cli/chunks/install-H4QD5NHJ.js +0 -2
  148. package/dist-cli/chunks/optional-demo-registry-Q2WTD5VQ.js +0 -2
  149. package/dist-cli/chunks/runtime-K3KCLSIW.js +0 -2
  150. package/dist-cli/chunks/screenshots-H4CT4M6U.js +0 -70
  151. package/dist-cli/chunks/setup-repo-I4NU3XER.js +0 -2
  152. package/dist-cli/chunks/store-YJZETFWA.js +0 -2
  153. package/dist-cli/chunks/telemetry-73CWUPJ6.js +0 -2
  154. package/dist-cli/chunks/upload-DR6AU55F.js +0 -2
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{C as T,D as A,E as y,P as _,Q as b,R as S}from"./chunk-AXNDFONG.js";import{c as N,e as F}from"./chunk-Q3UIMXJY.js";import{b as k}from"./chunk-T35Q6ZGG.js";import"./chunk-36RF3WRB.js";import"./chunk-ANTCWWXN.js";import"./chunk-A27TUQQZ.js";function O(n){let t=[];for(let e=0;e<n.length;e++)if(n[e]==="--since"&&e+1<n.length){t.push(e,e+1);let s=n[e+1].trim(),l=/^(\d+(?:\.\d+)?)(ms|s|m)?$/.exec(s);if(l){let r=Number(l[1]),u=l[2]??"ms",v=u==="s"?r*1e3:u==="m"?r*6e4:r;return{since:Date.now()-v,consumed:t}}let i=Number(s);if(Number.isFinite(i)&&i>1e12)return{since:i,consumed:t}}return{consumed:t}}function B(n){let t=[];for(let e=0;e<n.length;e++)if(n[e]==="--kinds"&&e+1<n.length)return t.push(e,e+1),{kinds:n[e+1].split(",").map(s=>s.trim()).filter(Boolean),consumed:t};return{consumed:t}}function D(n){let t=[];for(let e=0;e<n.length;e++)if(n[e]==="--limit"&&e+1<n.length){t.push(e,e+1);let s=Number(n[e+1]);if(Number.isFinite(s)&&s>0)return{limit:s,consumed:t}}return{consumed:t}}function x(n){let t=[],e={label:"initial state",events:[],startedAt:n[0]?.t??null};t.push(e);for(let s of n)if(e.events.push(s),s.kind==="screen"||s.kind==="route"){let l=s.data,i=l?.phase;if(!i||i==="enter"||i==="appear"||i==="active"){let r=l?.name||l?.activeName||l?.path||l?.pathname||s.kind;t.length===1&&e.events.length===1?e.label=`${s.kind}: ${r}`:(e={label:`${s.kind}: ${r}`,events:[],startedAt:s.t},t.push(e))}}return t}async function J(n,t){let e=N(n,{port:t.port,stripBooleanFlags:["--summary","--all","--json","--no-advance","--help","-h","--flow","--noisy"],stripValueFlags:["--since","--kinds","--limit"]});(n.includes("--help")||n.includes("-h"))&&(console.log(`
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{C as T,D as A,E as y,P as _,Q as b,R as S}from"./chunk-T7ERR3Y2.js";import{c as N,e as F}from"./chunk-VC7MRZW7.js";import{b as k}from"./chunk-SK3UIUQL.js";import"./chunk-4CLJIXM3.js";import"./chunk-MCQIOGWG.js";import"./chunk-ZO7E7WE5.js";function O(n){let t=[];for(let e=0;e<n.length;e++)if(n[e]==="--since"&&e+1<n.length){t.push(e,e+1);let s=n[e+1].trim(),l=/^(\d+(?:\.\d+)?)(ms|s|m)?$/.exec(s);if(l){let r=Number(l[1]),u=l[2]??"ms",v=u==="s"?r*1e3:u==="m"?r*6e4:r;return{since:Date.now()-v,consumed:t}}let i=Number(s);if(Number.isFinite(i)&&i>1e12)return{since:i,consumed:t}}return{consumed:t}}function B(n){let t=[];for(let e=0;e<n.length;e++)if(n[e]==="--kinds"&&e+1<n.length)return t.push(e,e+1),{kinds:n[e+1].split(",").map(s=>s.trim()).filter(Boolean),consumed:t};return{consumed:t}}function D(n){let t=[];for(let e=0;e<n.length;e++)if(n[e]==="--limit"&&e+1<n.length){t.push(e,e+1);let s=Number(n[e+1]);if(Number.isFinite(s)&&s>0)return{limit:s,consumed:t}}return{consumed:t}}function x(n){let t=[],e={label:"initial state",events:[],startedAt:n[0]?.t??null};t.push(e);for(let s of n)if(e.events.push(s),s.kind==="screen"||s.kind==="route"){let l=s.data,i=l?.phase;if(!i||i==="enter"||i==="appear"||i==="active"){let r=l?.name||l?.activeName||l?.path||l?.pathname||s.kind;t.length===1&&e.events.length===1?e.label=`${s.kind}: ${r}`:(e={label:`${s.kind}: ${r}`,events:[],startedAt:s.t},t.push(e))}}return t}async function J(n,t){let e=N(n,{port:t.port,stripBooleanFlags:["--summary","--all","--json","--no-advance","--help","-h","--flow","--noisy"],stripValueFlags:["--since","--kinds","--limit"]});(n.includes("--help")||n.includes("-h"))&&(console.log(`
3
3
  sootsim what-happened \u2014 show recent events from the semantic timeline
4
4
 
5
5
  usage:
@@ -1,2 +1,2 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{d as i}from"./chunk-3Q7M3ATN.js";import{d as n}from"./chunk-R76YX6KD.js";import"./chunk-ANTCWWXN.js";import"./chunk-A27TUQQZ.js";async function u(){let o=i();if(o||(console.log(" not signed in"),console.log(" set SOOTSIM_API_KEY=sk_sootsim_\u2026 or run `sootsim login`"),process.exit(1)),o.kind==="api-key"){let r=`${o.secret.slice(0,14)}\u2026`,t=o.source==="env"?"SOOTSIM_API_KEY env var":"saved key (~/.config/sootsim/credentials.json)";console.log(` api key ${r}`),console.log(` source: ${t}`);return}if(o.kind==="github"){console.log(` github token (${o.source})`),console.log(` repo: ${o.repoId}`);return}let e=await n(),s=e?.user;console.log(` ${s?.email||s?.name||s?.id||"signed in"}`),console.log(` origin: ${e?.origin??o.origin}`),e?.updatedAt&&console.log(` updated: ${e.updatedAt}`)}export{u as runWhoami};
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{d as i}from"./chunk-TM6NU7CC.js";import{d as n}from"./chunk-QEC2WZAU.js";import"./chunk-MCQIOGWG.js";import"./chunk-ZO7E7WE5.js";async function u(){let o=i();if(o||(console.log(" not signed in"),console.log(" set SOOTSIM_API_KEY=sk_sootsim_\u2026 or run `sootsim login`"),process.exit(1)),o.kind==="api-key"){let r=`${o.secret.slice(0,14)}\u2026`,t=o.source==="env"?"SOOTSIM_API_KEY env var":"saved key (~/.config/sootsim/credentials.json)";console.log(` api key ${r}`),console.log(` source: ${t}`);return}if(o.kind==="github"){console.log(` github token (${o.source})`),console.log(` repo: ${o.repoId}`);return}let e=await n(),s=e?.user;console.log(` ${s?.email||s?.name||s?.id||"signed in"}`),console.log(` origin: ${e?.origin??o.origin}`),e?.updatedAt&&console.log(` updated: ${e.updatedAt}`)}export{u as runWhoami};
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -2784,9 +2784,24 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
2784
2784
  // terminal 4001 code, so the client closes the abandoned window instead
2785
2785
  // of reconnecting the zombie straight back in.
2786
2786
  static SIM_IDLE_REAP_TTL_MS = 30 * 6e4;
2787
+ // automation sims (spawned by the playwright driver: `sootsim open --driver
2788
+ // playwright`, the factory/screenshot capture path — `meta.sootsimHostDriver
2789
+ // === 'playwright'`) are unattended one-shot browsers, never a human's tab. a
2790
+ // capture run opens one per slide/app/retry and the 30-min human/dev TTL lets
2791
+ // hundreds pile up inside the window — orphaned ms-playwright chromiums that
2792
+ // drove the box to swap-thrash (load hit 157 in one incident). give them a far
2793
+ // shorter idle TTL, and cap how many can sit concurrently: reap the most-idle
2794
+ // overflow, but never a sim active within ACTIVE_GRACE (an agent composing
2795
+ // between captures), nor primary / cli-attached / leased sims.
2796
+ static AUTOMATION_SIM_IDLE_REAP_TTL_MS = 10 * 6e4;
2797
+ static MAX_CONCURRENT_AUTOMATION_SIMS = 6;
2798
+ static AUTOMATION_SIM_ACTIVE_GRACE_MS = 6e4;
2787
2799
  preferredPort;
2788
2800
  portFallbackCount;
2789
2801
  simIdleReapTtlMs;
2802
+ automationSimIdleReapTtlMs;
2803
+ maxConcurrentAutomationSims;
2804
+ automationSimActiveGraceMs;
2790
2805
  shouldWriteLockfile;
2791
2806
  sootbeanOrigin = null;
2792
2807
  effectivePort = 0;
@@ -2823,6 +2838,9 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
2823
2838
  this.agentHost = new AgentHost({ getExcludePorts: opts.agentScanExcludes });
2824
2839
  this.sootbeanOrigin = opts.sootbeanOrigin?.replace(/\/$/, "") || null;
2825
2840
  this.simIdleReapTtlMs = opts.simIdleReapTtlMs ?? _SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS;
2841
+ this.automationSimIdleReapTtlMs = opts.automationSimIdleReapTtlMs ?? _SootSimBridgeHost.AUTOMATION_SIM_IDLE_REAP_TTL_MS;
2842
+ this.maxConcurrentAutomationSims = opts.maxConcurrentAutomationSims ?? _SootSimBridgeHost.MAX_CONCURRENT_AUTOMATION_SIMS;
2843
+ this.automationSimActiveGraceMs = opts.automationSimActiveGraceMs ?? _SootSimBridgeHost.AUTOMATION_SIM_ACTIVE_GRACE_MS;
2826
2844
  }
2827
2845
  /** expose the agent host so tests and embedders can inspect state or
2828
2846
  * inject behavior. not part of the public WS protocol. */
@@ -3819,15 +3837,41 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
3819
3837
  // using is the primary and/or holds a fresh user-active lease; one that is
3820
3838
  // none of those and idle past the TTL is abandoned regardless of a stale
3821
3839
  // focus flag.
3840
+ // automation sims are spawned by the playwright driver (`sootsim open
3841
+ // --driver playwright`, the factory/screenshot capture path), which tags them
3842
+ // with `meta.sootsimHostDriver === 'playwright'` via the host's addInitScript.
3843
+ // they are unattended one-shot browsers, never a human's tab, so they get a
3844
+ // shorter idle TTL and a concurrent cap.
3845
+ isAutomationSim(sim) {
3846
+ return sim.meta?.sootsimHostDriver === "playwright";
3847
+ }
3822
3848
  reapIdleSims(now = Date.now()) {
3823
3849
  const attachedSimIds = new Set(this.cliSimBySocket.values());
3850
+ const automationLive = [];
3824
3851
  for (const sim of this.sims.values()) {
3825
3852
  if (sim.id === this.primarySimId) continue;
3826
3853
  if (attachedSimIds.has(sim.id)) continue;
3827
3854
  if (this.getActiveLease(sim)) continue;
3828
3855
  const lastTouched = Math.max(sim.lastActiveAt, sim.connectedAt);
3829
- if (now - lastTouched < this.simIdleReapTtlMs) continue;
3830
- this.closeSimSocketFromHost(sim.ws);
3856
+ const idleMs = now - lastTouched;
3857
+ const automation = this.isAutomationSim(sim);
3858
+ const ttl = automation ? this.automationSimIdleReapTtlMs : this.simIdleReapTtlMs;
3859
+ if (idleMs >= ttl) {
3860
+ this.closeSimSocketFromHost(sim.ws);
3861
+ continue;
3862
+ }
3863
+ if (automation) automationLive.push({ sim, idleMs });
3864
+ }
3865
+ const cap = this.maxConcurrentAutomationSims;
3866
+ if (automationLive.length > cap) {
3867
+ automationLive.sort((a, b) => b.idleMs - a.idleMs);
3868
+ for (const { sim, idleMs } of automationLive.slice(
3869
+ 0,
3870
+ automationLive.length - cap
3871
+ )) {
3872
+ if (idleMs < this.automationSimActiveGraceMs) continue;
3873
+ this.closeSimSocketFromHost(sim.ws);
3874
+ }
3831
3875
  }
3832
3876
  }
3833
3877
  // ping every connected ws; if the previous round's ping went unanswered,
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // cli/commands/inspect/core.ts
4
4
  function resolveMaxMsFlag(args, fallbackMs) {
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -12824,9 +12824,24 @@ var init_bridge_host = __esm({
12824
12824
  // terminal 4001 code, so the client closes the abandoned window instead
12825
12825
  // of reconnecting the zombie straight back in.
12826
12826
  static SIM_IDLE_REAP_TTL_MS = 30 * 6e4;
12827
+ // automation sims (spawned by the playwright driver: `sootsim open --driver
12828
+ // playwright`, the factory/screenshot capture path — `meta.sootsimHostDriver
12829
+ // === 'playwright'`) are unattended one-shot browsers, never a human's tab. a
12830
+ // capture run opens one per slide/app/retry and the 30-min human/dev TTL lets
12831
+ // hundreds pile up inside the window — orphaned ms-playwright chromiums that
12832
+ // drove the box to swap-thrash (load hit 157 in one incident). give them a far
12833
+ // shorter idle TTL, and cap how many can sit concurrently: reap the most-idle
12834
+ // overflow, but never a sim active within ACTIVE_GRACE (an agent composing
12835
+ // between captures), nor primary / cli-attached / leased sims.
12836
+ static AUTOMATION_SIM_IDLE_REAP_TTL_MS = 10 * 6e4;
12837
+ static MAX_CONCURRENT_AUTOMATION_SIMS = 6;
12838
+ static AUTOMATION_SIM_ACTIVE_GRACE_MS = 6e4;
12827
12839
  preferredPort;
12828
12840
  portFallbackCount;
12829
12841
  simIdleReapTtlMs;
12842
+ automationSimIdleReapTtlMs;
12843
+ maxConcurrentAutomationSims;
12844
+ automationSimActiveGraceMs;
12830
12845
  shouldWriteLockfile;
12831
12846
  sootbeanOrigin = null;
12832
12847
  effectivePort = 0;
@@ -12863,6 +12878,9 @@ var init_bridge_host = __esm({
12863
12878
  this.agentHost = new AgentHost({ getExcludePorts: opts.agentScanExcludes });
12864
12879
  this.sootbeanOrigin = opts.sootbeanOrigin?.replace(/\/$/, "") || null;
12865
12880
  this.simIdleReapTtlMs = opts.simIdleReapTtlMs ?? _SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS;
12881
+ this.automationSimIdleReapTtlMs = opts.automationSimIdleReapTtlMs ?? _SootSimBridgeHost.AUTOMATION_SIM_IDLE_REAP_TTL_MS;
12882
+ this.maxConcurrentAutomationSims = opts.maxConcurrentAutomationSims ?? _SootSimBridgeHost.MAX_CONCURRENT_AUTOMATION_SIMS;
12883
+ this.automationSimActiveGraceMs = opts.automationSimActiveGraceMs ?? _SootSimBridgeHost.AUTOMATION_SIM_ACTIVE_GRACE_MS;
12866
12884
  }
12867
12885
  /** expose the agent host so tests and embedders can inspect state or
12868
12886
  * inject behavior. not part of the public WS protocol. */
@@ -13859,15 +13877,41 @@ var init_bridge_host = __esm({
13859
13877
  // using is the primary and/or holds a fresh user-active lease; one that is
13860
13878
  // none of those and idle past the TTL is abandoned regardless of a stale
13861
13879
  // focus flag.
13880
+ // automation sims are spawned by the playwright driver (`sootsim open
13881
+ // --driver playwright`, the factory/screenshot capture path), which tags them
13882
+ // with `meta.sootsimHostDriver === 'playwright'` via the host's addInitScript.
13883
+ // they are unattended one-shot browsers, never a human's tab, so they get a
13884
+ // shorter idle TTL and a concurrent cap.
13885
+ isAutomationSim(sim) {
13886
+ return sim.meta?.sootsimHostDriver === "playwright";
13887
+ }
13862
13888
  reapIdleSims(now = Date.now()) {
13863
13889
  const attachedSimIds = new Set(this.cliSimBySocket.values());
13890
+ const automationLive = [];
13864
13891
  for (const sim of this.sims.values()) {
13865
13892
  if (sim.id === this.primarySimId) continue;
13866
13893
  if (attachedSimIds.has(sim.id)) continue;
13867
13894
  if (this.getActiveLease(sim)) continue;
13868
13895
  const lastTouched = Math.max(sim.lastActiveAt, sim.connectedAt);
13869
- if (now - lastTouched < this.simIdleReapTtlMs) continue;
13870
- this.closeSimSocketFromHost(sim.ws);
13896
+ const idleMs = now - lastTouched;
13897
+ const automation = this.isAutomationSim(sim);
13898
+ const ttl = automation ? this.automationSimIdleReapTtlMs : this.simIdleReapTtlMs;
13899
+ if (idleMs >= ttl) {
13900
+ this.closeSimSocketFromHost(sim.ws);
13901
+ continue;
13902
+ }
13903
+ if (automation) automationLive.push({ sim, idleMs });
13904
+ }
13905
+ const cap = this.maxConcurrentAutomationSims;
13906
+ if (automationLive.length > cap) {
13907
+ automationLive.sort((a, b) => b.idleMs - a.idleMs);
13908
+ for (const { sim, idleMs } of automationLive.slice(
13909
+ 0,
13910
+ automationLive.length - cap
13911
+ )) {
13912
+ if (idleMs < this.automationSimActiveGraceMs) continue;
13913
+ this.closeSimSocketFromHost(sim.ws);
13914
+ }
13871
13915
  }
13872
13916
  }
13873
13917
  // ping every connected ws; if the previous round's ping went unanswered,
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.147 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sootsim",
3
- "version": "0.1.145",
3
+ "version": "0.1.147",
4
4
  "description": "sootsim CLI + vite/metro plugins + skills registry. bridge client for driving the proprietary sootsim-engine over WebSocket.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "MIT",
@@ -228,6 +228,18 @@ export interface BridgeHostOptions {
228
228
  * can drive the real timer path deterministically instead of mocking
229
229
  * time. production callers should never set this. */
230
230
  simIdleReapTtlMs?: number
231
+ /** override the idle GC TTL (ms) for automation sims (playwright-driver,
232
+ * capture/factory). defaults to AUTOMATION_SIM_IDLE_REAP_TTL_MS (10 min).
233
+ * test-only, like simIdleReapTtlMs. */
234
+ automationSimIdleReapTtlMs?: number
235
+ /** cap on concurrent non-primary automation sims before the reaper drops
236
+ * the most-idle overflow. defaults to MAX_CONCURRENT_AUTOMATION_SIMS (6).
237
+ * test-only. */
238
+ maxConcurrentAutomationSims?: number
239
+ /** grace (ms) below which an automation sim is never reaped by the cap,
240
+ * even when over it. defaults to AUTOMATION_SIM_ACTIVE_GRACE_MS (60 s).
241
+ * test-only. */
242
+ automationSimActiveGraceMs?: number
231
243
  }
232
244
 
233
245
  const DAEMON_HEARTBEAT_INTERVAL_MS = 5_000
@@ -354,10 +366,25 @@ export class SootSimBridgeHost {
354
366
  // terminal 4001 code, so the client closes the abandoned window instead
355
367
  // of reconnecting the zombie straight back in.
356
368
  private static SIM_IDLE_REAP_TTL_MS = 30 * 60_000
369
+ // automation sims (spawned by the playwright driver: `sootsim open --driver
370
+ // playwright`, the factory/screenshot capture path — `meta.sootsimHostDriver
371
+ // === 'playwright'`) are unattended one-shot browsers, never a human's tab. a
372
+ // capture run opens one per slide/app/retry and the 30-min human/dev TTL lets
373
+ // hundreds pile up inside the window — orphaned ms-playwright chromiums that
374
+ // drove the box to swap-thrash (load hit 157 in one incident). give them a far
375
+ // shorter idle TTL, and cap how many can sit concurrently: reap the most-idle
376
+ // overflow, but never a sim active within ACTIVE_GRACE (an agent composing
377
+ // between captures), nor primary / cli-attached / leased sims.
378
+ private static AUTOMATION_SIM_IDLE_REAP_TTL_MS = 10 * 60_000
379
+ private static MAX_CONCURRENT_AUTOMATION_SIMS = 6
380
+ private static AUTOMATION_SIM_ACTIVE_GRACE_MS = 60_000
357
381
 
358
382
  private preferredPort: number
359
383
  private portFallbackCount: number
360
384
  private simIdleReapTtlMs: number
385
+ private automationSimIdleReapTtlMs: number
386
+ private maxConcurrentAutomationSims: number
387
+ private automationSimActiveGraceMs: number
361
388
  private shouldWriteLockfile: boolean
362
389
  private sootbeanOrigin: string | null = null
363
390
  private effectivePort = 0
@@ -403,6 +430,12 @@ export class SootSimBridgeHost {
403
430
  this.sootbeanOrigin = opts.sootbeanOrigin?.replace(/\/$/, '') || null
404
431
  this.simIdleReapTtlMs =
405
432
  opts.simIdleReapTtlMs ?? SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS
433
+ this.automationSimIdleReapTtlMs =
434
+ opts.automationSimIdleReapTtlMs ?? SootSimBridgeHost.AUTOMATION_SIM_IDLE_REAP_TTL_MS
435
+ this.maxConcurrentAutomationSims =
436
+ opts.maxConcurrentAutomationSims ?? SootSimBridgeHost.MAX_CONCURRENT_AUTOMATION_SIMS
437
+ this.automationSimActiveGraceMs =
438
+ opts.automationSimActiveGraceMs ?? SootSimBridgeHost.AUTOMATION_SIM_ACTIVE_GRACE_MS
406
439
  }
407
440
 
408
441
  /** expose the agent host so tests and embedders can inspect state or
@@ -1624,8 +1657,20 @@ export class SootSimBridgeHost {
1624
1657
  // using is the primary and/or holds a fresh user-active lease; one that is
1625
1658
  // none of those and idle past the TTL is abandoned regardless of a stale
1626
1659
  // focus flag.
1660
+ // automation sims are spawned by the playwright driver (`sootsim open
1661
+ // --driver playwright`, the factory/screenshot capture path), which tags them
1662
+ // with `meta.sootsimHostDriver === 'playwright'` via the host's addInitScript.
1663
+ // they are unattended one-shot browsers, never a human's tab, so they get a
1664
+ // shorter idle TTL and a concurrent cap.
1665
+ private isAutomationSim(sim: BridgeSimConnection): boolean {
1666
+ return sim.meta?.sootsimHostDriver === 'playwright'
1667
+ }
1668
+
1627
1669
  private reapIdleSims(now = Date.now()) {
1628
1670
  const attachedSimIds = new Set(this.cliSimBySocket.values())
1671
+ // automation sims that pass every "someone cares" gate but are still under
1672
+ // their TTL — candidates for the concurrent-cap pass below.
1673
+ const automationLive: { sim: BridgeSimConnection; idleMs: number }[] = []
1629
1674
  for (const sim of this.sims.values()) {
1630
1675
  if (sim.id === this.primarySimId) continue
1631
1676
  if (attachedSimIds.has(sim.id)) continue
@@ -1633,10 +1678,31 @@ export class SootSimBridgeHost {
1633
1678
  // never-CLI-driven sims have lastActiveAt 0 — fall back to connectedAt
1634
1679
  // so a freshly opened, not-yet-primary tab isn't reaped on the spot.
1635
1680
  const lastTouched = Math.max(sim.lastActiveAt, sim.connectedAt)
1636
- if (now - lastTouched < this.simIdleReapTtlMs) continue
1637
- // terminal close → client closes the window, does not reconnect; the
1638
- // 'close' handler runs rememberDisconnectedSim this.sims.delete.
1639
- this.closeSimSocketFromHost(sim.ws)
1681
+ const idleMs = now - lastTouched
1682
+ const automation = this.isAutomationSim(sim)
1683
+ const ttl = automation ? this.automationSimIdleReapTtlMs : this.simIdleReapTtlMs
1684
+ if (idleMs >= ttl) {
1685
+ // terminal close → client closes the window, does not reconnect; the
1686
+ // 'close' handler runs rememberDisconnectedSim → this.sims.delete.
1687
+ this.closeSimSocketFromHost(sim.ws)
1688
+ continue
1689
+ }
1690
+ if (automation) automationLive.push({ sim, idleMs })
1691
+ }
1692
+ // concurrent cap: a burst of capture opens can pile up hundreds inside the
1693
+ // TTL window. keep at most maxConcurrentAutomationSims of the freshest;
1694
+ // reap the most-idle overflow, but never one active within the grace (an
1695
+ // agent mid-capture between screenshots).
1696
+ const cap = this.maxConcurrentAutomationSims
1697
+ if (automationLive.length > cap) {
1698
+ automationLive.sort((a, b) => b.idleMs - a.idleMs) // most-idle first
1699
+ for (const { sim, idleMs } of automationLive.slice(
1700
+ 0,
1701
+ automationLive.length - cap,
1702
+ )) {
1703
+ if (idleMs < this.automationSimActiveGraceMs) continue
1704
+ this.closeSimSocketFromHost(sim.ws)
1705
+ }
1640
1706
  }
1641
1707
  }
1642
1708
 
@@ -9,6 +9,7 @@ import {
9
9
  type ComposeSlideSource,
10
10
  } from './compose'
11
11
  import { createFramedScreenshotComposer } from './frame-compose'
12
+ import { applyOrgDeckToPlan, type OrgDeckFrame } from './org-deck'
12
13
  import { loadScreenshotsPlan, type NormalizedScreenshotsPlan } from './schema'
13
14
 
14
15
  export interface RunScreenshotsOverrides {
@@ -17,6 +18,9 @@ export interface RunScreenshotsOverrides {
17
18
  simId?: string | null
18
19
  captureOnly?: boolean
19
20
  composeOnly?: boolean
21
+ // copy/colors pulled from the team's org deck to overlay onto the plan's
22
+ // compose slides (sootsim screenshots --org … --repo …).
23
+ orgDeckFrames?: OrgDeckFrame[] | null
20
24
  }
21
25
 
22
26
  export interface CaptureResult {
@@ -186,6 +190,9 @@ export async function runScreenshotsPlan(
186
190
  overrides: RunScreenshotsOverrides = {},
187
191
  ): Promise<ScreenshotsRunResult> {
188
192
  const plan = loadScreenshotsPlan(planPath)
193
+ if (overrides.orgDeckFrames && overrides.orgDeckFrames.length > 0) {
194
+ applyOrgDeckToPlan(plan, overrides.orgDeckFrames)
195
+ }
189
196
  const effectiveDeviceModel = overrides.deviceModel ?? plan.deviceModel
190
197
  if (!overrides.composeOnly) {
191
198
  await runCaptureStage(plan, overrides)
@@ -0,0 +1,91 @@
1
+ // fetch the team's screenshot deck (the copy / colors founders set up in the
2
+ // org dashboard) and overlay it onto a screenshots plan, so the programmatic
3
+ // `sootsim screenshots --plan … --org … --repo …` flow renders the org's
4
+ // words + brand gradient instead of the local yaml's. the plan still owns
5
+ // which screen each slide captures and the device frame; the deck owns copy +
6
+ // colors. 3d scenes don't translate to the cli's 2d composer, so only copy and
7
+ // colors carry over (the 3d hero look stays the in-browser screenshot mode).
8
+
9
+ import { resolveBgColorValue } from 'sootsim-engine/screenshots/tokens'
10
+ import type { BackgroundSpec } from './registry'
11
+ import type { NormalizedScreenshotsPlan } from './schema'
12
+
13
+ export interface OrgDeckFetchOptions {
14
+ org: string
15
+ repo: string
16
+ origin: string
17
+ authHeader: string
18
+ }
19
+
20
+ export interface OrgDeckFrame {
21
+ title?: string
22
+ subtitle?: string
23
+ bgTop?: string
24
+ bgBottom?: string
25
+ titleColor?: string
26
+ }
27
+
28
+ function readString(value: unknown): string | undefined {
29
+ return typeof value === 'string' && value.length > 0 ? value : undefined
30
+ }
31
+
32
+ // GET the team deck and return its first deck's frames, or null when the team
33
+ // has no deck yet. throws on auth / network failure so the caller can surface a
34
+ // clear message rather than silently falling back to the local copy.
35
+ export async function fetchOrgDeckFrames(
36
+ opts: OrgDeckFetchOptions,
37
+ ): Promise<OrgDeckFrame[] | null> {
38
+ const base = opts.origin.replace(/\/$/, '')
39
+ const url = `${base}/api/sootsim/screenshot-decks?org=${encodeURIComponent(
40
+ opts.org,
41
+ )}&repo=${encodeURIComponent(opts.repo)}`
42
+ const res = await fetch(url, { headers: { Authorization: opts.authHeader } })
43
+ if (!res.ok) {
44
+ const detail = await res.text().catch(() => '')
45
+ throw new Error(
46
+ `org deck fetch failed (${res.status} ${res.statusText}) for ${opts.org}/${opts.repo}${
47
+ detail ? ` — ${detail.slice(0, 200)}` : ''
48
+ }`,
49
+ )
50
+ }
51
+ const body = (await res.json()) as { decks?: Array<{ config?: unknown }> }
52
+ const deck = body.decks?.[0]
53
+ if (!deck || !deck.config || typeof deck.config !== 'object') return null
54
+ const frames = (deck.config as { frames?: unknown }).frames
55
+ if (!Array.isArray(frames)) return null
56
+ return frames.map((entry) => {
57
+ const frame = (entry ?? {}) as Record<string, unknown>
58
+ return {
59
+ title: readString(frame.title),
60
+ subtitle: readString(frame.subtitle),
61
+ bgTop: readString(frame.bgTop),
62
+ bgBottom: readString(frame.bgBottom),
63
+ titleColor: readString(frame.titleColor),
64
+ }
65
+ })
66
+ }
67
+
68
+ // overlay deck frames onto the plan's compose slides, matched by order. a deck
69
+ // with fewer frames than the plan leaves the trailing slides on their yaml copy.
70
+ export function applyOrgDeckToPlan(
71
+ plan: NormalizedScreenshotsPlan,
72
+ frames: OrgDeckFrame[],
73
+ ): void {
74
+ plan.compose.slides.forEach((slide, index) => {
75
+ const frame = frames[index]
76
+ if (!frame) return
77
+ if (frame.title != null) slide.headline = frame.title
78
+ if (frame.subtitle != null) slide.subheadline = frame.subtitle
79
+ if (frame.bgTop && frame.bgBottom) {
80
+ const background: BackgroundSpec = {
81
+ type: 'gradient',
82
+ direction: 180,
83
+ stops: [
84
+ { offset: 0, color: resolveBgColorValue(frame.bgTop) },
85
+ { offset: 1, color: resolveBgColorValue(frame.bgBottom) },
86
+ ],
87
+ }
88
+ slide.background = background
89
+ }
90
+ })
91
+ }
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.145 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d}from"./chunk-NX6V5VAV.js";import"./chunk-Q3UIMXJY.js";import"./chunk-T35Q6ZGG.js";import"./chunk-36RF3WRB.js";import"./chunk-ANTCWWXN.js";import"./chunk-A27TUQQZ.js";export{c as ensureDaemonRunning,a as ensureRuntimeInstalled,d as ensureSootsimReady,b as resolveBootstrapPort};