@stigmer/server 3.21.0 → 3.22.1-dev.20260922095755

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 (383) hide show
  1. package/dist/authorization/authorizer.d.ts +4 -8
  2. package/dist/authorization/authorizer.d.ts.map +1 -1
  3. package/dist/authorization/authorizer.js +5 -9
  4. package/dist/authorization/authorizer.js.map +1 -1
  5. package/dist/authorization/derived-tuples.d.ts.map +1 -1
  6. package/dist/authorization/derived-tuples.js +10 -25
  7. package/dist/authorization/derived-tuples.js.map +1 -1
  8. package/dist/authorization/evaluator.d.ts +13 -8
  9. package/dist/authorization/evaluator.d.ts.map +1 -1
  10. package/dist/authorization/evaluator.js +4 -20
  11. package/dist/authorization/evaluator.js.map +1 -1
  12. package/dist/authorization/list-read-scope.d.ts.map +1 -1
  13. package/dist/authorization/list-read-scope.js +24 -20
  14. package/dist/authorization/list-read-scope.js.map +1 -1
  15. package/dist/authorization/model/agent.d.ts.map +1 -1
  16. package/dist/authorization/model/agent.js +4 -5
  17. package/dist/authorization/model/agent.js.map +1 -1
  18. package/dist/authorization/model/agent_instance.d.ts.map +1 -1
  19. package/dist/authorization/model/agent_instance.js +2 -2
  20. package/dist/authorization/model/agent_instance.js.map +1 -1
  21. package/dist/authorization/model/mcp_server.d.ts.map +1 -1
  22. package/dist/authorization/model/mcp_server.js +2 -2
  23. package/dist/authorization/model/mcp_server.js.map +1 -1
  24. package/dist/authorization/model/organization.d.ts.map +1 -1
  25. package/dist/authorization/model/organization.js +2 -0
  26. package/dist/authorization/model/organization.js.map +1 -1
  27. package/dist/authorization/model/plugin.d.ts.map +1 -1
  28. package/dist/authorization/model/plugin.js +3 -4
  29. package/dist/authorization/model/plugin.js.map +1 -1
  30. package/dist/authorization/model/rewrite.d.ts +8 -10
  31. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  32. package/dist/authorization/model/rewrite.js +0 -3
  33. package/dist/authorization/model/rewrite.js.map +1 -1
  34. package/dist/authorization/model/skill.d.ts.map +1 -1
  35. package/dist/authorization/model/skill.js +2 -2
  36. package/dist/authorization/model/skill.js.map +1 -1
  37. package/dist/authorization/model/workflow.d.ts.map +1 -1
  38. package/dist/authorization/model/workflow.js +2 -2
  39. package/dist/authorization/model/workflow.js.map +1 -1
  40. package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
  41. package/dist/authorization/model/workflow_instance.js +2 -2
  42. package/dist/authorization/model/workflow_instance.js.map +1 -1
  43. package/dist/authorization/organization-directory.d.ts.map +1 -1
  44. package/dist/authorization/organization-directory.js +2 -3
  45. package/dist/authorization/organization-directory.js.map +1 -1
  46. package/dist/authorization/store-test-kit.d.ts +0 -6
  47. package/dist/authorization/store-test-kit.d.ts.map +1 -1
  48. package/dist/authorization/store-test-kit.js +16 -62
  49. package/dist/authorization/store-test-kit.js.map +1 -1
  50. package/dist/authorization/tuples.d.ts +13 -32
  51. package/dist/authorization/tuples.d.ts.map +1 -1
  52. package/dist/authorization/tuples.js +3 -11
  53. package/dist/authorization/tuples.js.map +1 -1
  54. package/dist/domain/agent/controller.d.ts.map +1 -1
  55. package/dist/domain/agent/controller.js +6 -4
  56. package/dist/domain/agent/controller.js.map +1 -1
  57. package/dist/domain/agentchannel/controller.d.ts.map +1 -1
  58. package/dist/domain/agentchannel/controller.js +9 -2
  59. package/dist/domain/agentchannel/controller.js.map +1 -1
  60. package/dist/domain/agentchannel/steps.d.ts +19 -0
  61. package/dist/domain/agentchannel/steps.d.ts.map +1 -1
  62. package/dist/domain/agentchannel/steps.js +32 -0
  63. package/dist/domain/agentchannel/steps.js.map +1 -1
  64. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  65. package/dist/domain/agentexecution/controller.js +3 -1
  66. package/dist/domain/agentexecution/controller.js.map +1 -1
  67. package/dist/domain/agentinstance/controller.d.ts +5 -5
  68. package/dist/domain/agentinstance/controller.d.ts.map +1 -1
  69. package/dist/domain/agentinstance/controller.js +9 -5
  70. package/dist/domain/agentinstance/controller.js.map +1 -1
  71. package/dist/domain/agentinstance/steps.d.ts +43 -8
  72. package/dist/domain/agentinstance/steps.d.ts.map +1 -1
  73. package/dist/domain/agentinstance/steps.js +71 -8
  74. package/dist/domain/agentinstance/steps.js.map +1 -1
  75. package/dist/domain/agentshare/constants.d.ts +6 -11
  76. package/dist/domain/agentshare/constants.d.ts.map +1 -1
  77. package/dist/domain/agentshare/constants.js +8 -16
  78. package/dist/domain/agentshare/constants.js.map +1 -1
  79. package/dist/domain/agentshare/controller.d.ts.map +1 -1
  80. package/dist/domain/agentshare/controller.js +10 -2
  81. package/dist/domain/agentshare/controller.js.map +1 -1
  82. package/dist/domain/agentshare/steps.d.ts +61 -10
  83. package/dist/domain/agentshare/steps.d.ts.map +1 -1
  84. package/dist/domain/agentshare/steps.js +121 -117
  85. package/dist/domain/agentshare/steps.js.map +1 -1
  86. package/dist/domain/artifact/controller.js +21 -25
  87. package/dist/domain/artifact/controller.js.map +1 -1
  88. package/dist/domain/channelapp/controller.d.ts.map +1 -1
  89. package/dist/domain/channelapp/controller.js +2 -0
  90. package/dist/domain/channelapp/controller.js.map +1 -1
  91. package/dist/domain/environment/controller.d.ts.map +1 -1
  92. package/dist/domain/environment/controller.js +4 -3
  93. package/dist/domain/environment/controller.js.map +1 -1
  94. package/dist/domain/executioncontext/controller.d.ts.map +1 -1
  95. package/dist/domain/executioncontext/controller.js +4 -1
  96. package/dist/domain/executioncontext/controller.js.map +1 -1
  97. package/dist/domain/executioncontext/steps.d.ts.map +1 -1
  98. package/dist/domain/executioncontext/steps.js +2 -1
  99. package/dist/domain/executioncontext/steps.js.map +1 -1
  100. package/dist/domain/mcpserver/connect.d.ts.map +1 -1
  101. package/dist/domain/mcpserver/connect.js +8 -7
  102. package/dist/domain/mcpserver/connect.js.map +1 -1
  103. package/dist/domain/mcpserver/controller.d.ts.map +1 -1
  104. package/dist/domain/mcpserver/controller.js +5 -4
  105. package/dist/domain/mcpserver/controller.js.map +1 -1
  106. package/dist/domain/mcpserver/start-connect.d.ts.map +1 -1
  107. package/dist/domain/mcpserver/start-connect.js +8 -7
  108. package/dist/domain/mcpserver/start-connect.js.map +1 -1
  109. package/dist/domain/memory/controller.d.ts.map +1 -1
  110. package/dist/domain/memory/controller.js +6 -1
  111. package/dist/domain/memory/controller.js.map +1 -1
  112. package/dist/domain/memory/steps.d.ts +23 -0
  113. package/dist/domain/memory/steps.d.ts.map +1 -1
  114. package/dist/domain/memory/steps.js +35 -0
  115. package/dist/domain/memory/steps.js.map +1 -1
  116. package/dist/domain/oauthapp/controller.d.ts.map +1 -1
  117. package/dist/domain/oauthapp/controller.js +2 -0
  118. package/dist/domain/oauthapp/controller.js.map +1 -1
  119. package/dist/domain/plugin/controller.d.ts.map +1 -1
  120. package/dist/domain/plugin/controller.js +6 -27
  121. package/dist/domain/plugin/controller.js.map +1 -1
  122. package/dist/domain/plugin/push.d.ts +7 -5
  123. package/dist/domain/plugin/push.d.ts.map +1 -1
  124. package/dist/domain/plugin/push.js +8 -14
  125. package/dist/domain/plugin/push.js.map +1 -1
  126. package/dist/domain/schedule/controller.d.ts.map +1 -1
  127. package/dist/domain/schedule/controller.js +10 -2
  128. package/dist/domain/schedule/controller.js.map +1 -1
  129. package/dist/domain/schedule/steps.d.ts +19 -0
  130. package/dist/domain/schedule/steps.d.ts.map +1 -1
  131. package/dist/domain/schedule/steps.js +32 -0
  132. package/dist/domain/schedule/steps.js.map +1 -1
  133. package/dist/domain/session/controller.d.ts.map +1 -1
  134. package/dist/domain/session/controller.js +3 -2
  135. package/dist/domain/session/controller.js.map +1 -1
  136. package/dist/domain/session/steps.d.ts +8 -9
  137. package/dist/domain/session/steps.d.ts.map +1 -1
  138. package/dist/domain/session/steps.js +17 -21
  139. package/dist/domain/session/steps.js.map +1 -1
  140. package/dist/domain/skill/controller.d.ts.map +1 -1
  141. package/dist/domain/skill/controller.js +8 -19
  142. package/dist/domain/skill/controller.js.map +1 -1
  143. package/dist/domain/workflow/agent-call-references.d.ts +54 -0
  144. package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
  145. package/dist/domain/workflow/agent-call-references.js +97 -0
  146. package/dist/domain/workflow/agent-call-references.js.map +1 -0
  147. package/dist/domain/workflow/controller.d.ts.map +1 -1
  148. package/dist/domain/workflow/controller.js +44 -113
  149. package/dist/domain/workflow/controller.js.map +1 -1
  150. package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
  151. package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
  152. package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
  153. package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
  154. package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
  155. package/dist/domain/workflow/version-resolution.d.ts +5 -0
  156. package/dist/domain/workflow/version-resolution.d.ts.map +1 -0
  157. package/dist/domain/workflow/version-resolution.js +113 -0
  158. package/dist/domain/workflow/version-resolution.js.map +1 -0
  159. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  160. package/dist/domain/workflowexecution/controller.js +4 -2
  161. package/dist/domain/workflowexecution/controller.js.map +1 -1
  162. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  163. package/dist/domain/workflowinstance/controller.js +9 -5
  164. package/dist/domain/workflowinstance/controller.js.map +1 -1
  165. package/dist/domain/workflowinstance/steps.d.ts +20 -0
  166. package/dist/domain/workflowinstance/steps.d.ts.map +1 -1
  167. package/dist/domain/workflowinstance/steps.js +39 -0
  168. package/dist/domain/workflowinstance/steps.js.map +1 -1
  169. package/dist/extensions/authorization-queries.d.ts +6 -5
  170. package/dist/extensions/authorization-queries.d.ts.map +1 -1
  171. package/dist/extensions/identity.d.ts +19 -0
  172. package/dist/extensions/identity.d.ts.map +1 -1
  173. package/dist/extensions/identity.js +22 -3
  174. package/dist/extensions/identity.js.map +1 -1
  175. package/dist/extensions/list-read-scope.d.ts +42 -8
  176. package/dist/extensions/list-read-scope.d.ts.map +1 -1
  177. package/dist/extensions/list-read-scope.js +13 -2
  178. package/dist/extensions/list-read-scope.js.map +1 -1
  179. package/dist/extensions/resource-authorization.d.ts +3 -2
  180. package/dist/extensions/resource-authorization.d.ts.map +1 -1
  181. package/dist/index.d.ts +1 -1
  182. package/dist/index.d.ts.map +1 -1
  183. package/dist/index.js.map +1 -1
  184. package/dist/pipeline/apiresource-meta.d.ts +12 -3
  185. package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
  186. package/dist/pipeline/apiresource-meta.js +21 -10
  187. package/dist/pipeline/apiresource-meta.js.map +1 -1
  188. package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
  189. package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
  190. package/dist/pipeline/steps/authorization-tuples.js +9 -11
  191. package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
  192. package/dist/pipeline/steps/authorize-resolved-target.d.ts +88 -0
  193. package/dist/pipeline/steps/authorize-resolved-target.d.ts.map +1 -0
  194. package/dist/pipeline/steps/authorize-resolved-target.js +36 -0
  195. package/dist/pipeline/steps/authorize-resolved-target.js.map +1 -0
  196. package/dist/pipeline/steps/authorize.d.ts +27 -0
  197. package/dist/pipeline/steps/authorize.d.ts.map +1 -1
  198. package/dist/pipeline/steps/authorize.js +49 -10
  199. package/dist/pipeline/steps/authorize.js.map +1 -1
  200. package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
  201. package/dist/pipeline/steps/guard-memory-capture.js +2 -1
  202. package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
  203. package/dist/pipeline/steps/guard-reserved-labels.d.ts.map +1 -1
  204. package/dist/pipeline/steps/guard-reserved-labels.js +6 -7
  205. package/dist/pipeline/steps/guard-reserved-labels.js.map +1 -1
  206. package/dist/pipeline/steps/helpers.d.ts +9 -5
  207. package/dist/pipeline/steps/helpers.d.ts.map +1 -1
  208. package/dist/pipeline/steps/helpers.js +11 -15
  209. package/dist/pipeline/steps/helpers.js.map +1 -1
  210. package/dist/pipeline/steps/references.d.ts +178 -20
  211. package/dist/pipeline/steps/references.d.ts.map +1 -1
  212. package/dist/pipeline/steps/references.js +344 -43
  213. package/dist/pipeline/steps/references.js.map +1 -1
  214. package/dist/pipeline/steps/version-history.d.ts +11 -0
  215. package/dist/pipeline/steps/version-history.d.ts.map +1 -1
  216. package/dist/pipeline/steps/version-history.js +26 -0
  217. package/dist/pipeline/steps/version-history.js.map +1 -1
  218. package/dist/query/search/controller.js +0 -1
  219. package/dist/query/search/controller.js.map +1 -1
  220. package/dist/query/search/criteria.d.ts +1 -5
  221. package/dist/query/search/criteria.d.ts.map +1 -1
  222. package/dist/query/search/criteria.js +3 -13
  223. package/dist/query/search/criteria.js.map +1 -1
  224. package/dist/query/search/handler.d.ts +5 -4
  225. package/dist/query/search/handler.d.ts.map +1 -1
  226. package/dist/query/search/handler.js +7 -6
  227. package/dist/query/search/handler.js.map +1 -1
  228. package/dist/query/search/query-store.d.ts.map +1 -1
  229. package/dist/query/search/query-store.js +0 -2
  230. package/dist/query/search/query-store.js.map +1 -1
  231. package/dist/store/interface.d.ts +3 -6
  232. package/dist/store/interface.d.ts.map +1 -1
  233. package/dist/store/interface.js.map +1 -1
  234. package/dist/store/postgres/migrations.d.ts +3 -1
  235. package/dist/store/postgres/migrations.d.ts.map +1 -1
  236. package/dist/store/postgres/migrations.js +35 -1
  237. package/dist/store/postgres/migrations.js.map +1 -1
  238. package/dist/store/postgres/store.d.ts.map +1 -1
  239. package/dist/store/postgres/store.js +4 -9
  240. package/dist/store/postgres/store.js.map +1 -1
  241. package/dist/store/public-visibility-retired.d.ts +67 -0
  242. package/dist/store/public-visibility-retired.d.ts.map +1 -0
  243. package/dist/store/public-visibility-retired.js +55 -0
  244. package/dist/store/public-visibility-retired.js.map +1 -0
  245. package/dist/store/sqlite/migrations.d.ts +3 -1
  246. package/dist/store/sqlite/migrations.d.ts.map +1 -1
  247. package/dist/store/sqlite/migrations.js +36 -1
  248. package/dist/store/sqlite/migrations.js.map +1 -1
  249. package/dist/store/sqlite/store.d.ts.map +1 -1
  250. package/dist/store/sqlite/store.js +4 -8
  251. package/dist/store/sqlite/store.js.map +1 -1
  252. package/package.json +6 -6
  253. package/src/authorization/README.md +24 -26
  254. package/src/authorization/__tests__/authorizer.test.ts +10 -9
  255. package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
  256. package/src/authorization/__tests__/evaluator.test.ts +14 -73
  257. package/src/authorization/__tests__/facts.test.ts +2 -2
  258. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
  259. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
  260. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
  261. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
  262. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
  263. package/src/authorization/__tests__/list-read-scope.test.ts +41 -29
  264. package/src/authorization/__tests__/skip-lane-inventory.test.ts +160 -0
  265. package/src/authorization/__tests__/store-tests.test.ts +1 -6
  266. package/src/authorization/__tests__/tuples.test.ts +12 -11
  267. package/src/authorization/__tests__/wire-permissions.test.ts +5 -5
  268. package/src/authorization/authorizer.ts +4 -9
  269. package/src/authorization/derived-tuples.ts +14 -31
  270. package/src/authorization/evaluator.ts +15 -44
  271. package/src/authorization/list-read-scope.ts +27 -22
  272. package/src/authorization/model/__tests__/registry.test.ts +2 -0
  273. package/src/authorization/model/agent.ts +2 -5
  274. package/src/authorization/model/agent_instance.ts +0 -2
  275. package/src/authorization/model/mcp_server.ts +0 -2
  276. package/src/authorization/model/organization.ts +2 -0
  277. package/src/authorization/model/plugin.ts +1 -4
  278. package/src/authorization/model/rewrite.ts +8 -13
  279. package/src/authorization/model/skill.ts +0 -2
  280. package/src/authorization/model/workflow.ts +0 -2
  281. package/src/authorization/model/workflow_instance.ts +0 -2
  282. package/src/authorization/organization-directory.ts +1 -3
  283. package/src/authorization/store-test-kit.ts +16 -75
  284. package/src/authorization/tuples.ts +16 -39
  285. package/src/domain/agent/__tests__/agent.test.ts +11 -4
  286. package/src/domain/agent/controller.ts +17 -7
  287. package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
  288. package/src/domain/agentchannel/__tests__/create-authorization.test.ts +58 -0
  289. package/src/domain/agentchannel/controller.ts +25 -1
  290. package/src/domain/agentchannel/steps.ts +40 -0
  291. package/src/domain/agentexecution/controller.ts +6 -1
  292. package/src/domain/agentinstance/__tests__/create-authorization.composed.test.ts +243 -0
  293. package/src/domain/agentinstance/__tests__/create-authorization.test.ts +144 -0
  294. package/src/domain/agentinstance/controller.ts +30 -17
  295. package/src/domain/agentinstance/steps.ts +82 -8
  296. package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
  297. package/src/domain/agentshare/__tests__/create-authorization.test.ts +71 -0
  298. package/src/domain/agentshare/__tests__/member-audience.test.ts +113 -0
  299. package/src/domain/agentshare/constants.ts +14 -26
  300. package/src/domain/agentshare/controller.ts +27 -1
  301. package/src/domain/agentshare/steps.ts +168 -155
  302. package/src/domain/artifact/controller.ts +18 -22
  303. package/src/domain/channelapp/controller.ts +10 -0
  304. package/src/domain/environment/controller.ts +15 -8
  305. package/src/domain/executioncontext/__tests__/executioncontext.test.ts +1 -0
  306. package/src/domain/executioncontext/controller.ts +15 -1
  307. package/src/domain/executioncontext/steps.ts +2 -1
  308. package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
  309. package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
  310. package/src/domain/mcpserver/connect.ts +11 -10
  311. package/src/domain/mcpserver/controller.ts +19 -13
  312. package/src/domain/mcpserver/start-connect.ts +17 -11
  313. package/src/domain/memory/__tests__/create-authorization.test.ts +59 -0
  314. package/src/domain/memory/controller.ts +11 -0
  315. package/src/domain/memory/steps.ts +41 -0
  316. package/src/domain/oauthapp/controller.ts +10 -0
  317. package/src/domain/plugin/controller.ts +23 -44
  318. package/src/domain/plugin/push.ts +8 -16
  319. package/src/domain/schedule/__tests__/create-authorization.test.ts +58 -0
  320. package/src/domain/schedule/controller.ts +26 -1
  321. package/src/domain/schedule/steps.ts +45 -2
  322. package/src/domain/session/controller.ts +12 -4
  323. package/src/domain/session/steps.ts +24 -32
  324. package/src/domain/skill/controller.ts +24 -30
  325. package/src/domain/workflow/__tests__/agent-call-references.test.ts +222 -0
  326. package/src/domain/workflow/agent-call-references.ts +157 -0
  327. package/src/domain/workflow/controller.ts +86 -155
  328. package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
  329. package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
  330. package/src/domain/workflow/version-resolution.ts +166 -0
  331. package/src/domain/workflowexecution/controller.ts +7 -2
  332. package/src/domain/workflowinstance/__tests__/create-authorization.test.ts +109 -0
  333. package/src/domain/workflowinstance/controller.ts +25 -12
  334. package/src/domain/workflowinstance/steps.ts +49 -1
  335. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
  336. package/src/extensions/__tests__/composed-support.ts +38 -4
  337. package/src/extensions/__tests__/extension-composition.test.ts +4 -4
  338. package/src/extensions/__tests__/identity.test.ts +44 -1
  339. package/src/extensions/__tests__/list-read-scope-composed.test.ts +96 -7
  340. package/src/extensions/__tests__/list-read-scope.test.ts +63 -0
  341. package/src/extensions/authorization-queries.ts +6 -5
  342. package/src/extensions/identity.ts +23 -5
  343. package/src/extensions/list-read-scope.ts +48 -8
  344. package/src/extensions/resource-authorization.ts +3 -5
  345. package/src/index.ts +0 -1
  346. package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
  347. package/src/pipeline/apiresource-meta.ts +21 -10
  348. package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
  349. package/src/pipeline/steps/__tests__/authorize-resolved-target.test.ts +285 -0
  350. package/src/pipeline/steps/__tests__/references.test.ts +703 -0
  351. package/src/pipeline/steps/authorization-tuples.ts +9 -11
  352. package/src/pipeline/steps/authorize-resolved-target.ts +129 -0
  353. package/src/pipeline/steps/authorize.ts +70 -13
  354. package/src/pipeline/steps/guard-memory-capture.ts +2 -1
  355. package/src/pipeline/steps/guard-reserved-labels.ts +6 -9
  356. package/src/pipeline/steps/helpers.ts +11 -15
  357. package/src/pipeline/steps/references.ts +578 -70
  358. package/src/pipeline/steps/version-history.ts +34 -0
  359. package/src/query/search/__tests__/criteria.test.ts +0 -4
  360. package/src/query/search/__tests__/query-store.test.ts +11 -40
  361. package/src/query/search/controller.ts +0 -1
  362. package/src/query/search/criteria.ts +0 -14
  363. package/src/query/search/handler.ts +6 -7
  364. package/src/query/search/query-store.ts +0 -2
  365. package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
  366. package/src/store/__tests__/store-contract.ts +6 -51
  367. package/src/store/interface.ts +3 -6
  368. package/src/store/postgres/__tests__/migrations.test.ts +187 -6
  369. package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
  370. package/src/store/postgres/migrations.ts +48 -1
  371. package/src/store/postgres/store.ts +4 -11
  372. package/src/store/public-visibility-retired.ts +119 -0
  373. package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
  374. package/src/store/sqlite/migrations.ts +45 -1
  375. package/src/store/sqlite/store.ts +4 -10
  376. package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
  377. package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
  378. package/dist/pipeline/steps/visibility-gates.js +0 -139
  379. package/dist/pipeline/steps/visibility-gates.js.map +0 -1
  380. package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
  381. package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
  382. package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
  383. package/src/pipeline/steps/visibility-gates.ts +0 -174
@@ -53,11 +53,11 @@ import type { PipelineStep } from "../../pipeline/pipeline.js";
53
53
  import { callerIdentityOf } from "../../pipeline/interceptors/auth.js";
54
54
  import { RequestContext } from "../../pipeline/request-context.js";
55
55
  import { newAuthorizeStep } from "../../pipeline/steps/authorize.js";
56
- import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
57
56
  import {
58
- newAuthorizeVisibilityTransitionStep,
59
- newGuardPublicVisibilityStep,
60
- } from "../../pipeline/steps/visibility-gates.js";
57
+ loadedTargetAsMethod,
58
+ newAuthorizeResolvedTargetStep,
59
+ } from "../../pipeline/steps/authorize-resolved-target.js";
60
+ import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
61
61
  import {
62
62
  newBuildNewStateStep,
63
63
  setAuditFieldsForUpdate,
@@ -87,7 +87,10 @@ import {
87
87
  TARGET_RESOURCE_KEY,
88
88
  newLoadTargetStep,
89
89
  } from "../../pipeline/steps/load-target.js";
90
- import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
90
+ import {
91
+ newNormalizeReferencesStep,
92
+ newValidateReferencesStep,
93
+ } from "../../pipeline/steps/references.js";
91
94
  import {
92
95
  newCleanupIamPoliciesStep,
93
96
  newCreateAuthorizationTuplesStep,
@@ -108,6 +111,7 @@ import {
108
111
  newRejectDefaultWorkflowInstanceVisibilityUpdateStep,
109
112
  newValidateInstanceUpdateStep,
110
113
  newValidateSameOrgBusinessRuleStep,
114
+ resolveWorkflowInstanceCreateTargets,
111
115
  } from "./steps.js";
112
116
  import type { ParentWorkflowLoaderProvider } from "./steps.js";
113
117
 
@@ -182,12 +186,20 @@ async function createInstance(
182
186
  .addStep(newValidateVisibilityStep())
183
187
  .addStep(newResolveSlugStep())
184
188
  .addStep(newLoadParentWorkflowStep(deps.parentWorkflowLoader, deps.logger))
189
+ // Before the same-org rule, whose refusal names the parent's organization
190
+ // (steps.ts, resolveWorkflowInstanceCreateTargets).
191
+ .addStep(
192
+ newAuthorizeResolvedTargetStep(
193
+ deps.authorizer,
194
+ resolveWorkflowInstanceCreateTargets,
195
+ ),
196
+ )
185
197
  .addStep(newValidateSameOrgBusinessRuleStep(deps.logger))
186
198
  .addStep(newCheckDuplicateStep(deps.store))
187
199
  .addStep(newBuildNewStateStep())
188
- .addStep(newGuardPublicVisibilityStep(deps.authorizer))
189
200
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
190
201
  .addStep(newNormalizeReferencesStep())
202
+ .addStep(newValidateReferencesStep(deps.store))
191
203
  .addStep(newPersistStep(deps.store))
192
204
  .addStep(
193
205
  newCreateAuthorizationTuplesStep(
@@ -236,6 +248,7 @@ async function update(
236
248
  .addStep(newBuildUpdateStateStep())
237
249
  .addStep(newGuardReservedLabelsStep(deps.authorizer))
238
250
  .addStep(newNormalizeReferencesStep())
251
+ .addStep(newValidateReferencesStep(deps.store))
239
252
  .addStep(newPersistStep(deps.store))
240
253
  .addStep(
241
254
  newIndexSearchStep(
@@ -394,12 +407,6 @@ async function updateVisibility(
394
407
  ),
395
408
  )
396
409
  .addStep(newValidateVisibilityUpdateStep())
397
- .addStep(
398
- newAuthorizeVisibilityTransitionStep(
399
- UPDATE_VISIBILITY_INSTANCE_KEY,
400
- deps.authorizer,
401
- ),
402
- )
403
410
  .addStep(newSetInstanceVisibilityStep())
404
411
  .addStep(
405
412
  newPersistInstanceStep(
@@ -707,6 +714,12 @@ async function getByReference(
707
714
  )
708
715
  .addStep(newValidateProtoStep())
709
716
  .addStep(newLoadByReferenceStep(deps.store, WorkflowInstanceSchema))
717
+ .addStep(
718
+ newAuthorizeResolvedTargetStep(
719
+ deps.authorizer,
720
+ loadedTargetAsMethod(WorkflowInstanceQueryController.method.get),
721
+ ),
722
+ )
710
723
  .build()
711
724
  .execute(reqCtx);
712
725
  return reqCtx.get(TARGET_RESOURCE_KEY) as WorkflowInstance;
@@ -16,8 +16,10 @@ import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/ap
16
16
  import { WorkflowInstanceSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
17
17
  import type { WorkflowInstance } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
18
18
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
19
+ import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
19
20
 
20
21
  import type { Logger } from "../../boot/logger.js";
22
+ import { isServerComposedRequest } from "../../extensions/identity.js";
21
23
  import { isDefaultInstance } from "../../pipeline/apiresource-labels.js";
22
24
  import {
23
25
  failedPreconditionError,
@@ -27,6 +29,7 @@ import {
27
29
  } from "../../pipeline/errors.js";
28
30
  import type { PipelineStep } from "../../pipeline/pipeline.js";
29
31
  import type { RequestContext } from "../../pipeline/request-context.js";
32
+ import type { AuthorizationTarget } from "../../pipeline/steps/authorize.js";
30
33
  import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
31
34
  import { rejectDefaultInstanceVisibilityUpdate } from "../../pipeline/steps/validate-visibility.js";
32
35
  import { ResourceNotFoundError } from "../../store/interface.js";
@@ -72,7 +75,8 @@ export function newLoadParentWorkflowStep(
72
75
  parent = await loaderProvider().get(workflowId);
73
76
  } catch (error) {
74
77
  logger.warn("Parent workflow not found", {
75
- error: error instanceof ConnectError ? error.rawMessage : String(error),
78
+ error:
79
+ error instanceof ConnectError ? error.rawMessage : String(error),
76
80
  workflowId,
77
81
  });
78
82
  throw notFoundError("Workflow", workflowId);
@@ -83,6 +87,50 @@ export function newLoadParentWorkflowStep(
83
87
  };
84
88
  }
85
89
 
90
+ /** The deny copy of the parent's bar, the Java WorkflowInstanceCreateHandler's. */
91
+ export const WORKFLOW_INSTANCE_PARENT_DENIED_MESSAGE =
92
+ "You don't have permission to create instances of this workflow";
93
+
94
+ /**
95
+ * The create lane's authorization question, for AuthorizeResolvedTarget
96
+ * after LoadParentWorkflow and BEFORE ValidateSameOrgBusinessRule (whose
97
+ * refusal names the parent's organization, which a refused caller should
98
+ * not learn). The RPC is is_skip_authorization because its target is the
99
+ * PARENT, loaded from spec.workflow_id, not a request field.
100
+ *
101
+ * One question: may the caller run this workflow (can_execute on the
102
+ * parent, the relation the run gate asks; the same-org rule already binds
103
+ * the instance's organization to the workflow's, so there is no separate
104
+ * organization bar). A DEFAULT instance asks nothing, for the reason its
105
+ * agent twin gives (agentinstance/steps.ts): it is the workflow's row,
106
+ * composed by the server for the run's human, keyed on the in-process
107
+ * origin, the reserved label and the parent's organization together.
108
+ */
109
+ export function resolveWorkflowInstanceCreateTargets(
110
+ ctx: RequestContext<InstanceDesc>,
111
+ ): ReadonlyArray<AuthorizationTarget> {
112
+ const parent = ctx.get(PARENT_WORKFLOW_KEY) as Workflow | undefined;
113
+ if (parent === undefined) {
114
+ throw new Error("parent workflow not found in context");
115
+ }
116
+ const metadata = ctx.newState.metadata;
117
+ if (
118
+ isDefaultInstance(metadata) &&
119
+ isServerComposedRequest(ctx.callerIdentity) &&
120
+ (metadata?.org ?? "") === (parent.metadata?.org ?? "")
121
+ ) {
122
+ return [];
123
+ }
124
+ return [
125
+ {
126
+ permission: IamPermission.can_execute,
127
+ resourceKind: ApiResourceKind.workflow,
128
+ resourceId: parent.metadata?.id ?? "",
129
+ deniedMessage: WORKFLOW_INSTANCE_PARENT_DENIED_MESSAGE,
130
+ },
131
+ ];
132
+ }
133
+
86
134
  /**
87
135
  * ValidateSameOrgBusinessRule — create.go: instances must live in the same
88
136
  * organization as the parent workflow; cross-org instances could leak
@@ -16,8 +16,9 @@
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
- * nobody sets public visibility; an unprovisioned subject
20
- * writes nothing until provisioned. The LIST lanes are a member's
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
21
22
  * lists: an outsider lists nothing of an organization they hold no
22
23
  * row in; environments and API keys are their owner's even to the
23
24
  * organization's owner; the library (a search) omits a private agent
@@ -26,8 +27,9 @@
26
27
  * provisioned.
27
28
  * - trusted-local: the permissive default stays (one caller,
28
29
  * nothing to separate; the laptop's rows are stamped `"system"`):
29
- * tokenless reads, public visibility and organization enumeration all
30
- * keep working.
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).
31
33
  * - a unit's own Authorizer: nothing built in is installed; the unit's
32
34
  * answer stands even against the founder.
33
35
  *
@@ -80,7 +82,6 @@ import { loadConfig } from "../../boot/config.js";
80
82
  import { composeServer } from "../../boot/compose.js";
81
83
  import type { ComposedServer } from "../../boot/compose.js";
82
84
  import { metadataOf } from "../../pipeline/steps/shapes.js";
83
- import { PUBLIC_VISIBILITY_DENY_MESSAGE } from "../../pipeline/steps/visibility-gates.js";
84
85
  import type { Store } from "../../store/interface.js";
85
86
  import type { Authorizer } from "../authorizer.js";
86
87
  import type { ListReadScope } from "../list-read-scope.js";
@@ -132,6 +133,15 @@ function organizationInput(slug: string) {
132
133
  };
133
134
  }
134
135
 
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
+
135
145
  function agentInput(
136
146
  name: string,
137
147
  visibility: ApiResourceVisibility,
@@ -377,14 +387,14 @@ describe("built-in authorizer (composed server, OIDC with no unit Authorizer: th
377
387
  );
378
388
  });
379
389
 
380
- it("nobody sets PUBLIC visibility on an enforcing self-host — the platform gate denies the founder with the policy copy", async () => {
390
+ it("the retired public level is refused for the founder as an invalid level, before any permission question", async () => {
381
391
  const failure = await failureOf(
382
392
  createClient(AgentCommandController, asFounder()).create(
383
393
  agentInput("Public Agent", ApiResourceVisibility.visibility_public),
384
394
  ),
385
395
  );
386
- expect(failure.code).toBe(Code.PermissionDenied);
387
- expect(failure.rawMessage).toBe(PUBLIC_VISIBILITY_DENY_MESSAGE);
396
+ expect(failure.code).toBe(Code.InvalidArgument);
397
+ expect(failure.rawMessage).toBe(PUBLIC_LEVEL_REFUSED_FOR_AGENT);
388
398
  });
389
399
 
390
400
  describe("the list lanes (the built-in ListReadScope)", () => {
@@ -595,20 +605,18 @@ describe("built-in authorizer (composed server, trusted-local: the permissive de
595
605
  expect(read.metadata?.id).toBe(created.metadata!.id);
596
606
  });
597
607
 
598
- it("the laptop keeps public visibility and organization enumeration — no directory, no platform gate that bites", async () => {
608
+ it("the laptop keeps organization enumeration and refuses the retired public level like every posture — the level is gone, not gated", async () => {
599
609
  const anonymous = transportFor(port);
600
- const publicAgent = await createClient(
601
- AgentCommandController,
602
- anonymous,
603
- ).create(
604
- agentInput(
605
- "Public Laptop Agent",
606
- ApiResourceVisibility.visibility_public,
610
+ const failure = await failureOf(
611
+ createClient(AgentCommandController, anonymous).create(
612
+ agentInput(
613
+ "Public Laptop Agent",
614
+ ApiResourceVisibility.visibility_public,
615
+ ),
607
616
  ),
608
617
  );
609
- expect(publicAgent.metadata?.visibility).toBe(
610
- ApiResourceVisibility.visibility_public,
611
- );
618
+ expect(failure.code).toBe(Code.InvalidArgument);
619
+ expect(failure.rawMessage).toBe(PUBLIC_LEVEL_REFUSED_FOR_AGENT);
612
620
  const all = await createClient(OrganizationQueryController, anonymous).find(
613
621
  { org: ORG },
614
622
  );
@@ -617,6 +625,11 @@ describe("built-in authorizer (composed server, trusted-local: the permissive de
617
625
 
618
626
  it("the laptop's lists are the full scan — no scope composed, the rows stamped `system` included", async () => {
619
627
  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
+ );
620
633
  const environments = createClient(EnvironmentCommandController, anonymous);
621
634
  await environments.create(environmentInput("laptop env one"));
622
635
  await environments.create(environmentInput("laptop env two"));
@@ -23,7 +23,7 @@
23
23
  */
24
24
  import path from "node:path";
25
25
 
26
- import type { DescService } from "@bufbuild/protobuf";
26
+ import type { DescMethod, DescService } from "@bufbuild/protobuf";
27
27
  import type {
28
28
  ConnectRouter,
29
29
  Interceptor,
@@ -94,11 +94,30 @@ export function bearer(token: string): Interceptor {
94
94
  export function servedServices(
95
95
  routes: (router: ConnectRouter) => void,
96
96
  ): DescService[] {
97
- const served: DescService[] = [];
97
+ return servedRegistrations(routes).map((r) => r.service);
98
+ }
99
+
100
+ /** One `router.service(desc, implementation)` registration, as replayed. */
101
+ export interface ServedRegistration {
102
+ readonly service: DescService;
103
+ readonly implementation: Readonly<Record<string, unknown>>;
104
+ }
105
+
106
+ /**
107
+ * The registrations themselves, with each service's implementation map,
108
+ * for a reader that must know which of a service's methods are actually
109
+ * served: a partially implemented service (a method registered only when
110
+ * a composed capability carries it) lists the method on its descriptor
111
+ * and omits it from the map, and ConnectRPC answers UNIMPLEMENTED for it.
112
+ */
113
+ export function servedRegistrations(
114
+ routes: (router: ConnectRouter) => void,
115
+ ): ServedRegistration[] {
116
+ const served: ServedRegistration[] = [];
98
117
  const recorder = {
99
118
  handlers: [],
100
- service(desc: DescService) {
101
- served.push(desc);
119
+ service(desc: DescService, implementation: Record<string, unknown>) {
120
+ served.push({ service: desc, implementation });
102
121
  return recorder;
103
122
  },
104
123
  rpc() {
@@ -109,6 +128,21 @@ export function servedServices(
109
128
  return served;
110
129
  }
111
130
 
131
+ /** The methods a composed server actually serves: on a registered service AND in its implementation map. */
132
+ export function servedMethods(
133
+ routes: (router: ConnectRouter) => void,
134
+ ): DescMethod[] {
135
+ const methods: DescMethod[] = [];
136
+ for (const { service, implementation } of servedRegistrations(routes)) {
137
+ for (const method of service.methods) {
138
+ if (method.localName in implementation) {
139
+ methods.push(method);
140
+ }
141
+ }
142
+ }
143
+ return methods;
144
+ }
145
+
112
146
  /** A gRPC transport to the composed server on `port`, presenting `token` when given. */
113
147
  export function transportFor(port: number, token?: string): Transport {
114
148
  return createGrpcTransport({
@@ -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→public keeps the floor)", async () => {
1077
+ it("updateVisibility fires the set-diff transition (org→private deletes the org shape)", 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_public,
1094
+ visibility: ApiResourceVisibility.visibility_private,
1095
1095
  });
1096
1096
  expect(visibilityEvents).toHaveLength(1);
1097
1097
  const event = visibilityEvents[0]!;
1098
- expect(event.shapesToCreate).toEqual(["public-viewer"]);
1099
- expect(event.shapesToDelete).toEqual([]);
1098
+ expect(event.shapesToCreate).toEqual([]);
1099
+ expect(event.shapesToDelete).toEqual(["org-viewer"]);
1100
1100
  });
1101
1101
 
1102
1102
  it("delete fires the cleanup event; a cleanup failure never fails the delete", async () => {
@@ -6,10 +6,20 @@
6
6
  * `create` RPC's admission rule (2a A7) and the three IamPolicy system
7
7
  * RPCs' (Q-OR-7). A wire `user`, `runner` or a composition's own class
8
8
  * (guest) is never one.
9
+ *
10
+ * Also pins `isServerComposedRequest`, the narrower predicate the label
11
+ * guard, the memory-capture gate, the execution-context create check and
12
+ * the default-instance arm of the instance creates share: `internal` or an
13
+ * in-process origin, and never a wire `machine` account — the one row
14
+ * where the two predicates part, pinned so a step trusting server-composed
15
+ * state can never be widened to the wire by picking the wrong one.
9
16
  */
10
17
  import { describe, expect, it } from "vitest";
11
18
 
12
- import { isPlatformPipelineCaller } from "../identity.js";
19
+ import {
20
+ isPlatformPipelineCaller,
21
+ isServerComposedRequest,
22
+ } from "../identity.js";
13
23
  import type { CallerIdentity } from "../identity.js";
14
24
 
15
25
  function caller(fields: Partial<CallerIdentity>): CallerIdentity {
@@ -60,3 +70,36 @@ describe("isPlatformPipelineCaller", () => {
60
70
  );
61
71
  });
62
72
  });
73
+
74
+ describe("isServerComposedRequest", () => {
75
+ it("is true for the internal class and for any identity that entered in-process", () => {
76
+ expect(isServerComposedRequest(caller({ callerClass: "internal" }))).toBe(
77
+ true,
78
+ );
79
+ expect(
80
+ isServerComposedRequest(
81
+ caller({ callerClass: "user", origin: "in-process" }),
82
+ ),
83
+ ).toBe(true);
84
+ expect(
85
+ isServerComposedRequest(
86
+ caller({ callerClass: "channel", origin: "in-process" }),
87
+ ),
88
+ ).toBe(true);
89
+ });
90
+
91
+ it("is false for every wire caller, the machine class included — the row where it parts from isPlatformPipelineCaller", () => {
92
+ const wireMachine = caller({ callerClass: "machine" });
93
+ expect(isServerComposedRequest(wireMachine)).toBe(false);
94
+ expect(isPlatformPipelineCaller(wireMachine)).toBe(true);
95
+ expect(isServerComposedRequest(caller({ callerClass: "user" }))).toBe(
96
+ false,
97
+ );
98
+ expect(
99
+ isServerComposedRequest(caller({ callerClass: "user", origin: "wire" })),
100
+ ).toBe(false);
101
+ expect(isServerComposedRequest(caller({ callerClass: "runner" }))).toBe(
102
+ false,
103
+ );
104
+ });
105
+ });
@@ -10,9 +10,16 @@
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
- * crossOrgPublic bypass where FGA is never consulted — lane 21),
13
+ * proof that no request shape bypasses it — lane 21),
14
14
  * - the OUTAGE arm: a throwing scope answers the sanitized INTERNAL,
15
- * never an empty (or full!) list.
15
+ * never an empty (or full!) list;
16
+ * - the SERVER'S OWN reads over `inProcessTransport` (stigmer#1207):
17
+ * the bare in-process call is stamped `internal` and the helper
18
+ * answers it before the scope — agentexecution.listBySession (the
19
+ * lane every server-internal reader of a session's turns rides) and
20
+ * workflowexecution.list with the org arm still applied; a caller
21
+ * PROPAGATED through the in-process header keeps its class and is
22
+ * narrowed exactly like the wire.
16
23
  *
17
24
  * Per-lane logic beyond the wiring is pinned in the helper matrix
18
25
  * (list-read-scope.test.ts), the summaries suite, and the store-contract
@@ -31,6 +38,8 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
31
38
 
32
39
  import { ActivityQueryController } from "@stigmer/protos/ai/stigmer/activity/v1/query_pb";
33
40
  import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
41
+ import { AgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
42
+ import { AgentExecutionQueryController } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/query_pb";
34
43
  import { SessionSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
35
44
  import { SessionQueryController } from "@stigmer/protos/ai/stigmer/agentic/session/v1/query_pb";
36
45
  import { WorkflowExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
@@ -44,6 +53,11 @@ import { loadConfig } from "../../boot/config.js";
44
53
  import { composeServer } from "../../boot/compose.js";
45
54
  import type { ComposedServer } from "../../boot/compose.js";
46
55
  import { createLogger } from "../../boot/logger.js";
56
+ import { testCallerIdentity } from "../../pipeline/__tests__/support.js";
57
+ import {
58
+ IN_PROCESS_CALLER_HEADER,
59
+ encodeInProcessCaller,
60
+ } from "../../pipeline/interceptors/auth.js";
47
61
  import type { ListReadScope } from "../list-read-scope.js";
48
62
  import type { ServerExtension } from "../registry.js";
49
63
 
@@ -133,6 +147,27 @@ describe("list read scope (composed server, fake scope)", () => {
133
147
  }),
134
148
  );
135
149
  }
150
+ // Two turns of one session — the rows a server-internal reader of a
151
+ // conversation's executions asks listBySession for.
152
+ for (const id of ["aex_turn_1", "aex_turn_2"]) {
153
+ await server.store.saveResource(
154
+ ApiResourceKind.agent_execution,
155
+ id,
156
+ AgentExecutionSchema,
157
+ create(AgentExecutionSchema, {
158
+ apiVersion: "agentic.stigmer.ai/v1",
159
+ kind: "AgentExecution",
160
+ metadata: { id, name: id, org: "acme" },
161
+ spec: { sessionId: "ses_mine" },
162
+ status: {
163
+ audit: {
164
+ specAudit: { createdAt: { seconds: 1_700_000_000n } },
165
+ statusAudit: { updatedAt: { seconds: 1_700_000_000n } },
166
+ },
167
+ },
168
+ }),
169
+ );
170
+ }
136
171
  for (const [id, org] of [
137
172
  ["wfe_mine", "acme"],
138
173
  ["wfe_other_org", "rival"],
@@ -242,20 +277,23 @@ describe("list read scope (composed server, fake scope)", () => {
242
277
  ]);
243
278
  });
244
279
 
245
- it("search narrows through the engine allowlist; crossOrgPublic bypasses the scope (lane 21)", async () => {
280
+ it("search narrows through the engine allowlist on every request shape — nothing bypasses the scope (lane 21)", async () => {
246
281
  allowed = new Set(["agt_mine"]);
247
282
  const search = createClient(SearchService, transport);
248
283
  const scoped = await search.search({ query: "scopedagent" });
249
284
  expect(scoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
250
285
 
251
- // The Java bypass: public-widened discovery never consults FGA.
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.
252
289
  seenKinds.length = 0;
253
- await search.search({
290
+ const orgScoped = await search.search({
291
+ kinds: [ApiResourceKind.agent],
254
292
  query: "scopedagent",
255
293
  org: "acme",
256
- crossOrgPublic: true,
257
294
  });
258
- expect(seenKinds).toEqual([]);
295
+ expect(orgScoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
296
+ expect(seenKinds).toEqual([ApiResourceKind.agent]);
259
297
  });
260
298
 
261
299
  it("a scope outage answers the sanitized INTERNAL — never an unscoped or empty success", async () => {
@@ -273,4 +311,55 @@ describe("list read scope (composed server, fake scope)", () => {
273
311
  const list = await query.list({});
274
312
  expect(list.entries).toEqual([]);
275
313
  });
314
+
315
+ describe("the server's own reads over the in-process transport (stigmer#1207)", () => {
316
+ it("agentexecution.listBySession, bare: every turn of the session, the scope never asked", async () => {
317
+ // The scope would hide everything if it were asked.
318
+ allowed = new Set();
319
+ const query = createClient(
320
+ AgentExecutionQueryController,
321
+ server.inProcessTransport,
322
+ );
323
+ const turns = await query.listBySession({ sessionId: "ses_mine" });
324
+ expect(turns.entries.map((e) => e.metadata?.id).sort()).toEqual([
325
+ "aex_turn_1",
326
+ "aex_turn_2",
327
+ ]);
328
+ expect(seenKinds).toEqual([]);
329
+ });
330
+
331
+ it("a caller propagated through the in-process header keeps its class and is narrowed like the wire", async () => {
332
+ allowed = new Set(["aex_turn_2"]);
333
+ const query = createClient(
334
+ AgentExecutionQueryController,
335
+ server.inProcessTransport,
336
+ );
337
+ const turns = await query.listBySession(
338
+ { sessionId: "ses_mine" },
339
+ {
340
+ headers: {
341
+ [IN_PROCESS_CALLER_HEADER]: encodeInProcessCaller(
342
+ testCallerIdentity({ identityId: "alice" }),
343
+ ),
344
+ },
345
+ },
346
+ );
347
+ expect(turns.entries.map((e) => e.metadata?.id)).toEqual(["aex_turn_2"]);
348
+ expect(seenKinds).toEqual([ApiResourceKind.agent_execution]);
349
+ });
350
+
351
+ it("workflowexecution.list, bare: the request's org predicate still applies, the scope never asked", async () => {
352
+ allowed = new Set();
353
+ const query = createClient(
354
+ WorkflowExecutionQueryController,
355
+ server.inProcessTransport,
356
+ );
357
+ const acme = await query.list({ org: "acme" });
358
+ expect(acme.entries.map((e) => e.metadata?.id).sort()).toEqual([
359
+ "wfe_foreign",
360
+ "wfe_mine",
361
+ ]);
362
+ expect(seenKinds).toEqual([]);
363
+ });
364
+ });
276
365
  });
@@ -12,6 +12,11 @@
12
12
  * 20260913.04 T02 the scope is the last per-row predicate, so it is
13
13
  * offered the org's rows and never the kind's — the same result set,
14
14
  * a fraction of the candidates);
15
+ * - the `internal` class — the server acting as itself — gets the
16
+ * org-narrowed rows and the scope is never asked (the Authorize
17
+ * step's trust-domain rule on a list answer, stigmer#1207); the rule
18
+ * is class-only, so a caller PROPAGATED through the in-process
19
+ * transport (its own class, origin in-process) is still narrowed;
15
20
  * - the candidates carry {id, org, labels} — the driver's guest
16
21
  * cookie rule keys on labels — and the row's AUTHORIZATION FACTS
17
22
  * (creator stamp, visibility level, every parent link), read by the
@@ -160,6 +165,64 @@ describe("restrictListByReadScope", () => {
160
165
  ]);
161
166
  });
162
167
 
168
+ describe("the internal class — the server acting as itself (stigmer#1207)", () => {
169
+ /** A scope that must never be reached: any call is the failure under test. */
170
+ const neverAsked: ListReadScope = {
171
+ authorizedResourceIds: () =>
172
+ Promise.reject(new Error("the scope was asked for the internal class")),
173
+ restrictListEntries: () =>
174
+ Promise.reject(new Error("the scope was asked for the internal class")),
175
+ };
176
+ const internal = testCallerIdentity({
177
+ identityId: "system",
178
+ callerClass: "internal",
179
+ origin: "in-process",
180
+ });
181
+
182
+ it("gets the org-narrowed rows in the lane's order; the scope is never asked", async () => {
183
+ const withOrg = await restrictListByReadScope(
184
+ neverAsked,
185
+ internal,
186
+ ApiResourceKind.session,
187
+ rows,
188
+ "acme",
189
+ );
190
+ // The request's org predicate still applies — it is the request's,
191
+ // not the caller's; only the caller predicate is skipped.
192
+ expect(withOrg.map((r) => r.metadata.id)).toEqual(["ses_a", "ses_b"]);
193
+ const blankOrg = await restrictListByReadScope(
194
+ neverAsked,
195
+ internal,
196
+ ApiResourceKind.session,
197
+ rows,
198
+ "",
199
+ );
200
+ expect(blankOrg.map((r) => r.metadata.id)).toEqual([
201
+ "ses_a",
202
+ "ses_b",
203
+ "ses_c",
204
+ ]);
205
+ // A fresh array either way, as every arm of the helper returns.
206
+ expect(blankOrg).not.toBe(rows);
207
+ });
208
+
209
+ it("the rule is class-only: a caller propagated through the in-process transport keeps its class and is still narrowed", async () => {
210
+ const propagated = testCallerIdentity({
211
+ identityId: "alice",
212
+ callerClass: "user",
213
+ origin: "in-process",
214
+ });
215
+ const kept = await restrictListByReadScope(
216
+ scopeKeeping(["ses_c"]),
217
+ propagated,
218
+ ApiResourceKind.session,
219
+ rows,
220
+ "",
221
+ );
222
+ expect(kept.map((r) => r.metadata.id)).toEqual(["ses_c"]);
223
+ });
224
+ });
225
+
163
226
  it("an empty kept set is a real answer — the empty list", async () => {
164
227
  const kept = await restrictListByReadScope(
165
228
  scopeKeeping([]),