@stigmer/server 3.23.0 → 3.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authorization/authorizer.d.ts +4 -8
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +5 -9
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +10 -25
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +7 -8
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +4 -20
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/list-read-scope.d.ts.map +1 -1
- package/dist/authorization/list-read-scope.js +5 -9
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/agent.d.ts.map +1 -1
- package/dist/authorization/model/agent.js +4 -5
- package/dist/authorization/model/agent.js.map +1 -1
- package/dist/authorization/model/agent_instance.d.ts.map +1 -1
- package/dist/authorization/model/agent_instance.js +2 -2
- package/dist/authorization/model/agent_instance.js.map +1 -1
- package/dist/authorization/model/mcp_server.d.ts.map +1 -1
- package/dist/authorization/model/mcp_server.js +2 -2
- package/dist/authorization/model/mcp_server.js.map +1 -1
- package/dist/authorization/model/plugin.d.ts.map +1 -1
- package/dist/authorization/model/plugin.js +3 -4
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +8 -10
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +0 -3
- package/dist/authorization/model/rewrite.js.map +1 -1
- package/dist/authorization/model/skill.d.ts.map +1 -1
- package/dist/authorization/model/skill.js +2 -2
- package/dist/authorization/model/skill.js.map +1 -1
- package/dist/authorization/model/workflow.d.ts.map +1 -1
- package/dist/authorization/model/workflow.js +2 -2
- package/dist/authorization/model/workflow.js.map +1 -1
- package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
- package/dist/authorization/model/workflow_instance.js +2 -2
- package/dist/authorization/model/workflow_instance.js.map +1 -1
- package/dist/authorization/organization-directory.d.ts.map +1 -1
- package/dist/authorization/organization-directory.js +2 -3
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/store-test-kit.d.ts +0 -6
- package/dist/authorization/store-test-kit.d.ts.map +1 -1
- package/dist/authorization/store-test-kit.js +16 -62
- package/dist/authorization/store-test-kit.js.map +1 -1
- package/dist/authorization/tuples.d.ts +13 -32
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +3 -11
- package/dist/authorization/tuples.js.map +1 -1
- package/dist/domain/agent/controller.d.ts.map +1 -1
- package/dist/domain/agent/controller.js +4 -4
- package/dist/domain/agent/controller.js.map +1 -1
- package/dist/domain/agentchannel/controller.d.ts.map +1 -1
- package/dist/domain/agentchannel/controller.js +3 -1
- package/dist/domain/agentchannel/controller.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +3 -1
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentinstance/controller.d.ts.map +1 -1
- package/dist/domain/agentinstance/controller.js +3 -4
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentshare/constants.d.ts +6 -11
- package/dist/domain/agentshare/constants.d.ts.map +1 -1
- package/dist/domain/agentshare/constants.js +8 -16
- package/dist/domain/agentshare/constants.js.map +1 -1
- package/dist/domain/agentshare/controller.d.ts.map +1 -1
- package/dist/domain/agentshare/controller.js +3 -1
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +18 -25
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +41 -142
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/environment/controller.d.ts.map +1 -1
- package/dist/domain/environment/controller.js +2 -3
- package/dist/domain/environment/controller.js.map +1 -1
- package/dist/domain/executioncontext/controller.d.ts.map +1 -1
- package/dist/domain/executioncontext/controller.js +2 -1
- package/dist/domain/executioncontext/controller.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +3 -4
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/plugin/controller.d.ts.map +1 -1
- package/dist/domain/plugin/controller.js +1 -3
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +7 -5
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +8 -14
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/schedule/controller.d.ts.map +1 -1
- package/dist/domain/schedule/controller.js +3 -1
- package/dist/domain/schedule/controller.js.map +1 -1
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +3 -2
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/skill/controller.d.ts.map +1 -1
- package/dist/domain/skill/controller.js +0 -2
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/workflow/agent-call-references.d.ts +81 -0
- package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
- package/dist/domain/workflow/agent-call-references.js +117 -0
- package/dist/domain/workflow/agent-call-references.js.map +1 -0
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +14 -6
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +3 -3
- package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
- package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +4 -2
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +3 -4
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/extensions/authorization-queries.d.ts +6 -5
- package/dist/extensions/authorization-queries.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +1 -1
- package/dist/extensions/resource-authorization.d.ts +3 -2
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-labels.d.ts +10 -9
- package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
- package/dist/pipeline/apiresource-labels.js +10 -9
- package/dist/pipeline/apiresource-labels.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +12 -3
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +21 -10
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +9 -11
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +204 -20
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +356 -43
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/query/search/controller.js +0 -1
- package/dist/query/search/controller.js.map +1 -1
- package/dist/query/search/criteria.d.ts +1 -5
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +3 -13
- package/dist/query/search/criteria.js.map +1 -1
- package/dist/query/search/handler.d.ts +5 -4
- package/dist/query/search/handler.d.ts.map +1 -1
- package/dist/query/search/handler.js +7 -6
- package/dist/query/search/handler.js.map +1 -1
- package/dist/query/search/query-store.d.ts.map +1 -1
- package/dist/query/search/query-store.js +0 -2
- package/dist/query/search/query-store.js.map +1 -1
- package/dist/store/interface.d.ts +3 -6
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +3 -1
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +35 -1
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +4 -9
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/public-visibility-retired.d.ts +67 -0
- package/dist/store/public-visibility-retired.d.ts.map +1 -0
- package/dist/store/public-visibility-retired.js +55 -0
- package/dist/store/public-visibility-retired.js.map +1 -0
- package/dist/store/sqlite/migrations.d.ts +3 -1
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +36 -1
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +4 -8
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/README.md +24 -26
- package/src/authorization/__tests__/authorizer.test.ts +10 -9
- package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
- package/src/authorization/__tests__/evaluator.test.ts +14 -73
- package/src/authorization/__tests__/facts.test.ts +2 -2
- package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +19 -27
- package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
- package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
- package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +1 -15
- package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
- package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
- package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
- package/src/authorization/__tests__/list-read-scope.test.ts +18 -17
- package/src/authorization/__tests__/store-tests.test.ts +2 -6
- package/src/authorization/__tests__/tuples.test.ts +12 -11
- package/src/authorization/authorizer.ts +4 -9
- package/src/authorization/derived-tuples.ts +14 -31
- package/src/authorization/evaluator.ts +8 -44
- package/src/authorization/list-read-scope.ts +5 -11
- package/src/authorization/model/agent.ts +2 -5
- package/src/authorization/model/agent_instance.ts +0 -2
- package/src/authorization/model/mcp_server.ts +0 -2
- package/src/authorization/model/plugin.ts +1 -4
- package/src/authorization/model/rewrite.ts +8 -13
- package/src/authorization/model/skill.ts +0 -2
- package/src/authorization/model/workflow.ts +0 -2
- package/src/authorization/model/workflow_instance.ts +0 -2
- package/src/authorization/organization-directory.ts +1 -3
- package/src/authorization/store-test-kit.ts +16 -75
- package/src/authorization/tuples.ts +16 -39
- package/src/domain/agent/__tests__/agent.test.ts +11 -4
- package/src/domain/agent/controller.ts +7 -7
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
- package/src/domain/agentchannel/controller.ts +6 -1
- package/src/domain/agentexecution/controller.ts +6 -1
- package/src/domain/agentinstance/controller.ts +6 -12
- package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +16 -31
- package/src/domain/agentshare/constants.ts +14 -26
- package/src/domain/agentshare/controller.ts +6 -1
- package/src/domain/agentshare/steps.ts +41 -194
- package/src/domain/environment/controller.ts +5 -8
- package/src/domain/executioncontext/controller.ts +5 -1
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
- package/src/domain/mcpserver/controller.ts +6 -12
- package/src/domain/plugin/controller.ts +1 -8
- package/src/domain/plugin/push.ts +8 -16
- package/src/domain/schedule/controller.ts +6 -1
- package/src/domain/session/controller.ts +12 -4
- package/src/domain/skill/controller.ts +0 -7
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +373 -0
- package/src/domain/workflow/agent-call-references.ts +204 -0
- package/src/domain/workflow/controller.ts +28 -10
- package/src/domain/workflow/registry/data/task-kind-registry.json +3 -3
- package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
- package/src/domain/workflowexecution/controller.ts +7 -2
- package/src/domain/workflowinstance/controller.ts +6 -12
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
- package/src/extensions/__tests__/extension-composition.test.ts +4 -4
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +9 -6
- package/src/extensions/authorization-queries.ts +6 -5
- package/src/extensions/list-read-scope.ts +1 -1
- package/src/extensions/resource-authorization.ts +3 -5
- package/src/index.ts +0 -1
- package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
- package/src/pipeline/apiresource-labels.ts +10 -9
- package/src/pipeline/apiresource-meta.ts +21 -10
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
- package/src/pipeline/steps/__tests__/references.test.ts +737 -0
- package/src/pipeline/steps/authorization-tuples.ts +9 -11
- package/src/pipeline/steps/references.ts +622 -70
- package/src/query/search/__tests__/criteria.test.ts +0 -4
- package/src/query/search/__tests__/query-store.test.ts +11 -40
- package/src/query/search/controller.ts +0 -1
- package/src/query/search/criteria.ts +0 -14
- package/src/query/search/handler.ts +6 -7
- package/src/query/search/query-store.ts +0 -2
- package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
- package/src/store/__tests__/store-contract.ts +6 -51
- package/src/store/interface.ts +3 -6
- package/src/store/postgres/__tests__/migrations.test.ts +187 -6
- package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
- package/src/store/postgres/migrations.ts +48 -1
- package/src/store/postgres/store.ts +4 -11
- package/src/store/public-visibility-retired.ts +119 -0
- package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
- package/src/store/sqlite/migrations.ts +45 -1
- package/src/store/sqlite/store.ts +4 -10
- package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
- package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
- package/dist/pipeline/steps/visibility-gates.js +0 -139
- package/dist/pipeline/steps/visibility-gates.js.map +0 -1
- package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
- package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
- package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
- package/src/pipeline/steps/visibility-gates.ts +0 -174
|
@@ -25,8 +25,6 @@ function criteria(overrides?: {
|
|
|
25
25
|
kinds?: ApiResourceKind[];
|
|
26
26
|
query?: string;
|
|
27
27
|
org?: string;
|
|
28
|
-
excludePublic?: boolean;
|
|
29
|
-
crossOrgPublic?: boolean;
|
|
30
28
|
pageNumber?: number;
|
|
31
29
|
pageSize?: number;
|
|
32
30
|
}): SearchCriteria {
|
|
@@ -34,8 +32,6 @@ function criteria(overrides?: {
|
|
|
34
32
|
overrides?.kinds ?? [],
|
|
35
33
|
overrides?.query ?? "",
|
|
36
34
|
overrides?.org ?? "",
|
|
37
|
-
overrides?.excludePublic ?? false,
|
|
38
|
-
overrides?.crossOrgPublic ?? false,
|
|
39
35
|
overrides?.pageNumber ?? 1,
|
|
40
36
|
overrides?.pageSize ?? 20,
|
|
41
37
|
);
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* sqlite_search_query_store_test.go (the kind-parsing table) and
|
|
4
4
|
* sqlite_search_query_store_query_test.go (the end-to-end
|
|
5
5
|
* write→index→query pins: session list mode, the #440 emptiness arm, the
|
|
6
|
-
* #439 newly-searchable-kind arm), plus the scope
|
|
7
|
-
*
|
|
6
|
+
* #439 newly-searchable-kind arm), plus the scope filter (org strict:
|
|
7
|
+
* visibility never widens or narrows it) and RebuildIndex —
|
|
8
8
|
* including the DD-D proof: rebuilding over an ADOPTED Go-created
|
|
9
9
|
* database re-indexes the rows it already holds. The escaping and
|
|
10
10
|
* score-normalization tables moved with their code into the sqlite
|
|
@@ -118,8 +118,6 @@ function criteria(overrides: {
|
|
|
118
118
|
kinds?: ApiResourceKind[];
|
|
119
119
|
query?: string;
|
|
120
120
|
org?: string;
|
|
121
|
-
excludePublic?: boolean;
|
|
122
|
-
crossOrgPublic?: boolean;
|
|
123
121
|
pageNumber?: number;
|
|
124
122
|
pageSize?: number;
|
|
125
123
|
}): SearchCriteria {
|
|
@@ -127,8 +125,6 @@ function criteria(overrides: {
|
|
|
127
125
|
overrides.kinds ?? [],
|
|
128
126
|
overrides.query ?? "",
|
|
129
127
|
overrides.org ?? "",
|
|
130
|
-
overrides.excludePublic ?? false,
|
|
131
|
-
overrides.crossOrgPublic ?? false,
|
|
132
128
|
overrides.pageNumber ?? 1,
|
|
133
129
|
overrides.pageSize ?? 20,
|
|
134
130
|
);
|
|
@@ -249,7 +245,7 @@ describe("write→index→query pins (Go's query_test.go)", () => {
|
|
|
249
245
|
});
|
|
250
246
|
});
|
|
251
247
|
|
|
252
|
-
describe("scope
|
|
248
|
+
describe("scope filter (the org filter is strict)", () => {
|
|
253
249
|
beforeEach(async () => {
|
|
254
250
|
await seed(
|
|
255
251
|
ApiResourceKind.session,
|
|
@@ -269,10 +265,10 @@ describe("scope-filter matrix (Go buildScopeFilter)", () => {
|
|
|
269
265
|
);
|
|
270
266
|
await seed(
|
|
271
267
|
ApiResourceKind.session,
|
|
272
|
-
"ses-b-
|
|
268
|
+
"ses-b-org",
|
|
273
269
|
SessionSchema,
|
|
274
|
-
newSession("ses-b-
|
|
275
|
-
visibility: ApiResourceVisibility.
|
|
270
|
+
newSession("ses-b-org", "org-b", "b org", {
|
|
271
|
+
visibility: ApiResourceVisibility.visibility_org,
|
|
276
272
|
createdAtSeconds: 1_700_000_001,
|
|
277
273
|
}),
|
|
278
274
|
);
|
|
@@ -288,38 +284,15 @@ describe("scope-filter matrix (Go buildScopeFilter)", () => {
|
|
|
288
284
|
expect(ids(result)).toEqual(["ses-a-private"]);
|
|
289
285
|
});
|
|
290
286
|
|
|
291
|
-
it("
|
|
287
|
+
it("another org's rows stay out of an org filter whatever their visibility", async () => {
|
|
292
288
|
const result = await queryStore.search(
|
|
293
|
-
criteria({
|
|
294
|
-
kinds: [ApiResourceKind.session],
|
|
295
|
-
org: "org-a",
|
|
296
|
-
crossOrgPublic: true,
|
|
297
|
-
}),
|
|
289
|
+
criteria({ kinds: [ApiResourceKind.session], org: "org-b" }),
|
|
298
290
|
);
|
|
299
291
|
expect(new Set(ids(result))).toEqual(
|
|
300
|
-
new Set(["ses-
|
|
292
|
+
new Set(["ses-b-private", "ses-b-org"]),
|
|
301
293
|
);
|
|
302
294
|
});
|
|
303
295
|
|
|
304
|
-
it("excludePublic subtracts public rows from any scope", async () => {
|
|
305
|
-
const unscoped = await queryStore.search(
|
|
306
|
-
criteria({ kinds: [ApiResourceKind.session], excludePublic: true }),
|
|
307
|
-
);
|
|
308
|
-
expect(new Set(ids(unscoped))).toEqual(
|
|
309
|
-
new Set(["ses-a-private", "ses-b-private"]),
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
const widened = await queryStore.search(
|
|
313
|
-
criteria({
|
|
314
|
-
kinds: [ApiResourceKind.session],
|
|
315
|
-
org: "org-a",
|
|
316
|
-
crossOrgPublic: true,
|
|
317
|
-
excludePublic: true,
|
|
318
|
-
}),
|
|
319
|
-
);
|
|
320
|
-
expect(ids(widened)).toEqual(["ses-a-private"]);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
296
|
it("no org filter returns every row, newest first (list mode ordering)", async () => {
|
|
324
297
|
const result = await queryStore.search(
|
|
325
298
|
criteria({ kinds: [ApiResourceKind.session] }),
|
|
@@ -327,7 +300,7 @@ describe("scope-filter matrix (Go buildScopeFilter)", () => {
|
|
|
327
300
|
expect(ids(result)).toEqual([
|
|
328
301
|
"ses-a-private",
|
|
329
302
|
"ses-b-private",
|
|
330
|
-
"ses-b-
|
|
303
|
+
"ses-b-org",
|
|
331
304
|
]);
|
|
332
305
|
});
|
|
333
306
|
});
|
|
@@ -474,9 +447,7 @@ describe("rebuildIndex", () => {
|
|
|
474
447
|
"skl_1",
|
|
475
448
|
SkillSchema,
|
|
476
449
|
);
|
|
477
|
-
expect(toBinary(SkillSchema, raw)).toEqual(
|
|
478
|
-
toBinary(SkillSchema, skill),
|
|
479
|
-
);
|
|
450
|
+
expect(toBinary(SkillSchema, raw)).toEqual(toBinary(SkillSchema, skill));
|
|
480
451
|
} finally {
|
|
481
452
|
await adopted.close();
|
|
482
453
|
fixture.cleanup();
|
|
@@ -98,8 +98,6 @@ export class SearchCriteria {
|
|
|
98
98
|
private readonly requestedKinds: readonly ApiResourceKind[],
|
|
99
99
|
private readonly queryText: string,
|
|
100
100
|
private readonly orgFilterValue: string,
|
|
101
|
-
private readonly excludePublicFlag: boolean,
|
|
102
|
-
private readonly crossOrgPublicFlag: boolean,
|
|
103
101
|
private readonly pageNumberValue: number,
|
|
104
102
|
private readonly pageSizeValue: number,
|
|
105
103
|
) {}
|
|
@@ -121,8 +119,6 @@ export class SearchCriteria {
|
|
|
121
119
|
kinds: readonly ApiResourceKind[],
|
|
122
120
|
query: string,
|
|
123
121
|
orgFilter: string,
|
|
124
|
-
excludePublic: boolean,
|
|
125
|
-
crossOrgPublic: boolean,
|
|
126
122
|
pageNumber: number,
|
|
127
123
|
pageSize: number,
|
|
128
124
|
): SearchCriteria {
|
|
@@ -136,8 +132,6 @@ export class SearchCriteria {
|
|
|
136
132
|
[...kinds],
|
|
137
133
|
normalizedQuery,
|
|
138
134
|
goTrimSpace(orgFilter),
|
|
139
|
-
excludePublic,
|
|
140
|
-
crossOrgPublic,
|
|
141
135
|
pageNumber < 1 ? 1 : pageNumber,
|
|
142
136
|
pageSize < 1 ? DEFAULT_PAGE_SIZE : Math.min(pageSize, MAX_PAGE_SIZE),
|
|
143
137
|
);
|
|
@@ -156,14 +150,6 @@ export class SearchCriteria {
|
|
|
156
150
|
return this.orgFilterValue;
|
|
157
151
|
}
|
|
158
152
|
|
|
159
|
-
excludePublic(): boolean {
|
|
160
|
-
return this.excludePublicFlag;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
crossOrgPublic(): boolean {
|
|
164
|
-
return this.crossOrgPublicFlag;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
153
|
pageNumber(): number {
|
|
168
154
|
return this.pageNumberValue;
|
|
169
155
|
}
|
|
@@ -52,10 +52,11 @@ export class SearchHandler {
|
|
|
52
52
|
* mapping string-matches them (#478 sanitization contract).
|
|
53
53
|
*
|
|
54
54
|
* With a composed ListReadScope (20260830.01, census lane 21) the
|
|
55
|
-
* store read carries a per-effective-kind authorized-id map —
|
|
56
|
-
* SearchHandler's QueryAuthorizedIds step
|
|
57
|
-
*
|
|
58
|
-
*
|
|
55
|
+
* store read ALWAYS carries a per-effective-kind authorized-id map —
|
|
56
|
+
* the Java SearchHandler's QueryAuthorizedIds step. There is no request
|
|
57
|
+
* shape that skips the scope: the one that did (the retired public
|
|
58
|
+
* level's cross-organization discovery, which the engine filtered by
|
|
59
|
+
* visibility with FGA never consulted) left with the level. A scope
|
|
59
60
|
* failure propagates — the controller's default arm answers the
|
|
60
61
|
* sanitized Internal, never a silently unscoped result.
|
|
61
62
|
*/
|
|
@@ -74,8 +75,6 @@ export class SearchHandler {
|
|
|
74
75
|
request.kinds,
|
|
75
76
|
request.query,
|
|
76
77
|
request.org,
|
|
77
|
-
request.excludePublic,
|
|
78
|
-
request.crossOrgPublic,
|
|
79
78
|
request.page?.num ?? 0,
|
|
80
79
|
request.page?.size ?? 0,
|
|
81
80
|
);
|
|
@@ -87,7 +86,7 @@ export class SearchHandler {
|
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
let authorizedIdsByKind: Map<string, ReadonlySet<string>> | undefined;
|
|
90
|
-
if (this.listReadScope !== undefined
|
|
89
|
+
if (this.listReadScope !== undefined) {
|
|
91
90
|
authorizedIdsByKind = new Map<string, ReadonlySet<string>>();
|
|
92
91
|
for (const kind of criteria.effectiveKinds()) {
|
|
93
92
|
authorizedIdsByKind.set(
|
|
@@ -81,8 +81,6 @@ export class SqliteSearchQueryStore implements SearchQueryStore {
|
|
|
81
81
|
// is already goTrimSpace'd). Engine syntax is the driver's job.
|
|
82
82
|
terms: criteria.hasQuery() ? goFields(criteria.query()) : undefined,
|
|
83
83
|
orgFilter: criteria.orgFilter(),
|
|
84
|
-
crossOrgPublic: criteria.crossOrgPublic(),
|
|
85
|
-
excludePublic: criteria.excludePublic(),
|
|
86
84
|
limit: criteria.pageSize(),
|
|
87
85
|
offset: criteria.offset(),
|
|
88
86
|
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the driver-neutral half of the public-visibility data migration
|
|
3
|
+
* (public-visibility-retired.ts): the frozen kind table is exactly the
|
|
4
|
+
* seven kinds that could hold the level, a public row is moved to org with
|
|
5
|
+
* every other byte it carried intact (an unknown field a newer release
|
|
6
|
+
* wrote included), a row at any other level is left alone (undefined, so
|
|
7
|
+
* the driver never rewrites bytes it did not change; a row with no
|
|
8
|
+
* metadata holds no level and is one of these), and bytes that do not
|
|
9
|
+
* decode are a thrown fault, never a skipped row.
|
|
10
|
+
* The drivers' replay tests pin the SQL around this function.
|
|
11
|
+
*/
|
|
12
|
+
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
13
|
+
import { BinaryWriter, WireType } from "@bufbuild/protobuf/wire";
|
|
14
|
+
import { describe, expect, it } from "vitest";
|
|
15
|
+
|
|
16
|
+
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
17
|
+
import { SkillSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb";
|
|
18
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
LEVEL_AFTER_RETIREMENT,
|
|
22
|
+
PUBLIC_ROW_KINDS_AT_RETIREMENT,
|
|
23
|
+
movePublicRowToOrg,
|
|
24
|
+
} from "../public-visibility-retired.js";
|
|
25
|
+
|
|
26
|
+
const AGENT = PUBLIC_ROW_KINDS_AT_RETIREMENT[0]!;
|
|
27
|
+
|
|
28
|
+
function agentBytes(visibility: ApiResourceVisibility): Uint8Array {
|
|
29
|
+
return toBinary(
|
|
30
|
+
AgentSchema,
|
|
31
|
+
create(AgentSchema, {
|
|
32
|
+
apiVersion: "agentic.stigmer.ai/v1",
|
|
33
|
+
kind: "Agent",
|
|
34
|
+
metadata: {
|
|
35
|
+
id: "agt_1",
|
|
36
|
+
name: "Public Agent",
|
|
37
|
+
slug: "public-agent",
|
|
38
|
+
org: "acme",
|
|
39
|
+
visibility,
|
|
40
|
+
labels: { team: "platform" },
|
|
41
|
+
},
|
|
42
|
+
spec: { instructions: "a conformant instruction body" },
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** `bytes` followed by one field this schema does not declare (a newer release's). */
|
|
48
|
+
function withUnknownField(bytes: Uint8Array): Uint8Array {
|
|
49
|
+
const trailer = new BinaryWriter()
|
|
50
|
+
.tag(4093, WireType.LengthDelimited)
|
|
51
|
+
.string("written by a release this one does not know")
|
|
52
|
+
.finish();
|
|
53
|
+
const joined = new Uint8Array(bytes.length + trailer.length);
|
|
54
|
+
joined.set(bytes, 0);
|
|
55
|
+
joined.set(trailer, bytes.length);
|
|
56
|
+
return joined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("the frozen kind table", () => {
|
|
60
|
+
it("is exactly the seven kinds that could hold the level, by the enum names the drivers store", () => {
|
|
61
|
+
expect(PUBLIC_ROW_KINDS_AT_RETIREMENT.map((entry) => entry.kind)).toEqual([
|
|
62
|
+
"agent",
|
|
63
|
+
"skill",
|
|
64
|
+
"mcp_server",
|
|
65
|
+
"agent_instance",
|
|
66
|
+
"workflow",
|
|
67
|
+
"workflow_instance",
|
|
68
|
+
"plugin",
|
|
69
|
+
]);
|
|
70
|
+
expect(AGENT.schema).toBe(AgentSchema);
|
|
71
|
+
expect(PUBLIC_ROW_KINDS_AT_RETIREMENT[1]!.schema).toBe(SkillSchema);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("movePublicRowToOrg", () => {
|
|
76
|
+
it("moves a public row to org and changes nothing else", () => {
|
|
77
|
+
const moved = movePublicRowToOrg(
|
|
78
|
+
AGENT,
|
|
79
|
+
agentBytes(ApiResourceVisibility.visibility_public),
|
|
80
|
+
);
|
|
81
|
+
expect(moved).toBeDefined();
|
|
82
|
+
const after = fromBinary(AgentSchema, moved!);
|
|
83
|
+
expect(after.metadata?.visibility).toBe(LEVEL_AFTER_RETIREMENT);
|
|
84
|
+
expect(after.metadata?.visibility).toBe(
|
|
85
|
+
ApiResourceVisibility.visibility_org,
|
|
86
|
+
);
|
|
87
|
+
// Every other byte: the moved row re-encodes to exactly the bytes an
|
|
88
|
+
// org row with the same content encodes to.
|
|
89
|
+
expect(moved).toEqual(agentBytes(ApiResourceVisibility.visibility_org));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("keeps an unknown field a newer release wrote through the round trip", () => {
|
|
93
|
+
const moved = movePublicRowToOrg(
|
|
94
|
+
AGENT,
|
|
95
|
+
withUnknownField(agentBytes(ApiResourceVisibility.visibility_public)),
|
|
96
|
+
);
|
|
97
|
+
expect(moved).toEqual(
|
|
98
|
+
withUnknownField(agentBytes(ApiResourceVisibility.visibility_org)),
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("leaves a row at any other level alone", () => {
|
|
103
|
+
for (const level of [
|
|
104
|
+
ApiResourceVisibility.api_resource_visibility_unspecified,
|
|
105
|
+
ApiResourceVisibility.visibility_private,
|
|
106
|
+
ApiResourceVisibility.visibility_org,
|
|
107
|
+
ApiResourceVisibility.visibility_platform,
|
|
108
|
+
]) {
|
|
109
|
+
expect(movePublicRowToOrg(AGENT, agentBytes(level))).toBeUndefined();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("leaves a message with no metadata alone — it holds no level", () => {
|
|
114
|
+
expect(
|
|
115
|
+
movePublicRowToOrg(
|
|
116
|
+
AGENT,
|
|
117
|
+
toBinary(AgentSchema, create(AgentSchema, { kind: "Agent" })),
|
|
118
|
+
),
|
|
119
|
+
).toBeUndefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("refuses bytes that do not decode as the kind", () => {
|
|
123
|
+
expect(() =>
|
|
124
|
+
movePublicRowToOrg(AGENT, new Uint8Array([0xff, 0xff, 0xff])),
|
|
125
|
+
).toThrow();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -934,8 +934,6 @@ export function describeStoreContract(
|
|
|
934
934
|
kinds: ["agent"],
|
|
935
935
|
terms: ["kubernetes"],
|
|
936
936
|
orgFilter: "",
|
|
937
|
-
crossOrgPublic: false,
|
|
938
|
-
excludePublic: false,
|
|
939
937
|
limit: 20,
|
|
940
938
|
offset: 0,
|
|
941
939
|
});
|
|
@@ -959,8 +957,6 @@ export function describeStoreContract(
|
|
|
959
957
|
kinds: ["agent"],
|
|
960
958
|
terms: ["kuber"],
|
|
961
959
|
orgFilter: "",
|
|
962
|
-
crossOrgPublic: false,
|
|
963
|
-
excludePublic: false,
|
|
964
960
|
limit: 20,
|
|
965
961
|
offset: 0,
|
|
966
962
|
});
|
|
@@ -970,8 +966,6 @@ export function describeStoreContract(
|
|
|
970
966
|
kinds: ["agent"],
|
|
971
967
|
terms: ["kubernetes", "deployment"],
|
|
972
968
|
orgFilter: "",
|
|
973
|
-
crossOrgPublic: false,
|
|
974
|
-
excludePublic: false,
|
|
975
969
|
limit: 20,
|
|
976
970
|
offset: 0,
|
|
977
971
|
});
|
|
@@ -981,8 +975,6 @@ export function describeStoreContract(
|
|
|
981
975
|
kinds: ["agent"],
|
|
982
976
|
terms: ["kubernetes", "absent"],
|
|
983
977
|
orgFilter: "",
|
|
984
|
-
crossOrgPublic: false,
|
|
985
|
-
excludePublic: false,
|
|
986
978
|
limit: 20,
|
|
987
979
|
offset: 0,
|
|
988
980
|
});
|
|
@@ -1008,8 +1000,6 @@ export function describeStoreContract(
|
|
|
1008
1000
|
kinds: ["agent"],
|
|
1009
1001
|
terms: ["NEAR", "plain"],
|
|
1010
1002
|
orgFilter: "",
|
|
1011
|
-
crossOrgPublic: false,
|
|
1012
|
-
excludePublic: false,
|
|
1013
1003
|
limit: 20,
|
|
1014
1004
|
offset: 0,
|
|
1015
1005
|
});
|
|
@@ -1039,8 +1029,6 @@ export function describeStoreContract(
|
|
|
1039
1029
|
kinds: ["agent", "workflow"],
|
|
1040
1030
|
terms: ["scoped"],
|
|
1041
1031
|
orgFilter: "",
|
|
1042
|
-
crossOrgPublic: false,
|
|
1043
|
-
excludePublic: false,
|
|
1044
1032
|
authorizedIdsByKind: new Map([["agent", new Set(["agt-mine"])]]),
|
|
1045
1033
|
limit: 20,
|
|
1046
1034
|
offset: 0,
|
|
@@ -1056,8 +1044,6 @@ export function describeStoreContract(
|
|
|
1056
1044
|
kinds: ["agent", "workflow"],
|
|
1057
1045
|
terms: ["scoped"],
|
|
1058
1046
|
orgFilter: "",
|
|
1059
|
-
crossOrgPublic: false,
|
|
1060
|
-
excludePublic: false,
|
|
1061
1047
|
authorizedIdsByKind: new Map([
|
|
1062
1048
|
["agent", new Set<string>()],
|
|
1063
1049
|
["workflow", new Set(["wfl-any"])],
|
|
@@ -1072,8 +1058,6 @@ export function describeStoreContract(
|
|
|
1072
1058
|
kinds: ["agent", "workflow"],
|
|
1073
1059
|
terms: ["scoped"],
|
|
1074
1060
|
orgFilter: "",
|
|
1075
|
-
crossOrgPublic: false,
|
|
1076
|
-
excludePublic: false,
|
|
1077
1061
|
authorizedIdsByKind: new Map([
|
|
1078
1062
|
["agent", new Set<string>()],
|
|
1079
1063
|
["workflow", new Set<string>()],
|
|
@@ -1089,15 +1073,13 @@ export function describeStoreContract(
|
|
|
1089
1073
|
kinds: ["agent", "workflow"],
|
|
1090
1074
|
terms: ["scoped"],
|
|
1091
1075
|
orgFilter: "",
|
|
1092
|
-
crossOrgPublic: false,
|
|
1093
|
-
excludePublic: false,
|
|
1094
1076
|
limit: 20,
|
|
1095
1077
|
offset: 0,
|
|
1096
1078
|
});
|
|
1097
1079
|
expect(unscoped.totalCount).toBe(3);
|
|
1098
1080
|
});
|
|
1099
1081
|
|
|
1100
|
-
it("org scoping
|
|
1082
|
+
it("org scoping is strict: the visibility column never widens or narrows the org filter", async () => {
|
|
1101
1083
|
await fx.store.upsertSearchIndex(
|
|
1102
1084
|
ApiResourceKind.agent,
|
|
1103
1085
|
"agt-mine",
|
|
@@ -1105,11 +1087,11 @@ export function describeStoreContract(
|
|
|
1105
1087
|
);
|
|
1106
1088
|
await fx.store.upsertSearchIndex(
|
|
1107
1089
|
ApiResourceKind.agent,
|
|
1108
|
-
"agt-
|
|
1090
|
+
"agt-platform",
|
|
1109
1091
|
entry({
|
|
1110
1092
|
name: "searchable beta",
|
|
1111
1093
|
org: "globex",
|
|
1112
|
-
visibility: "
|
|
1094
|
+
visibility: "visibility_platform",
|
|
1113
1095
|
}),
|
|
1114
1096
|
);
|
|
1115
1097
|
await fx.store.upsertSearchIndex(
|
|
@@ -1122,37 +1104,20 @@ export function describeStoreContract(
|
|
|
1122
1104
|
kinds: ["agent"],
|
|
1123
1105
|
terms: ["searchable"],
|
|
1124
1106
|
orgFilter: "acme",
|
|
1125
|
-
crossOrgPublic: false,
|
|
1126
|
-
excludePublic: false,
|
|
1127
1107
|
limit: 20,
|
|
1128
1108
|
offset: 0,
|
|
1129
1109
|
});
|
|
1130
1110
|
expect(strict.hits.map((hit) => hit.resourceId)).toEqual(["agt-mine"]);
|
|
1131
1111
|
|
|
1132
|
-
const
|
|
1133
|
-
kinds: ["agent"],
|
|
1134
|
-
terms: ["searchable"],
|
|
1135
|
-
orgFilter: "acme",
|
|
1136
|
-
crossOrgPublic: true,
|
|
1137
|
-
excludePublic: false,
|
|
1138
|
-
limit: 20,
|
|
1139
|
-
offset: 0,
|
|
1140
|
-
});
|
|
1141
|
-
expect(new Set(widened.hits.map((hit) => hit.resourceId))).toEqual(
|
|
1142
|
-
new Set(["agt-mine", "agt-public"]),
|
|
1143
|
-
);
|
|
1144
|
-
|
|
1145
|
-
const noPublic = await fx.store.querySearchIndex({
|
|
1112
|
+
const unscoped = await fx.store.querySearchIndex({
|
|
1146
1113
|
kinds: ["agent"],
|
|
1147
1114
|
terms: ["searchable"],
|
|
1148
1115
|
orgFilter: "",
|
|
1149
|
-
crossOrgPublic: false,
|
|
1150
|
-
excludePublic: true,
|
|
1151
1116
|
limit: 20,
|
|
1152
1117
|
offset: 0,
|
|
1153
1118
|
});
|
|
1154
|
-
expect(new Set(
|
|
1155
|
-
new Set(["agt-mine", "agt-foreign"]),
|
|
1119
|
+
expect(new Set(unscoped.hits.map((hit) => hit.resourceId))).toEqual(
|
|
1120
|
+
new Set(["agt-mine", "agt-platform", "agt-foreign"]),
|
|
1156
1121
|
);
|
|
1157
1122
|
});
|
|
1158
1123
|
|
|
@@ -1172,8 +1137,6 @@ export function describeStoreContract(
|
|
|
1172
1137
|
kinds: ["agent"],
|
|
1173
1138
|
terms: undefined,
|
|
1174
1139
|
orgFilter: "",
|
|
1175
|
-
crossOrgPublic: false,
|
|
1176
|
-
excludePublic: false,
|
|
1177
1140
|
limit: 20,
|
|
1178
1141
|
offset: 0,
|
|
1179
1142
|
});
|
|
@@ -1203,8 +1166,6 @@ export function describeStoreContract(
|
|
|
1203
1166
|
kinds: ["organization"],
|
|
1204
1167
|
terms: ["original"],
|
|
1205
1168
|
orgFilter: "",
|
|
1206
|
-
crossOrgPublic: false,
|
|
1207
|
-
excludePublic: false,
|
|
1208
1169
|
limit: 20,
|
|
1209
1170
|
offset: 0,
|
|
1210
1171
|
});
|
|
@@ -1214,8 +1175,6 @@ export function describeStoreContract(
|
|
|
1214
1175
|
kinds: ["organization"],
|
|
1215
1176
|
terms: ["renamed"],
|
|
1216
1177
|
orgFilter: "",
|
|
1217
|
-
crossOrgPublic: false,
|
|
1218
|
-
excludePublic: false,
|
|
1219
1178
|
limit: 20,
|
|
1220
1179
|
offset: 0,
|
|
1221
1180
|
});
|
|
@@ -1226,8 +1185,6 @@ export function describeStoreContract(
|
|
|
1226
1185
|
kinds: ["organization"],
|
|
1227
1186
|
terms: undefined,
|
|
1228
1187
|
orgFilter: "",
|
|
1229
|
-
crossOrgPublic: false,
|
|
1230
|
-
excludePublic: false,
|
|
1231
1188
|
limit: 20,
|
|
1232
1189
|
offset: 0,
|
|
1233
1190
|
});
|
|
@@ -1243,8 +1200,6 @@ export function describeStoreContract(
|
|
|
1243
1200
|
kinds: ["organization"],
|
|
1244
1201
|
terms: undefined,
|
|
1245
1202
|
orgFilter: "",
|
|
1246
|
-
crossOrgPublic: false,
|
|
1247
|
-
excludePublic: false,
|
|
1248
1203
|
limit: 20,
|
|
1249
1204
|
offset: 0,
|
|
1250
1205
|
});
|
package/src/store/interface.ts
CHANGED
|
@@ -135,8 +135,9 @@ export interface SearchIndexEntry {
|
|
|
135
135
|
/** Owning org (metadata.org) — org-scoped filtering. */
|
|
136
136
|
readonly org: string;
|
|
137
137
|
/**
|
|
138
|
-
* Visibility enum NAME (e.g. "
|
|
139
|
-
*
|
|
138
|
+
* Visibility enum NAME (e.g. "visibility_org"), carried for display;
|
|
139
|
+
* scope filtering is by org and by the authorized-id map, never by
|
|
140
|
+
* this column.
|
|
140
141
|
*/
|
|
141
142
|
readonly visibility: string;
|
|
142
143
|
/** Unix seconds creation time — sorting in list mode (no query). */
|
|
@@ -166,10 +167,6 @@ export interface SearchIndexQuery {
|
|
|
166
167
|
readonly terms: readonly string[] | undefined;
|
|
167
168
|
/** Org scope; "" = no org filter. */
|
|
168
169
|
readonly orgFilter: string;
|
|
169
|
-
/** With orgFilter: also admit visibility_public rows from ANY org. */
|
|
170
|
-
readonly crossOrgPublic: boolean;
|
|
171
|
-
/** Independent subtraction: drop visibility_public rows from any scope. */
|
|
172
|
-
readonly excludePublic: boolean;
|
|
173
170
|
/**
|
|
174
171
|
* Optional per-kind authorized-id allowlist (kind NAME → ids) — the
|
|
175
172
|
* multi-tenant list-read scoping arm (20260830.01): a listed kind
|