@stigmer/server 3.23.0 → 3.23.1-dev.20260923075205

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 (267) 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 +7 -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 +5 -9
  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/plugin.d.ts.map +1 -1
  25. package/dist/authorization/model/plugin.js +3 -4
  26. package/dist/authorization/model/plugin.js.map +1 -1
  27. package/dist/authorization/model/rewrite.d.ts +8 -10
  28. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  29. package/dist/authorization/model/rewrite.js +0 -3
  30. package/dist/authorization/model/rewrite.js.map +1 -1
  31. package/dist/authorization/model/skill.d.ts.map +1 -1
  32. package/dist/authorization/model/skill.js +2 -2
  33. package/dist/authorization/model/skill.js.map +1 -1
  34. package/dist/authorization/model/workflow.d.ts.map +1 -1
  35. package/dist/authorization/model/workflow.js +2 -2
  36. package/dist/authorization/model/workflow.js.map +1 -1
  37. package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
  38. package/dist/authorization/model/workflow_instance.js +2 -2
  39. package/dist/authorization/model/workflow_instance.js.map +1 -1
  40. package/dist/authorization/organization-directory.d.ts.map +1 -1
  41. package/dist/authorization/organization-directory.js +2 -3
  42. package/dist/authorization/organization-directory.js.map +1 -1
  43. package/dist/authorization/store-test-kit.d.ts +0 -6
  44. package/dist/authorization/store-test-kit.d.ts.map +1 -1
  45. package/dist/authorization/store-test-kit.js +16 -62
  46. package/dist/authorization/store-test-kit.js.map +1 -1
  47. package/dist/authorization/tuples.d.ts +13 -32
  48. package/dist/authorization/tuples.d.ts.map +1 -1
  49. package/dist/authorization/tuples.js +3 -11
  50. package/dist/authorization/tuples.js.map +1 -1
  51. package/dist/domain/agent/controller.d.ts.map +1 -1
  52. package/dist/domain/agent/controller.js +4 -4
  53. package/dist/domain/agent/controller.js.map +1 -1
  54. package/dist/domain/agentchannel/controller.d.ts.map +1 -1
  55. package/dist/domain/agentchannel/controller.js +3 -1
  56. package/dist/domain/agentchannel/controller.js.map +1 -1
  57. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  58. package/dist/domain/agentexecution/controller.js +3 -1
  59. package/dist/domain/agentexecution/controller.js.map +1 -1
  60. package/dist/domain/agentinstance/controller.d.ts.map +1 -1
  61. package/dist/domain/agentinstance/controller.js +3 -4
  62. package/dist/domain/agentinstance/controller.js.map +1 -1
  63. package/dist/domain/agentshare/constants.d.ts +6 -11
  64. package/dist/domain/agentshare/constants.d.ts.map +1 -1
  65. package/dist/domain/agentshare/constants.js +8 -16
  66. package/dist/domain/agentshare/constants.js.map +1 -1
  67. package/dist/domain/agentshare/controller.d.ts.map +1 -1
  68. package/dist/domain/agentshare/controller.js +3 -1
  69. package/dist/domain/agentshare/controller.js.map +1 -1
  70. package/dist/domain/agentshare/steps.d.ts +18 -25
  71. package/dist/domain/agentshare/steps.d.ts.map +1 -1
  72. package/dist/domain/agentshare/steps.js +41 -142
  73. package/dist/domain/agentshare/steps.js.map +1 -1
  74. package/dist/domain/environment/controller.d.ts.map +1 -1
  75. package/dist/domain/environment/controller.js +2 -3
  76. package/dist/domain/environment/controller.js.map +1 -1
  77. package/dist/domain/executioncontext/controller.d.ts.map +1 -1
  78. package/dist/domain/executioncontext/controller.js +2 -1
  79. package/dist/domain/executioncontext/controller.js.map +1 -1
  80. package/dist/domain/mcpserver/controller.d.ts.map +1 -1
  81. package/dist/domain/mcpserver/controller.js +3 -4
  82. package/dist/domain/mcpserver/controller.js.map +1 -1
  83. package/dist/domain/plugin/controller.d.ts.map +1 -1
  84. package/dist/domain/plugin/controller.js +1 -3
  85. package/dist/domain/plugin/controller.js.map +1 -1
  86. package/dist/domain/plugin/push.d.ts +7 -5
  87. package/dist/domain/plugin/push.d.ts.map +1 -1
  88. package/dist/domain/plugin/push.js +8 -14
  89. package/dist/domain/plugin/push.js.map +1 -1
  90. package/dist/domain/schedule/controller.d.ts.map +1 -1
  91. package/dist/domain/schedule/controller.js +3 -1
  92. package/dist/domain/schedule/controller.js.map +1 -1
  93. package/dist/domain/session/controller.d.ts.map +1 -1
  94. package/dist/domain/session/controller.js +3 -2
  95. package/dist/domain/session/controller.js.map +1 -1
  96. package/dist/domain/skill/controller.d.ts.map +1 -1
  97. package/dist/domain/skill/controller.js +0 -2
  98. package/dist/domain/skill/controller.js.map +1 -1
  99. package/dist/domain/workflow/agent-call-references.d.ts +54 -0
  100. package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
  101. package/dist/domain/workflow/agent-call-references.js +97 -0
  102. package/dist/domain/workflow/agent-call-references.js.map +1 -0
  103. package/dist/domain/workflow/controller.d.ts.map +1 -1
  104. package/dist/domain/workflow/controller.js +14 -6
  105. package/dist/domain/workflow/controller.js.map +1 -1
  106. package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
  107. package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
  108. package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
  109. package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
  110. package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
  111. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  112. package/dist/domain/workflowexecution/controller.js +4 -2
  113. package/dist/domain/workflowexecution/controller.js.map +1 -1
  114. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  115. package/dist/domain/workflowinstance/controller.js +3 -4
  116. package/dist/domain/workflowinstance/controller.js.map +1 -1
  117. package/dist/extensions/authorization-queries.d.ts +6 -5
  118. package/dist/extensions/authorization-queries.d.ts.map +1 -1
  119. package/dist/extensions/list-read-scope.d.ts +1 -1
  120. package/dist/extensions/resource-authorization.d.ts +3 -2
  121. package/dist/extensions/resource-authorization.d.ts.map +1 -1
  122. package/dist/index.d.ts +1 -1
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js.map +1 -1
  125. package/dist/pipeline/apiresource-meta.d.ts +12 -3
  126. package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
  127. package/dist/pipeline/apiresource-meta.js +21 -10
  128. package/dist/pipeline/apiresource-meta.js.map +1 -1
  129. package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
  130. package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
  131. package/dist/pipeline/steps/authorization-tuples.js +9 -11
  132. package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
  133. package/dist/pipeline/steps/references.d.ts +178 -20
  134. package/dist/pipeline/steps/references.d.ts.map +1 -1
  135. package/dist/pipeline/steps/references.js +344 -43
  136. package/dist/pipeline/steps/references.js.map +1 -1
  137. package/dist/query/search/controller.js +0 -1
  138. package/dist/query/search/controller.js.map +1 -1
  139. package/dist/query/search/criteria.d.ts +1 -5
  140. package/dist/query/search/criteria.d.ts.map +1 -1
  141. package/dist/query/search/criteria.js +3 -13
  142. package/dist/query/search/criteria.js.map +1 -1
  143. package/dist/query/search/handler.d.ts +5 -4
  144. package/dist/query/search/handler.d.ts.map +1 -1
  145. package/dist/query/search/handler.js +7 -6
  146. package/dist/query/search/handler.js.map +1 -1
  147. package/dist/query/search/query-store.d.ts.map +1 -1
  148. package/dist/query/search/query-store.js +0 -2
  149. package/dist/query/search/query-store.js.map +1 -1
  150. package/dist/store/interface.d.ts +3 -6
  151. package/dist/store/interface.d.ts.map +1 -1
  152. package/dist/store/interface.js.map +1 -1
  153. package/dist/store/postgres/migrations.d.ts +3 -1
  154. package/dist/store/postgres/migrations.d.ts.map +1 -1
  155. package/dist/store/postgres/migrations.js +35 -1
  156. package/dist/store/postgres/migrations.js.map +1 -1
  157. package/dist/store/postgres/store.d.ts.map +1 -1
  158. package/dist/store/postgres/store.js +4 -9
  159. package/dist/store/postgres/store.js.map +1 -1
  160. package/dist/store/public-visibility-retired.d.ts +67 -0
  161. package/dist/store/public-visibility-retired.d.ts.map +1 -0
  162. package/dist/store/public-visibility-retired.js +55 -0
  163. package/dist/store/public-visibility-retired.js.map +1 -0
  164. package/dist/store/sqlite/migrations.d.ts +3 -1
  165. package/dist/store/sqlite/migrations.d.ts.map +1 -1
  166. package/dist/store/sqlite/migrations.js +36 -1
  167. package/dist/store/sqlite/migrations.js.map +1 -1
  168. package/dist/store/sqlite/store.d.ts.map +1 -1
  169. package/dist/store/sqlite/store.js +4 -8
  170. package/dist/store/sqlite/store.js.map +1 -1
  171. package/package.json +6 -6
  172. package/src/authorization/README.md +24 -26
  173. package/src/authorization/__tests__/authorizer.test.ts +10 -9
  174. package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
  175. package/src/authorization/__tests__/evaluator.test.ts +14 -73
  176. package/src/authorization/__tests__/facts.test.ts +2 -2
  177. package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +19 -27
  178. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
  179. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
  180. package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +1 -15
  181. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
  182. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
  183. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
  184. package/src/authorization/__tests__/list-read-scope.test.ts +18 -17
  185. package/src/authorization/__tests__/store-tests.test.ts +2 -6
  186. package/src/authorization/__tests__/tuples.test.ts +12 -11
  187. package/src/authorization/authorizer.ts +4 -9
  188. package/src/authorization/derived-tuples.ts +14 -31
  189. package/src/authorization/evaluator.ts +8 -44
  190. package/src/authorization/list-read-scope.ts +5 -11
  191. package/src/authorization/model/agent.ts +2 -5
  192. package/src/authorization/model/agent_instance.ts +0 -2
  193. package/src/authorization/model/mcp_server.ts +0 -2
  194. package/src/authorization/model/plugin.ts +1 -4
  195. package/src/authorization/model/rewrite.ts +8 -13
  196. package/src/authorization/model/skill.ts +0 -2
  197. package/src/authorization/model/workflow.ts +0 -2
  198. package/src/authorization/model/workflow_instance.ts +0 -2
  199. package/src/authorization/organization-directory.ts +1 -3
  200. package/src/authorization/store-test-kit.ts +16 -75
  201. package/src/authorization/tuples.ts +16 -39
  202. package/src/domain/agent/__tests__/agent.test.ts +11 -4
  203. package/src/domain/agent/controller.ts +7 -7
  204. package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
  205. package/src/domain/agentchannel/controller.ts +6 -1
  206. package/src/domain/agentexecution/controller.ts +6 -1
  207. package/src/domain/agentinstance/controller.ts +6 -12
  208. package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
  209. package/src/domain/agentshare/__tests__/create-authorization.test.ts +16 -31
  210. package/src/domain/agentshare/constants.ts +14 -26
  211. package/src/domain/agentshare/controller.ts +6 -1
  212. package/src/domain/agentshare/steps.ts +41 -194
  213. package/src/domain/environment/controller.ts +5 -8
  214. package/src/domain/executioncontext/controller.ts +5 -1
  215. package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
  216. package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
  217. package/src/domain/mcpserver/controller.ts +6 -12
  218. package/src/domain/plugin/controller.ts +1 -8
  219. package/src/domain/plugin/push.ts +8 -16
  220. package/src/domain/schedule/controller.ts +6 -1
  221. package/src/domain/session/controller.ts +12 -4
  222. package/src/domain/skill/controller.ts +0 -7
  223. package/src/domain/workflow/__tests__/agent-call-references.test.ts +222 -0
  224. package/src/domain/workflow/agent-call-references.ts +157 -0
  225. package/src/domain/workflow/controller.ts +28 -10
  226. package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
  227. package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
  228. package/src/domain/workflowexecution/controller.ts +7 -2
  229. package/src/domain/workflowinstance/controller.ts +6 -12
  230. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
  231. package/src/extensions/__tests__/extension-composition.test.ts +4 -4
  232. package/src/extensions/__tests__/list-read-scope-composed.test.ts +9 -6
  233. package/src/extensions/authorization-queries.ts +6 -5
  234. package/src/extensions/list-read-scope.ts +1 -1
  235. package/src/extensions/resource-authorization.ts +3 -5
  236. package/src/index.ts +0 -1
  237. package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
  238. package/src/pipeline/apiresource-meta.ts +21 -10
  239. package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
  240. package/src/pipeline/steps/__tests__/references.test.ts +703 -0
  241. package/src/pipeline/steps/authorization-tuples.ts +9 -11
  242. package/src/pipeline/steps/references.ts +578 -70
  243. package/src/query/search/__tests__/criteria.test.ts +0 -4
  244. package/src/query/search/__tests__/query-store.test.ts +11 -40
  245. package/src/query/search/controller.ts +0 -1
  246. package/src/query/search/criteria.ts +0 -14
  247. package/src/query/search/handler.ts +6 -7
  248. package/src/query/search/query-store.ts +0 -2
  249. package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
  250. package/src/store/__tests__/store-contract.ts +6 -51
  251. package/src/store/interface.ts +3 -6
  252. package/src/store/postgres/__tests__/migrations.test.ts +187 -6
  253. package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
  254. package/src/store/postgres/migrations.ts +48 -1
  255. package/src/store/postgres/store.ts +4 -11
  256. package/src/store/public-visibility-retired.ts +119 -0
  257. package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
  258. package/src/store/sqlite/migrations.ts +45 -1
  259. package/src/store/sqlite/store.ts +4 -10
  260. package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
  261. package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
  262. package/dist/pipeline/steps/visibility-gates.js +0 -139
  263. package/dist/pipeline/steps/visibility-gates.js.map +0 -1
  264. package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
  265. package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
  266. package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
  267. package/src/pipeline/steps/visibility-gates.ts +0 -174
@@ -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"));
@@ -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 () => {
@@ -10,7 +10,7 @@
10
10
  * narrows, blank spans orgs — lane 6),
11
11
  * - activity.listRecentActivity (enumeration verb, two kinds — lane 22),
12
12
  * - search (enumeration verb feeding the engine allowlist, and the
13
- * 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
15
  * never an empty (or full!) list;
16
16
  * - the SERVER'S OWN reads over `inProcessTransport` (stigmer#1207):
@@ -277,20 +277,23 @@ describe("list read scope (composed server, fake scope)", () => {
277
277
  ]);
278
278
  });
279
279
 
280
- 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 () => {
281
281
  allowed = new Set(["agt_mine"]);
282
282
  const search = createClient(SearchService, transport);
283
283
  const scoped = await search.search({ query: "scopedagent" });
284
284
  expect(scoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
285
285
 
286
- // The 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.
287
289
  seenKinds.length = 0;
288
- await search.search({
290
+ const orgScoped = await search.search({
291
+ kinds: [ApiResourceKind.agent],
289
292
  query: "scopedagent",
290
293
  org: "acme",
291
- crossOrgPublic: true,
292
294
  });
293
- expect(seenKinds).toEqual([]);
295
+ expect(orgScoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
296
+ expect(seenKinds).toEqual([ApiResourceKind.agent]);
294
297
  });
295
298
 
296
299
  it("a scope outage answers the sanitized INTERNAL — never an unscoped or empty success", async () => {
@@ -32,9 +32,10 @@
32
32
  * Semantics the controller and the conformance suite rely on (the Java
33
33
  * check-handler posture the cloud's client already implements):
34
34
  *
35
- * - `check` answers a public grant (a wildcard principal resolves), so
36
- * "may I read this public agent" is true for anyone.
37
- * - the two listings never expand wildcards, and `listPrincipalIds`
35
+ * - `check` resolves usersets (a grant to `organization:acme#member`
36
+ * answers true for a member), so "may I read this" is the model's
37
+ * answer and not a lookup of who was named.
38
+ * - the two listings never expand usersets, and `listPrincipalIds`
38
39
  * answers DIRECT principals only — a listing is an inventory of who
39
40
  * was named, never a resolution of who could reach.
40
41
  * - contextual policies are evaluated as if held, and nothing is written.
@@ -74,7 +75,7 @@ export interface AuthorizationQueryEngine {
74
75
  ): Promise<boolean>;
75
76
  /**
76
77
  * The ids of `resourceKind` resources on which `principal` holds
77
- * `relation` (ListAuthorizedResourceIdsInput's field order). Wildcards
78
+ * `relation` (ListAuthorizedResourceIdsInput's field order). Usersets
78
79
  * are not expanded.
79
80
  */
80
81
  listResourceIds(
@@ -86,7 +87,7 @@ export interface AuthorizationQueryEngine {
86
87
  /**
87
88
  * The ids of `principalKind` principals holding `relation` on `resource`
88
89
  * (ListAuthorizedPrincipalIdsInput's field order). Direct principals
89
- * only; wildcards are not expanded.
90
+ * only; usersets are not expanded.
90
91
  */
91
92
  listPrincipalIds(
92
93
  resource: ApiResourceRef,
@@ -8,7 +8,7 @@
8
8
  * is yours). On a multi-tenant edition the same scans serve OTHER
9
9
  * tenants' rows, so the cloud baseline (the Java list handlers) narrows
10
10
  * every one of them to the caller's FGA-authorized id set (ListObjects
11
- * on can_view, public wildcards suppressed), with a second guest
11
+ * on can_view), with a second guest
12
12
  * cookie-label rule on the session and agent-execution lanes.
13
13
  *
14
14
  * This port is the ONE seam for that fork (DD-007 addendum 3: every
@@ -65,14 +65,12 @@ import type { CallerIdentity } from "./identity.js";
65
65
  * The visibility tuple shapes of the shared FGA model, named
66
66
  * edition-neutrally. The driver maps each to its tuple:
67
67
  * - org-viewer: <kind>:<id>#viewer@organization:<org>#viewer
68
- * - public-viewer: <kind>:<id>#viewer@identity_account:* (conditional)
69
68
  * - platform-viewer: <kind>:<id>#platform_viewer@identity_provider:<idp>#platform_user
70
69
  * (fans out per IdP the org owns — the driver's lookup, not OSS's)
70
+ * Every shape names a bounded set of readers; the model has no shape that
71
+ * reaches every account.
71
72
  */
72
- export type VisibilityTupleShape =
73
- | "org-viewer"
74
- | "public-viewer"
75
- | "platform-viewer";
73
+ export type VisibilityTupleShape = "org-viewer" | "platform-viewer";
76
74
 
77
75
  /**
78
76
  * One resolved structural link from the created resource to a parent
package/src/index.ts CHANGED
@@ -185,7 +185,6 @@ export {
185
185
  newModel,
186
186
  } from "./authorization/model/index.js";
187
187
  export type {
188
- CheckContext,
189
188
  ObjectRef,
190
189
  Person,
191
190
  Subject,
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Pins `supportsVisibility` and `supportedVisibilityLevels`
3
+ * (pipeline/apiresource-meta.ts), the one predicate behind the
4
+ * ValidateVisibility doors on every create and updateVisibility chain.
5
+ *
6
+ * The retired public level is the reason this file exists: no kind config
7
+ * declares it any more, so the predicate cannot read a flag for it and must
8
+ * answer from its own text. The first test walks EVERY kind that has
9
+ * `kind_meta` and asserts the level is refused; the door is one function,
10
+ * and this is the pin that it holds for kinds nobody thought to try. The
11
+ * rest of the file pins the levels that remain against the proto's
12
+ * declarations for the kinds whose configs differ (a blueprint, an
13
+ * instance, an org-only kind, a kind with no config), and the copy fragment
14
+ * both editions build the INVALID_ARGUMENT sentence from.
15
+ */
16
+ import { describe, expect, it } from "vitest";
17
+
18
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
19
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
20
+
21
+ import {
22
+ getKindMeta,
23
+ supportedVisibilityLevels,
24
+ supportsVisibility,
25
+ } from "../apiresource-meta.js";
26
+
27
+ const V = ApiResourceVisibility;
28
+
29
+ /** Every enum member that carries kind_meta (the unknown kind does not). */
30
+ const KINDS_WITH_META: ApiResourceKind[] = Object.values(ApiResourceKind)
31
+ .filter((value): value is ApiResourceKind => typeof value === "number")
32
+ .filter((kind) => {
33
+ try {
34
+ getKindMeta(kind);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ });
40
+
41
+ describe("supportsVisibility", () => {
42
+ it("refuses the retired public level for every kind", () => {
43
+ expect(KINDS_WITH_META.length).toBeGreaterThan(20);
44
+ for (const kind of KINDS_WITH_META) {
45
+ expect(supportsVisibility(kind, V.visibility_public)).toBe(false);
46
+ }
47
+ });
48
+
49
+ it("private and unspecified are supported by every kind", () => {
50
+ for (const kind of KINDS_WITH_META) {
51
+ expect(supportsVisibility(kind, V.visibility_private)).toBe(true);
52
+ expect(
53
+ supportsVisibility(kind, V.api_resource_visibility_unspecified),
54
+ ).toBe(true);
55
+ }
56
+ });
57
+
58
+ it("a blueprint kind holds org and platform", () => {
59
+ expect(supportsVisibility(ApiResourceKind.agent, V.visibility_org)).toBe(
60
+ true,
61
+ );
62
+ expect(
63
+ supportsVisibility(ApiResourceKind.agent, V.visibility_platform),
64
+ ).toBe(true);
65
+ });
66
+
67
+ it("an instance kind holds org but never platform (tenant isolation)", () => {
68
+ expect(
69
+ supportsVisibility(ApiResourceKind.agent_instance, V.visibility_org),
70
+ ).toBe(true);
71
+ expect(
72
+ supportsVisibility(ApiResourceKind.agent_instance, V.visibility_platform),
73
+ ).toBe(false);
74
+ });
75
+
76
+ it("an org-only kind holds org alone (environment: secrets never cross the org)", () => {
77
+ expect(
78
+ supportsVisibility(ApiResourceKind.environment, V.visibility_org),
79
+ ).toBe(true);
80
+ expect(
81
+ supportsVisibility(ApiResourceKind.environment, V.visibility_platform),
82
+ ).toBe(false);
83
+ });
84
+
85
+ it("a kind with no visibility config is private-only", () => {
86
+ expect(supportsVisibility(ApiResourceKind.session, V.visibility_org)).toBe(
87
+ false,
88
+ );
89
+ expect(
90
+ supportsVisibility(ApiResourceKind.session, V.visibility_platform),
91
+ ).toBe(false);
92
+ });
93
+ });
94
+
95
+ describe("supportedVisibilityLevels (the copy fragment)", () => {
96
+ it("names the levels that remain, private first, never the retired one", () => {
97
+ expect(supportedVisibilityLevels(ApiResourceKind.agent)).toBe(
98
+ "visibility_private, visibility_org, visibility_platform",
99
+ );
100
+ expect(supportedVisibilityLevels(ApiResourceKind.agent_instance)).toBe(
101
+ "visibility_private, visibility_org",
102
+ );
103
+ expect(supportedVisibilityLevels(ApiResourceKind.session)).toBe(
104
+ "visibility_private",
105
+ );
106
+ for (const kind of KINDS_WITH_META) {
107
+ expect(supportedVisibilityLevels(kind)).not.toContain(
108
+ "visibility_public",
109
+ );
110
+ }
111
+ });
112
+ });
@@ -266,9 +266,18 @@ function tierRank(tier: ResourceTier): number {
266
266
  }
267
267
 
268
268
  /**
269
- * Go SupportsVisibility: PRIVATE and UNSPECIFIED are always supported (no
270
- * visibility grant); every other level requires the matching supports_*
271
- * flag. Kinds with no VisibilityConfig are private-only.
269
+ * Whether a kind may hold a visibility level — the one predicate behind the
270
+ * ValidateVisibility doors on every create and updateVisibility chain.
271
+ *
272
+ * PRIVATE and UNSPECIFIED are always supported (no visibility grant is
273
+ * written for them); ORG and PLATFORM require the matching supports_* flag
274
+ * on the kind's VisibilityConfig, so kinds with no config are private-only.
275
+ * PUBLIC is refused for every kind: the level is retired (it made a row
276
+ * readable to every account on the server), no config may declare it, and
277
+ * the answer is fixed here rather than read from a flag that no longer
278
+ * exists. Every level is named so a level added to the enum must be taught
279
+ * here before any row can hold it; the closing arm is unreachable by wire
280
+ * input, because both doors validate the field `defined_only` first.
272
281
  */
273
282
  export function supportsVisibility(
274
283
  kind: ApiResourceKind,
@@ -276,14 +285,19 @@ export function supportsVisibility(
276
285
  ): boolean {
277
286
  const config = getKindMeta(kind).authorization?.visibility;
278
287
  switch (visibility) {
279
- case ApiResourceVisibility.visibility_public:
280
- return config?.supportsPublic === true;
288
+ case ApiResourceVisibility.api_resource_visibility_unspecified:
289
+ case ApiResourceVisibility.visibility_private:
290
+ return true;
281
291
  case ApiResourceVisibility.visibility_org:
282
292
  return config?.supportsOrg === true;
283
293
  case ApiResourceVisibility.visibility_platform:
284
294
  return config?.supportsPlatform === true;
285
- default:
286
- return true;
295
+ case ApiResourceVisibility.visibility_public:
296
+ return false;
297
+ default: {
298
+ const exhaustive: never = visibility;
299
+ throw new Error(`unknown visibility level: ${String(exhaustive)}`);
300
+ }
287
301
  }
288
302
  }
289
303
 
@@ -298,9 +312,6 @@ export function supportedVisibilityLevels(kind: ApiResourceKind): string {
298
312
  if (config?.supportsOrg === true) {
299
313
  levels += ", visibility_org";
300
314
  }
301
- if (config?.supportsPublic === true) {
302
- levels += ", visibility_public";
303
- }
304
315
  if (config?.supportsPlatform === true) {
305
316
  levels += ", visibility_platform";
306
317
  }
@@ -41,15 +41,10 @@ const caller: CallerIdentity = {
41
41
  const V = ApiResourceVisibility;
42
42
 
43
43
  describe("visibilityShapesFor (the reconciler's level→shape policy)", () => {
44
- it("agent (blueprint with org floor): org / public / platform expansions", () => {
44
+ it("agent (blueprint with org floor): org / platform expansions", () => {
45
45
  expect([
46
46
  ...visibilityShapesFor(ApiResourceKind.agent, V.visibility_org),
47
47
  ]).toEqual(["org-viewer"]);
48
- expect(
49
- [
50
- ...visibilityShapesFor(ApiResourceKind.agent, V.visibility_public),
51
- ].sort(),
52
- ).toEqual(["org-viewer", "public-viewer"]);
53
48
  expect(
54
49
  [
55
50
  ...visibilityShapesFor(ApiResourceKind.agent, V.visibility_platform),
@@ -60,25 +55,27 @@ describe("visibilityShapesFor (the reconciler's level→shape policy)", () => {
60
55
  ]).toEqual([]);
61
56
  });
62
57
 
63
- it("workflow_instance (no org floor): public expands to the wildcard shape alone", () => {
64
- expect([
65
- ...visibilityShapesFor(
66
- ApiResourceKind.workflow_instance,
67
- V.visibility_public,
68
- ),
69
- ]).toEqual(["public-viewer"]);
58
+ it("the retired public level expands to nothing for every kind — no wildcard shape exists", () => {
59
+ for (const kind of [
60
+ ApiResourceKind.agent,
61
+ ApiResourceKind.workflow_instance,
62
+ ApiResourceKind.plugin,
63
+ ApiResourceKind.session,
64
+ ]) {
65
+ expect([...visibilityShapesFor(kind, V.visibility_public)]).toEqual([]);
66
+ }
70
67
  });
71
68
 
72
69
  it("session (no visibility config): every level yields nothing, silently", () => {
73
70
  expect([
74
- ...visibilityShapesFor(ApiResourceKind.session, V.visibility_public),
71
+ ...visibilityShapesFor(ApiResourceKind.session, V.visibility_org),
75
72
  ]).toEqual([]);
76
73
  expect([
77
- ...visibilityShapesFor(ApiResourceKind.session, V.visibility_org),
74
+ ...visibilityShapesFor(ApiResourceKind.session, V.visibility_platform),
78
75
  ]).toEqual([]);
79
76
  });
80
77
 
81
- it("agent_instance supports org+public but never platform (tenant isolation)", () => {
78
+ it("agent_instance supports org but never platform (tenant isolation)", () => {
82
79
  expect([
83
80
  ...visibilityShapesFor(
84
81
  ApiResourceKind.agent_instance,
@@ -101,15 +98,15 @@ describe("diffVisibilityShapes (the reconciler's transition matrix, re-pinned)",
101
98
  expect(diff.shapesToDelete).toEqual([]);
102
99
  });
103
100
 
104
- it("unspecified→public creates public + the org floor", () => {
101
+ it("unspecified→platform creates the platform shape + the org floor", () => {
105
102
  const diff = diffVisibilityShapes(
106
103
  agent,
107
104
  V.api_resource_visibility_unspecified,
108
- V.visibility_public,
105
+ V.visibility_platform,
109
106
  );
110
107
  expect([...diff.shapesToCreate].sort()).toEqual([
111
108
  "org-viewer",
112
- "public-viewer",
109
+ "platform-viewer",
113
110
  ]);
114
111
  expect(diff.shapesToDelete).toEqual([]);
115
112
  });
@@ -124,23 +121,13 @@ describe("diffVisibilityShapes (the reconciler's transition matrix, re-pinned)",
124
121
  expect(diff.shapesToDelete).toEqual(["org-viewer"]);
125
122
  });
126
123
 
127
- it("public→org deletes ONLY the wildcard — the shared org shape stays untouched", () => {
124
+ it("platform→org deletes ONLY the platform shape — the shared org floor stays untouched", () => {
128
125
  const diff = diffVisibilityShapes(
129
126
  agent,
130
- V.visibility_public,
127
+ V.visibility_platform,
131
128
  V.visibility_org,
132
129
  );
133
130
  expect(diff.shapesToCreate).toEqual([]);
134
- expect(diff.shapesToDelete).toEqual(["public-viewer"]);
135
- });
136
-
137
- it("platform→public swaps the family shape and never touches the org floor", () => {
138
- const diff = diffVisibilityShapes(
139
- agent,
140
- V.visibility_platform,
141
- V.visibility_public,
142
- );
143
- expect(diff.shapesToCreate).toEqual(["public-viewer"]);
144
131
  expect(diff.shapesToDelete).toEqual(["platform-viewer"]);
145
132
  });
146
133