@opengeni/contracts 5.5.0 → 5.5.2-canary.40656248492001

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 (543) hide show
  1. package/dist/agent-authored-durable-text.d.ts +9 -9
  2. package/dist/agent-config.d.ts +148 -37
  3. package/dist/api-key-presets.d.ts +19 -0
  4. package/dist/app-action-catalog-types.d.ts +32 -0
  5. package/dist/app-action-catalog.gen.d.ts +2 -0
  6. package/dist/app-actions.d.ts +91 -0
  7. package/dist/app-actions.js +10130 -0
  8. package/dist/app-actions.js.map +1 -0
  9. package/dist/app-shot.d.ts +55 -0
  10. package/dist/artifact-catalog.d.ts +33 -0
  11. package/dist/artifacts.d.ts +166 -0
  12. package/dist/atlassian-native-retirement.d.ts +16 -0
  13. package/dist/atlassian-native-retirement.js +30 -0
  14. package/dist/atlassian-native-retirement.js.map +1 -0
  15. package/dist/atlassian.js +48 -23
  16. package/dist/automation-templates.d.ts +434 -0
  17. package/dist/automation-templates.js +31 -0
  18. package/dist/automation-templates.js.map +1 -0
  19. package/dist/billing-subscriptions.d.ts +61 -0
  20. package/dist/bots.d.ts +583 -0
  21. package/dist/bundled-skills.d.ts +58 -2
  22. package/dist/canonical-human-identities.js +3 -1
  23. package/dist/child-lifecycle-notice-protocol.d.ts +45 -0
  24. package/dist/chunk-2QKIEAGY.js +396 -0
  25. package/dist/chunk-2QKIEAGY.js.map +1 -0
  26. package/dist/chunk-2WOWEWXV.js +426 -0
  27. package/dist/chunk-2WOWEWXV.js.map +1 -0
  28. package/dist/chunk-2ZIHHL72.js +153 -0
  29. package/dist/chunk-2ZIHHL72.js.map +1 -0
  30. package/dist/chunk-32U6NKQQ.js +389 -0
  31. package/dist/chunk-32U6NKQQ.js.map +1 -0
  32. package/dist/chunk-4YDDHSO2.js +799 -0
  33. package/dist/chunk-4YDDHSO2.js.map +1 -0
  34. package/dist/chunk-56SNIMDO.js +9 -0
  35. package/dist/chunk-56SNIMDO.js.map +1 -0
  36. package/dist/chunk-5RWB6QVL.js +101 -0
  37. package/dist/chunk-5RWB6QVL.js.map +1 -0
  38. package/dist/chunk-5UGUOT3L.js +243 -0
  39. package/dist/chunk-5UGUOT3L.js.map +1 -0
  40. package/dist/chunk-6LAT33BH.js +130 -0
  41. package/dist/chunk-6LAT33BH.js.map +1 -0
  42. package/dist/chunk-7272KVWZ.js +60 -0
  43. package/dist/chunk-7272KVWZ.js.map +1 -0
  44. package/dist/chunk-72QC2GTL.js +36 -0
  45. package/dist/chunk-72QC2GTL.js.map +1 -0
  46. package/dist/chunk-7EQGGZBY.js +105 -0
  47. package/dist/chunk-7EQGGZBY.js.map +1 -0
  48. package/dist/chunk-A3LXWVYU.js +382 -0
  49. package/dist/chunk-A3LXWVYU.js.map +1 -0
  50. package/dist/chunk-AQYDZUJV.js +34 -0
  51. package/dist/chunk-AQYDZUJV.js.map +1 -0
  52. package/dist/chunk-BHCEVVUP.js +93 -0
  53. package/dist/chunk-BHCEVVUP.js.map +1 -0
  54. package/dist/chunk-BMUTAZX5.js +486 -0
  55. package/dist/chunk-BMUTAZX5.js.map +1 -0
  56. package/dist/chunk-DGG6IBBK.js +150 -0
  57. package/dist/chunk-DGG6IBBK.js.map +1 -0
  58. package/dist/{chunk-YBW5JSA3.js → chunk-FBCTUMHI.js} +123 -102
  59. package/dist/chunk-FBCTUMHI.js.map +1 -0
  60. package/dist/chunk-G2KJTFIC.js +97 -0
  61. package/dist/chunk-G2KJTFIC.js.map +1 -0
  62. package/dist/chunk-G4RKARXZ.js +1167 -0
  63. package/dist/chunk-G4RKARXZ.js.map +1 -0
  64. package/dist/chunk-H6UJJIUM.js +350 -0
  65. package/dist/chunk-H6UJJIUM.js.map +1 -0
  66. package/dist/chunk-JOTHDW4H.js +18 -0
  67. package/dist/chunk-JOTHDW4H.js.map +1 -0
  68. package/dist/chunk-L4GUEP7K.js +56480 -0
  69. package/dist/chunk-L4GUEP7K.js.map +1 -0
  70. package/dist/{chunk-SIOU2K4U.js → chunk-LJH2EYGT.js} +23 -23
  71. package/dist/chunk-MTJKSIGA.js +161 -0
  72. package/dist/chunk-MTJKSIGA.js.map +1 -0
  73. package/dist/chunk-MYHMB6GZ.js +202 -0
  74. package/dist/chunk-MYHMB6GZ.js.map +1 -0
  75. package/dist/chunk-PI5VIKJY.js +660 -0
  76. package/dist/chunk-PI5VIKJY.js.map +1 -0
  77. package/dist/{chunk-RIY7GYQS.js → chunk-PTSROVDG.js} +2 -2
  78. package/dist/chunk-PUIUXIOL.js +189 -0
  79. package/dist/chunk-PUIUXIOL.js.map +1 -0
  80. package/dist/chunk-R6VXJIMB.js +19 -0
  81. package/dist/chunk-R6VXJIMB.js.map +1 -0
  82. package/dist/chunk-RLJUNVAM.js +15 -0
  83. package/dist/chunk-RLJUNVAM.js.map +1 -0
  84. package/dist/chunk-RR6APCBK.js +213 -0
  85. package/dist/chunk-RR6APCBK.js.map +1 -0
  86. package/dist/{chunk-TXTQIYCV.js → chunk-RUQ4I6DO.js} +59 -13
  87. package/dist/chunk-RUQ4I6DO.js.map +1 -0
  88. package/dist/{chunk-WYBDERIY.js → chunk-RWEEUDHD.js} +53 -42
  89. package/dist/chunk-RWEEUDHD.js.map +1 -0
  90. package/dist/chunk-S7MSQQL6.js +158 -0
  91. package/dist/chunk-S7MSQQL6.js.map +1 -0
  92. package/dist/{chunk-5Q54VCCH.js → chunk-SHO4YO4B.js} +20 -36
  93. package/dist/chunk-SHO4YO4B.js.map +1 -0
  94. package/dist/{chunk-HHZSZYYF.js → chunk-T3HSWMA5.js} +20 -4
  95. package/dist/chunk-T3HSWMA5.js.map +1 -0
  96. package/dist/{chunk-YRKRZ6OF.js → chunk-TOOJIBJC.js} +2 -2
  97. package/dist/chunk-UCUOTRS6.js +431 -0
  98. package/dist/chunk-UCUOTRS6.js.map +1 -0
  99. package/dist/chunk-UH4K7GP7.js +424 -0
  100. package/dist/chunk-UH4K7GP7.js.map +1 -0
  101. package/dist/chunk-UIZWGPHV.js +120 -0
  102. package/dist/chunk-UIZWGPHV.js.map +1 -0
  103. package/dist/chunk-VLUKBV6Y.js +1170 -0
  104. package/dist/chunk-VLUKBV6Y.js.map +1 -0
  105. package/dist/chunk-W2QQ5Y6K.js +55 -0
  106. package/dist/chunk-W2QQ5Y6K.js.map +1 -0
  107. package/dist/{chunk-Y3KNXVTD.js → chunk-W3NBPR4E.js} +131 -31
  108. package/dist/chunk-W3NBPR4E.js.map +1 -0
  109. package/dist/{chunk-NRMCVHAU.js → chunk-WMGO4HJU.js} +11 -9
  110. package/dist/chunk-WMGO4HJU.js.map +1 -0
  111. package/dist/{chunk-MYBZRDTY.js → chunk-WQI7VNN4.js} +19 -9
  112. package/dist/chunk-WQI7VNN4.js.map +1 -0
  113. package/dist/{chunk-PX3VCKAU.js → chunk-WVCFSOUZ.js} +1 -1
  114. package/dist/{chunk-PX3VCKAU.js.map → chunk-WVCFSOUZ.js.map} +1 -1
  115. package/dist/chunk-XB6GQLBH.js +76 -0
  116. package/dist/chunk-XB6GQLBH.js.map +1 -0
  117. package/dist/{chunk-D2HKF2EF.js → chunk-XP5M3IKT.js} +2 -2
  118. package/dist/{chunk-O77VSFYZ.js → chunk-ZMPU5BWE.js} +12 -2
  119. package/dist/chunk-ZMPU5BWE.js.map +1 -0
  120. package/dist/{chunk-3SIZGL3O.js → chunk-ZYVEJFNU.js} +22 -18
  121. package/dist/chunk-ZYVEJFNU.js.map +1 -0
  122. package/dist/claude-provider-account-authority.d.ts +18 -0
  123. package/dist/claude-subscription-oauth.d.ts +25 -0
  124. package/dist/claude-subscription-usage.d.ts +95 -1
  125. package/dist/client-error-report.d.ts +154 -8
  126. package/dist/client-error-report.js +157 -2
  127. package/dist/client-error-report.js.map +1 -1
  128. package/dist/code-search.d.ts +1 -1
  129. package/dist/code-search.js.map +1 -1
  130. package/dist/codex-provider-account-authority.js +23 -12
  131. package/dist/codex-provider-account-authority.js.map +1 -1
  132. package/dist/company-profile.d.ts +144 -0
  133. package/dist/connect.d.ts +38 -0
  134. package/dist/connect.js +244 -164
  135. package/dist/connect.js.map +1 -1
  136. package/dist/connection-account-label.d.ts +2 -0
  137. package/dist/connection-account-label.js +34 -0
  138. package/dist/connection-account-label.js.map +1 -0
  139. package/dist/connection-authority.js +49 -24
  140. package/dist/connector-attachments.js +3 -1
  141. package/dist/connector-destinations.js +3 -1
  142. package/dist/connector-tool-permissions.d.ts +68 -4
  143. package/dist/credential-profiles.d.ts +166 -0
  144. package/dist/cua-desktop-tools.d.ts +19 -0
  145. package/dist/define-schema.d.ts +8 -0
  146. package/dist/desktop-app.d.ts +28 -0
  147. package/dist/direct-model-provider.d.ts +38 -0
  148. package/dist/document-artifact-commands.js +2 -2
  149. package/dist/document-artifact-query.js +2 -2
  150. package/dist/editable-artifact-binary.d.ts +7 -0
  151. package/dist/editable-artifact-causal-frontier.js +2 -2
  152. package/dist/editable-artifact-codec-registry.js +7 -7
  153. package/dist/editable-artifact-committed-transaction.js +4 -2
  154. package/dist/editable-artifact-live.js +8 -8
  155. package/dist/editable-artifact-serialized-commit.js +9 -9
  156. package/dist/editable-artifact-versions.d.ts +7 -1
  157. package/dist/editable-artifact-versions.js +1 -1
  158. package/dist/editable-artifacts.js +49 -43
  159. package/dist/event-preview.d.ts +22 -0
  160. package/dist/event-triggers.d.ts +1253 -0
  161. package/dist/event-triggers.js +148 -0
  162. package/dist/event-triggers.js.map +1 -0
  163. package/dist/external-identities.d.ts +34 -0
  164. package/dist/external-identities.js +4 -2
  165. package/dist/feedback.d.ts +181 -0
  166. package/dist/file-access.d.ts +23 -0
  167. package/dist/first-party-tool-authorization.d.ts +852 -0
  168. package/dist/github-repository-contracts.js +64 -43
  169. package/dist/github-repository-contracts.js.map +1 -1
  170. package/dist/google-drive-native-retirement.d.ts +12 -0
  171. package/dist/google-drive-native-retirement.js +20 -0
  172. package/dist/google-drive-native-retirement.js.map +1 -0
  173. package/dist/google-drive.d.ts +1 -1
  174. package/dist/google-drive.js +323 -239
  175. package/dist/google-drive.js.map +1 -1
  176. package/dist/host-mcp-bindings.js +250 -198
  177. package/dist/host-mcp-bindings.js.map +1 -1
  178. package/dist/index.d.ts +29072 -20593
  179. package/dist/index.js +2432 -366
  180. package/dist/inference-residency.d.ts +22 -0
  181. package/dist/insights-usage.d.ts +948 -0
  182. package/dist/insights-usage.js +608 -0
  183. package/dist/insights-usage.js.map +1 -0
  184. package/dist/integration-capability-id.d.ts +8 -0
  185. package/dist/interaction-action-deadline.d.ts +48 -0
  186. package/dist/interaction-driver-diagnostic.d.ts +28 -0
  187. package/dist/interaction.d.ts +2126 -881
  188. package/dist/knowledge-entries.d.ts +38 -0
  189. package/dist/knowledge-preparation.d.ts +4 -0
  190. package/dist/learning-refusal.d.ts +43 -0
  191. package/dist/learning-refusal.js +49 -0
  192. package/dist/learning-refusal.js.map +1 -0
  193. package/dist/managed-auth-session-sets.d.ts +1 -0
  194. package/dist/managed-auth-session-sets.js +207 -146
  195. package/dist/managed-auth-session-sets.js.map +1 -1
  196. package/dist/managed-sign-in-methods.d.ts +96 -0
  197. package/dist/managed-sign-in-methods.js +157 -35
  198. package/dist/managed-sign-in-methods.js.map +1 -1
  199. package/dist/mcp-structured-content.d.ts +25 -0
  200. package/dist/message-time-zone.d.ts +38 -0
  201. package/dist/message-time-zone.js +19 -0
  202. package/dist/message-time-zone.js.map +1 -0
  203. package/dist/modal-native-proof-v2.d.ts +587 -0
  204. package/dist/modal-native-proof-v2.js +39 -0
  205. package/dist/modal-native-proof-v2.js.map +1 -0
  206. package/dist/model-account-choice.d.ts +237 -0
  207. package/dist/model-account-settings.d.ts +422 -0
  208. package/dist/model-availability.d.ts +15 -0
  209. package/dist/model-connection-access.d.ts +13 -0
  210. package/dist/model-context-inspector.d.ts +8 -0
  211. package/dist/model-display.d.ts +43 -0
  212. package/dist/model-display.js +17 -0
  213. package/dist/model-display.js.map +1 -0
  214. package/dist/model-picker-order.js.map +1 -1
  215. package/dist/model-residency.d.ts +304 -0
  216. package/dist/organization-access.d.ts +416 -0
  217. package/dist/organization-access.js +40 -0
  218. package/dist/organization-access.js.map +1 -0
  219. package/dist/organization-integration-policy.d.ts +18 -0
  220. package/dist/organization-membership-lifecycle.d.ts +66 -0
  221. package/dist/organization-model-usage.d.ts +146 -0
  222. package/dist/organization-model-usage.js +87 -0
  223. package/dist/organization-model-usage.js.map +1 -0
  224. package/dist/organization-recovery.js +3 -1
  225. package/dist/organization-usage.d.ts +12 -0
  226. package/dist/passkey-only-account.d.ts +14 -0
  227. package/dist/passkey-only-account.js +13 -0
  228. package/dist/passkey-only-account.js.map +1 -0
  229. package/dist/permissions.d.ts +19 -0
  230. package/dist/personal-github.js +220 -148
  231. package/dist/personal-github.js.map +1 -1
  232. package/dist/personal-integration-setup.d.ts +81 -0
  233. package/dist/personal-integration-setup.js +42 -0
  234. package/dist/personal-integration-setup.js.map +1 -0
  235. package/dist/prepared-mcp-setup.d.ts +54 -0
  236. package/dist/prepared-mcp-setup.js +13 -0
  237. package/dist/prepared-mcp-setup.js.map +1 -0
  238. package/dist/presentation-artifact-commands.js +2 -2
  239. package/dist/presentation-artifact-query.js +2 -2
  240. package/dist/primitives.d.ts +18 -0
  241. package/dist/product-analytics.d.ts +27 -10
  242. package/dist/product-lifecycle-facts.d.ts +26 -3
  243. package/dist/prompt-cache-diagnostics.d.ts +92 -0
  244. package/dist/recordings.d.ts +1371 -0
  245. package/dist/remember.d.ts +98 -2
  246. package/dist/retained-output.d.ts +4 -2
  247. package/dist/sandbox-file-artifacts.d.ts +19 -2
  248. package/dist/sandbox-provider-command.d.ts +5 -44
  249. package/dist/sandbox-recovery.d.ts +139 -8
  250. package/dist/secret-generator.d.ts +44 -0
  251. package/dist/secret-mask-strings.d.ts +32 -0
  252. package/dist/secret-references.d.ts +90 -0
  253. package/dist/secret-resolution.d.ts +143 -0
  254. package/dist/secret-shape-masking.d.ts +52 -0
  255. package/dist/secret-shape-masking.js +19 -0
  256. package/dist/secret-shape-masking.js.map +1 -0
  257. package/dist/secret-totp.d.ts +41 -0
  258. package/dist/secret-use-approval-question.d.ts +11 -0
  259. package/dist/secret-use-approval-question.js +11 -0
  260. package/dist/secret-use-approval-question.js.map +1 -0
  261. package/dist/secret-use-approvals.d.ts +106 -0
  262. package/dist/secret-value-masking.d.ts +299 -0
  263. package/dist/secret-value-masking.js +62 -0
  264. package/dist/secret-value-masking.js.map +1 -0
  265. package/dist/secret-value.d.ts +19 -0
  266. package/dist/session-history-import.d.ts +444 -8
  267. package/dist/session-history.d.ts +111 -0
  268. package/dist/session-history.js +35 -0
  269. package/dist/session-history.js.map +1 -0
  270. package/dist/session-list-entries.d.ts +8 -0
  271. package/dist/session-list-entries.js +61 -0
  272. package/dist/session-list-entries.js.map +1 -0
  273. package/dist/session-mcp-projections.d.ts +13 -1
  274. package/dist/session-mcp-tool-permissions.d.ts +62 -0
  275. package/dist/session-target.d.ts +18 -0
  276. package/dist/session-titles.d.ts +8 -0
  277. package/dist/session-titles.js +5 -1
  278. package/dist/session-usage-limit-refusal.d.ts +34 -0
  279. package/dist/session-usage-limit-refusal.js +15 -0
  280. package/dist/session-usage-limit-refusal.js.map +1 -0
  281. package/dist/session-usage-limits.d.ts +127 -0
  282. package/dist/session-usage-limits.js +43 -0
  283. package/dist/session-usage-limits.js.map +1 -0
  284. package/dist/site-data.d.ts +286 -0
  285. package/dist/site-operations.d.ts +39 -0
  286. package/dist/site-publishing.d.ts +813 -0
  287. package/dist/site-session-http.d.ts +103 -4
  288. package/dist/site-session-http.js +27 -1
  289. package/dist/site-sql.d.ts +425 -0
  290. package/dist/skills.d.ts +38 -0
  291. package/dist/slack-bot-scopes.d.ts +9 -7
  292. package/dist/slack-bot-scopes.js +6 -3
  293. package/dist/slack-rest-mcp.d.ts +36 -0
  294. package/dist/slack-rest-mcp.js +25 -0
  295. package/dist/slack-rest-mcp.js.map +1 -0
  296. package/dist/spreadsheet-artifact-commands.d.ts +14 -1
  297. package/dist/spreadsheet-artifact-commands.js +9 -2
  298. package/dist/spreadsheet-artifact-query.d.ts +13 -6
  299. package/dist/spreadsheet-artifact-query.js +4 -3
  300. package/dist/subscription-account-actions.d.ts +83 -0
  301. package/dist/subscription-account-pools.d.ts +326 -0
  302. package/dist/subscription-personal-authority-v2.d.ts +28 -0
  303. package/dist/subscription-personal-authority-v2.js +13 -0
  304. package/dist/subscription-personal-authority-v2.js.map +1 -0
  305. package/dist/subscription-plans.d.ts +38 -0
  306. package/dist/subscription-usage-pool.d.ts +89 -0
  307. package/dist/tool-action-review.d.ts +141 -0
  308. package/dist/tool-catalog.d.ts +479 -0
  309. package/dist/tool-policy.d.ts +5 -0
  310. package/dist/tool-review-presentation.d.ts +29 -0
  311. package/dist/tool-review-presentation.js +21 -0
  312. package/dist/tool-review-presentation.js.map +1 -0
  313. package/dist/turn-serving-policy.d.ts +223 -0
  314. package/dist/usage-allowances.d.ts +30 -0
  315. package/dist/usage-allowances.js +5 -1
  316. package/dist/values.d.ts +11 -0
  317. package/dist/values.js +19 -0
  318. package/dist/values.js.map +1 -0
  319. package/dist/vault-provider-wire.d.ts +907 -0
  320. package/dist/vault-provider-wire.js +74 -0
  321. package/dist/vault-provider-wire.js.map +1 -0
  322. package/dist/vaults.d.ts +2185 -0
  323. package/dist/video-generation.d.ts +75 -0
  324. package/dist/video-generation.js +8 -1
  325. package/dist/workspace-control-event.d.ts +18 -0
  326. package/dist/workspace-control-event.js +18 -0
  327. package/dist/workspace-control-event.js.map +1 -0
  328. package/dist/workspace-governance-context.d.ts +18 -0
  329. package/dist/workspace-integration-wire.d.ts +1 -1
  330. package/dist/workspace-integration-wire.js +1 -1
  331. package/dist/workspace-integrations.d.ts +212 -12
  332. package/dist/xai-provider-account-authority.js +3 -1
  333. package/package.json +121 -4
  334. package/src/agent-authored-durable-text.ts +14 -11
  335. package/src/agent-config.ts +382 -203
  336. package/src/agent-instruction-changes.ts +94 -74
  337. package/src/agent-learning.ts +94 -66
  338. package/src/agent-topology.ts +58 -52
  339. package/src/api-key-presets.ts +28 -0
  340. package/src/app-action-catalog-types.ts +32 -0
  341. package/src/app-action-catalog.gen.ts +9905 -0
  342. package/src/app-actions.ts +365 -0
  343. package/src/app-shot.ts +176 -0
  344. package/src/artifact-catalog.ts +81 -46
  345. package/src/artifacts.ts +360 -227
  346. package/src/atlassian-native-retirement.ts +33 -0
  347. package/src/atlassian.ts +136 -108
  348. package/src/automation-templates.ts +201 -0
  349. package/src/billing-subscriptions.ts +48 -0
  350. package/src/bots.ts +238 -0
  351. package/src/bundled-skills.ts +79 -22
  352. package/src/canonical-human-identities.ts +65 -62
  353. package/src/child-lifecycle-notice-protocol.gen.json +1310 -0
  354. package/src/child-lifecycle-notice-protocol.ts +224 -0
  355. package/src/claude-provider-account-authority.ts +12 -0
  356. package/src/claude-subscription-oauth.ts +61 -19
  357. package/src/claude-subscription-usage.ts +64 -35
  358. package/src/client-error-report.ts +344 -9
  359. package/src/code-search.ts +1 -1
  360. package/src/codex-provider-account-authority.ts +22 -16
  361. package/src/company-brain-governed-writes.ts +265 -244
  362. package/src/company-brain.ts +165 -154
  363. package/src/company-profile.ts +362 -297
  364. package/src/connect.ts +222 -173
  365. package/src/connection-account-label.ts +45 -0
  366. package/src/connection-authority.ts +172 -152
  367. package/src/connector-attachments.ts +139 -123
  368. package/src/connector-destinations.ts +48 -43
  369. package/src/connector-tool-permissions.ts +104 -46
  370. package/src/credential-profiles.ts +161 -0
  371. package/src/cua-desktop-tools.gen.json +6722 -0
  372. package/src/cua-desktop-tools.linux.gen.json +6798 -0
  373. package/src/cua-desktop-tools.ts +42 -0
  374. package/src/cua-desktop-tools.windows.gen.json +6739 -0
  375. package/src/define-schema.ts +10 -0
  376. package/src/desktop-app.ts +50 -0
  377. package/src/direct-model-provider.ts +112 -0
  378. package/src/editable-artifact-binary.ts +29 -8
  379. package/src/editable-artifact-committed-transaction.ts +19 -18
  380. package/src/editable-artifact-serialized-commit.ts +8 -2
  381. package/src/editable-artifact-versions.ts +7 -1
  382. package/src/event-preview.ts +53 -3
  383. package/src/event-triggers.ts +891 -0
  384. package/src/external-identities.ts +239 -195
  385. package/src/feedback.ts +125 -30
  386. package/src/file-access.ts +26 -0
  387. package/src/first-party-tool-authorization.ts +383 -0
  388. package/src/github-repository-contracts.ts +78 -64
  389. package/src/google-drive-native-retirement.ts +20 -0
  390. package/src/google-drive.ts +286 -234
  391. package/src/governed-learning-activation.ts +159 -137
  392. package/src/governed-learning-evaluator.ts +141 -133
  393. package/src/host-mcp-bindings.ts +244 -221
  394. package/src/image-generation.ts +105 -95
  395. package/src/index.ts +13928 -9899
  396. package/src/inference-residency.ts +36 -0
  397. package/src/insights-usage.ts +774 -0
  398. package/src/integration-capability-id.ts +16 -0
  399. package/src/interaction-action-deadline.ts +143 -0
  400. package/src/interaction-driver-diagnostic.ts +89 -0
  401. package/src/interaction.ts +3527 -2427
  402. package/src/knowledge-entries.ts +386 -299
  403. package/src/knowledge-preparation.ts +49 -41
  404. package/src/knowledge.ts +242 -213
  405. package/src/learning-refusal.ts +90 -0
  406. package/src/managed-auth-session-sets.ts +180 -133
  407. package/src/managed-sign-in-methods.ts +156 -34
  408. package/src/mcp-oauth.ts +58 -46
  409. package/src/mcp-receipts.ts +199 -194
  410. package/src/mcp-structured-content.ts +81 -0
  411. package/src/memory-slack-delivery.ts +172 -142
  412. package/src/message-time-zone.ts +109 -0
  413. package/src/modal-native-proof-v2.ts +430 -0
  414. package/src/model-account-choice.ts +178 -0
  415. package/src/model-account-settings.ts +210 -0
  416. package/src/model-availability.ts +22 -0
  417. package/src/model-connection-access.ts +62 -14
  418. package/src/model-context-inspector.ts +130 -109
  419. package/src/model-display.ts +221 -0
  420. package/src/model-picker-order.ts +1 -1
  421. package/src/model-residency.ts +535 -0
  422. package/src/organization-access.ts +216 -0
  423. package/src/organization-integration-policy.ts +47 -28
  424. package/src/organization-membership-lifecycle.ts +558 -384
  425. package/src/organization-model-usage.ts +82 -0
  426. package/src/organization-recovery.ts +148 -135
  427. package/src/organization-usage.ts +84 -52
  428. package/src/passkey-only-account.ts +29 -0
  429. package/src/permissions.ts +94 -52
  430. package/src/personal-github.ts +211 -171
  431. package/src/personal-integration-setup.ts +150 -0
  432. package/src/preference-registry.ts +319 -263
  433. package/src/prepared-mcp-setup.ts +190 -0
  434. package/src/primitives.ts +22 -0
  435. package/src/product-analytics.ts +40 -15
  436. package/src/product-lifecycle-facts.ts +50 -20
  437. package/src/prompt-cache-diagnostics.ts +212 -0
  438. package/src/recordings.ts +1153 -0
  439. package/src/remember.ts +184 -172
  440. package/src/retained-output.ts +181 -168
  441. package/src/sandbox-file-artifacts.ts +78 -57
  442. package/src/sandbox-provider-command.ts +55 -72
  443. package/src/sandbox-recovery.ts +163 -77
  444. package/src/scoped-knowledge.ts +62 -43
  445. package/src/secret-generator.ts +156 -0
  446. package/src/secret-mask-strings.ts +175 -0
  447. package/src/secret-references.ts +414 -0
  448. package/src/secret-resolution.ts +322 -0
  449. package/src/secret-shape-masking.ts +573 -0
  450. package/src/secret-totp.ts +188 -0
  451. package/src/secret-use-approval-question.ts +22 -0
  452. package/src/secret-use-approvals.ts +219 -0
  453. package/src/secret-value-masking.ts +1283 -0
  454. package/src/secret-value.ts +22 -0
  455. package/src/session-goal-reports.ts +55 -49
  456. package/src/session-history-import.ts +84 -68
  457. package/src/session-history.ts +197 -0
  458. package/src/session-list-entries.ts +106 -0
  459. package/src/session-mcp-projections.ts +21 -3
  460. package/src/session-mcp-tool-permissions.ts +109 -0
  461. package/src/session-message-search.ts +46 -38
  462. package/src/session-target.ts +58 -0
  463. package/src/session-titles.ts +40 -4
  464. package/src/session-topology-primitives.ts +20 -12
  465. package/src/session-usage-limit-refusal.ts +101 -0
  466. package/src/session-usage-limits.ts +169 -0
  467. package/src/signup-attribution.ts +20 -15
  468. package/src/site-data.ts +337 -0
  469. package/src/site-operations.ts +154 -0
  470. package/src/site-publishing.ts +903 -0
  471. package/src/site-session-http.ts +344 -16
  472. package/src/site-sql.ts +1120 -0
  473. package/src/skill-use.ts +18 -17
  474. package/src/skills.ts +105 -50
  475. package/src/slack-bot-scopes.ts +18 -35
  476. package/src/slack-rest-mcp.ts +222 -0
  477. package/src/slack-task-policy.ts +97 -73
  478. package/src/spreadsheet-artifact-commands.ts +74 -13
  479. package/src/spreadsheet-artifact-query.ts +188 -50
  480. package/src/subscription-account-actions.ts +103 -0
  481. package/src/subscription-account-pools.ts +74 -0
  482. package/src/subscription-personal-authority-v2.ts +69 -0
  483. package/src/subscription-plans.ts +51 -0
  484. package/src/subscription-usage-pool.ts +44 -0
  485. package/src/task-notes.ts +70 -68
  486. package/src/tool-action-review.ts +271 -0
  487. package/src/tool-catalog.ts +694 -379
  488. package/src/tool-policy.ts +82 -0
  489. package/src/tool-result-spill.ts +13 -9
  490. package/src/tool-review-presentation.ts +643 -0
  491. package/src/turn-serving-policy.ts +167 -0
  492. package/src/usage-allowances.ts +212 -136
  493. package/src/values.ts +25 -0
  494. package/src/vault-provider-wire.ts +580 -0
  495. package/src/vaults.ts +1296 -0
  496. package/src/video-generation.ts +430 -361
  497. package/src/work-claims.ts +183 -166
  498. package/src/workspace-control-event.ts +134 -0
  499. package/src/workspace-governance-context.ts +63 -0
  500. package/src/workspace-instruction-policies.ts +346 -279
  501. package/src/workspace-integration-wire.ts +10 -0
  502. package/src/workspace-integrations.ts +488 -398
  503. package/src/workspace-learning-administration.ts +58 -44
  504. package/src/workspace-learning-policy.ts +126 -99
  505. package/src/workspace-state.ts +239 -220
  506. package/src/xai-provider-account-authority.ts +23 -17
  507. package/dist/chunk-3SIZGL3O.js.map +0 -1
  508. package/dist/chunk-566IFOVP.js +0 -26133
  509. package/dist/chunk-566IFOVP.js.map +0 -1
  510. package/dist/chunk-5IXYF6XN.js +0 -728
  511. package/dist/chunk-5IXYF6XN.js.map +0 -1
  512. package/dist/chunk-5Q54VCCH.js.map +0 -1
  513. package/dist/chunk-BCS7WHCK.js +0 -139
  514. package/dist/chunk-BCS7WHCK.js.map +0 -1
  515. package/dist/chunk-EK7NDGFR.js +0 -128
  516. package/dist/chunk-EK7NDGFR.js.map +0 -1
  517. package/dist/chunk-GRG7PHWQ.js +0 -25
  518. package/dist/chunk-GRG7PHWQ.js.map +0 -1
  519. package/dist/chunk-HHZSZYYF.js.map +0 -1
  520. package/dist/chunk-ICDZGUX2.js +0 -186
  521. package/dist/chunk-ICDZGUX2.js.map +0 -1
  522. package/dist/chunk-JUFK3T4Q.js +0 -80
  523. package/dist/chunk-JUFK3T4Q.js.map +0 -1
  524. package/dist/chunk-MYBZRDTY.js.map +0 -1
  525. package/dist/chunk-NRMCVHAU.js.map +0 -1
  526. package/dist/chunk-O77VSFYZ.js.map +0 -1
  527. package/dist/chunk-P3HLDT77.js +0 -59
  528. package/dist/chunk-P3HLDT77.js.map +0 -1
  529. package/dist/chunk-TXTQIYCV.js.map +0 -1
  530. package/dist/chunk-WYBDERIY.js.map +0 -1
  531. package/dist/chunk-Y3KNXVTD.js.map +0 -1
  532. package/dist/chunk-YBW5JSA3.js.map +0 -1
  533. package/dist/chunk-YRXAWWCU.js +0 -45
  534. package/dist/chunk-YRXAWWCU.js.map +0 -1
  535. package/dist/chunk-YWMOIZ6A.js +0 -185
  536. package/dist/chunk-YWMOIZ6A.js.map +0 -1
  537. package/dist/site-request-headers.test.d.ts +0 -1
  538. package/dist/transcription-recordings.d.ts +0 -300
  539. package/src/transcription-recordings.ts +0 -169
  540. /package/dist/{chunk-SIOU2K4U.js.map → chunk-LJH2EYGT.js.map} +0 -0
  541. /package/dist/{chunk-RIY7GYQS.js.map → chunk-PTSROVDG.js.map} +0 -0
  542. /package/dist/{chunk-YRKRZ6OF.js.map → chunk-TOOJIBJC.js.map} +0 -0
  543. /package/dist/{chunk-D2HKF2EF.js.map → chunk-XP5M3IKT.js.map} +0 -0
@@ -0,0 +1,1283 @@
1
+ /**
2
+ * Known-value secret masking: one deterministic engine for every place that
3
+ * must not show a secret value it knows about (tool results, stored history
4
+ * and events, browser and desktop observations, history scrubbing).
5
+ *
6
+ * A value is searched for in the forms it takes in text: as written, trimmed,
7
+ * line by line, JSON-escaped (once and twice), percent-encoded (URI component,
8
+ * `+` for spaces, form encoding) and, optionally, base64 (standard and
9
+ * URL-safe, standalone and inside a longer base64 run). Each match is replaced
10
+ * by the value's label, normally the reference that resolved it
11
+ * (`{{secret:Vault/Item/field}}`), so whoever reads the text knows which secret
12
+ * it was and can keep using it by reference.
13
+ *
14
+ * Properties:
15
+ * - Values shorter than the minimum (8 characters by default) are never
16
+ * searched for: a short value such as a PIN would mangle unrelated text.
17
+ * - The longest form wins where several match at one position.
18
+ * - Idempotent: the labels and Opengeni's markers already in the text are
19
+ * kept, so masking masked text changes nothing. Other reference-shaped text
20
+ * is searched like any text: a value inside it is still the value.
21
+ * - A text that ends, or reaches a truncation mark (`...`, `…`), partway
22
+ * through a value has that clipped start replaced too, when the start is
23
+ * distinctive (at least 16 characters and half of the value).
24
+ * - Pure and isomorphic: no I/O, no clock, no randomness.
25
+ *
26
+ * Labels must be safe inside JSON strings and URLs. `secretReferenceMaskLabel`
27
+ * guarantees this for references; `secretCredentialMaskLabel` for Opengeni's
28
+ * own credentials.
29
+ */
30
+ import { formatSecretReference, parseSecretReference } from "./secret-references";
31
+ import type { ResolvedSecretValue, SecretResolver } from "./secret-resolution";
32
+ import { isBase64DataUrl, isTextMediaType, mapSecretMaskableStrings } from "./secret-mask-strings";
33
+ import {
34
+ collectSecretShapes,
35
+ maskSecretShapes,
36
+ maskSecretShapesInText,
37
+ secretShapeMaskLabel,
38
+ type SecretShapeMatch,
39
+ } from "./secret-shape-masking";
40
+
41
+ export { isBase64DataUrl, isTextMediaType, mapSecretMaskableStrings };
42
+
43
+ /** Values shorter than this are not masked by default. Eight characters keeps
44
+ * PINs, one-time codes, ports and short words out while still covering real
45
+ * passwords, tokens and keys (which are almost always longer). */
46
+ export const SECRET_VALUE_MASK_MIN_CHARS = 8;
47
+
48
+ /** A text that ends (or reaches a truncation mark) partway through a value
49
+ * has that clipped start masked only when it is distinctive: at least this
50
+ * many characters... */
51
+ export const SECRET_VALUE_CLIPPED_MIN_CHARS = 16;
52
+ /** ...and at least this share of the value's form. A shared prefix (a URL
53
+ * scheme, a token's fixed header, `-----BEGIN`) is never enough on its own. */
54
+ export const SECRET_VALUE_CLIPPED_MIN_SHARE = 0.5;
55
+
56
+ /** Distinct values one memory keeps; the oldest drop first. */
57
+ export const SECRET_VALUE_MEMORY_MAX_ENTRIES = 512;
58
+
59
+ export type SecretValueMaskEntry = {
60
+ /** The plaintext to find. Never logged or returned by this module. */
61
+ value: string;
62
+ /** What replaces it: a reference or a fixed marker. Must be JSON- and URL-safe text. */
63
+ label: string;
64
+ };
65
+
66
+ export type SecretValueMaskOptions = {
67
+ /** Values (and derived forms) shorter than this are skipped. Default 8. */
68
+ minChars?: number;
69
+ /** Also search base64 forms. Default true. */
70
+ base64?: boolean;
71
+ /** Minimum characters for a clipped start (default 16, never below `minChars`). */
72
+ clippedMinChars?: number;
73
+ /** Minimum share of the form for a clipped start, 0 to 1 (default 0.5). */
74
+ clippedMinShare?: number;
75
+ /**
76
+ * The end of a text counts as a cut only when the text has at least this
77
+ * many characters (default 1,024: output clipped to a size). Truncation
78
+ * marks always count.
79
+ */
80
+ clippedTextEndMinChars?: number;
81
+ /**
82
+ * Lines of a multi-line value searched on their own. "key-material"
83
+ * (default): lines of at least 16 characters made only of base64 or hex
84
+ * characters (PEM body lines), so a structured value's ordinary lines
85
+ * (`NODE_ENV=production`) stay searchable text; "all": every line of at
86
+ * least `minChars`.
87
+ */
88
+ lines?: "key-material" | "all";
89
+ /**
90
+ * Which forms are indexed for clipped starts. "primary" (default): the
91
+ * value as written, trimmed and JSON-escaped once, which bounds memory for
92
+ * long values. "all": every form, including lines and percent-encoded
93
+ * forms (short typed values in browser and desktop results).
94
+ */
95
+ clippedForms?: "primary" | "all";
96
+ /**
97
+ * Object keys whose string values stay untouched (identifiers, digests).
98
+ * Binary payloads are always skipped: a string that is entirely a base64
99
+ * data URL (`data:<type>/<subtype>;base64,<base64>`) and the `data` of an
100
+ * object that also carries a MIME type (image and file content). Payloads
101
+ * of a text type are searched decoded and encoded again.
102
+ */
103
+ skipKey?: (key: string) => boolean;
104
+ };
105
+
106
+ export type SecretValueMaskDiagnostic = {
107
+ /** Where masking failed, for example `tool_result` or `event_writer`. */
108
+ stage: string;
109
+ /** The error's class name only; never its message, which could carry text. */
110
+ error: string;
111
+ };
112
+
113
+ /** The marker that replaces a credential Opengeni itself put into the attempt. */
114
+ const CREDENTIAL_LABEL_OPEN = "[opengeni credential: ";
115
+ /** How a `[masked <kind>]` marker (a shape value that has no reference) starts. */
116
+ const SHAPE_MARKER_OPEN = "[masked ";
117
+
118
+ export function secretCredentialMaskLabel(kind: string): string {
119
+ const safe = kind.replace(/[^A-Za-z0-9 ._-]/gu, "").slice(0, 60) || "credential";
120
+ return `${CREDENTIAL_LABEL_OPEN}${safe}]`;
121
+ }
122
+
123
+ /** Generic replacement when no usable reference exists. */
124
+ export const SECRET_VALUE_GENERIC_MASK_LABEL = "[secret]";
125
+
126
+ const REFERENCE_OPEN = "\u007B\u007Bsecret:";
127
+ const FILE_REFERENCE_OPEN = "\u007B\u007Bsecretfile:";
128
+ const JSON_UNSAFE = /["\\\u0000-\u001f\u007f-\u009f\u2028\u2029]/u;
129
+
130
+ /**
131
+ * The label for a value a reference resolved to: the reference as written
132
+ * (a `secretfile` reference is shown as the equivalent `secret` reference,
133
+ * since what appears in text is the value, not the file path). A reference
134
+ * whose text is unsafe inside JSON (a name with a quote) is written in its ID
135
+ * form instead, which resolves to the same field.
136
+ */
137
+ export function secretReferenceMaskLabel(input: {
138
+ raw: string;
139
+ itemId?: string | null;
140
+ fieldName?: string | null;
141
+ }): string {
142
+ let raw = input.raw;
143
+ if (raw.startsWith(FILE_REFERENCE_OPEN)) {
144
+ raw = REFERENCE_OPEN + raw.slice(FILE_REFERENCE_OPEN.length);
145
+ }
146
+ if (raw.startsWith(REFERENCE_OPEN) && !JSON_UNSAFE.test(raw)) return raw;
147
+ if (input.itemId) {
148
+ try {
149
+ let previous = false;
150
+ try {
151
+ previous = parseSecretReference(raw).previous;
152
+ } catch {
153
+ previous = false;
154
+ }
155
+ const id = formatSecretReference({
156
+ itemId: input.itemId,
157
+ ...(input.fieldName ? { field: input.fieldName } : {}),
158
+ ...(previous ? { previous: true } : {}),
159
+ });
160
+ if (!JSON_UNSAFE.test(id)) return id;
161
+ } catch {
162
+ // Fall through to the generic marker.
163
+ }
164
+ }
165
+ return SECRET_VALUE_GENERIC_MASK_LABEL;
166
+ }
167
+
168
+ export function isSecretReferenceMaskLabel(label: string): boolean {
169
+ return label.startsWith(REFERENCE_OPEN);
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // Forms
174
+ // ---------------------------------------------------------------------------
175
+
176
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
177
+
178
+ function utf8Bytes(value: string): Uint8Array | null {
179
+ try {
180
+ return new TextEncoder().encode(value);
181
+ } catch {
182
+ return null;
183
+ }
184
+ }
185
+
186
+ function base64(bytes: Uint8Array): string {
187
+ let out = "";
188
+ for (let index = 0; index < bytes.length; index += 3) {
189
+ const a = bytes[index]!;
190
+ const b = index + 1 < bytes.length ? bytes[index + 1]! : 0;
191
+ const c = index + 2 < bytes.length ? bytes[index + 2]! : 0;
192
+ const triple = (a << 16) | (b << 8) | c;
193
+ out += BASE64_ALPHABET[(triple >> 18) & 63]!;
194
+ out += BASE64_ALPHABET[(triple >> 12) & 63]!;
195
+ out += index + 1 < bytes.length ? BASE64_ALPHABET[(triple >> 6) & 63]! : "=";
196
+ out += index + 2 < bytes.length ? BASE64_ALPHABET[triple & 63]! : "=";
197
+ }
198
+ return out;
199
+ }
200
+
201
+ /** Standalone base64 (padded and unpadded) and the part of the encoding that
202
+ * depends only on the value at each of the three byte alignments a value can
203
+ * have inside a longer base64 run (for example `user:token` in Basic auth). */
204
+ function base64Forms(value: string): string[] {
205
+ const bytes = utf8Bytes(value);
206
+ if (bytes === null || bytes.length === 0) return [];
207
+ const forms: string[] = [];
208
+ const whole = base64(bytes);
209
+ forms.push(whole, whole.replace(/=+$/u, ""));
210
+ for (let offset = 0; offset < 3; offset += 1) {
211
+ const padded = new Uint8Array(offset + bytes.length);
212
+ padded.set(bytes, offset);
213
+ const encoded = base64(padded);
214
+ const start = Math.ceil((offset * 8) / 6);
215
+ const end = Math.floor(((offset + bytes.length) * 8) / 6);
216
+ if (end > start) forms.push(encoded.slice(start, end));
217
+ }
218
+ const withUrlSafe: string[] = [];
219
+ for (const form of forms) {
220
+ withUrlSafe.push(form, form.replaceAll("+", "-").replaceAll("/", "_"));
221
+ }
222
+ return withUrlSafe;
223
+ }
224
+
225
+ const PEM_BOUNDARY = /^-----(BEGIN|END) [A-Z0-9 ]+-----$/u;
226
+
227
+ /**
228
+ * A line of key material (a PEM body line): pure hex, or base64 characters
229
+ * with an upper-case letter, a lower-case letter and a digit. Not `NAME=value`
230
+ * and not identifier-like lines such as `production-cluster-name`.
231
+ */
232
+ const KEY_MATERIAL_LINE =
233
+ /^(?:[0-9A-Fa-f]+|(?=[^\n]*[A-Z])(?=[^\n]*[a-z])(?=[^\n]*[0-9])[A-Za-z0-9+/_-]+={0,2})$/u;
234
+
235
+ /**
236
+ * Every form of one value searched for in text, longest first. Forms shorter
237
+ * than `minChars` are dropped; a value shorter than it has no forms.
238
+ */
239
+ export function secretValueForms(value: string, options: SecretValueMaskOptions = {}): string[] {
240
+ const minChars = options.minChars ?? SECRET_VALUE_MASK_MIN_CHARS;
241
+ if (typeof value !== "string" || value.length < minChars) return [];
242
+ const forms = new Set<string>();
243
+ const add = (form: string | null | undefined) => {
244
+ if (form && form.length >= minChars) forms.add(form);
245
+ };
246
+ add(value);
247
+ // Tools trim the text they report and keep its first line, so a value's
248
+ // trimmed form and each line of a multi-line value are searched too (but
249
+ // not PEM boundary lines, which are the same in every key).
250
+ add(value.trim());
251
+ // By default only lines that look like key material (base64 or hex only,
252
+ // at least 16 characters), so a structured value's ordinary lines
253
+ // (`NODE_ENV=production`) stay searchable text.
254
+ const lines = value.split(/\r?\n|\r/u);
255
+ if (lines.length > 1) {
256
+ for (const line of lines) {
257
+ const trimmed = line.trim();
258
+ if (PEM_BOUNDARY.test(trimmed)) continue;
259
+ if (options.lines === "all" || (trimmed.length >= 16 && KEY_MATERIAL_LINE.test(trimmed))) {
260
+ add(trimmed);
261
+ }
262
+ }
263
+ }
264
+ const escaped = JSON.stringify(value).slice(1, -1);
265
+ add(escaped);
266
+ add(JSON.stringify(escaped).slice(1, -1));
267
+ try {
268
+ const uri = encodeURIComponent(value);
269
+ add(uri);
270
+ add(uri.replaceAll("%20", "+"));
271
+ add(uri.replace(/%[0-9A-F]{2}/gu, (match) => match.toLowerCase()));
272
+ } catch {
273
+ // A lone surrogate has no URI form; the raw value is still searched.
274
+ }
275
+ // Submitted forms and URLSearchParams encode more characters (for example
276
+ // "!" as "%21") than encodeURIComponent does.
277
+ try {
278
+ add(new URLSearchParams({ v: value }).toString().slice(2));
279
+ } catch {
280
+ // Same as above.
281
+ }
282
+ if (options.base64 !== false) for (const form of base64Forms(value)) add(form);
283
+ return [...forms].sort((left, right) => right.length - left.length);
284
+ }
285
+
286
+ // ---------------------------------------------------------------------------
287
+ // Compiled masks
288
+ // ---------------------------------------------------------------------------
289
+
290
+ /**
291
+ * Markers that are never searched inside: Opengeni's own credential and
292
+ * capture markers and the generic `[secret]`. A compiled set also keeps its
293
+ * own labels exactly. Other reference-shaped text is searched like any text:
294
+ * a value written inside reference syntax is still the value.
295
+ */
296
+ const PROTECTED_PATTERNS = [
297
+ "\\[opengeni credential: [A-Za-z0-9 ._-]{1,60}\\]",
298
+ "\\[masked [a-z0-9 _-]{1,40}\\]",
299
+ "\\[secret\\]",
300
+ ];
301
+
302
+ function escapeRegExp(text: string): string {
303
+ return text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
304
+ }
305
+
306
+ /** Where text may have been cut: its end, and each truncation mark a tool
307
+ * leaves where it clipped a longer text before adding more after it. */
308
+ const TRUNCATION_MARK = /\.\.\.|\u2026/gu;
309
+
310
+ export type CompiledSecretValueMasks = {
311
+ readonly empty: boolean;
312
+ readonly minChars: number;
313
+ readonly skipKey: ((key: string) => boolean) | undefined;
314
+ /** Labels and markers kept as they are; null when there is nothing to search for. */
315
+ readonly pattern: RegExp | null;
316
+ readonly labels: ReadonlyMap<string, string>;
317
+ /** Forms longest first. */
318
+ readonly forms: readonly string[];
319
+ /** Forms by their first `minChars` characters, longest first. */
320
+ readonly byPrefix: ReadonlyMap<string, readonly string[]>;
321
+ /** The first two character codes of every form, as one number. */
322
+ readonly firstPairs: ReadonlySet<number>;
323
+ /** Clipped starts: at least this many characters... */
324
+ readonly clippedMinChars: number;
325
+ /** ...and at least this share of the form. */
326
+ readonly clippedMinShare: number;
327
+ /** The end of a text is a cut only for texts at least this long. */
328
+ readonly clippedTextEndMinChars: number;
329
+ /**
330
+ * Clipped starts of forms by their last `clippedMinChars` characters,
331
+ * longest first, so a cut needs one lookup.
332
+ */
333
+ readonly byClippedEnd: ReadonlyMap<string, readonly ClippedStart[]>;
334
+ readonly longestForm: number;
335
+ };
336
+
337
+ /** A distinctive start of a form: `form.slice(0, length)`. */
338
+ type ClippedStart = { form: string; length: number };
339
+
340
+ /** Forms longer than this are not indexed for clipped starts (bounded memory). */
341
+ const CLIPPED_INDEX_MAX_FORM_CHARS = 8_192;
342
+ /**
343
+ * Index entries for clipped starts across all values; once reached, the
344
+ * remaining (longest) forms are matched whole only.
345
+ */
346
+ const CLIPPED_INDEX_MAX_ENTRIES = 200_000;
347
+ /** Truncation marks examined per text, and candidates per mark. */
348
+ const CLIPPED_MAX_CUTS = 256;
349
+ const CLIPPED_MAX_CANDIDATES = 32;
350
+ /** The end of a text counts as a cut only for long texts (output clipped to a size). */
351
+ export const SECRET_VALUE_CLIPPED_TEXT_END_MIN_CHARS = 1_024;
352
+
353
+ /** Compile entries once for repeated masking. A form keeps the label of the first entry that has it. */
354
+ export function compileSecretValueMasks(
355
+ entries: readonly SecretValueMaskEntry[],
356
+ options: SecretValueMaskOptions = {},
357
+ ): CompiledSecretValueMasks {
358
+ const minChars = Math.max(2, options.minChars ?? SECRET_VALUE_MASK_MIN_CHARS);
359
+ const clippedMinChars = Math.max(
360
+ minChars,
361
+ options.clippedMinChars ?? SECRET_VALUE_CLIPPED_MIN_CHARS,
362
+ );
363
+ const clippedMinShare = Math.min(
364
+ 1,
365
+ Math.max(0, options.clippedMinShare ?? SECRET_VALUE_CLIPPED_MIN_SHARE),
366
+ );
367
+ const labels = new Map<string, string>();
368
+ const labelTexts = new Set<string>();
369
+ // Clipped starts are indexed for the forms text usually shows (as written,
370
+ // trimmed, JSON-escaped once), not the encoded variants.
371
+ const clippable = new Set<string>();
372
+ for (const entry of entries) {
373
+ if (typeof entry?.value !== "string" || typeof entry.label !== "string") continue;
374
+ const forms = secretValueForms(entry.value, { ...options, minChars });
375
+ if (forms.length === 0) continue;
376
+ labelTexts.add(entry.label);
377
+ for (const form of forms) if (!labels.has(form)) labels.set(form, entry.label);
378
+ clippable.add(entry.value);
379
+ clippable.add(entry.value.trim());
380
+ clippable.add(JSON.stringify(entry.value).slice(1, -1));
381
+ }
382
+ const forms = [...labels.keys()].sort((left, right) => right.length - left.length);
383
+ const byPrefix = new Map<string, string[]>();
384
+ const byClippedEnd = new Map<string, ClippedStart[]>();
385
+ const firstPairs = new Set<number>();
386
+ for (const form of forms) {
387
+ const prefix = form.slice(0, minChars);
388
+ const bucket = byPrefix.get(prefix);
389
+ if (bucket) bucket.push(form);
390
+ else byPrefix.set(prefix, [form]);
391
+ firstPairs.add(form.charCodeAt(0) * 65_536 + form.charCodeAt(1));
392
+ }
393
+ // Shortest forms first, so the global cap leaves out the longest values.
394
+ let indexed = 0;
395
+ for (let index = forms.length - 1; index >= 0; index -= 1) {
396
+ const form = forms[index]!;
397
+ if (options.clippedForms !== "all" && !clippable.has(form)) continue;
398
+ if (form.length <= clippedMinChars || form.length > CLIPPED_INDEX_MAX_FORM_CHARS) continue;
399
+ const shortest = Math.max(clippedMinChars, Math.ceil(form.length * clippedMinShare));
400
+ if (indexed + (form.length - shortest) > CLIPPED_INDEX_MAX_ENTRIES) break;
401
+ for (let length = shortest; length < form.length; length += 1) {
402
+ const key = form.slice(length - clippedMinChars, length);
403
+ const starts = byClippedEnd.get(key);
404
+ if (starts) starts.push({ form, length });
405
+ else byClippedEnd.set(key, [{ form, length }]);
406
+ }
407
+ indexed += form.length - shortest;
408
+ }
409
+ for (const starts of byClippedEnd.values()) {
410
+ starts.sort((left, right) => right.length - left.length);
411
+ }
412
+ const pattern =
413
+ forms.length === 0
414
+ ? null
415
+ : new RegExp(
416
+ [
417
+ ...[...labelTexts].sort((left, right) => right.length - left.length).map(escapeRegExp),
418
+ ...PROTECTED_PATTERNS,
419
+ ]
420
+ .map((source) => `(?:${source})`)
421
+ .join("|"),
422
+ "gu",
423
+ );
424
+ return {
425
+ empty: forms.length === 0,
426
+ minChars,
427
+ skipKey: options.skipKey,
428
+ pattern,
429
+ labels,
430
+ forms,
431
+ byPrefix,
432
+ firstPairs,
433
+ clippedMinChars,
434
+ clippedMinShare,
435
+ clippedTextEndMinChars: Math.max(
436
+ 0,
437
+ options.clippedTextEndMinChars ?? SECRET_VALUE_CLIPPED_TEXT_END_MIN_CHARS,
438
+ ),
439
+ byClippedEnd,
440
+ longestForm: forms[0]?.length ?? 0,
441
+ };
442
+ }
443
+
444
+ /**
445
+ * The longest start of a form that `text` ends with at `cut` (text clipped to
446
+ * a length can end partway through a value, and that partial value is still
447
+ * the secret's start). Only distinctive starts count: at least
448
+ * `clippedMinChars` characters and `clippedMinShare` of the form, so a
449
+ * shared prefix such as a URL scheme or a token's fixed header is not one.
450
+ */
451
+ function clippedSecretTail(
452
+ text: string,
453
+ cut: number,
454
+ masks: CompiledSecretValueMasks,
455
+ ): { length: number; label: string } | null {
456
+ const probe = masks.clippedMinChars;
457
+ if (cut < probe || masks.byClippedEnd.size === 0) return null;
458
+ const candidates = masks.byClippedEnd.get(text.slice(cut - probe, cut));
459
+ if (!candidates) return null;
460
+ // Longest first: the longest clipped value wins.
461
+ let checked = 0;
462
+ for (const { form, length } of candidates) {
463
+ if (length > cut) continue;
464
+ if (checked++ >= CLIPPED_MAX_CANDIDATES) break;
465
+ if (!text.startsWith(form.slice(0, length), cut - length)) continue;
466
+ const label = masks.labels.get(form);
467
+ if (label !== undefined) return { length, label };
468
+ }
469
+ return null;
470
+ }
471
+
472
+ function overlapsKept(start: number, end: number, kept: ReadonlyArray<[number, number]>): boolean {
473
+ for (const [from, to] of kept) if (start < to && from < end) return true;
474
+ return false;
475
+ }
476
+
477
+ function keptRanges(text: string, pattern: RegExp): Array<[number, number]> {
478
+ const kept: Array<[number, number]> = [];
479
+ pattern.lastIndex = 0;
480
+ for (const match of text.matchAll(pattern)) {
481
+ if (match[0].length > 0) kept.push([match.index, match.index + match[0].length]);
482
+ }
483
+ return kept;
484
+ }
485
+
486
+ /** Mask one text with compiled entries. */
487
+ export function maskSecretValuesInText(text: string, masks: CompiledSecretValueMasks): string {
488
+ if (masks.empty || masks.pattern === null || text.length < masks.minChars) return text;
489
+ const size = masks.minChars;
490
+ // Existing labels and markers are skipped; where one starts, it wins, and
491
+ // no match may run into one.
492
+ const kept = keptRanges(text, masks.pattern);
493
+ let out = "";
494
+ let copied = 0;
495
+ let next = 0;
496
+ for (let at = 0; at <= text.length - size;) {
497
+ while (next < kept.length && kept[next]![1] <= at) next += 1;
498
+ if (next < kept.length && kept[next]![0] <= at) {
499
+ at = kept[next]![1];
500
+ next += 1;
501
+ continue;
502
+ }
503
+ if (!masks.firstPairs.has(text.charCodeAt(at) * 65_536 + text.charCodeAt(at + 1))) {
504
+ at += 1;
505
+ continue;
506
+ }
507
+ const candidates = masks.byPrefix.get(text.slice(at, at + size));
508
+ const limit = next < kept.length ? kept[next]![0] : text.length;
509
+ const form = candidates?.find(
510
+ (candidate) => at + candidate.length <= limit && text.startsWith(candidate, at),
511
+ );
512
+ if (form === undefined) {
513
+ at += 1;
514
+ continue;
515
+ }
516
+ out += text.slice(copied, at) + masks.labels.get(form)!;
517
+ at += form.length;
518
+ copied = at;
519
+ }
520
+ out += text.slice(copied);
521
+ if (masks.byClippedEnd.size === 0) return out;
522
+ // Truncation marks (not one right after another, as in `......`), and the
523
+ // end of a long text. From the last cut back, so earlier positions stay valid.
524
+ const cuts: number[] = out.length >= masks.clippedTextEndMinChars ? [out.length] : [];
525
+ const marks = [...out.matchAll(TRUNCATION_MARK)];
526
+ for (let index = marks.length - 1; index >= 0; index -= 1) {
527
+ if (cuts.length >= CLIPPED_MAX_CUTS) break;
528
+ const mark = marks[index]!;
529
+ const previous = marks[index - 1];
530
+ if (previous && previous.index + previous[0].length === mark.index) continue;
531
+ cuts.push(mark.index);
532
+ }
533
+ if (cuts.length === 0) return out;
534
+ const keptBefore = keptRanges(out, masks.pattern);
535
+ let changedFrom = out.length + 1;
536
+ for (const cut of cuts) {
537
+ // A tail replaced at a later cut already covers this one.
538
+ if (cut > changedFrom) continue;
539
+ const tail = clippedSecretTail(out, cut, masks);
540
+ if (!tail) continue;
541
+ const start = cut - tail.length;
542
+ if (overlapsKept(start, cut, keptBefore)) continue;
543
+ out = out.slice(0, start) + tail.label + out.slice(cut);
544
+ changedFrom = start;
545
+ }
546
+ return out;
547
+ }
548
+
549
+ /**
550
+ * Deep copy of `value` with every known secret value replaced in every string
551
+ * (object keys are kept). Inputs that need no change keep their identity.
552
+ */
553
+ export function maskSecretValues<T>(
554
+ value: T,
555
+ entries: readonly SecretValueMaskEntry[] | CompiledSecretValueMasks,
556
+ options: SecretValueMaskOptions = {},
557
+ ): T {
558
+ const masks = Array.isArray(entries)
559
+ ? compileSecretValueMasks(entries as readonly SecretValueMaskEntry[], options)
560
+ : (entries as CompiledSecretValueMasks);
561
+ if (masks.empty) return value;
562
+ return mapSecretMaskableStrings(
563
+ value,
564
+ (text) => maskSecretValuesInText(text, masks),
565
+ masks.skipKey,
566
+ );
567
+ }
568
+
569
+ // ---------------------------------------------------------------------------
570
+ // First-party call metadata
571
+ // ---------------------------------------------------------------------------
572
+
573
+ /**
574
+ * The MCP `_meta` key on a worker's call to Opengeni's own server that
575
+ * carries the values its arguments resolved to (with their references), so
576
+ * the server masks them in what it stores and returns. Never logged.
577
+ */
578
+ export const FIRST_PARTY_SECRET_VALUES_META_KEY = "opengeniSecretValues";
579
+
580
+ /** The resolved values a first-party call's `_meta` carries, if any. */
581
+ export function firstPartySecretValuesFromMeta(meta: unknown): SecretValueMaskEntry[] {
582
+ if (meta === null || typeof meta !== "object") return [];
583
+ const entries = (meta as Record<string, unknown>)[FIRST_PARTY_SECRET_VALUES_META_KEY];
584
+ if (!Array.isArray(entries)) return [];
585
+ return entries.filter(
586
+ (entry): entry is SecretValueMaskEntry =>
587
+ entry !== null &&
588
+ typeof entry === "object" &&
589
+ typeof (entry as SecretValueMaskEntry).value === "string" &&
590
+ typeof (entry as SecretValueMaskEntry).label === "string",
591
+ );
592
+ }
593
+
594
+ /**
595
+ * Whether a secret-shaped value is distinctive enough to search for in any
596
+ * text: at least 16 characters with three kinds of characters (lower case,
597
+ * upper case, digits, others), or 24 with two, and not the user name beside it.
598
+ */
599
+ function isDistinctiveSecret(value: string, username?: string): boolean {
600
+ if (username !== undefined && value === username) return false;
601
+ const kinds =
602
+ Number(/[a-z]/.test(value)) +
603
+ Number(/[A-Z]/.test(value)) +
604
+ Number(/[0-9]/.test(value)) +
605
+ Number(/[^A-Za-z0-9]/.test(value));
606
+ return (value.length >= 16 && kinds >= 3) || (value.length >= 24 && kinds >= 2);
607
+ }
608
+
609
+ /** A revealed value as it appears in text: as written and JSON-escaped. */
610
+ function revealedForms(value: string): string[] {
611
+ if (value.length === 0) return [];
612
+ const escaped = JSON.stringify(value).slice(1, -1);
613
+ return escaped === value ? [value] : [value, escaped];
614
+ }
615
+
616
+ // ---------------------------------------------------------------------------
617
+ // Memory
618
+ // ---------------------------------------------------------------------------
619
+
620
+ /**
621
+ * Saves detected secret-shaped values somewhere they can be used by
622
+ * reference (the task vault) and returns, per value it saved, the reference
623
+ * to write instead. Values it leaves out are masked with a fixed marker.
624
+ */
625
+ export type SecretShapeCapturer = (
626
+ matches: readonly SecretShapeMatch[],
627
+ ) => Promise<ReadonlyMap<string, string>>;
628
+
629
+ /** Distinct new values captured from one tool result; the rest get a marker. */
630
+ export const SECRET_SHAPE_CAPTURES_PER_RESULT = 16;
631
+
632
+ /**
633
+ * The secret values one owner (an agent attempt, a browser session) knows
634
+ * about, with their labels, and masking against all of them. Masking never
635
+ * throws: an internal failure returns the input unchanged and reports a
636
+ * metadata-only diagnostic.
637
+ */
638
+ export class SecretValueMemory {
639
+ private readonly values = new Map<string, string>();
640
+ private readonly operations = new Map<string, SecretValueMaskEntry[]>();
641
+ private compiled: CompiledSecretValueMasks | null = null;
642
+ /** Masks without values that only have a `[masked <kind>]` marker, by version. */
643
+ private compiledReferenced: { version: number; masks: CompiledSecretValueMasks } | null = null;
644
+ private changes = 0;
645
+ private readonly options: SecretValueMaskOptions;
646
+ private readonly maxEntries: number;
647
+ private onDiagnostic: ((diagnostic: SecretValueMaskDiagnostic) => void) | undefined;
648
+ private refresher: (() => Promise<void>) | undefined;
649
+ /** Values an explicit, audited reveal (`secret_get`) handed to the agent. */
650
+ private readonly revealed = new Set<string>();
651
+ private disposed = false;
652
+ /** Secret-shaped values seen in tool output, with what replaces them (any length). */
653
+ private readonly shapes = new Map<string, string>();
654
+ private shapeCapturer: SecretShapeCapturer | undefined;
655
+
656
+ constructor(
657
+ options: SecretValueMaskOptions & {
658
+ maxEntries?: number;
659
+ onDiagnostic?: (diagnostic: SecretValueMaskDiagnostic) => void;
660
+ } = {},
661
+ ) {
662
+ const { maxEntries, onDiagnostic, ...maskOptions } = options;
663
+ this.options = maskOptions;
664
+ this.maxEntries = maxEntries ?? SECRET_VALUE_MEMORY_MAX_ENTRIES;
665
+ this.onDiagnostic = onDiagnostic;
666
+ }
667
+
668
+ /** Replace the diagnostic sink (for example once observability is ready). */
669
+ setDiagnosticSink(sink: ((diagnostic: SecretValueMaskDiagnostic) => void) | undefined): void {
670
+ this.onDiagnostic = sink;
671
+ }
672
+
673
+ /**
674
+ * Attach a loader that may remember more values (for example uses recorded
675
+ * by another process). It owns its own throttling.
676
+ */
677
+ setRefresher(refresher: (() => Promise<void>) | undefined): void {
678
+ this.refresher = refresher;
679
+ }
680
+
681
+ /** Run the refresher, waiting at most `maxWaitMs`; never throws. */
682
+ async refresh(maxWaitMs = 2_000): Promise<void> {
683
+ const refresher = this.refresher;
684
+ if (!refresher) return;
685
+ let timer: ReturnType<typeof setTimeout> | undefined;
686
+ try {
687
+ await Promise.race([
688
+ refresher(),
689
+ new Promise<void>((resolve) => {
690
+ timer = setTimeout(resolve, maxWaitMs);
691
+ }),
692
+ ]);
693
+ } catch (error) {
694
+ this.report("refresh", error);
695
+ } finally {
696
+ if (timer !== undefined) clearTimeout(timer);
697
+ }
698
+ }
699
+
700
+ get minChars(): number {
701
+ return this.options.minChars ?? SECRET_VALUE_MASK_MIN_CHARS;
702
+ }
703
+
704
+ get size(): number {
705
+ return this.values.size;
706
+ }
707
+
708
+ /** Increases whenever the searched values change (for memoized masking). */
709
+ get version(): number {
710
+ return this.changes;
711
+ }
712
+
713
+ /**
714
+ * Remember a value. Returns whether it is searched for (false when shorter
715
+ * than the minimum). A value keeps its first reference label; a reference
716
+ * label replaces a credential marker, because it is the more useful name.
717
+ */
718
+ remember(value: string, label: string): boolean {
719
+ try {
720
+ if (typeof value !== "string" || typeof label !== "string") return false;
721
+ if (this.disposed || value.length < this.minChars || this.isRevealed(value)) return false;
722
+ const existing = this.values.get(value);
723
+ const keep =
724
+ existing !== undefined &&
725
+ (existing === label ||
726
+ isSecretReferenceMaskLabel(existing) ||
727
+ !isSecretReferenceMaskLabel(label));
728
+ this.values.delete(value);
729
+ this.values.set(value, keep ? existing! : label);
730
+ this.evict();
731
+ if (!keep) {
732
+ this.compiled = null;
733
+ this.changes += 1;
734
+ }
735
+ return true;
736
+ } catch (error) {
737
+ this.report("remember", error);
738
+ return false;
739
+ }
740
+ }
741
+
742
+ /**
743
+ * Over the limit, the oldest values go first, but Opengeni's own
744
+ * credentials (`[opengeni credential: …]`) stay while others can go.
745
+ */
746
+ private evict(): void {
747
+ if (this.values.size <= this.maxEntries) return;
748
+ for (const [value, label] of this.values) {
749
+ if (this.values.size <= this.maxEntries) break;
750
+ if (label.startsWith(CREDENTIAL_LABEL_OPEN)) continue;
751
+ this.values.delete(value);
752
+ }
753
+ while (this.values.size > this.maxEntries) {
754
+ const oldest = this.values.keys().next().value;
755
+ if (oldest === undefined) break;
756
+ this.values.delete(oldest);
757
+ }
758
+ this.compiled = null;
759
+ this.changes += 1;
760
+ }
761
+
762
+ /**
763
+ * An explicit, audited plaintext read (`secret_get`) handed this value to
764
+ * the agent on purpose: it is not masked for the rest of this memory's
765
+ * life. Never throws.
766
+ */
767
+ reveal(value: string): void {
768
+ try {
769
+ if (typeof value !== "string" || value.length === 0) return;
770
+ this.revealed.add(value);
771
+ // A value inside the revealed one (the password of a revealed URL) was
772
+ // handed over with it, so it is released too; Opengeni's own
773
+ // credentials stay masked.
774
+ let changed = false;
775
+ for (const [known, label] of this.values) {
776
+ if (label.startsWith(CREDENTIAL_LABEL_OPEN) || !value.includes(known)) continue;
777
+ this.values.delete(known);
778
+ changed = true;
779
+ }
780
+ for (const known of this.shapes.keys()) {
781
+ if (value.includes(known)) this.shapes.delete(known);
782
+ }
783
+ if (changed) {
784
+ this.compiled = null;
785
+ this.changes += 1;
786
+ }
787
+ } catch (error) {
788
+ this.report("reveal", error);
789
+ }
790
+ }
791
+
792
+ /**
793
+ * Whether `value` is, or lies within, a value `secret_get` revealed. Only a
794
+ * value of at least the minimum length counts as lying within one, so a
795
+ * short value is not released by a chance match.
796
+ */
797
+ private isRevealed(value: string): boolean {
798
+ if (this.revealed.size === 0) return false;
799
+ if (this.revealed.has(value)) return true;
800
+ if (value.length < this.minChars) return false;
801
+ for (const revealed of this.revealed) if (revealed.includes(value)) return true;
802
+ return false;
803
+ }
804
+
805
+ /**
806
+ * Forget every value at the end of the owner's life (an attempt), so work
807
+ * that outlives it neither keeps the plaintext nor masks with it.
808
+ */
809
+ dispose(): void {
810
+ this.disposed = true;
811
+ this.values.clear();
812
+ this.operations.clear();
813
+ this.revealed.clear();
814
+ this.shapes.clear();
815
+ this.shapeCapturer = undefined;
816
+ this.refresher = undefined;
817
+ this.compiled = null;
818
+ this.compiledReferenced = null;
819
+ this.changes += 1;
820
+ }
821
+
822
+ /**
823
+ * Remember a value and note that one tool call (its operation ID) used it,
824
+ * so the receiver of that call can mask its own stored text with exactly
825
+ * the values it was given.
826
+ */
827
+ rememberForOperation(operationId: string, value: string, label: string): boolean {
828
+ const searched = this.remember(value, label);
829
+ if (!searched || !operationId) return searched;
830
+ try {
831
+ const entries = this.operations.get(operationId) ?? [];
832
+ if (!entries.some((entry) => entry.value === value)) entries.push({ value, label });
833
+ this.operations.delete(operationId);
834
+ this.operations.set(operationId, entries);
835
+ while (this.operations.size > 256) {
836
+ const oldest = this.operations.keys().next().value;
837
+ if (oldest === undefined) break;
838
+ this.operations.delete(oldest);
839
+ }
840
+ } catch (error) {
841
+ this.report("remember", error);
842
+ }
843
+ return searched;
844
+ }
845
+
846
+ /** The values one tool call used (see `rememberForOperation`). */
847
+ operationEntries(operationId: string): SecretValueMaskEntry[] {
848
+ return [...(this.operations.get(operationId) ?? [])];
849
+ }
850
+
851
+ /** Whether a value is remembered (for tests and dedupe; never exposes values). */
852
+ has(value: string): boolean {
853
+ return this.values.has(value);
854
+ }
855
+
856
+ entries(): SecretValueMaskEntry[] {
857
+ return [...this.values].map(([value, label]) => ({ value, label }));
858
+ }
859
+
860
+ private masks(): CompiledSecretValueMasks {
861
+ this.compiled ??= compileSecretValueMasks(this.entries(), this.options);
862
+ return this.compiled;
863
+ }
864
+
865
+ /** Deep copy with every remembered value replaced; never throws. */
866
+ mask<T>(value: T, stage = "mask"): T {
867
+ if (this.values.size === 0) return value;
868
+ try {
869
+ return maskSecretValues(value, this.masks());
870
+ } catch (error) {
871
+ this.report(stage, error);
872
+ return value;
873
+ }
874
+ }
875
+
876
+ /** `mask` without the values whose label is only a `[masked <kind>]` marker. */
877
+ private maskReferenced<T>(value: T, stage: string): T {
878
+ if (this.values.size === 0) return value;
879
+ let markers = false;
880
+ for (const label of this.values.values()) {
881
+ if (label.startsWith(SHAPE_MARKER_OPEN)) {
882
+ markers = true;
883
+ break;
884
+ }
885
+ }
886
+ if (!markers) return this.mask(value, stage);
887
+ try {
888
+ if (this.compiledReferenced?.version !== this.changes) {
889
+ this.compiledReferenced = {
890
+ version: this.changes,
891
+ masks: compileSecretValueMasks(
892
+ this.entries().filter((entry) => !entry.label.startsWith(SHAPE_MARKER_OPEN)),
893
+ this.options,
894
+ ),
895
+ };
896
+ }
897
+ return maskSecretValues(value, this.compiledReferenced.masks);
898
+ } catch (error) {
899
+ this.report(stage, error);
900
+ return value;
901
+ }
902
+ }
903
+
904
+ /** Mask one text; never throws. */
905
+ maskText(text: string, stage = "mask"): string {
906
+ if (this.values.size === 0 || typeof text !== "string") return text;
907
+ try {
908
+ return maskSecretValuesInText(text, this.masks());
909
+ } catch (error) {
910
+ this.report(stage, error);
911
+ return text;
912
+ }
913
+ }
914
+
915
+ /** Mask with the remembered values plus, for this call only, extra ones. */
916
+ maskWith<T>(value: T, extra: readonly SecretValueMaskEntry[], stage = "mask"): T {
917
+ if (extra.length === 0) return this.mask(value, stage);
918
+ try {
919
+ return maskSecretValues(value, [...this.entries(), ...extra], this.options);
920
+ } catch (error) {
921
+ this.report(stage, error);
922
+ return value;
923
+ }
924
+ }
925
+
926
+ /**
927
+ * Attach the capturer that saves secret-shaped values found in tool output
928
+ * (normally into the task vault). Without one they are masked with a marker.
929
+ */
930
+ setShapeCapturer(capturer: SecretShapeCapturer | undefined): void {
931
+ this.shapeCapturer = capturer;
932
+ }
933
+
934
+ /**
935
+ * Remember a secret-shaped value from tool output: it is replaced wherever
936
+ * a shape detector finds it, and (when long enough) searched for as a known
937
+ * value everywhere else. A reference replaces an earlier marker.
938
+ */
939
+ rememberShape(value: string, label: string, username?: string): void {
940
+ try {
941
+ if (typeof value !== "string" || typeof label !== "string" || value.length === 0) return;
942
+ // An ended attempt keeps nothing; a value `secret_get` revealed stays readable.
943
+ if (this.disposed || this.isRevealed(value)) return;
944
+ const existing = this.shapes.get(value);
945
+ if (
946
+ existing === undefined ||
947
+ (!isSecretReferenceMaskLabel(existing) && isSecretReferenceMaskLabel(label))
948
+ ) {
949
+ this.shapes.set(value, label);
950
+ while (this.shapes.size > this.maxEntries) {
951
+ const oldest = this.shapes.keys().next().value;
952
+ if (oldest === undefined) break;
953
+ this.shapes.delete(oldest);
954
+ }
955
+ }
956
+ // Searched everywhere else only when it is distinctive: a weak or
957
+ // common password (`postgres`, the URL's own user name) would mask
958
+ // ordinary words. Where a detector finds it, it is masked regardless.
959
+ if (isDistinctiveSecret(value, username)) {
960
+ this.remember(value, this.shapes.get(value) ?? label);
961
+ }
962
+ } catch (error) {
963
+ this.report("remember_shape", error);
964
+ }
965
+ }
966
+
967
+ /**
968
+ * Find secret-shaped values in tool output that are not known yet and
969
+ * capture them, waiting at most `maxWaitMs`. Values the capturer does not
970
+ * return a reference for (or all of them, without a capturer or on any
971
+ * failure) are remembered with a `[masked <kind>]` marker. Never throws.
972
+ */
973
+ async captureShapes(value: unknown, stage = "tool_result", maxWaitMs = 5_000): Promise<void> {
974
+ let pending: SecretShapeMatch[];
975
+ try {
976
+ // Known values are masked first (a detected part of a vault secret is
977
+ // not captured again), except values that only have a marker: those
978
+ // are detected again, so a failed or skipped capture is retried.
979
+ const known = this.maskReferenced(value, stage);
980
+ // Values that already have a reference are done; markers are retried.
981
+ pending = collectSecretShapes(known, 256, {
982
+ ...(this.options.skipKey ? { skipKey: this.options.skipKey } : {}),
983
+ keep: (match, text) => !this.insideRevealed(match, text) && !this.isRevealed(match.value),
984
+ }).filter((match) => !isSecretReferenceMaskLabel(this.shapes.get(match.value) ?? ""));
985
+ } catch (error) {
986
+ this.report(stage, error);
987
+ return;
988
+ }
989
+ if (pending.length === 0) return;
990
+ const capturable = pending.slice(0, SECRET_SHAPE_CAPTURES_PER_RESULT);
991
+ const usernames = new Map(pending.map((match) => [match.value, match.username]));
992
+ let captured: ReadonlyMap<string, string> = new Map();
993
+ const capturer = this.shapeCapturer;
994
+ if (capturer) {
995
+ let timer: ReturnType<typeof setTimeout> | undefined;
996
+ try {
997
+ const late = capturer(capturable);
998
+ // A capture that finishes after the wait still upgrades the markers.
999
+ late.then(
1000
+ (references) => {
1001
+ for (const [secret, reference] of references) {
1002
+ if (!isSecretReferenceMaskLabel(reference)) continue;
1003
+ this.rememberShape(secret, reference, usernames.get(secret));
1004
+ }
1005
+ },
1006
+ () => {},
1007
+ );
1008
+ captured =
1009
+ (await Promise.race([
1010
+ late,
1011
+ new Promise<null>((resolve) => {
1012
+ timer = setTimeout(() => resolve(null), maxWaitMs);
1013
+ }),
1014
+ ])) ?? new Map();
1015
+ } catch (error) {
1016
+ this.report("shape_capture", error);
1017
+ } finally {
1018
+ if (timer !== undefined) clearTimeout(timer);
1019
+ }
1020
+ }
1021
+ for (const match of pending) {
1022
+ const reference = captured.get(match.value);
1023
+ this.rememberShape(
1024
+ match.value,
1025
+ reference && isSecretReferenceMaskLabel(reference)
1026
+ ? reference
1027
+ : secretShapeMaskLabel(match.kind),
1028
+ match.username,
1029
+ );
1030
+ }
1031
+ }
1032
+
1033
+ /**
1034
+ * Whether a detected part lies within a value `secret_get` revealed (as
1035
+ * written or JSON-escaped), for example the password of a revealed URL.
1036
+ */
1037
+ private insideRevealed(match: SecretShapeMatch, text: string): boolean {
1038
+ if (this.revealed.size === 0) return false;
1039
+ for (const value of this.revealed) {
1040
+ for (const form of revealedForms(value)) {
1041
+ const at = text.indexOf(form, Math.max(0, match.start - form.length + 1));
1042
+ if (at >= 0 && at < match.end) return true;
1043
+ }
1044
+ }
1045
+ return false;
1046
+ }
1047
+
1048
+ /** What replaces a detected shape: its capture, a known label, a marker, or nothing when revealed. */
1049
+ private shapeLabel = (match: SecretShapeMatch, text: string): string | null =>
1050
+ this.insideRevealed(match, text) || this.isRevealed(match.value)
1051
+ ? null
1052
+ : (this.shapes.get(match.value) ??
1053
+ this.values.get(match.value) ??
1054
+ secretShapeMaskLabel(match.kind));
1055
+
1056
+ /**
1057
+ * Mask tool output: known values first, then every secret-shaped part
1058
+ * (with its captured reference, or a marker). Never throws.
1059
+ */
1060
+ maskToolOutput<T>(value: T, stage = "tool_result"): T {
1061
+ const known = this.mask(value, stage);
1062
+ try {
1063
+ return maskSecretShapes(known, this.shapeLabel, this.options.skipKey);
1064
+ } catch (error) {
1065
+ this.report(stage, error);
1066
+ return known;
1067
+ }
1068
+ }
1069
+
1070
+ /** `maskToolOutput` for one text. */
1071
+ maskToolOutputText(text: string, stage = "tool_result"): string {
1072
+ const known = this.maskText(text, stage);
1073
+ if (typeof known !== "string") return known;
1074
+ try {
1075
+ return maskSecretShapesInText(known, this.shapeLabel);
1076
+ } catch (error) {
1077
+ this.report(stage, error);
1078
+ return known;
1079
+ }
1080
+ }
1081
+
1082
+ private report(stage: string, error: unknown): void {
1083
+ try {
1084
+ this.onDiagnostic?.({ stage, error: error instanceof Error ? error.name : typeof error });
1085
+ } catch {
1086
+ // Diagnostics never affect masking.
1087
+ }
1088
+ }
1089
+ }
1090
+
1091
+ /**
1092
+ * Mask an error's message, stack, causes and inner errors (`AggregateError`)
1093
+ * in place with `memory` (bounded depth) and return it, so a rethrown error
1094
+ * carries references, not values. In "tool_output" mode secret-shaped parts
1095
+ * are masked too.
1096
+ * Never throws: on failure the original error is returned as it is.
1097
+ */
1098
+ export function maskSecretValuesInError<E>(
1099
+ error: E,
1100
+ memory: SecretValueMemory | null | undefined,
1101
+ mode: "known" | "tool_output" = "known",
1102
+ depth = 0,
1103
+ ): E {
1104
+ // Tool output also masks secret-shaped parts (with or without known values).
1105
+ if (!memory || !(error instanceof Error) || depth > 4) return error;
1106
+ if (mode === "known" && memory.size === 0) return error;
1107
+ // Each part on its own, so one read-only field does not stop the rest.
1108
+ const field = (name: "message" | "stack") => {
1109
+ try {
1110
+ const text = error[name];
1111
+ if (typeof text !== "string") return;
1112
+ const masked =
1113
+ mode === "tool_output"
1114
+ ? memory.maskToolOutputText(text, "tool_error")
1115
+ : memory.maskText(text, "tool_error");
1116
+ if (masked !== text) error[name] = masked;
1117
+ } catch {
1118
+ // The original text stays.
1119
+ }
1120
+ };
1121
+ field("message");
1122
+ field("stack");
1123
+ try {
1124
+ if (error.cause !== undefined) maskSecretValuesInError(error.cause, memory, mode, depth + 1);
1125
+ } catch {
1126
+ // The cause stays as it is.
1127
+ }
1128
+ try {
1129
+ const inner = (error as { errors?: unknown }).errors;
1130
+ if (Array.isArray(inner)) {
1131
+ for (const item of inner.slice(0, 32)) {
1132
+ maskSecretValuesInError(item, memory, mode, depth + 1);
1133
+ }
1134
+ }
1135
+ } catch {
1136
+ // The inner errors stay as they are.
1137
+ }
1138
+ return error;
1139
+ }
1140
+
1141
+ // ---------------------------------------------------------------------------
1142
+ // Explicit reveals
1143
+ // ---------------------------------------------------------------------------
1144
+
1145
+ /**
1146
+ * Opengeni's audited plaintext read, which hands a value to the agent on
1147
+ * purpose: exactly its own `secret_get` (as the agent sees it, bare or with
1148
+ * the first-party prefix). A connector's tool of the same name is not one.
1149
+ */
1150
+ export function isSecretRevealToolName(name: string): boolean {
1151
+ return name === "secret_get" || name === "opengeni__secret_get";
1152
+ }
1153
+
1154
+ function decodedBase64Text(value: string): string | null {
1155
+ try {
1156
+ const binary = atob(value.replace(/\s+/gu, ""));
1157
+ const bytes = new Uint8Array(binary.length);
1158
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
1159
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
1160
+ } catch {
1161
+ return null;
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * The values a `secret_get` result reveals (`{ value, encoding }`, possibly
1167
+ * as JSON text). A base64 result reveals both the encoded text and the
1168
+ * decoded UTF-8 text, since the memory holds the decoded value.
1169
+ */
1170
+ export function secretGetRevealedValues(result: unknown, depth = 0): string[] {
1171
+ if (depth > 6) return [];
1172
+ if (typeof result === "string") {
1173
+ const trimmed = result.trim();
1174
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return [];
1175
+ try {
1176
+ return secretGetRevealedValues(JSON.parse(trimmed), depth + 1);
1177
+ } catch {
1178
+ return [];
1179
+ }
1180
+ }
1181
+ if (result === null || typeof result !== "object") return [];
1182
+ if (Array.isArray(result)) {
1183
+ return result.flatMap((item) => secretGetRevealedValues(item, depth + 1));
1184
+ }
1185
+ const record = result as Record<string, unknown>;
1186
+ if (typeof record.value === "string" && record.encoding === "utf8") return [record.value];
1187
+ if (typeof record.value === "string" && record.encoding === "base64") {
1188
+ const decoded = decodedBase64Text(record.value);
1189
+ return decoded === null ? [record.value] : [record.value, decoded];
1190
+ }
1191
+ return Object.values(record).flatMap((value) => secretGetRevealedValues(value, depth + 1));
1192
+ }
1193
+
1194
+ /**
1195
+ * When `toolName` is `secret_get`, mark what its result reveals as readable
1196
+ * for the rest of the memory's life, so masking the result leaves it intact.
1197
+ * Never throws.
1198
+ */
1199
+ export function revealSecretGetResult(
1200
+ memory: SecretValueMemory | null | undefined,
1201
+ toolName: string,
1202
+ result: unknown,
1203
+ ): void {
1204
+ if (!memory) return;
1205
+ try {
1206
+ if (!isSecretRevealToolName(toolName)) return;
1207
+ for (const value of secretGetRevealedValues(result)) memory.reveal(value);
1208
+ } catch {
1209
+ // Masking applies as usual.
1210
+ }
1211
+ }
1212
+
1213
+ // ---------------------------------------------------------------------------
1214
+ // Resolver integration
1215
+ // ---------------------------------------------------------------------------
1216
+
1217
+ function textMaterial(value: ResolvedSecretValue): string | null {
1218
+ const material = value.material;
1219
+ if (material.type === "text") return material.value;
1220
+ if (material.type === "bytes") {
1221
+ try {
1222
+ return new TextDecoder("utf-8", { fatal: true }).decode(material.value);
1223
+ } catch {
1224
+ return null;
1225
+ }
1226
+ }
1227
+ // A one-time code is six or eight digits that change every period: below
1228
+ // the useful minimum and not worth searching for.
1229
+ return null;
1230
+ }
1231
+
1232
+ /** Remember what one resolver call returned, labelled by reference; never throws. */
1233
+ export function rememberResolvedSecretValues(
1234
+ memory: SecretValueMemory | null | undefined,
1235
+ resolved: ReadonlyMap<string, ResolvedSecretValue>,
1236
+ operationId?: string,
1237
+ ): void {
1238
+ if (!memory) return;
1239
+ for (const value of resolved.values()) {
1240
+ try {
1241
+ // A plain text field (a username, a host name) is not secret: masking it
1242
+ // would rewrite every page, log line and URL that mentions it.
1243
+ if (value.fieldKind === "text") continue;
1244
+ const text = textMaterial(value);
1245
+ if (text === null) continue;
1246
+ const label = secretReferenceMaskLabel({
1247
+ raw: value.reference.raw,
1248
+ itemId: value.itemId,
1249
+ fieldName: value.fieldName,
1250
+ });
1251
+ if (operationId) memory.rememberForOperation(operationId, text, label);
1252
+ else memory.remember(text, label);
1253
+ } catch {
1254
+ // Remembering never fails a resolution.
1255
+ }
1256
+ }
1257
+ }
1258
+
1259
+ /** The same resolver, remembering every value it returns in the memory `target` names at resolution time. */
1260
+ export function rememberingSecretResolver(
1261
+ resolver: SecretResolver,
1262
+ target: SecretValueMemory | (() => SecretValueMemory | null | undefined),
1263
+ ): SecretResolver {
1264
+ return {
1265
+ get principal() {
1266
+ return resolver.principal;
1267
+ },
1268
+ get enabled() {
1269
+ return resolver.enabled;
1270
+ },
1271
+ async resolve(refs, use) {
1272
+ const resolved = await resolver.resolve(refs, use);
1273
+ let memory: SecretValueMemory | null | undefined;
1274
+ try {
1275
+ memory = typeof target === "function" ? target() : target;
1276
+ } catch {
1277
+ memory = null;
1278
+ }
1279
+ rememberResolvedSecretValues(memory, resolved, use.operationId);
1280
+ return resolved;
1281
+ },
1282
+ };
1283
+ }