@segmentstream-cloud/ontology 0.22.0 → 0.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/README.md +27 -2
- package/dist/aggregate-routes.d.ts +46 -9
- package/dist/aggregate-routes.js +186 -20
- package/dist/analyze.d.ts +12 -1
- package/dist/analyze.js +3 -1
- package/dist/diagnostics.d.ts +3 -3
- package/dist/diagnostics.js +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/manifest.d.ts +113 -2
- package/dist/manifest.js +33 -2
- package/dist/measure.d.ts +57 -6
- package/dist/measure.js +7 -2
- package/dist/migrate/apply.js +2 -1
- package/dist/migrate/model.d.ts +22 -0
- package/dist/migrate/plan.js +9 -1
- package/dist/migrate/storage.d.ts +3 -0
- package/dist/migrate/storage.js +10 -0
- package/dist/model-measures.d.ts +22 -0
- package/dist/model-measures.js +543 -0
- package/dist/ontology.d.ts +5 -4
- package/dist/permissions.d.ts +3 -0
- package/dist/permissions.js +8 -1
- package/dist/render.d.ts +15 -3
- package/dist/render.js +56 -3
- package/dist/runtime/history.d.ts +22 -0
- package/dist/runtime/history.js +2 -0
- package/dist/runtime/pipeline.js +6 -2
- package/dist/runtime/principals.d.ts +2 -0
- package/dist/runtime/principals.js +55 -0
- package/dist/runtime/reads.js +9 -1
- package/dist/runtime/service-binding.js +2 -1
- package/dist/runtime/workload.d.ts +23 -0
- package/dist/runtime/workload.js +22 -0
- package/dist/runtime/writes.js +7 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/service/authentication.d.ts +11 -0
- package/dist/service/authentication.js +114 -5
- package/dist/service/catalog.d.ts +2 -1
- package/dist/service/catalog.js +7 -3
- package/dist/service/config.d.ts +4 -0
- package/dist/service/config.js +11 -0
- package/dist/service/handlers.js +2 -1
- package/dist/service/mcp/server.js +10 -6
- package/dist/service/mcp/tools.js +25 -10
- package/dist/service/operations.d.ts +25 -5
- package/dist/service/runtime-contract.d.ts +1 -0
- package/dist/service/schemas.d.ts +143 -0
- package/dist/service/schemas.js +11 -2
- package/dist/storage/attachments.js +4 -4
- package/dist/storage/authorization.d.ts +1 -1
- package/dist/storage/authorization.js +9 -4
- package/dist/storage/sessions.js +3 -3
- package/dist/storage/writes.js +1 -1
- package/dist/warehouse-backed.js +4 -0
- package/package.json +1 -1
package/dist/service/handlers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { effectivePermissions } from "../permissions.js";
|
|
1
2
|
import { HttpServerResponse } from "effect/unstable/http";
|
|
2
3
|
import { Effect, Layer, Schema } from "effect";
|
|
3
4
|
import { HttpApiBuilder, OpenApi } from "effect/unstable/httpapi";
|
|
@@ -145,7 +146,7 @@ function serviceHandlers(built, operationsOf, aggregate) {
|
|
|
145
146
|
return undefined;
|
|
146
147
|
}
|
|
147
148
|
const principal = yield* CurrentPrincipal;
|
|
148
|
-
return principal
|
|
149
|
+
return effectivePermissions(principal);
|
|
149
150
|
});
|
|
150
151
|
return handlers
|
|
151
152
|
.handle("catalog", (request) => Effect.map(permissionsFor(request.query.as), (permissions) => catalogFor(manifest, permissions)))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { effectivePermissions } from "../../permissions.js";
|
|
1
2
|
import { Cause, Data, Effect, Layer, Option, Schema, SchemaTransformation } from "effect";
|
|
2
3
|
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
3
4
|
import { HttpApiBuilder, HttpApiSecurity } from "effect/unstable/httpapi";
|
|
@@ -7,7 +8,7 @@ import { SCOPE_MISMATCH } from "../aggregate-platform.js";
|
|
|
7
8
|
import { CurrentPrincipal, withPrincipal } from "../authentication.js";
|
|
8
9
|
import { catalogFor } from "../catalog.js";
|
|
9
10
|
import { ConversationSearch } from "../agent/conversations.js";
|
|
10
|
-
import { describeCatalog, measureDescription, measuresFor, RECORDS_OR_AGGREGATES, } from "../../render.js";
|
|
11
|
+
import { describeCatalog, measureDescription, measuresFor, modelMeasureDescription, modelMeasuresFor, RECORDS_OR_AGGREGATES, } from "../../render.js";
|
|
11
12
|
import { RequestId, errorBodyForWire, expectedFailure, internalError, schemaIssues, } from "../errors.js";
|
|
12
13
|
import { publicOrigin } from "../oauth/protocol.js";
|
|
13
14
|
import { mcpChallenge, mcpResource } from "../oauth/server.js";
|
|
@@ -175,7 +176,7 @@ function runTool({ built, operations, aggregate, conversations }, tool, args, re
|
|
|
175
176
|
case "describe": {
|
|
176
177
|
const principal = yield* CurrentPrincipal;
|
|
177
178
|
return {
|
|
178
|
-
text: describeCatalog(built.plan.ontology.manifest, principal
|
|
179
|
+
text: describeCatalog(built.plan.ontology.manifest, effectivePermissions(principal)),
|
|
179
180
|
};
|
|
180
181
|
}
|
|
181
182
|
case "aggregate": {
|
|
@@ -285,7 +286,7 @@ function readResource({ built }, uri) {
|
|
|
285
286
|
{
|
|
286
287
|
uri,
|
|
287
288
|
mimeType: "application/json",
|
|
288
|
-
text: JSON.stringify(encodeManifest(catalogFor(manifest, principal
|
|
289
|
+
text: JSON.stringify(encodeManifest(catalogFor(manifest, effectivePermissions(principal)))),
|
|
289
290
|
},
|
|
290
291
|
];
|
|
291
292
|
case DESCRIBE_URI:
|
|
@@ -293,7 +294,7 @@ function readResource({ built }, uri) {
|
|
|
293
294
|
{
|
|
294
295
|
uri,
|
|
295
296
|
mimeType: "text/plain",
|
|
296
|
-
text: describeCatalog(manifest, principal
|
|
297
|
+
text: describeCatalog(manifest, effectivePermissions(principal)),
|
|
297
298
|
},
|
|
298
299
|
];
|
|
299
300
|
default:
|
|
@@ -313,7 +314,10 @@ function instructions({ built }, permissions) {
|
|
|
313
314
|
return (definition.source === undefined && isAllowed(permissionOf(name, READ_VERB), { permissions }));
|
|
314
315
|
})
|
|
315
316
|
.map(({ name, resolved }) => `- ${name}: ${resolved.entity.definition.description}`);
|
|
316
|
-
const measures =
|
|
317
|
+
const measures = [
|
|
318
|
+
...measuresFor(manifest, permissions).map(({ name, entity, measure }) => `- ${name}: ${measureDescription(entity, measure)}`),
|
|
319
|
+
...modelMeasuresFor(manifest, permissions).map((measure) => `- ${measure.name}: ${modelMeasureDescription(measure)}`),
|
|
320
|
+
];
|
|
317
321
|
return [
|
|
318
322
|
"This server is the app's ontology: the records the company works with and the actions " +
|
|
319
323
|
"that change them. Every change goes through an action, is checked against your " +
|
|
@@ -351,7 +355,7 @@ function dispatch(server, message, request) {
|
|
|
351
355
|
title: "Ontology",
|
|
352
356
|
version: server.built.plan.ontology.manifest.hash,
|
|
353
357
|
},
|
|
354
|
-
instructions: instructions(server, principal
|
|
358
|
+
instructions: instructions(server, effectivePermissions(principal)),
|
|
355
359
|
});
|
|
356
360
|
case "ping":
|
|
357
361
|
return rpcResult(id, {});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { AGGREGATE_NAME_PATTERN } from "../../aggregate-routes.js";
|
|
3
3
|
import { InvalidOntology } from "../../diagnostics.js";
|
|
4
|
-
import { isValueMatched } from "../../manifest.js";
|
|
4
|
+
import { isValueMatched, } from "../../manifest.js";
|
|
5
5
|
import { GENERATED_UPSERT_PREFIX, humanize, mcpToolName } from "../../names.js";
|
|
6
6
|
import { isAllowed, permissionOf, READ_VERB } from "../../permissions.js";
|
|
7
7
|
import { queryJsonSchema } from "../../query.js";
|
|
8
|
-
import { measureDescription, measuresFor, RECORDS_OR_AGGREGATES, } from "../../render.js";
|
|
8
|
+
import { measureDescription, measuresFor, modelMeasureDescription, modelMeasuresFor, RECORDS_OR_AGGREGATES, } from "../../render.js";
|
|
9
9
|
import { actionInputJsonSchema } from "../../runtime/generated-actions.js";
|
|
10
10
|
import { uploadFields } from "../../storage/attachments.js";
|
|
11
11
|
import { actionDescription, requires } from "../descriptions.js";
|
|
@@ -281,8 +281,15 @@ function measureChoice({ name, entity, measure }) {
|
|
|
281
281
|
measureDescription(entity, measure),
|
|
282
282
|
};
|
|
283
283
|
}
|
|
284
|
+
/** A measure of the model, by its bare name, read only by a caller who may read every input. */
|
|
285
|
+
function modelMeasureChoice(measure) {
|
|
286
|
+
return {
|
|
287
|
+
const: measure.name,
|
|
288
|
+
description: `Model · ${measure.label} (${measure.unit}): ` + modelMeasureDescription(measure),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
284
291
|
/** The aggregate tool's input for a caller: the request of `POST /api/ontology/aggregate`. */
|
|
285
|
-
function aggregateInput(measures) {
|
|
292
|
+
function aggregateInput({ measures, model }) {
|
|
286
293
|
return {
|
|
287
294
|
type: "object",
|
|
288
295
|
properties: {
|
|
@@ -291,9 +298,11 @@ function aggregateInput(measures) {
|
|
|
291
298
|
minItems: 1,
|
|
292
299
|
maxItems: AGGREGATE_LIMITS.measures,
|
|
293
300
|
uniqueItems: true,
|
|
294
|
-
items: { anyOf: measures.map(measureChoice) },
|
|
295
|
-
description: "What to compute, as <entity>.<measure
|
|
296
|
-
"computed over their own entity's rows, then
|
|
301
|
+
items: { anyOf: [...measures.map(measureChoice), ...model.map(modelMeasureChoice)] },
|
|
302
|
+
description: "What to compute, as <entity>.<measure>, or a measure of the model by its name. " +
|
|
303
|
+
"Measures of several entities are each computed over their own entity's rows, then " +
|
|
304
|
+
"combined on the dimensions; a measure of the model is computed on the combined rows " +
|
|
305
|
+
"from the entity measures it reads.",
|
|
297
306
|
},
|
|
298
307
|
dimensions: {
|
|
299
308
|
type: "array",
|
|
@@ -385,17 +394,23 @@ const AGGREGATE_DESCRIPTION = "Numbers about the model: measures from one or sev
|
|
|
385
394
|
"which dates were used. " +
|
|
386
395
|
"Answers come from the warehouse, and stored records from its replica: execution.sources says " +
|
|
387
396
|
"how fresh each is, and execution.warnings says when a filter did not apply to a measure. " +
|
|
388
|
-
"Requires reading every entity the request reads, path intermediates included
|
|
389
|
-
"
|
|
397
|
+
"Requires reading every entity the request reads, path intermediates included, and every " +
|
|
398
|
+
"entity a measure of the model reads. A measure of the model is named without an entity; a " +
|
|
399
|
+
"filter must reach every entity it reads or none of them, unless its alignment gives an entity " +
|
|
400
|
+
"no counterpart. Call describe for the model's measures and relationships.";
|
|
390
401
|
function aggregateTool(manifest) {
|
|
391
402
|
return {
|
|
392
403
|
name: "aggregate",
|
|
393
404
|
title: "Aggregate measures",
|
|
394
405
|
description: AGGREGATE_DESCRIPTION,
|
|
395
|
-
inputSchema: aggregateInput(
|
|
406
|
+
inputSchema: aggregateInput({
|
|
407
|
+
measures: measuresFor(manifest),
|
|
408
|
+
model: modelMeasuresFor(manifest),
|
|
409
|
+
}),
|
|
396
410
|
inputSchemaFor: (caller) => {
|
|
397
411
|
const measures = measuresFor(manifest, caller.permissions);
|
|
398
|
-
|
|
412
|
+
const model = modelMeasuresFor(manifest, caller.permissions);
|
|
413
|
+
return measures.length === 0 ? undefined : aggregateInput({ measures, model });
|
|
399
414
|
},
|
|
400
415
|
annotations: READ,
|
|
401
416
|
permissions: [],
|
|
@@ -74,19 +74,39 @@ export declare function makeOperations(backend: Backend, plan: ApiPlan, hooks: R
|
|
|
74
74
|
readonly after?: string;
|
|
75
75
|
readonly limit?: number;
|
|
76
76
|
}) => Effect.Effect<QueryResult<{
|
|
77
|
+
readonly at: string;
|
|
77
78
|
readonly id: string;
|
|
78
|
-
readonly recordId: string;
|
|
79
|
-
readonly action: string;
|
|
80
|
-
readonly actionId: string;
|
|
81
79
|
readonly kind: "create" | "update" | "delete" | "link" | "unlink";
|
|
82
|
-
readonly
|
|
83
|
-
readonly at: string;
|
|
80
|
+
readonly action: string;
|
|
84
81
|
readonly changes: {
|
|
85
82
|
readonly [x: string]: {
|
|
86
83
|
readonly before: import("effect/Schema").Json;
|
|
87
84
|
readonly after: import("effect/Schema").Json;
|
|
88
85
|
};
|
|
89
86
|
};
|
|
87
|
+
readonly principalId: string | null;
|
|
88
|
+
readonly recordId: string;
|
|
89
|
+
readonly actionId: string;
|
|
90
|
+
readonly execution?: {
|
|
91
|
+
readonly issuer: string;
|
|
92
|
+
readonly audience: string;
|
|
93
|
+
readonly organizationId: string;
|
|
94
|
+
readonly workspaceId: string;
|
|
95
|
+
readonly subject: string;
|
|
96
|
+
readonly deploymentId: string;
|
|
97
|
+
readonly ontologyDeploymentId: string;
|
|
98
|
+
readonly servingOntologyDeploymentId: string;
|
|
99
|
+
readonly ontologyOrigin: string;
|
|
100
|
+
readonly environment: "production";
|
|
101
|
+
readonly jobName: string;
|
|
102
|
+
readonly runId: string;
|
|
103
|
+
readonly executionId: string | null;
|
|
104
|
+
readonly attemptId: string;
|
|
105
|
+
readonly generation: number;
|
|
106
|
+
readonly tokenId: string;
|
|
107
|
+
readonly issuedAt: number;
|
|
108
|
+
readonly expiresAt: number;
|
|
109
|
+
} | undefined;
|
|
90
110
|
}>, PermissionDenied | import("../errors.js").NotFound | import("../index.js").InvalidQuery, CurrentPrincipal>;
|
|
91
111
|
/** The records a relationship reaches from one record, as a page of the target entity. */
|
|
92
112
|
traverse: (planned: PlannedEntity, traversal: Traversal, id: string, query: Query) => Effect.Effect<QueryResult<Readonly<Record<string, unknown>>>, PermissionDenied | import("../errors.js").NotFound | import("../index.js").InvalidQuery, CurrentPrincipal>;
|
|
@@ -274,6 +274,7 @@ declare const OntologyBackend_base: Context.ServiceClass<OntologyBackend, "@segm
|
|
|
274
274
|
* Finds, links or creates the member's user. `undefined` when the user may not sign in, and
|
|
275
275
|
* {@link NotInvited} when the sign-in policy turns away a person nobody invited.
|
|
276
276
|
*/
|
|
277
|
+
readonly resolvePipelineWorker?: (execution: import("../runtime/workload.js").WorkloadExecution) => Effect.Effect<Principal | undefined>;
|
|
277
278
|
readonly resolvePrincipal: (identity: SignInIdentity) => Effect.Effect<Principal | undefined, NotInvited>;
|
|
278
279
|
/**
|
|
279
280
|
* Whether a person may sign in with this address, and how, read before the app emails them.
|
|
@@ -40,6 +40,26 @@ export declare const Validation: Schema.Struct<{
|
|
|
40
40
|
}>>;
|
|
41
41
|
}>;
|
|
42
42
|
export declare const HistoryEntry: Schema.decodeTo<Schema.Struct<{
|
|
43
|
+
readonly execution: Schema.optionalKey<Schema.Struct<{
|
|
44
|
+
readonly issuer: Schema.String;
|
|
45
|
+
readonly audience: Schema.String;
|
|
46
|
+
readonly organizationId: Schema.String;
|
|
47
|
+
readonly workspaceId: Schema.String;
|
|
48
|
+
readonly subject: Schema.String;
|
|
49
|
+
readonly deploymentId: Schema.String;
|
|
50
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
51
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
52
|
+
readonly ontologyOrigin: Schema.String;
|
|
53
|
+
readonly environment: Schema.Literal<"production">;
|
|
54
|
+
readonly jobName: Schema.String;
|
|
55
|
+
readonly runId: Schema.String;
|
|
56
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
57
|
+
readonly attemptId: Schema.String;
|
|
58
|
+
readonly generation: Schema.Number;
|
|
59
|
+
readonly tokenId: Schema.String;
|
|
60
|
+
readonly issuedAt: Schema.Number;
|
|
61
|
+
readonly expiresAt: Schema.Number;
|
|
62
|
+
}>>;
|
|
43
63
|
readonly id: Schema.String;
|
|
44
64
|
readonly recordId: Schema.String;
|
|
45
65
|
readonly action: Schema.String;
|
|
@@ -52,6 +72,26 @@ export declare const HistoryEntry: Schema.decodeTo<Schema.Struct<{
|
|
|
52
72
|
readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
53
73
|
}>>;
|
|
54
74
|
}>, Schema.Struct<{
|
|
75
|
+
readonly execution: Schema.toEncoded<Schema.optionalKey<Schema.Struct<{
|
|
76
|
+
readonly issuer: Schema.String;
|
|
77
|
+
readonly audience: Schema.String;
|
|
78
|
+
readonly organizationId: Schema.String;
|
|
79
|
+
readonly workspaceId: Schema.String;
|
|
80
|
+
readonly subject: Schema.String;
|
|
81
|
+
readonly deploymentId: Schema.String;
|
|
82
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
83
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
84
|
+
readonly ontologyOrigin: Schema.String;
|
|
85
|
+
readonly environment: Schema.Literal<"production">;
|
|
86
|
+
readonly jobName: Schema.String;
|
|
87
|
+
readonly runId: Schema.String;
|
|
88
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
89
|
+
readonly attemptId: Schema.String;
|
|
90
|
+
readonly generation: Schema.Number;
|
|
91
|
+
readonly tokenId: Schema.String;
|
|
92
|
+
readonly issuedAt: Schema.Number;
|
|
93
|
+
readonly expiresAt: Schema.Number;
|
|
94
|
+
}>>>;
|
|
55
95
|
readonly id: Schema.toEncoded<Schema.String>;
|
|
56
96
|
readonly record_id: Schema.toEncoded<Schema.String>;
|
|
57
97
|
readonly action: Schema.toEncoded<Schema.String>;
|
|
@@ -66,6 +106,26 @@ export declare const HistoryEntry: Schema.decodeTo<Schema.Struct<{
|
|
|
66
106
|
}>, never, never>;
|
|
67
107
|
export declare const HistoryPage: Schema.decodeTo<Schema.Struct<{
|
|
68
108
|
readonly items: Schema.$Array<Schema.decodeTo<Schema.Struct<{
|
|
109
|
+
readonly execution: Schema.optionalKey<Schema.Struct<{
|
|
110
|
+
readonly issuer: Schema.String;
|
|
111
|
+
readonly audience: Schema.String;
|
|
112
|
+
readonly organizationId: Schema.String;
|
|
113
|
+
readonly workspaceId: Schema.String;
|
|
114
|
+
readonly subject: Schema.String;
|
|
115
|
+
readonly deploymentId: Schema.String;
|
|
116
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
117
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
118
|
+
readonly ontologyOrigin: Schema.String;
|
|
119
|
+
readonly environment: Schema.Literal<"production">;
|
|
120
|
+
readonly jobName: Schema.String;
|
|
121
|
+
readonly runId: Schema.String;
|
|
122
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
123
|
+
readonly attemptId: Schema.String;
|
|
124
|
+
readonly generation: Schema.Number;
|
|
125
|
+
readonly tokenId: Schema.String;
|
|
126
|
+
readonly issuedAt: Schema.Number;
|
|
127
|
+
readonly expiresAt: Schema.Number;
|
|
128
|
+
}>>;
|
|
69
129
|
readonly id: Schema.String;
|
|
70
130
|
readonly recordId: Schema.String;
|
|
71
131
|
readonly action: Schema.String;
|
|
@@ -78,6 +138,26 @@ export declare const HistoryPage: Schema.decodeTo<Schema.Struct<{
|
|
|
78
138
|
readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
79
139
|
}>>;
|
|
80
140
|
}>, Schema.Struct<{
|
|
141
|
+
readonly execution: Schema.toEncoded<Schema.optionalKey<Schema.Struct<{
|
|
142
|
+
readonly issuer: Schema.String;
|
|
143
|
+
readonly audience: Schema.String;
|
|
144
|
+
readonly organizationId: Schema.String;
|
|
145
|
+
readonly workspaceId: Schema.String;
|
|
146
|
+
readonly subject: Schema.String;
|
|
147
|
+
readonly deploymentId: Schema.String;
|
|
148
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
149
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
150
|
+
readonly ontologyOrigin: Schema.String;
|
|
151
|
+
readonly environment: Schema.Literal<"production">;
|
|
152
|
+
readonly jobName: Schema.String;
|
|
153
|
+
readonly runId: Schema.String;
|
|
154
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
155
|
+
readonly attemptId: Schema.String;
|
|
156
|
+
readonly generation: Schema.Number;
|
|
157
|
+
readonly tokenId: Schema.String;
|
|
158
|
+
readonly issuedAt: Schema.Number;
|
|
159
|
+
readonly expiresAt: Schema.Number;
|
|
160
|
+
}>>>;
|
|
81
161
|
readonly id: Schema.toEncoded<Schema.String>;
|
|
82
162
|
readonly record_id: Schema.toEncoded<Schema.String>;
|
|
83
163
|
readonly action: Schema.toEncoded<Schema.String>;
|
|
@@ -93,6 +173,26 @@ export declare const HistoryPage: Schema.decodeTo<Schema.Struct<{
|
|
|
93
173
|
readonly nextCursor: Schema.NullOr<Schema.String>;
|
|
94
174
|
}>, Schema.Struct<{
|
|
95
175
|
readonly items: Schema.toEncoded<Schema.$Array<Schema.decodeTo<Schema.Struct<{
|
|
176
|
+
readonly execution: Schema.optionalKey<Schema.Struct<{
|
|
177
|
+
readonly issuer: Schema.String;
|
|
178
|
+
readonly audience: Schema.String;
|
|
179
|
+
readonly organizationId: Schema.String;
|
|
180
|
+
readonly workspaceId: Schema.String;
|
|
181
|
+
readonly subject: Schema.String;
|
|
182
|
+
readonly deploymentId: Schema.String;
|
|
183
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
184
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
185
|
+
readonly ontologyOrigin: Schema.String;
|
|
186
|
+
readonly environment: Schema.Literal<"production">;
|
|
187
|
+
readonly jobName: Schema.String;
|
|
188
|
+
readonly runId: Schema.String;
|
|
189
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
190
|
+
readonly attemptId: Schema.String;
|
|
191
|
+
readonly generation: Schema.Number;
|
|
192
|
+
readonly tokenId: Schema.String;
|
|
193
|
+
readonly issuedAt: Schema.Number;
|
|
194
|
+
readonly expiresAt: Schema.Number;
|
|
195
|
+
}>>;
|
|
96
196
|
readonly id: Schema.String;
|
|
97
197
|
readonly recordId: Schema.String;
|
|
98
198
|
readonly action: Schema.String;
|
|
@@ -105,6 +205,26 @@ export declare const HistoryPage: Schema.decodeTo<Schema.Struct<{
|
|
|
105
205
|
readonly after: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
106
206
|
}>>;
|
|
107
207
|
}>, Schema.Struct<{
|
|
208
|
+
readonly execution: Schema.toEncoded<Schema.optionalKey<Schema.Struct<{
|
|
209
|
+
readonly issuer: Schema.String;
|
|
210
|
+
readonly audience: Schema.String;
|
|
211
|
+
readonly organizationId: Schema.String;
|
|
212
|
+
readonly workspaceId: Schema.String;
|
|
213
|
+
readonly subject: Schema.String;
|
|
214
|
+
readonly deploymentId: Schema.String;
|
|
215
|
+
readonly ontologyDeploymentId: Schema.String;
|
|
216
|
+
readonly servingOntologyDeploymentId: Schema.String;
|
|
217
|
+
readonly ontologyOrigin: Schema.String;
|
|
218
|
+
readonly environment: Schema.Literal<"production">;
|
|
219
|
+
readonly jobName: Schema.String;
|
|
220
|
+
readonly runId: Schema.String;
|
|
221
|
+
readonly executionId: Schema.NullOr<Schema.String>;
|
|
222
|
+
readonly attemptId: Schema.String;
|
|
223
|
+
readonly generation: Schema.Number;
|
|
224
|
+
readonly tokenId: Schema.String;
|
|
225
|
+
readonly issuedAt: Schema.Number;
|
|
226
|
+
readonly expiresAt: Schema.Number;
|
|
227
|
+
}>>>;
|
|
108
228
|
readonly id: Schema.toEncoded<Schema.String>;
|
|
109
229
|
readonly record_id: Schema.toEncoded<Schema.String>;
|
|
110
230
|
readonly action: Schema.toEncoded<Schema.String>;
|
|
@@ -259,6 +379,7 @@ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
|
|
|
259
379
|
}>, {
|
|
260
380
|
readonly builtIn: "built_in";
|
|
261
381
|
}>>;
|
|
382
|
+
readonly runtimeCapabilities: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
262
383
|
readonly version: Schema.Int;
|
|
263
384
|
readonly hash: Schema.String;
|
|
264
385
|
readonly signIn: Schema.optionalKey<Schema.Struct<{
|
|
@@ -268,6 +389,16 @@ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
|
|
|
268
389
|
readonly key: Schema.String;
|
|
269
390
|
readonly description: Schema.String;
|
|
270
391
|
}>>>;
|
|
392
|
+
readonly measures: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
393
|
+
readonly name: Schema.String;
|
|
394
|
+
readonly label: Schema.String;
|
|
395
|
+
readonly description: Schema.String;
|
|
396
|
+
readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
|
|
397
|
+
readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
|
|
398
|
+
readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
399
|
+
readonly inputs: Schema.$Array<Schema.String>;
|
|
400
|
+
readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
|
|
401
|
+
}>>>;
|
|
271
402
|
}>, Schema.Struct<{
|
|
272
403
|
readonly entities: Schema.toEncoded<Schema.$Array<Schema.encodeKeys<Schema.Struct<{
|
|
273
404
|
readonly measures: Schema.optionalKey<Schema.$Array<Schema.encodeKeys<Schema.Struct<{
|
|
@@ -378,6 +509,7 @@ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
|
|
|
378
509
|
}>, {
|
|
379
510
|
readonly builtIn: "built_in";
|
|
380
511
|
}>>>;
|
|
512
|
+
readonly runtimeCapabilities: Schema.toEncoded<Schema.optionalKey<Schema.$Array<Schema.String>>>;
|
|
381
513
|
readonly version: Schema.toEncoded<Schema.Int>;
|
|
382
514
|
readonly hash: Schema.toEncoded<Schema.String>;
|
|
383
515
|
readonly sign_in: Schema.toEncoded<Schema.optionalKey<Schema.Struct<{
|
|
@@ -387,6 +519,16 @@ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
|
|
|
387
519
|
readonly key: Schema.String;
|
|
388
520
|
readonly description: Schema.String;
|
|
389
521
|
}>>>>;
|
|
522
|
+
readonly measures: Schema.toEncoded<Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
523
|
+
readonly name: Schema.String;
|
|
524
|
+
readonly label: Schema.String;
|
|
525
|
+
readonly description: Schema.String;
|
|
526
|
+
readonly unit: Schema.Literals<readonly ["number", "currency", "ratio", "percent"]>;
|
|
527
|
+
readonly format: Schema.Literals<readonly ["decimal", "currency", "percent"]>;
|
|
528
|
+
readonly expression: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
529
|
+
readonly inputs: Schema.$Array<Schema.String>;
|
|
530
|
+
readonly align: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.String>>>;
|
|
531
|
+
}>>>>;
|
|
390
532
|
}>, never, never>;
|
|
391
533
|
export declare const Graph: Schema.Struct<{
|
|
392
534
|
readonly hash: Schema.String;
|
|
@@ -505,6 +647,7 @@ export declare const AggregateResult: Schema.Struct<{
|
|
|
505
647
|
readonly additive: Schema.optionalKey<Schema.Boolean>;
|
|
506
648
|
readonly filters: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
507
649
|
readonly balanceOver: Schema.optionalKey<Schema.String>;
|
|
650
|
+
readonly inputs: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
508
651
|
}>>;
|
|
509
652
|
readonly rows: Schema.$Array<Schema.$Record<Schema.String, Schema.NullOr<Schema.Union<readonly [Schema.String, Schema.Boolean]>>>>;
|
|
510
653
|
readonly truncated: Schema.Boolean;
|
package/dist/service/schemas.js
CHANGED
|
@@ -366,7 +366,10 @@ const aggregateFields = {
|
|
|
366
366
|
export const AggregateRequest = closedStruct({
|
|
367
367
|
measures: Schema.Array(Schema.String)
|
|
368
368
|
.check(Schema.isMaxLength(AGGREGATE_LIMITS.measures))
|
|
369
|
-
.annotate({
|
|
369
|
+
.annotate({
|
|
370
|
+
description: "<entity>.<measure>, or a measure of the model by its name, such as plan_attainment; " +
|
|
371
|
+
"at least one, at most 30 of both together.",
|
|
372
|
+
}),
|
|
370
373
|
dimensions: Schema.optionalKey(Schema.Array(Schema.String).check(Schema.isMaxLength(AGGREGATE_LIMITS.dimensions))).annotate({
|
|
371
374
|
description: "<entity>.<property>, or a route from a measure's entity: " +
|
|
372
375
|
"<entity>.<relationship>[.<relationship>].<property>.",
|
|
@@ -408,7 +411,9 @@ export const EntityAggregateRequest = closedStruct({
|
|
|
408
411
|
/** SegmentStream's answer to an aggregate, which the service forwards unchanged. */
|
|
409
412
|
export const AggregateResult = Schema.Struct({
|
|
410
413
|
fields: Schema.Array(Schema.Struct({
|
|
411
|
-
name: Schema.String.annotate({
|
|
414
|
+
name: Schema.String.annotate({
|
|
415
|
+
description: "A dimension identity, <entity>.<measure>, or the name of a measure of the model.",
|
|
416
|
+
}),
|
|
412
417
|
label: Schema.String,
|
|
413
418
|
type: Schema.String,
|
|
414
419
|
unit: Schema.optionalKey(Schema.String),
|
|
@@ -425,6 +430,10 @@ export const AggregateResult = Schema.Struct({
|
|
|
425
430
|
description: "The date or timestamp dimension a balance measure was grouped by or filtered to one " +
|
|
426
431
|
"day of.",
|
|
427
432
|
}),
|
|
433
|
+
inputs: Schema.optionalKey(Schema.Array(Schema.String)).annotate({
|
|
434
|
+
description: "The entity measures a measure of the model reads, <entity>.<measure>, in declaration " +
|
|
435
|
+
"order; present only on such a measure.",
|
|
436
|
+
}),
|
|
428
437
|
})),
|
|
429
438
|
rows: Schema.Array(Schema.Record(Schema.String, Schema.NullOr(Schema.Union([Schema.String, Schema.Boolean])))),
|
|
430
439
|
truncated: Schema.Boolean,
|
|
@@ -128,7 +128,7 @@ export function attachmentRuntime(ontology, sql, provider) {
|
|
|
128
128
|
]);
|
|
129
129
|
}
|
|
130
130
|
// The member must still be active; a deactivated member's agent reads nothing.
|
|
131
|
-
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, []);
|
|
131
|
+
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, [], caller.principal.execution);
|
|
132
132
|
return row;
|
|
133
133
|
});
|
|
134
134
|
/**
|
|
@@ -154,7 +154,7 @@ export function attachmentRuntime(ontology, sql, provider) {
|
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
156
|
const reserved = yield* sql.withTransaction(Effect.gen(function* () {
|
|
157
|
-
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, []);
|
|
157
|
+
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, [], caller.principal.execution);
|
|
158
158
|
yield* sql.unsafe(`SELECT pg_advisory_xact_lock(818724)`);
|
|
159
159
|
const [existing] = yield* sql.unsafe(`SELECT id::text FROM "${ATTACHMENTS_TABLE}"
|
|
160
160
|
WHERE owner_id = $1::uuid AND conversation = $2 AND source_ref = $3`, [caller.principal.id, caller.conversation, input.sourceRef]);
|
|
@@ -272,7 +272,7 @@ export function attachmentRuntime(ontology, sql, provider) {
|
|
|
272
272
|
});
|
|
273
273
|
/** The caller's attachments in this chat and session that are still readable, newest first. */
|
|
274
274
|
const list = (caller) => sql.withTransaction(Effect.gen(function* () {
|
|
275
|
-
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, []);
|
|
275
|
+
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, [], caller.principal.execution);
|
|
276
276
|
const rows = yield* sql.unsafe(`${SELECT} WHERE a.owner_id = $1::uuid AND a.conversation = $2 AND a.branch_id = $3
|
|
277
277
|
AND a.state IN ('ready', 'promoted') AND a.expires_at > now()
|
|
278
278
|
AND (a.session_id IS NULL OR a.session_id = $4)
|
|
@@ -365,7 +365,7 @@ export function attachmentRuntime(ontology, sql, provider) {
|
|
|
365
365
|
`${row.content_type}, ${row.size} bytes.`,
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
|
-
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, found.permissions);
|
|
368
|
+
yield* authorizeBlobOperation(ontology, sql, caller.principal.id, found.permissions, caller.principal.execution);
|
|
369
369
|
const [earlier] = yield* sql.unsafe(`SELECT id::text, promotion FROM "${ATTACHMENTS_TABLE}"
|
|
370
370
|
WHERE sha256 = $1 AND branch_id = $2 AND state = 'promoted' AND id <> $3::uuid
|
|
371
371
|
AND promotion->>'action' = $4 AND promotion->>'deduplicated_from' IS NULL
|
|
@@ -5,4 +5,4 @@ import { PermissionDenied } from "../errors.js";
|
|
|
5
5
|
/** Re-read user and roles in the same transaction that authorizes a storage operation. */
|
|
6
6
|
export declare function authorizeBlobOperation(ontology: Ontology, sql: {
|
|
7
7
|
readonly unsafe: (text: string, args: ReadonlyArray<unknown>) => Effect.Effect<ReadonlyArray<Readonly<Record<string, unknown>>>, SqlError>;
|
|
8
|
-
}, principalId: string, required: ReadonlyArray<string
|
|
8
|
+
}, principalId: string, required: ReadonlyArray<string>, execution?: import("../runtime/workload.js").WorkloadExecution): Effect.Effect<undefined, PermissionDenied | SqlError, never>;
|
|
@@ -2,7 +2,7 @@ import { Effect } from "effect";
|
|
|
2
2
|
import { missingPermission, permissionsOf } from "../permissions.js";
|
|
3
3
|
import { PermissionDenied } from "../errors.js";
|
|
4
4
|
/** Re-read user and roles in the same transaction that authorizes a storage operation. */
|
|
5
|
-
export function authorizeBlobOperation(ontology, sql, principalId, required) {
|
|
5
|
+
export function authorizeBlobOperation(ontology, sql, principalId, required, execution) {
|
|
6
6
|
return Effect.gen(function* () {
|
|
7
7
|
const [catalog] = yield* sql.unsafe(`SELECT hash FROM "_ontology" WHERE id = true FOR SHARE`, []);
|
|
8
8
|
if (catalog?.hash !== ontology.manifest.hash) {
|
|
@@ -10,17 +10,22 @@ export function authorizeBlobOperation(ontology, sql, principalId, required) {
|
|
|
10
10
|
message: "The deployed ontology no longer matches the database catalog.",
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
const users = yield* sql.unsafe(`SELECT kind::text, active FROM "user" WHERE id = $1::uuid FOR SHARE`, [principalId]);
|
|
13
|
+
const users = yield* sql.unsafe(`SELECT kind::text, active, identity_provider, identity_subject FROM "user" WHERE id = $1::uuid FOR SHARE`, [principalId]);
|
|
14
14
|
const user = users[0];
|
|
15
15
|
const roles = yield* sql.unsafe(`SELECT role FROM "_user_roles" WHERE user_id = $1::uuid FOR SHARE`, [principalId]);
|
|
16
16
|
const held = roles.map((row) => String(row.role));
|
|
17
17
|
if (user?.kind === "member") {
|
|
18
18
|
held.push("member");
|
|
19
19
|
}
|
|
20
|
+
const worker = execution !== undefined &&
|
|
21
|
+
user?.kind === "service" &&
|
|
22
|
+
user.identity_provider === "segmentstream-ontology-workload" &&
|
|
23
|
+
user.identity_subject === `pipeline-workers:${execution.workspaceId}`;
|
|
20
24
|
const missing = missingPermission(required, {
|
|
21
|
-
|
|
25
|
+
...(worker ? { execution } : {}),
|
|
26
|
+
permissions: worker ? ["*:*"] : permissionsOf(ontology.roles, held),
|
|
22
27
|
});
|
|
23
|
-
if (!user?.active || missing) {
|
|
28
|
+
if (!user?.active || missing || (execution !== undefined && !worker)) {
|
|
24
29
|
return yield* new PermissionDenied({
|
|
25
30
|
message: "The member cannot use this file operation.",
|
|
26
31
|
...(missing ? { permission: missing } : {}),
|
package/dist/storage/sessions.js
CHANGED
|
@@ -98,7 +98,7 @@ export function blobRuntime(ontology, sql, provider) {
|
|
|
98
98
|
if (!found || row.manifest_hash !== ontology.manifest.hash) {
|
|
99
99
|
return yield* conflict();
|
|
100
100
|
}
|
|
101
|
-
yield* authorizeBlobOperation(ontology, sql, principal.id, found.permissions);
|
|
101
|
+
yield* authorizeBlobOperation(ontology, sql, principal.id, found.permissions, principal.execution);
|
|
102
102
|
return row;
|
|
103
103
|
});
|
|
104
104
|
const begin = (input, principal) => Effect.gen(function* () {
|
|
@@ -129,7 +129,7 @@ export function blobRuntime(ontology, sql, provider) {
|
|
|
129
129
|
const claim = randomUUID();
|
|
130
130
|
const key = `revisions/${blobId}`;
|
|
131
131
|
const reservation = yield* sql.withTransaction(Effect.gen(function* () {
|
|
132
|
-
yield* authorizeBlobOperation(ontology, sql, principal.id, found.permissions);
|
|
132
|
+
yield* authorizeBlobOperation(ontology, sql, principal.id, found.permissions, principal.execution);
|
|
133
133
|
if (input.recordId) {
|
|
134
134
|
yield* makeOntologyRuntime(ontology, sql).get({
|
|
135
135
|
entity: input.entity,
|
|
@@ -310,7 +310,7 @@ export function blobRuntime(ontology, sql, provider) {
|
|
|
310
310
|
});
|
|
311
311
|
}
|
|
312
312
|
const blob = yield* sql.withTransaction(Effect.gen(function* () {
|
|
313
|
-
yield* authorizeBlobOperation(ontology, sql, principal.id, [`${entityName}:read`]);
|
|
313
|
+
yield* authorizeBlobOperation(ontology, sql, principal.id, [`${entityName}:read`], principal.execution);
|
|
314
314
|
let revisionId = input.revisionId;
|
|
315
315
|
if (!revisionId) {
|
|
316
316
|
const record = yield* makeOntologyRuntime(ontology, sql).get({
|
package/dist/storage/writes.js
CHANGED
|
@@ -39,7 +39,7 @@ export function resolveBlobWrites(context, entity, recordId, values, creating) {
|
|
|
39
39
|
}
|
|
40
40
|
return context.run(statement, params);
|
|
41
41
|
},
|
|
42
|
-
}, context.scope.principalId, authored ? [authored.permission] : generatedActionPermissions(actionEntity, actionName)).pipe(Effect.catchTag("PermissionDenied", () => Effect.fail(reject())));
|
|
42
|
+
}, context.scope.principalId, authored ? [authored.permission] : generatedActionPermissions(actionEntity, actionName), context.scope.execution).pipe(Effect.catchTag("PermissionDenied", () => Effect.fail(reject())));
|
|
43
43
|
const params = new Params();
|
|
44
44
|
const rows = yield* context.run(`SELECT u.*, b.id::text AS blob_id, b.filename, b.content_type, b.size::text AS size,
|
|
45
45
|
to_char(b.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS created_at
|
package/dist/warehouse-backed.js
CHANGED
|
@@ -253,6 +253,10 @@ function checkMeasures(input) {
|
|
|
253
253
|
!["decimal", "currency", "percent"].includes(measure.format)) {
|
|
254
254
|
report("invalid-measure", field, "Invalid measure format.");
|
|
255
255
|
}
|
|
256
|
+
if (measure.align !== undefined) {
|
|
257
|
+
report("invalid-measure", field, "Entity measures take no align; a measure that aligns the properties of several " +
|
|
258
|
+
"entities belongs in defineOntology({ measures }).");
|
|
259
|
+
}
|
|
256
260
|
let remaining = 10_000;
|
|
257
261
|
/** The balance properties of the measure's aggregates and of the measures it references. */
|
|
258
262
|
const balances = new Set();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/ontology",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Define a SegmentStream workspace app's ontology: entities, relationships, roles and actions, written with Effect.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|