@segmentstream-cloud/ontology 0.14.0 → 0.15.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 +35 -10
- package/dist/agent-chat.d.ts +75 -0
- package/dist/agent-chat.js +176 -0
- package/dist/analyze.d.ts +6 -2
- package/dist/analyze.js +29 -18
- package/dist/authoring.d.ts +6 -3
- package/dist/authoring.js +6 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/permissions.d.ts +11 -2
- package/dist/permissions.js +15 -3
- package/dist/replication/baseline.js +1 -1
- package/dist/replication/model.js +2 -2
- package/dist/replication/schema.js +1 -1
- package/dist/runtime/agents.d.ts +1 -0
- package/dist/runtime/agents.js +160 -0
- package/dist/runtime/generated-actions.d.ts +2 -1
- package/dist/runtime/generated-actions.js +18 -13
- package/dist/service/agent/channel.d.ts +16 -0
- package/dist/service/agent/channel.js +88 -0
- package/dist/service/agent/protocol.d.ts +14 -0
- package/dist/service/agent/protocol.js +26 -0
- package/dist/service/agent/server.d.ts +2 -1
- package/dist/service/agent/server.js +24 -2
- package/dist/service/aggregate-platform.d.ts +5 -0
- package/dist/service/aggregate-platform.js +1 -1
- package/dist/service/config.d.ts +10 -0
- package/dist/service/config.js +8 -0
- package/dist/service/handlers.d.ts +2 -1
- package/dist/service/handlers.js +2 -2
- package/dist/service/mcp/server.d.ts +3 -0
- package/dist/service/mcp/server.js +1 -1
- package/dist/service/operations.d.ts +6 -2
- package/dist/service/operations.js +13 -5
- package/dist/service/runtime-contract.d.ts +26 -0
- package/dist/service/service.js +7 -1
- package/package.json +1 -1
package/dist/service/config.js
CHANGED
|
@@ -34,6 +34,11 @@ export const RUNTIME_IDENTITY_ENVIRONMENT_VARIABLES = {
|
|
|
34
34
|
audience: "SEGMENTSTREAM_RUNTIME_OIDC_AUDIENCE",
|
|
35
35
|
projectId: "VERCEL_PROJECT_ID",
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* The project's production domain, which Vercel sets: the agent of a deployment with runtime identity
|
|
39
|
+
* takes the messages of `agent_chat.send_message` there.
|
|
40
|
+
*/
|
|
41
|
+
export const AGENT_HOST_VARIABLE = "VERCEL_PROJECT_PRODUCTION_URL";
|
|
37
42
|
/** The service cannot start: its configuration is incomplete or invalid. */
|
|
38
43
|
export class OntologyServiceConfigError extends Schema.TaggedError()("OntologyServiceConfigError", { message: Schema.String }) {
|
|
39
44
|
}
|
|
@@ -160,6 +165,9 @@ export function configFromEnv(environment = process.env) {
|
|
|
160
165
|
: {}),
|
|
161
166
|
...(neonAuth === undefined ? {} : { neonAuth }),
|
|
162
167
|
...(runtimeIdentity === undefined ? {} : { runtimeIdentity }),
|
|
168
|
+
...(runtimeIdentity === undefined || read(AGENT_HOST_VARIABLE) === ""
|
|
169
|
+
? {}
|
|
170
|
+
: { agentUrl: normalizeUrl(AGENT_HOST_VARIABLE, `https://${read(AGENT_HOST_VARIABLE)}`) }),
|
|
163
171
|
...(localConnections === undefined ? {} : { localConnections }),
|
|
164
172
|
};
|
|
165
173
|
}
|
|
@@ -4,6 +4,7 @@ import type { Aggregates } from "./aggregate.js";
|
|
|
4
4
|
import { type BuiltApi } from "./api.js";
|
|
5
5
|
import type { AfterCommitHook } from "./config.js";
|
|
6
6
|
import type { ReplicationKick } from "./replication.js";
|
|
7
|
+
import type { AgentChannelFactory } from "./agent/channel.js";
|
|
7
8
|
import type { ConnectionTransportFactory } from "./connections.js";
|
|
8
9
|
import { type RateLimit } from "./rate-limit.js";
|
|
9
10
|
import { OntologyBackend } from "./runtime-contract.js";
|
|
@@ -13,4 +14,4 @@ import { OntologyBackend } from "./runtime-contract.js";
|
|
|
13
14
|
*/
|
|
14
15
|
export declare const SIGN_IN_CHECK_LIMIT: RateLimit;
|
|
15
16
|
/** The handlers of every group of the API; they require {@link OntologyBackend}. */
|
|
16
|
-
export declare function handlersLayer(built: BuiltApi, hooks: ReadonlyMap<string, AfterCommitHook>, connections: ConnectionTransportFactory, replicate: ReplicationKick, aggregates: Aggregates): Layer.Layer<HttpApiGroup.Service<"ontology", string>, never, OntologyBackend>;
|
|
17
|
+
export declare function handlersLayer(built: BuiltApi, hooks: ReadonlyMap<string, AfterCommitHook>, connections: ConnectionTransportFactory, replicate: ReplicationKick, aggregates: Aggregates, agentChannel: AgentChannelFactory | undefined): Layer.Layer<HttpApiGroup.Service<"ontology", string>, never, OntologyBackend>;
|
package/dist/service/handlers.js
CHANGED
|
@@ -216,8 +216,8 @@ function storageHandlers(built) {
|
|
|
216
216
|
}));
|
|
217
217
|
}
|
|
218
218
|
/** The handlers of every group of the API; they require {@link OntologyBackend}. */
|
|
219
|
-
export function handlersLayer(built, hooks, connections, replicate, aggregates) {
|
|
220
|
-
const operationsOf = (backend) => makeOperations(backend, built.plan, hooks, connections, replicate);
|
|
219
|
+
export function handlersLayer(built, hooks, connections, replicate, aggregates, agentChannel) {
|
|
220
|
+
const operationsOf = (backend) => makeOperations(backend, built.plan, hooks, connections, replicate, agentChannel);
|
|
221
221
|
const layers = [
|
|
222
222
|
serviceHandlers(built, operationsOf, aggregates.model),
|
|
223
223
|
...(built.plan.ontology.manifest.entities.some((entity) => entity.properties.some((property) => property.type === "blob"))
|
|
@@ -4,6 +4,7 @@ import type { Aggregates } from "../aggregate.js";
|
|
|
4
4
|
import type { BuiltApi } from "../api.js";
|
|
5
5
|
import { type TokenVerifier } from "../authentication.js";
|
|
6
6
|
import type { AfterCommitHook } from "../config.js";
|
|
7
|
+
import type { AgentChannelFactory } from "../agent/channel.js";
|
|
7
8
|
import type { ConnectionTransportFactory } from "../connections.js";
|
|
8
9
|
import { RequestId } from "../errors.js";
|
|
9
10
|
import type { ReplicationKick } from "../replication.js";
|
|
@@ -23,6 +24,8 @@ export interface McpServerOptions {
|
|
|
23
24
|
readonly verifyRuntime?: RuntimeTokenVerifier | undefined;
|
|
24
25
|
/** The facade the aggregate routes run; the aggregate tool runs its model-level aggregate. */
|
|
25
26
|
readonly aggregates: Aggregates;
|
|
27
|
+
/** The workspace's agent, for `agent_chat.send_message`; `undefined` without one. */
|
|
28
|
+
readonly agentChannel?: AgentChannelFactory | undefined;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* The ontology's MCP server at `POST /mcp`: Streamable HTTP without sessions, so any instance can
|
|
@@ -417,7 +417,7 @@ export function mcpLayer(options) {
|
|
|
417
417
|
const server = {
|
|
418
418
|
built: options.built,
|
|
419
419
|
tools,
|
|
420
|
-
operations: makeOperations(backend, options.built.plan, options.hooks, options.connections, options.replicate),
|
|
420
|
+
operations: makeOperations(backend, options.built.plan, options.hooks, options.connections, options.replicate, options.agentChannel),
|
|
421
421
|
aggregate: options.aggregates.model,
|
|
422
422
|
};
|
|
423
423
|
yield* router.add("POST", MCP_PATH, handlePost(server, options.verifyToken, options.verifyRuntime, backend));
|
|
@@ -5,6 +5,7 @@ import type { Query, QueryResult } from "../query.js";
|
|
|
5
5
|
import { CurrentPrincipal } from "./authentication.js";
|
|
6
6
|
import type { AfterCommitHook } from "./config.js";
|
|
7
7
|
import type { ReplicationKick } from "./replication.js";
|
|
8
|
+
import type { AgentChannelFactory } from "./agent/channel.js";
|
|
8
9
|
import { type ConnectionTransportFactory } from "./connections.js";
|
|
9
10
|
import { InvalidActionInput, RequestId } from "./errors.js";
|
|
10
11
|
import type { ApiPlan, PlannedAction, PlannedEntity, Traversal } from "./plan.js";
|
|
@@ -59,8 +60,11 @@ export interface Caller {
|
|
|
59
60
|
readonly permissions: ReadonlyArray<string>;
|
|
60
61
|
}
|
|
61
62
|
type Backend = OntologyBackend["Service"];
|
|
62
|
-
/**
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The operations of a planned ontology, bound to its backend, after-commit hooks, connections and,
|
|
65
|
+
* on deployments with one, the workspace's agent.
|
|
66
|
+
*/
|
|
67
|
+
export declare function makeOperations(backend: Backend, plan: ApiPlan, hooks: ReadonlyMap<string, AfterCommitHook>, connections: ConnectionTransportFactory, replicate: ReplicationKick, agentChannel?: AgentChannelFactory): {
|
|
64
68
|
/** A page of records. Requires `read` on the entity and on every entity the query reaches. */
|
|
65
69
|
find: (planned: PlannedEntity, query: Query) => Effect.Effect<QueryResult<Readonly<Record<string, unknown>>>, PermissionDenied | import("../index.js").InvalidQuery, CurrentPrincipal>;
|
|
66
70
|
/** One record. Requires `read` on the entity. */
|
|
@@ -6,6 +6,7 @@ import { PermissionDenied } from "../errors.js";
|
|
|
6
6
|
import { toCamelCase } from "../names.js";
|
|
7
7
|
import { missingPermission, permissionOf, READ_VERB } from "../permissions.js";
|
|
8
8
|
import { CurrentPrincipal } from "./authentication.js";
|
|
9
|
+
import { AgentChannel } from "../agent-chat.js";
|
|
9
10
|
import { OIDC_TOKEN_HEADER } from "./connections.js";
|
|
10
11
|
import { InvalidActionInput, RequestId } from "./errors.js";
|
|
11
12
|
/**
|
|
@@ -145,12 +146,12 @@ function traverseOf(backend, targets, planned, traversal, id, query) {
|
|
|
145
146
|
return page(yield* backend.find({ entity: traversal.target, query: { ...query, where } }));
|
|
146
147
|
});
|
|
147
148
|
}
|
|
148
|
-
function runActionOf(backend, ontology, hooks, connections, replicate) {
|
|
149
|
+
function runActionOf(backend, ontology, hooks, connections, replicate, agentChannel) {
|
|
149
150
|
return (planned, { name, generated }, call) => Effect.gen(function* () {
|
|
150
151
|
const principal = yield* CurrentPrincipal;
|
|
151
152
|
const requestId = yield* RequestId;
|
|
152
153
|
const hook = hooks.get(`${planned.name}.${name}`);
|
|
153
|
-
const
|
|
154
|
+
const run = backend.runAction({
|
|
154
155
|
entity: planned.name,
|
|
155
156
|
action: name,
|
|
156
157
|
input: call.input,
|
|
@@ -175,6 +176,10 @@ function runActionOf(backend, ontology, hooks, connections, replicate) {
|
|
|
175
176
|
}
|
|
176
177
|
: {}),
|
|
177
178
|
});
|
|
179
|
+
// Built-in actions reach the workspace's agent through it, when the deployment has one.
|
|
180
|
+
const outcome = yield* agentChannel === undefined
|
|
181
|
+
? run
|
|
182
|
+
: run.pipe(Effect.provideService(AgentChannel, agentChannel({ identityToken: call.identityToken, store: backend.agents })));
|
|
178
183
|
const { issues } = outcome.validation;
|
|
179
184
|
if (!outcome.validation.ok && !call.validateOnly) {
|
|
180
185
|
return yield* new InvalidActionInput({ issues });
|
|
@@ -208,8 +213,11 @@ function callerOf(backend) {
|
|
|
208
213
|
};
|
|
209
214
|
});
|
|
210
215
|
}
|
|
211
|
-
/**
|
|
212
|
-
|
|
216
|
+
/**
|
|
217
|
+
* The operations of a planned ontology, bound to its backend, after-commit hooks, connections and,
|
|
218
|
+
* on deployments with one, the workspace's agent.
|
|
219
|
+
*/
|
|
220
|
+
export function makeOperations(backend, plan, hooks, connections, replicate, agentChannel) {
|
|
213
221
|
const targets = relationshipTargets(plan);
|
|
214
222
|
return {
|
|
215
223
|
/** A page of records. Requires `read` on the entity and on every entity the query reaches. */
|
|
@@ -221,7 +229,7 @@ export function makeOperations(backend, plan, hooks, connections, replicate) {
|
|
|
221
229
|
/** The records a relationship reaches from one record, as a page of the target entity. */
|
|
222
230
|
traverse: (planned, traversal, id, query) => traverseOf(backend, targets, planned, traversal, id, query),
|
|
223
231
|
/** Runs an action as the caller, with the action's after-commit hook and connections. */
|
|
224
|
-
runAction: runActionOf(backend, plan.ontology, hooks, connections, replicate),
|
|
232
|
+
runAction: runActionOf(backend, plan.ontology, hooks, connections, replicate, agentChannel),
|
|
225
233
|
/** The caller's user, roles and permissions. It needs no permission. */
|
|
226
234
|
caller: callerOf(backend),
|
|
227
235
|
};
|
|
@@ -180,6 +180,13 @@ export interface AgentMember {
|
|
|
180
180
|
readonly name: string;
|
|
181
181
|
readonly principal: Principal;
|
|
182
182
|
}
|
|
183
|
+
/** A group chat the agent saw, as `POST /api/ontology/agent/chats/seen` answers. */
|
|
184
|
+
export interface SeenChat {
|
|
185
|
+
readonly id: string;
|
|
186
|
+
readonly title: string;
|
|
187
|
+
readonly purpose: string | null;
|
|
188
|
+
readonly whenToUse: string | null;
|
|
189
|
+
}
|
|
183
190
|
/**
|
|
184
191
|
* Where the app's agent keeps pairing codes and the identities they create: the runtime's agent
|
|
185
192
|
* tables in the workspace database. Codes are stored only as SHA-256 hashes. Expiry is checked
|
|
@@ -218,6 +225,25 @@ export interface AgentStore {
|
|
|
218
225
|
readonly member: (userId: string) => Effect.Effect<AgentMember | undefined>;
|
|
219
226
|
/** The member a runtime caller acts for: active, and with a live identity. */
|
|
220
227
|
readonly actingMember: (userId: string) => Effect.Effect<Principal | undefined>;
|
|
228
|
+
/**
|
|
229
|
+
* Records a message the agent saw in a group chat: creates the `agent_chat` or keeps its title
|
|
230
|
+
* current, records when the chat last had a message (at most every 15 minutes, so that history
|
|
231
|
+
* keeps it without a row per message), links the writer to its members when they are an active
|
|
232
|
+
* member, and unlinks `leftMemberId`, a member who left, when linked. With `agentLeft`, the agent
|
|
233
|
+
* itself left the chat: every member is unlinked, and the chat stays. With `migratedFrom`, the
|
|
234
|
+
* chat had that id before it became a supergroup: its record moves to `externalId`, or, when both
|
|
235
|
+
* ids have one, the new record takes the old one's members, and its purpose and when to use where
|
|
236
|
+
* it has none. Concurrent calls for one chat leave one record.
|
|
237
|
+
*/
|
|
238
|
+
readonly seeChat: (input: {
|
|
239
|
+
readonly provider: string;
|
|
240
|
+
readonly externalId: string;
|
|
241
|
+
readonly title: string;
|
|
242
|
+
readonly memberId?: string;
|
|
243
|
+
readonly leftMemberId?: string;
|
|
244
|
+
readonly agentLeft?: true;
|
|
245
|
+
readonly migratedFrom?: string;
|
|
246
|
+
}) => Effect.Effect<SeenChat>;
|
|
221
247
|
}
|
|
222
248
|
declare const OntologyBackend_base: Context.ServiceClass<OntologyBackend, "@segmentstream-cloud/ontology/OntologyBackend", {
|
|
223
249
|
readonly blobs?: BlobRuntime;
|
package/dist/service/service.js
CHANGED
|
@@ -4,6 +4,7 @@ import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
|
4
4
|
import { storageMaintenanceAuthenticationLayer } from "../storage/maintenance-auth.js";
|
|
5
5
|
import { nameToWire } from "../authoring-names.js";
|
|
6
6
|
import { sqlServiceRuntime } from "../runtime/service-binding.js";
|
|
7
|
+
import { deployedAgentChannel } from "./agent/channel.js";
|
|
7
8
|
import { agentLayer } from "./agent/server.js";
|
|
8
9
|
import { makeAggregates } from "./aggregate.js";
|
|
9
10
|
import { platformAggregates } from "./aggregate-platform.js";
|
|
@@ -89,11 +90,15 @@ export function createOntologyService(ontology, config) {
|
|
|
89
90
|
? deployedConnections(config)
|
|
90
91
|
: localConnections(config.localConnections));
|
|
91
92
|
const replicate = deployedReplication(config);
|
|
93
|
+
// The agent's runtime identity is configured only on deployments that have an agent.
|
|
94
|
+
const agentChannel = config.runtimeIdentity === undefined || config.agentUrl === undefined
|
|
95
|
+
? undefined
|
|
96
|
+
: deployedAgentChannel(config.agentUrl);
|
|
92
97
|
const aggregates = makeAggregates({
|
|
93
98
|
manifest: ontology.manifest,
|
|
94
99
|
transport: platformAggregates({ ...config, manifestHash: ontology.manifest.hash }),
|
|
95
100
|
});
|
|
96
|
-
const routes = HttpApiBuilder.layer(built.api).pipe(Layer.provide(handlersLayer(built, hooks, connections, replicate, aggregates)), Layer.provide([
|
|
101
|
+
const routes = HttpApiBuilder.layer(built.api).pipe(Layer.provide(handlersLayer(built, hooks, connections, replicate, aggregates, agentChannel)), Layer.provide([
|
|
97
102
|
authenticationLayer(verifyToken, exchangeSession, verifyRuntime),
|
|
98
103
|
ErrorResponsesLive,
|
|
99
104
|
storageMaintenanceAuthenticationLayer(config.storageMaintenanceKey),
|
|
@@ -108,6 +113,7 @@ export function createOntologyService(ontology, config) {
|
|
|
108
113
|
verifyRuntime,
|
|
109
114
|
replicate,
|
|
110
115
|
aggregates,
|
|
116
|
+
agentChannel,
|
|
111
117
|
}), oauthLayer({ verifyToken }));
|
|
112
118
|
const agent = agentLayer({ verifyToken, verifyRuntime, exchangeSession });
|
|
113
119
|
const application = Layer.mergeAll(routes, mcp, agent, DocsRoute, UnmatchedRoutes).pipe(Layer.provide(backend), Layer.provide(HttpServer.layerServices));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/ontology",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|