@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
@@ -8,18 +8,22 @@
8
8
  * are indistinguishable past this point.
9
9
  *
10
10
  * Skills carried this step alone (the Go ResolveArtifactSource port);
11
- * plugins are pushed the same two ways over the same upload slots (one
12
- * upload surface, as boot/compose.ts wires it), so the step lives
13
- * here and each kind names its lane-absent sentence and the request
14
- * fields it reads.
11
+ * plugins are pushed the same two ways over the same staging port (one
12
+ * upload surface, as boot/compose.ts wires it), so the step lives here
13
+ * and each kind names its lane-absent sentence and the request fields it
14
+ * reads. The staging port (domain/skill/transfer/staging.ts) hides which
15
+ * blob driver holds the bytes: the step never sees a slot or a bucket.
15
16
  *
16
17
  * The unusable-reference arm is INVALID_ARGUMENT, not a server fault: an
17
- * unknown, expired, already consumed, or minted-but-never-uploaded
18
- * reference all mean the client must re-mint and re-upload.
18
+ * unknown, expired, already consumed, or never-uploaded reference all mean
19
+ * the client must re-mint and re-upload. Any other failure reading the
20
+ * staged bytes is the driver's — an infrastructure fault the executor
21
+ * answers as a sanitized Internal, never dressed as the client's mistake.
19
22
  */
20
23
  import type { DescMessage, MessageShape } from "@bufbuild/protobuf";
21
24
 
22
- import type { UploadSlots } from "../../domain/skill/transfer/slots.js";
25
+ import { SlotUnknownError } from "../../domain/skill/transfer/slots.js";
26
+ import type { ArchiveStaging } from "../../domain/skill/transfer/staging.js";
23
27
  import { failedPreconditionError, invalidArgumentError } from "../errors.js";
24
28
  import type { PipelineStep } from "../pipeline.js";
25
29
  import type { RequestContext } from "../request-context.js";
@@ -38,7 +42,7 @@ export interface ArtifactSourceBinding<Desc extends DescMessage> {
38
42
  }
39
43
 
40
44
  export function newResolveArtifactSourceStep<Desc extends DescMessage>(
41
- slots: UploadSlots | undefined,
45
+ staging: ArchiveStaging | undefined,
42
46
  binding: ArtifactSourceBinding<Desc>,
43
47
  ): PipelineStep<Desc> {
44
48
  return {
@@ -49,16 +53,19 @@ export function newResolveArtifactSourceStep<Desc extends DescMessage>(
49
53
  ctx.set(ARTIFACT_BYTES_KEY, artifact);
50
54
  return;
51
55
  }
52
- if (slots === undefined) {
56
+ if (staging === undefined) {
53
57
  throw failedPreconditionError(binding.laneNotConfigured);
54
58
  }
55
59
  let data: Uint8Array;
56
60
  try {
57
- data = await slots.consume(artifactUploadRef);
61
+ data = await staging.consume(artifactUploadRef);
58
62
  } catch (error) {
59
- throw invalidArgumentError(
60
- `artifact_upload_ref not usable: ${error instanceof Error ? error.message : String(error)} — request a new upload URL via createArtifactUploadUrl`,
61
- );
63
+ if (error instanceof SlotUnknownError) {
64
+ throw invalidArgumentError(
65
+ `artifact_upload_ref not usable: ${error.message} — request a new upload URL via createArtifactUploadUrl`,
66
+ );
67
+ }
68
+ throw error;
62
69
  }
63
70
  ctx.set(ARTIFACT_BYTES_KEY, data);
64
71
  },
@@ -0,0 +1,174 @@
1
+ /**
2
+ * The PUBLIC-visibility operator gates — the two write doors a resource
3
+ * can become public through, both consulting the ONE composed Authorizer
4
+ * with the same platform-scoped check (the Java
5
+ * PublicVisibilityEscalationPolicy port; C2 Stage 3, 20260827.10).
6
+ *
7
+ * PUBLIC is the only level that crosses every org boundary (one flip
8
+ * lists a resource in every tenant's cross-org catalog), so entering it
9
+ * is a curation decision: the owner requests, the platform team grants.
10
+ * The answer is `can_set_public_visibility` on `platform:stigmer` — a
11
+ * named operator capability, never a bare operator lookup.
12
+ *
13
+ * - GuardPublicVisibility: the CREATE door, in the create chains of
14
+ * the kinds whose VisibilityConfig supports PUBLIC (agent, workflow,
15
+ * mcp_server, agent_instance, workflow_instance). After
16
+ * BuildNewState — it inspects the state that will persist — which
17
+ * keeps the pinned INVALID_ARGUMENT level-support rejections (the
18
+ * earlier ValidateVisibility) their precedence over this
19
+ * PERMISSION_DENIED.
20
+ * - AuthorizeVisibilityTransition: the UPDATE door, in every
21
+ * updateVisibility chain after ValidateVisibilityUpdate (the pinned
22
+ * FAILED_PRECONDITION default-instance rejection keeps precedence,
23
+ * the Java ordering the conformance suite depends on). Gates only
24
+ * the ESCALATION to public (stored level is anything else).
25
+ *
26
+ * Both run LAZILY — only requests that actually involve the PUBLIC level
27
+ * reach the Authorizer — and both pass for internal callers (server-side
28
+ * flows publish platform resources by design). The OSS permissive
29
+ * default allows, so the local posture keeps today's behavior
30
+ * byte-identically; the cloud's FGA Authorizer supplies the gate.
31
+ *
32
+ * DELIBERATE partial port, recorded owner-visibly (Stage-3 execution
33
+ * record): Java's transition step additionally lets operators WITHOUT
34
+ * resource can_edit publish and take down (its updateVisibility chains
35
+ * authorize post-load, transition-aware); this edition's position-1
36
+ * Authorize enforces can_edit on updateVisibility before any step here
37
+ * runs, so those operator-without-edit lanes require can_edit until the
38
+ * Stage-4 chain disposition addresses them. No conformance lane pins
39
+ * them.
40
+ */
41
+ import { Code, ConnectError } from "@connectrpc/connect";
42
+ import type { DescMessage } from "@bufbuild/protobuf";
43
+
44
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
45
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
46
+ import type { UpdateVisibilityInput } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
47
+ import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
48
+
49
+ import type { Authorizer, AuthzDecision } from "../../extensions/authorizer.js";
50
+ import type { CallerIdentity } from "../../extensions/identity.js";
51
+ import { internalError } from "../errors.js";
52
+ import type { PipelineStep } from "../pipeline.js";
53
+ import type { RequestContext } from "../request-context.js";
54
+ import { metadataOf } from "./shapes.js";
55
+
56
+ /**
57
+ * The denial callers see at both doors (the Java policy copy verbatim).
58
+ * Deliberately not byte-pinned by the OSS conformance suite; the cloud
59
+ * gate itself is recorded via the clientPublicVisibilityWrites flag.
60
+ */
61
+ export const PUBLIC_VISIBILITY_DENY_MESSAGE =
62
+ "Public visibility is granted by the platform team. " +
63
+ "Ask a platform operator to publish this resource.";
64
+
65
+ /** The CREATE door: a new state declaring PUBLIC needs the operator grant. */
66
+ export function newGuardPublicVisibilityStep<Desc extends DescMessage>(
67
+ authorizer: Authorizer,
68
+ ): PipelineStep<Desc> {
69
+ return {
70
+ name: "GuardPublicVisibility",
71
+ async execute(ctx: RequestContext<Desc>): Promise<void> {
72
+ if (ctx.callerIdentity.callerClass === "internal") {
73
+ return;
74
+ }
75
+ if (
76
+ metadataOf(ctx.newState)?.visibility !==
77
+ ApiResourceVisibility.visibility_public
78
+ ) {
79
+ return;
80
+ }
81
+ await requireOperatorMaySetPublic(authorizer, ctx.callerIdentity);
82
+ },
83
+ };
84
+ }
85
+
86
+ /**
87
+ * The UPDATE door: escalation to PUBLIC (the stored level is anything
88
+ * else) needs the operator grant. Reads the loaded target from the
89
+ * chain's own context key (every updateVisibility chain stores it —
90
+ * the RecordVisibilityBeforeUpdate pattern).
91
+ */
92
+ export function newAuthorizeVisibilityTransitionStep<Desc extends DescMessage>(
93
+ targetKey: string,
94
+ authorizer: Authorizer,
95
+ ): PipelineStep<Desc> {
96
+ return {
97
+ name: "AuthorizeVisibilityTransition",
98
+ async execute(ctx: RequestContext<Desc>): Promise<void> {
99
+ if (ctx.callerIdentity.callerClass === "internal") {
100
+ return;
101
+ }
102
+ const requested = (ctx.input as unknown as UpdateVisibilityInput)
103
+ .visibility;
104
+ if (requested !== ApiResourceVisibility.visibility_public) {
105
+ return;
106
+ }
107
+ const target = ctx.get(targetKey);
108
+ if (target === undefined) {
109
+ // Wiring error, not a user error: without the loaded target there
110
+ // is no stored level, and a gate that silently passes un-guards
111
+ // the boundary it exists to protect.
112
+ throw internalError(
113
+ new Error(
114
+ "AuthorizeVisibilityTransition ran without a loaded target — step must be placed after the load step",
115
+ ),
116
+ "visibility authorization requires the loaded resource",
117
+ );
118
+ }
119
+ const stored = metadataOf(target as typeof ctx.newState)?.visibility;
120
+ if (stored === ApiResourceVisibility.visibility_public) {
121
+ return; // Re-asserting a stored PUBLIC is not an escalation.
122
+ }
123
+ await requireOperatorMaySetPublic(authorizer, ctx.callerIdentity);
124
+ },
125
+ };
126
+ }
127
+
128
+ /**
129
+ * The single owner of "may this caller set public?" — both doors call it,
130
+ * and so does the plugin push chain, whose requested level rides the
131
+ * request (the level then flows to every member through the members' own
132
+ * create and updateVisibility doors, which run this check again).
133
+ */
134
+ export async function requireOperatorMaySetPublic(
135
+ authorizer: Authorizer,
136
+ caller: CallerIdentity,
137
+ ): Promise<void> {
138
+ let decision: AuthzDecision;
139
+ try {
140
+ decision = await authorizer.authorize(caller, {
141
+ permission: IamPermission.can_set_public_visibility,
142
+ resourceKind: ApiResourceKind.platform,
143
+ resourceId: "stigmer",
144
+ });
145
+ } catch (error) {
146
+ throw publicVisibilityCheckFailure(error);
147
+ }
148
+ switch (decision.kind) {
149
+ case "allow":
150
+ return;
151
+ case "deny":
152
+ case "not-found":
153
+ throw new ConnectError(
154
+ PUBLIC_VISIBILITY_DENY_MESSAGE,
155
+ Code.PermissionDenied,
156
+ );
157
+ case "unavailable":
158
+ throw publicVisibilityCheckFailure(decision.cause);
159
+ default: {
160
+ const exhaustive: never = decision;
161
+ throw publicVisibilityCheckFailure(
162
+ new Error(`unknown decision ${JSON.stringify(exhaustive)}`),
163
+ );
164
+ }
165
+ }
166
+ }
167
+
168
+ /** Authorization infrastructure failure: fail closed, sanitized (#478). */
169
+ function publicVisibilityCheckFailure(error: unknown): ConnectError {
170
+ return internalError(
171
+ error instanceof Error ? error : new Error(String(error)),
172
+ "public-visibility authorization could not be completed",
173
+ );
174
+ }
@@ -25,6 +25,8 @@ function criteria(overrides?: {
25
25
  kinds?: ApiResourceKind[];
26
26
  query?: string;
27
27
  org?: string;
28
+ excludePublic?: boolean;
29
+ crossOrgPublic?: boolean;
28
30
  pageNumber?: number;
29
31
  pageSize?: number;
30
32
  }): SearchCriteria {
@@ -32,6 +34,8 @@ function criteria(overrides?: {
32
34
  overrides?.kinds ?? [],
33
35
  overrides?.query ?? "",
34
36
  overrides?.org ?? "",
37
+ overrides?.excludePublic ?? false,
38
+ overrides?.crossOrgPublic ?? false,
35
39
  overrides?.pageNumber ?? 1,
36
40
  overrides?.pageSize ?? 20,
37
41
  );
@@ -3,8 +3,8 @@
3
3
  * sqlite_search_query_store_test.go (the kind-parsing table) and
4
4
  * sqlite_search_query_store_query_test.go (the end-to-end
5
5
  * write→index→query pins: session list mode, the #440 emptiness arm, the
6
- * #439 newly-searchable-kind arm), plus the scope filter (org strict:
7
- * visibility never widens or narrows it) and RebuildIndex —
6
+ * #439 newly-searchable-kind arm), plus the scope-filter matrix
7
+ * (org strict / crossOrgPublic / excludePublic) and RebuildIndex —
8
8
  * including the DD-D proof: rebuilding over an ADOPTED Go-created
9
9
  * database re-indexes the rows it already holds. The escaping and
10
10
  * score-normalization tables moved with their code into the sqlite
@@ -118,6 +118,8 @@ function criteria(overrides: {
118
118
  kinds?: ApiResourceKind[];
119
119
  query?: string;
120
120
  org?: string;
121
+ excludePublic?: boolean;
122
+ crossOrgPublic?: boolean;
121
123
  pageNumber?: number;
122
124
  pageSize?: number;
123
125
  }): SearchCriteria {
@@ -125,6 +127,8 @@ function criteria(overrides: {
125
127
  overrides.kinds ?? [],
126
128
  overrides.query ?? "",
127
129
  overrides.org ?? "",
130
+ overrides.excludePublic ?? false,
131
+ overrides.crossOrgPublic ?? false,
128
132
  overrides.pageNumber ?? 1,
129
133
  overrides.pageSize ?? 20,
130
134
  );
@@ -245,7 +249,7 @@ describe("write→index→query pins (Go's query_test.go)", () => {
245
249
  });
246
250
  });
247
251
 
248
- describe("scope filter (the org filter is strict)", () => {
252
+ describe("scope-filter matrix (Go buildScopeFilter)", () => {
249
253
  beforeEach(async () => {
250
254
  await seed(
251
255
  ApiResourceKind.session,
@@ -265,10 +269,10 @@ describe("scope filter (the org filter is strict)", () => {
265
269
  );
266
270
  await seed(
267
271
  ApiResourceKind.session,
268
- "ses-b-org",
272
+ "ses-b-public",
269
273
  SessionSchema,
270
- newSession("ses-b-org", "org-b", "b org", {
271
- visibility: ApiResourceVisibility.visibility_org,
274
+ newSession("ses-b-public", "org-b", "b public", {
275
+ visibility: ApiResourceVisibility.visibility_public,
272
276
  createdAtSeconds: 1_700_000_001,
273
277
  }),
274
278
  );
@@ -284,15 +288,38 @@ describe("scope filter (the org filter is strict)", () => {
284
288
  expect(ids(result)).toEqual(["ses-a-private"]);
285
289
  });
286
290
 
287
- it("another org's rows stay out of an org filter whatever their visibility", async () => {
291
+ it("crossOrgPublic widens to OTHER orgs' public rows only", async () => {
288
292
  const result = await queryStore.search(
289
- criteria({ kinds: [ApiResourceKind.session], org: "org-b" }),
293
+ criteria({
294
+ kinds: [ApiResourceKind.session],
295
+ org: "org-a",
296
+ crossOrgPublic: true,
297
+ }),
290
298
  );
291
299
  expect(new Set(ids(result))).toEqual(
292
- new Set(["ses-b-private", "ses-b-org"]),
300
+ new Set(["ses-a-private", "ses-b-public"]),
293
301
  );
294
302
  });
295
303
 
304
+ it("excludePublic subtracts public rows from any scope", async () => {
305
+ const unscoped = await queryStore.search(
306
+ criteria({ kinds: [ApiResourceKind.session], excludePublic: true }),
307
+ );
308
+ expect(new Set(ids(unscoped))).toEqual(
309
+ new Set(["ses-a-private", "ses-b-private"]),
310
+ );
311
+
312
+ const widened = await queryStore.search(
313
+ criteria({
314
+ kinds: [ApiResourceKind.session],
315
+ org: "org-a",
316
+ crossOrgPublic: true,
317
+ excludePublic: true,
318
+ }),
319
+ );
320
+ expect(ids(widened)).toEqual(["ses-a-private"]);
321
+ });
322
+
296
323
  it("no org filter returns every row, newest first (list mode ordering)", async () => {
297
324
  const result = await queryStore.search(
298
325
  criteria({ kinds: [ApiResourceKind.session] }),
@@ -300,7 +327,7 @@ describe("scope filter (the org filter is strict)", () => {
300
327
  expect(ids(result)).toEqual([
301
328
  "ses-a-private",
302
329
  "ses-b-private",
303
- "ses-b-org",
330
+ "ses-b-public",
304
331
  ]);
305
332
  });
306
333
  });
@@ -447,7 +474,9 @@ describe("rebuildIndex", () => {
447
474
  "skl_1",
448
475
  SkillSchema,
449
476
  );
450
- expect(toBinary(SkillSchema, raw)).toEqual(toBinary(SkillSchema, skill));
477
+ expect(toBinary(SkillSchema, raw)).toEqual(
478
+ toBinary(SkillSchema, skill),
479
+ );
451
480
  } finally {
452
481
  await adopted.close();
453
482
  fixture.cleanup();
@@ -61,6 +61,7 @@ async function search(
61
61
  kinds: request.kinds.map((kind) => ApiResourceKind[kind] ?? String(kind)),
62
62
  query: request.query,
63
63
  org: request.org,
64
+ exclude_public: request.excludePublic,
64
65
  });
65
66
 
66
67
  try {
@@ -98,6 +98,8 @@ export class SearchCriteria {
98
98
  private readonly requestedKinds: readonly ApiResourceKind[],
99
99
  private readonly queryText: string,
100
100
  private readonly orgFilterValue: string,
101
+ private readonly excludePublicFlag: boolean,
102
+ private readonly crossOrgPublicFlag: boolean,
101
103
  private readonly pageNumberValue: number,
102
104
  private readonly pageSizeValue: number,
103
105
  ) {}
@@ -119,6 +121,8 @@ export class SearchCriteria {
119
121
  kinds: readonly ApiResourceKind[],
120
122
  query: string,
121
123
  orgFilter: string,
124
+ excludePublic: boolean,
125
+ crossOrgPublic: boolean,
122
126
  pageNumber: number,
123
127
  pageSize: number,
124
128
  ): SearchCriteria {
@@ -132,6 +136,8 @@ export class SearchCriteria {
132
136
  [...kinds],
133
137
  normalizedQuery,
134
138
  goTrimSpace(orgFilter),
139
+ excludePublic,
140
+ crossOrgPublic,
135
141
  pageNumber < 1 ? 1 : pageNumber,
136
142
  pageSize < 1 ? DEFAULT_PAGE_SIZE : Math.min(pageSize, MAX_PAGE_SIZE),
137
143
  );
@@ -150,6 +156,14 @@ export class SearchCriteria {
150
156
  return this.orgFilterValue;
151
157
  }
152
158
 
159
+ excludePublic(): boolean {
160
+ return this.excludePublicFlag;
161
+ }
162
+
163
+ crossOrgPublic(): boolean {
164
+ return this.crossOrgPublicFlag;
165
+ }
166
+
153
167
  pageNumber(): number {
154
168
  return this.pageNumberValue;
155
169
  }
@@ -52,11 +52,10 @@ export class SearchHandler {
52
52
  * mapping string-matches them (#478 sanitization contract).
53
53
  *
54
54
  * With a composed ListReadScope (20260830.01, census lane 21) the
55
- * store read ALWAYS carries a per-effective-kind authorized-id map —
56
- * the Java SearchHandler's QueryAuthorizedIds step. There is no request
57
- * shape that skips the scope: the one that did (the retired public
58
- * level's cross-organization discovery, which the engine filtered by
59
- * visibility with FGA never consulted) left with the level. A scope
55
+ * store read carries a per-effective-kind authorized-id map — the Java
56
+ * SearchHandler's QueryAuthorizedIds step, with its crossOrgPublic
57
+ * bypass preserved verbatim (public-widened discovery is
58
+ * visibility-filtered by the engine, FGA never consulted). A scope
60
59
  * failure propagates — the controller's default arm answers the
61
60
  * sanitized Internal, never a silently unscoped result.
62
61
  */
@@ -75,6 +74,8 @@ export class SearchHandler {
75
74
  request.kinds,
76
75
  request.query,
77
76
  request.org,
77
+ request.excludePublic,
78
+ request.crossOrgPublic,
78
79
  request.page?.num ?? 0,
79
80
  request.page?.size ?? 0,
80
81
  );
@@ -86,7 +87,7 @@ export class SearchHandler {
86
87
  }
87
88
 
88
89
  let authorizedIdsByKind: Map<string, ReadonlySet<string>> | undefined;
89
- if (this.listReadScope !== undefined) {
90
+ if (this.listReadScope !== undefined && !criteria.crossOrgPublic()) {
90
91
  authorizedIdsByKind = new Map<string, ReadonlySet<string>>();
91
92
  for (const kind of criteria.effectiveKinds()) {
92
93
  authorizedIdsByKind.set(
@@ -81,6 +81,8 @@ export class SqliteSearchQueryStore implements SearchQueryStore {
81
81
  // is already goTrimSpace'd). Engine syntax is the driver's job.
82
82
  terms: criteria.hasQuery() ? goFields(criteria.query()) : undefined,
83
83
  orgFilter: criteria.orgFilter(),
84
+ crossOrgPublic: criteria.crossOrgPublic(),
85
+ excludePublic: criteria.excludePublic(),
84
86
  limit: criteria.pageSize(),
85
87
  offset: criteria.offset(),
86
88
  });
@@ -934,6 +934,8 @@ export function describeStoreContract(
934
934
  kinds: ["agent"],
935
935
  terms: ["kubernetes"],
936
936
  orgFilter: "",
937
+ crossOrgPublic: false,
938
+ excludePublic: false,
937
939
  limit: 20,
938
940
  offset: 0,
939
941
  });
@@ -957,6 +959,8 @@ export function describeStoreContract(
957
959
  kinds: ["agent"],
958
960
  terms: ["kuber"],
959
961
  orgFilter: "",
962
+ crossOrgPublic: false,
963
+ excludePublic: false,
960
964
  limit: 20,
961
965
  offset: 0,
962
966
  });
@@ -966,6 +970,8 @@ export function describeStoreContract(
966
970
  kinds: ["agent"],
967
971
  terms: ["kubernetes", "deployment"],
968
972
  orgFilter: "",
973
+ crossOrgPublic: false,
974
+ excludePublic: false,
969
975
  limit: 20,
970
976
  offset: 0,
971
977
  });
@@ -975,6 +981,8 @@ export function describeStoreContract(
975
981
  kinds: ["agent"],
976
982
  terms: ["kubernetes", "absent"],
977
983
  orgFilter: "",
984
+ crossOrgPublic: false,
985
+ excludePublic: false,
978
986
  limit: 20,
979
987
  offset: 0,
980
988
  });
@@ -1000,6 +1008,8 @@ export function describeStoreContract(
1000
1008
  kinds: ["agent"],
1001
1009
  terms: ["NEAR", "plain"],
1002
1010
  orgFilter: "",
1011
+ crossOrgPublic: false,
1012
+ excludePublic: false,
1003
1013
  limit: 20,
1004
1014
  offset: 0,
1005
1015
  });
@@ -1029,6 +1039,8 @@ export function describeStoreContract(
1029
1039
  kinds: ["agent", "workflow"],
1030
1040
  terms: ["scoped"],
1031
1041
  orgFilter: "",
1042
+ crossOrgPublic: false,
1043
+ excludePublic: false,
1032
1044
  authorizedIdsByKind: new Map([["agent", new Set(["agt-mine"])]]),
1033
1045
  limit: 20,
1034
1046
  offset: 0,
@@ -1044,6 +1056,8 @@ export function describeStoreContract(
1044
1056
  kinds: ["agent", "workflow"],
1045
1057
  terms: ["scoped"],
1046
1058
  orgFilter: "",
1059
+ crossOrgPublic: false,
1060
+ excludePublic: false,
1047
1061
  authorizedIdsByKind: new Map([
1048
1062
  ["agent", new Set<string>()],
1049
1063
  ["workflow", new Set(["wfl-any"])],
@@ -1058,6 +1072,8 @@ export function describeStoreContract(
1058
1072
  kinds: ["agent", "workflow"],
1059
1073
  terms: ["scoped"],
1060
1074
  orgFilter: "",
1075
+ crossOrgPublic: false,
1076
+ excludePublic: false,
1061
1077
  authorizedIdsByKind: new Map([
1062
1078
  ["agent", new Set<string>()],
1063
1079
  ["workflow", new Set<string>()],
@@ -1073,13 +1089,15 @@ export function describeStoreContract(
1073
1089
  kinds: ["agent", "workflow"],
1074
1090
  terms: ["scoped"],
1075
1091
  orgFilter: "",
1092
+ crossOrgPublic: false,
1093
+ excludePublic: false,
1076
1094
  limit: 20,
1077
1095
  offset: 0,
1078
1096
  });
1079
1097
  expect(unscoped.totalCount).toBe(3);
1080
1098
  });
1081
1099
 
1082
- it("org scoping is strict: the visibility column never widens or narrows the org filter", async () => {
1100
+ it("org scoping: strict filter, cross-org public widening, and the public subtraction", async () => {
1083
1101
  await fx.store.upsertSearchIndex(
1084
1102
  ApiResourceKind.agent,
1085
1103
  "agt-mine",
@@ -1087,11 +1105,11 @@ export function describeStoreContract(
1087
1105
  );
1088
1106
  await fx.store.upsertSearchIndex(
1089
1107
  ApiResourceKind.agent,
1090
- "agt-platform",
1108
+ "agt-public",
1091
1109
  entry({
1092
1110
  name: "searchable beta",
1093
1111
  org: "globex",
1094
- visibility: "visibility_platform",
1112
+ visibility: "visibility_public",
1095
1113
  }),
1096
1114
  );
1097
1115
  await fx.store.upsertSearchIndex(
@@ -1104,20 +1122,37 @@ export function describeStoreContract(
1104
1122
  kinds: ["agent"],
1105
1123
  terms: ["searchable"],
1106
1124
  orgFilter: "acme",
1125
+ crossOrgPublic: false,
1126
+ excludePublic: false,
1107
1127
  limit: 20,
1108
1128
  offset: 0,
1109
1129
  });
1110
1130
  expect(strict.hits.map((hit) => hit.resourceId)).toEqual(["agt-mine"]);
1111
1131
 
1112
- const unscoped = await fx.store.querySearchIndex({
1132
+ const widened = await fx.store.querySearchIndex({
1133
+ kinds: ["agent"],
1134
+ terms: ["searchable"],
1135
+ orgFilter: "acme",
1136
+ crossOrgPublic: true,
1137
+ excludePublic: false,
1138
+ limit: 20,
1139
+ offset: 0,
1140
+ });
1141
+ expect(new Set(widened.hits.map((hit) => hit.resourceId))).toEqual(
1142
+ new Set(["agt-mine", "agt-public"]),
1143
+ );
1144
+
1145
+ const noPublic = await fx.store.querySearchIndex({
1113
1146
  kinds: ["agent"],
1114
1147
  terms: ["searchable"],
1115
1148
  orgFilter: "",
1149
+ crossOrgPublic: false,
1150
+ excludePublic: true,
1116
1151
  limit: 20,
1117
1152
  offset: 0,
1118
1153
  });
1119
- expect(new Set(unscoped.hits.map((hit) => hit.resourceId))).toEqual(
1120
- new Set(["agt-mine", "agt-platform", "agt-foreign"]),
1154
+ expect(new Set(noPublic.hits.map((hit) => hit.resourceId))).toEqual(
1155
+ new Set(["agt-mine", "agt-foreign"]),
1121
1156
  );
1122
1157
  });
1123
1158
 
@@ -1137,6 +1172,8 @@ export function describeStoreContract(
1137
1172
  kinds: ["agent"],
1138
1173
  terms: undefined,
1139
1174
  orgFilter: "",
1175
+ crossOrgPublic: false,
1176
+ excludePublic: false,
1140
1177
  limit: 20,
1141
1178
  offset: 0,
1142
1179
  });
@@ -1166,6 +1203,8 @@ export function describeStoreContract(
1166
1203
  kinds: ["organization"],
1167
1204
  terms: ["original"],
1168
1205
  orgFilter: "",
1206
+ crossOrgPublic: false,
1207
+ excludePublic: false,
1169
1208
  limit: 20,
1170
1209
  offset: 0,
1171
1210
  });
@@ -1175,6 +1214,8 @@ export function describeStoreContract(
1175
1214
  kinds: ["organization"],
1176
1215
  terms: ["renamed"],
1177
1216
  orgFilter: "",
1217
+ crossOrgPublic: false,
1218
+ excludePublic: false,
1178
1219
  limit: 20,
1179
1220
  offset: 0,
1180
1221
  });
@@ -1185,6 +1226,8 @@ export function describeStoreContract(
1185
1226
  kinds: ["organization"],
1186
1227
  terms: undefined,
1187
1228
  orgFilter: "",
1229
+ crossOrgPublic: false,
1230
+ excludePublic: false,
1188
1231
  limit: 20,
1189
1232
  offset: 0,
1190
1233
  });
@@ -1200,6 +1243,8 @@ export function describeStoreContract(
1200
1243
  kinds: ["organization"],
1201
1244
  terms: undefined,
1202
1245
  orgFilter: "",
1246
+ crossOrgPublic: false,
1247
+ excludePublic: false,
1203
1248
  limit: 20,
1204
1249
  offset: 0,
1205
1250
  });
@@ -135,9 +135,8 @@ export interface SearchIndexEntry {
135
135
  /** Owning org (metadata.org) — org-scoped filtering. */
136
136
  readonly org: string;
137
137
  /**
138
- * Visibility enum NAME (e.g. "visibility_org"), carried for display;
139
- * scope filtering is by org and by the authorized-id map, never by
140
- * this column.
138
+ * Visibility enum NAME (e.g. "visibility_public"); scope filtering only
139
+ * special-cases "visibility_public", all other levels are org-scoped.
141
140
  */
142
141
  readonly visibility: string;
143
142
  /** Unix seconds creation time — sorting in list mode (no query). */
@@ -167,6 +166,10 @@ export interface SearchIndexQuery {
167
166
  readonly terms: readonly string[] | undefined;
168
167
  /** Org scope; "" = no org filter. */
169
168
  readonly orgFilter: string;
169
+ /** With orgFilter: also admit visibility_public rows from ANY org. */
170
+ readonly crossOrgPublic: boolean;
171
+ /** Independent subtraction: drop visibility_public rows from any scope. */
172
+ readonly excludePublic: boolean;
170
173
  /**
171
174
  * Optional per-kind authorized-id allowlist (kind NAME → ids) — the
172
175
  * multi-tenant list-read scoping arm (20260830.01): a listed kind