@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.
- package/dist/authorization/authorizer.d.ts +8 -4
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +9 -5
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +25 -10
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +8 -7
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +20 -4
- 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 +9 -5
- 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 +5 -4
- 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 +4 -3
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +10 -8
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +3 -0
- 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 +3 -2
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/store-test-kit.d.ts +6 -0
- package/dist/authorization/store-test-kit.d.ts.map +1 -1
- package/dist/authorization/store-test-kit.js +62 -16
- package/dist/authorization/store-test-kit.js.map +1 -1
- package/dist/authorization/tuples.d.ts +32 -13
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +11 -3
- 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 +1 -3
- 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 +1 -3
- 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 +4 -3
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentshare/constants.d.ts +11 -6
- package/dist/domain/agentshare/constants.d.ts.map +1 -1
- package/dist/domain/agentshare/constants.js +16 -8
- 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 +1 -3
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +25 -18
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +142 -41
- 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 +3 -2
- 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 +1 -2
- 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 +4 -3
- 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 +3 -1
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +5 -7
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +14 -8
- 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 +1 -3
- 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 +2 -3
- 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 +2 -0
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +6 -14
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts +1 -10
- package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.js +2 -3
- 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 +2 -4
- 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 +4 -3
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/extensions/authorization-queries.d.ts +5 -6
- 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 +2 -3
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +3 -12
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +10 -21
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +3 -9
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +11 -9
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +20 -178
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +43 -344
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/pipeline/steps/visibility-gates.d.ts +27 -0
- package/dist/pipeline/steps/visibility-gates.d.ts.map +1 -0
- package/dist/pipeline/steps/visibility-gates.js +139 -0
- package/dist/pipeline/steps/visibility-gates.js.map +1 -0
- package/dist/query/search/controller.js +1 -0
- package/dist/query/search/controller.js.map +1 -1
- package/dist/query/search/criteria.d.ts +5 -1
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +13 -3
- package/dist/query/search/criteria.js.map +1 -1
- package/dist/query/search/handler.d.ts +4 -5
- package/dist/query/search/handler.d.ts.map +1 -1
- package/dist/query/search/handler.js +6 -7
- 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 +2 -0
- package/dist/query/search/query-store.js.map +1 -1
- package/dist/store/interface.d.ts +6 -3
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +1 -3
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +1 -35
- 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 +9 -4
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/sqlite/migrations.d.ts +1 -3
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +1 -36
- 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 +8 -4
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/README.md +26 -24
- package/src/authorization/__tests__/authorizer.test.ts +9 -10
- package/src/authorization/__tests__/derived-tuples.test.ts +11 -4
- package/src/authorization/__tests__/evaluator.test.ts +73 -14
- package/src/authorization/__tests__/facts.test.ts +2 -2
- package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +185 -0
- package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +98 -6
- package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +98 -0
- package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +61 -4
- package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +143 -0
- package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +21 -1
- package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +25 -1
- package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +52 -0
- package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +3 -1
- package/src/authorization/__tests__/list-read-scope.test.ts +17 -18
- package/src/authorization/__tests__/store-tests.test.ts +8 -1
- package/src/authorization/__tests__/tuples.test.ts +11 -12
- package/src/authorization/authorizer.ts +9 -4
- package/src/authorization/derived-tuples.ts +31 -14
- package/src/authorization/evaluator.ts +44 -8
- package/src/authorization/list-read-scope.ts +11 -5
- package/src/authorization/model/agent.ts +5 -2
- package/src/authorization/model/agent_instance.ts +2 -0
- package/src/authorization/model/mcp_server.ts +2 -0
- package/src/authorization/model/plugin.ts +4 -1
- package/src/authorization/model/rewrite.ts +13 -8
- package/src/authorization/model/skill.ts +2 -0
- package/src/authorization/model/workflow.ts +2 -0
- package/src/authorization/model/workflow_instance.ts +2 -0
- package/src/authorization/organization-directory.ts +3 -1
- package/src/authorization/store-test-kit.ts +75 -16
- package/src/authorization/tuples.ts +39 -16
- package/src/domain/agent/__tests__/agent.test.ts +4 -11
- package/src/domain/agent/controller.ts +7 -7
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +48 -223
- package/src/domain/agentchannel/controller.ts +1 -6
- package/src/domain/agentexecution/controller.ts +1 -6
- package/src/domain/agentinstance/controller.ts +12 -6
- package/src/domain/agentshare/__tests__/agentshare.test.ts +172 -196
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +31 -16
- package/src/domain/agentshare/constants.ts +26 -14
- package/src/domain/agentshare/controller.ts +1 -6
- package/src/domain/agentshare/steps.ts +194 -41
- package/src/domain/environment/controller.ts +8 -5
- package/src/domain/executioncontext/controller.ts +1 -5
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +28 -115
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +18 -53
- package/src/domain/mcpserver/controller.ts +12 -6
- package/src/domain/plugin/controller.ts +8 -1
- package/src/domain/plugin/push.ts +16 -8
- package/src/domain/schedule/controller.ts +1 -6
- package/src/domain/session/controller.ts +4 -12
- package/src/domain/skill/controller.ts +7 -0
- package/src/domain/workflow/controller.ts +10 -28
- package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/src/domain/workflow/validation/task-config-constraints.ts +3 -7
- package/src/domain/workflowexecution/controller.ts +2 -7
- package/src/domain/workflowinstance/controller.ts +12 -6
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +19 -32
- package/src/extensions/__tests__/extension-composition.test.ts +4 -4
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +6 -9
- package/src/extensions/authorization-queries.ts +5 -6
- package/src/extensions/list-read-scope.ts +1 -1
- package/src/extensions/resource-authorization.ts +5 -3
- package/src/index.ts +14 -0
- package/src/pipeline/apiresource-meta.ts +10 -21
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +31 -18
- package/src/pipeline/steps/__tests__/visibility-gates.test.ts +187 -0
- package/src/pipeline/steps/authorization-tuples.ts +11 -9
- package/src/pipeline/steps/references.ts +70 -578
- package/src/pipeline/steps/visibility-gates.ts +174 -0
- package/src/query/search/__tests__/criteria.test.ts +4 -0
- package/src/query/search/__tests__/query-store.test.ts +40 -11
- package/src/query/search/controller.ts +1 -0
- package/src/query/search/criteria.ts +14 -0
- package/src/query/search/handler.ts +7 -6
- package/src/query/search/query-store.ts +2 -0
- package/src/store/__tests__/store-contract.ts +51 -6
- package/src/store/interface.ts +6 -3
- package/src/store/postgres/__tests__/migrations.test.ts +6 -187
- package/src/store/postgres/__tests__/store-contract.test.ts +2 -0
- package/src/store/postgres/migrations.ts +1 -48
- package/src/store/postgres/store.ts +11 -4
- package/src/store/sqlite/__tests__/migrations.test.ts +5 -191
- package/src/store/sqlite/migrations.ts +1 -45
- package/src/store/sqlite/store.ts +10 -4
- package/dist/domain/workflow/agent-call-references.d.ts +0 -54
- package/dist/domain/workflow/agent-call-references.d.ts.map +0 -1
- package/dist/domain/workflow/agent-call-references.js +0 -97
- package/dist/domain/workflow/agent-call-references.js.map +0 -1
- package/dist/store/public-visibility-retired.d.ts +0 -67
- package/dist/store/public-visibility-retired.d.ts.map +0 -1
- package/dist/store/public-visibility-retired.js +0 -55
- package/dist/store/public-visibility-retired.js.map +0 -1
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +0 -222
- package/src/domain/workflow/agent-call-references.ts +0 -157
- package/src/pipeline/__tests__/supports-visibility.test.ts +0 -112
- package/src/pipeline/steps/__tests__/references.test.ts +0 -703
- package/src/store/__tests__/public-visibility-retired.test.ts +0 -127
- 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.
|
|
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 {
|
|
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
|
|
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
|
-
|
|
135
|
-
|
|
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
|
);
|
|
@@ -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 =
|
|
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
|
-
//
|
|
819
|
-
//
|
|
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(
|
|
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.
|
|
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
|
-
|
|
302
|
-
|
|
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 =
|
|
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
|
|
824
|
-
//
|
|
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(
|
|
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 —
|