@opengeni/contracts 0.44.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/dist/agent-authored-durable-text.d.ts +70 -0
  2. package/dist/agent-config.d.ts +869 -0
  3. package/dist/agent-instruction-changes.d.ts +110 -0
  4. package/dist/agent-learning.d.ts +243 -0
  5. package/dist/agent-topology.d.ts +292 -0
  6. package/dist/allowance-refusal.d.ts +9 -0
  7. package/dist/allowance-refusal.js +50 -0
  8. package/dist/allowance-refusal.js.map +1 -0
  9. package/dist/analytics-consent-report.d.ts +19 -0
  10. package/dist/analytics-consent-report.js +10 -0
  11. package/dist/analytics-consent-report.js.map +1 -0
  12. package/dist/api-key-presets.d.ts +19 -0
  13. package/dist/artifact-catalog.d.ts +161 -0
  14. package/dist/artifacts.d.ts +135 -11
  15. package/dist/atlassian-native-retirement.d.ts +16 -0
  16. package/dist/atlassian-native-retirement.js +30 -0
  17. package/dist/atlassian-native-retirement.js.map +1 -0
  18. package/dist/atlassian.d.ts +2 -0
  19. package/dist/atlassian.js +27 -11
  20. package/dist/browser-storage.d.ts +5 -0
  21. package/dist/browser-storage.js +9 -0
  22. package/dist/browser-storage.js.map +1 -0
  23. package/dist/bundled-skills.d.ts +40 -0
  24. package/dist/canonical-human-identities.d.ts +157 -0
  25. package/dist/canonical-human-identities.js +23 -0
  26. package/dist/canonical-human-identities.js.map +1 -0
  27. package/dist/chunk-2ACYHZFB.js +292 -0
  28. package/dist/chunk-2ACYHZFB.js.map +1 -0
  29. package/dist/chunk-2VORLN2R.js +353 -0
  30. package/dist/chunk-2VORLN2R.js.map +1 -0
  31. package/dist/{chunk-Y3MLSLB4.js → chunk-3SIZGL3O.js} +42 -30
  32. package/dist/chunk-3SIZGL3O.js.map +1 -0
  33. package/dist/chunk-46KLD2UH.js +186 -0
  34. package/dist/chunk-46KLD2UH.js.map +1 -0
  35. package/dist/chunk-4JGHSSB7.js +11 -0
  36. package/dist/chunk-4JGHSSB7.js.map +1 -0
  37. package/dist/{chunk-52SGB2QO.js → chunk-5IXYF6XN.js} +50 -10
  38. package/dist/chunk-5IXYF6XN.js.map +1 -0
  39. package/dist/chunk-BCS7WHCK.js +139 -0
  40. package/dist/chunk-BCS7WHCK.js.map +1 -0
  41. package/dist/chunk-EK7NDGFR.js +128 -0
  42. package/dist/chunk-EK7NDGFR.js.map +1 -0
  43. package/dist/{chunk-5UDNP7AO.js → chunk-FZ6BXQHL.js} +78 -38
  44. package/dist/chunk-FZ6BXQHL.js.map +1 -0
  45. package/dist/chunk-GRG7PHWQ.js +25 -0
  46. package/dist/chunk-GRG7PHWQ.js.map +1 -0
  47. package/dist/chunk-JUFK3T4Q.js +80 -0
  48. package/dist/chunk-JUFK3T4Q.js.map +1 -0
  49. package/dist/chunk-KHHZTBP7.js +11 -0
  50. package/dist/chunk-KHHZTBP7.js.map +1 -0
  51. package/dist/chunk-KR2V42UZ.js +187 -0
  52. package/dist/chunk-KR2V42UZ.js.map +1 -0
  53. package/dist/chunk-MZLBPKFE.js +168 -0
  54. package/dist/chunk-MZLBPKFE.js.map +1 -0
  55. package/dist/{chunk-H5BVCZKF.js → chunk-NRMCVHAU.js} +6 -5
  56. package/dist/chunk-NRMCVHAU.js.map +1 -0
  57. package/dist/{chunk-TZ6YEYN4.js → chunk-NVJMJWLL.js} +3 -1
  58. package/dist/chunk-NVJMJWLL.js.map +1 -0
  59. package/dist/chunk-O77VSFYZ.js +70 -0
  60. package/dist/chunk-O77VSFYZ.js.map +1 -0
  61. package/dist/chunk-PX3VCKAU.js +29 -0
  62. package/dist/chunk-PX3VCKAU.js.map +1 -0
  63. package/dist/chunk-QR5D2IQC.js +61 -0
  64. package/dist/chunk-QR5D2IQC.js.map +1 -0
  65. package/dist/chunk-RJ76DRPO.js +117 -0
  66. package/dist/chunk-RJ76DRPO.js.map +1 -0
  67. package/dist/chunk-RKX3NQYS.js +28 -0
  68. package/dist/chunk-RKX3NQYS.js.map +1 -0
  69. package/dist/{chunk-FJBG5E4O.js → chunk-SIOU2K4U.js} +45 -22
  70. package/dist/chunk-SIOU2K4U.js.map +1 -0
  71. package/dist/chunk-TSCDAGET.js +75 -0
  72. package/dist/chunk-TSCDAGET.js.map +1 -0
  73. package/dist/{chunk-LSB7GRLG.js → chunk-TXTQIYCV.js} +15 -11
  74. package/dist/chunk-TXTQIYCV.js.map +1 -0
  75. package/dist/chunk-X7ETVO4P.js +26544 -0
  76. package/dist/chunk-X7ETVO4P.js.map +1 -0
  77. package/dist/{chunk-CADSX6UN.js → chunk-Y3KNXVTD.js} +2 -2
  78. package/dist/chunk-YBW5JSA3.js +251 -0
  79. package/dist/chunk-YBW5JSA3.js.map +1 -0
  80. package/dist/chunk-YRXAWWCU.js +45 -0
  81. package/dist/chunk-YRXAWWCU.js.map +1 -0
  82. package/dist/claude-provider-account-authority.d.ts +18 -0
  83. package/dist/claude-subscription-oauth.d.ts +42 -0
  84. package/dist/claude-subscription-usage.d.ts +159 -0
  85. package/dist/client-error-report.d.ts +109 -0
  86. package/dist/client-error-report.js +87 -0
  87. package/dist/client-error-report.js.map +1 -0
  88. package/dist/code-search.d.ts +52 -0
  89. package/dist/code-search.js +40 -0
  90. package/dist/code-search.js.map +1 -0
  91. package/dist/codex-provider-account-authority.d.ts +23 -0
  92. package/dist/codex-provider-account-authority.js +23 -0
  93. package/dist/codex-provider-account-authority.js.map +1 -0
  94. package/dist/company-brain-governed-writes.d.ts +475 -0
  95. package/dist/company-brain.d.ts +336 -0
  96. package/dist/company-profile.d.ts +566 -0
  97. package/dist/connect.d.ts +325 -0
  98. package/dist/connect.js +191 -0
  99. package/dist/connect.js.map +1 -0
  100. package/dist/connection-account-label.d.ts +2 -0
  101. package/dist/connection-account-label.js +35 -0
  102. package/dist/connection-account-label.js.map +1 -0
  103. package/dist/connection-authority.d.ts +193 -0
  104. package/dist/connection-authority.js +50 -0
  105. package/dist/connection-authority.js.map +1 -0
  106. package/dist/connector-attachments.d.ts +95 -0
  107. package/dist/connector-attachments.js +51 -0
  108. package/dist/connector-attachments.js.map +1 -0
  109. package/dist/connector-tool-permissions.d.ts +73 -0
  110. package/dist/direct-model-provider.d.ts +36 -0
  111. package/dist/editable-artifact-codec-registry.d.ts +16 -7
  112. package/dist/editable-artifact-codec-registry.js +19 -9
  113. package/dist/editable-artifact-committed-transaction.d.ts +2 -2
  114. package/dist/editable-artifact-committed-transaction.js +2 -2
  115. package/dist/editable-artifact-live.d.ts +11 -12
  116. package/dist/editable-artifact-live.js +110 -45
  117. package/dist/editable-artifact-live.js.map +1 -1
  118. package/dist/editable-artifact-serialized-commit.d.ts +2 -1
  119. package/dist/editable-artifact-serialized-commit.js +8 -7
  120. package/dist/editable-artifact-versions.d.ts +20 -5
  121. package/dist/editable-artifact-versions.js +23 -5
  122. package/dist/editable-artifacts.d.ts +2 -0
  123. package/dist/editable-artifacts.js +48 -36
  124. package/dist/external-identities.d.ts +784 -0
  125. package/dist/external-identities.js +44 -0
  126. package/dist/external-identities.js.map +1 -0
  127. package/dist/feedback.d.ts +51 -0
  128. package/dist/github-repository-contracts.d.ts +36 -0
  129. package/dist/github-repository-contracts.js +59 -0
  130. package/dist/github-repository-contracts.js.map +1 -0
  131. package/dist/github-repository.d.ts +14 -0
  132. package/dist/github-repository.js +36 -0
  133. package/dist/github-repository.js.map +1 -0
  134. package/dist/google-drive.d.ts +90 -1
  135. package/dist/google-drive.js +91 -15
  136. package/dist/google-drive.js.map +1 -1
  137. package/dist/governed-learning-activation.d.ts +154 -0
  138. package/dist/governed-learning-evaluator.d.ts +152 -0
  139. package/dist/host-mcp-bindings.d.ts +326 -0
  140. package/dist/host-mcp-bindings.js +252 -0
  141. package/dist/host-mcp-bindings.js.map +1 -0
  142. package/dist/image-generation.d.ts +1 -0
  143. package/dist/index.d.ts +27574 -5374
  144. package/dist/index.js +2531 -177
  145. package/dist/insights-usage.d.ts +877 -0
  146. package/dist/insights-usage.js +509 -0
  147. package/dist/insights-usage.js.map +1 -0
  148. package/dist/interaction.d.ts +1752 -13
  149. package/dist/knowledge-entries.d.ts +982 -0
  150. package/dist/knowledge-preparation.d.ts +237 -0
  151. package/dist/knowledge.d.ts +740 -0
  152. package/dist/managed-auth-session-sets.d.ts +241 -0
  153. package/dist/managed-auth-session-sets.js +191 -0
  154. package/dist/managed-auth-session-sets.js.map +1 -0
  155. package/dist/managed-sign-in-methods.d.ts +56 -0
  156. package/dist/managed-sign-in-methods.js +46 -0
  157. package/dist/managed-sign-in-methods.js.map +1 -0
  158. package/dist/mcp-catalog-limits.d.ts +2 -0
  159. package/dist/mcp-endpoint.d.ts +7 -0
  160. package/dist/mcp-endpoint.js +9 -0
  161. package/dist/mcp-endpoint.js.map +1 -0
  162. package/dist/mcp-oauth.d.ts +71 -0
  163. package/dist/modal-native-proof-v2.d.ts +587 -0
  164. package/dist/modal-native-proof-v2.js +37 -0
  165. package/dist/modal-native-proof-v2.js.map +1 -0
  166. package/dist/model-availability.d.ts +15 -0
  167. package/dist/model-connection-access.d.ts +24 -0
  168. package/dist/model-context-inspector.d.ts +329 -0
  169. package/dist/model-display.d.ts +38 -0
  170. package/dist/model-display.js +134 -0
  171. package/dist/model-display.js.map +1 -0
  172. package/dist/model-picker-order.d.ts +25 -0
  173. package/dist/model-picker-order.js +42 -0
  174. package/dist/model-picker-order.js.map +1 -0
  175. package/dist/organization-access.d.ts +231 -0
  176. package/dist/organization-access.js +34 -0
  177. package/dist/organization-access.js.map +1 -0
  178. package/dist/organization-integration-policy.d.ts +27 -0
  179. package/dist/organization-membership-lifecycle.d.ts +1022 -0
  180. package/dist/organization-model-usage.d.ts +146 -0
  181. package/dist/organization-model-usage.js +67 -0
  182. package/dist/organization-model-usage.js.map +1 -0
  183. package/dist/organization-recovery.d.ts +337 -0
  184. package/dist/organization-recovery.js +37 -0
  185. package/dist/organization-recovery.js.map +1 -0
  186. package/dist/organization-usage.d.ts +138 -0
  187. package/dist/permissions.d.ts +54 -0
  188. package/dist/personal-github.d.ts +320 -0
  189. package/dist/personal-github.js +222 -0
  190. package/dist/personal-github.js.map +1 -0
  191. package/dist/plugin-discovery.d.ts +35 -0
  192. package/dist/plugin-discovery.js +7 -0
  193. package/dist/plugin-discovery.js.map +1 -0
  194. package/dist/preference-registry.d.ts +44 -1
  195. package/dist/product-analytics.d.ts +113 -0
  196. package/dist/product-lifecycle-facts.d.ts +73 -0
  197. package/dist/remember.d.ts +350 -0
  198. package/dist/retained-output.d.ts +8 -0
  199. package/dist/sandbox-file-artifacts.d.ts +57 -0
  200. package/dist/sandbox-provider-command.d.ts +113 -0
  201. package/dist/sandbox-recovery.d.ts +146 -0
  202. package/dist/sandbox-snapshots.d.ts +39 -0
  203. package/dist/session-final-reply.d.ts +6 -0
  204. package/dist/session-final-reply.js +9 -0
  205. package/dist/session-final-reply.js.map +1 -0
  206. package/dist/session-goal-reports.d.ts +21 -0
  207. package/dist/session-history-import.d.ts +947 -0
  208. package/dist/session-list-entries.d.ts +4 -0
  209. package/dist/session-list-entries.js +51 -0
  210. package/dist/session-list-entries.js.map +1 -0
  211. package/dist/session-mcp-projections.d.ts +215 -0
  212. package/dist/session-message-search.d.ts +68 -0
  213. package/dist/session-titles.d.ts +68 -0
  214. package/dist/session-titles.js +25 -0
  215. package/dist/session-titles.js.map +1 -0
  216. package/dist/session-topology-primitives.d.ts +22 -0
  217. package/dist/signup-attribution.d.ts +39 -0
  218. package/dist/site-request-headers.test.d.ts +1 -0
  219. package/dist/site-session-http.d.ts +11 -0
  220. package/dist/site-session-http.js +11 -0
  221. package/dist/site-session-http.js.map +1 -0
  222. package/dist/skill-catalog-context.d.ts +7 -0
  223. package/dist/skill-files.d.ts +12 -0
  224. package/dist/skill-metadata.d.ts +9 -0
  225. package/dist/skill-use.d.ts +61 -0
  226. package/dist/skills.d.ts +185 -0
  227. package/dist/slack-bot-scopes.d.ts +46 -21
  228. package/dist/slack-bot-scopes.js +14 -3
  229. package/dist/slack-rest-mcp.d.ts +31 -0
  230. package/dist/slack-rest-mcp.js +19 -0
  231. package/dist/slack-rest-mcp.js.map +1 -0
  232. package/dist/slack-task-policy.d.ts +294 -0
  233. package/dist/spreadsheet-artifact-commands.d.ts +2 -3
  234. package/dist/spreadsheet-artifact-commands.js +4 -2
  235. package/dist/spreadsheet-artifact-query.js +3 -2
  236. package/dist/subscription-account-pools.d.ts +292 -0
  237. package/dist/task-notes.d.ts +169 -0
  238. package/dist/tool-catalog.d.ts +286 -0
  239. package/dist/tool-result-spill.d.ts +16 -0
  240. package/dist/usage-allowances.d.ts +285 -0
  241. package/dist/usage-allowances.js +49 -0
  242. package/dist/usage-allowances.js.map +1 -0
  243. package/dist/video-generation.d.ts +51 -0
  244. package/dist/video-generation.js +9 -1
  245. package/dist/work-claims.d.ts +411 -0
  246. package/dist/workspace-instruction-policies.d.ts +94 -0
  247. package/dist/workspace-integration-wire.d.ts +36 -0
  248. package/dist/workspace-integration-wire.js +23 -0
  249. package/dist/workspace-integration-wire.js.map +1 -0
  250. package/dist/workspace-integrations.d.ts +948 -0
  251. package/dist/workspace-learning-administration.d.ts +289 -0
  252. package/dist/workspace-learning-policy.d.ts +1 -1
  253. package/dist/workspace-state.d.ts +85 -416
  254. package/dist/xai-provider-account-authority.d.ts +26 -0
  255. package/dist/xai-provider-account-authority.js +9 -0
  256. package/dist/xai-provider-account-authority.js.map +1 -0
  257. package/package.json +146 -1
  258. package/src/agent-authored-durable-text.ts +116 -0
  259. package/src/agent-config.ts +1527 -0
  260. package/src/agent-instruction-changes.ts +98 -0
  261. package/src/agent-learning.ts +161 -0
  262. package/src/agent-topology.ts +69 -0
  263. package/src/allowance-refusal.ts +81 -0
  264. package/src/analytics-consent-report.ts +20 -0
  265. package/src/api-key-presets.ts +25 -0
  266. package/src/artifact-catalog.ts +60 -0
  267. package/src/artifacts.ts +146 -18
  268. package/src/atlassian-native-retirement.ts +33 -0
  269. package/src/browser-storage.ts +10 -0
  270. package/src/bundled-skills.ts +73 -0
  271. package/src/canonical-human-identities.ts +93 -0
  272. package/src/claude-provider-account-authority.ts +12 -0
  273. package/src/claude-subscription-oauth.ts +62 -0
  274. package/src/claude-subscription-usage.ts +59 -0
  275. package/src/client-error-report.ts +184 -0
  276. package/src/code-search.ts +105 -0
  277. package/src/codex-provider-account-authority.ts +37 -0
  278. package/src/company-brain-governed-writes.ts +356 -0
  279. package/src/company-brain.ts +201 -0
  280. package/src/company-profile.ts +167 -1
  281. package/src/connect.ts +202 -0
  282. package/src/connection-account-label.ts +46 -0
  283. package/src/connection-authority.ts +213 -0
  284. package/src/connector-attachments.ts +327 -0
  285. package/src/connector-tool-permissions.ts +60 -0
  286. package/src/direct-model-provider.ts +98 -0
  287. package/src/editable-artifact-codec-registry.ts +55 -17
  288. package/src/editable-artifact-committed-transaction.ts +44 -31
  289. package/src/editable-artifact-live.ts +123 -57
  290. package/src/editable-artifact-serialized-commit.ts +2 -1
  291. package/src/editable-artifact-versions.ts +26 -5
  292. package/src/editable-artifacts.ts +2 -0
  293. package/src/external-identities.ts +285 -0
  294. package/src/feedback.ts +39 -0
  295. package/src/github-repository-contracts.ts +85 -0
  296. package/src/github-repository.ts +59 -0
  297. package/src/google-drive.ts +76 -4
  298. package/src/governed-learning-activation.ts +191 -0
  299. package/src/governed-learning-evaluator.ts +176 -0
  300. package/src/host-mcp-bindings.ts +298 -0
  301. package/src/image-generation.ts +8 -2
  302. package/src/index.ts +8019 -1799
  303. package/src/insights-usage.ts +679 -0
  304. package/src/interaction.ts +1102 -6
  305. package/src/knowledge-entries.ts +375 -0
  306. package/src/knowledge-preparation.ts +50 -0
  307. package/src/knowledge.ts +335 -0
  308. package/src/managed-auth-session-sets.ts +253 -0
  309. package/src/managed-sign-in-methods.ts +37 -0
  310. package/src/mcp-catalog-limits.ts +2 -0
  311. package/src/mcp-endpoint.ts +30 -0
  312. package/src/mcp-oauth.ts +66 -0
  313. package/src/modal-native-proof-v2.ts +398 -0
  314. package/src/model-availability.ts +22 -0
  315. package/src/model-connection-access.ts +17 -0
  316. package/src/model-context-inspector.ts +139 -0
  317. package/src/model-display.ts +195 -0
  318. package/src/model-picker-order.ts +97 -0
  319. package/src/organization-access.ts +176 -0
  320. package/src/organization-integration-policy.ts +54 -0
  321. package/src/organization-membership-lifecycle.ts +559 -0
  322. package/src/organization-model-usage.ts +72 -0
  323. package/src/organization-recovery.ts +186 -0
  324. package/src/organization-usage.ts +79 -0
  325. package/src/permissions.ts +58 -0
  326. package/src/personal-github.ts +267 -0
  327. package/src/plugin-discovery.ts +33 -0
  328. package/src/preference-registry.ts +20 -1
  329. package/src/product-analytics.ts +253 -0
  330. package/src/product-lifecycle-facts.ts +121 -0
  331. package/src/remember.ts +243 -0
  332. package/src/retained-output.ts +20 -3
  333. package/src/sandbox-file-artifacts.ts +73 -0
  334. package/src/sandbox-provider-command.ts +82 -0
  335. package/src/sandbox-recovery.ts +116 -0
  336. package/src/sandbox-snapshots.ts +142 -0
  337. package/src/session-final-reply.ts +16 -0
  338. package/src/session-goal-reports.ts +55 -0
  339. package/src/session-history-import.ts +159 -0
  340. package/src/session-list-entries.ts +91 -0
  341. package/src/session-mcp-projections.ts +273 -0
  342. package/src/session-message-search.ts +54 -0
  343. package/src/session-titles.ts +467 -0
  344. package/src/session-topology-primitives.ts +21 -0
  345. package/src/signup-attribution.ts +81 -0
  346. package/src/site-request-headers.test.ts +28 -0
  347. package/src/site-session-http.ts +63 -0
  348. package/src/skill-catalog-context.ts +50 -0
  349. package/src/skill-files.ts +52 -0
  350. package/src/skill-metadata.ts +40 -0
  351. package/src/skill-use.ts +77 -0
  352. package/src/skills.ts +136 -0
  353. package/src/slack-bot-scopes.ts +106 -42
  354. package/src/slack-rest-mcp.ts +194 -0
  355. package/src/slack-task-policy.ts +215 -0
  356. package/src/spreadsheet-artifact-commands.ts +12 -13
  357. package/src/subscription-account-pools.ts +44 -0
  358. package/src/task-notes.ts +105 -0
  359. package/src/tool-catalog.ts +155 -0
  360. package/src/tool-result-spill.ts +35 -0
  361. package/src/usage-allowances.ts +187 -0
  362. package/src/video-generation.ts +43 -6
  363. package/src/work-claims.ts +266 -0
  364. package/src/workspace-instruction-policies.ts +31 -0
  365. package/src/workspace-integration-wire.ts +99 -0
  366. package/src/workspace-integrations.ts +640 -0
  367. package/src/workspace-learning-administration.ts +88 -0
  368. package/src/workspace-learning-policy.ts +4 -3
  369. package/src/workspace-state.ts +16 -126
  370. package/src/xai-provider-account-authority.ts +38 -0
  371. package/dist/chunk-52SGB2QO.js.map +0 -1
  372. package/dist/chunk-5UDNP7AO.js.map +0 -1
  373. package/dist/chunk-BVGIYW7D.js +0 -11
  374. package/dist/chunk-BVGIYW7D.js.map +0 -1
  375. package/dist/chunk-FJBG5E4O.js.map +0 -1
  376. package/dist/chunk-H5BVCZKF.js.map +0 -1
  377. package/dist/chunk-LSB7GRLG.js.map +0 -1
  378. package/dist/chunk-TZ6YEYN4.js.map +0 -1
  379. package/dist/chunk-VA22KRHS.js +0 -15177
  380. package/dist/chunk-VA22KRHS.js.map +0 -1
  381. package/dist/chunk-Y3MLSLB4.js.map +0 -1
  382. /package/dist/{chunk-CADSX6UN.js.map → chunk-Y3KNXVTD.js.map} +0 -0
@@ -0,0 +1,50 @@
1
+ /** Durable, model-visible catalog context. Text markers survive SDK/provider replay. */
2
+ export const SKILL_CATALOG_CONTEXT_PREFIX = "<opengeni_skill_catalog>\n";
3
+ export const SKILL_CATALOG_CONTEXT_SUFFIX = "\n</opengeni_skill_catalog>";
4
+ const GUIDANCE =
5
+ "This is the current configured Skill index and replaces earlier configured Skill indexes. " +
6
+ "Entries describe discoverability, not permission or full instructions. " +
7
+ "Use skill_read for instructions and skill_search for current discovery; tool authorization remains authoritative.\n\n";
8
+
9
+ export function skillCatalogContextItem(catalog: string): Record<string, unknown> {
10
+ return {
11
+ type: "message",
12
+ role: "developer",
13
+ content: SKILL_CATALOG_CONTEXT_PREFIX + GUIDANCE + catalog + SKILL_CATALOG_CONTEXT_SUFFIX,
14
+ };
15
+ }
16
+
17
+ /** Accept the Chat Completions projection and SDK text-part normalization too. */
18
+ export function readSkillCatalogContext(item: Record<string, unknown>): string | null {
19
+ if (item.type === "unknown" && item.providerData && typeof item.providerData === "object") {
20
+ const data = item.providerData as Record<string, unknown>;
21
+ if (data.type === "message") return readSkillCatalogContext(data);
22
+ }
23
+ if (item.type !== "message" || (item.role !== "developer" && item.role !== "system")) return null;
24
+ const content =
25
+ typeof item.content === "string"
26
+ ? item.content
27
+ : Array.isArray(item.content) &&
28
+ item.content.every(
29
+ (part) =>
30
+ part &&
31
+ (part.type === "input_text" || part.type === "output_text" || part.type === "text") &&
32
+ typeof part.text === "string",
33
+ )
34
+ ? item.content.map((part) => part.text).join("")
35
+ : null;
36
+ const prefix = SKILL_CATALOG_CONTEXT_PREFIX + GUIDANCE;
37
+ return content?.startsWith(prefix) && content.endsWith(SKILL_CATALOG_CONTEXT_SUFFIX)
38
+ ? content.slice(prefix.length, -SKILL_CATALOG_CONTEXT_SUFFIX.length)
39
+ : null;
40
+ }
41
+
42
+ export function latestSkillCatalogContext(
43
+ items: readonly Record<string, unknown>[],
44
+ ): Record<string, unknown> | undefined {
45
+ for (let index = items.length - 1; index >= 0; index--) {
46
+ const catalog = readSkillCatalogContext(items[index]!);
47
+ if (catalog !== null) return skillCatalogContextItem(catalog);
48
+ }
49
+ return undefined;
50
+ }
@@ -0,0 +1,52 @@
1
+ /** Shared text-folder limits for API admission, runtime imports and checkout. */
2
+ export const SKILL_MAX_FILES = 1024;
3
+ export const SKILL_MAX_FILE_BYTES = 2 * 1024 * 1024;
4
+ export const SKILL_MAX_TOTAL_BYTES = 8 * 1024 * 1024;
5
+
6
+ export function isSafeSkillRelativePath(path: string): boolean {
7
+ return (
8
+ path.length > 0 &&
9
+ path.length <= 512 &&
10
+ !/[\\:\u0000-\u001f\u007f]/u.test(path) &&
11
+ path.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..") &&
12
+ new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
13
+ new TextEncoder().encode(path),
14
+ ) === path
15
+ );
16
+ }
17
+
18
+ /** Validates bytes, not extensions. Does not rewrite text or interpret metadata. */
19
+ export function validateSkillTextFiles(files: readonly { path: string; content: string }[]): {
20
+ totalBytes: number;
21
+ } {
22
+ if (files.length === 0 || files.length > SKILL_MAX_FILES)
23
+ throw new Error(`Skill artifact must contain 1-${SKILL_MAX_FILES} files`);
24
+ const paths = new Set<string>();
25
+ const encoder = new TextEncoder();
26
+ const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
27
+ let totalBytes = 0;
28
+ for (const { path, content } of files) {
29
+ if (!isSafeSkillRelativePath(path))
30
+ throw new Error(`Skill artifact contains an unsafe path: ${path}`);
31
+ if (paths.has(path)) throw new Error(`Skill artifact contains duplicate file path: ${path}`);
32
+ paths.add(path);
33
+ if (content.includes("\u0000")) throw new Error(`Skill artifact contains NUL bytes: ${path}`);
34
+ const bytes = encoder.encode(content);
35
+ if (decoder.decode(bytes) !== content)
36
+ throw new Error(`Skill artifact contains malformed Unicode text: ${path}`);
37
+ if (bytes.byteLength > SKILL_MAX_FILE_BYTES)
38
+ throw new Error(`Skill artifact file exceeds ${SKILL_MAX_FILE_BYTES} bytes: ${path}`);
39
+ totalBytes += bytes.byteLength;
40
+ if (totalBytes > SKILL_MAX_TOTAL_BYTES)
41
+ throw new Error(`Skill artifact exceeds ${SKILL_MAX_TOTAL_BYTES} bytes`);
42
+ }
43
+ for (const path of paths) {
44
+ const segments = path.split("/");
45
+ for (let index = 1; index < segments.length; index++) {
46
+ const prefix = segments.slice(0, index).join("/");
47
+ if (paths.has(prefix))
48
+ throw new Error(`Skill artifact uses a path as both a file and a directory: ${prefix}`);
49
+ }
50
+ }
51
+ return { totalBytes };
52
+ }
@@ -0,0 +1,40 @@
1
+ import { parseDocument } from "yaml";
2
+
3
+ /** The one interpretation of Skill metadata for imports, writes and migration. */
4
+ export function parseSkillFrontmatter(markdown: string): {
5
+ name: string | null;
6
+ description: string | null;
7
+ } {
8
+ const lines = markdown.split(/\r?\n/u);
9
+ if (lines[0]?.trim() !== "---") return { name: null, description: null };
10
+ const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
11
+ if (end === -1) return { name: null, description: null };
12
+ const document = parseDocument(lines.slice(1, end).join("\n"), {
13
+ schema: "core",
14
+ uniqueKeys: true,
15
+ prettyErrors: false,
16
+ });
17
+ if (document.errors.length || document.warnings.length) {
18
+ const issue = document.errors[0] ?? document.warnings[0]!;
19
+ throw new Error(
20
+ `This skill's SKILL.md header contains invalid YAML: ${issue.message}. The source file needs correcting.`,
21
+ );
22
+ }
23
+ const metadata: unknown = document.toJS({ maxAliasCount: 20 });
24
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
25
+ throw new Error("Skill artifact SKILL.md frontmatter must be a mapping");
26
+ const fields = metadata as Record<string, unknown>;
27
+ return {
28
+ name: typeof fields.name === "string" ? fields.name : null,
29
+ description: typeof fields.description === "string" ? fields.description : null,
30
+ };
31
+ }
32
+
33
+ export function readSkillMetadata(markdown: string): { name: string; description: string } {
34
+ const { name, description } = parseSkillFrontmatter(markdown);
35
+ if (!name || name.length > 64 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(name))
36
+ throw new Error("Skill artifact SKILL.md must declare a safe name");
37
+ if (!description?.trim() || description.length > 1024)
38
+ throw new Error("Skill artifact SKILL.md must declare a description of 1–1024 characters");
39
+ return { name, description };
40
+ }
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * `_meta` key of the content-free Skill-use fact on a model `skill_read`
5
+ * result. MCP `_meta` never reaches the model: it survives only into the
6
+ * `agent.toolCall.output` event projection, never into model history.
7
+ */
8
+ export const SKILL_USE_META_KEY = "opengeni/skillUse" as const;
9
+
10
+ /** Where the Skill a read resolved to comes from. `personal` is a user-scope Skill. */
11
+ export const SkillUseSource = z.enum([
12
+ "builtin",
13
+ "session",
14
+ "workspace",
15
+ "organization",
16
+ "personal",
17
+ ]);
18
+ export type SkillUseSource = z.infer<typeof SkillUseSource>;
19
+
20
+ /**
21
+ * What a `skill_read` returned: `full` is the default SKILL.md read, `files`
22
+ * explicit paths, `list` a path inventory, and `already_in_context` the short
23
+ * repeat receipt. `refused` means the read returned an error instead of Skill
24
+ * text; it has no result, so it appears only in metrics.
25
+ */
26
+ export const SkillReadKind = z.enum(["full", "already_in_context", "files", "list", "refused"]);
27
+ export type SkillReadKind = z.infer<typeof SkillReadKind>;
28
+
29
+ const skillUseFields = {
30
+ id: z.string().min(1).max(512),
31
+ source: SkillUseSource,
32
+ label: z.string().min(1).max(128).optional(),
33
+ /** Whole-artifact digest, for a Skill without a ledger revision. */
34
+ contentSha256: z
35
+ .string()
36
+ .regex(/^[0-9a-f]{64}$/u)
37
+ .optional(),
38
+ /** Ledger revision of a workspace, organization, or personal Skill. */
39
+ revisionId: z.string().min(1).max(128).optional(),
40
+ kind: SkillReadKind.exclude(["refused"]),
41
+ /** UTF-8 bytes of the result text the read returned, before model tool-output truncation. */
42
+ bytes: z.number().int().nonnegative(),
43
+ /** The Skill appeared in the Skill index the model saw this turn. */
44
+ inIndex: z.boolean(),
45
+ /** skill_search returned this Skill earlier in the same turn attempt. */
46
+ searchedThisTurn: z.boolean(),
47
+ };
48
+ const oneProvenance = (use: {
49
+ contentSha256?: string | undefined;
50
+ revisionId?: string | undefined;
51
+ }) => use.contentSha256 === undefined || use.revisionId === undefined;
52
+ const oneProvenanceMessage = {
53
+ message: "A Skill use carries a content digest or a revision, not both",
54
+ };
55
+
56
+ /**
57
+ * Ids, digests, and counts only. Never Skill text, user text, or a Skill's
58
+ * title or description. `label` is reserved for a platform display label of a
59
+ * built-in; user-authored Skills never set it. This is the writer's closed
60
+ * shape: an unknown field fails the parse, so nothing else can be attached.
61
+ */
62
+ export const SkillUse = z.strictObject(skillUseFields).refine(oneProvenance, oneProvenanceMessage);
63
+ export type SkillUse = z.infer<typeof SkillUse>;
64
+
65
+ // Stored events outlive the worker that wrote them. A reader drops fields a
66
+ // newer writer added instead of dropping the whole fact, and what it returns
67
+ // still holds only the known, content-free fields.
68
+ const StoredSkillUse = z.object(skillUseFields).refine(oneProvenance, oneProvenanceMessage);
69
+
70
+ /** The Skill-use fact on a tool result or its event projection, when present and valid. */
71
+ export function skillUseFromToolOutput(output: unknown): SkillUse | null {
72
+ if (!output || typeof output !== "object" || Array.isArray(output)) return null;
73
+ const meta = (output as { _meta?: unknown })._meta;
74
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return null;
75
+ const parsed = StoredSkillUse.safeParse((meta as Record<string, unknown>)[SKILL_USE_META_KEY]);
76
+ return parsed.success ? parsed.data : null;
77
+ }
package/src/skills.ts ADDED
@@ -0,0 +1,136 @@
1
+ import { z } from "zod";
2
+ export { parseSkillFrontmatter, readSkillMetadata } from "./skill-metadata";
3
+ export * from "./skill-files";
4
+
5
+ /** Files are UTF-8 text, relative to the Skill root. No executable authority. */
6
+ export const SkillFile = z.object({ path: z.string().min(1).max(512), content: z.string() });
7
+ export type SkillFile = z.infer<typeof SkillFile>;
8
+ export const SkillActor = z.discriminatedUnion("kind", [
9
+ z.object({
10
+ kind: z.literal("service"),
11
+ subjectId: z.string().min(1),
12
+ principalKind: z.enum(["service", "api_key", "configured_key"]),
13
+ }),
14
+ z.object({
15
+ kind: z.literal("human"),
16
+ subjectId: z.string().min(1),
17
+ principalKind: z.literal("human_session"),
18
+ }),
19
+ z.object({
20
+ kind: z.literal("agent"),
21
+ sessionId: z.uuid(),
22
+ turnId: z.uuid(),
23
+ attemptId: z.uuid(),
24
+ executionGeneration: z.number().int().positive(),
25
+ }),
26
+ ]);
27
+ export type SkillActor = z.infer<typeof SkillActor>;
28
+ export type SkillScope = "workspace" | "organization" | "user";
29
+ export type SkillWriteContext = { accountId: string; workspaceId: string; actor: SkillActor };
30
+ export type SkillSaveInput = SkillWriteContext & {
31
+ operationId: string;
32
+ skillId: string;
33
+ expectedRevisionId: string | null;
34
+ expectedScopeVersion: number;
35
+ stableKey: string;
36
+ files: SkillFile[];
37
+ scope?: SkillScope;
38
+ reason: string;
39
+ };
40
+ export type SkillRevisionInput = SkillWriteContext & {
41
+ removalOperationId?: string | undefined;
42
+ operationId: string;
43
+ skillId: string;
44
+ revisionId: string;
45
+ expectedRevisionId: string | null;
46
+ expectedScopeVersion: number;
47
+ reason: string;
48
+ };
49
+ export type SkillRemoveInput = SkillWriteContext & {
50
+ operationId: string;
51
+ skillId: string;
52
+ expectedRevisionId: string | null;
53
+ expectedScopeVersion: number;
54
+ reason: string;
55
+ };
56
+ export type SkillInstallInput = SkillWriteContext & {
57
+ operationId: string;
58
+ skillFacetId: string;
59
+ reason: string;
60
+ };
61
+ export const SkillReviewReference = z.object({
62
+ removalOperationId: z.uuid().optional(),
63
+ sourceOperationId: z.uuid(),
64
+ skillId: z.uuid(),
65
+ revisionId: z.uuid(),
66
+ expectedRevisionId: z.uuid().nullable(),
67
+ expectedScopeVersion: z.number().int().positive(),
68
+ });
69
+ export type SkillReviewReference = z.infer<typeof SkillReviewReference>;
70
+ export function skillReviewHumanInput(skillReview: SkillReviewReference) {
71
+ const removing = Boolean(skillReview.removalOperationId);
72
+ return {
73
+ questions: [
74
+ {
75
+ id: `skill:${skillReview.revisionId}`,
76
+ kind: "single_select" as const,
77
+ label: removing ? "Permanently delete this Skill?" : "Save this Skill?",
78
+ prompt: removing
79
+ ? "Permanently delete this Skill and all its stored revisions?"
80
+ : "Save this exact Skill revision for this workspace?",
81
+ helpText: removing
82
+ ? "This cannot be undone. All stored Skill revisions will be deleted; conversations remain unchanged."
83
+ : "Review the complete files before saving. Saving activates this revision immediately.",
84
+ options: [
85
+ { id: "save", label: removing ? "Permanently delete" : "Save" },
86
+ { id: "skip", label: removing ? "Keep Skill" : "Don't save" },
87
+ ],
88
+ required: true,
89
+ allowOther: false,
90
+ skillReview,
91
+ },
92
+ ],
93
+ };
94
+ }
95
+ export const SkillWriteReceipt = z.object({
96
+ removed: z.boolean().optional(),
97
+ operationId: z.uuid(),
98
+ skillId: z.uuid(),
99
+ revisionId: z.uuid(),
100
+ outcome: z.enum(["applied", "pending", "preserved"]),
101
+ decision: z.literal("rejected").optional(),
102
+ pendingReason: z.enum(["approval", "source_finalization"]).optional(),
103
+ skillReview: SkillReviewReference.optional(),
104
+ replayed: z.boolean(),
105
+ });
106
+ export type SkillWriteReceipt = z.infer<typeof SkillWriteReceipt>;
107
+ export const SkillPublicationReceipt = SkillWriteReceipt.extend({
108
+ sourceOperationId: z.uuid(),
109
+ activationEventId: z.uuid().nullable(),
110
+ });
111
+ export type SkillPublicationReceipt = z.infer<typeof SkillPublicationReceipt>;
112
+ export const SkillSourceReleaseReceipt = z.object({
113
+ skillId: z.uuid(),
114
+ revisionId: z.uuid().nullable(),
115
+ disposition: z.enum(["deactivated", "preserved", "inactive"]),
116
+ eventId: z.uuid().nullable(),
117
+ warning: z.string().nullable(),
118
+ });
119
+ export type SkillSourceReleaseReceipt = z.infer<typeof SkillSourceReleaseReceipt>;
120
+ export type SkillRecord = {
121
+ removalOperationId?: string | null;
122
+ activationMode: "workspace_managed" | "session_selected";
123
+ pendingRevisionIds: string[];
124
+ id: string;
125
+ stableKey: string;
126
+ scope: SkillScope;
127
+ scopeVersion: number;
128
+ status: string;
129
+ activeRevisionId: string | null;
130
+ revisionId: string | null;
131
+ title: string | null;
132
+ description: string | null;
133
+ contentHash: string | null;
134
+ files: SkillFile[];
135
+ source: { pluginId: string; facetKey: string; skillFacetId: string } | null;
136
+ };
@@ -1,3 +1,5 @@
1
+ import { OPENGENI_SLACK_REST_USER_SCOPES } from "./slack-rest-mcp";
2
+
1
3
  export const OPENGENI_SLACK_BOT_REQUIRED_SCOPES = [
2
4
  "app_mentions:read",
3
5
  "canvases:read",
@@ -19,20 +21,45 @@ export const OPENGENI_SLACK_BOT_REQUIRED_SCOPES = [
19
21
  /** Read-only scope needed only for the optional emoji-reaction summon surface. */
20
22
  export const OPENGENI_SLACK_REACTION_REQUIRED_SCOPE = "reactions:read" as const;
21
23
 
24
+ /** Optional grant for explicit retained-file delivery to a task's Slack thread. */
25
+ export const OPENGENI_SLACK_FILE_UPLOAD_REQUIRED_SCOPE = "files:write" as const;
26
+
27
+ /**
28
+ * Bot-token search scopes for Slack's Real-time Search API
29
+ * (`assistant.search.context`). Slack accepts these as BOT scopes, so
30
+ * workspace-wide public search runs under the bot identity and never needs a
31
+ * human proxy token. Private-channel, DM, and MPIM search remain user-token
32
+ * only and belong exclusively to the personal hosted-MCP grant.
33
+ */
34
+ export const OPENGENI_SLACK_BOT_SEARCH_SCOPES = [
35
+ "search:read.public",
36
+ "search:read.files",
37
+ "search:read.users",
38
+ ] as const;
39
+
22
40
  /**
23
41
  * Scopes requested by the managed and generated self-hosted manifests.
24
42
  *
25
- * `reactions:read` is deliberately not part of the base eligibility contract:
26
- * legacy installations may continue using existing Slack interactions and
27
- * tools while the reaction setting stays disabled and the UI asks an admin to
28
- * reinstall.
43
+ * `reactions:read`, `files:write`, and the bot search scopes are not part of the
44
+ * base eligibility contract: legacy installations may continue using existing
45
+ * Slack interactions and tools while the reaction setting stays disabled, bot
46
+ * search and file delivery stay unavailable, and the tools ask an admin to reinstall.
29
47
  */
30
48
  export const OPENGENI_SLACK_BOT_REQUESTED_SCOPES = [
31
49
  ...OPENGENI_SLACK_BOT_REQUIRED_SCOPES,
32
50
  OPENGENI_SLACK_REACTION_REQUIRED_SCOPE,
51
+ OPENGENI_SLACK_FILE_UPLOAD_REQUIRED_SCOPE,
33
52
  ] as const;
34
53
 
54
+ export function openGeniSlackBotRequestedScopes(mode: "limited" | "full" = "limited"): string[] {
55
+ return [
56
+ ...OPENGENI_SLACK_BOT_REQUESTED_SCOPES,
57
+ ...(mode === "full" ? OPENGENI_SLACK_BOT_SEARCH_SCOPES : []),
58
+ ];
59
+ }
60
+
35
61
  export const OPENGENI_SLACK_BOT_EVENTS = [
62
+ "app_home_opened",
36
63
  "app_mention",
37
64
  "message.channels",
38
65
  "message.groups",
@@ -41,37 +68,42 @@ export const OPENGENI_SLACK_BOT_EVENTS = [
41
68
  "reaction_added",
42
69
  ] as const;
43
70
 
44
- /** Full user-token scope set for every tool currently exposed by Slack's hosted MCP server. */
45
- export const OPENGENI_SLACK_MCP_USER_SCOPES = [
46
- "search:read.public",
47
- "search:read.private",
48
- "search:read.mpim",
49
- "search:read.im",
50
- "search:read.files",
51
- "files:read",
52
- "emoji:read",
53
- "search:read.users",
54
- "chat:write",
55
- "channels:history",
56
- "groups:history",
57
- "mpim:history",
58
- "im:history",
59
- "channels:write",
60
- "groups:write",
61
- "im:write",
62
- "mpim:write",
63
- "reactions:write",
64
- "canvases:read",
65
- "canvases:write",
66
- "users:read",
67
- "users:read.email",
68
- "channels:read",
69
- "groups:read",
70
- "mpim:read",
71
- ] as const;
71
+ /** User grants required by the reviewed Slack API MCP tools. */
72
+ export const OPENGENI_SLACK_MCP_USER_SCOPES = OPENGENI_SLACK_REST_USER_SCOPES;
72
73
 
73
74
  export const OPENGENI_MANAGED_PUBLIC_BASE_URL = "https://app.opengeni.ai" as const;
74
75
 
76
+ export type OpenGeniSlackBotManifestOptions = Readonly<{
77
+ appName?: string;
78
+ botDisplayName?: string;
79
+ slashCommand?: string;
80
+ shortcutName?: string;
81
+ accessMode?: "limited" | "full";
82
+ }>;
83
+
84
+ function normalizedSlackManifestText(
85
+ value: string | undefined,
86
+ fallback: string,
87
+ label: string,
88
+ maxLength: number,
89
+ ): string {
90
+ const normalized = value?.trim() || fallback;
91
+ if (normalized.length > maxLength || /[\r\n\0]/u.test(normalized)) {
92
+ throw new Error(`Slack bot manifest ${label} must be at most ${maxLength} plain characters`);
93
+ }
94
+ return normalized;
95
+ }
96
+
97
+ function normalizedSlackCommand(value: string | undefined): string {
98
+ const command = value?.trim() || "/opengeni";
99
+ if (!/^\/[a-z0-9_-]{1,31}$/u.test(command)) {
100
+ throw new Error(
101
+ "Slack bot manifest slash command must start with / and contain at most 31 lowercase letters, digits, hyphens, or underscores",
102
+ );
103
+ }
104
+ return command;
105
+ }
106
+
75
107
  function normalizedSlackManifestBaseUrl(publicBaseUrl: string): string {
76
108
  const parsed = new URL(publicBaseUrl);
77
109
  if (parsed.protocol !== "https:" || parsed.username || parsed.password) {
@@ -84,16 +116,38 @@ function normalizedSlackManifestBaseUrl(publicBaseUrl: string): string {
84
116
  }
85
117
 
86
118
  /** Canonical managed/self-hosted Slack app manifest. Slack accepts JSON manifests. */
87
- export function buildOpenGeniSlackBotManifest(publicBaseUrl: string) {
119
+ export function buildOpenGeniSlackBotManifest(
120
+ publicBaseUrl: string,
121
+ options: OpenGeniSlackBotManifestOptions = {},
122
+ ) {
88
123
  const baseUrl = normalizedSlackManifestBaseUrl(publicBaseUrl);
124
+ const appName = normalizedSlackManifestText(options.appName, "OpenGeni", "app name", 35);
125
+ const botDisplayName = normalizedSlackManifestText(
126
+ options.botDisplayName,
127
+ "OpenGeni",
128
+ "bot display name",
129
+ 80,
130
+ );
131
+ const slashCommand = normalizedSlackCommand(options.slashCommand);
132
+ const shortcutName = normalizedSlackManifestText(
133
+ options.shortcutName,
134
+ "Open in Opengeni",
135
+ "shortcut name",
136
+ 35,
137
+ );
89
138
  return {
90
- display_information: { name: "OpenGeni" },
139
+ display_information: { name: appName },
91
140
  features: {
92
- bot_user: { display_name: "OpenGeni", always_online: false },
141
+ app_home: {
142
+ home_tab_enabled: true,
143
+ messages_tab_enabled: true,
144
+ messages_tab_read_only_enabled: false,
145
+ },
146
+ bot_user: { display_name: botDisplayName, always_online: false },
93
147
  slash_commands: [
94
148
  {
95
- command: "/opengeni",
96
- description: "Start an OpenGeni task in this channel",
149
+ command: slashCommand,
150
+ description: "Start an Opengeni task in this channel",
97
151
  should_escape: false,
98
152
  url: `${baseUrl}/v1/integrations/slack/commands`,
99
153
  },
@@ -101,8 +155,8 @@ export function buildOpenGeniSlackBotManifest(publicBaseUrl: string) {
101
155
  shortcuts: [
102
156
  {
103
157
  callback_id: "opengeni_message",
104
- description: "Start an OpenGeni task from this Slack message",
105
- name: "Open in OpenGeni",
158
+ description: "Start an Opengeni task from this Slack message",
159
+ name: shortcutName,
106
160
  type: "message",
107
161
  },
108
162
  ],
@@ -113,7 +167,7 @@ export function buildOpenGeniSlackBotManifest(publicBaseUrl: string) {
113
167
  `${baseUrl}/v1/integrations/slack/callback`,
114
168
  ],
115
169
  scopes: {
116
- bot: [...OPENGENI_SLACK_BOT_REQUESTED_SCOPES],
170
+ bot: openGeniSlackBotRequestedScopes(options.accessMode),
117
171
  user: [...OPENGENI_SLACK_MCP_USER_SCOPES],
118
172
  },
119
173
  },
@@ -135,15 +189,25 @@ export function buildOpenGeniSlackBotManifest(publicBaseUrl: string) {
135
189
  }
136
190
 
137
191
  /**
138
- * Optional bot grants that remain inside the shipped bot's read/identity
139
- * boundary. Every other unrequired scope fails closed, including unknown future
192
+ * Optional bot grants used by the shipped read/identity and task-file-delivery
193
+ * surfaces. Every other unrequired scope fails closed, including unknown future
140
194
  * Slack scopes, so verification, core routing, and UI eligibility cannot drift.
141
195
  */
142
196
  export const OPENGENI_SLACK_BOT_SAFE_OPTIONAL_SCOPES = [
143
197
  "team:read",
144
198
  OPENGENI_SLACK_REACTION_REQUIRED_SCOPE,
199
+ OPENGENI_SLACK_FILE_UPLOAD_REQUIRED_SCOPE,
200
+ ...OPENGENI_SLACK_BOT_SEARCH_SCOPES,
145
201
  ] as const;
146
202
 
203
+ export function hasOpenGeniSlackFileUploadScope(grantedScopes: readonly string[]): boolean {
204
+ return grantedScopes.includes(OPENGENI_SLACK_FILE_UPLOAD_REQUIRED_SCOPE);
205
+ }
206
+
207
+ export function hasOpenGeniSlackBotSearchScopes(grantedScopes: readonly string[]): boolean {
208
+ return OPENGENI_SLACK_BOT_SEARCH_SCOPES.every((scope) => grantedScopes.includes(scope));
209
+ }
210
+
147
211
  /** @deprecated Use evaluateOpenGeniSlackBotScopes; an allowlist is the policy. */
148
212
  export const OPENGENI_SLACK_BOT_FORBIDDEN_SCOPES = ["channels:join", "chat:write.public"] as const;
149
213