@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
@@ -33,8 +33,10 @@ import { AgentCommandController } from "@stigmer/protos/ai/stigmer/agentic/agent
33
33
  import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
34
34
  import { AgentShareCommandController } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/command_pb";
35
35
  import { AgentShareQueryController } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/query_pb";
36
+ import { AgentShareSchema } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/api_pb";
36
37
  import type { AgentShare } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/api_pb";
37
38
  import { AgentShareAudience } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/spec_pb";
39
+ import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
38
40
  import { SkillSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb";
39
41
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
40
42
  import { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
@@ -47,11 +49,15 @@ import { createLogger } from "../../../boot/logger.js";
47
49
  import type { Store } from "../../../store/interface.js";
48
50
  import {
49
51
  ORG_REQUIRED_FOR_LOOKUP_MESSAGE,
50
- crossOrgAudienceMessage,
52
+ sameOrgInvariantMessage,
51
53
  } from "../constants.js";
52
54
  import { findShareByOrgAndSlug, sharingLinkTokenAllowed } from "../steps.js";
53
55
 
54
- const silentLogger = createLogger({ level: "error", pretty: false, write: () => {} });
56
+ const silentLogger = createLogger({
57
+ level: "error",
58
+ pretty: false,
59
+ write: () => {},
60
+ });
55
61
 
56
62
  const API_VERSION = "agentic.stigmer.ai/v1";
57
63
 
@@ -68,7 +74,10 @@ let dir: string;
68
74
  beforeAll(async () => {
69
75
  dir = mkdtempSync(path.join(tmpdir(), "agentshare-domain-test-"));
70
76
  vi.stubEnv("STIGMER_ENCRYPTION_KEY", Buffer.alloc(32, 7).toString("base64"));
71
- vi.stubEnv("STIGMER_RUNNER_TOKEN_KEY", Buffer.alloc(32, 8).toString("base64"));
77
+ vi.stubEnv(
78
+ "STIGMER_RUNNER_TOKEN_KEY",
79
+ Buffer.alloc(32, 8).toString("base64"),
80
+ );
72
81
  server = await composeServer({
73
82
  config: loadConfig({
74
83
  STIGMER_MODEL_REGISTRY_REFRESH: "off",
@@ -118,13 +127,6 @@ async function createTestAgent(name: string, org: string): Promise<Agent> {
118
127
  }
119
128
 
120
129
  /** Flips the agent marketplace-public through the real pipeline (D1). */
121
- async function makeAgentPublic(agent: Agent): Promise<void> {
122
- await agents.updateVisibility({
123
- resourceId: agent.metadata!.id,
124
- visibility: ApiResourceVisibility.visibility_public,
125
- });
126
- }
127
-
128
130
  /**
129
131
  * Writes a skill fixture directly to the store — the established
130
132
  * cross-domain fixture pattern (the skill domain is not ported yet, and
@@ -142,7 +144,12 @@ async function saveSkill(
142
144
  kind: "Skill",
143
145
  metadata: { id, name: slug, slug, org, visibility },
144
146
  });
145
- await server.store.saveResource(ApiResourceKind.skill, id, SkillSchema, skill);
147
+ await server.store.saveResource(
148
+ ApiResourceKind.skill,
149
+ id,
150
+ SkillSchema,
151
+ skill,
152
+ );
146
153
  }
147
154
 
148
155
  /** Minimal canonical share: no slug, no name — both default from the agent. */
@@ -178,7 +185,10 @@ describe("agentshare create", () => {
178
185
  const ORG = "create-test-org";
179
186
 
180
187
  it("canonical share defaults slug and name from the agent; ash_ id; ref normalized", async () => {
181
- const agent = await createTestAgent(uniqueName("Canonical Default Agent"), ORG);
188
+ const agent = await createTestAgent(
189
+ uniqueName("Canonical Default Agent"),
190
+ ORG,
191
+ );
182
192
  const share = await shares.create(shareFor(agent, true));
183
193
 
184
194
  expect(share.metadata?.slug).toBe(agent.metadata!.slug);
@@ -202,13 +212,20 @@ describe("agentshare create", () => {
202
212
  expect(err.rawMessage).toBe("Agent not found: no-such-agent");
203
213
  });
204
214
 
205
- it("cross-org ref to a nonexistent agent is NOT_FOUND — no cross-org fallback", async () => {
206
- const agent = await createTestAgent(uniqueName("Cross Org Ghost Agent"), ORG);
215
+ it("an agent_ref naming another organization is FAILED_PRECONDITION with the same-organization copy, before any lookup", async () => {
216
+ const agent = await createTestAgent(
217
+ uniqueName("Cross Org Ghost Agent"),
218
+ ORG,
219
+ );
207
220
  const share = shareFor(agent, true);
208
- share.spec.agentRef = { ...share.spec.agentRef, org: "some-other-org" } as never;
221
+ share.spec.agentRef = {
222
+ ...share.spec.agentRef,
223
+ org: "some-other-org",
224
+ } as never;
209
225
 
210
226
  const err = await grpcError(() => shares.create(share));
211
- expect(err.code).toBe(Code.NotFound);
227
+ expect(err.code).toBe(Code.FailedPrecondition);
228
+ expect(err.rawMessage).toBe(sameOrgInvariantMessage("some-other-org"));
212
229
  });
213
230
 
214
231
  it("stamps the agent-id pin on the created share", async () => {
@@ -236,7 +253,10 @@ describe("agentshare create", () => {
236
253
  });
237
254
 
238
255
  it("second canonical share for the same agent is ALREADY_EXISTS", async () => {
239
- const agent = await createTestAgent(uniqueName("Duplicate Share Agent"), ORG);
256
+ const agent = await createTestAgent(
257
+ uniqueName("Duplicate Share Agent"),
258
+ ORG,
259
+ );
240
260
  await shares.create(shareFor(agent, true));
241
261
 
242
262
  const err = await grpcError(() => shares.create(shareFor(agent, true)));
@@ -248,7 +268,8 @@ describe("agentshare create", () => {
248
268
  await shares.create(shareFor(agent, true));
249
269
 
250
270
  const named = shareFor(agent, true);
251
- (named.metadata as Record<string, string>).name = uniqueName("Docs Site Channel");
271
+ (named.metadata as Record<string, string>).name =
272
+ uniqueName("Docs Site Channel");
252
273
  const second = await shares.create(named);
253
274
  expect(second.metadata?.slug).not.toBe(agent.metadata!.slug);
254
275
  });
@@ -264,7 +285,10 @@ describe("launch-gate config", () => {
264
285
  const ORG = "launchgate-test-org";
265
286
 
266
287
  it("allowed_origins and messages persist and round-trip", async () => {
267
- const agent = await createTestAgent(uniqueName("Launch Gate Config Agent"), ORG);
288
+ const agent = await createTestAgent(
289
+ uniqueName("Launch Gate Config Agent"),
290
+ ORG,
291
+ );
268
292
  const created = await shares.create({
269
293
  ...shareFor(agent, true),
270
294
  spec: {
@@ -289,7 +313,10 @@ describe("launch-gate config", () => {
289
313
  });
290
314
 
291
315
  it("malformed origins are INVALID_ARGUMENT", async () => {
292
- const agent = await createTestAgent(uniqueName("Origin Validation Agent"), ORG);
316
+ const agent = await createTestAgent(
317
+ uniqueName("Origin Validation Agent"),
318
+ ORG,
319
+ );
293
320
  for (const origin of [
294
321
  "docs.example.com", // missing scheme
295
322
  "https://example.com/path", // path not allowed
@@ -308,7 +335,10 @@ describe("launch-gate config", () => {
308
335
  });
309
336
 
310
337
  it("overlong custom message is INVALID_ARGUMENT", async () => {
311
- const agent = await createTestAgent(uniqueName("Message Length Agent"), ORG);
338
+ const agent = await createTestAgent(
339
+ uniqueName("Message Length Agent"),
340
+ ORG,
341
+ );
312
342
  const err = await grpcError(() =>
313
343
  shares.create({
314
344
  ...shareFor(agent, true),
@@ -322,7 +352,27 @@ describe("launch-gate config", () => {
322
352
  });
323
353
 
324
354
  it("environment_refs on an org-audience share is INVALID_ARGUMENT; public persists", async () => {
325
- const agent = await createTestAgent(uniqueName("Env Refs Audience Agent"), ORG);
355
+ const agent = await createTestAgent(
356
+ uniqueName("Env Refs Audience Agent"),
357
+ ORG,
358
+ );
359
+ // The environment must exist when the share is written (the reference rule).
360
+ await server.store.saveResource(
361
+ ApiResourceKind.environment,
362
+ "env_shared_credentials",
363
+ EnvironmentSchema,
364
+ create(EnvironmentSchema, {
365
+ apiVersion: "agentic.stigmer.ai/v1",
366
+ kind: "Environment",
367
+ metadata: {
368
+ id: "env_shared_credentials",
369
+ name: "shared-credentials",
370
+ slug: "shared-credentials",
371
+ org: ORG,
372
+ visibility: ApiResourceVisibility.visibility_org,
373
+ },
374
+ }),
375
+ );
326
376
  const envRef = {
327
377
  kind: ApiResourceKind.environment,
328
378
  org: ORG,
@@ -378,8 +428,14 @@ describe("agentshare update", () => {
378
428
  });
379
429
 
380
430
  it("agent_ref is immutable — FAILED_PRECONDITION with the pinned copy", async () => {
381
- const agentA = await createTestAgent(uniqueName("Immutable Ref Agent A"), ORG);
382
- const agentB = await createTestAgent(uniqueName("Immutable Ref Agent B"), ORG);
431
+ const agentA = await createTestAgent(
432
+ uniqueName("Immutable Ref Agent A"),
433
+ ORG,
434
+ );
435
+ const agentB = await createTestAgent(
436
+ uniqueName("Immutable Ref Agent B"),
437
+ ORG,
438
+ );
383
439
  const created = await shares.create(shareFor(agentA, true));
384
440
 
385
441
  created.spec!.agentRef = create(ApiResourceReferenceSchema, {
@@ -397,146 +453,83 @@ describe("agentshare update", () => {
397
453
  // Cross-org contract (Go TestAgentShareController_CrossOrg, decision 013).
398
454
  // ---------------------------------------------------------------------------
399
455
 
400
- describe("cross-org shares (decision 013)", () => {
456
+ describe("the same-organization invariant", () => {
401
457
  const PROVIDER = "provider-org";
402
458
  const CONSUMER = "consumer-org";
403
459
 
404
- function crossOrgShare(agent: Agent, enabled: boolean) {
460
+ function shareInAnotherOrg(agent: Agent, enabled: boolean) {
405
461
  const share = shareFor(agent, enabled);
406
462
  share.metadata.org = CONSUMER;
407
- share.spec.agentRef = { ...share.spec.agentRef, org: agent.metadata!.org } as never;
463
+ share.spec.agentRef = {
464
+ ...share.spec.agentRef,
465
+ org: agent.metadata!.org,
466
+ } as never;
408
467
  return share;
409
468
  }
410
469
 
411
- it("public agent is shareable cross-org: pin stamped, slug defaulted, profile resolves", async () => {
412
- const agent = await createTestAgent(uniqueName("Public Provider Agent"), PROVIDER);
413
- await makeAgentPublic(agent);
414
-
415
- const created = await shares.create(crossOrgShare(agent, true));
416
- expect(created.metadata?.org).toBe(CONSUMER);
417
- expect(created.spec?.agentRef?.org).toBe(PROVIDER);
418
- expect(created.metadata?.slug).toBe(agent.metadata!.slug);
419
- expect(created.status?.agentId).toBe(agent.metadata!.id);
420
-
421
- const profile = await query.getSharedProfile({
422
- org: CONSUMER,
423
- slug: created.metadata!.slug,
424
- });
425
- expect(profile.org).toBe(CONSUMER);
426
- expect(profile.name).toBe(agent.metadata!.name);
427
- });
428
-
429
- it("non-public agent is NOT_FOUND, indistinguishable from absence", async () => {
430
- const agent = await createTestAgent(uniqueName("Private Provider Agent"), PROVIDER);
431
-
432
- const err = await grpcError(() => shares.create(crossOrgShare(agent, true)));
433
- expect(err.code).toBe(Code.NotFound);
434
-
435
- // The refusal must match a genuinely missing agent — no existence
436
- // probe for private slugs (the T09 indistinguishability contract).
437
- const ghost = crossOrgShare(agent, true);
438
- ghost.spec.agentRef = { ...ghost.spec.agentRef, org: "empty-provider-org" } as never;
470
+ it("another organization's agent cannot be shared — the same sentence whether the agent exists or not, at any level", async () => {
471
+ const existing = await createTestAgent(
472
+ uniqueName("Provider Agent"),
473
+ PROVIDER,
474
+ );
475
+ const err = await grpcError(() =>
476
+ shares.create(shareInAnotherOrg(existing, true)),
477
+ );
478
+ expect(err.code).toBe(Code.FailedPrecondition);
479
+ expect(err.rawMessage).toBe(sameOrgInvariantMessage(PROVIDER));
480
+
481
+ const ghost = shareFor(existing, true);
482
+ ghost.metadata.org = CONSUMER;
483
+ ghost.spec.agentRef = {
484
+ ...ghost.spec.agentRef,
485
+ org: PROVIDER,
486
+ slug: "no-such-agent",
487
+ } as never;
439
488
  const ghostErr = await grpcError(() => shares.create(ghost));
440
- expect(err.rawMessage).toBe(ghostErr.rawMessage);
441
- });
442
-
443
- it("org audience is refused cross-org, on create and on update", async () => {
444
- const agent = await createTestAgent(uniqueName("Audience Rule Agent"), PROVIDER);
445
- await makeAgentPublic(agent);
446
-
447
- const orgAudience = crossOrgShare(agent, true);
448
- (orgAudience.spec as Record<string, unknown>).audience = AgentShareAudience.org;
449
- const createErr = await grpcError(() => shares.create(orgAudience));
450
- expect(createErr.code).toBe(Code.FailedPrecondition);
451
- expect(createErr.rawMessage).toBe(crossOrgAudienceMessage(PROVIDER));
452
-
453
- const created = await shares.create(crossOrgShare(agent, true));
454
- created.spec!.audience = AgentShareAudience.org;
455
- const updateErr = await grpcError(() => shares.update(created));
456
- expect(updateErr.code).toBe(Code.FailedPrecondition);
457
- expect(updateErr.rawMessage).toBe(crossOrgAudienceMessage(PROVIDER));
489
+ expect(ghostErr.code).toBe(Code.FailedPrecondition);
490
+ expect(ghostErr.rawMessage).toBe(sameOrgInvariantMessage(PROVIDER));
458
491
  });
459
492
 
460
- it("non-public dependencies block creation, naming every blocker sorted", async () => {
461
- await saveSkill(
462
- "skl_private_dep",
463
- PROVIDER,
464
- "private-skill",
465
- ApiResourceVisibility.visibility_private,
466
- );
467
- await saveSkill(
468
- "skl_public_dep",
493
+ it("a stored share whose agent is in another organization fails the profile closed, like a dangling reference", async () => {
494
+ const agent = await createTestAgent(
495
+ uniqueName("Legacy Provider Agent"),
469
496
  PROVIDER,
470
- "public-skill",
471
- ApiResourceVisibility.visibility_public,
472
497
  );
473
-
474
- const agent = await agents.create({
498
+ // A row from before the invariant, written straight to the store.
499
+ const legacy = create(AgentShareSchema, {
475
500
  apiVersion: API_VERSION,
476
- kind: "Agent",
477
- metadata: { name: uniqueName("Tooling Agent"), org: PROVIDER },
501
+ kind: "AgentShare",
502
+ metadata: {
503
+ id: "ash_legacy_cross_org",
504
+ name: "legacy-cross-org",
505
+ slug: "legacy-cross-org",
506
+ org: CONSUMER,
507
+ },
478
508
  spec: {
479
- instructions: "You are a tool-using cross-org test agent.",
480
- skillRefs: [
481
- { kind: ApiResourceKind.skill, slug: "private-skill" },
482
- { kind: ApiResourceKind.skill, slug: "public-skill" },
483
- ],
509
+ agentRef: {
510
+ kind: ApiResourceKind.agent,
511
+ org: PROVIDER,
512
+ slug: agent.metadata!.slug,
513
+ },
514
+ enabled: true,
515
+ audience: AgentShareAudience.public,
484
516
  },
517
+ status: { agentId: agent.metadata!.id },
485
518
  });
486
- await makeAgentPublic(agent);
487
-
488
- const err = await grpcError(() => shares.create(crossOrgShare(agent, true)));
489
- expect(err.code).toBe(Code.FailedPrecondition);
490
- expect(err.rawMessage).toContain("provider-org/private-skill");
491
- expect(err.rawMessage).not.toContain("public-skill");
492
-
493
- // Same-org sharing of the same agent stays unaffected — the sweep is
494
- // a cross-org rule only.
495
- await shares.create(shareFor(agent, true));
496
- });
497
-
498
- it("visibility flip fails the profile closed", async () => {
499
- const agent = await createTestAgent(uniqueName("Revocable Agent"), PROVIDER);
500
- await makeAgentPublic(agent);
501
- const created = await shares.create(crossOrgShare(agent, true));
502
-
503
- const ref = { org: CONSUMER, slug: created.metadata!.slug };
504
- await query.getSharedProfile(ref);
505
-
506
- await agents.updateVisibility({
507
- resourceId: agent.metadata!.id,
508
- visibility: ApiResourceVisibility.visibility_private,
509
- });
519
+ await server.store.saveResource(
520
+ ApiResourceKind.agent_share,
521
+ legacy.metadata!.id,
522
+ AgentShareSchema,
523
+ legacy,
524
+ );
510
525
 
511
- const err = await grpcError(() => query.getSharedProfile(ref));
526
+ const err = await grpcError(() =>
527
+ query.getSharedProfile({ org: CONSUMER, slug: "legacy-cross-org" }),
528
+ );
512
529
  expect(err.code).toBe(Code.NotFound);
513
- });
514
-
515
- it("agent delete leaves the cross-org share failing closed; recreate never rebinds", async () => {
516
- const name = uniqueName("Ephemeral Provider Agent");
517
- const agent = await createTestAgent(name, PROVIDER);
518
- await makeAgentPublic(agent);
519
- const created = await shares.create(crossOrgShare(agent, true));
520
- const ref = { org: CONSUMER, slug: created.metadata!.slug };
521
-
522
- await agents.delete({ value: agent.metadata!.id });
523
-
524
- // The consumer org's share survives the provider's delete (cross-org
525
- // shares are NOT cascaded — not the provider's resource to destroy)
526
- // but fails closed.
527
- const survived = await query.get({ value: created.metadata!.id });
528
- expect(survived.status?.agentId).toBe(agent.metadata!.id);
529
- const danglingErr = await grpcError(() => query.getSharedProfile(ref));
530
- expect(danglingErr.code).toBe(Code.NotFound);
531
-
532
- // A DIFFERENT public agent later claims the same org/slug: the pin
533
- // must keep the old share dark.
534
- const recreated = await createTestAgent(name, PROVIDER);
535
- await makeAgentPublic(recreated);
536
- expect(recreated.metadata!.slug).toBe(agent.metadata!.slug);
537
-
538
- const rebindErr = await grpcError(() => query.getSharedProfile(ref));
539
- expect(rebindErr.code).toBe(Code.NotFound);
530
+ // The row itself is still readable by id — nothing is deleted.
531
+ const row = await query.get({ value: legacy.metadata!.id });
532
+ expect(row.status?.agentId).toBe(agent.metadata!.id);
540
533
  });
541
534
  });
542
535
 
@@ -548,13 +541,18 @@ describe("getSharedProfile (anonymous lane)", () => {
548
541
  const ORG = "profile-test-org";
549
542
 
550
543
  it("uniform NotFound across no-share / disabled / deleted; enabled resolves trimmed", async () => {
551
- const agent = await createTestAgent(uniqueName("Shared Profile Agent"), ORG);
544
+ const agent = await createTestAgent(
545
+ uniqueName("Shared Profile Agent"),
546
+ ORG,
547
+ );
552
548
  const ref = { org: ORG, slug: agent.metadata!.slug };
553
549
 
554
550
  // No share yet.
555
551
  const noShareErr = await grpcError(() => query.getSharedProfile(ref));
556
552
  expect(noShareErr.code).toBe(Code.NotFound);
557
- expect(noShareErr.rawMessage).toBe(`Agent not found: ${agent.metadata!.slug}`);
553
+ expect(noShareErr.rawMessage).toBe(
554
+ `Agent not found: ${agent.metadata!.slug}`,
555
+ );
558
556
 
559
557
  // Enabled share resolves to the trimmed profile — display fields from
560
558
  // the AGENT, URL identity from the SHARE, never the full Agent.
@@ -609,7 +607,10 @@ describe("audience and the member resolution lane", () => {
609
607
  const ORG = "audience-test-org";
610
608
 
611
609
  it("audience persists and round-trips", async () => {
612
- const agent = await createTestAgent(uniqueName("Audience Round Trip Agent"), ORG);
610
+ const agent = await createTestAgent(
611
+ uniqueName("Audience Round Trip Agent"),
612
+ ORG,
613
+ );
613
614
  const created = await shares.create({
614
615
  ...shareFor(agent, true),
615
616
  spec: { ...shareFor(agent, true).spec, audience: AgentShareAudience.org },
@@ -621,10 +622,15 @@ describe("audience and the member resolution lane", () => {
621
622
  });
622
623
 
623
624
  it("member path resolves shares in either audience (OSS: membership always holds)", async () => {
624
- const agent = await createTestAgent(uniqueName("Member Resolution Agent"), ORG);
625
+ const agent = await createTestAgent(
626
+ uniqueName("Member Resolution Agent"),
627
+ ORG,
628
+ );
625
629
  const ref = { org: ORG, slug: agent.metadata!.slug };
626
630
 
627
- const noShareErr = await grpcError(() => query.getSharedProfileForMember(ref));
631
+ const noShareErr = await grpcError(() =>
632
+ query.getSharedProfileForMember(ref),
633
+ );
628
634
  expect(noShareErr.code).toBe(Code.NotFound);
629
635
 
630
636
  await shares.create({
@@ -637,7 +643,10 @@ describe("audience and the member resolution lane", () => {
637
643
  });
638
644
 
639
645
  it("the ANONYMOUS path collapses an org-audience share to the uniform NotFound (C2 close-out — the proto's audience contract)", async () => {
640
- const agent = await createTestAgent(uniqueName("Org Audience Anon Agent"), ORG);
646
+ const agent = await createTestAgent(
647
+ uniqueName("Org Audience Anon Agent"),
648
+ ORG,
649
+ );
641
650
  await shares.create({
642
651
  ...shareFor(agent, true),
643
652
  spec: { ...shareFor(agent, true).spec, audience: AgentShareAudience.org },
@@ -706,7 +715,9 @@ describe("rotateShareLink", () => {
706
715
 
707
716
  // Capture the no-share NOT_FOUND before creating — the locked-link
708
717
  // refusal must be byte-identical to it.
709
- const noShareErr = await grpcError(() => query.getSharedProfile(request("")));
718
+ const noShareErr = await grpcError(() =>
719
+ query.getSharedProfile(request("")),
720
+ );
710
721
 
711
722
  const share = await shares.create(shareFor(agent, true));
712
723
 
@@ -714,23 +725,31 @@ describe("rotateShareLink", () => {
714
725
  await query.getSharedProfile(request("stray-token"));
715
726
 
716
727
  // Rotation locks the link: 27 url-safe chars, status-resident.
717
- const rotated = await shares.rotateShareLink({ resourceId: share.metadata!.id });
728
+ const rotated = await shares.rotateShareLink({
729
+ resourceId: share.metadata!.id,
730
+ });
718
731
  const firstToken = rotated.status?.shareLinkToken ?? "";
719
732
  expect(firstToken).toHaveLength(27);
720
733
  expect(firstToken).toMatch(/^[A-Za-z0-9_-]+$/);
721
734
 
722
- const lockedErr = await grpcError(() => query.getSharedProfile(request("")));
735
+ const lockedErr = await grpcError(() =>
736
+ query.getSharedProfile(request("")),
737
+ );
723
738
  expect(lockedErr.code).toBe(Code.NotFound);
724
739
  expect(lockedErr.rawMessage).toBe(noShareErr.rawMessage);
725
740
 
726
741
  await query.getSharedProfile(request(firstToken));
727
742
 
728
743
  // Re-rotation kills the previous token.
729
- const reRotated = await shares.rotateShareLink({ resourceId: share.metadata!.id });
744
+ const reRotated = await shares.rotateShareLink({
745
+ resourceId: share.metadata!.id,
746
+ });
730
747
  const secondToken = reRotated.status?.shareLinkToken ?? "";
731
748
  expect(secondToken).not.toBe(firstToken);
732
749
 
733
- const deadErr = await grpcError(() => query.getSharedProfile(request(firstToken)));
750
+ const deadErr = await grpcError(() =>
751
+ query.getSharedProfile(request(firstToken)),
752
+ );
734
753
  expect(deadErr.code).toBe(Code.NotFound);
735
754
  await query.getSharedProfile(request(secondToken));
736
755
 
@@ -757,7 +776,10 @@ describe("rotateShareLink", () => {
757
776
  });
758
777
 
759
778
  it("member path stays open for org-audience shares even when a token exists", async () => {
760
- const agent = await createTestAgent(uniqueName("Org Audience Rotate Agent"), ORG);
779
+ const agent = await createTestAgent(
780
+ uniqueName("Org Audience Rotate Agent"),
781
+ ORG,
782
+ );
761
783
  const created = await shares.create({
762
784
  ...shareFor(agent, true),
763
785
  spec: { ...shareFor(agent, true).spec, audience: AgentShareAudience.org },
@@ -778,11 +800,15 @@ describe("rotateShareLink", () => {
778
800
 
779
801
  describe("getByAgent", () => {
780
802
  it("finds the canonical share and a renamed share", async () => {
781
- const agent = await createTestAgent(uniqueName("Get By Agent Agent"), "gba-org");
803
+ const agent = await createTestAgent(
804
+ uniqueName("Get By Agent Agent"),
805
+ "gba-org",
806
+ );
782
807
  const canonical = await shares.create(shareFor(agent, true));
783
808
 
784
809
  const renamed = shareFor(agent, true);
785
- (renamed.metadata as Record<string, string>).name = uniqueName("Renamed Channel");
810
+ (renamed.metadata as Record<string, string>).name =
811
+ uniqueName("Renamed Channel");
786
812
  const second = await shares.create(renamed);
787
813
 
788
814
  const list = await query.getByAgent({ agentId: agent.metadata!.id });
@@ -802,25 +828,21 @@ describe("getByAgent", () => {
802
828
  uniqueName("Org Scoped List Agent"),
803
829
  "gba-provider-org",
804
830
  );
805
- await makeAgentPublic(agent);
806
831
  await shares.create(shareFor(agent, true));
807
-
808
- const crossOrg = shareFor(agent, true);
809
- crossOrg.metadata.org = "gba-consumer-org";
810
- crossOrg.spec.agentRef = {
811
- ...crossOrg.spec.agentRef,
812
- org: agent.metadata!.org,
813
- } as never;
814
- await shares.create(crossOrg);
832
+ const named = shareFor(agent, true);
833
+ named.metadata = { ...named.metadata, name: "second link" } as never;
834
+ await shares.create(named);
815
835
 
816
836
  const cases: Array<{ org: string; want: number; wantOrg: string }> = [
817
837
  { org: "", want: 2, wantOrg: "" },
818
- { org: "gba-provider-org", want: 1, wantOrg: "gba-provider-org" },
819
- { org: "gba-consumer-org", want: 1, wantOrg: "gba-consumer-org" },
838
+ { org: "gba-provider-org", want: 2, wantOrg: "gba-provider-org" },
820
839
  { org: "gba-bystander-org", want: 0, wantOrg: "" },
821
840
  ];
822
841
  for (const tt of cases) {
823
- const list = await query.getByAgent({ agentId: agent.metadata!.id, org: tt.org });
842
+ const list = await query.getByAgent({
843
+ agentId: agent.metadata!.id,
844
+ org: tt.org,
845
+ });
824
846
  expect(list.totalCount, `org ${tt.org}`).toBe(tt.want);
825
847
  for (const item of list.items) {
826
848
  if (tt.wantOrg !== "") {
@@ -848,7 +870,9 @@ describe("apply semantics", () => {
848
870
  expect(created.metadata?.id).not.toBe("");
849
871
 
850
872
  const again = shareFor(agent, true);
851
- (again.spec as Record<string, unknown>).allowedOrigins = ["https://docs.example.com"];
873
+ (again.spec as Record<string, unknown>).allowedOrigins = [
874
+ "https://docs.example.com",
875
+ ];
852
876
  const updated = await shares.apply(again);
853
877
  expect(updated.metadata?.id).toBe(created.metadata!.id);
854
878
  expect(updated.spec?.allowedOrigins).toHaveLength(1);