@maudecode/talaria-web 1.15.0 → 1.16.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 (312) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +21 -7
  3. package/dist/api/automation-router.js +3 -0
  4. package/dist/api/automation-router.js.map +1 -1
  5. package/dist/api/chat-router.d.ts +142 -6
  6. package/dist/api/chat-router.js +11 -5
  7. package/dist/api/chat-router.js.map +1 -1
  8. package/dist/api/git-router.js +44 -32
  9. package/dist/api/git-router.js.map +1 -1
  10. package/dist/api/raw-routes.js +5 -58
  11. package/dist/api/raw-routes.js.map +1 -1
  12. package/dist/api/router.d.ts +4 -2
  13. package/dist/api/router.js +8 -8
  14. package/dist/api/router.js.map +1 -1
  15. package/dist/api/sessions-router.d.ts +714 -42
  16. package/dist/api/sessions-router.js +57 -23
  17. package/dist/api/sessions-router.js.map +1 -1
  18. package/dist/api/settings-router.d.ts +153 -9
  19. package/dist/api/settings-router.js +61 -16
  20. package/dist/api/settings-router.js.map +1 -1
  21. package/dist/api/sse-routes.js +1 -1
  22. package/dist/api/sse-routes.js.map +1 -1
  23. package/dist/api/tools-router.d.ts +13 -1
  24. package/dist/api/tools-router.js +10 -8
  25. package/dist/api/tools-router.js.map +1 -1
  26. package/dist/app.d.ts +1047 -67
  27. package/dist/auth/gate.js +8 -4
  28. package/dist/auth/gate.js.map +1 -1
  29. package/dist/auth/oidc.d.ts +6 -2
  30. package/dist/auth/oidc.js +16 -11
  31. package/dist/auth/oidc.js.map +1 -1
  32. package/dist/auth/store.d.ts +14 -2
  33. package/dist/auth/store.js +26 -1
  34. package/dist/auth/store.js.map +1 -1
  35. package/dist/cli/dotenv.d.ts +1 -1
  36. package/dist/cli/dotenv.js +36 -3
  37. package/dist/cli/dotenv.js.map +1 -1
  38. package/dist/http/addresses.d.ts +6 -0
  39. package/dist/http/addresses.js +26 -0
  40. package/dist/http/addresses.js.map +1 -1
  41. package/dist/http/context.d.ts +3 -3
  42. package/dist/links.d.ts +26 -0
  43. package/dist/links.js +60 -0
  44. package/dist/links.js.map +1 -0
  45. package/dist/onboarding.js +26 -7
  46. package/dist/onboarding.js.map +1 -1
  47. package/dist/profiles/profiles.js +1 -1
  48. package/dist/profiles/profiles.js.map +1 -1
  49. package/dist/providers/catalog.d.ts +74 -1
  50. package/dist/providers/catalog.js +248 -42
  51. package/dist/providers/catalog.js.map +1 -1
  52. package/dist/providers/tables.d.ts +3 -1
  53. package/dist/providers/tables.js +9 -4
  54. package/dist/providers/tables.js.map +1 -1
  55. package/dist/redact.js +493 -13
  56. package/dist/redact.js.map +1 -1
  57. package/dist/runtime.js +52 -28
  58. package/dist/runtime.js.map +1 -1
  59. package/dist/sessions/anchor.d.ts +22 -3
  60. package/dist/sessions/anchor.js +87 -42
  61. package/dist/sessions/anchor.js.map +1 -1
  62. package/dist/sessions/background-tasks.d.ts +4 -1
  63. package/dist/sessions/background-tasks.js +7 -4
  64. package/dist/sessions/background-tasks.js.map +1 -1
  65. package/dist/sessions/completions.js +1 -1
  66. package/dist/sessions/completions.js.map +1 -1
  67. package/dist/sessions/list.d.ts +8 -4
  68. package/dist/sessions/list.js +24 -17
  69. package/dist/sessions/list.js.map +1 -1
  70. package/dist/sessions/merge.d.ts +70 -7
  71. package/dist/sessions/merge.js +331 -23
  72. package/dist/sessions/merge.js.map +1 -1
  73. package/dist/sessions/pending.d.ts +5 -0
  74. package/dist/sessions/pending.js +30 -10
  75. package/dist/sessions/pending.js.map +1 -1
  76. package/dist/sessions/service.d.ts +57 -3
  77. package/dist/sessions/service.js +327 -56
  78. package/dist/sessions/service.js.map +1 -1
  79. package/dist/sessions/session.d.ts +6 -1
  80. package/dist/sessions/session.js +6 -0
  81. package/dist/sessions/session.js.map +1 -1
  82. package/dist/sessions/shares.js +11 -2
  83. package/dist/sessions/shares.js.map +1 -1
  84. package/dist/sessions/store.js +1 -1
  85. package/dist/sessions/store.js.map +1 -1
  86. package/dist/sessions/turn.d.ts +19 -1
  87. package/dist/sessions/turn.js +218 -113
  88. package/dist/sessions/turn.js.map +1 -1
  89. package/dist/settings.js +9 -0
  90. package/dist/settings.js.map +1 -1
  91. package/dist/sidecar/fake.js +2 -0
  92. package/dist/sidecar/fake.js.map +1 -1
  93. package/dist/tools/commands.d.ts +15 -0
  94. package/dist/tools/commands.js +52 -0
  95. package/dist/tools/commands.js.map +1 -0
  96. package/dist/tools/crons.d.ts +4 -1
  97. package/dist/tools/crons.js +58 -11
  98. package/dist/tools/crons.js.map +1 -1
  99. package/dist/tools/mcp.js +5 -1
  100. package/dist/tools/mcp.js.map +1 -1
  101. package/dist/tools/update-notifications.d.ts +1 -1
  102. package/dist/tools/update-notifications.js +1 -1
  103. package/dist/tools/update-notifications.js.map +1 -1
  104. package/dist/tools/updates.d.ts +8 -3
  105. package/dist/tools/updates.js +133 -34
  106. package/dist/tools/updates.js.map +1 -1
  107. package/dist/workspace/git.d.ts +57 -10
  108. package/dist/workspace/git.js +302 -195
  109. package/dist/workspace/git.js.map +1 -1
  110. package/dist/workspace/media-refs.d.ts +13 -0
  111. package/dist/workspace/media-refs.js +165 -0
  112. package/dist/workspace/media-refs.js.map +1 -0
  113. package/dist/workspace/media.d.ts +19 -0
  114. package/dist/workspace/media.js +61 -2
  115. package/dist/workspace/media.js.map +1 -1
  116. package/dist/workspace/workspaces.d.ts +7 -1
  117. package/dist/workspace/workspaces.js +30 -21
  118. package/dist/workspace/workspaces.js.map +1 -1
  119. package/dist/workspace/worktrees.d.ts +2 -4
  120. package/dist/workspace/worktrees.js +28 -33
  121. package/dist/workspace/worktrees.js.map +1 -1
  122. package/package.json +4 -2
  123. package/sidecar/scripts/replay_sidecar.py +3 -1
  124. package/sidecar/talaria_sidecar/__init__.py +1 -1
  125. package/sidecar/talaria_sidecar/home.py +59 -2
  126. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  127. package/sidecar/talaria_sidecar/methods/aux.py +19 -3
  128. package/sidecar/talaria_sidecar/methods/chat.py +129 -23
  129. package/sidecar/talaria_sidecar/methods/commands.py +46 -31
  130. package/sidecar/talaria_sidecar/methods/config.py +14 -10
  131. package/sidecar/talaria_sidecar/methods/oauth.py +439 -0
  132. package/sidecar/talaria_sidecar/methods/profiles.py +5 -4
  133. package/sidecar/talaria_sidecar/methods/providers.py +161 -1
  134. package/sidecar/talaria_sidecar/methods/runtime.py +2 -0
  135. package/sidecar/talaria_sidecar/methods/text.py +1 -20
  136. package/sidecar/talaria_sidecar/methods/usage.py +7 -1
  137. package/static/dist/FILES.txt +124 -120
  138. package/static/dist/assets/AppShell-ef3bhu32.js +1 -0
  139. package/static/dist/assets/Button-nx37f4u1.js +1 -0
  140. package/static/dist/assets/ChatPage-cdcpykcg.js +25 -0
  141. package/static/dist/assets/Dialog-mec42hvb.js +1 -0
  142. package/static/dist/assets/HubRoute-ecb65zpr.js +2 -0
  143. package/static/dist/assets/{Markdown-dy8t4dy7.js → Markdown-jqrx5es0.js} +125 -125
  144. package/static/dist/assets/RightPanel-d1lgvk4s.js +1 -0
  145. package/static/dist/assets/Select-h4ivl9qf.js +1 -0
  146. package/static/dist/assets/SessionListPanel-ewib046b.js +1 -0
  147. package/static/dist/assets/SettingsLayout-lx654cup.js +1 -0
  148. package/static/dist/assets/States-ehsb6lzo.js +1 -0
  149. package/static/dist/assets/Toaster-mch1pa3t.js +1 -0
  150. package/static/dist/assets/_app-m8d961ef.js +1 -0
  151. package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +1 -0
  152. package/static/dist/assets/_app.index-dk6zp7e0.js +1 -0
  153. package/static/dist/assets/_app.insights-jt2fk4i1.js +1 -0
  154. package/static/dist/assets/_app.kanban-if4pubeu.js +1 -0
  155. package/static/dist/assets/_app.logs-mfaw9m6g.js +1 -0
  156. package/static/dist/assets/_app.memory-h3eaarb6.js +1 -0
  157. package/static/dist/assets/_app.profiles-ienktovs.js +1 -0
  158. package/static/dist/assets/{_app.session._sessionId-gyoh5ylx.js → _app.session._sessionId-hh27wwu2.js} +1 -1
  159. package/static/dist/assets/_app.settings-dpoe0bzf.js +1 -0
  160. package/static/dist/assets/_app.settings._section-l468ynag.js +3 -0
  161. package/static/dist/assets/_app.skills-kcbncc8i.js +1 -0
  162. package/static/dist/assets/_app.tasks-occdrqhw.js +2 -0
  163. package/static/dist/assets/_app.todos-nnke2rcc.js +1 -0
  164. package/static/dist/assets/_app.workspaces-cdlatvwj.js +1 -0
  165. package/static/dist/assets/{abnfDiagram-VCTEODGH-cjqletra.js → abnfDiagram-VCTEODGH-oity9y4o.js} +1 -1
  166. package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +1 -0
  167. package/static/dist/assets/architectureDiagram-5GKGNRK7-ceaepnyr.js +36 -0
  168. package/static/dist/assets/{back-e4m6nsnm.js → back-eu64w89w.js} +1 -1
  169. package/static/dist/assets/{blockDiagram-I7D4REHJ-b195z021.js → blockDiagram-I7D4REHJ-n79st4pj.js} +1 -1
  170. package/static/dist/assets/{c4Diagram-7LVT6UL2-olu212k1.js → c4Diagram-7LVT6UL2-mi73akzk.js} +1 -1
  171. package/static/dist/assets/cancel-mzvkdjg8.js +1 -0
  172. package/static/dist/assets/channel-mbvmf2vv.js +1 -0
  173. package/static/dist/assets/{chunk-4HAMMTFA-b8o1nufs.js → chunk-4HAMMTFA-laat0g1q.js} +1 -1
  174. package/static/dist/assets/{chunk-75Z2AOVW-l1seddto.js → chunk-75Z2AOVW-0psy1tr2.js} +1 -1
  175. package/static/dist/assets/{chunk-DU6HZSFF-lo5zg15v.js → chunk-DU6HZSFF-lw8uox8x.js} +3 -3
  176. package/static/dist/assets/{chunk-F27PBJKO-dsdzrp16.js → chunk-F27PBJKO-erassib3.js} +1 -1
  177. package/static/dist/assets/{chunk-GMAD6QVW-f3fxqq8b.js → chunk-GMAD6QVW-fz7rx619.js} +1 -1
  178. package/static/dist/assets/{chunk-GVQU2GXP-h8egxtf6.js → chunk-GVQU2GXP-gdb0epb2.js} +1 -1
  179. package/static/dist/assets/{chunk-IMKFNOWR-iolwd8s0.js → chunk-IMKFNOWR-ea8t26qw.js} +1 -1
  180. package/static/dist/assets/{chunk-L3NEJ4N5-b88bbtwi.js → chunk-L3NEJ4N5-mb1gu9kz.js} +1 -1
  181. package/static/dist/assets/{chunk-OSK3NFVY-e3e7nr6s.js → chunk-OSK3NFVY-eovmccvy.js} +1 -1
  182. package/static/dist/assets/{chunk-P2QGCYS3-mdc46rt1.js → chunk-P2QGCYS3-bo55zjo5.js} +1 -1
  183. package/static/dist/assets/{chunk-POPQ4Y6H-kqlllrrm.js → chunk-POPQ4Y6H-ja7ohe4h.js} +1 -1
  184. package/static/dist/assets/{chunk-PWAF6VOD-he585k2x.js → chunk-PWAF6VOD-e30g9nus.js} +1 -1
  185. package/static/dist/assets/{chunk-SHT3W25Y-gh94y9a4.js → chunk-SHT3W25Y-gbb798f7.js} +3 -3
  186. package/static/dist/assets/{chunk-SVP7TREG-euxp9y7e.js → chunk-SVP7TREG-h2iw8urm.js} +1 -1
  187. package/static/dist/assets/{chunk-TICWLB2K-c8cxes1q.js → chunk-TICWLB2K-fd2cottz.js} +1 -1
  188. package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +1 -0
  189. package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +1 -0
  190. package/static/dist/assets/{copied-kq0u3ht7.js → copied-h65fc3cx.js} +1 -1
  191. package/static/dist/assets/copy-e6977uwq.js +1 -0
  192. package/static/dist/assets/{cynefin-OW5HDTMX-lxd44ux4.js → cynefin-OW5HDTMX-lbltyl5j.js} +1 -1
  193. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-njpiwat8.js → cynefinDiagram-5FMLGOSQ-lxhsqq4a.js} +1 -1
  194. package/static/dist/assets/{dagre-GXQ25YYZ-nvgy6yju.js → dagre-GXQ25YYZ-gn4nnaln.js} +4 -4
  195. package/static/dist/assets/{diagram-S7CK7UJ4-g98nyv72.js → diagram-S7CK7UJ4-pa3ij6q7.js} +3 -3
  196. package/static/dist/assets/{diagram-UQ7AKVKN-ovgm6lvv.js → diagram-UQ7AKVKN-zbipyz57.js} +1 -1
  197. package/static/dist/assets/{diagram-VSXAHHWV-pb2bn2e4.js → diagram-VSXAHHWV-d1kbpzgf.js} +1 -1
  198. package/static/dist/assets/{diagram-VX7I27RA-6j4mvfqo.js → diagram-VX7I27RA-i3ty14xl.js} +1 -1
  199. package/static/dist/assets/{diagram-Z3DM3KII-dqmb982r.js → diagram-Z3DM3KII-m8xefqa7.js} +1 -1
  200. package/static/dist/assets/{ebnfDiagram-PWID7BFC-ozbpzb6d.js → ebnfDiagram-PWID7BFC-nz7wj05l.js} +1 -1
  201. package/static/dist/assets/endpoints-k7yf25m0.js +64 -0
  202. package/static/dist/assets/{erDiagram-RLTQ6QDP-hl7m8vua.js → erDiagram-RLTQ6QDP-fa41ib83.js} +1 -1
  203. package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +1 -0
  204. package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +1 -0
  205. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ompixqmh.js → ganttDiagram-EL5Y4UJY-n7zq01qu.js} +1 -1
  206. package/static/dist/assets/{gitGraph-4MIJSDKK-b6vxwp9q.js → gitGraph-4MIJSDKK-c89emb07.js} +1 -1
  207. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-k98f57on.js → gitGraphDiagram-WWUBYQGX-cz7cgr6w.js} +1 -1
  208. package/static/dist/assets/{highlighted-body-KPVGNVTW-cd3ff6ge.js → highlighted-body-KPVGNVTW-d9bapmni.js} +1 -1
  209. package/static/dist/assets/{index-nqed3oum.js → index-h00usru2.js} +3 -3
  210. package/static/dist/assets/index-n73ijkv2.css +2 -0
  211. package/static/dist/assets/{info-A6RAGUB7-hjcwjami.js → info-A6RAGUB7-ndddhhhh.js} +1 -1
  212. package/static/dist/assets/infoDiagram-27XIBGKW-ccxigcpf.js +2 -0
  213. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-bxak4mwu.js → ishikawaDiagram-5VMMS53U-le0z9vtc.js} +1 -1
  214. package/static/dist/assets/{journeyDiagram-3NMN7TZE-khmihclr.js → journeyDiagram-3NMN7TZE-ivsmb26o.js} +1 -1
  215. package/static/dist/assets/{kanban-definition-UXKFOSKX-h1r32wdx.js → kanban-definition-UXKFOSKX-h6n2awt0.js} +1 -1
  216. package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +1 -0
  217. package/static/dist/assets/{line-friyjof3.js → line-dmzlfe0u.js} +1 -1
  218. package/static/dist/assets/link-budeg5t9.js +1 -0
  219. package/static/dist/assets/login-kjhasvp8.js +1 -0
  220. package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +1 -0
  221. package/static/dist/assets/{mermaid-parser.core-f1gvpwrt.js → mermaid-parser.core-gwyd5bnq.js} +3 -3
  222. package/static/dist/assets/{mindmap-definition-YA3MSWOX-mt2oxwue.js → mindmap-definition-YA3MSWOX-l68gg5h7.js} +1 -1
  223. package/static/dist/assets/onboarding-cdfrk0pj.js +1 -0
  224. package/static/dist/assets/{packet-AYTQ26CC-jik4ixlf.js → packet-AYTQ26CC-gpv06065.js} +1 -1
  225. package/static/dist/assets/{pegDiagram-XKGWAZYB-erxlv6cc.js → pegDiagram-XKGWAZYB-e52vs23u.js} +1 -1
  226. package/static/dist/assets/{pie-WAS4IAKB-mnkoldfl.js → pie-WAS4IAKB-l6m7nz5s.js} +1 -1
  227. package/static/dist/assets/{pieDiagram-E7YTZNPT-i7lwrada.js → pieDiagram-E7YTZNPT-b4is36bq.js} +3 -3
  228. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-fr8ejdoy.js → quadrantDiagram-AXDQQJYC-m8vt2788.js} +1 -1
  229. package/static/dist/assets/{radar-RG4KPBEZ-otarjte2.js → radar-RG4KPBEZ-nxbrhiov.js} +1 -1
  230. package/static/dist/assets/{railroad-74A4TZTK-lw362oto.js → railroad-74A4TZTK-222mu9hi.js} +1 -1
  231. package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +1 -0
  232. package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +1 -0
  233. package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +1 -0
  234. package/static/dist/assets/{railroadDiagram-O6MQD6OU-esyn75pw.js → railroadDiagram-O6MQD6OU-o8lldfzv.js} +1 -1
  235. package/static/dist/assets/{refresh-cw-k66keho6.js → refresh-cw-kbcje1uy.js} +1 -1
  236. package/static/dist/assets/{requirementDiagram-BXWQKSXE-cyhi5u29.js → requirementDiagram-BXWQKSXE-ovyq38kv.js} +1 -1
  237. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-jljpbo0h.js → sankeyDiagram-P5KCCOFB-ou7lznor.js} +1 -1
  238. package/static/dist/assets/{saved-do49si3v.js → saved-bazmcup9.js} +1 -1
  239. package/static/dist/assets/search-jen74z4u.js +1 -0
  240. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fz0dhlu7.js → sequenceDiagram-WJ2MYXX4-ovckdzf2.js} +1 -1
  241. package/static/dist/assets/{settings_section_extensions_title-b7lojn0o.js → settings_section_extensions_title-ertwwhlx.js} +1 -1
  242. package/static/dist/assets/share._token-dan9hgvj.js +2 -0
  243. package/static/dist/assets/{sliders-horizontal-c4wya4dy.js → sliders-horizontal-f9cb3lh3.js} +1 -1
  244. package/static/dist/assets/{stateDiagram-D77RDMKH-kdnithdx.js → stateDiagram-D77RDMKH-fcrexkkv.js} +1 -1
  245. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +1 -0
  246. package/static/dist/assets/{swimlanes-42K2YHIH-esd6gmz3.js → swimlanes-42K2YHIH-f7usu8fw.js} +1 -1
  247. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +8 -0
  248. package/static/dist/assets/{timeline-definition-24CTP7MA-g0qygccv.js → timeline-definition-24CTP7MA-gp1hvz9f.js} +1 -1
  249. package/static/dist/assets/todoStore-eqtid4r4.js +1 -0
  250. package/static/dist/assets/{treeView-Q6P3EWNA-ndchzkuu.js → treeView-Q6P3EWNA-gb9gf11u.js} +1 -1
  251. package/static/dist/assets/{treemap-WGGIJYW6-hwjxj0zz.js → treemap-WGGIJYW6-nuvfnvvi.js} +1 -1
  252. package/static/dist/assets/{untitled-mbr6tuhu.js → untitled-htelhc68.js} +1 -1
  253. package/static/dist/assets/useForm-h2t0u14e.js +1 -0
  254. package/static/dist/assets/useLocale-hcsb84pj.js +1 -0
  255. package/static/dist/assets/useNavigate-nwy70ztj.js +1 -0
  256. package/static/dist/assets/{useParams-lo9u33d1.js → useParams-dqd64jv7.js} +1 -1
  257. package/static/dist/assets/{useSearch-lq1mr090.js → useSearch-j30pvsy7.js} +1 -1
  258. package/static/dist/assets/{vennDiagram-4TSXK5OY-gubovd1r.js → vennDiagram-4TSXK5OY-fzchbzaj.js} +1 -1
  259. package/static/dist/assets/{wardley-WFR3VGLG-ir3om65j.js → wardley-WFR3VGLG-m47mhfbh.js} +1 -1
  260. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ce0or1ie.js → wardleyDiagram-VM6X3IG4-odg6ct1z.js} +2 -2
  261. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dexylcjg.js → xychartDiagram-S5SC5T6Z-mrhms5um.js} +1 -1
  262. package/static/dist/index.html +1 -1
  263. package/static/dist/sw.js +1 -1
  264. package/static/dist/assets/AppShell-orat1s43.js +0 -1
  265. package/static/dist/assets/ChatPage-ojhka3hy.js +0 -22
  266. package/static/dist/assets/HubRoute-gxgw1t33.js +0 -2
  267. package/static/dist/assets/RightPanel-c66ani1s.js +0 -1
  268. package/static/dist/assets/Select-fswf66up.js +0 -1
  269. package/static/dist/assets/SessionListPanel-goh5mtt8.js +0 -1
  270. package/static/dist/assets/SettingsLayout-n5d4wq4m.js +0 -1
  271. package/static/dist/assets/States-kxyu50at.js +0 -1
  272. package/static/dist/assets/Toaster-6zmbcqgg.js +0 -1
  273. package/static/dist/assets/_app-fowyruiu.js +0 -1
  274. package/static/dist/assets/_app.ext._extensionId-nvjyrwhy.js +0 -1
  275. package/static/dist/assets/_app.index-c9zv4y7w.js +0 -1
  276. package/static/dist/assets/_app.insights-jf12nofo.js +0 -1
  277. package/static/dist/assets/_app.kanban-lri5a4yu.js +0 -1
  278. package/static/dist/assets/_app.logs-md1p0zqo.js +0 -1
  279. package/static/dist/assets/_app.memory-bzvf2e28.js +0 -1
  280. package/static/dist/assets/_app.profiles-jpg7zmbn.js +0 -1
  281. package/static/dist/assets/_app.settings-mpuwlx4j.js +0 -1
  282. package/static/dist/assets/_app.settings._section-knz8rwoj.js +0 -1
  283. package/static/dist/assets/_app.skills-fwu1dkir.js +0 -1
  284. package/static/dist/assets/_app.tasks-h9gphtl3.js +0 -2
  285. package/static/dist/assets/_app.todos-hd1dnvrh.js +0 -1
  286. package/static/dist/assets/_app.workspaces-l8toyn5f.js +0 -1
  287. package/static/dist/assets/architecture-7GRP2DOG-cavnusaw.js +0 -1
  288. package/static/dist/assets/architectureDiagram-5GKGNRK7-k5a7v36n.js +0 -36
  289. package/static/dist/assets/channel-gozy3dp3.js +0 -1
  290. package/static/dist/assets/classDiagram-ZZMXUADV-n0vs2f2q.js +0 -1
  291. package/static/dist/assets/classDiagram-v2-VYDZK3BY-n0vs2f2q.js +0 -1
  292. package/static/dist/assets/endpoints-k8agnro8.js +0 -9
  293. package/static/dist/assets/eventmodeling-NTZA5JFV-j1y2ekec.js +0 -1
  294. package/static/dist/assets/flowDiagram-HODETNUW-g2cvcbm5.js +0 -1
  295. package/static/dist/assets/index-nfy0ezs6.css +0 -2
  296. package/static/dist/assets/infoDiagram-27XIBGKW-fm06rj36.js +0 -2
  297. package/static/dist/assets/lazyRouteComponent-b2eg0fws.js +0 -1
  298. package/static/dist/assets/link-m90ahx3n.js +0 -1
  299. package/static/dist/assets/login-bw7m2fzo.js +0 -1
  300. package/static/dist/assets/mermaid-HWGCJPDP-njwcl62n.js +0 -1
  301. package/static/dist/assets/onboarding-ret37j1e.js +0 -1
  302. package/static/dist/assets/railroad-abnf-HS5TGJTU-m741owar.js +0 -1
  303. package/static/dist/assets/railroad-ebnf-LZEXJU2U-noa4fbia.js +0 -1
  304. package/static/dist/assets/railroad-peg-WCYAUIDC-o4bpw4nb.js +0 -1
  305. package/static/dist/assets/search-j5ki6a9o.js +0 -1
  306. package/static/dist/assets/share._token-kg8w647b.js +0 -2
  307. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-k2wg2645.js +0 -1
  308. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n5kvjtgr.js +0 -8
  309. package/static/dist/assets/tabBuild-bjmyvmn5.js +0 -56
  310. package/static/dist/assets/todoStore-kjk5l0sy.js +0 -1
  311. package/static/dist/assets/useForm-ocxu460f.js +0 -1
  312. package/static/dist/assets/useLocale-fzy5jhg6.js +0 -1
package/dist/redact.js CHANGED
@@ -4,9 +4,10 @@ import { snapshotArgs, toolArgs, toolDisplay, toolName } from './sessions/tool-d
4
4
  * Credential redaction and the public session projection (Python
5
5
  * `api/helpers.py`). API responses are a hard boundary: transcript-bearing
6
6
  * fields are masked with the local pattern set, and private replay aliases
7
- * are stripped at their schema positions. The Agent's broader redactor is
8
- * not consulted (it lives in-process in Python); the local patterns mirror
9
- * its known credential prefixes.
7
+ * are stripped at their schema positions. The Agent's redactor
8
+ * (`agent/redact.py`, `redact_sensitive_text(force=True)`) is not consulted
9
+ * at runtime; its pattern families are ported here and held to parity by
10
+ * the `Agent redactor parity` tests.
10
11
  */
11
12
  const CRED_RE = new RegExp('(?<![A-Za-z0-9_-])(' +
12
13
  'sk-[A-Za-z0-9_-]{10,}' +
@@ -16,6 +17,7 @@ const CRED_RE = new RegExp('(?<![A-Za-z0-9_-])(' +
16
17
  '|ghu_[A-Za-z0-9]{10,}' +
17
18
  '|ghs_[A-Za-z0-9]{10,}' +
18
19
  '|ghr_[A-Za-z0-9]{10,}' +
20
+ '|xapp-\\d+-[A-Za-z0-9-]{10,}' +
19
21
  '|xox[baprs]-[A-Za-z0-9-]{10,}' +
20
22
  '|AIza[A-Za-z0-9_-]{30,}' +
21
23
  '|pplx-[A-Za-z0-9]{10,}' +
@@ -44,6 +46,14 @@ const CRED_RE = new RegExp('(?<![A-Za-z0-9_-])(' +
44
46
  '|hsk-[A-Za-z0-9]{10,}' +
45
47
  '|mem0_[A-Za-z0-9]{10,}' +
46
48
  '|brv_[A-Za-z0-9]{10,}' +
49
+ '|xai-[A-Za-z0-9]{30,}' +
50
+ '|ntn_[A-Za-z0-9]{10,}' +
51
+ '|fw[-_][A-Za-z0-9]{30,}' +
52
+ '|fpk_[A-Za-z0-9]{30,}' +
53
+ '|(?:glpat|gloas|gldt|glcbt|glptt|glft|glimt|glagent|glsoat|glffct|glwt)-[A-Za-z0-9_-]{10,}' +
54
+ '|glrtr?-[A-Za-z0-9_.-]{10,}' +
55
+ '|GR1348941[A-Za-z0-9_-]{10,}' +
56
+ '|pk-lf-[A-Za-z0-9-]{8,}' +
47
57
  ')(?![A-Za-z0-9_-])', 'g');
48
58
  /** One `name=value` auth parameter: an escaped-quoted, quoted or bare value. */
49
59
  const AUTH_PARAM = String.raw `[A-Za-z0-9_-]+=(?:\\"(?:[^"\\\r\n]|\\[^"])*\\"|"(?:[^"\\\r\n]|\\.)*"|'[^'\r\n]*'|[^\s,"'\\]*)`;
@@ -54,8 +64,8 @@ const AUTH_PARAM = String.raw `[A-Za-z0-9_-]+=(?:\\"(?:[^"\\\r\n]|\\[^"])*\\"|"(
54
64
  * A parameterized credential (`Digest username="bob", response="..."`, `Credential=..., Signature=...`) is masked whole.
55
65
  */
56
66
  const AUTH_HDR_RE = new RegExp(String.raw `(Authorization:\s*(?:(?!\*+\s)[A-Za-z0-9!#$%&*+.^_|~-]+\s+(?=[^\s,\])]))?)(${AUTH_PARAM}(?:\s*,\s*${AUTH_PARAM})*|[^\s,\])][^\s'",\])]*)`, 'gi');
57
- /** A JSON Web Token anywhere (`eyJ<header>.<payload>.<signature>`). */
58
- const JWT_RE = /\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}/g;
67
+ /** A JSON Web Token anywhere (`eyJ<header>.<payload>.<signature>`), or its header alone or with its payload. */
68
+ const JWT_RE = /\beyJ(?:[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}|[A-Za-z0-9_-]{10,}(?:\.[A-Za-z0-9_=-]{4,}){0,2})/g;
59
69
  /** A bearer credential in any header or text (`X-Auth: Bearer ...`); `AUTH_HDR_RE` owns the `Authorization:` header. */
60
70
  const BEARER_RE = /((?<!Authorization:\s{0,8})\bBearer\s+)([^\s,\])][^\s'",\])]*)/gi;
61
71
  /** One shell-quoted piece, which may span lines: `'...'`, `"..."` (with backslash escapes), bash `$'...'` / `$"..."`, or JSON escaped inside a shell string (`\"...\"`). */
@@ -100,6 +110,36 @@ const percentDecode = (text) => text.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => S
100
110
  const LISTED_FLAG_RE = /(["'])(-{1,2})([A-Za-z0-9_][A-Za-z0-9_.-]*)\1(\s*,\s*)(?:(["'])((?:\\.|(?!\5)[^\\])*)\5|(\[[^[\]]*\]|\{[^{}]*\}|[^\s,\])}'"[{]+))/g;
101
111
  const EMBEDDED_AWS_RE = /(?:AKIA|ASIA)[A-Z0-9]{16}/g;
102
112
  const ENV_RE = /([A-Z0-9_]{0,50}(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)[A-Z0-9_]{0,50})\s*=\s*(['"]?)(\S+)\2/g;
113
+ /**
114
+ * The Agent's other env names: an all-caps one holding `KEY`, `PASS` or `PW` (`OPENAI_KEY`, `DB_PW`), or a `name_key` /
115
+ * `name_pass` / `name_pw` in any case, URL query parameters included (the Agent skips any text with a URL). One attempt per
116
+ * identifier, which must hold a keyword, so the scan stays linear on long runs (`PWPWPW…`). The value is the shell word
117
+ * after `=`, read by `shellWordEnd` (quotes, escapes, `$'…'`).
118
+ */
119
+ const ENV_SUFFIX_RE = /(?<![A-Za-z0-9_])((?=[A-Z0-9_]*(?:KEY|PASS|PW))[A-Z0-9_]+|[A-Za-z0-9_]+_(?:[Kk][Ee][Yy]|[Pp][Aa][Ss][Ss]|[Pp][Ww])(?![A-Za-z0-9_]))[ \t]*=[ \t]*/g;
120
+ /** The prefilter's view of `ENV_SUFFIX_RE`. */
121
+ const ENV_SUFFIX_TEST_RE = new RegExp(ENV_SUFFIX_RE.source);
122
+ /**
123
+ * The Agent's env-name keywords, as whole name words (`DB_PW`, `GITHUB_TOKEN`), never inside a word (`KEYBOARD`, `PASSAGE`,
124
+ * `TOKENIZER`, `AUTHOR_KEY`'s `AUTH`).
125
+ */
126
+ const ENV_KEYWORD_RE = /(?:^|[^A-Za-z])(?:(?:api|auth|access|refresh|session|secret)[ _.-]?(?:key|token)|token|secret|passwd|password|pass|pw|credential|auth|key)s?(?![A-Za-z])/i;
127
+ /**
128
+ * Env names whose value is a credential whatever its shape, as whole name words (`DB_PASS`, not `COMPASS_KEY` or
129
+ * `AUTHOR_KEY`); a bare `KEY` needs an opaque value (`SORT_KEY=name` stays).
130
+ */
131
+ const ENV_STRONG_NAME_RE = /(?:^|[^A-Za-z])(?:(?:api|auth|access|refresh|session|id|bearer)[ _.-]?(?:key|token)|key[ _.-]?material|secret|passwd|password|pass|pw|credentials?|auth|bearer)s?(?![A-Za-z])/i;
132
+ /**
133
+ * The Agent's env-assignment gate: a name holding a whole keyword whose value is a credential, because the name is strong
134
+ * or the value opaque (`GITHUB_TOKEN=abc` and `MAX_TOKENS=100` stay). Env lookups (`os.getenv(…)`) name a variable.
135
+ */
136
+ const isEnvCredential = (key, value) => !/^(?:os\.(?:getenv|environ)|process\.env|\$ENV\{)/.test(value) && ENV_KEYWORD_RE.test(key) && (ENV_STRONG_NAME_RE.test(key) || looksOpaque(value));
137
+ /** The Agent's `_looks_like_opaque_credential`: a value shaped like a generated secret rather than a word. */
138
+ function looksOpaque(value) {
139
+ if (value === '***' || /^[A-Fa-f0-9]{16,}$/.test(value) || /^[A-Za-z0-9_./+=-]{20,}$/.test(value))
140
+ return true;
141
+ return value.length >= 12 && [/[a-z]/, /[A-Z]/, /[0-9]/].filter((re) => re.test(value)).length >= 2;
142
+ }
103
143
  /**
104
144
  * `scheme://user:secret@host` (database and basic-auth URLs): the password is masked, the user and host stay. The user
105
145
  * and password may be assembled from quoted and escaped shell pieces (`bob:hun'ter2'@`), and every delimiter may be
@@ -171,12 +211,211 @@ const USER_FLAG_RE = /(?<![A-Za-z0-9-])(?:-[uU][ \t]*|--(?:user|proxy-u(?:s(?:e(
171
211
  /** The prefilter's view of `USER_FLAG_RE`. */
172
212
  const USER_FLAG_TEST_RE = new RegExp(USER_FLAG_RE.source);
173
213
  const QUERY_KEY_RE = /([?&]key=)([^\s"'&#]+)/gi;
214
+ /** A Telegram bot token (`bot<id>:<secret>`): the id stays. */
215
+ const TELEGRAM_TOKEN_RE = /(bot)?(\d{8,}):([-A-Za-z0-9_]{30,})/g;
216
+ /** An E.164 phone number, masked to its first and last digits; never inside a word or encoded data (`ab+1234567`). */
217
+ const PHONE_RE = /(?<![A-Za-z0-9])\+[1-9]\d{6,14}(?![A-Za-z0-9])/g;
218
+ /**
219
+ * A bare token as URL userinfo (`https://TOKEN@github.com`, `ssh://…@`): no `user:` part, at least 8 characters. Round-trip
220
+ * URLs carry tokens in the query, so a bare userinfo credential is never one.
221
+ */
222
+ const URL_BARE_TOKEN_RE = /((?:https?|wss?|git|ssh|ftps?|sftp):\/\/)([^\s:@/?#]{8,})(?=@\S)/gi;
223
+ /** C0, DEL, C1 and zero-width characters that can split a token body (`ghp_abc\x1bdef`, `sk-abc\u200bdef`). */
224
+ const CONTROL_CHAR_RE = /[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202f\u2060\ufeff]/;
225
+ const CONTROL_CHARS_RE = new RegExp(CONTROL_CHAR_RE.source, 'g');
226
+ /** C0 controls a terminal runs inside an escape sequence without ending it (all but CAN, SUB and ESC), and DEL. */
227
+ const SEQ_C0 = String.raw `\x00-\x17\x19\x1c-\x1f\x7f`;
228
+ /** Where a sequence ends early: CAN or SUB cancels it, and ESC, a C1 control or the end of the text interrupts it. */
229
+ const SEQ_CUT = String.raw `(?:[\x18\x1a]|(?=[\x1b\x80-\x9f]|$))`;
230
+ const SEQ_ESC = String.raw `\x1b[${SEQ_C0}]*`;
231
+ /** Parameters and intermediates in any order: a parameter after an intermediate makes a terminal ignore the CSI. */
232
+ const SEQ_CSI = String.raw `(?:${SEQ_ESC}\[|\x9b)[ -?${SEQ_C0}]*(?:[@-~]|${SEQ_CUT})`;
233
+ const SEQ_NF = String.raw `${SEQ_ESC}[ -/][ -/${SEQ_C0}]*(?:[0-~]|${SEQ_CUT})`;
234
+ const SEQ_SINGLE = String.raw `${SEQ_ESC}[0-OQ-WYZ\\\x60-~]`;
235
+ /**
236
+ * An ECMA-48 escape sequence as a terminal parses it, or one control or zero-width character. The sequences, 7-bit
237
+ * `ESC …` or the C1 code point: CSI (`ESC [` … final byte), the strings OSC (… BEL or ST) and DCS, SOS, PM and APC
238
+ * (… ST), a character-set selection (`ESC ( B`), and a single-character escape (`ESC 7`); each also ends at `SEQ_CUT`.
239
+ * No part of a sequence can start the part after it, which keeps the scan linear.
240
+ */
241
+ const ANSI_GAPS_RE = new RegExp([
242
+ SEQ_CSI,
243
+ String.raw `(?:${SEQ_ESC}\]|\x9d)[^\x07\x18\x1a\x1b\x80-\x9f]*(?:\x07|\x1b\\|\x9c|${SEQ_CUT})`,
244
+ String.raw `(?:${SEQ_ESC}[PX^_]|[\x90\x98\x9e\x9f])[^\x18\x1a\x1b\x80-\x9f]*(?:\x1b\\|\x9c|${SEQ_CUT})`,
245
+ SEQ_NF,
246
+ SEQ_SINGLE,
247
+ CONTROL_CHAR_RE.source,
248
+ ].join('|'), 'g');
249
+ /** `ANSI_GAPS_RE` with a string's payload kept: only its opener and terminator are gaps. */
250
+ const ANSI_PAYLOAD_GAPS_RE = new RegExp([SEQ_CSI, String.raw `${SEQ_ESC}[\]PX^_]`, SEQ_NF, SEQ_SINGLE, CONTROL_CHAR_RE.source].join('|'), 'g');
251
+ /**
252
+ * The views a split token is matched on: without whole escape sequences, without them but keeping string payloads (a
253
+ * terminal title can hold a token), and without control characters only (an escape's final byte can be a token's).
254
+ */
255
+ const SPLIT_VIEWS = [ANSI_GAPS_RE, ANSI_PAYLOAD_GAPS_RE, CONTROL_CHARS_RE];
256
+ const ANSI_GAP_AT_RE = new RegExp(ANSI_GAPS_RE.source, 'y');
257
+ const TOKEN_CHAR_RE = /[A-Za-z0-9_-]/;
258
+ /** A string sequence's opener, with an OSC's command number (`8;`), which is no payload. */
259
+ const STRING_OPENER_RE = new RegExp(String.raw `^(?:(?:${SEQ_ESC}\]|\x9d)(?:\d*;)?|${SEQ_ESC}[PX^_]|[\x90\x98\x9e\x9f])`);
260
+ const PAYLOAD_RUN_CAP = 65_536;
261
+ const CRED_TEST_RE = new RegExp(CRED_RE.source);
262
+ /** `CRED_RE`'s prefix and body from a position, without its boundaries (a split token checks the original ones). */
263
+ const CRED_RUN_RE = new RegExp(CRED_RE.source.replace(/^\(\?<!\[A-Za-z0-9_-\]\)/, '').replace(/\(\?!\[A-Za-z0-9_-\]\)$/, ''), 'y');
264
+ const SPLIT_TOKEN_CAP = 1024;
265
+ /** Where a `CRED_RE` prefix and body start at a boundary, however they end. */
266
+ const CRED_START_RE = new RegExp(CRED_RE.source.replace(/\(\?!\[A-Za-z0-9_-\]\)$/, ''), 'g');
267
+ const CRED_WHOLE_RE = new RegExp(`^${CRED_RE.source}$`);
268
+ /** `CRED_START_RE` and `CRED_RUN_RE` without their body: a credential's prefix, however little of its body follows. */
269
+ const CRED_PREFIX_RE = new RegExp(CRED_START_RE.source.replace(/\{\d+,?\}/g, '{0}'), 'g');
270
+ const CRED_PREFIX_RUN_RE = new RegExp(CRED_RUN_RE.source.replace(/\{\d+,?\}/g, '{0}'), 'y');
271
+ /** A URL query parameter's value, up to the next `&` or `#`. */
272
+ const QUERY_VALUE_RE = /[^&#]*/y;
174
273
  const PRIVKEY_RE = /-----BEGIN[A-Z ]*PRIVATE KEY-----[\s\S]*?-----END[A-Z ]*PRIVATE KEY-----/g;
175
274
  /** A private key whose end marker is missing (a display cap cut it off): masked to the end of the text. */
176
275
  const PRIVKEY_OPEN_RE = /-----BEGIN[A-Z ]*PRIVATE KEY-----[\s\S]*$/;
177
276
  const CODE_ENV_KEY_LITERAL_RE = /([A-Z0-9_]{0,50}(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)[A-Z0-9_]{0,50}=)(["'][)\]:,]+|[)\]:,]+)/y;
178
277
  const ENV_KEY_PREFIX_RE = /([A-Z0-9_]{0,50}(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)[A-Z0-9_]{0,50}=)/g;
179
278
  const REDACTED_ENV_VALUE_RE = /(?:\*{3,}|[A-Za-z0-9][A-Za-z0-9_.:/+-]{0,32}\.\.\.[A-Za-z0-9_.:/+-]{1,16})/y;
279
+ /**
280
+ * Where a credential split by control or zero-width characters (`ghp_abc\x1bdef`, `ghp_abc\ndef…`) starting at `i` of the
281
+ * stripped text ends there, or -1. The run of its prefix and body is read once (capped), then the longest candidate is
282
+ * taken that ends at a piece boundary of the original, is a whole credential, is not the name of a `KEY=` that follows,
283
+ * and never reaches a line holding a whole token itself (that line is the prefix pass's and joins nothing).
284
+ */
285
+ function splitTokenEnd(text, stripped, kept, i) {
286
+ CRED_RUN_RE.lastIndex = 0;
287
+ // ponytail: a split credential longer than SPLIT_TOKEN_CAP is cut at the cap; raise it if longer tokens appear.
288
+ const run = CRED_RUN_RE.exec(stripped.slice(i, i + SPLIT_TOKEN_CAP))?.[0];
289
+ if (!run)
290
+ return -1;
291
+ const start = kept[i];
292
+ let limit = kept[i + run.length - 1] + 1;
293
+ for (let lineStart = start; lineStart < limit;) {
294
+ const lineEnd = text.slice(lineStart, limit).search(/[\r\n]/);
295
+ const end = lineEnd === -1 ? limit : lineStart + lineEnd;
296
+ if (CRED_TEST_RE.test(text.slice(lineStart, end))) {
297
+ limit = lineStart === start ? end : lineStart;
298
+ break;
299
+ }
300
+ if (lineEnd === -1)
301
+ break;
302
+ lineStart = end + 1;
303
+ }
304
+ for (let e = i + run.length; e > i; e -= 1) {
305
+ const atBoundary = e === stripped.length || kept[e] - kept[e - 1] > 1 || (e === i + run.length && !/[A-Za-z0-9_-]/.test(stripped[e]));
306
+ const originalEnd = kept[e - 1] + 1;
307
+ if (!atBoundary || originalEnd > limit || !CRED_WHOLE_RE.test(stripped.slice(i, e)) || /^[ \t]*=/.test(text.slice(originalEnd, originalEnd + 64)))
308
+ continue;
309
+ return e;
310
+ }
311
+ return -1;
312
+ }
313
+ /**
314
+ * The original spans, and the joined text, of prefixed credentials whose body a gap of one of `SPLIT_VIEWS` splits,
315
+ * matched on the text without those gaps, plus `payloadRunSpan`'s from each prefix. A token starts at a boundary of the
316
+ * stripped text, or where a stripped gap hid the original one (`note\nghp_…`); one starting inside a token already
317
+ * masked is part of it.
318
+ */
319
+ function splitTokenSpans(text, gaps) {
320
+ const stripped = text.replace(gaps, '');
321
+ if (stripped.length === text.length)
322
+ return [];
323
+ // The original index of each kept character.
324
+ const kept = [];
325
+ let at = 0;
326
+ for (const m of text.matchAll(gaps)) {
327
+ while (at < m.index)
328
+ kept.push(at++);
329
+ at += m[0].length;
330
+ }
331
+ while (at < text.length)
332
+ kept.push(at++);
333
+ const starts = [...stripped.matchAll(CRED_START_RE)].map((m) => m.index);
334
+ const prefixStarts = [...stripped.matchAll(CRED_PREFIX_RE)].map((m) => m.index);
335
+ for (let i = 1; i < kept.length; i += 1) {
336
+ if (kept[i] - kept[i - 1] <= 1 || !TOKEN_CHAR_RE.test(stripped[i - 1]))
337
+ continue;
338
+ starts.push(i);
339
+ CRED_PREFIX_RUN_RE.lastIndex = i;
340
+ if (CRED_PREFIX_RUN_RE.test(stripped))
341
+ prefixStarts.push(i);
342
+ }
343
+ const spans = [];
344
+ let scanned = 0;
345
+ for (const i of starts.sort((a, b) => a - b)) {
346
+ if (i < scanned)
347
+ continue;
348
+ const e = splitTokenEnd(text, stripped, kept, i);
349
+ if (e === -1)
350
+ continue;
351
+ spans.push([kept[i], kept[e - 1] + 1, stripped.slice(i, e)]);
352
+ scanned = e;
353
+ }
354
+ let walked = 0;
355
+ for (const i of prefixStarts.sort((a, b) => a - b)) {
356
+ if (kept[i] < walked)
357
+ continue;
358
+ const [span, reached] = payloadRunSpan(text, kept[i]);
359
+ if (span)
360
+ spans.push(span);
361
+ walked = reached;
362
+ }
363
+ return spans;
364
+ }
365
+ /**
366
+ * Fail-closed for a token split across string payloads: from a credential prefix at `start`, the span through the run of
367
+ * token characters and whole escape sequences after it, when a string payload in the run holds token characters, and
368
+ * where the run stopped. Any subset of payloads can hold a token's pieces and the rest junk
369
+ * (`\x1b]0;ghp_ab\x1b]0;x_y\x07cd…`), so the run is masked whole rather than rebuilt.
370
+ */
371
+ function payloadRunSpan(text, start) {
372
+ let token = '';
373
+ let end = start;
374
+ let hidden = false;
375
+ let k = start;
376
+ // ponytail: a run longer than PAYLOAD_RUN_CAP is masked in pieces; raise it if longer hidden tokens appear.
377
+ while (k < text.length && k - start < PAYLOAD_RUN_CAP) {
378
+ ANSI_GAP_AT_RE.lastIndex = k;
379
+ const gap = ANSI_GAP_AT_RE.exec(text)?.[0];
380
+ if (gap) {
381
+ const from = STRING_OPENER_RE.exec(gap)?.[0].length ?? gap.length;
382
+ for (let j = from; j < gap.length; j += 1) {
383
+ if (!TOKEN_CHAR_RE.test(gap[j]))
384
+ continue;
385
+ token += gap[j];
386
+ end = k + j + 1;
387
+ hidden = true;
388
+ }
389
+ k += gap.length;
390
+ }
391
+ else if (TOKEN_CHAR_RE.test(text[k])) {
392
+ token += text[k];
393
+ end = k += 1;
394
+ }
395
+ else
396
+ break;
397
+ }
398
+ return [hidden ? [start, end, token] : null, k];
399
+ }
400
+ /** Masks each union of the split-token spans every view finds, so no view masks a piece of a token another one joins. */
401
+ function maskSplitTokens(text) {
402
+ const spans = SPLIT_VIEWS.flatMap((gaps) => splitTokenSpans(text, gaps)).sort((a, b) => a[0] - b[0]);
403
+ let out = '';
404
+ let last = 0;
405
+ for (let k = 0; k < spans.length;) {
406
+ const start = spans[k][0];
407
+ let [, end, token] = spans[k];
408
+ for (k += 1; k < spans.length && spans[k][0] < end; k += 1) {
409
+ const [, nextEnd, nextToken] = spans[k];
410
+ end = Math.max(end, nextEnd);
411
+ if (nextToken.length > token.length)
412
+ token = nextToken;
413
+ }
414
+ out += text.slice(last, start) + mask(token);
415
+ last = end;
416
+ }
417
+ return out + text.slice(last);
418
+ }
180
419
  function mask(token) {
181
420
  // By code point, so a partial mask never splits a surrogate pair into invalid JSON.
182
421
  const chars = Array.from(token);
@@ -437,6 +676,38 @@ function maskShellWord(value) {
437
676
  }
438
677
  /** A shell word's content, for the "nothing to mask" checks: the inside of a single quoted piece, else the word. */
439
678
  const shellWordInner = (value) => splitQuoted(value)?.inner ?? value;
679
+ /**
680
+ * `ENV_SUFFIX_RE` assignments whose name is a credential (`DB_PASS=`, `openai_key=`). A password-class name masks any value,
681
+ * punctuation-only included; a bare `KEY` masks an opaque one. A plain value keeps the Agent's partial mask; a quoted,
682
+ * escaped or composed one is masked whole. Env lookups (`os.getenv(…)`) and masked values stay.
683
+ */
684
+ function redactEnvSuffixes(text) {
685
+ let out = '';
686
+ let last = 0;
687
+ const quoteAt = quoteTracker(text);
688
+ const closeOf = enclosingClose(text);
689
+ // A skipped value does not move the scan past its word: a later match inside that word reuses its end, and a value is
690
+ // judged by its head, so many assignments inside one long word (`a_key=x\ a_key=x\ …`) still scan it once.
691
+ let wordEnd = -1;
692
+ ENV_SUFFIX_RE.lastIndex = 0;
693
+ for (let m = ENV_SUFFIX_RE.exec(text); m; m = ENV_SUFFIX_RE.exec(text)) {
694
+ const key = m[1];
695
+ const valueStart = m.index + m[0].length;
696
+ wordEnd = valueStart < wordEnd ? wordEnd : shellWordEnd(text, valueStart, quoteAt(valueStart), closeOf);
697
+ // A URL query parameter's value ends at the next `&` or `#`, as in `redactCredentialParams`.
698
+ QUERY_VALUE_RE.lastIndex = valueStart;
699
+ const valueEnd = /[?&]/.test(text[m.index - 1] ?? '') ? Math.min(wordEnd, valueStart + QUERY_VALUE_RE.exec(text)[0].length) : wordEnd;
700
+ const value = text.slice(valueStart, valueEnd);
701
+ const head = value.slice(0, 128);
702
+ const inner = shellWordInner(head);
703
+ if (!inner.trim() || inner === '***' || !isEnvCredential(key, shellDequote(head)))
704
+ continue;
705
+ out += text.slice(last, valueStart) + (/^[^\s'"\\$`]+$/.test(value) ? mask(value) : maskShellWord(value));
706
+ last = valueEnd;
707
+ ENV_SUFFIX_RE.lastIndex = Math.max(valueEnd, ENV_SUFFIX_RE.lastIndex);
708
+ }
709
+ return out + text.slice(last);
710
+ }
440
711
  /** Credential parameters (`CRED_PARAM_RE`) with their whole shell-word value fully masked. */
441
712
  function redactCredentialParams(text) {
442
713
  let out = '';
@@ -565,6 +836,198 @@ function redactUserFlags(text) {
565
836
  }
566
837
  return out + text.slice(last);
567
838
  }
839
+ const REGISTRY_LOGIN = { separate: ['-p'], attached: ['-p'], login: true, switches: ['-D', '--debug', '--tls', '--tlsverify', '-r', '--remote', '--syslog'] };
840
+ const MYSQL = { attached: ['-p'] };
841
+ const COMMAND_FLAGS = {
842
+ docker: REGISTRY_LOGIN, podman: REGISTRY_LOGIN, buildah: REGISTRY_LOGIN, nerdctl: REGISTRY_LOGIN, skopeo: REGISTRY_LOGIN, oras: REGISTRY_LOGIN, helm: REGISTRY_LOGIN,
843
+ mysql: MYSQL, mysqladmin: MYSQL, mysqldump: MYSQL, mysqlimport: MYSQL, mysqlshow: MYSQL, mysqlcheck: MYSQL, mysqlslap: MYSQL, mysql_upgrade: MYSQL, mysqlbinlog: MYSQL, mysqlsh: MYSQL,
844
+ mariadb: MYSQL, 'mariadb-admin': MYSQL, 'mariadb-dump': MYSQL, 'mariadb-import': MYSQL, 'mariadb-show': MYSQL, 'mariadb-check': MYSQL, 'mariadb-slap': MYSQL, 'mariadb-upgrade': MYSQL, 'mariadb-binlog': MYSQL,
845
+ sshpass: { separate: ['-p'], attached: ['-p'], values: ['-f', '-d', '-P'], stop: true },
846
+ 'redis-cli': { separate: ['-a', '--pass'] },
847
+ smbclient: { separate: ['-U', '--user'], attached: ['-U', '--user='], percent: true },
848
+ };
849
+ /**
850
+ * A known command's name as a word: bare, after a path, a listed argv element, or composed by quotes (`do"cker"`); or a
851
+ * word the shell computes in whole or part (`$CLIENT`, `$(echo $(which mysql))`, `` `printf docker` ``, `my${EMPTY}sql`),
852
+ * which may name any of them wherever it stands (`sudo -u root $CLIENT -ppw`). A computed word is matched whole when its
853
+ * pieces are flat, so a quoted one reads as a listed element; otherwise up to its first expansion, and read from there.
854
+ */
855
+ /** A parameter, substitution or backtick piece of a word; a name is read whole, so a word splits into pieces one way only. */
856
+ const EXPANSION = String.raw `\$[A-Za-z_][A-Za-z0-9_]*(?![A-Za-z0-9_])|\$\{[^{}\n]*\}|\$\([^()\n]*\)|\x60[^\x60\n]*\x60`;
857
+ const COMMAND_FLAG_RE = new RegExp(String.raw `(?<![^\s;&|()\x60'"/,[\\])(?:(?:${Object.keys(COMMAND_FLAGS).map((name) => name.replaceAll(/(?<=.)(?=.)/g, String.raw `["'\\]*`)).join('|')}|[\w./-]*(?:${EXPANSION})(?:[\w./-]|${EXPANSION})*)(?=[\s;&|)'",\]]|$)|[\w./-]*(?=\$[({A-Za-z_]|\x60))`, 'g');
858
+ /** Every command's flags, for an executable the shell computes. */
859
+ const ALL_COMMAND_FLAGS = [...new Set(Object.values(COMMAND_FLAGS))];
860
+ const COMMAND_FLAG_TEST_RE = new RegExp(COMMAND_FLAG_RE.source);
861
+ /**
862
+ * For an argv (`words` as the programs receive them), the words holding a command's secret, each with the offset it starts
863
+ * at. Every known command named so far is read at once, so a wrapper's operand naming one (`sudo -u mysql docker login -p
864
+ * pw`) does not hide the command it runs.
865
+ */
866
+ function commandFlagMasks(words) {
867
+ const masks = new Map();
868
+ const states = new Map();
869
+ const secret = (flags, index, value, from) => {
870
+ if (value === undefined)
871
+ return;
872
+ const at = flags.percent ? value.indexOf('%', from) + 1 : from;
873
+ if ((at || !flags.percent) && value.slice(at) && value.slice(at) !== '***')
874
+ masks.set(index, at);
875
+ };
876
+ for (let i = 0; i < words.length; i += 1) {
877
+ const word = words[i];
878
+ for (const [flags, state] of states) {
879
+ if (i < state.next)
880
+ continue;
881
+ if (!state.active) {
882
+ // The subcommand is the first operand past the options; one right after a bare option may be its value. One the shell
883
+ // computes (`docker "$ACTION" -p pw`) may be `login`.
884
+ if (word === 'login' || /[$`]/.test(word))
885
+ state.active = true;
886
+ else if (!word.startsWith('-') && word !== 'registry' && !(/^-[^=]*$/.test(words[i - 1]) && !flags.switches?.includes(words[i - 1])))
887
+ states.delete(flags);
888
+ continue;
889
+ }
890
+ if (flags.separate?.includes(word)) {
891
+ secret(flags, i + 1, words[i + 1], 0);
892
+ state.next = i + 2;
893
+ continue;
894
+ }
895
+ const prefix = flags.attached?.find((p) => word.length > p.length && word.startsWith(p));
896
+ if (prefix !== undefined) {
897
+ secret(flags, i, word, prefix.length + (word[prefix.length] === '=' ? 1 : 0));
898
+ continue;
899
+ }
900
+ if (flags.values?.includes(word)) {
901
+ state.next = i + 2;
902
+ continue;
903
+ }
904
+ if (flags.stop && !word.startsWith('-'))
905
+ states.delete(flags);
906
+ }
907
+ const name = word.slice(word.lastIndexOf('/') + 1);
908
+ // A command named again (`docker login docker -p pw`, a host) keeps the state it is in. A word the shell computes may
909
+ // be the executable (`sudo -u root $CLIENT -ppw`), so it may be any known command.
910
+ const named = Object.hasOwn(COMMAND_FLAGS, name) ? [COMMAND_FLAGS[name]] : !word.startsWith('-') && /[$`]/.test(word) ? ALL_COMMAND_FLAGS : [];
911
+ for (const flags of named)
912
+ if (!states.has(flags))
913
+ states.set(flags, { active: !flags.login, next: i + 1 });
914
+ }
915
+ return masks;
916
+ }
917
+ /** `raw`, a shell word reading `plain`, masked from `plain`'s offset `from`; a word whose kept part is not literal is masked whole. */
918
+ function maskWordFrom(raw, plain, from) {
919
+ if (!from)
920
+ return maskShellWord(raw);
921
+ const kept = plain.slice(0, from);
922
+ const quoted = splitQuoted(raw);
923
+ if (quoted?.inner.startsWith(kept))
924
+ return `${quoted.open}${kept}***${quoted.close}`;
925
+ return raw.startsWith(kept) ? kept + maskShellWord(raw.slice(from)) : maskShellWord(raw);
926
+ }
927
+ /** A redirection operator, with its file descriptor. */
928
+ const REDIRECT_RE = /[0-9]*(?:&>>?|<<<|<<-?|<>|>>|>&|<&|>\||[<>])/y;
929
+ /**
930
+ * The words of the simple command (or `list`ed argv) at `from`, up to a shell metacharacter, the line end, a list's close
931
+ * or the close of the `enclosing` quote, and where the scan stopped. An unterminated quote ends the command with its word.
932
+ * Only a list's elements end at `,`, `]` or `}`; in a shell word they are ordinary characters (`-phunter,2`).
933
+ */
934
+ function commandWords(text, from, enclosing, list) {
935
+ const spans = [];
936
+ const [gap, close, stop] = list ? [/[ \t,<>]/, /[\n;&|()\]}]/, /[\s;&|()<>,\]}]/] : [/[ \t]/, /[\n;&|()]/, /[\s;&|()<>]/];
937
+ let i = from;
938
+ let redirect = false;
939
+ for (;;) {
940
+ while (i < text.length && gap.test(text[i]))
941
+ i += 1;
942
+ // A redirection and its target (`</dev/null`, `2> /tmp/e`, `2>&1`) are no argument: the shell removes them.
943
+ REDIRECT_RE.lastIndex = i;
944
+ if (!list && i < text.length && text[i] !== enclosing && REDIRECT_RE.test(text)) {
945
+ i = REDIRECT_RE.lastIndex;
946
+ redirect = true;
947
+ continue;
948
+ }
949
+ if (i >= text.length || text[i] === enclosing || close.test(text[i]))
950
+ return { spans, end: i };
951
+ const start = i;
952
+ while (i < text.length && text[i] !== enclosing && !stop.test(text[i])) {
953
+ const c = text[i];
954
+ if (c === '\\')
955
+ i += 2;
956
+ else if ((c === "'" || c === '"') && c !== enclosing) {
957
+ // A quote groups across newlines, and an inner quote of the other kind (`sh -c "mysql -p'a b'"`) groups for the shell
958
+ // that runs the command.
959
+ let k = i + 1;
960
+ while (k < text.length && text[k] !== c && text[k] !== enclosing)
961
+ k += c === '"' && text[k] === '\\' ? 2 : 1;
962
+ // An unterminated quote runs to the text end (or the enclosing close); its word is still read, so its secret is masked.
963
+ if (text[k] !== c) {
964
+ spans.push([start, Math.min(k, text.length)]);
965
+ return { spans, end: Math.min(k, text.length) };
966
+ }
967
+ i = k + 1;
968
+ }
969
+ else if (c === '`' || (c === '$' && (text[i + 1] === '(' || text[i + 1] === '{'))) {
970
+ // A substitution is part of its word (`-p$(printf pw)`), spaces and nested ones included.
971
+ const [open, close] = c === '`' ? ['', '`'] : [text[i + 1], text[i + 1] === '(' ? ')' : '}'];
972
+ let k = i + 1;
973
+ for (let depth = 1; k < text.length && text[k] !== enclosing; k += 1) {
974
+ if (text[k] === close && (depth -= 1) === 0)
975
+ break;
976
+ if (text[k] === open && k > i + 1)
977
+ depth += 1;
978
+ }
979
+ if (text[k] !== close) {
980
+ spans.push([start, Math.min(k, text.length)]);
981
+ return { spans, end: Math.min(k, text.length) };
982
+ }
983
+ i = k + 1;
984
+ }
985
+ else
986
+ i += 1;
987
+ }
988
+ if (!redirect)
989
+ spans.push([start, Math.min(i, text.length)]);
990
+ redirect = false;
991
+ }
992
+ }
993
+ /**
994
+ * `COMMAND_FLAGS` secrets in shell text and listed argv text (`['mysql', '-phunter2']`). A quoted word that holds a known
995
+ * command (`sh -c 'mysql -phunter2'`) is read as a command of its own.
996
+ */
997
+ function redactCommandFlags(text) {
998
+ let out = '';
999
+ let last = 0;
1000
+ const quoteAt = quoteTracker(text);
1001
+ const re = new RegExp(COMMAND_FLAG_RE);
1002
+ for (let m = re.exec(text); m; m = re.exec(text)) {
1003
+ const quote = quoteAt(m.index).slice(-1);
1004
+ // A listed element (`'mysql',`) is read from its opening quote; a command inside a quoted argument ends at its close.
1005
+ let listed = quote !== '' && text[m.index + m[0].length] === quote;
1006
+ // A computed word matched only to its first expansion (`"$(echo $(which docker))"`) is listed when it fills its quotes.
1007
+ if (!listed && quote && text[m.index - 1] === quote) {
1008
+ const probe = commandWords(text, m.index, quote, false);
1009
+ listed = probe.spans.length === 1 && text[probe.end] === quote;
1010
+ }
1011
+ const { spans, end } = commandWords(text, listed ? text.lastIndexOf(quote, m.index) : m.index, listed ? '' : quote, listed);
1012
+ const words = spans.map(([start, stop]) => shellDequote(text.slice(start, stop)));
1013
+ const masks = commandFlagMasks(words);
1014
+ for (const [k, [start, stop]] of spans.entries()) {
1015
+ if (start < last)
1016
+ continue;
1017
+ const raw = text.slice(start, stop);
1018
+ const from = masks.get(k);
1019
+ // A nested command is a quoted or substituted word with a space, strictly inside this text.
1020
+ const nested = (listed || !quote) && raw.length < text.length && /['"]|\$\(|\x60/.test(raw) && /\s/.test(raw) && COMMAND_FLAG_TEST_RE.test(raw);
1021
+ const replaced = from !== undefined ? maskWordFrom(raw, words[k], from) : nested ? redactCommandFlags(raw) : raw;
1022
+ if (replaced === raw)
1023
+ continue;
1024
+ out += text.slice(last, start) + replaced;
1025
+ last = stop;
1026
+ }
1027
+ re.lastIndex = Math.max(end, re.lastIndex, m.index + 1);
1028
+ }
1029
+ return out + text.slice(last);
1030
+ }
568
1031
  /**
569
1032
  * Header credentials (`AUTH_HDR_RE`, `BEARER_RE`: a head group, then the credential). A parameterized credential is masked
570
1033
  * whole; a single credential is read to the end of its shell word, so an adjacent quoted piece (`Bearer foo'bar`, which
@@ -1315,7 +1778,7 @@ export function redactSensitive(text) {
1315
1778
  function redactRules(text) {
1316
1779
  if (!text)
1317
1780
  return text;
1318
- let out = text.replace(CRED_RE, (_, t) => mask(t));
1781
+ let out = maskSplitTokens(text).replace(CRED_RE, (_, t) => mask(t));
1319
1782
  out = out.replace(EMBEDDED_AWS_RE, (t) => mask(t));
1320
1783
  out = redactHeaderCredentials(out, AUTH_HDR_RE);
1321
1784
  out = out.replace(JWT_RE, (t) => mask(t));
@@ -1323,7 +1786,8 @@ function redactRules(text) {
1323
1786
  for (const re of [COOKIE_ANSI_RE, COOKIE_SQ_RE, COOKIE_DQ_RE, COOKIE_BARE_RE])
1324
1787
  out = out.replace(re, (whole, head, value) => (/[A-Za-z0-9]/.test(value) ? `${head}***` : whole));
1325
1788
  out = redactCredentialParams(out);
1326
- out = out.replace(ENV_RE, (whole, key, quote, value) => (/[A-Za-z0-9]/.test(value) ? `${key}=${quote}${mask(value)}${quote}` : whole));
1789
+ out = out.replace(ENV_RE, (whole, key, quote, value) => (/[A-Za-z0-9]/.test(value) && isEnvCredential(key, value) ? `${key}=${quote}${mask(value)}${quote}` : whole));
1790
+ out = redactEnvSuffixes(out);
1327
1791
  out = out.replace(LISTED_FLAG_RE, (whole, q, dash, key, gap, vq, quotedValue, bare) => {
1328
1792
  // An unquoted value (a number, `True`, a nested list) is masked whole.
1329
1793
  if (bare !== undefined)
@@ -1338,7 +1802,11 @@ function redactRules(text) {
1338
1802
  out = out.replace(PERCENT_KEY_RE, (whole, head, value) => (value && value !== '***' && isCredentialKey(percentDecode(head.slice(0, -1))) ? `${head}***` : whole));
1339
1803
  out = out.replace(BARE_USERINFO_RE, (_, head, secret) => head + mask(secret));
1340
1804
  out = out.replace(URL_USERINFO_RE, (_, head, secret) => head + (/['"\\]/.test(secret) ? '***' : mask(secret)));
1805
+ out = out.replace(URL_BARE_TOKEN_RE, (_, head, token) => head + mask(token));
1806
+ out = out.replace(TELEGRAM_TOKEN_RE, (_, bot, id) => `${bot ?? ''}${id}:***`);
1807
+ out = out.replace(PHONE_RE, (phone) => { const keep = phone.length <= 8 ? 2 : 4; return `${phone.slice(0, keep)}****${phone.slice(-keep)}`; });
1341
1808
  out = redactUserFlags(out);
1809
+ out = redactCommandFlags(out);
1342
1810
  out = out.replace(QUERY_KEY_RE, (whole, head, value) => (/[A-Za-z0-9]/.test(value) ? head + mask(value) : whole));
1343
1811
  out = out.replace(PRIVKEY_RE, '[REDACTED PRIVATE KEY]').replace(PRIVKEY_OPEN_RE, '[REDACTED PRIVATE KEY]');
1344
1812
  return restoreCodeEnvKeyLiterals(text, out);
@@ -1346,20 +1814,24 @@ function redactRules(text) {
1346
1814
  const CASE_MARKERS = [
1347
1815
  'sk-', 'ghp_', 'github_pat_', 'gho_', 'ghu_', 'ghs_', 'ghr_', 'AKIA', 'ASIA', 'xoxb-', 'xoxa-', 'xoxp-', 'xoxr-', 'xoxs-', 'AIza', 'pplx-', 'fal_', 'fc-',
1348
1816
  'bb_live_', 'gAAAA', 'sk_live_', 'sk_test_', 'rk_live_', 'SG.', 'hf_', 'r8_', 'npm_', 'pypi-', 'dop_v1_', 'doo_v1_', 'am_', 'sk_', 'tvly-', 'exa_',
1349
- 'gsk_', 'syt_', 'retaindb_', 'hsk-', 'mem0_', 'brv_', 'eyJ', '-----BEGIN',
1817
+ 'gsk_', 'syt_', 'retaindb_', 'hsk-', 'mem0_', 'brv_', 'xapp-', 'xai-', 'ntn_', 'fw-', 'fw_', 'fpk_', 'glpat-', 'gloas-', 'gldt-', 'glrt-', 'glrtr-',
1818
+ 'glcbt-', 'glptt-', 'glft-', 'glimt-', 'glagent-', 'glsoat-', 'glffct-', 'glwt-', 'GR1348941', 'pk-lf-', 'eyJ', '-----BEGIN',
1350
1819
  ];
1351
1820
  const LOWER_MARKERS = [
1352
1821
  'authorization: bearer ', 'authorization: bot ', 'private key', 'postgres://', 'postgresql://', 'mysql://', 'mongodb://', 'redis://', 'amqp://', '://',
1353
1822
  'access_token', 'refresh_token', 'id_token', 'api_key', 'apikey', 'client_secret', 'auth_token', 'raw_secret', 'secret_input', 'key_material',
1354
1823
  'x-amz-signature', 'token=', 'secret=', 'password=', 'passwd', 'password', 'secret', 'token', 'api-key', 'apikey', 'clientsecret', 'private_key', 'credential', ' -u ', '--user ', 'authorization', 'signature', 'bearer ', 'cookie:', 'authorization=', 'key=', '"token"', '"secret"', '"password"', '"bearer"',
1355
1824
  ];
1356
- const TELEGRAM_RE = /(?:bot)?\d{8,}:[-A-Za-z0-9_]{30,}/;
1357
1825
  const DISCORD_RE = /<@!?\d{17,20}>/;
1358
- const PHONE_RE = /(?<![A-Za-z0-9])\+[1-9]\d{6,14}(?![A-Za-z0-9])/;
1826
+ const TELEGRAM_TEST_RE = new RegExp(TELEGRAM_TOKEN_RE.source);
1827
+ const PHONE_TEST_RE = new RegExp(PHONE_RE.source);
1359
1828
  export function mightContainSensitiveText(text) {
1360
1829
  if (!text)
1361
1830
  return false;
1362
- if (CASE_MARKERS.some((m) => text.includes(m)))
1831
+ // A control or zero-width character or an ANSI escape sequence inside a prefix (`x\u200bai-…`) does not hide it: the
1832
+ // redactor joins split tokens.
1833
+ const views = SPLIT_VIEWS.map((gaps) => text.replace(gaps, ''));
1834
+ if (CASE_MARKERS.some((m) => views.some((view) => view.includes(m))))
1363
1835
  return true;
1364
1836
  const lower = text.toLowerCase();
1365
1837
  if (LOWER_MARKERS.some((m) => lower.includes(m)))
@@ -1380,11 +1852,15 @@ export function mightContainSensitiveText(text) {
1380
1852
  return true;
1381
1853
  if (USER_FLAG_TEST_RE.test(text))
1382
1854
  return true;
1383
- if (text.includes(':') && TELEGRAM_RE.test(text))
1855
+ if (COMMAND_FLAG_TEST_RE.test(text))
1856
+ return true;
1857
+ if (text.includes(':') && TELEGRAM_TEST_RE.test(text))
1384
1858
  return true;
1385
1859
  if (text.includes('<@') && DISCORD_RE.test(text))
1386
1860
  return true;
1387
- if (text.includes('+') && PHONE_RE.test(text))
1861
+ if (text.includes('+') && PHONE_TEST_RE.test(text))
1862
+ return true;
1863
+ if (text.includes('=') && ENV_SUFFIX_TEST_RE.test(text))
1388
1864
  return true;
1389
1865
  return false;
1390
1866
  }
@@ -1599,7 +2075,11 @@ function redactArgs(value, enabled) {
1599
2075
  if (value.length === 2 && typeof value[0] === 'string' && isCredentialKey(value[0]))
1600
2076
  return [value[0], maskLeaves(value[1])];
1601
2077
  // An argv array (`['login', '--password', 'hunter2']`): a credential flag's value is the next element.
2078
+ const commandMasks = commandFlagMasks(value.map((item) => (typeof item === 'string' || typeof item === 'number' ? String(item) : '')));
1602
2079
  return value.map((item, i) => {
2080
+ const from = commandMasks.get(i);
2081
+ if (from !== undefined)
2082
+ return typeof item === 'string' ? maskWordFrom(item, item, from) : maskLeaves(item);
1603
2083
  const flag = value[i - 1];
1604
2084
  if (typeof flag === 'string' && item !== '' && item !== null && item !== undefined) {
1605
2085
  if (ARGV_USER_FLAG_RE.test(flag))