@stigmer/server 3.22.1-dev.20260922095755 → 3.23.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 (330) hide show
  1. package/dist/artifactstorage/artifact-storage.d.ts +29 -31
  2. package/dist/artifactstorage/artifact-storage.d.ts.map +1 -1
  3. package/dist/artifactstorage/artifact-storage.js +5 -7
  4. package/dist/artifactstorage/artifact-storage.js.map +1 -1
  5. package/dist/artifactstorage/r2-storage.d.ts +12 -7
  6. package/dist/artifactstorage/r2-storage.d.ts.map +1 -1
  7. package/dist/artifactstorage/r2-storage.js +6 -14
  8. package/dist/artifactstorage/r2-storage.js.map +1 -1
  9. package/dist/authorization/authorizer.d.ts +8 -4
  10. package/dist/authorization/authorizer.d.ts.map +1 -1
  11. package/dist/authorization/authorizer.js +9 -5
  12. package/dist/authorization/authorizer.js.map +1 -1
  13. package/dist/authorization/derived-tuples.d.ts.map +1 -1
  14. package/dist/authorization/derived-tuples.js +25 -10
  15. package/dist/authorization/derived-tuples.js.map +1 -1
  16. package/dist/authorization/evaluator.d.ts +8 -7
  17. package/dist/authorization/evaluator.d.ts.map +1 -1
  18. package/dist/authorization/evaluator.js +20 -4
  19. package/dist/authorization/evaluator.js.map +1 -1
  20. package/dist/authorization/list-read-scope.d.ts.map +1 -1
  21. package/dist/authorization/list-read-scope.js +9 -5
  22. package/dist/authorization/list-read-scope.js.map +1 -1
  23. package/dist/authorization/model/agent.d.ts.map +1 -1
  24. package/dist/authorization/model/agent.js +5 -4
  25. package/dist/authorization/model/agent.js.map +1 -1
  26. package/dist/authorization/model/agent_instance.d.ts.map +1 -1
  27. package/dist/authorization/model/agent_instance.js +2 -2
  28. package/dist/authorization/model/agent_instance.js.map +1 -1
  29. package/dist/authorization/model/mcp_server.d.ts.map +1 -1
  30. package/dist/authorization/model/mcp_server.js +2 -2
  31. package/dist/authorization/model/mcp_server.js.map +1 -1
  32. package/dist/authorization/model/plugin.d.ts.map +1 -1
  33. package/dist/authorization/model/plugin.js +4 -3
  34. package/dist/authorization/model/plugin.js.map +1 -1
  35. package/dist/authorization/model/rewrite.d.ts +10 -8
  36. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  37. package/dist/authorization/model/rewrite.js +3 -0
  38. package/dist/authorization/model/rewrite.js.map +1 -1
  39. package/dist/authorization/model/skill.d.ts.map +1 -1
  40. package/dist/authorization/model/skill.js +2 -2
  41. package/dist/authorization/model/skill.js.map +1 -1
  42. package/dist/authorization/model/workflow.d.ts.map +1 -1
  43. package/dist/authorization/model/workflow.js +2 -2
  44. package/dist/authorization/model/workflow.js.map +1 -1
  45. package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
  46. package/dist/authorization/model/workflow_instance.js +2 -2
  47. package/dist/authorization/model/workflow_instance.js.map +1 -1
  48. package/dist/authorization/organization-directory.d.ts.map +1 -1
  49. package/dist/authorization/organization-directory.js +3 -2
  50. package/dist/authorization/organization-directory.js.map +1 -1
  51. package/dist/authorization/store-test-kit.d.ts +6 -0
  52. package/dist/authorization/store-test-kit.d.ts.map +1 -1
  53. package/dist/authorization/store-test-kit.js +62 -16
  54. package/dist/authorization/store-test-kit.js.map +1 -1
  55. package/dist/authorization/tuples.d.ts +32 -13
  56. package/dist/authorization/tuples.d.ts.map +1 -1
  57. package/dist/authorization/tuples.js +11 -3
  58. package/dist/authorization/tuples.js.map +1 -1
  59. package/dist/boot/compose.d.ts.map +1 -1
  60. package/dist/boot/compose.js +49 -24
  61. package/dist/boot/compose.js.map +1 -1
  62. package/dist/boot/config.d.ts +4 -3
  63. package/dist/boot/config.d.ts.map +1 -1
  64. package/dist/boot/config.js.map +1 -1
  65. package/dist/boot/skill-transfer-origin.d.ts +52 -0
  66. package/dist/boot/skill-transfer-origin.d.ts.map +1 -0
  67. package/dist/boot/skill-transfer-origin.js +54 -0
  68. package/dist/boot/skill-transfer-origin.js.map +1 -0
  69. package/dist/domain/agent/controller.d.ts.map +1 -1
  70. package/dist/domain/agent/controller.js +4 -4
  71. package/dist/domain/agent/controller.js.map +1 -1
  72. package/dist/domain/agentchannel/controller.d.ts.map +1 -1
  73. package/dist/domain/agentchannel/controller.js +1 -3
  74. package/dist/domain/agentchannel/controller.js.map +1 -1
  75. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  76. package/dist/domain/agentexecution/controller.js +1 -3
  77. package/dist/domain/agentexecution/controller.js.map +1 -1
  78. package/dist/domain/agentinstance/controller.d.ts.map +1 -1
  79. package/dist/domain/agentinstance/controller.js +4 -3
  80. package/dist/domain/agentinstance/controller.js.map +1 -1
  81. package/dist/domain/agentshare/constants.d.ts +11 -6
  82. package/dist/domain/agentshare/constants.d.ts.map +1 -1
  83. package/dist/domain/agentshare/constants.js +16 -8
  84. package/dist/domain/agentshare/constants.js.map +1 -1
  85. package/dist/domain/agentshare/controller.d.ts.map +1 -1
  86. package/dist/domain/agentshare/controller.js +1 -3
  87. package/dist/domain/agentshare/controller.js.map +1 -1
  88. package/dist/domain/agentshare/steps.d.ts +25 -18
  89. package/dist/domain/agentshare/steps.d.ts.map +1 -1
  90. package/dist/domain/agentshare/steps.js +142 -41
  91. package/dist/domain/agentshare/steps.js.map +1 -1
  92. package/dist/domain/environment/controller.d.ts.map +1 -1
  93. package/dist/domain/environment/controller.js +3 -2
  94. package/dist/domain/environment/controller.js.map +1 -1
  95. package/dist/domain/executioncontext/controller.d.ts.map +1 -1
  96. package/dist/domain/executioncontext/controller.js +1 -2
  97. package/dist/domain/executioncontext/controller.js.map +1 -1
  98. package/dist/domain/mcpserver/controller.d.ts.map +1 -1
  99. package/dist/domain/mcpserver/controller.js +4 -3
  100. package/dist/domain/mcpserver/controller.js.map +1 -1
  101. package/dist/domain/plugin/controller.d.ts +3 -7
  102. package/dist/domain/plugin/controller.d.ts.map +1 -1
  103. package/dist/domain/plugin/controller.js +11 -9
  104. package/dist/domain/plugin/controller.js.map +1 -1
  105. package/dist/domain/plugin/push.d.ts +5 -7
  106. package/dist/domain/plugin/push.d.ts.map +1 -1
  107. package/dist/domain/plugin/push.js +14 -8
  108. package/dist/domain/plugin/push.js.map +1 -1
  109. package/dist/domain/schedule/controller.d.ts.map +1 -1
  110. package/dist/domain/schedule/controller.js +1 -3
  111. package/dist/domain/schedule/controller.js.map +1 -1
  112. package/dist/domain/session/controller.d.ts.map +1 -1
  113. package/dist/domain/session/controller.js +2 -3
  114. package/dist/domain/session/controller.js.map +1 -1
  115. package/dist/domain/skill/constants.d.ts +26 -3
  116. package/dist/domain/skill/constants.d.ts.map +1 -1
  117. package/dist/domain/skill/constants.js +26 -3
  118. package/dist/domain/skill/constants.js.map +1 -1
  119. package/dist/domain/skill/controller.d.ts +6 -11
  120. package/dist/domain/skill/controller.d.ts.map +1 -1
  121. package/dist/domain/skill/controller.js +35 -22
  122. package/dist/domain/skill/controller.js.map +1 -1
  123. package/dist/domain/skill/push.d.ts +2 -2
  124. package/dist/domain/skill/push.d.ts.map +1 -1
  125. package/dist/domain/skill/push.js +2 -2
  126. package/dist/domain/skill/push.js.map +1 -1
  127. package/dist/domain/skill/transfer/handler.d.ts +17 -9
  128. package/dist/domain/skill/transfer/handler.d.ts.map +1 -1
  129. package/dist/domain/skill/transfer/handler.js +19 -8
  130. package/dist/domain/skill/transfer/handler.js.map +1 -1
  131. package/dist/domain/skill/transfer/slots.d.ts +26 -41
  132. package/dist/domain/skill/transfer/slots.d.ts.map +1 -1
  133. package/dist/domain/skill/transfer/slots.js +80 -87
  134. package/dist/domain/skill/transfer/slots.js.map +1 -1
  135. package/dist/domain/skill/transfer/staging.d.ts +48 -0
  136. package/dist/domain/skill/transfer/staging.d.ts.map +1 -0
  137. package/dist/domain/skill/transfer/staging.js +127 -0
  138. package/dist/domain/skill/transfer/staging.js.map +1 -0
  139. package/dist/domain/workflow/controller.d.ts.map +1 -1
  140. package/dist/domain/workflow/controller.js +6 -14
  141. package/dist/domain/workflow/controller.js.map +1 -1
  142. package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
  143. package/dist/domain/workflow/validation/task-config-constraints.d.ts +1 -10
  144. package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
  145. package/dist/domain/workflow/validation/task-config-constraints.js +2 -3
  146. package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
  147. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  148. package/dist/domain/workflowexecution/controller.js +2 -4
  149. package/dist/domain/workflowexecution/controller.js.map +1 -1
  150. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  151. package/dist/domain/workflowinstance/controller.js +4 -3
  152. package/dist/domain/workflowinstance/controller.js.map +1 -1
  153. package/dist/extensions/authorization-queries.d.ts +5 -6
  154. package/dist/extensions/authorization-queries.d.ts.map +1 -1
  155. package/dist/extensions/list-read-scope.d.ts +1 -1
  156. package/dist/extensions/resource-authorization.d.ts +2 -3
  157. package/dist/extensions/resource-authorization.d.ts.map +1 -1
  158. package/dist/index.d.ts +3 -1
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/index.js +13 -0
  161. package/dist/index.js.map +1 -1
  162. package/dist/pipeline/apiresource-meta.d.ts +3 -12
  163. package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
  164. package/dist/pipeline/apiresource-meta.js +10 -21
  165. package/dist/pipeline/apiresource-meta.js.map +1 -1
  166. package/dist/pipeline/steps/authorization-tuples.d.ts +3 -9
  167. package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
  168. package/dist/pipeline/steps/authorization-tuples.js +11 -9
  169. package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
  170. package/dist/pipeline/steps/references.d.ts +20 -178
  171. package/dist/pipeline/steps/references.d.ts.map +1 -1
  172. package/dist/pipeline/steps/references.js +43 -344
  173. package/dist/pipeline/steps/references.js.map +1 -1
  174. package/dist/pipeline/steps/resolve-artifact-source.d.ts +11 -8
  175. package/dist/pipeline/steps/resolve-artifact-source.d.ts.map +1 -1
  176. package/dist/pipeline/steps/resolve-artifact-source.js +8 -4
  177. package/dist/pipeline/steps/resolve-artifact-source.js.map +1 -1
  178. package/dist/pipeline/steps/visibility-gates.d.ts +27 -0
  179. package/dist/pipeline/steps/visibility-gates.d.ts.map +1 -0
  180. package/dist/pipeline/steps/visibility-gates.js +139 -0
  181. package/dist/pipeline/steps/visibility-gates.js.map +1 -0
  182. package/dist/query/search/controller.js +1 -0
  183. package/dist/query/search/controller.js.map +1 -1
  184. package/dist/query/search/criteria.d.ts +5 -1
  185. package/dist/query/search/criteria.d.ts.map +1 -1
  186. package/dist/query/search/criteria.js +13 -3
  187. package/dist/query/search/criteria.js.map +1 -1
  188. package/dist/query/search/handler.d.ts +4 -5
  189. package/dist/query/search/handler.d.ts.map +1 -1
  190. package/dist/query/search/handler.js +6 -7
  191. package/dist/query/search/handler.js.map +1 -1
  192. package/dist/query/search/query-store.d.ts.map +1 -1
  193. package/dist/query/search/query-store.js +2 -0
  194. package/dist/query/search/query-store.js.map +1 -1
  195. package/dist/store/interface.d.ts +6 -3
  196. package/dist/store/interface.d.ts.map +1 -1
  197. package/dist/store/interface.js.map +1 -1
  198. package/dist/store/postgres/migrations.d.ts +1 -3
  199. package/dist/store/postgres/migrations.d.ts.map +1 -1
  200. package/dist/store/postgres/migrations.js +1 -35
  201. package/dist/store/postgres/migrations.js.map +1 -1
  202. package/dist/store/postgres/store.d.ts.map +1 -1
  203. package/dist/store/postgres/store.js +9 -4
  204. package/dist/store/postgres/store.js.map +1 -1
  205. package/dist/store/sqlite/migrations.d.ts +1 -3
  206. package/dist/store/sqlite/migrations.d.ts.map +1 -1
  207. package/dist/store/sqlite/migrations.js +1 -36
  208. package/dist/store/sqlite/migrations.js.map +1 -1
  209. package/dist/store/sqlite/store.d.ts.map +1 -1
  210. package/dist/store/sqlite/store.js +8 -4
  211. package/dist/store/sqlite/store.js.map +1 -1
  212. package/package.json +6 -6
  213. package/src/artifactstorage/__tests__/artifact-storage.test.ts +36 -33
  214. package/src/artifactstorage/__tests__/r2-storage.test.ts +11 -13
  215. package/src/artifactstorage/artifact-storage.ts +37 -33
  216. package/src/artifactstorage/r2-storage.ts +6 -15
  217. package/src/authorization/README.md +26 -24
  218. package/src/authorization/__tests__/authorizer.test.ts +9 -10
  219. package/src/authorization/__tests__/derived-tuples.test.ts +11 -4
  220. package/src/authorization/__tests__/evaluator.test.ts +73 -14
  221. package/src/authorization/__tests__/facts.test.ts +2 -2
  222. package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +185 -0
  223. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +98 -6
  224. package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +98 -0
  225. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +61 -4
  226. package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +143 -0
  227. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +21 -1
  228. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +25 -1
  229. package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +52 -0
  230. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +3 -1
  231. package/src/authorization/__tests__/list-read-scope.test.ts +17 -18
  232. package/src/authorization/__tests__/store-tests.test.ts +8 -1
  233. package/src/authorization/__tests__/tuples.test.ts +11 -12
  234. package/src/authorization/authorizer.ts +9 -4
  235. package/src/authorization/derived-tuples.ts +31 -14
  236. package/src/authorization/evaluator.ts +44 -8
  237. package/src/authorization/list-read-scope.ts +11 -5
  238. package/src/authorization/model/agent.ts +5 -2
  239. package/src/authorization/model/agent_instance.ts +2 -0
  240. package/src/authorization/model/mcp_server.ts +2 -0
  241. package/src/authorization/model/plugin.ts +4 -1
  242. package/src/authorization/model/rewrite.ts +13 -8
  243. package/src/authorization/model/skill.ts +2 -0
  244. package/src/authorization/model/workflow.ts +2 -0
  245. package/src/authorization/model/workflow_instance.ts +2 -0
  246. package/src/authorization/organization-directory.ts +3 -1
  247. package/src/authorization/store-test-kit.ts +75 -16
  248. package/src/authorization/tuples.ts +39 -16
  249. package/src/boot/__tests__/skill-transfer-origin.test.ts +72 -0
  250. package/src/boot/compose.ts +62 -25
  251. package/src/boot/config.ts +4 -3
  252. package/src/boot/skill-transfer-origin.ts +76 -0
  253. package/src/domain/agent/__tests__/agent.test.ts +4 -11
  254. package/src/domain/agent/controller.ts +7 -7
  255. package/src/domain/agentchannel/__tests__/agentchannel.test.ts +48 -223
  256. package/src/domain/agentchannel/controller.ts +1 -6
  257. package/src/domain/agentexecution/controller.ts +1 -6
  258. package/src/domain/agentinstance/controller.ts +12 -6
  259. package/src/domain/agentshare/__tests__/agentshare.test.ts +172 -196
  260. package/src/domain/agentshare/__tests__/create-authorization.test.ts +31 -16
  261. package/src/domain/agentshare/constants.ts +26 -14
  262. package/src/domain/agentshare/controller.ts +1 -6
  263. package/src/domain/agentshare/steps.ts +194 -41
  264. package/src/domain/environment/controller.ts +8 -5
  265. package/src/domain/executioncontext/controller.ts +1 -5
  266. package/src/domain/mcpserver/__tests__/mcpserver.test.ts +28 -115
  267. package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +18 -53
  268. package/src/domain/mcpserver/controller.ts +12 -6
  269. package/src/domain/plugin/controller.ts +23 -18
  270. package/src/domain/plugin/push.ts +16 -8
  271. package/src/domain/schedule/controller.ts +1 -6
  272. package/src/domain/session/controller.ts +4 -12
  273. package/src/domain/skill/__tests__/skill.test.ts +5 -3
  274. package/src/domain/skill/__tests__/slots.test.ts +113 -60
  275. package/src/domain/skill/__tests__/staging.test.ts +304 -0
  276. package/src/domain/skill/constants.ts +28 -3
  277. package/src/domain/skill/controller.ts +51 -35
  278. package/src/domain/skill/push.ts +3 -3
  279. package/src/domain/skill/transfer/handler.ts +33 -15
  280. package/src/domain/skill/transfer/slots.ts +89 -100
  281. package/src/domain/skill/transfer/staging.ts +191 -0
  282. package/src/domain/workflow/controller.ts +10 -28
  283. package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
  284. package/src/domain/workflow/validation/task-config-constraints.ts +3 -7
  285. package/src/domain/workflowexecution/controller.ts +2 -7
  286. package/src/domain/workflowinstance/controller.ts +12 -6
  287. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +19 -32
  288. package/src/extensions/__tests__/extension-composition.test.ts +4 -4
  289. package/src/extensions/__tests__/list-read-scope-composed.test.ts +6 -9
  290. package/src/extensions/authorization-queries.ts +5 -6
  291. package/src/extensions/list-read-scope.ts +1 -1
  292. package/src/extensions/resource-authorization.ts +5 -3
  293. package/src/index.ts +14 -0
  294. package/src/pipeline/apiresource-meta.ts +10 -21
  295. package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +31 -18
  296. package/src/pipeline/steps/__tests__/visibility-gates.test.ts +187 -0
  297. package/src/pipeline/steps/authorization-tuples.ts +11 -9
  298. package/src/pipeline/steps/references.ts +70 -578
  299. package/src/pipeline/steps/resolve-artifact-source.ts +20 -13
  300. package/src/pipeline/steps/visibility-gates.ts +174 -0
  301. package/src/query/search/__tests__/criteria.test.ts +4 -0
  302. package/src/query/search/__tests__/query-store.test.ts +40 -11
  303. package/src/query/search/controller.ts +1 -0
  304. package/src/query/search/criteria.ts +14 -0
  305. package/src/query/search/handler.ts +7 -6
  306. package/src/query/search/query-store.ts +2 -0
  307. package/src/store/__tests__/store-contract.ts +51 -6
  308. package/src/store/interface.ts +6 -3
  309. package/src/store/postgres/__tests__/migrations.test.ts +6 -187
  310. package/src/store/postgres/__tests__/store-contract.test.ts +2 -0
  311. package/src/store/postgres/migrations.ts +1 -48
  312. package/src/store/postgres/store.ts +11 -4
  313. package/src/store/sqlite/__tests__/migrations.test.ts +5 -191
  314. package/src/store/sqlite/migrations.ts +1 -45
  315. package/src/store/sqlite/store.ts +10 -4
  316. package/src/transport/registry/__tests__/lanes.test.ts +1 -1
  317. package/dist/domain/workflow/agent-call-references.d.ts +0 -54
  318. package/dist/domain/workflow/agent-call-references.d.ts.map +0 -1
  319. package/dist/domain/workflow/agent-call-references.js +0 -97
  320. package/dist/domain/workflow/agent-call-references.js.map +0 -1
  321. package/dist/store/public-visibility-retired.d.ts +0 -67
  322. package/dist/store/public-visibility-retired.d.ts.map +0 -1
  323. package/dist/store/public-visibility-retired.js +0 -55
  324. package/dist/store/public-visibility-retired.js.map +0 -1
  325. package/src/domain/workflow/__tests__/agent-call-references.test.ts +0 -222
  326. package/src/domain/workflow/agent-call-references.ts +0 -157
  327. package/src/pipeline/__tests__/supports-visibility.test.ts +0 -112
  328. package/src/pipeline/steps/__tests__/references.test.ts +0 -703
  329. package/src/store/__tests__/public-visibility-retired.test.ts +0 -127
  330. package/src/store/public-visibility-retired.ts +0 -119
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Archive staging — how an artifact above the gRPC message cap travels,
3
+ * on every blob driver, through one code path (#675, stigmer#1219).
4
+ *
5
+ * The domain owns the two identities and the mapping between them: the
6
+ * wire reference a client is handed (`sau_<hex>`, the capability the
7
+ * push presents) and the staging key the bytes land under in the blob
8
+ * store (`skills/staging/<hex>.zip`, the surface a sweep targets). A
9
+ * driver is asked only to sign or stage a PUT for a key it is given, to
10
+ * read the key back, and to delete it — so the local driver rides the
11
+ * transfer lane's slot registry and a bucket driver signs a PUT straight
12
+ * to the bucket, and nothing above this module can tell which.
13
+ *
14
+ * Consume reads then deletes: the delete is what keeps a reference
15
+ * single-use on a bucket, whose signed URL is otherwise repeatable within
16
+ * its TTL. The delete is best effort and loud — the bytes are already in
17
+ * hand, a replay of the same reference is a content-addressed no-op, and
18
+ * the bucket lifecycle rule on the staging prefix sweeps what the delete
19
+ * missed. On the local driver the same delete retires the staged file the
20
+ * slot registry would otherwise sweep at expiry.
21
+ *
22
+ * A reference the driver cannot read — malformed, never uploaded,
23
+ * expired, already consumed — is one condition on every driver
24
+ * ("unknown or expired"): a bucket cannot distinguish them, so neither
25
+ * does the wire.
26
+ *
27
+ * Proven by __tests__/staging.test.ts over a fake driver and over the
28
+ * real local driver with its slot registry; the conformance suite's
29
+ * transfer-lane block pins the wire behaviour on both editions.
30
+ */
31
+ import { randomBytes } from "node:crypto";
32
+
33
+ import type { ArtifactStorage } from "../../../artifactstorage/artifact-storage.js";
34
+ import { ArtifactStorageNotFoundError } from "../../../artifactstorage/artifact-storage.js";
35
+ import type { Logger } from "../../../boot/logger.js";
36
+ import {
37
+ DEFAULT_SLOT_TTL_MS,
38
+ DOWNLOAD_URL_TTL_MS,
39
+ MAX_ZIP_SIZE,
40
+ REF_BYTE_LEN,
41
+ REF_PREFIX,
42
+ STAGING_KEY_PREFIX,
43
+ } from "../constants.js";
44
+ import { SlotUnknownError } from "./slots.js";
45
+
46
+ /** The hex the reference and the staging key share; 128 bits, lowercase. */
47
+ const HEX = new RegExp(`^[0-9a-f]{${REF_BYTE_LEN * 2}}$`);
48
+ const STAGED_ARCHIVE_SUFFIX = ".zip";
49
+
50
+ /** The shared hex of a well-formed reference or key, else undefined. */
51
+ function hexBetween(
52
+ value: string,
53
+ prefix: string,
54
+ suffix: string,
55
+ ): string | undefined {
56
+ if (!value.startsWith(prefix) || !value.endsWith(suffix)) {
57
+ return undefined;
58
+ }
59
+ const hex = value.slice(prefix.length, value.length - suffix.length);
60
+ return HEX.test(hex) ? hex : undefined;
61
+ }
62
+
63
+ /** A fresh upload reference: the prefix and 128 random bits, hex. */
64
+ export function newUploadRef(): string {
65
+ return REF_PREFIX + randomBytes(REF_BYTE_LEN).toString("hex");
66
+ }
67
+
68
+ /**
69
+ * The staging key a reference's bytes land under, or undefined when the
70
+ * reference is not one this server could have minted. Refs arrive from
71
+ * clients (the push request, the lane's URL path), so the shape is
72
+ * checked before it becomes a storage key.
73
+ */
74
+ export function stagingKeyOf(ref: string): string | undefined {
75
+ const hex = hexBetween(ref, REF_PREFIX, "");
76
+ return hex === undefined
77
+ ? undefined
78
+ : `${STAGING_KEY_PREFIX}${hex}${STAGED_ARCHIVE_SUFFIX}`;
79
+ }
80
+
81
+ /**
82
+ * The reference for a staging key this module minted. Keys reach here from
83
+ * the domain's own mint, never from a client, so a foreign shape is a
84
+ * programming error and throws.
85
+ */
86
+ export function uploadRefOf(stagingKey: string): string {
87
+ const hex = hexBetween(stagingKey, STAGING_KEY_PREFIX, STAGED_ARCHIVE_SUFFIX);
88
+ if (hex === undefined) {
89
+ throw new Error(`${stagingKey} is not a staging key this domain minted`);
90
+ }
91
+ return REF_PREFIX + hex;
92
+ }
93
+
94
+ /** What createArtifactUploadUrl hands the client. */
95
+ export interface StagedUpload {
96
+ /** The single-use reference the push presents. */
97
+ readonly ref: string;
98
+ /** Accepts one HTTP PUT of exactly the declared byte count. */
99
+ readonly url: string;
100
+ /** How long the reference and the URL stay valid. */
101
+ readonly ttlMs: number;
102
+ }
103
+
104
+ /** What getArtifactDownloadUrl hands the client. */
105
+ export interface DownloadCapability {
106
+ readonly url: string;
107
+ /** The floor of the URL's validity (DOWNLOAD_URL_TTL_MS). */
108
+ readonly ttlMs: number;
109
+ }
110
+
111
+ /** The staging port both archive kinds (skills, plugins) push through. */
112
+ export interface ArchiveStaging {
113
+ /** Mints a reference and the URL its bytes are PUT to. */
114
+ mint(declaredSizeBytes: number): Promise<StagedUpload>;
115
+ /**
116
+ * Reads the staged bytes for a reference and retires it. Throws
117
+ * SlotUnknownError when the driver holds nothing under the reference;
118
+ * any other failure is the driver's and propagates as an infrastructure
119
+ * fault.
120
+ */
121
+ consume(ref: string): Promise<Uint8Array>;
122
+ /** A time-limited download URL for a stored archive key. */
123
+ downloadUrl(storageKey: string): Promise<DownloadCapability>;
124
+ }
125
+
126
+ export function newArchiveStaging(
127
+ driver: ArtifactStorage,
128
+ logger: Logger,
129
+ ): ArchiveStaging {
130
+ return {
131
+ async mint(declaredSizeBytes) {
132
+ // The controllers refuse over-limit declarations with the limit in
133
+ // the message before reaching here; this is the module's own
134
+ // invariant, so no driver is ever asked to stage more than the
135
+ // archive ceiling.
136
+ if (declaredSizeBytes <= 0 || declaredSizeBytes > MAX_ZIP_SIZE) {
137
+ throw new Error(
138
+ `declared size ${declaredSizeBytes} outside (0, ${MAX_ZIP_SIZE}]`,
139
+ );
140
+ }
141
+ const ref = newUploadRef();
142
+ const key = stagingKeyOf(ref);
143
+ if (key === undefined) {
144
+ throw new Error(`minted reference ${ref} has no staging key`);
145
+ }
146
+ const presigned = await driver.presignPut(
147
+ key,
148
+ declaredSizeBytes,
149
+ DEFAULT_SLOT_TTL_MS,
150
+ );
151
+ return { ref, url: presigned.url, ttlMs: presigned.ttlMs };
152
+ },
153
+
154
+ async consume(ref) {
155
+ const key = stagingKeyOf(ref);
156
+ if (key === undefined) {
157
+ throw new SlotUnknownError();
158
+ }
159
+ let data: Uint8Array;
160
+ try {
161
+ // The allocation is bounded by construction: a bucket accepts only
162
+ // the signed Content-Length, the slot registry only the reserved
163
+ // size, and both were checked against MAX_ZIP_SIZE at mint.
164
+ data = await driver.download(key);
165
+ } catch (error) {
166
+ if (error instanceof ArtifactStorageNotFoundError) {
167
+ throw new SlotUnknownError();
168
+ }
169
+ throw error;
170
+ }
171
+ try {
172
+ await driver.delete(key);
173
+ } catch (error) {
174
+ logger.error("failed to delete consumed staged artifact", {
175
+ stagingKey: key,
176
+ error: error instanceof Error ? error.message : String(error),
177
+ });
178
+ }
179
+ return data;
180
+ },
181
+
182
+ async downloadUrl(storageKey) {
183
+ const url = await driver.getSignedUrl(
184
+ storageKey,
185
+ DOWNLOAD_URL_TTL_MS,
186
+ "",
187
+ );
188
+ return { url, ttlMs: DOWNLOAD_URL_TTL_MS };
189
+ },
190
+ };
191
+ }
@@ -78,6 +78,10 @@ import {
78
78
  } from "../../pipeline/steps/authorize-resolved-target.js";
79
79
  import { versionHistoryTarget } from "../../pipeline/steps/version-history.js";
80
80
  import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
81
+ import {
82
+ newAuthorizeVisibilityTransitionStep,
83
+ newGuardPublicVisibilityStep,
84
+ } from "../../pipeline/steps/visibility-gates.js";
81
85
  import { newBuildUpdateStateStep } from "../../pipeline/steps/build-update-state.js";
82
86
  import {
83
87
  setAuditFieldsForUpdate,
@@ -108,16 +112,7 @@ import {
108
112
  TARGET_RESOURCE_KEY,
109
113
  newLoadTargetStep,
110
114
  } from "../../pipeline/steps/load-target.js";
111
- import {
112
- collectSpecReferences,
113
- newGuardReferenceFloorOnEscalationStep,
114
- newNormalizeReferencesStep,
115
- newValidateReferencesStep,
116
- } from "../../pipeline/steps/references.js";
117
- import {
118
- collectAgentCallReferences,
119
- newValidateAgentCallReferencesStep,
120
- } from "./agent-call-references.js";
115
+ import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
121
116
  import {
122
117
  newCleanupIamPoliciesStep,
123
118
  newCreateAuthorizationTuplesStep,
@@ -243,10 +238,9 @@ async function createWorkflow(
243
238
  .addStep(newResolveSlugStep())
244
239
  .addStep(newCheckDuplicateStep(deps.store))
245
240
  .addStep(newBuildNewStateStep())
241
+ .addStep(newGuardPublicVisibilityStep(deps.authorizer))
246
242
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
247
243
  .addStep(newNormalizeReferencesStep())
248
- .addStep(newValidateReferencesStep(deps.store))
249
- .addStep(newValidateAgentCallReferencesStep(deps.store))
250
244
  .addStep(newPopulateServerlessValidationStep(deps.logger))
251
245
  .addStep(newComputeVersionHashStep(deps.logger))
252
246
  .addStep(newPopulateVersionHashStep(true))
@@ -311,8 +305,6 @@ async function update(
311
305
  .addStep(newBuildUpdateStateStep())
312
306
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
313
307
  .addStep(newNormalizeReferencesStep())
314
- .addStep(newValidateReferencesStep(deps.store))
315
- .addStep(newValidateAgentCallReferencesStep(deps.store))
316
308
  .addStep(newPopulateServerlessValidationStepForUpdate(deps.logger))
317
309
  .addStep(newComputeVersionHashStep(deps.logger))
318
310
  .addStep(newCheckVersionChangedStep(deps.logger))
@@ -419,8 +411,8 @@ async function deleteWorkflow(
419
411
  // updateVisibility — update_visibility.go: a targeted metadata update (only
420
412
  // metadata.visibility changes; spec/status untouched). The level check runs
421
413
  // AFTER load, preserving the cross-edition error precedence: unknown id +
422
- // bad level = NOT_FOUND on both editions. The workflow conformance suite's
423
- // visibility block is the wire pin.
414
+ // bad level = NOT_FOUND on both editions. CW-9's suite block is the wire
415
+ // pin (clientPublicVisibilityWrites, true on local targets).
424
416
  // ---------------------------------------------------------------------------
425
417
 
426
418
  const UPDATE_VISIBILITY_WORKFLOW_KEY = "updateVisibilityWorkflow";
@@ -460,20 +452,10 @@ async function updateVisibility(
460
452
  newRecordVisibilityBeforeUpdateStep(UPDATE_VISIBILITY_WORKFLOW_KEY),
461
453
  )
462
454
  .addStep(newValidateVisibilityUpdateStep())
463
- // The reference floor's second door: a workflow may not be raised above
464
- // the agents its agent_call tasks run.
465
455
  .addStep(
466
- newGuardReferenceFloorOnEscalationStep(
467
- deps.store,
456
+ newAuthorizeVisibilityTransitionStep(
468
457
  UPDATE_VISIBILITY_WORKFLOW_KEY,
469
- [
470
- (row) => collectSpecReferences(WorkflowSchema, row),
471
- (row) =>
472
- collectAgentCallReferences(
473
- (row as Workflow).spec,
474
- (row as Workflow).metadata?.org ?? "",
475
- ),
476
- ],
458
+ deps.authorizer,
477
459
  ),
478
460
  )
479
461
  .addStep(newSetWorkflowVisibilityStep())
@@ -1525,7 +1525,7 @@
1525
1525
  "type": "string"
1526
1526
  },
1527
1527
  "org": {
1528
- "description": "Organization that owns the referenced resource. When non-empty: must be a valid org slug (lowercase alphanumeric with hyphens, starts with a letter, 1-63 characters). Example: \"stigmer\", \"acme-corp\". When empty: the reference is relative — the server resolves it to the parent resource's organization at write time. All stored and returned references always have org populated (absolute form). Use empty org for same-org references (the common case). An explicit other org is accepted only when that organization is a platform that shares the resource with yours (visibility_platform).",
1528
+ "description": "Organization that owns the referenced resource. When non-empty: must be a valid org slug (lowercase alphanumeric with hyphens, starts with a letter, 1-63 characters). Example: \"stigmer\", \"acme-corp\". When empty: the reference is relative — the server resolves it to the parent resource's organization at write time. All stored and returned references always have org populated (absolute form). Use empty org for same-org references (the common case). Use explicit org for cross-org references (e.g., marketplace resources).",
1529
1529
  "maxLength": 63,
1530
1530
  "pattern": "^$|^[a-z][a-z0-9-]*[a-z0-9]$",
1531
1531
  "type": "string"
@@ -105,10 +105,7 @@ function taskConstraintViolations(task: WorkflowTask): string[] {
105
105
  * task_config is a Struct again — that is what the walker's recursion is
106
106
  * for).
107
107
  */
108
- function configConstraintViolations(
109
- task: WorkflowTask,
110
- cfg: Message,
111
- ): string[] {
108
+ function configConstraintViolations(task: WorkflowTask, cfg: Message): string[] {
112
109
  const schema = taskConfigSchemaFor(task.kind);
113
110
  if (schema === undefined) {
114
111
  // Unknown kinds were already rejected by validateTaskKinds; an
@@ -132,10 +129,9 @@ function configConstraintViolations(
132
129
  * The WorkflowTask lists embedded in a control-flow task config, in
133
130
  * declaration order — the same recursion set the converter walks
134
131
  * (convertForTask/convertForkTask/convertTryTask) and the cloud Java
135
- * TaskConfigStrictParser mirrors. Exported for the write-time reference
136
- * collector (../agent-call-references.ts), which walks the same tree.
132
+ * TaskConfigStrictParser mirrors.
137
133
  */
138
- export function nestedTasks(cfg: Message): WorkflowTask[] {
134
+ function nestedTasks(cfg: Message): WorkflowTask[] {
139
135
  switch (cfg.$typeName) {
140
136
  case ForTaskConfigSchema.typeName:
141
137
  return (cfg as ForTaskConfig).do;
@@ -72,10 +72,7 @@ import {
72
72
  TARGET_RESOURCE_KEY,
73
73
  newLoadTargetStep,
74
74
  } from "../../pipeline/steps/load-target.js";
75
- import {
76
- newNormalizeReferencesStep,
77
- newValidateReferencesStep,
78
- } from "../../pipeline/steps/references.js";
75
+ import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
79
76
  import {
80
77
  newCleanupIamPoliciesStep,
81
78
  newCreateAuthorizationTuplesStep,
@@ -302,8 +299,7 @@ async function createExecution(
302
299
  .addStep(newCheckDuplicateStep(deps.store))
303
300
  .addStep(newBuildNewStateStep())
304
301
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
305
- .addStep(newNormalizeReferencesStep())
306
- .addStep(newValidateReferencesStep(deps.store));
302
+ .addStep(newNormalizeReferencesStep());
307
303
  // The ratified sandbox-acquisition gate slot (blueprint 03 §3a; C4):
308
304
  // the Java-verified capacity-gate position — after Authorize and every
309
305
  // resolution step (the default instance, like Java's, side-effects
@@ -393,7 +389,6 @@ async function update(
393
389
  .addStep(newBuildUpdateStateStep())
394
390
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
395
391
  .addStep(newNormalizeReferencesStep())
396
- .addStep(newValidateReferencesStep(deps.store))
397
392
  .addStep(newPersistStep(deps.store))
398
393
  .addStep(
399
394
  newIndexSearchStep(
@@ -58,6 +58,10 @@ import {
58
58
  newAuthorizeResolvedTargetStep,
59
59
  } from "../../pipeline/steps/authorize-resolved-target.js";
60
60
  import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
61
+ import {
62
+ newAuthorizeVisibilityTransitionStep,
63
+ newGuardPublicVisibilityStep,
64
+ } from "../../pipeline/steps/visibility-gates.js";
61
65
  import {
62
66
  newBuildNewStateStep,
63
67
  setAuditFieldsForUpdate,
@@ -87,10 +91,7 @@ import {
87
91
  TARGET_RESOURCE_KEY,
88
92
  newLoadTargetStep,
89
93
  } from "../../pipeline/steps/load-target.js";
90
- import {
91
- newNormalizeReferencesStep,
92
- newValidateReferencesStep,
93
- } from "../../pipeline/steps/references.js";
94
+ import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
94
95
  import {
95
96
  newCleanupIamPoliciesStep,
96
97
  newCreateAuthorizationTuplesStep,
@@ -197,9 +198,9 @@ async function createInstance(
197
198
  .addStep(newValidateSameOrgBusinessRuleStep(deps.logger))
198
199
  .addStep(newCheckDuplicateStep(deps.store))
199
200
  .addStep(newBuildNewStateStep())
201
+ .addStep(newGuardPublicVisibilityStep(deps.authorizer))
200
202
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
201
203
  .addStep(newNormalizeReferencesStep())
202
- .addStep(newValidateReferencesStep(deps.store))
203
204
  .addStep(newPersistStep(deps.store))
204
205
  .addStep(
205
206
  newCreateAuthorizationTuplesStep(
@@ -248,7 +249,6 @@ async function update(
248
249
  .addStep(newBuildUpdateStateStep())
249
250
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
250
251
  .addStep(newNormalizeReferencesStep())
251
- .addStep(newValidateReferencesStep(deps.store))
252
252
  .addStep(newPersistStep(deps.store))
253
253
  .addStep(
254
254
  newIndexSearchStep(
@@ -407,6 +407,12 @@ async function updateVisibility(
407
407
  ),
408
408
  )
409
409
  .addStep(newValidateVisibilityUpdateStep())
410
+ .addStep(
411
+ newAuthorizeVisibilityTransitionStep(
412
+ UPDATE_VISIBILITY_INSTANCE_KEY,
413
+ deps.authorizer,
414
+ ),
415
+ )
410
416
  .addStep(newSetInstanceVisibilityStep())
411
417
  .addStep(
412
418
  newPersistInstanceStep(
@@ -16,9 +16,8 @@
16
16
  * outsider on an EXISTING resource hears PERMISSION_DENIED, the
17
17
  * cloud's answer; `findMyOrganizations` lists what a person may view
18
18
  * and `find` refuses enumeration; `checkMyPermission` tells the truth;
19
- * the retired public level is refused for the founder too, as an
20
- * invalid level and not a permission question; an unprovisioned
21
- * subject writes nothing until provisioned. The LIST lanes are a member's
19
+ * nobody sets public visibility; an unprovisioned subject
20
+ * writes nothing until provisioned. The LIST lanes are a member's
22
21
  * lists: an outsider lists nothing of an organization they hold no
23
22
  * row in; environments and API keys are their owner's even to the
24
23
  * organization's owner; the library (a search) omits a private agent
@@ -27,9 +26,8 @@
27
26
  * provisioned.
28
27
  * - trusted-local: the permissive default stays (one caller,
29
28
  * nothing to separate; the laptop's rows are stamped `"system"`):
30
- * tokenless reads and organization enumeration keep working, and
31
- * the retired public level is refused here exactly as it is under
32
- * every other posture (the level is gone, not gated).
29
+ * tokenless reads, public visibility and organization enumeration all
30
+ * keep working.
33
31
  * - a unit's own Authorizer: nothing built in is installed; the unit's
34
32
  * answer stands even against the founder.
35
33
  *
@@ -82,6 +80,7 @@ import { loadConfig } from "../../boot/config.js";
82
80
  import { composeServer } from "../../boot/compose.js";
83
81
  import type { ComposedServer } from "../../boot/compose.js";
84
82
  import { metadataOf } from "../../pipeline/steps/shapes.js";
83
+ import { PUBLIC_VISIBILITY_DENY_MESSAGE } from "../../pipeline/steps/visibility-gates.js";
85
84
  import type { Store } from "../../store/interface.js";
86
85
  import type { Authorizer } from "../authorizer.js";
87
86
  import type { ListReadScope } from "../list-read-scope.js";
@@ -133,15 +132,6 @@ function organizationInput(slug: string) {
133
132
  };
134
133
  }
135
134
 
136
- /**
137
- * The one sentence for a level a kind does not support, as the agent chain
138
- * answers it (pipeline/steps/validate-visibility.ts builds it from the
139
- * kind's config); pinned as bytes because clients render it.
140
- */
141
- const PUBLIC_LEVEL_REFUSED_FOR_AGENT =
142
- "agent resources cannot be set to visibility_public. " +
143
- "Supported visibility levels: visibility_private, visibility_org, visibility_platform.";
144
-
145
135
  function agentInput(
146
136
  name: string,
147
137
  visibility: ApiResourceVisibility,
@@ -387,14 +377,14 @@ describe("built-in authorizer (composed server, OIDC with no unit Authorizer: th
387
377
  );
388
378
  });
389
379
 
390
- it("the retired public level is refused for the founder as an invalid level, before any permission question", async () => {
380
+ it("nobody sets PUBLIC visibility on an enforcing self-host — the platform gate denies the founder with the policy copy", async () => {
391
381
  const failure = await failureOf(
392
382
  createClient(AgentCommandController, asFounder()).create(
393
383
  agentInput("Public Agent", ApiResourceVisibility.visibility_public),
394
384
  ),
395
385
  );
396
- expect(failure.code).toBe(Code.InvalidArgument);
397
- expect(failure.rawMessage).toBe(PUBLIC_LEVEL_REFUSED_FOR_AGENT);
386
+ expect(failure.code).toBe(Code.PermissionDenied);
387
+ expect(failure.rawMessage).toBe(PUBLIC_VISIBILITY_DENY_MESSAGE);
398
388
  });
399
389
 
400
390
  describe("the list lanes (the built-in ListReadScope)", () => {
@@ -605,18 +595,20 @@ describe("built-in authorizer (composed server, trusted-local: the permissive de
605
595
  expect(read.metadata?.id).toBe(created.metadata!.id);
606
596
  });
607
597
 
608
- it("the laptop keeps organization enumeration and refuses the retired public level like every posture — the level is gone, not gated", async () => {
598
+ it("the laptop keeps public visibility and organization enumeration — no directory, no platform gate that bites", async () => {
609
599
  const anonymous = transportFor(port);
610
- const failure = await failureOf(
611
- createClient(AgentCommandController, anonymous).create(
612
- agentInput(
613
- "Public Laptop Agent",
614
- ApiResourceVisibility.visibility_public,
615
- ),
600
+ const publicAgent = await createClient(
601
+ AgentCommandController,
602
+ anonymous,
603
+ ).create(
604
+ agentInput(
605
+ "Public Laptop Agent",
606
+ ApiResourceVisibility.visibility_public,
616
607
  ),
617
608
  );
618
- expect(failure.code).toBe(Code.InvalidArgument);
619
- expect(failure.rawMessage).toBe(PUBLIC_LEVEL_REFUSED_FOR_AGENT);
609
+ expect(publicAgent.metadata?.visibility).toBe(
610
+ ApiResourceVisibility.visibility_public,
611
+ );
620
612
  const all = await createClient(OrganizationQueryController, anonymous).find(
621
613
  { org: ORG },
622
614
  );
@@ -625,11 +617,6 @@ describe("built-in authorizer (composed server, trusted-local: the permissive de
625
617
 
626
618
  it("the laptop's lists are the full scan — no scope composed, the rows stamped `system` included", async () => {
627
619
  const anonymous = transportFor(port);
628
- // A second agent beside the private one the earlier test created, so
629
- // the search below proves a scan and not a single-row coincidence.
630
- await createClient(AgentCommandController, anonymous).create(
631
- agentInput("Second Laptop Agent", ApiResourceVisibility.visibility_org),
632
- );
633
620
  const environments = createClient(EnvironmentCommandController, anonymous);
634
621
  await environments.create(environmentInput("laptop env one"));
635
622
  await environments.create(environmentInput("laptop env two"));
@@ -1074,7 +1074,7 @@ describe("extension composition (C2 tuple lifecycle + organization directory)",
1074
1074
  });
1075
1075
  });
1076
1076
 
1077
- it("updateVisibility fires the set-diff transition (org→private deletes the org shape)", async () => {
1077
+ it("updateVisibility fires the set-diff transition (org→public keeps the floor)", async () => {
1078
1078
  const command = createClient(AgentCommandController, portTransport);
1079
1079
  const agent = await command.create(
1080
1080
  create(AgentSchema, {
@@ -1091,12 +1091,12 @@ describe("extension composition (C2 tuple lifecycle + organization directory)",
1091
1091
  visibilityEvents.length = 0;
1092
1092
  await command.updateVisibility({
1093
1093
  resourceId: agent.metadata?.id ?? "",
1094
- visibility: ApiResourceVisibility.visibility_private,
1094
+ visibility: ApiResourceVisibility.visibility_public,
1095
1095
  });
1096
1096
  expect(visibilityEvents).toHaveLength(1);
1097
1097
  const event = visibilityEvents[0]!;
1098
- expect(event.shapesToCreate).toEqual([]);
1099
- expect(event.shapesToDelete).toEqual(["org-viewer"]);
1098
+ expect(event.shapesToCreate).toEqual(["public-viewer"]);
1099
+ expect(event.shapesToDelete).toEqual([]);
1100
1100
  });
1101
1101
 
1102
1102
  it("delete fires the cleanup event; a cleanup failure never fails the delete", async () => {
@@ -10,7 +10,7 @@
10
10
  * narrows, blank spans orgs — lane 6),
11
11
  * - activity.listRecentActivity (enumeration verb, two kinds — lane 22),
12
12
  * - search (enumeration verb feeding the engine allowlist, and the
13
- * proof that no request shape bypasses it — lane 21),
13
+ * crossOrgPublic bypass where FGA is never consulted — lane 21),
14
14
  * - the OUTAGE arm: a throwing scope answers the sanitized INTERNAL,
15
15
  * never an empty (or full!) list;
16
16
  * - the SERVER'S OWN reads over `inProcessTransport` (stigmer#1207):
@@ -277,23 +277,20 @@ describe("list read scope (composed server, fake scope)", () => {
277
277
  ]);
278
278
  });
279
279
 
280
- it("search narrows through the engine allowlist on every request shape — nothing bypasses the scope (lane 21)", async () => {
280
+ it("search narrows through the engine allowlist; crossOrgPublic bypasses the scope (lane 21)", async () => {
281
281
  allowed = new Set(["agt_mine"]);
282
282
  const search = createClient(SearchService, transport);
283
283
  const scoped = await search.search({ query: "scopedagent" });
284
284
  expect(scoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
285
285
 
286
- // The org-filtered shape asks the scope too: the one request shape
287
- // that once skipped it (the retired public level's cross-organization
288
- // discovery) no longer exists on the wire.
286
+ // The Java bypass: public-widened discovery never consults FGA.
289
287
  seenKinds.length = 0;
290
- const orgScoped = await search.search({
291
- kinds: [ApiResourceKind.agent],
288
+ await search.search({
292
289
  query: "scopedagent",
293
290
  org: "acme",
291
+ crossOrgPublic: true,
294
292
  });
295
- expect(orgScoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
296
- expect(seenKinds).toEqual([ApiResourceKind.agent]);
293
+ expect(seenKinds).toEqual([]);
297
294
  });
298
295
 
299
296
  it("a scope outage answers the sanitized INTERNAL — never an unscoped or empty success", async () => {
@@ -32,10 +32,9 @@
32
32
  * Semantics the controller and the conformance suite rely on (the Java
33
33
  * check-handler posture the cloud's client already implements):
34
34
  *
35
- * - `check` resolves usersets (a grant to `organization:acme#member`
36
- * answers true for a member), so "may I read this" is the model's
37
- * answer and not a lookup of who was named.
38
- * - the two listings never expand usersets, and `listPrincipalIds`
35
+ * - `check` answers a public grant (a wildcard principal resolves), so
36
+ * "may I read this public agent" is true for anyone.
37
+ * - the two listings never expand wildcards, and `listPrincipalIds`
39
38
  * answers DIRECT principals only — a listing is an inventory of who
40
39
  * was named, never a resolution of who could reach.
41
40
  * - contextual policies are evaluated as if held, and nothing is written.
@@ -75,7 +74,7 @@ export interface AuthorizationQueryEngine {
75
74
  ): Promise<boolean>;
76
75
  /**
77
76
  * The ids of `resourceKind` resources on which `principal` holds
78
- * `relation` (ListAuthorizedResourceIdsInput's field order). Usersets
77
+ * `relation` (ListAuthorizedResourceIdsInput's field order). Wildcards
79
78
  * are not expanded.
80
79
  */
81
80
  listResourceIds(
@@ -87,7 +86,7 @@ export interface AuthorizationQueryEngine {
87
86
  /**
88
87
  * The ids of `principalKind` principals holding `relation` on `resource`
89
88
  * (ListAuthorizedPrincipalIdsInput's field order). Direct principals
90
- * only; usersets are not expanded.
89
+ * only; wildcards are not expanded.
91
90
  */
92
91
  listPrincipalIds(
93
92
  resource: ApiResourceRef,
@@ -8,7 +8,7 @@
8
8
  * is yours). On a multi-tenant edition the same scans serve OTHER
9
9
  * tenants' rows, so the cloud baseline (the Java list handlers) narrows
10
10
  * every one of them to the caller's FGA-authorized id set (ListObjects
11
- * on can_view), with a second guest
11
+ * on can_view, public wildcards suppressed), with a second guest
12
12
  * cookie-label rule on the session and agent-execution lanes.
13
13
  *
14
14
  * This port is the ONE seam for that fork (DD-007 addendum 3: every
@@ -65,12 +65,14 @@ import type { CallerIdentity } from "./identity.js";
65
65
  * The visibility tuple shapes of the shared FGA model, named
66
66
  * edition-neutrally. The driver maps each to its tuple:
67
67
  * - org-viewer: <kind>:<id>#viewer@organization:<org>#viewer
68
+ * - public-viewer: <kind>:<id>#viewer@identity_account:* (conditional)
68
69
  * - platform-viewer: <kind>:<id>#platform_viewer@identity_provider:<idp>#platform_user
69
70
  * (fans out per IdP the org owns — the driver's lookup, not OSS's)
70
- * Every shape names a bounded set of readers; the model has no shape that
71
- * reaches every account.
72
71
  */
73
- export type VisibilityTupleShape = "org-viewer" | "platform-viewer";
72
+ export type VisibilityTupleShape =
73
+ | "org-viewer"
74
+ | "public-viewer"
75
+ | "platform-viewer";
74
76
 
75
77
  /**
76
78
  * One resolved structural link from the created resource to a parent
package/src/index.ts CHANGED
@@ -185,6 +185,7 @@ export {
185
185
  newModel,
186
186
  } from "./authorization/model/index.js";
187
187
  export type {
188
+ CheckContext,
188
189
  ObjectRef,
189
190
  Person,
190
191
  Subject,
@@ -308,6 +309,19 @@ export { newAuthorizeStep } from "./pipeline/steps/authorize.js";
308
309
  // composition never re-derives the set.
309
310
  export { isRunGateCheck } from "./pipeline/steps/authorize-run-target.js";
310
311
  export { newValidateProtoStep } from "./pipeline/steps/validation.js";
312
+ // The create-shaping steps, for an envelope kind a composition serves
313
+ // itself (a cloud_only kind has no OSS controller to run them): between
314
+ // ValidateProto and its own domain step the composition runs the SAME
315
+ // ResolveSlug and BuildNewState every OSS create runs, so how a slug is
316
+ // derived from a name, how an id is spelled, how status.audit is stamped
317
+ // for the caller and how visibility defaults live exactly once and move
318
+ // together. The RequestContext must carry the kind (its fourth
319
+ // constructor argument) for BuildNewState to pick the id prefix.
320
+ // CheckDuplicate is deliberately not here: it reads this server's Store,
321
+ // and a composition's own table answers that question in a step of the
322
+ // same name over its own repo.
323
+ export { newBuildNewStateStep } from "./pipeline/steps/defaults.js";
324
+ export { newResolveSlugStep } from "./pipeline/steps/slug.js";
311
325
 
312
326
  // The driver interfaces and the store-fault classes the ratified mapping
313
327
  // keys on (typed not-found → NotFound; anything else rethrows as an