@stigmer/server 3.22.1-dev.20260922095755 → 3.22.1

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 (268) hide show
  1. package/dist/authorization/authorizer.d.ts +8 -4
  2. package/dist/authorization/authorizer.d.ts.map +1 -1
  3. package/dist/authorization/authorizer.js +9 -5
  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 +25 -10
  7. package/dist/authorization/derived-tuples.js.map +1 -1
  8. package/dist/authorization/evaluator.d.ts +8 -7
  9. package/dist/authorization/evaluator.d.ts.map +1 -1
  10. package/dist/authorization/evaluator.js +20 -4
  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 +9 -5
  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 +5 -4
  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 +4 -3
  26. package/dist/authorization/model/plugin.js.map +1 -1
  27. package/dist/authorization/model/rewrite.d.ts +10 -8
  28. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  29. package/dist/authorization/model/rewrite.js +3 -0
  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 +3 -2
  42. package/dist/authorization/organization-directory.js.map +1 -1
  43. package/dist/authorization/store-test-kit.d.ts +6 -0
  44. package/dist/authorization/store-test-kit.d.ts.map +1 -1
  45. package/dist/authorization/store-test-kit.js +62 -16
  46. package/dist/authorization/store-test-kit.js.map +1 -1
  47. package/dist/authorization/tuples.d.ts +32 -13
  48. package/dist/authorization/tuples.d.ts.map +1 -1
  49. package/dist/authorization/tuples.js +11 -3
  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 +1 -3
  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 +1 -3
  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 +4 -3
  62. package/dist/domain/agentinstance/controller.js.map +1 -1
  63. package/dist/domain/agentshare/constants.d.ts +11 -6
  64. package/dist/domain/agentshare/constants.d.ts.map +1 -1
  65. package/dist/domain/agentshare/constants.js +16 -8
  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 +1 -3
  69. package/dist/domain/agentshare/controller.js.map +1 -1
  70. package/dist/domain/agentshare/steps.d.ts +25 -18
  71. package/dist/domain/agentshare/steps.d.ts.map +1 -1
  72. package/dist/domain/agentshare/steps.js +142 -41
  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 +3 -2
  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 +1 -2
  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 +4 -3
  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 +3 -1
  85. package/dist/domain/plugin/controller.js.map +1 -1
  86. package/dist/domain/plugin/push.d.ts +5 -7
  87. package/dist/domain/plugin/push.d.ts.map +1 -1
  88. package/dist/domain/plugin/push.js +14 -8
  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 +1 -3
  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 +2 -3
  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 +2 -0
  98. package/dist/domain/skill/controller.js.map +1 -1
  99. package/dist/domain/workflow/controller.d.ts.map +1 -1
  100. package/dist/domain/workflow/controller.js +6 -14
  101. package/dist/domain/workflow/controller.js.map +1 -1
  102. package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
  103. package/dist/domain/workflow/validation/task-config-constraints.d.ts +1 -10
  104. package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
  105. package/dist/domain/workflow/validation/task-config-constraints.js +2 -3
  106. package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
  107. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  108. package/dist/domain/workflowexecution/controller.js +2 -4
  109. package/dist/domain/workflowexecution/controller.js.map +1 -1
  110. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  111. package/dist/domain/workflowinstance/controller.js +4 -3
  112. package/dist/domain/workflowinstance/controller.js.map +1 -1
  113. package/dist/extensions/authorization-queries.d.ts +5 -6
  114. package/dist/extensions/authorization-queries.d.ts.map +1 -1
  115. package/dist/extensions/list-read-scope.d.ts +1 -1
  116. package/dist/extensions/resource-authorization.d.ts +2 -3
  117. package/dist/extensions/resource-authorization.d.ts.map +1 -1
  118. package/dist/index.d.ts +3 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +13 -0
  121. package/dist/index.js.map +1 -1
  122. package/dist/pipeline/apiresource-meta.d.ts +3 -12
  123. package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
  124. package/dist/pipeline/apiresource-meta.js +10 -21
  125. package/dist/pipeline/apiresource-meta.js.map +1 -1
  126. package/dist/pipeline/steps/authorization-tuples.d.ts +3 -9
  127. package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
  128. package/dist/pipeline/steps/authorization-tuples.js +11 -9
  129. package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
  130. package/dist/pipeline/steps/references.d.ts +20 -178
  131. package/dist/pipeline/steps/references.d.ts.map +1 -1
  132. package/dist/pipeline/steps/references.js +43 -344
  133. package/dist/pipeline/steps/references.js.map +1 -1
  134. package/dist/pipeline/steps/visibility-gates.d.ts +27 -0
  135. package/dist/pipeline/steps/visibility-gates.d.ts.map +1 -0
  136. package/dist/pipeline/steps/visibility-gates.js +139 -0
  137. package/dist/pipeline/steps/visibility-gates.js.map +1 -0
  138. package/dist/query/search/controller.js +1 -0
  139. package/dist/query/search/controller.js.map +1 -1
  140. package/dist/query/search/criteria.d.ts +5 -1
  141. package/dist/query/search/criteria.d.ts.map +1 -1
  142. package/dist/query/search/criteria.js +13 -3
  143. package/dist/query/search/criteria.js.map +1 -1
  144. package/dist/query/search/handler.d.ts +4 -5
  145. package/dist/query/search/handler.d.ts.map +1 -1
  146. package/dist/query/search/handler.js +6 -7
  147. package/dist/query/search/handler.js.map +1 -1
  148. package/dist/query/search/query-store.d.ts.map +1 -1
  149. package/dist/query/search/query-store.js +2 -0
  150. package/dist/query/search/query-store.js.map +1 -1
  151. package/dist/store/interface.d.ts +6 -3
  152. package/dist/store/interface.d.ts.map +1 -1
  153. package/dist/store/interface.js.map +1 -1
  154. package/dist/store/postgres/migrations.d.ts +1 -3
  155. package/dist/store/postgres/migrations.d.ts.map +1 -1
  156. package/dist/store/postgres/migrations.js +1 -35
  157. package/dist/store/postgres/migrations.js.map +1 -1
  158. package/dist/store/postgres/store.d.ts.map +1 -1
  159. package/dist/store/postgres/store.js +9 -4
  160. package/dist/store/postgres/store.js.map +1 -1
  161. package/dist/store/sqlite/migrations.d.ts +1 -3
  162. package/dist/store/sqlite/migrations.d.ts.map +1 -1
  163. package/dist/store/sqlite/migrations.js +1 -36
  164. package/dist/store/sqlite/migrations.js.map +1 -1
  165. package/dist/store/sqlite/store.d.ts.map +1 -1
  166. package/dist/store/sqlite/store.js +8 -4
  167. package/dist/store/sqlite/store.js.map +1 -1
  168. package/package.json +6 -6
  169. package/src/authorization/README.md +26 -24
  170. package/src/authorization/__tests__/authorizer.test.ts +9 -10
  171. package/src/authorization/__tests__/derived-tuples.test.ts +11 -4
  172. package/src/authorization/__tests__/evaluator.test.ts +73 -14
  173. package/src/authorization/__tests__/facts.test.ts +2 -2
  174. package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +185 -0
  175. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +98 -6
  176. package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +98 -0
  177. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +61 -4
  178. package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +143 -0
  179. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +21 -1
  180. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +25 -1
  181. package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +52 -0
  182. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +3 -1
  183. package/src/authorization/__tests__/list-read-scope.test.ts +17 -18
  184. package/src/authorization/__tests__/store-tests.test.ts +8 -1
  185. package/src/authorization/__tests__/tuples.test.ts +11 -12
  186. package/src/authorization/authorizer.ts +9 -4
  187. package/src/authorization/derived-tuples.ts +31 -14
  188. package/src/authorization/evaluator.ts +44 -8
  189. package/src/authorization/list-read-scope.ts +11 -5
  190. package/src/authorization/model/agent.ts +5 -2
  191. package/src/authorization/model/agent_instance.ts +2 -0
  192. package/src/authorization/model/mcp_server.ts +2 -0
  193. package/src/authorization/model/plugin.ts +4 -1
  194. package/src/authorization/model/rewrite.ts +13 -8
  195. package/src/authorization/model/skill.ts +2 -0
  196. package/src/authorization/model/workflow.ts +2 -0
  197. package/src/authorization/model/workflow_instance.ts +2 -0
  198. package/src/authorization/organization-directory.ts +3 -1
  199. package/src/authorization/store-test-kit.ts +75 -16
  200. package/src/authorization/tuples.ts +39 -16
  201. package/src/domain/agent/__tests__/agent.test.ts +4 -11
  202. package/src/domain/agent/controller.ts +7 -7
  203. package/src/domain/agentchannel/__tests__/agentchannel.test.ts +48 -223
  204. package/src/domain/agentchannel/controller.ts +1 -6
  205. package/src/domain/agentexecution/controller.ts +1 -6
  206. package/src/domain/agentinstance/controller.ts +12 -6
  207. package/src/domain/agentshare/__tests__/agentshare.test.ts +172 -196
  208. package/src/domain/agentshare/__tests__/create-authorization.test.ts +31 -16
  209. package/src/domain/agentshare/constants.ts +26 -14
  210. package/src/domain/agentshare/controller.ts +1 -6
  211. package/src/domain/agentshare/steps.ts +194 -41
  212. package/src/domain/environment/controller.ts +8 -5
  213. package/src/domain/executioncontext/controller.ts +1 -5
  214. package/src/domain/mcpserver/__tests__/mcpserver.test.ts +28 -115
  215. package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +18 -53
  216. package/src/domain/mcpserver/controller.ts +12 -6
  217. package/src/domain/plugin/controller.ts +8 -1
  218. package/src/domain/plugin/push.ts +16 -8
  219. package/src/domain/schedule/controller.ts +1 -6
  220. package/src/domain/session/controller.ts +4 -12
  221. package/src/domain/skill/controller.ts +7 -0
  222. package/src/domain/workflow/controller.ts +10 -28
  223. package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
  224. package/src/domain/workflow/validation/task-config-constraints.ts +3 -7
  225. package/src/domain/workflowexecution/controller.ts +2 -7
  226. package/src/domain/workflowinstance/controller.ts +12 -6
  227. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +19 -32
  228. package/src/extensions/__tests__/extension-composition.test.ts +4 -4
  229. package/src/extensions/__tests__/list-read-scope-composed.test.ts +6 -9
  230. package/src/extensions/authorization-queries.ts +5 -6
  231. package/src/extensions/list-read-scope.ts +1 -1
  232. package/src/extensions/resource-authorization.ts +5 -3
  233. package/src/index.ts +14 -0
  234. package/src/pipeline/apiresource-meta.ts +10 -21
  235. package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +31 -18
  236. package/src/pipeline/steps/__tests__/visibility-gates.test.ts +187 -0
  237. package/src/pipeline/steps/authorization-tuples.ts +11 -9
  238. package/src/pipeline/steps/references.ts +70 -578
  239. package/src/pipeline/steps/visibility-gates.ts +174 -0
  240. package/src/query/search/__tests__/criteria.test.ts +4 -0
  241. package/src/query/search/__tests__/query-store.test.ts +40 -11
  242. package/src/query/search/controller.ts +1 -0
  243. package/src/query/search/criteria.ts +14 -0
  244. package/src/query/search/handler.ts +7 -6
  245. package/src/query/search/query-store.ts +2 -0
  246. package/src/store/__tests__/store-contract.ts +51 -6
  247. package/src/store/interface.ts +6 -3
  248. package/src/store/postgres/__tests__/migrations.test.ts +6 -187
  249. package/src/store/postgres/__tests__/store-contract.test.ts +2 -0
  250. package/src/store/postgres/migrations.ts +1 -48
  251. package/src/store/postgres/store.ts +11 -4
  252. package/src/store/sqlite/__tests__/migrations.test.ts +5 -191
  253. package/src/store/sqlite/migrations.ts +1 -45
  254. package/src/store/sqlite/store.ts +10 -4
  255. package/dist/domain/workflow/agent-call-references.d.ts +0 -54
  256. package/dist/domain/workflow/agent-call-references.d.ts.map +0 -1
  257. package/dist/domain/workflow/agent-call-references.js +0 -97
  258. package/dist/domain/workflow/agent-call-references.js.map +0 -1
  259. package/dist/store/public-visibility-retired.d.ts +0 -67
  260. package/dist/store/public-visibility-retired.d.ts.map +0 -1
  261. package/dist/store/public-visibility-retired.js +0 -55
  262. package/dist/store/public-visibility-retired.js.map +0 -1
  263. package/src/domain/workflow/__tests__/agent-call-references.test.ts +0 -222
  264. package/src/domain/workflow/agent-call-references.ts +0 -157
  265. package/src/pipeline/__tests__/supports-visibility.test.ts +0 -112
  266. package/src/pipeline/steps/__tests__/references.test.ts +0 -703
  267. package/src/store/__tests__/public-visibility-retired.test.ts +0 -127
  268. package/src/store/public-visibility-retired.ts +0 -119
@@ -4,10 +4,7 @@
4
4
  * idempotent, a mid-chain database resumes (v1 → v2 picks up the sweep
5
5
  * index), and concurrent first boots serialize on the advisory lock
6
6
  * instead of racing the chain — the multi-instance failure class sqlite's
7
- * single-file lock never had. v5, the chain's first row-decoding step,
8
- * moves every row of the seven kinds that held the retired public level to
9
- * org and leaves every other row's bytes as they were; a row it cannot
10
- * decode fails the step and leaves the database at v4.
7
+ * single-file lock never had.
11
8
  *
12
9
  * Gated on TEST_DATABASE_URL (see support.ts): visible skips without a
13
10
  * database, always exercised in CI via the ci.stigmer-server service
@@ -17,14 +14,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
17
14
 
18
15
  import pg from "pg";
19
16
 
20
- import { create, fromJson, toBinary } from "@bufbuild/protobuf";
21
- import type { DescMessage } from "@bufbuild/protobuf";
22
-
23
- import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
24
- import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
25
-
26
- import { PUBLIC_ROW_KINDS_AT_RETIREMENT } from "../../public-visibility-retired.js";
27
- import { CURRENT_SCHEMA_VERSION, SCHEMA_VERSION_5 } from "../migrations.js";
17
+ import { CURRENT_SCHEMA_VERSION } from "../migrations.js";
28
18
  import { PostgresStore } from "../store.js";
29
19
  import {
30
20
  createTestDatabase,
@@ -115,7 +105,7 @@ describe.skipIf(testDatabaseAdminUrl() === undefined)(
115
105
  await client.query(`DROP INDEX idx_wfee_created_at`);
116
106
  await client.query(`DROP INDEX idx_oauth_grant_resource`);
117
107
  await client.query(
118
- `DELETE FROM schema_version WHERE version IN (2, 3, 4, 5)`,
108
+ `DELETE FROM schema_version WHERE version IN (2, 3, 4)`,
119
109
  );
120
110
  await client.query(
121
111
  `INSERT INTO resources (kind, id, data) VALUES ('project', 'prj_seeded', '\\x00'), ('organization', 'acme', '\\x00')`,
@@ -130,10 +120,9 @@ describe.skipIf(testDatabaseAdminUrl() === undefined)(
130
120
  const remaining = await client.query(
131
121
  `SELECT kind, id FROM resources ORDER BY kind`,
132
122
  );
133
- expect(
134
- remaining.rows,
135
- "v4 removed the Project row and kept the rest",
136
- ).toEqual([{ kind: "organization", id: "acme" }]);
123
+ expect(remaining.rows, "v4 removed the Project row and kept the rest").toEqual([
124
+ { kind: "organization", id: "acme" },
125
+ ]);
137
126
  const projectAudit = await client.query(
138
127
  `SELECT count(*)::int AS n FROM resource_audit WHERE kind = 'project'`,
139
128
  );
@@ -201,175 +190,5 @@ describe.skipIf(testDatabaseAdminUrl() === undefined)(
201
190
  await client.end();
202
191
  }
203
192
  });
204
-
205
- describe("v5: the retired public level leaves every row", () => {
206
- /** A row of `schema` carrying only the envelope every kind shares, at `visibility`. */
207
- function envelopeBytes(
208
- schema: DescMessage,
209
- id: string,
210
- visibility: ApiResourceVisibility,
211
- ): Buffer {
212
- return Buffer.from(
213
- toBinary(
214
- schema,
215
- fromJson(schema, {
216
- metadata: {
217
- id,
218
- name: id,
219
- slug: id,
220
- org: "acme",
221
- visibility: ApiResourceVisibility[visibility],
222
- },
223
- }),
224
- ),
225
- );
226
- }
227
-
228
- function agentBytes(
229
- id: string,
230
- visibility: ApiResourceVisibility,
231
- ): Buffer {
232
- return Buffer.from(
233
- toBinary(
234
- AgentSchema,
235
- create(AgentSchema, {
236
- apiVersion: "agentic.stigmer.ai/v1",
237
- kind: "Agent",
238
- metadata: { id, name: id, slug: id, org: "acme", visibility },
239
- spec: { instructions: "a conformant instruction body" },
240
- }),
241
- ),
242
- );
243
- }
244
-
245
- /** Opens the store once (the chain replays), then rewinds to v4 for the seeding. */
246
- async function v4Client(): Promise<pg.Client> {
247
- const store = await PostgresStore.open(db.databaseUrl);
248
- await store.close();
249
- const client = new pg.Client({ connectionString: db.databaseUrl });
250
- await client.connect();
251
- await client.query(`DELETE FROM schema_version WHERE version = $1`, [
252
- SCHEMA_VERSION_5,
253
- ]);
254
- return client;
255
- }
256
-
257
- async function row(
258
- client: pg.Client,
259
- kind: string,
260
- id: string,
261
- ): Promise<{ data: Buffer; updated_at: Date }> {
262
- const result = await client.query<{ data: Buffer; updated_at: Date }>(
263
- `SELECT data, updated_at FROM resources WHERE kind = $1 AND id = $2`,
264
- [kind, id],
265
- );
266
- return result.rows[0]!;
267
- }
268
-
269
- it("moves a public row of every kind in the frozen table to org, and leaves an org row and a private row byte-for-byte", async () => {
270
- const client = await v4Client();
271
- const seededAt = new Date("2026-09-01T00:00:00Z");
272
- try {
273
- for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
274
- await client.query(
275
- `INSERT INTO resources (kind, id, data, updated_at) VALUES ($1, $2, $3, $4)`,
276
- [
277
- entry.kind,
278
- `${entry.kind}_public`,
279
- envelopeBytes(
280
- entry.schema,
281
- `${entry.kind}_public`,
282
- ApiResourceVisibility.visibility_public,
283
- ),
284
- seededAt,
285
- ],
286
- );
287
- }
288
- const orgBytes = agentBytes(
289
- "agt_org",
290
- ApiResourceVisibility.visibility_org,
291
- );
292
- const privateBytes = agentBytes(
293
- "agt_private",
294
- ApiResourceVisibility.visibility_private,
295
- );
296
- await client.query(
297
- `INSERT INTO resources (kind, id, data, updated_at) VALUES ('agent', 'agt_org', $1, $3), ('agent', 'agt_private', $2, $3)`,
298
- [orgBytes, privateBytes, seededAt],
299
- );
300
-
301
- const reopened = await PostgresStore.open(db.databaseUrl);
302
- await reopened.close();
303
-
304
- const version = await client.query(
305
- `SELECT COALESCE(MAX(version), 0) AS version FROM schema_version`,
306
- );
307
- expect(Number((version.rows[0] as { version: string }).version)).toBe(
308
- CURRENT_SCHEMA_VERSION,
309
- );
310
- for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
311
- const moved = await row(client, entry.kind, `${entry.kind}_public`);
312
- expect(
313
- moved.data.equals(
314
- envelopeBytes(
315
- entry.schema,
316
- `${entry.kind}_public`,
317
- ApiResourceVisibility.visibility_org,
318
- ),
319
- ),
320
- entry.kind,
321
- ).toBe(true);
322
- expect(
323
- moved.updated_at.getTime(),
324
- `${entry.kind} updated_at bumped`,
325
- ).toBeGreaterThan(seededAt.getTime());
326
- }
327
- const org = await row(client, "agent", "agt_org");
328
- expect(org.data.equals(orgBytes)).toBe(true);
329
- expect(org.updated_at.getTime()).toBe(seededAt.getTime());
330
- const priv = await row(client, "agent", "agt_private");
331
- expect(priv.data.equals(privateBytes)).toBe(true);
332
- expect(priv.updated_at.getTime()).toBe(seededAt.getTime());
333
- } finally {
334
- await client.end();
335
- }
336
- });
337
-
338
- it("a row of a table kind that does not decode fails the step, names the row, and leaves the database at v4 with every row untouched", async () => {
339
- const client = await v4Client();
340
- try {
341
- const publicBytes = agentBytes(
342
- "agt_public",
343
- ApiResourceVisibility.visibility_public,
344
- );
345
- await client.query(
346
- `INSERT INTO resources (kind, id, data) VALUES ('agent', 'agt_public', $1), ('agent', 'agt_broken', '\\xffffff'), ('session', 'ses_opaque', '\\xffff')`,
347
- [publicBytes],
348
- );
349
-
350
- await expect(PostgresStore.open(db.databaseUrl)).rejects.toThrow(
351
- /migrate to v5: .*agent 'agt_broken' cannot be moved off the retired public level/,
352
- );
353
-
354
- const version = await client.query(
355
- `SELECT COALESCE(MAX(version), 0) AS version FROM schema_version`,
356
- );
357
- expect(Number((version.rows[0] as { version: string }).version)).toBe(
358
- SCHEMA_VERSION_5 - 1,
359
- );
360
- expect(
361
- (await row(client, "agent", "agt_public")).data.equals(publicBytes),
362
- ).toBe(true);
363
- // A kind outside the frozen table is never decoded, whatever it holds.
364
- expect(
365
- (await row(client, "session", "ses_opaque")).data.equals(
366
- Buffer.from([0xff, 0xff]),
367
- ),
368
- ).toBe(true);
369
- } finally {
370
- await client.end();
371
- }
372
- });
373
- });
374
193
  },
375
194
  );
@@ -111,6 +111,8 @@ describe.skipIf(testDatabaseAdminUrl() === undefined)(
111
111
  kinds: ["agent"],
112
112
  terms: ["billing"],
113
113
  orgFilter: "",
114
+ crossOrgPublic: false,
115
+ excludePublic: false,
114
116
  limit: 20,
115
117
  offset: 0,
116
118
  });
@@ -38,21 +38,14 @@
38
38
  */
39
39
  import type { PoolClient } from "pg";
40
40
 
41
- import {
42
- PUBLIC_ROW_KINDS_AT_RETIREMENT,
43
- movePublicRowToOrg,
44
- } from "../public-visibility-retired.js";
45
-
46
41
  export const SCHEMA_VERSION_1 = 1;
47
42
  export const SCHEMA_VERSION_2 = 2;
48
43
  export const SCHEMA_VERSION_3 = 3;
49
44
  /** v4: the rows of the removed Project kind deleted. */
50
45
  export const SCHEMA_VERSION_4 = 4;
51
- /** v5: every row holding the retired public visibility level moved to org. */
52
- export const SCHEMA_VERSION_5 = 5;
53
46
 
54
47
  /** Target version for new databases. */
55
- export const CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_5;
48
+ export const CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_4;
56
49
 
57
50
  /**
58
51
  * Advisory lock key for the migration chain. Arbitrary but stable 64-bit
@@ -86,7 +79,6 @@ export async function runMigrations(client: PoolClient): Promise<void> {
86
79
  [SCHEMA_VERSION_2, migrateToV2],
87
80
  [SCHEMA_VERSION_3, migrateToV3],
88
81
  [SCHEMA_VERSION_4, migrateToV4],
89
- [SCHEMA_VERSION_5, migrateToV5],
90
82
  ];
91
83
 
92
84
  for (const [version, migrate] of chain) {
@@ -323,42 +315,3 @@ async function migrateToV4(client: PoolClient): Promise<void> {
323
315
  await client.query(`DELETE FROM resources WHERE kind = 'project'`);
324
316
  await client.query(`DELETE FROM resource_audit WHERE kind = 'project'`);
325
317
  }
326
-
327
- /**
328
- * v5: the public visibility level is retired, so every row that still
329
- * holds it is moved to org visibility — the same step as the sqlite
330
- * driver's v10 (public-visibility-retired.ts says why a migration, why the
331
- * kind table is frozen there, and why an undecodable row fails the step).
332
- * The rows are read FOR UPDATE inside applyInTransaction's transaction,
333
- * under the chain's advisory lock, so a second instance booting against
334
- * the same database waits rather than moving a row twice; `updated_at` is
335
- * bumped the way saveResource bumps it; rows that hold any other level are
336
- * left byte-for-byte as they are. On the hosted edition every such row was
337
- * moved through the API before this release deployed, so this step finds
338
- * none and records its version.
339
- */
340
- async function migrateToV5(client: PoolClient): Promise<void> {
341
- for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
342
- const result = await client.query<{ id: string; data: Buffer }>(
343
- `SELECT id, data FROM resources WHERE kind = $1 FOR UPDATE`,
344
- [entry.kind],
345
- );
346
- for (const row of result.rows) {
347
- let moved: Uint8Array | undefined;
348
- try {
349
- moved = movePublicRowToOrg(entry, new Uint8Array(row.data));
350
- } catch (error) {
351
- throw new Error(
352
- `${entry.kind} '${row.id}' cannot be moved off the retired public level: ${String(error)}`,
353
- { cause: error },
354
- );
355
- }
356
- if (moved !== undefined) {
357
- await client.query(
358
- `UPDATE resources SET data = $1, updated_at = now() WHERE kind = $2 AND id = $3`,
359
- [Buffer.from(moved), entry.kind, row.id],
360
- );
361
- }
362
- }
363
- }
364
- }
@@ -814,13 +814,20 @@ export class PostgresStore implements Store {
814
814
  // right side").
815
815
  const kindParam = `${push(query.kinds as string[])}::text[]`;
816
816
 
817
- // The scope-filter fragments — same composition as the sqlite driver:
818
- // the org filter, strict (visibility never widens or narrows the
819
- // scope), then the authorized-id allowlist.
817
+ // The scope-filter fragments — same composition as the sqlite driver's
818
+ // port of Go's buildScopeFilter: org (strict or public-widened) and
819
+ // the independent public subtraction.
820
820
  const scopeClauses: string[] = [];
821
821
  if (query.orgFilter !== "") {
822
822
  const orgParam = push(query.orgFilter);
823
- scopeClauses.push(`AND org = ${orgParam}`);
823
+ scopeClauses.push(
824
+ query.crossOrgPublic
825
+ ? `AND (org = ${orgParam} OR visibility = 'visibility_public')`
826
+ : `AND org = ${orgParam}`,
827
+ );
828
+ }
829
+ if (query.excludePublic) {
830
+ scopeClauses.push(`AND visibility != 'visibility_public'`);
824
831
  }
825
832
  if (query.authorizedIdsByKind !== undefined) {
826
833
  // The 20260830.01 scoping arm: per-kind resource_id allowlists.
@@ -6,10 +6,7 @@
6
6
  * predates Go's idempotent ALTERs gains its columns; a legacy pre-v2
7
7
  * database gets its prefix-based audit rows migrated. Rollback safety
8
8
  * (DD-006): a v7 database re-opened by Go-shaped version checks
9
- * (< 6) runs nothing. v10, the chain's first row-decoding step, moves
10
- * every row of the seven kinds that held the retired public level to org
11
- * and leaves every other row's bytes as they were; a row it cannot decode
12
- * fails the step and leaves the database at v9.
9
+ * (< 6) runs nothing.
13
10
  */
14
11
  import { mkdtempSync, rmSync } from "node:fs";
15
12
  import { tmpdir } from "node:os";
@@ -18,20 +15,12 @@ import { DatabaseSync } from "node:sqlite";
18
15
 
19
16
  import { afterEach, describe, expect, it } from "vitest";
20
17
 
21
- import { create, fromBinary, fromJson, toBinary } from "@bufbuild/protobuf";
22
- import type { DescMessage } from "@bufbuild/protobuf";
23
-
24
- import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
25
- import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
26
-
27
18
  import { SqliteStore } from "../store.js";
28
19
  import {
29
20
  CURRENT_SCHEMA_VERSION,
30
- SCHEMA_VERSION_10,
31
21
  getSchemaVersion,
32
22
  runMigrations,
33
23
  } from "../migrations.js";
34
- import { PUBLIC_ROW_KINDS_AT_RETIREMENT } from "../../public-visibility-retired.js";
35
24
  import { materializeGoFixture } from "./support.js";
36
25
 
37
26
  const cleanups: Array<() => void | Promise<void>> = [];
@@ -92,9 +81,7 @@ describe("fresh database", () => {
92
81
  const rows = db
93
82
  .prepare(`SELECT version FROM schema_version ORDER BY version`)
94
83
  .all() as Array<{ version: number }>;
95
- expect(rows.map((row) => row.version)).toEqual([
96
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
97
- ]);
84
+ expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
98
85
  });
99
86
 
100
87
  it("re-opening an already-migrated database is a no-op", async () => {
@@ -151,9 +138,7 @@ describe("Go-created v6 database adoption (DD-002 fixture)", () => {
151
138
  expect(org.id).toBe("acme");
152
139
 
153
140
  const audit = db
154
- .prepare(
155
- `SELECT version_hash, tag FROM resource_audit WHERE kind = 'organization'`,
156
- )
141
+ .prepare(`SELECT version_hash, tag FROM resource_audit WHERE kind = 'organization'`)
157
142
  .get() as { version_hash: string; tag: string };
158
143
  expect(audit).toEqual({ version_hash: "hash-v1", tag: "stable" });
159
144
 
@@ -298,10 +283,8 @@ describe("legacy pre-v2 database", () => {
298
283
  CREATE TABLE schema_version (version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT (datetime('now')));
299
284
  INSERT INTO schema_version (version) VALUES (1);
300
285
  CREATE TABLE resources (kind TEXT NOT NULL, id TEXT NOT NULL, data BLOB NOT NULL, updated_at TEXT NOT NULL DEFAULT (datetime('now')), PRIMARY KEY (kind, id)) WITHOUT ROWID;
301
- -- Field 1 (api_version) holding one ASCII byte: bytes v10 can decode
302
- -- as a Skill, since it walks every live row of that kind.
303
- INSERT INTO resources (kind, id, data) VALUES ('skill', 'skl_1', X'0a0176');
304
- INSERT INTO resources (kind, id, data) VALUES ('skill', 'skill_audit/skl_1/1706123456789', X'0a0177');
286
+ INSERT INTO resources (kind, id, data) VALUES ('skill', 'skl_1', X'0a01aa');
287
+ INSERT INTO resources (kind, id, data) VALUES ('skill', 'skill_audit/skl_1/1706123456789', X'0a01bb');
305
288
  `);
306
289
  setup.close();
307
290
 
@@ -347,172 +330,3 @@ describe("rollback safety (DD-006)", () => {
347
330
  expect(version.v).toBeGreaterThanOrEqual(6);
348
331
  });
349
332
  });
350
-
351
- describe("v10: the retired public level leaves every row", () => {
352
- /** A v9 database: the chain replayed, then v10's version row removed. */
353
- function v9Database(): { dbPath: string; db: DatabaseSync } {
354
- const dbPath = tempDbPath();
355
- const setup = new DatabaseSync(dbPath);
356
- runMigrations(setup);
357
- setup
358
- .prepare(`DELETE FROM schema_version WHERE version = ?`)
359
- .run(SCHEMA_VERSION_10);
360
- expect(getSchemaVersion(setup)).toBe(SCHEMA_VERSION_10 - 1);
361
- return { dbPath, db: setup };
362
- }
363
-
364
- function agentBytes(
365
- id: string,
366
- visibility: ApiResourceVisibility,
367
- ): Uint8Array {
368
- return toBinary(
369
- AgentSchema,
370
- create(AgentSchema, {
371
- apiVersion: "agentic.stigmer.ai/v1",
372
- kind: "Agent",
373
- metadata: { id, name: id, slug: id, org: "acme", visibility },
374
- spec: { instructions: "a conformant instruction body" },
375
- }),
376
- );
377
- }
378
-
379
- /** A row of `schema` carrying only the envelope every kind shares, at `visibility`. */
380
- function envelopeBytes(
381
- schema: DescMessage,
382
- id: string,
383
- visibility: ApiResourceVisibility,
384
- ): Uint8Array {
385
- return toBinary(
386
- schema,
387
- fromJson(schema, {
388
- metadata: {
389
- id,
390
- name: id,
391
- slug: id,
392
- org: "acme",
393
- visibility: ApiResourceVisibility[visibility],
394
- },
395
- }),
396
- );
397
- }
398
-
399
- function insert(
400
- db: DatabaseSync,
401
- kind: string,
402
- id: string,
403
- data: Uint8Array,
404
- ): void {
405
- db.prepare(
406
- `INSERT INTO resources (kind, id, data, updated_at) VALUES (?, ?, ?, '2026-09-01 00:00:00')`,
407
- ).run(kind, id, data);
408
- }
409
-
410
- function row(
411
- db: DatabaseSync,
412
- kind: string,
413
- id: string,
414
- ): { data: Uint8Array; updated_at: string } {
415
- return db
416
- .prepare(
417
- `SELECT data, updated_at FROM resources WHERE kind = ? AND id = ?`,
418
- )
419
- .get(kind, id) as { data: Uint8Array; updated_at: string };
420
- }
421
-
422
- it("moves a public row of every kind in the frozen table to org, and leaves an org row and a private row byte-for-byte", () => {
423
- const { dbPath, db: setup } = v9Database();
424
- // One public row per kind the level could live under, each encoded
425
- // through its own schema.
426
- for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
427
- insert(
428
- setup,
429
- entry.kind,
430
- `${entry.kind}_public`,
431
- envelopeBytes(
432
- entry.schema,
433
- `${entry.kind}_public`,
434
- ApiResourceVisibility.visibility_public,
435
- ),
436
- );
437
- }
438
- const orgBytes = agentBytes(
439
- "agt_org",
440
- ApiResourceVisibility.visibility_org,
441
- );
442
- const privateBytes = agentBytes(
443
- "agt_private",
444
- ApiResourceVisibility.visibility_private,
445
- );
446
- insert(setup, "agent", "agt_org", orgBytes);
447
- insert(setup, "agent", "agt_private", privateBytes);
448
- setup.close();
449
-
450
- const store = SqliteStore.open(dbPath);
451
- cleanups.push(() => store.close());
452
- const db = new DatabaseSync(dbPath);
453
- cleanups.push(() => db.close());
454
-
455
- expect(getSchemaVersion(db)).toBe(CURRENT_SCHEMA_VERSION);
456
- for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
457
- const moved = row(db, entry.kind, `${entry.kind}_public`);
458
- expect(moved.data, entry.kind).toEqual(
459
- envelopeBytes(
460
- entry.schema,
461
- `${entry.kind}_public`,
462
- ApiResourceVisibility.visibility_org,
463
- ),
464
- );
465
- expect(moved.updated_at, `${entry.kind} updated_at bumped`).not.toBe(
466
- "2026-09-01 00:00:00",
467
- );
468
- }
469
- expect(row(db, "agent", "agt_org")).toEqual({
470
- data: orgBytes,
471
- updated_at: "2026-09-01 00:00:00",
472
- });
473
- expect(row(db, "agent", "agt_private")).toEqual({
474
- data: privateBytes,
475
- updated_at: "2026-09-01 00:00:00",
476
- });
477
- const publicLeft = db
478
- .prepare(`SELECT count(*) AS n FROM resources WHERE kind = 'agent'`)
479
- .get() as { n: number };
480
- expect(publicLeft.n).toBe(3);
481
- });
482
-
483
- it("a row of a kind outside the frozen table is never decoded", () => {
484
- const { dbPath, db: setup } = v9Database();
485
- // Bytes no schema decodes, under a kind the level never applied to.
486
- insert(setup, "session", "ses_opaque", new Uint8Array([0xff, 0xff]));
487
- setup.close();
488
-
489
- const store = SqliteStore.open(dbPath);
490
- cleanups.push(() => store.close());
491
- const db = new DatabaseSync(dbPath);
492
- cleanups.push(() => db.close());
493
- expect(getSchemaVersion(db)).toBe(CURRENT_SCHEMA_VERSION);
494
- expect(row(db, "session", "ses_opaque").data).toEqual(
495
- new Uint8Array([0xff, 0xff]),
496
- );
497
- });
498
-
499
- it("a row of a table kind that does not decode fails the step, names the row, and leaves the database at v9 with every row untouched", () => {
500
- const { dbPath, db: setup } = v9Database();
501
- const publicBytes = agentBytes(
502
- "agt_public",
503
- ApiResourceVisibility.visibility_public,
504
- );
505
- insert(setup, "agent", "agt_public", publicBytes);
506
- insert(setup, "agent", "agt_broken", new Uint8Array([0xff, 0xff, 0xff]));
507
- setup.close();
508
-
509
- expect(() => SqliteStore.open(dbPath)).toThrow(
510
- /migrate to v10: .*agent 'agt_broken' cannot be moved off the retired public level/,
511
- );
512
-
513
- const db = new DatabaseSync(dbPath);
514
- cleanups.push(() => db.close());
515
- expect(getSchemaVersion(db)).toBe(SCHEMA_VERSION_10 - 1);
516
- expect(row(db, "agent", "agt_public").data).toEqual(publicBytes);
517
- });
518
- });
@@ -32,11 +32,6 @@
32
32
  */
33
33
  import type { DatabaseSync } from "node:sqlite";
34
34
 
35
- import {
36
- PUBLIC_ROW_KINDS_AT_RETIREMENT,
37
- movePublicRowToOrg,
38
- } from "../public-visibility-retired.js";
39
-
40
35
  export const SCHEMA_VERSION_1 = 1;
41
36
  export const SCHEMA_VERSION_2 = 2;
42
37
  export const SCHEMA_VERSION_3 = 3;
@@ -49,11 +44,9 @@ export const SCHEMA_VERSION_7 = 7;
49
44
  export const SCHEMA_VERSION_8 = 8;
50
45
  /** v9: the rows of the removed Project kind deleted. */
51
46
  export const SCHEMA_VERSION_9 = 9;
52
- /** v10: every row holding the retired public visibility level moved to org. */
53
- export const SCHEMA_VERSION_10 = 10;
54
47
 
55
48
  /** Target version for new databases. */
56
- export const CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_10;
49
+ export const CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_9;
57
50
 
58
51
  /** Applies all pending migrations in order. */
59
52
  export function runMigrations(db: DatabaseSync): void {
@@ -76,7 +69,6 @@ export function runMigrations(db: DatabaseSync): void {
76
69
  [SCHEMA_VERSION_7, migrateToV7],
77
70
  [SCHEMA_VERSION_8, migrateToV8],
78
71
  [SCHEMA_VERSION_9, migrateToV9],
79
- [SCHEMA_VERSION_10, migrateToV10],
80
72
  ];
81
73
 
82
74
  for (const [version, migrate] of chain) {
@@ -387,39 +379,3 @@ function migrateToV9(db: DatabaseSync): void {
387
379
  DELETE FROM resource_audit WHERE kind = 'project';
388
380
  `);
389
381
  }
390
-
391
- /**
392
- * v10: the public visibility level is retired, so every row that still
393
- * holds it is moved to org visibility — the chain's first migration that
394
- * decodes a row (public-visibility-retired.ts says why a migration, why
395
- * the kind table is frozen there, and why an undecodable row fails the
396
- * step). This step owns the SQL: one read per kind in the frozen table,
397
- * one UPDATE per moved row with `updated_at` bumped the way saveResource
398
- * bumps it, rows that hold any other level left byte-for-byte as they
399
- * are. Runs inside applyInTransaction's BEGIN, so a throw from the mover
400
- * rolls the whole step back and the boot stops on the row it names.
401
- */
402
- function migrateToV10(db: DatabaseSync): void {
403
- const update = db.prepare(
404
- `UPDATE resources SET data = ?, updated_at = datetime('now') WHERE kind = ? AND id = ?`,
405
- );
406
- for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
407
- const rows = db
408
- .prepare(`SELECT id, data FROM resources WHERE kind = ?`)
409
- .all(entry.kind) as Array<{ id: string; data: Uint8Array }>;
410
- for (const row of rows) {
411
- let moved: Uint8Array | undefined;
412
- try {
413
- moved = movePublicRowToOrg(entry, row.data);
414
- } catch (error) {
415
- throw new Error(
416
- `${entry.kind} '${row.id}' cannot be moved off the retired public level: ${String(error)}`,
417
- { cause: error },
418
- );
419
- }
420
- if (moved !== undefined) {
421
- update.run(moved, entry.kind, row.id);
422
- }
423
- }
424
- }
425
- }
@@ -820,15 +820,21 @@ export class SqliteStore implements Store {
820
820
  const kindPlaceholders = query.kinds.map(() => "?").join(",");
821
821
  const kindArgs = [...query.kinds];
822
822
 
823
- // The scope-filter fragments: the org filter, strict (a row is in the
824
- // requested org or it is not; visibility never widens or narrows the
825
- // scope), then the authorized-id allowlist.
823
+ // The scope-filter fragments, byte-identical to Go's buildScopeFilter:
824
+ // org (strict or public-widened) and the independent public subtraction.
826
825
  const scopeClauses: string[] = [];
827
826
  const scopeArgs: string[] = [];
828
827
  if (query.orgFilter !== "") {
829
- scopeClauses.push(`AND org = ?`);
828
+ scopeClauses.push(
829
+ query.crossOrgPublic
830
+ ? `AND (org = ? OR visibility = 'visibility_public')`
831
+ : `AND org = ?`,
832
+ );
830
833
  scopeArgs.push(query.orgFilter);
831
834
  }
835
+ if (query.excludePublic) {
836
+ scopeClauses.push(`AND visibility != 'visibility_public'`);
837
+ }
832
838
  if (query.authorizedIdsByKind !== undefined) {
833
839
  // The 20260830.01 scoping arm: per-kind resource_id allowlists.
834
840
  // An empty set contributes NO clause — that kind matches nothing —