bazilion 0.12.2 → 0.14.0

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 (217) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +795 -240
  3. package/dist/cli.js.map +1 -1
  4. package/dist/daemon.js +4555 -1280
  5. package/dist/daemon.js.map +1 -1
  6. package/dist/migrations/0001_init.sql +21 -0
  7. package/dist/web/client/assets/AgentTabs-BcmmwYJc.js +1 -0
  8. package/dist/web/client/assets/ConfigPage-BCLp-FUC.js +1 -0
  9. package/dist/web/client/assets/ConfirmDialog-CyL9A20V.js +1 -0
  10. package/dist/web/client/assets/FieldRow-D6REbYUa.js +1 -0
  11. package/dist/web/client/assets/Page-NV96O47A.js +1 -0
  12. package/dist/web/client/assets/ProductionTeamPolicyEditor-DBKnOezm.js +7 -0
  13. package/dist/web/client/assets/{ProfileCommunicationEditor-C43HvUe-.js → ProfileCommunicationEditor-4RK2laqO.js} +1 -1
  14. package/dist/web/client/assets/SectionTabs-4kCQXuFY.js +1 -0
  15. package/dist/web/client/assets/TeamTabs-CdtNEqt4.js +1 -0
  16. package/dist/web/client/assets/{TemplatesTabs-6Jd_xQj7.js → TemplatesTabs-CZyss2Y0.js} +1 -1
  17. package/dist/web/client/assets/UnsavedChangesGuard-CEoK-ZcN.js +1 -0
  18. package/dist/web/client/assets/_id-4ZS-w_Aj.js +1 -0
  19. package/dist/web/client/assets/{_id-AOAT0H_5.js → _id-DIqtdQij.js} +1 -1
  20. package/dist/web/client/assets/_id-DbcC2E7F.js +1 -0
  21. package/dist/web/client/assets/_id-f_r5M0p3.js +1 -0
  22. package/dist/web/client/assets/activity-CB9Ufth-.js +1 -0
  23. package/dist/web/client/assets/agents-D-JGXVg9.js +1 -0
  24. package/dist/web/client/assets/agents-DS2XafoA.js +1 -0
  25. package/dist/web/client/assets/approvals-Cke_mrJv.js +1 -0
  26. package/dist/web/client/assets/attention-PYwRM2wC.js +1 -0
  27. package/dist/web/client/assets/chevron-right-BG4OeFRQ.js +1 -0
  28. package/dist/web/client/assets/config-DF9Gqmfa.js +3 -0
  29. package/dist/web/client/assets/dm-sans-latin-400-normal-BwCSEQnW.woff +0 -0
  30. package/dist/web/client/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2 +0 -0
  31. package/dist/web/client/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2 +0 -0
  32. package/dist/web/client/assets/dm-sans-latin-500-normal-Dr3UlScf.woff +0 -0
  33. package/dist/web/client/assets/dm-sans-latin-600-normal-Aqo67rzb.woff2 +0 -0
  34. package/dist/web/client/assets/dm-sans-latin-600-normal-BmdmIIQ2.woff +0 -0
  35. package/dist/web/client/assets/dm-sans-latin-700-normal-CUSSCpQX.woff +0 -0
  36. package/dist/web/client/assets/dm-sans-latin-700-normal-DvUfVpUG.woff2 +0 -0
  37. package/dist/web/client/assets/dm-serif-display-latin-400-normal-C5_t9oOD.woff2 +0 -0
  38. package/dist/web/client/assets/dm-serif-display-latin-400-normal-_kVFYEpx.woff +0 -0
  39. package/dist/web/client/assets/{inbox-Bn1GbUsJ.js → inbox-DC_HdOfI.js} +1 -1
  40. package/dist/web/client/assets/index-Cofc7zhl.css +1 -0
  41. package/dist/web/client/assets/index-Cp6UHWr2.js +52 -0
  42. package/dist/web/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  43. package/dist/web/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  44. package/dist/web/client/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  45. package/dist/web/client/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  46. package/dist/web/client/assets/learning-BfrQ4YZY.js +1 -0
  47. package/dist/web/client/assets/login-D4n3AcBR.js +1 -0
  48. package/dist/web/client/assets/mcp-CnjQc_JJ.js +1 -0
  49. package/dist/web/client/assets/members-wxsVTJdW.js +1 -0
  50. package/dist/web/client/assets/memory-Du60O3pE.js +1 -0
  51. package/dist/web/client/assets/{policy-DaijITy7.js → policy-BOfnkJsL.js} +1 -1
  52. package/dist/web/client/assets/{preload-helper-mNuBjNXU.js → preload-helper-bztXdkDj.js} +1 -1
  53. package/dist/web/client/assets/routes-B9CuhzUW.js +1 -0
  54. package/dist/web/client/assets/services-DyvqJqM2.js +1 -0
  55. package/dist/web/client/assets/skills-DQo7ioW7.js +1 -0
  56. package/dist/web/client/assets/styles-B5vcgXln.css +2 -0
  57. package/dist/web/client/assets/{teams-D92R9Pb7.js → teams-DPggy97H.js} +1 -1
  58. package/dist/web/client/assets/teams-DdawcfNR.js +1 -0
  59. package/dist/web/client/assets/telegram-DcTeDpP5.js +1 -0
  60. package/dist/web/client/assets/tokens-CIEKwZXc.js +1 -0
  61. package/dist/web/client/assets/triggers-Bc2RrUPm.js +1 -0
  62. package/dist/web/client/assets/utils-B6KiDbIe.js +1 -0
  63. package/dist/web/client/assets/welcome-DlhNXUIa.js +1 -0
  64. package/dist/web/client/assets/wire-constants-BzdHSc6A.js +74 -0
  65. package/dist/web/client/assets/x-DAXGIe_Q.js +1 -0
  66. package/dist/web/server/assets/{AgentTabs-aXdsiiMp.js → AgentTabs-Dq8hDM2Q.js} +9 -2
  67. package/dist/web/server/assets/{ChatPane-Ba5wTsVk.js → ChatPane-CgSe7tsy.js} +81 -43
  68. package/dist/web/server/assets/{ConfigPage-BKlh5vOU.js → ConfigPage-CiQ-gxmJ.js} +3 -2
  69. package/dist/web/server/assets/ConfirmDialog-C-MPYYSU.js +99 -0
  70. package/dist/web/server/assets/FieldRow-DCOF07kl.js +160 -0
  71. package/dist/web/server/assets/{Page-LENulSXY.js → Page-Bz-ydB8j.js} +4 -10
  72. package/dist/web/server/assets/{ProductionTeamPolicyEditor-Y4mzFFvR.js → ProductionTeamPolicyEditor-DQSdP7Yx.js} +140 -123
  73. package/dist/web/server/assets/{SectionTabs-BJTPr5nU.js → SectionTabs-B2Yw81XN.js} +2 -2
  74. package/dist/web/server/assets/{Sidebar-DsRVCszF.js → Sidebar-DKyk7oIo.js} +563 -283
  75. package/dist/web/server/assets/{TeamTabs-zZzbP1t_.js → TeamTabs-Co3n2exj.js} +2 -6
  76. package/dist/web/server/assets/{TemplatesTabs-CcRxZhJe.js → TemplatesTabs-B0fA361R.js} +1 -1
  77. package/dist/web/server/assets/UnsavedChangesGuard-CiStXyBI.js +40 -0
  78. package/dist/web/server/assets/{_id-Dj1yZ3zJ.js → _id-BAA9KJBU.js} +4 -4
  79. package/dist/web/server/assets/{_id-LrvXU4Nr.js → _id-BM5m2FLT.js} +4 -4
  80. package/dist/web/server/assets/{_id-CwyGjJeP.js → _id-BYKpT2Tu.js} +4 -5
  81. package/dist/web/server/assets/{_id-BHYLL8M-.js → _id-BeKhPKw3.js} +350 -185
  82. package/dist/web/server/assets/{_id-C3HY4lWg.js → _id-BqWTB8Wl.js} +188 -86
  83. package/dist/web/server/assets/{_id-DinTp_7X.js → _id-BwvCtzlD.js} +3 -3
  84. package/dist/web/server/assets/{_id-D27T85mU.js → _id-DYU6uIBx.js} +4 -5
  85. package/dist/web/server/assets/{_id-IzadFZV9.js → _id-DvlDkk04.js} +4 -4
  86. package/dist/web/server/assets/{_id-BhOB8Umi.js → _id-DxCnEFpm.js} +4 -4
  87. package/dist/web/server/assets/{_id-tB47Vsfp.js → _id-HMy7jM3P.js} +3 -3
  88. package/dist/web/server/assets/{_id-D3Ap7zZA.js → _id-ndxFHx0t.js} +75 -71
  89. package/dist/web/server/assets/{_id-FcHdSocl.js → _id-sNu5aXZt.js} +5 -4
  90. package/dist/web/server/assets/{_tanstack-start-manifest_v-BGDBdnUs.js → _tanstack-start-manifest_v-C-OiOZeM.js} +118 -94
  91. package/dist/web/server/assets/{activity-DohitgwW.js → activity-9VP6QDTH.js} +3 -3
  92. package/dist/web/server/assets/{activity-BCI4mfNS.js → activity-BWv1goK6.js} +3 -3
  93. package/dist/web/server/assets/{activity-D9SMqBhy.js → activity-LjpblbDL.js} +3 -3
  94. package/dist/web/server/assets/{agents-C9xlWK0Z.js → agents-BDFGrU3o.js} +4 -4
  95. package/dist/web/server/assets/{agents-BY74v7fM.js → agents-BJAv919x.js} +3 -3
  96. package/dist/web/server/assets/{agents-BvttypqB.js → agents-CJk4pxKu.js} +3 -3
  97. package/dist/web/server/assets/{agents-CgZMHj80.js → agents-CYw1xHoY.js} +138 -16
  98. package/dist/web/server/assets/{agents-DZuFEj0D.js → agents-DfNKc_Q7.js} +52 -8
  99. package/dist/web/server/assets/{agents-BPKOshR5.js → agents-DjyQk94u.js} +4 -4
  100. package/dist/web/server/assets/{approvals-CWcFHlor.js → approvals-7JOoBchn.js} +3 -3
  101. package/dist/web/server/assets/{approvals-BGU-XGIO.js → approvals-BYi84gwl.js} +3 -3
  102. package/dist/web/server/assets/{approvals-DCaVw1Mz.js → approvals-Bz123liE.js} +59 -21
  103. package/dist/web/server/assets/{attention-BxkTf337.js → attention-B35JvhOL.js} +3 -3
  104. package/dist/web/server/assets/{attention-w21GQIGH.js → attention-C1pPWSXw.js} +3 -3
  105. package/dist/web/server/assets/{attention-CM-Ks1N9.js → attention-UhqPK_Ro.js} +3 -3
  106. package/dist/web/server/assets/{auth-byfSx6Ja.js → auth-7BHTH-KS.js} +5 -5
  107. package/dist/web/server/assets/{config-BDCJdtj2.js → config-BMiajTZP.js} +10 -5
  108. package/dist/web/server/assets/{config-1w2GiJUn.js → config-BlMkHStx.js} +3 -3
  109. package/dist/web/server/assets/config-ExacOzHX.js +958 -0
  110. package/dist/web/server/assets/{createServerRpc-pU-IQV2Y.js → createServerRpc-B7Yr1K8i.js} +1 -1
  111. package/dist/web/server/assets/{createSsrRpc-DFsIAfFd.js → createSsrRpc-6LHNEsSK.js} +1 -1
  112. package/dist/web/server/assets/{daemon-client-2NRMzlTr.js → daemon-client-CI5NHwNC.js} +16 -4
  113. package/dist/web/server/assets/{inbox-CzlKT9Wg.js → inbox-Dv55rGYq.js} +4 -4
  114. package/dist/web/server/assets/{inbox-BLUzA5sQ.js → inbox-RweGCV3D.js} +3 -3
  115. package/dist/web/server/assets/{inbox-ZvDNAjaM.js → inbox-cu3jLzNP.js} +4 -4
  116. package/dist/web/server/assets/{learning-B7PBkvER.js → learning-BaKM6qaw.js} +4 -4
  117. package/dist/web/server/assets/{learning-8zav4pIT.js → learning-C2upa1CE.js} +4 -4
  118. package/dist/web/server/assets/{learning-Cq7K2heb.js → learning-CbtNlqYo.js} +76 -23
  119. package/dist/web/server/assets/{login-CmWvxfTK.js → login-BNj3U6ok.js} +22 -12
  120. package/dist/web/server/assets/{login-Bx5LEyGP.js → login-Be8eP4Jp.js} +2 -2
  121. package/dist/web/server/assets/{mcp-DIWTogHM.js → mcp-B0goWO8Q.js} +3 -3
  122. package/dist/web/server/assets/{mcp-2pwT9HU8.js → mcp-BvnvlVFd.js} +3 -3
  123. package/dist/web/server/assets/mcp-CiBs8unU.js +332 -0
  124. package/dist/web/server/assets/members-BP3UTqu1.js +12 -0
  125. package/dist/web/server/assets/members-DHYTVgwb.js +100 -0
  126. package/dist/web/server/assets/members-DiYtr-zm.js +12 -0
  127. package/dist/web/server/assets/{memory-Dngc13Nw.js → memory-BqV-Afy1.js} +4 -4
  128. package/dist/web/server/assets/{memory-a8Kz57Kh.js → memory-CkXUgf5s.js} +150 -48
  129. package/dist/web/server/assets/{memory-pZ4pZT7p.js → memory-DTFEmfF3.js} +4 -4
  130. package/dist/web/server/assets/{policy-Bb-vEgqg.js → policy-18fdTFO4.js} +3 -3
  131. package/dist/web/server/assets/{policy-C30AeFIb.js → policy-BddK4xom.js} +3 -3
  132. package/dist/web/server/assets/{policy-BcGELcc9.js → policy-Bvvav8fh.js} +4 -4
  133. package/dist/web/server/assets/public-origin-xV0lm0Ty.js +25 -0
  134. package/dist/web/server/assets/router-pjH2t8Rl.js +813 -0
  135. package/dist/web/server/assets/{routes-B6cMLIPk.js → routes-BgHisAxH.js} +22 -5
  136. package/dist/web/server/assets/{routes-BrA-U_Hm.js → routes-COtkyZG1.js} +5 -5
  137. package/dist/web/server/assets/{routes-C19CMLZt.js → routes-DAU2g7Uz.js} +4 -4
  138. package/dist/web/server/assets/services-ClyGBU7W.js +83 -0
  139. package/dist/web/server/assets/{services-C4szdhPx.js → services-DgoU1udp.js} +3 -3
  140. package/dist/web/server/assets/{services-wG8I06oS.js → services-DozuJtzX.js} +3 -3
  141. package/dist/web/server/assets/{skills-BZdqiDig.js → skills-CYih9TJw.js} +47 -6
  142. package/dist/web/server/assets/{skills-a9VSWHWq.js → skills-DfGSJTCB.js} +3 -3
  143. package/dist/web/server/assets/{skills-C-UgkuCW.js → skills-Dw3cI3si.js} +3 -3
  144. package/dist/web/server/assets/start-D4Wq9iTa.js +70 -0
  145. package/dist/web/server/assets/{teams-OIkHuxy1.js → teams-BJuoNOTB.js} +3 -3
  146. package/dist/web/server/assets/{teams-CpQ0YgWf.js → teams-DF3RViAg.js} +3 -3
  147. package/dist/web/server/assets/{teams-DEpQR2QA.js → teams-DFnuc660.js} +3 -3
  148. package/dist/web/server/assets/{teams-DqfrEoH6.js → teams-DPnAX4wD.js} +4 -4
  149. package/dist/web/server/assets/{teams-jBtvnk59.js → teams-TV6nbEN_.js} +101 -10
  150. package/dist/web/server/assets/{teams-MjqgsmC3.js → teams-be05ci17.js} +3 -3
  151. package/dist/web/server/assets/{telegram-DNgCWebD.js → telegram-1AWL6lFw.js} +3 -3
  152. package/dist/web/server/assets/{telegram-XwyKSEtj.js → telegram-DbOFpTFm.js} +3 -3
  153. package/dist/web/server/assets/{telegram-CBF8SJzB.js → telegram-Dj4Wwhd6.js} +262 -43
  154. package/dist/web/server/assets/tokens-DDwtO15P.js +359 -0
  155. package/dist/web/server/assets/tokens-DjM8GsgS.js +19 -0
  156. package/dist/web/server/assets/{tokens-BnwFbV7T.js → tokens-_2hptHP6.js} +4 -4
  157. package/dist/web/server/assets/{triggers-BZPl5CHs.js → triggers-BvjYNmmY.js} +4 -4
  158. package/dist/web/server/assets/triggers-CHk9O3rU.js +367 -0
  159. package/dist/web/server/assets/{triggers-D79NoSXk.js → triggers-Nezfw-Bd.js} +4 -4
  160. package/dist/web/server/assets/utils-C_uf36nf.js +8 -0
  161. package/dist/web/server/assets/{welcome-DDB11L5j.js → welcome-B8nchXdF.js} +9 -8
  162. package/dist/web/server/server.js +29 -33
  163. package/dist/web-server.js +4 -0
  164. package/dist/web-server.js.map +1 -1
  165. package/dist/worker.js +1744 -362
  166. package/dist/worker.js.map +1 -1
  167. package/package.json +7 -6
  168. package/dist/web/client/assets/AgentTabs-R6gSjIj3.js +0 -1
  169. package/dist/web/client/assets/ConfigPage-BCwfgyjD.js +0 -1
  170. package/dist/web/client/assets/FieldRow-avAFLgYW.js +0 -1
  171. package/dist/web/client/assets/Page-CwNuDhWz.js +0 -1
  172. package/dist/web/client/assets/ProductionTeamPolicyEditor-CsWpTsy6.js +0 -7
  173. package/dist/web/client/assets/SectionTabs-DZzy01az.js +0 -1
  174. package/dist/web/client/assets/TeamTabs-DvqWieCq.js +0 -1
  175. package/dist/web/client/assets/_id-BUuz7Drv.js +0 -1
  176. package/dist/web/client/assets/_id-BegmVRmT.js +0 -1
  177. package/dist/web/client/assets/_id-BrA4Crh_.js +0 -1
  178. package/dist/web/client/assets/activity-n0Fw9_w3.js +0 -1
  179. package/dist/web/client/assets/agents-B33VQtbl.js +0 -1
  180. package/dist/web/client/assets/agents-C50pdxp7.js +0 -1
  181. package/dist/web/client/assets/approvals-DVW0hX8h.js +0 -1
  182. package/dist/web/client/assets/attention-B2490jcN.js +0 -1
  183. package/dist/web/client/assets/chevron-down-B0aNNOyn.js +0 -1
  184. package/dist/web/client/assets/config-CMMxxeX2.js +0 -3
  185. package/dist/web/client/assets/context-DcFzsfZ3.js +0 -1
  186. package/dist/web/client/assets/index-3nrK5iFr.js +0 -12
  187. package/dist/web/client/assets/learning-BUX1MFu2.js +0 -1
  188. package/dist/web/client/assets/login-CMWpGDKv.js +0 -1
  189. package/dist/web/client/assets/mcp-lZV08dqc.js +0 -1
  190. package/dist/web/client/assets/members-DAeaY2Cy.js +0 -1
  191. package/dist/web/client/assets/memory-HmMWxxdF.js +0 -1
  192. package/dist/web/client/assets/plus-CPzRJg1V.js +0 -1
  193. package/dist/web/client/assets/routes-BNk270AM.js +0 -1
  194. package/dist/web/client/assets/services-DtrpJrdo.js +0 -1
  195. package/dist/web/client/assets/skills-Cv93A3P3.js +0 -1
  196. package/dist/web/client/assets/styles-DMltY3su.css +0 -2
  197. package/dist/web/client/assets/teams-CCFnSNKi.js +0 -1
  198. package/dist/web/client/assets/telegram-BF1vdgDB.js +0 -1
  199. package/dist/web/client/assets/tokens-BXj6hWW-.js +0 -1
  200. package/dist/web/client/assets/triggers-NT24i4hZ.js +0 -1
  201. package/dist/web/client/assets/welcome-D0cy68eZ.js +0 -1
  202. package/dist/web/client/assets/wire-constants-BhVKOCQa.js +0 -74
  203. package/dist/web/server/assets/FieldRow-B4eTegH9.js +0 -94
  204. package/dist/web/server/assets/config-BmixCpz-.js +0 -372
  205. package/dist/web/server/assets/context-B-XDf6Ym.js +0 -12
  206. package/dist/web/server/assets/context-BXGo5Vfi.js +0 -12
  207. package/dist/web/server/assets/context-DrkrIWun.js +0 -47
  208. package/dist/web/server/assets/mcp-BJjLIdP0.js +0 -261
  209. package/dist/web/server/assets/members-CCv17cjQ.js +0 -46
  210. package/dist/web/server/assets/members-CW5aBe81.js +0 -12
  211. package/dist/web/server/assets/members-VdPwD6CE.js +0 -12
  212. package/dist/web/server/assets/router-CIVjTgNp.js +0 -537
  213. package/dist/web/server/assets/services-B9yBWDX0.js +0 -79
  214. package/dist/web/server/assets/start-WH8WzTL_.js +0 -4
  215. package/dist/web/server/assets/tokens-DPDMSN5O.js +0 -12
  216. package/dist/web/server/assets/tokens-yyTSfBSN.js +0 -186
  217. package/dist/web/server/assets/triggers-CSNNzRIi.js +0 -216
@@ -1,16 +1,29 @@
1
- import { t as Route } from "./_id-CwyGjJeP.js";
2
- import { r as PageShell } from "./Page-LENulSXY.js";
1
+ import { t as Route } from "./_id-BYKpT2Tu.js";
2
+ import { r as PageShell } from "./Page-Bz-ydB8j.js";
3
+ import { t as UnsavedChangesGuard } from "./UnsavedChangesGuard-CiStXyBI.js";
3
4
  import { t as DEFAULT_PROFILE_COMMUNICATION } from "./team-policy-vPG01onj.js";
4
5
  import { t as ProfileCommunicationEditor } from "./ProfileCommunicationEditor-C2RU-2QB.js";
5
- import { t as TemplatesTabs } from "./TemplatesTabs-CcRxZhJe.js";
6
- import { useState } from "react";
6
+ import { t as TemplatesTabs } from "./TemplatesTabs-B0fA361R.js";
7
+ import { useCallback, useEffect, useState } from "react";
7
8
  import { useRouter } from "@tanstack/react-router";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/routes/templates/agents/$id/index.tsx?tsr-split=component
10
11
  function ProfileDetailPage() {
11
12
  const { loaded, modelGroups, skills } = Route.useLoaderData();
12
13
  const [tab, setTab] = useState("basics");
14
+ const [dirtySections, setDirtySections] = useState({});
15
+ const reportDirty = useCallback((section, dirty) => {
16
+ setDirtySections((current) => current[section] === dirty ? current : {
17
+ ...current,
18
+ [section]: dirty
19
+ });
20
+ }, []);
21
+ const dirtyCount = Object.values(dirtySections).filter(Boolean).length;
13
22
  return /* @__PURE__ */ jsxs(PageShell, { children: [
23
+ /* @__PURE__ */ jsx(UnsavedChangesGuard, {
24
+ when: dirtyCount > 0,
25
+ subject: "Agent template changes"
26
+ }),
14
27
  /* @__PURE__ */ jsx(TemplatesTabs, {}),
15
28
  /* @__PURE__ */ jsxs("header", {
16
29
  className: "mb-1",
@@ -58,77 +71,134 @@ function ProfileDetailPage() {
58
71
  active: tab,
59
72
  onChange: (t) => setTab(t)
60
73
  }),
61
- tab === "basics" && /* @__PURE__ */ jsxs("div", { children: [
62
- /* @__PURE__ */ jsx(SettingsCard, {
63
- profileId: loaded.profile.id,
64
- initialName: loaded.profile.name,
65
- currentModel: loaded.profile.defaultModel,
66
- modelGroups
67
- }),
68
- /* @__PURE__ */ jsx(FileCard, {
69
- profileId: loaded.profile.id,
70
- file: "SOUL.md",
71
- initialContent: loaded.files.soul,
72
- rows: 14
73
- }),
74
- /* @__PURE__ */ jsx(FileCard, {
75
- profileId: loaded.profile.id,
76
- file: "IDENTITY.md",
77
- initialContent: loaded.files.identity,
78
- rows: 10
79
- }),
80
- loaded.files.bootstrap === null ? /* @__PURE__ */ jsxs("section", {
81
- className: "file-card",
82
- children: [/* @__PURE__ */ jsx("div", {
83
- className: "file-head",
84
- children: /* @__PURE__ */ jsx("h3", { children: "BOOTSTRAP.md" })
85
- }), /* @__PURE__ */ jsx("p", {
86
- className: "muted",
87
- children: "This profile was created without a bootstrap prompt. Spawned agents won't see a first-run intro message."
88
- })]
89
- }) : /* @__PURE__ */ jsx(FileCard, {
90
- profileId: loaded.profile.id,
91
- file: "BOOTSTRAP.md",
92
- initialContent: loaded.files.bootstrap,
93
- rows: 10
94
- }),
95
- /* @__PURE__ */ jsx(OptionalFileCard, {
96
- profileId: loaded.profile.id,
97
- file: "AGENTS.md",
98
- initialContent: loaded.files.agents,
99
- hint: "Peers and routing notes who this agent can hand off to."
100
- }),
101
- /* @__PURE__ */ jsx(OptionalFileCard, {
74
+ dirtyCount > 0 && /* @__PURE__ */ jsxs("p", {
75
+ role: "status",
76
+ className: "mb-4 text-sm text-warning",
77
+ children: [
78
+ dirtyCount,
79
+ " unsaved ",
80
+ dirtyCount === 1 ? "section" : "sections",
81
+ " switching tabs keeps the draft, but leaving this page will ask for confirmation."
82
+ ]
83
+ }),
84
+ /* @__PURE__ */ jsxs("div", {
85
+ hidden: tab !== "basics",
86
+ children: [
87
+ /* @__PURE__ */ jsx(SettingsCard, {
88
+ profileId: loaded.profile.id,
89
+ initialName: loaded.profile.name,
90
+ currentModel: loaded.profile.defaultModel,
91
+ modelGroups,
92
+ onDirtyChange: reportDirty
93
+ }),
94
+ /* @__PURE__ */ jsx(FileCard, {
95
+ profileId: loaded.profile.id,
96
+ file: "SOUL.md",
97
+ initialContent: loaded.files.soul,
98
+ rows: 14,
99
+ onDirtyChange: reportDirty
100
+ }),
101
+ /* @__PURE__ */ jsx(FileCard, {
102
+ profileId: loaded.profile.id,
103
+ file: "IDENTITY.md",
104
+ initialContent: loaded.files.identity,
105
+ rows: 10,
106
+ onDirtyChange: reportDirty
107
+ }),
108
+ loaded.files.bootstrap === null ? /* @__PURE__ */ jsxs("section", {
109
+ className: "file-card",
110
+ children: [/* @__PURE__ */ jsx("div", {
111
+ className: "file-head",
112
+ children: /* @__PURE__ */ jsx("h3", { children: "BOOTSTRAP.md" })
113
+ }), /* @__PURE__ */ jsx("p", {
114
+ className: "muted",
115
+ children: "This profile was created without a bootstrap prompt. Spawned agents won't see a first-run intro message."
116
+ })]
117
+ }) : /* @__PURE__ */ jsx(FileCard, {
118
+ profileId: loaded.profile.id,
119
+ file: "BOOTSTRAP.md",
120
+ initialContent: loaded.files.bootstrap,
121
+ rows: 10,
122
+ onDirtyChange: reportDirty
123
+ }),
124
+ /* @__PURE__ */ jsx(OptionalFileCard, {
125
+ profileId: loaded.profile.id,
126
+ file: "AGENTS.md",
127
+ initialContent: loaded.files.agents,
128
+ hint: "Peers and routing notes — who this agent can hand off to.",
129
+ onDirtyChange: reportDirty
130
+ }),
131
+ /* @__PURE__ */ jsx(OptionalFileCard, {
132
+ profileId: loaded.profile.id,
133
+ file: "TOOLS.md",
134
+ initialContent: loaded.files.tools,
135
+ hint: "Tool playbook. Agent-specific usage notes that go beyond the generic tool descriptions.",
136
+ onDirtyChange: reportDirty
137
+ })
138
+ ]
139
+ }),
140
+ /* @__PURE__ */ jsx("div", {
141
+ hidden: tab !== "skills",
142
+ children: /* @__PURE__ */ jsx(SkillsCard, {
102
143
  profileId: loaded.profile.id,
103
- file: "TOOLS.md",
104
- initialContent: loaded.files.tools,
105
- hint: "Tool playbook. Agent-specific usage notes that go beyond the generic tool descriptions."
144
+ initialMode: loaded.profile.skillsMode,
145
+ initialPicked: new Set(loaded.defaultSkills),
146
+ skills,
147
+ onDirtyChange: reportDirty
106
148
  })
107
- ] }),
108
- tab === "skills" && /* @__PURE__ */ jsx(SkillsCard, {
109
- profileId: loaded.profile.id,
110
- initialMode: loaded.profile.skillsMode,
111
- initialPicked: new Set(loaded.defaultSkills),
112
- skills
113
149
  }),
114
- tab === "communication" && /* @__PURE__ */ jsx(ProfileCommunicationCard, {
115
- profileId: loaded.profile.id,
116
- initial: loaded.profile.communicationDefaults ?? DEFAULT_PROFILE_COMMUNICATION
150
+ /* @__PURE__ */ jsx("div", {
151
+ hidden: tab !== "communication",
152
+ children: /* @__PURE__ */ jsx(ProfileCommunicationCard, {
153
+ profileId: loaded.profile.id,
154
+ initial: loaded.profile.communicationDefaults ?? DEFAULT_PROFILE_COMMUNICATION,
155
+ onDirtyChange: reportDirty
156
+ })
117
157
  })
118
158
  ] });
119
159
  }
120
- function ProfileCommunicationCard({ profileId, initial }) {
160
+ function useDirtyReporter(section, dirty, onDirtyChange) {
161
+ useEffect(() => {
162
+ onDirtyChange(section, dirty);
163
+ }, [
164
+ dirty,
165
+ onDirtyChange,
166
+ section
167
+ ]);
168
+ }
169
+ function ProfileCommunicationCard({ profileId, initial, onDirtyChange }) {
121
170
  const [value, setValue] = useState(initial);
122
- const [savedAt, setSavedAt] = useState(null);
123
- const dirty = JSON.stringify(value) !== JSON.stringify(initial);
171
+ const [original, setOriginal] = useState(initial);
172
+ const [status, setStatus] = useState({
173
+ kind: "idle",
174
+ message: ""
175
+ });
176
+ const dirty = JSON.stringify(value) !== JSON.stringify(original);
177
+ const display = visibleStatus(dirty, status);
178
+ useDirtyReporter("communication", dirty, onDirtyChange);
124
179
  const save = async () => {
125
- const response = await fetch(`/api/profiles/${encodeURIComponent(profileId)}`, {
126
- method: "PATCH",
127
- headers: { "content-type": "application/json" },
128
- body: JSON.stringify({ communicationDefaults: value })
180
+ setStatus({
181
+ kind: "saving",
182
+ message: "saving…"
129
183
  });
130
- if (!response.ok) throw new Error((await response.json().catch(() => null))?.error ?? response.statusText);
131
- setSavedAt(Date.now());
184
+ try {
185
+ const response = await fetch(`/api/profiles/${encodeURIComponent(profileId)}`, {
186
+ method: "PATCH",
187
+ headers: { "content-type": "application/json" },
188
+ body: JSON.stringify({ communicationDefaults: value })
189
+ });
190
+ if (!response.ok) throw new Error((await response.json().catch(() => null))?.error ?? response.statusText);
191
+ setOriginal(value);
192
+ setStatus({
193
+ kind: "saved",
194
+ message: "saved to daemon"
195
+ });
196
+ } catch (err) {
197
+ setStatus({
198
+ kind: "error",
199
+ message: `error: ${err instanceof Error ? err.message : String(err)}`
200
+ });
201
+ }
132
202
  };
133
203
  return /* @__PURE__ */ jsxs("section", {
134
204
  className: "rounded-md border border-frost bg-snow p-5 shadow-baziu-sm",
@@ -153,12 +223,13 @@ function ProfileCommunicationCard({ profileId, initial }) {
153
223
  children: [/* @__PURE__ */ jsx("button", {
154
224
  type: "button",
155
225
  className: "btn-primary",
156
- disabled: !dirty,
226
+ disabled: !dirty || status.kind === "saving",
157
227
  onClick: save,
158
228
  children: "save communication defaults"
159
- }), savedAt && /* @__PURE__ */ jsx("span", {
160
- className: "text-xs text-sapphire",
161
- children: "saved to daemon"
229
+ }), display.message && /* @__PURE__ */ jsx("span", {
230
+ role: display.kind === "error" ? "alert" : "status",
231
+ className: `text-xs ${statusColor(display.kind)}`,
232
+ children: display.message
162
233
  })]
163
234
  })
164
235
  ]
@@ -184,6 +255,12 @@ function statusColor(kind) {
184
255
  if (kind === "dirty") return "text-mocha";
185
256
  return "text-mocha-light";
186
257
  }
258
+ function visibleStatus(dirty, status) {
259
+ return dirty && status.kind !== "saving" && status.kind !== "error" ? {
260
+ kind: "dirty",
261
+ message: "unsaved changes"
262
+ } : status;
263
+ }
187
264
  function FileCardShell({ title, status, children }) {
188
265
  return /* @__PURE__ */ jsxs("section", {
189
266
  className: "file-card mb-5 rounded-[16px] border border-frost bg-snow p-5 shadow-baziu-sm",
@@ -193,23 +270,23 @@ function FileCardShell({ title, status, children }) {
193
270
  className: "m-0 font-mono text-[0.95em] font-semibold normal-case tracking-normal text-chocolate",
194
271
  children: title
195
272
  }), status && /* @__PURE__ */ jsx("span", {
273
+ role: status.kind === "error" ? "alert" : status.message ? "status" : void 0,
196
274
  className: `text-[0.82em] ${statusColor(status.kind)}`,
197
275
  children: status.message
198
276
  })]
199
277
  }), children]
200
278
  });
201
279
  }
202
- function FileCard({ profileId, file, initialContent, rows = 10 }) {
280
+ function FileCard({ profileId, file, initialContent, rows = 10, onDirtyChange }) {
203
281
  const [original, setOriginal] = useState(initialContent);
204
282
  const [content, setContent] = useState(initialContent);
205
283
  const [status, setStatus] = useState({
206
284
  kind: "idle",
207
285
  message: ""
208
286
  });
209
- const display = content !== original && status.kind !== "saving" && status.kind !== "error" ? {
210
- kind: "dirty",
211
- message: "unsaved changes"
212
- } : status;
287
+ const dirty = content !== original;
288
+ useDirtyReporter(`file:${file}`, dirty, onDirtyChange);
289
+ const display = visibleStatus(dirty, status);
213
290
  async function save() {
214
291
  setStatus({
215
292
  kind: "saving",
@@ -248,6 +325,7 @@ function FileCard({ profileId, file, initialContent, rows = 10 }) {
248
325
  title: file,
249
326
  status: display,
250
327
  children: [/* @__PURE__ */ jsx("textarea", {
328
+ "aria-label": `${file} contents`,
251
329
  rows,
252
330
  value: content,
253
331
  onChange: (e) => setContent(e.target.value),
@@ -269,7 +347,7 @@ function FileCard({ profileId, file, initialContent, rows = 10 }) {
269
347
  })]
270
348
  });
271
349
  }
272
- function OptionalFileCard({ profileId, file, initialContent, hint }) {
350
+ function OptionalFileCard({ profileId, file, initialContent, hint, onDirtyChange }) {
273
351
  const router = useRouter();
274
352
  const [creating, setCreating] = useState(false);
275
353
  const [status, setStatus] = useState({
@@ -325,20 +403,28 @@ function OptionalFileCard({ profileId, file, initialContent, hint }) {
325
403
  profileId,
326
404
  file,
327
405
  initialContent,
328
- rows: 10
406
+ rows: 10,
407
+ onDirtyChange
329
408
  })] });
330
409
  }
331
- function SettingsCard({ profileId, initialName, currentModel, modelGroups }) {
410
+ function SettingsCard({ profileId, initialName, currentModel, modelGroups, onDirtyChange }) {
332
411
  const router = useRouter();
333
412
  const [name, setName] = useState(initialName);
334
413
  const [defaultModel, setDefaultModel] = useState(currentModel);
414
+ const [originalName, setOriginalName] = useState(initialName);
415
+ const [originalModel, setOriginalModel] = useState(currentModel);
335
416
  const [status, setStatus] = useState({
336
417
  kind: "idle",
337
418
  message: ""
338
419
  });
420
+ const dirty = name !== originalName || defaultModel !== originalModel;
421
+ const display = visibleStatus(dirty, status);
422
+ useDirtyReporter("settings", dirty, onDirtyChange);
339
423
  const modelMissing = !modelGroups.flatMap((g) => g.models.map((m) => `${g.provider}:${m}`)).includes(currentModel);
340
424
  async function save(e) {
341
425
  e.preventDefault();
426
+ const savedName = name.trim();
427
+ const savedModel = defaultModel.trim();
342
428
  setStatus({
343
429
  kind: "saving",
344
430
  message: "saving…"
@@ -348,8 +434,8 @@ function SettingsCard({ profileId, initialName, currentModel, modelGroups }) {
348
434
  method: "PATCH",
349
435
  headers: { "content-type": "application/json" },
350
436
  body: JSON.stringify({
351
- name: name.trim(),
352
- defaultModel: defaultModel.trim()
437
+ name: savedName,
438
+ defaultModel: savedModel
353
439
  })
354
440
  });
355
441
  if (!res.ok) {
@@ -357,6 +443,10 @@ function SettingsCard({ profileId, initialName, currentModel, modelGroups }) {
357
443
  throw new Error(e2?.error ?? res.statusText);
358
444
  }
359
445
  await router.invalidate();
446
+ setName(savedName);
447
+ setDefaultModel(savedModel);
448
+ setOriginalName(savedName);
449
+ setOriginalModel(savedModel);
360
450
  setStatus({
361
451
  kind: "saved",
362
452
  message: "saved"
@@ -370,7 +460,7 @@ function SettingsCard({ profileId, initialName, currentModel, modelGroups }) {
370
460
  }
371
461
  return /* @__PURE__ */ jsx(FileCardShell, {
372
462
  title: "settings",
373
- status,
463
+ status: display,
374
464
  children: /* @__PURE__ */ jsxs("form", {
375
465
  onSubmit: save,
376
466
  children: [/* @__PURE__ */ jsxs("div", {
@@ -405,21 +495,26 @@ function SettingsCard({ profileId, initialName, currentModel, modelGroups }) {
405
495
  className: "mt-3 flex gap-2",
406
496
  children: /* @__PURE__ */ jsx("button", {
407
497
  type: "submit",
408
- disabled: status.kind === "saving",
498
+ disabled: !dirty || status.kind === "saving",
409
499
  children: "save settings"
410
500
  })
411
501
  })]
412
502
  })
413
503
  });
414
504
  }
415
- function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
505
+ function SkillsCard({ profileId, initialMode, initialPicked, skills, onDirtyChange }) {
416
506
  const router = useRouter();
417
507
  const [mode, setMode] = useState(initialMode);
418
508
  const [picked, setPicked] = useState(initialPicked);
509
+ const [originalMode, setOriginalMode] = useState(initialMode);
510
+ const [originalPicked, setOriginalPicked] = useState(initialPicked);
419
511
  const [status, setStatus] = useState({
420
512
  kind: "idle",
421
513
  message: ""
422
514
  });
515
+ const dirty = mode !== originalMode || !sameSet(picked, originalPicked);
516
+ const display = visibleStatus(dirty, status);
517
+ useDirtyReporter("skills", dirty, onDirtyChange);
423
518
  function toggle(name) {
424
519
  setPicked((prev) => {
425
520
  const next = new Set(prev);
@@ -430,6 +525,7 @@ function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
430
525
  }
431
526
  async function save(e) {
432
527
  e.preventDefault();
528
+ const savedPicked = mode === "selected" ? new Set(picked) : /* @__PURE__ */ new Set();
433
529
  setStatus({
434
530
  kind: "saving",
435
531
  message: "saving…"
@@ -440,7 +536,7 @@ function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
440
536
  headers: { "content-type": "application/json" },
441
537
  body: JSON.stringify({
442
538
  skillsMode: mode,
443
- defaultSkills: mode === "selected" ? Array.from(picked) : []
539
+ defaultSkills: Array.from(savedPicked)
444
540
  })
445
541
  });
446
542
  if (!res.ok) {
@@ -448,6 +544,9 @@ function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
448
544
  throw new Error(e2?.error ?? res.statusText);
449
545
  }
450
546
  await router.invalidate();
547
+ setOriginalMode(mode);
548
+ setPicked(savedPicked);
549
+ setOriginalPicked(savedPicked);
451
550
  setStatus({
452
551
  kind: "saved",
453
552
  message: "saved"
@@ -461,7 +560,7 @@ function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
461
560
  }
462
561
  return /* @__PURE__ */ jsxs(FileCardShell, {
463
562
  title: "skills",
464
- status,
563
+ status: display,
465
564
  children: [/* @__PURE__ */ jsx("p", {
466
565
  className: "muted my-2",
467
566
  children: "Choose what agents spawned from this profile inherit. Existing agents aren't affected — they keep whatever skills they were spawned with."
@@ -512,7 +611,7 @@ function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
512
611
  className: "mt-3 flex gap-2",
513
612
  children: /* @__PURE__ */ jsx("button", {
514
613
  type: "submit",
515
- disabled: status.kind === "saving",
614
+ disabled: !dirty || status.kind === "saving",
516
615
  children: "save skills"
517
616
  })
518
617
  })
@@ -520,5 +619,8 @@ function SkillsCard({ profileId, initialMode, initialPicked, skills }) {
520
619
  })]
521
620
  });
522
621
  }
622
+ function sameSet(left, right) {
623
+ return left.size === right.size && Array.from(left).every((item) => right.has(item));
624
+ }
523
625
  //#endregion
524
626
  export { ProfileDetailPage as component };
@@ -1,9 +1,9 @@
1
- import { t as createServerFn } from "../server.js";
2
- import { t as createSsrRpc } from "./createSsrRpc-DFsIAfFd.js";
1
+ import { n as createServerFn } from "../server.js";
2
+ import { t as createSsrRpc } from "./createSsrRpc-6LHNEsSK.js";
3
3
  import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
4
4
  //#region src/routes/templates/teams/$id/index.tsx
5
5
  var $$splitErrorComponentImporter = () => import("./_id-A8Xiv6Lc.js");
6
- var $$splitComponentImporter = () => import("./_id-LrvXU4Nr.js");
6
+ var $$splitComponentImporter = () => import("./_id-BM5m2FLT.js");
7
7
  var fetchTeam = createServerFn({ method: "POST" }).validator((data) => data).handler(createSsrRpc("a0bbbee5d0c1240a05e35c04d56a0e95ec78f05265e4e937eec48c8ac2041e58"));
8
8
  var Route = createFileRoute("/templates/teams/$id/")({
9
9
  loader: ({ params }) => fetchTeam({ data: params }),
@@ -1,14 +1,13 @@
1
- import { t as createServerFn } from "../server.js";
2
- import { t as createServerRpc } from "./createServerRpc-pU-IQV2Y.js";
3
- import { n as daemonClient, r as ApiClientError } from "./daemon-client-2NRMzlTr.js";
4
- import "react";
1
+ import { n as createServerFn } from "../server.js";
2
+ import { t as createServerRpc } from "./createServerRpc-B7Yr1K8i.js";
3
+ import { n as daemonClient, r as ApiClientError } from "./daemon-client-CI5NHwNC.js";
5
4
  //#region src/routes/templates/agents/$id/index.tsx?tss-serverfn-split
6
5
  var fetchProfile_createServerFn_handler = createServerRpc({
7
6
  id: "032d74b7d41c718191dbdde6b06ba44993340ce1154af1fa6723a6d4f7a7236e",
8
7
  name: "fetchProfile",
9
8
  filename: "src/routes/templates/agents/$id/index.tsx"
10
9
  }, (opts) => fetchProfile.__executeServer(opts));
11
- var fetchProfile = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(fetchProfile_createServerFn_handler, async ({ data }) => {
10
+ var fetchProfile = createServerFn({ method: "POST" }).validator((d) => d).handler(fetchProfile_createServerFn_handler, async ({ data }) => {
12
11
  const c = daemonClient();
13
12
  let loaded;
14
13
  try {
@@ -1,13 +1,13 @@
1
- import { t as createServerFn } from "../server.js";
2
- import { t as createServerRpc } from "./createServerRpc-pU-IQV2Y.js";
3
- import { n as daemonClient, r as ApiClientError } from "./daemon-client-2NRMzlTr.js";
1
+ import { n as createServerFn } from "../server.js";
2
+ import { t as createServerRpc } from "./createServerRpc-B7Yr1K8i.js";
3
+ import { n as daemonClient, r as ApiClientError } from "./daemon-client-CI5NHwNC.js";
4
4
  //#region src/routes/teams/$id/index.tsx?tss-serverfn-split
5
5
  var fetchGroup_createServerFn_handler = createServerRpc({
6
6
  id: "4773184b3ddd3656fb80ec74375a6d304e25acb56b79f1d349dcabf9f0914359",
7
7
  name: "fetchGroup",
8
8
  filename: "src/routes/teams/$id/index.tsx"
9
9
  }, (opts) => fetchGroup.__executeServer(opts));
10
- var fetchGroup = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(fetchGroup_createServerFn_handler, async ({ data }) => {
10
+ var fetchGroup = createServerFn({ method: "POST" }).validator((d) => d).handler(fetchGroup_createServerFn_handler, async ({ data }) => {
11
11
  const c = daemonClient();
12
12
  let team;
13
13
  try {
@@ -1,13 +1,13 @@
1
- import { t as createServerFn } from "../server.js";
2
- import { t as createServerRpc } from "./createServerRpc-pU-IQV2Y.js";
3
- import { n as daemonClient, r as ApiClientError } from "./daemon-client-2NRMzlTr.js";
1
+ import { n as createServerFn } from "../server.js";
2
+ import { t as createServerRpc } from "./createServerRpc-B7Yr1K8i.js";
3
+ import { n as daemonClient, r as ApiClientError } from "./daemon-client-CI5NHwNC.js";
4
4
  //#region src/routes/agents/$id/index.tsx?tss-serverfn-split
5
5
  var fetchAgent_createServerFn_handler = createServerRpc({
6
6
  id: "846c9081aed17057351730985f133b0aab67a5dabfca9562552829a90ff30849",
7
7
  name: "fetchAgent",
8
8
  filename: "src/routes/agents/$id/index.tsx"
9
9
  }, (opts) => fetchAgent.__executeServer(opts));
10
- var fetchAgent = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(fetchAgent_createServerFn_handler, async ({ data }) => {
10
+ var fetchAgent = createServerFn({ method: "POST" }).validator((d) => d).handler(fetchAgent_createServerFn_handler, async ({ data }) => {
11
11
  const c = daemonClient();
12
12
  let resolved;
13
13
  try {
@@ -1,6 +1,6 @@
1
- import { t as createServerFn } from "../server.js";
2
- import { t as createServerRpc } from "./createServerRpc-pU-IQV2Y.js";
3
- import { n as daemonClient } from "./daemon-client-2NRMzlTr.js";
1
+ import { n as createServerFn } from "../server.js";
2
+ import { t as createServerRpc } from "./createServerRpc-B7Yr1K8i.js";
3
+ import { n as daemonClient } from "./daemon-client-CI5NHwNC.js";
4
4
  //#region src/routes/templates/teams/$id/index.tsx?tss-serverfn-split
5
5
  var fetchTeam_createServerFn_handler = createServerRpc({
6
6
  id: "a0bbbee5d0c1240a05e35c04d56a0e95ec78f05265e4e937eec48c8ac2041e58",