@segmentstream-cloud/ontology 0.13.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 +65 -3
- 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 +7 -2
- package/dist/index.js +5 -1
- package/dist/migrate/apply.js +2 -1
- package/dist/migrate/plan.js +21 -9
- package/dist/migrate/storage.d.ts +12 -0
- package/dist/migrate/storage.js +48 -0
- package/dist/names.d.ts +1 -1
- package/dist/names.js +1 -0
- 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 +10 -0
- package/dist/runtime/agents.js +305 -0
- package/dist/runtime/generated-actions.d.ts +2 -1
- package/dist/runtime/generated-actions.js +18 -13
- package/dist/runtime/service-binding.js +2 -0
- package/dist/service/agent/channel.d.ts +16 -0
- package/dist/service/agent/channel.js +88 -0
- package/dist/service/agent/protocol.d.ts +61 -0
- package/dist/service/agent/protocol.js +85 -0
- package/dist/service/agent/server.d.ts +28 -0
- package/dist/service/agent/server.js +257 -0
- package/dist/service/aggregate-platform.d.ts +5 -0
- package/dist/service/aggregate-platform.js +1 -1
- package/dist/service/authentication.d.ts +16 -5
- package/dist/service/authentication.js +51 -6
- package/dist/service/config.d.ts +29 -2
- package/dist/service/config.js +52 -2
- package/dist/service/errors.d.ts +6 -0
- package/dist/service/errors.js +16 -0
- package/dist/service/handlers.d.ts +2 -1
- package/dist/service/handlers.js +2 -2
- package/dist/service/mcp/server.d.ts +6 -0
- package/dist/service/mcp/server.js +15 -10
- package/dist/service/operations.d.ts +6 -2
- package/dist/service/operations.js +13 -5
- package/dist/service/runtime-contract.d.ts +94 -0
- package/dist/service/runtime-identity.d.ts +59 -0
- package/dist/service/runtime-identity.js +102 -0
- package/dist/service/service.d.ts +2 -2
- package/dist/service/service.js +24 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,9 @@ one Effect version.
|
|
|
28
28
|
`ontology/` is a package of its own. The SDK is an ES module, so `ontology/package.json`
|
|
29
29
|
must set `"type": "module"`.
|
|
30
30
|
|
|
31
|
+
SDK 0.14.0 lets the app's own agent call the service as a paired member (see
|
|
32
|
+
[Agents](#agents)). Its migration adds the agent's runtime tables.
|
|
33
|
+
|
|
31
34
|
SDK 0.12.0 adds warehouse replication enabled by default for all stored business entities,
|
|
32
35
|
fields and business relationships. Existing history captures immutable snapshots and the
|
|
33
36
|
service wakes the publisher after commits. Upgrade and redeploy to activate it; workspace
|
|
@@ -140,7 +143,8 @@ export default defineOntology({
|
|
|
140
143
|
- `rel.one` and `rel.many` declare relationships. Between stored entities, `rel.one` adds a
|
|
141
144
|
key and `rel.many` a join table. With a warehouse-backed end, `rel.one` declares one `on`
|
|
142
145
|
pair and is matched by value. `User` is the built-in entity for app users and service
|
|
143
|
-
principals
|
|
146
|
+
principals; `AgentChat` (`agent_chat`) is the built-in entity for the group chats of the
|
|
147
|
+
app's agent (see [Agents](#agents)). Every ontology includes both.
|
|
144
148
|
- `role(name, { description, permissions })` declares a role: a named set of permissions.
|
|
145
149
|
`member` is built in: every active app user holds it, with the permissions of
|
|
146
150
|
`defineOntology({ member })`.
|
|
@@ -159,7 +163,9 @@ export default defineOntology({
|
|
|
159
163
|
|
|
160
164
|
A permission is `<entity>:<verb>`. The verbs are `read`, the generated `create`, `update`,
|
|
161
165
|
`delete`, `link` and `unlink`, and the names of authored actions. A role may use `*` for the
|
|
162
|
-
entity, the verb or both: `lead:*`, `*:read`, `*:*`.
|
|
166
|
+
entity, the verb or both: `lead:*`, `*:read`, `*:*`. An entity wildcard never covers the
|
|
167
|
+
built-in `agent_chat`: reading or changing it needs a permission that names it, such as
|
|
168
|
+
`agent_chat:read`.
|
|
163
169
|
|
|
164
170
|
- Reads require `<entity>:read`: lists, queries, records, history, both entities of a
|
|
165
171
|
relationship traversal, and every entity an aggregate touches. `include` and a filter through a relationship require `read` on
|
|
@@ -211,9 +217,13 @@ one is missing or invalid, and never repeats the database URL.
|
|
|
211
217
|
| `NEON_AUTH_BASE_URL` | Optional. The workspace's Neon Auth URL; its origin issues users' tokens. |
|
|
212
218
|
| `NEON_AUTH_JWKS_URL` | Optional, set with the base URL. The keys that verify those tokens. |
|
|
213
219
|
| `SEGMENTSTREAM_APP_CREDENTIAL` | Optional. The workspace app credential; without it, aggregates answer 503. |
|
|
220
|
+
| `SEGMENTSTREAM_RUNTIME_OIDC_ISSUER` | Optional. The team's Vercel OIDC issuer, for the app's agent. |
|
|
221
|
+
| `SEGMENTSTREAM_RUNTIME_OIDC_AUDIENCE` | Optional, set with the issuer. The audience of the team's tokens. |
|
|
222
|
+
| `VERCEL_PROJECT_PRODUCTION_URL` | Set by Vercel. With the runtime pair, where `agent_chat.send_message` reaches the agent. |
|
|
214
223
|
|
|
215
224
|
The platform sets these variables in the workspace deployment, the Neon Auth pair once the
|
|
216
|
-
workspace has Neon Auth.
|
|
225
|
+
workspace has Neon Auth and the runtime pair on deployments with an agent. With the runtime
|
|
226
|
+
pair, Vercel's `VERCEL_PROJECT_ID` must be set too.
|
|
217
227
|
|
|
218
228
|
### Authentication
|
|
219
229
|
|
|
@@ -249,6 +259,58 @@ address (`x-real-ip`, else the first `x-forwarded-for` hop) ten checks a minute,
|
|
|
249
259
|
answers 429 `rate_limited` with `retry-after`. `checkSignIn(ontology, email)` is the same
|
|
250
260
|
lookup for code that holds a SQL client.
|
|
251
261
|
|
|
262
|
+
## Agents
|
|
263
|
+
|
|
264
|
+
The app's own agent runs in the same Vercel project as the service and calls it as the
|
|
265
|
+
deployment: `Authorization: Bearer <Vercel OIDC token>`. The service accepts the token when it
|
|
266
|
+
is RS256 from `SEGMENTSTREAM_RUNTIME_OIDC_ISSUER` (keys at `<issuer>/.well-known/jwks`), for
|
|
267
|
+
`SEGMENTSTREAM_RUNTIME_OIDC_AUDIENCE`, with `project_id` equal to `VERCEL_PROJECT_ID` and
|
|
268
|
+
`environment` `production`. Without the issuer and audience such tokens are a 401.
|
|
269
|
+
|
|
270
|
+
The agent acts for a member by adding `X-SegmentStream-Act-As: <user id>` on any route,
|
|
271
|
+
`/mcp` included: the request runs with the member's current roles and history names the
|
|
272
|
+
member, as if they had called. A member who is inactive or has no live pairing is a 403.
|
|
273
|
+
Without the header the agent may call only the agent routes:
|
|
274
|
+
|
|
275
|
+
| Route | Caller | Does |
|
|
276
|
+
| ------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
277
|
+
| `POST /api/ontology/agent/pairings` | agent | `{ provider, external_id, display? }` → `{ code, expires_at }` |
|
|
278
|
+
| `GET /api/ontology/agent/pairings/{code}` | member | `{ provider, display, expires_at }`, for the connect page |
|
|
279
|
+
| `POST /api/ontology/agent/pairings/{code}/approve` | member | Pairs the account with the member → `{ identity }` |
|
|
280
|
+
| `GET /api/ontology/agent/identities/{provider}/{external_id}` | agent | `{ user: { id, name }, roles }`; 404 unpaired, 403 revoked, inactive |
|
|
281
|
+
| `GET /api/ontology/agent/identities` | member | `{ identities }`: the member's live identities |
|
|
282
|
+
| `DELETE /api/ontology/agent/identities/{id}` | member | Revokes one of them: 204 |
|
|
283
|
+
| `POST /api/ontology/agent/chats/seen` | agent | `{ provider, externalId, title, memberId?, leftMemberId?, agentLeft?, migratedFrom? }` → `{ id, title, purpose, whenToUse }` |
|
|
284
|
+
|
|
285
|
+
A pairing code is eight characters without look-alikes (no 0, O, 1, I or L), stored as its
|
|
286
|
+
SHA-256, valid ten minutes and used once. Each instance allows a client address 60 new
|
|
287
|
+
pairings a minute. An account and a member each have one live identity per provider:
|
|
288
|
+
approving a code revokes the account's earlier identity and the member's earlier one on the
|
|
289
|
+
same provider. Member routes take a Neon Auth session or token and refuse API keys and the
|
|
290
|
+
agent, even acting as a member, so only members pair. Pairings and identities live in the
|
|
291
|
+
runtime tables `_agent_pairings` and `_agent_identities`; the migration creates them in a
|
|
292
|
+
database an earlier SDK migrated.
|
|
293
|
+
|
|
294
|
+
The built-in entity `agent_chat` keeps the group chats the agent is in. The agent reports each
|
|
295
|
+
message it sees there to `chats/seen`, which creates the chat or keeps its title current,
|
|
296
|
+
records `lastMessageAt` at most every 15 minutes, links a paired, active writer to
|
|
297
|
+
`members` and unlinks `leftMemberId`, a paired member who left, when linked, or every member
|
|
298
|
+
with `agentLeft: true`, when the agent itself left (the chat stays). With `migratedFrom`, the
|
|
299
|
+
old id of a group that became a supergroup, it moves the old record to `externalId`, or, when
|
|
300
|
+
both are recorded, moves the members and a missing `purpose` and `whenToUse` to the new one and
|
|
301
|
+
deletes the old one. `provider`, `externalId`, `title` and `lastMessageAt` are
|
|
302
|
+
managed. Members set `purpose` and `whenToUse` with the generated
|
|
303
|
+
`agent_chat.update`, the chat's only generated action. The built-in action
|
|
304
|
+
`agent_chat.send_message` (`{ message, chat?, person? }`, `message` at most 4000 characters) asks the agent to write to the chat
|
|
305
|
+
given, else to the person's one chat, else to their paired private chat; a person in several
|
|
306
|
+
chats is refused with the candidates, or only their number without `agent_chat:read`. The service posts the message to the agent at
|
|
307
|
+
`https://<VERCEL_PROJECT_PRODUCTION_URL>/eve/v1/telegram/messages` with the deployment's OIDC
|
|
308
|
+
token, naming the requester with their `id`, `name`, `kind`, `roles` and `actAs`, true for a
|
|
309
|
+
member with a live pairing, whom the agent's turn can act as; without the runtime
|
|
310
|
+
pair, or when the agent refuses, the action is rejected with the reason. Both actions need their own permission (`agent_chat:update`,
|
|
311
|
+
`agent_chat:send_message`), which no role holds until the workspace grants it, and reading
|
|
312
|
+
chats needs `agent_chat:read` by name: `*:read` and `*:*` do not cover `agent_chat`.
|
|
313
|
+
|
|
252
314
|
## The `segmentstream-ontology` binary
|
|
253
315
|
|
|
254
316
|
Run the binary in the `ontology/` folder. The entry defaults to `./index.ts`. The binary
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Context, Effect, Schema } from "effect";
|
|
2
|
+
import { ActionRejected } from "./errors.js";
|
|
3
|
+
/** The only channel the agent delivers to so far, and the provider of its chats. */
|
|
4
|
+
export declare const TELEGRAM = "telegram";
|
|
5
|
+
/**
|
|
6
|
+
* What `agent_chat.send_message` asks the agent to deliver: the body of the agent's
|
|
7
|
+
* `POST /eve/v1/telegram/messages`. A Telegram private chat's id is the person's Telegram user id.
|
|
8
|
+
*/
|
|
9
|
+
export interface AgentMessage {
|
|
10
|
+
readonly target: {
|
|
11
|
+
readonly kind: "group" | "private";
|
|
12
|
+
readonly provider: typeof TELEGRAM;
|
|
13
|
+
readonly chatId: string;
|
|
14
|
+
};
|
|
15
|
+
/** The request for the agent, written as an instruction to it. */
|
|
16
|
+
readonly message: string;
|
|
17
|
+
/** Whom the agent should address, when the caller named someone. */
|
|
18
|
+
readonly person: {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
} | null;
|
|
22
|
+
/** Who asked: the principal that ran the action. */
|
|
23
|
+
readonly requester: {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly kind: "member" | "service";
|
|
27
|
+
/** The role names the principal holds, which pick the model of a session the request starts. */
|
|
28
|
+
readonly roles: ReadonlyArray<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the agent can act as the requester in the turn: a member with a live paired identity.
|
|
31
|
+
* Otherwise the turn runs without a member and has no ontology.
|
|
32
|
+
*/
|
|
33
|
+
readonly actAs: boolean;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
declare const AgentChannel_base: Context.ServiceClass<AgentChannel, "@segmentstream-cloud/ontology/AgentChannel", {
|
|
37
|
+
/** The Telegram user id of the member's live paired identity: their private chat's id. */
|
|
38
|
+
readonly privateChat: (userId: string) => Effect.Effect<string | undefined>;
|
|
39
|
+
/** Hands the message to the agent; a refusal or failure explains itself to the caller. */
|
|
40
|
+
readonly send: (message: AgentMessage) => Effect.Effect<void, ActionRejected>;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* The workspace's agent, as `agent_chat.send_message` reaches it. The ontology service provides it to
|
|
44
|
+
* actions only on deployments with an agent; without it, the action is refused.
|
|
45
|
+
*/
|
|
46
|
+
export declare class AgentChannel extends AgentChannel_base {
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The built-in agent chat: a group chat the workspace's agent is in. Every ontology includes it,
|
|
50
|
+
* after `User`. The agent records each chat it sees and the members it sees writing there, through
|
|
51
|
+
* `POST /api/ontology/agent/chats/seen`; members say what a chat is for with the generated `update`,
|
|
52
|
+
* which changes only `purpose` and `whenToUse`. `send_message` asks the agent to write to a chat.
|
|
53
|
+
*/
|
|
54
|
+
export declare const AgentChat: import("./entity.js").Entity<"agent_chat", {
|
|
55
|
+
readonly provider: import("./property.js").Property<string, false, false>;
|
|
56
|
+
readonly externalId: import("./property.js").Property<string, false, false>;
|
|
57
|
+
readonly title: import("./property.js").Property<string, false, false>;
|
|
58
|
+
readonly lastMessageAt: Schema.optionalKey<import("./property.js").Property<string, false, false>>;
|
|
59
|
+
readonly purpose: Schema.optionalKey<import("./property.js").Property<string, false, false>>;
|
|
60
|
+
readonly whenToUse: Schema.optionalKey<import("./property.js").Property<string, false, false>>;
|
|
61
|
+
}, {
|
|
62
|
+
readonly members: import("./relationship.js").ManyRelationship;
|
|
63
|
+
}, {
|
|
64
|
+
readonly sendMessage: import("./action.js").Action<Schema.Struct<{
|
|
65
|
+
readonly message: import("./property.js").Property<string, false, false>;
|
|
66
|
+
readonly chat: Schema.optionalKey<Schema.Codec<string, string, never, never>>;
|
|
67
|
+
readonly person: Schema.optionalKey<Schema.Codec<string, string, never, never>>;
|
|
68
|
+
}>, {
|
|
69
|
+
chat: string;
|
|
70
|
+
target: "group" | "private";
|
|
71
|
+
delivered: true;
|
|
72
|
+
}>;
|
|
73
|
+
}, Readonly<Record<never, never>>>;
|
|
74
|
+
export type AgentChat = typeof AgentChat.Type;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Context, Effect, Option, Schema } from "effect";
|
|
2
|
+
import { action } from "./action.js";
|
|
3
|
+
import { entity } from "./entity.js";
|
|
4
|
+
import { ActionRejected } from "./errors.js";
|
|
5
|
+
import { isAllowed, permissionOf, READ_VERB } from "./permissions.js";
|
|
6
|
+
import { p } from "./property.js";
|
|
7
|
+
import { rel } from "./relationship.js";
|
|
8
|
+
import { User } from "./user.js";
|
|
9
|
+
/** The only channel the agent delivers to so far, and the provider of its chats. */
|
|
10
|
+
export const TELEGRAM = "telegram";
|
|
11
|
+
/**
|
|
12
|
+
* The workspace's agent, as `agent_chat.send_message` reaches it. The ontology service provides it to
|
|
13
|
+
* actions only on deployments with an agent; without it, the action is refused.
|
|
14
|
+
*/
|
|
15
|
+
export class AgentChannel extends Context.Service()("@segmentstream-cloud/ontology/AgentChannel") {
|
|
16
|
+
}
|
|
17
|
+
/** A group chat as the agent's target; the agent delivers only to Telegram so far. */
|
|
18
|
+
function groupTarget(chat) {
|
|
19
|
+
return chat.provider === TELEGRAM
|
|
20
|
+
? Effect.succeed({ kind: "group", provider: TELEGRAM, chatId: chat.externalId })
|
|
21
|
+
: Effect.fail(new ActionRejected({
|
|
22
|
+
message: `The agent delivers only to Telegram chats; ${chat.title} is on ${chat.provider}.`,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
const NO_AGENT = "The workspace has no agent deployed, so nobody can deliver the message. " +
|
|
26
|
+
"Deploy the workspace's agent first.";
|
|
27
|
+
/**
|
|
28
|
+
* The built-in agent chat: a group chat the workspace's agent is in. Every ontology includes it,
|
|
29
|
+
* after `User`. The agent records each chat it sees and the members it sees writing there, through
|
|
30
|
+
* `POST /api/ontology/agent/chats/seen`; members say what a chat is for with the generated `update`,
|
|
31
|
+
* which changes only `purpose` and `whenToUse`. `send_message` asks the agent to write to a chat.
|
|
32
|
+
*/
|
|
33
|
+
export const AgentChat = entity("agent_chat", {
|
|
34
|
+
id: "ent_agent_chat",
|
|
35
|
+
label: "Agent chat",
|
|
36
|
+
plural: "Agent chats",
|
|
37
|
+
title: "title",
|
|
38
|
+
description: "A group chat the workspace's agent is in. The agent records each chat it sees; " +
|
|
39
|
+
"members say what the chat is for, so that agents know where to write.",
|
|
40
|
+
properties: {
|
|
41
|
+
provider: p.text({ managed: true, description: "The channel, for example telegram." }),
|
|
42
|
+
externalId: p.text({ managed: true, description: "The chat's id at the provider." }),
|
|
43
|
+
title: p.text({
|
|
44
|
+
managed: true,
|
|
45
|
+
description: "The chat's name at the provider, kept current by the agent.",
|
|
46
|
+
}),
|
|
47
|
+
lastMessageAt: p.timestamp({
|
|
48
|
+
optional: true,
|
|
49
|
+
managed: true,
|
|
50
|
+
label: "Last message",
|
|
51
|
+
description: "When the agent last saw a message in the chat, recorded at most every 15 minutes.",
|
|
52
|
+
}),
|
|
53
|
+
purpose: p.text({
|
|
54
|
+
optional: true,
|
|
55
|
+
maxLength: 64,
|
|
56
|
+
description: "A short key that code and agents look the chat up by, such as accounting.",
|
|
57
|
+
}),
|
|
58
|
+
whenToUse: p.text({
|
|
59
|
+
optional: true,
|
|
60
|
+
maxLength: 1000,
|
|
61
|
+
display: "multiline",
|
|
62
|
+
label: "When to use",
|
|
63
|
+
description: "For agents: what the chat is for and when to write there, such as " +
|
|
64
|
+
'"Invoices and payments; Yulia answers on weekdays."',
|
|
65
|
+
}),
|
|
66
|
+
},
|
|
67
|
+
relationships: {
|
|
68
|
+
// By name, so that an ontology rebuilt with the deployed `user` still resolves it.
|
|
69
|
+
members: rel.many("user", {
|
|
70
|
+
inverse: "agentChats",
|
|
71
|
+
phrase: ["includes", "is in"],
|
|
72
|
+
description: "Workspace members the agent has seen writing in the chat.",
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
indexes: [{ properties: ["provider", "externalId"], unique: true }],
|
|
76
|
+
}).actions((self) => ({
|
|
77
|
+
sendMessage: action({
|
|
78
|
+
description: "Ask the workspace's agent to write to a group chat or a person, for example " +
|
|
79
|
+
'"ask Yulia for the amount on invoice 118". message is the request for the agent, written ' +
|
|
80
|
+
"as an instruction to it, not necessarily the text to post. The target is chosen without " +
|
|
81
|
+
"guessing: with chat, that chat (person, if given, is whom the agent addresses there); with " +
|
|
82
|
+
"only person, the one agent chat whose members include them, or, when they are in none, " +
|
|
83
|
+
"their private chat with the agent if they paired a Telegram account. A person in several " +
|
|
84
|
+
"chats is refused with the candidates (id, title, purpose, when to use; only their number " +
|
|
85
|
+
"without agent_chat:read): call again with chat. The agent then writes in that chat and " +
|
|
86
|
+
"handles the replies there, in that chat's own session; this action only delivers the " +
|
|
87
|
+
"request and does not wait for an answer. Requires an agent deployed in the workspace.",
|
|
88
|
+
input: Schema.Struct({
|
|
89
|
+
message: p.text({
|
|
90
|
+
maxLength: 4000,
|
|
91
|
+
description: "The request for the agent, as an instruction: what to ask or tell, of whom.",
|
|
92
|
+
}),
|
|
93
|
+
chat: p.ref("agent_chat", { optional: true, description: "The agent chat to write in." }),
|
|
94
|
+
person: p.ref("user", {
|
|
95
|
+
optional: true,
|
|
96
|
+
description: "The member to address; alone, it picks their chat with the agent.",
|
|
97
|
+
}),
|
|
98
|
+
}),
|
|
99
|
+
hints: { destructive: false },
|
|
100
|
+
connections: [],
|
|
101
|
+
prepare: ({ input, principal }) => Effect.gen(function* () {
|
|
102
|
+
const channel = yield* Effect.serviceOption(AgentChannel);
|
|
103
|
+
if (Option.isNone(channel)) {
|
|
104
|
+
return yield* new ActionRejected({ message: NO_AGENT });
|
|
105
|
+
}
|
|
106
|
+
const person = input.person === undefined ? undefined : yield* User.get(input.person);
|
|
107
|
+
if (person !== undefined && (person.kind !== "member" || !person.active)) {
|
|
108
|
+
return yield* new ActionRejected({
|
|
109
|
+
message: `${person.name} is not an active member, so the agent does not contact them.`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
let chat;
|
|
113
|
+
let target;
|
|
114
|
+
if (input.chat !== undefined) {
|
|
115
|
+
chat = yield* self.get(input.chat);
|
|
116
|
+
target = yield* groupTarget(chat);
|
|
117
|
+
}
|
|
118
|
+
else if (person !== undefined) {
|
|
119
|
+
const chats = yield* self.find({
|
|
120
|
+
where: { members: { some: { id: { eq: person.id } } } },
|
|
121
|
+
});
|
|
122
|
+
if (chats.length > 1) {
|
|
123
|
+
// The action reads chats whatever the caller holds; only a reader sees which they are.
|
|
124
|
+
if (!isAllowed(permissionOf(self.name, READ_VERB), principal)) {
|
|
125
|
+
return yield* new ActionRejected({
|
|
126
|
+
message: `${person.name} is in ${chats.length} chats with the agent; call again with ` +
|
|
127
|
+
"the chat's id as chat. Listing them requires agent_chat:read.",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const candidates = chats.map((candidate) => `- ${candidate.id}: ${candidate.title}; purpose: ${candidate.purpose ?? "none"}; ` +
|
|
131
|
+
`when to use: ${candidate.whenToUse ?? "not set"}`);
|
|
132
|
+
return yield* new ActionRejected({
|
|
133
|
+
message: `${person.name} is in ${chats.length} chats with the agent; ` +
|
|
134
|
+
`call again with one of them as chat:\n${candidates.join("\n")}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
chat = chats[0];
|
|
138
|
+
if (chat !== undefined) {
|
|
139
|
+
target = yield* groupTarget(chat);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const chatId = yield* channel.value.privateChat(person.id);
|
|
143
|
+
if (chatId === undefined) {
|
|
144
|
+
return yield* new ActionRejected({
|
|
145
|
+
message: `${person.name} is in no chat with the agent and has not paired a Telegram ` +
|
|
146
|
+
"account with it, so the agent cannot reach them.",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
target = { kind: "private", provider: TELEGRAM, chatId };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
return yield* new ActionRejected({
|
|
154
|
+
message: "Name the chat to write in, the person to write to, or both.",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const requester = yield* User.get(principal.id);
|
|
158
|
+
// The agent acts as a member only through their pairing.
|
|
159
|
+
const actAs = principal.kind === "member" &&
|
|
160
|
+
(yield* channel.value.privateChat(principal.id)) !== undefined;
|
|
161
|
+
yield* channel.value.send({
|
|
162
|
+
target,
|
|
163
|
+
message: input.message,
|
|
164
|
+
person: person === undefined ? null : { id: person.id, name: person.name },
|
|
165
|
+
requester: {
|
|
166
|
+
id: requester.id,
|
|
167
|
+
name: requester.name,
|
|
168
|
+
kind: principal.kind,
|
|
169
|
+
roles: principal.roles,
|
|
170
|
+
actAs,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
return { chat: chat?.id ?? null, target: target.kind, delivered: true };
|
|
174
|
+
}),
|
|
175
|
+
}),
|
|
176
|
+
}));
|
package/dist/analyze.d.ts
CHANGED
|
@@ -9,7 +9,10 @@ import { type Role } from "./role.js";
|
|
|
9
9
|
import { type SignInOptions } from "./sign-in.js";
|
|
10
10
|
/** The input of `defineOntology` and `check`. */
|
|
11
11
|
export interface OntologyConfig<Entities extends ReadonlyArray<AnyEntity> = ReadonlyArray<AnyEntity>> {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The app's entities. The built-in `User` and `AgentChat` are always included; listing them is
|
|
14
|
+
* optional.
|
|
15
|
+
*/
|
|
13
16
|
readonly entities: Entities;
|
|
14
17
|
/** Business roles. The built-in `member` role is always included. */
|
|
15
18
|
readonly roles?: ReadonlyArray<Role>;
|
|
@@ -82,7 +85,8 @@ export interface ResolvedIndex {
|
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
84
87
|
* The permissions of an entity's reads and generated actions. The built-in User has no generated
|
|
85
|
-
* actions
|
|
88
|
+
* actions and the built-in agent chat only `update`; `link` and `unlink` exist when the entity
|
|
89
|
+
* declares a `rel.many`.
|
|
86
90
|
*/
|
|
87
91
|
export interface EntityPermissions {
|
|
88
92
|
readonly read: string;
|
package/dist/analyze.js
CHANGED
|
@@ -7,10 +7,11 @@ import { CONNECTION_KEY, isConnection } from "./connection.js";
|
|
|
7
7
|
import { isEntity } from "./entity.js";
|
|
8
8
|
import { AGGREGATE_ROUTE, LEGACY_MEMBER_NAME, ENTITY_ID, GENERATED_ACTIONS, GENERATED_UPSERT_PREFIX, HISTORY_SUFFIX, MCP_READ_TOOLS, MCP_TOOL_NAME_MAX_LENGTH, MEMBER_ROLE, mcpToolName, RESERVED_ENTITY_NAMES, RESERVED_MEMBER_NAMES, RESERVED_RELATIONSHIP_NAMES, SNAKE_CASE_NAME, SYSTEM_PROPERTIES, isSystemProperty, } from "./names.js";
|
|
9
9
|
import { REFERENCE_ANNOTATION, propertyDefinition, referenceTarget, } from "./property.js";
|
|
10
|
-
import { generatedActionPermissions, parsePermission, permissionOf, READ_VERB, rolesGranting, WILDCARD, } from "./permissions.js";
|
|
10
|
+
import { EXPLICIT_GRANT_ENTITIES, generatedActionPermissions, parsePermission, permissionOf, READ_VERB, rolesGranting, WILDCARD, } from "./permissions.js";
|
|
11
11
|
import { ON_DELETE, isRelationship } from "./relationship.js";
|
|
12
12
|
import { isRole, member, role } from "./role.js";
|
|
13
13
|
import { DEFAULT_SIGN_IN_POLICY, isSignInPolicy } from "./sign-in.js";
|
|
14
|
+
import { AgentChat } from "./agent-chat.js";
|
|
14
15
|
import { User } from "./user.js";
|
|
15
16
|
import { authoringToWire } from "./authoring.js";
|
|
16
17
|
import { nameToWire } from "./authoring-names.js";
|
|
@@ -34,10 +35,9 @@ export function analyze(config, options = {}) {
|
|
|
34
35
|
diagnostics.push({ severity, code, path, message });
|
|
35
36
|
};
|
|
36
37
|
const original = config;
|
|
37
|
-
const normalized = authoringToWire(config, options.user ?? User, report);
|
|
38
|
+
const normalized = authoringToWire(config, [options.user ?? User, AgentChat], report);
|
|
38
39
|
config = normalized.config;
|
|
39
|
-
const
|
|
40
|
-
const entities = collectEntities(config, user, report);
|
|
40
|
+
const entities = collectEntities(config, normalized.builtIns, report);
|
|
41
41
|
const roles = collectRoles(config, report);
|
|
42
42
|
const signIn = signInOptions(config, report);
|
|
43
43
|
const connections = collectConnections(config, report);
|
|
@@ -49,7 +49,7 @@ export function analyze(config, options = {}) {
|
|
|
49
49
|
}
|
|
50
50
|
const scope = {
|
|
51
51
|
report,
|
|
52
|
-
|
|
52
|
+
builtIns: new Set(normalized.builtIns.map((candidate) => candidate.definition)),
|
|
53
53
|
entitiesByName,
|
|
54
54
|
definitions: new Set(entities.map((candidate) => candidate.definition)),
|
|
55
55
|
entityProperties: new Set([...entities, ...(original.entities ?? []).filter(isEntity)].flatMap((candidate) => Object.values(candidate.definition.properties ?? {}))),
|
|
@@ -74,7 +74,7 @@ export function analyze(config, options = {}) {
|
|
|
74
74
|
function toolOperations(candidate) {
|
|
75
75
|
const { permissions } = candidate;
|
|
76
76
|
const generated = [
|
|
77
|
-
...
|
|
77
|
+
...["create", "update", "delete"].filter((verb) => permissions[verb] !== undefined),
|
|
78
78
|
...(permissions.create !== undefined
|
|
79
79
|
? candidate.properties
|
|
80
80
|
.filter(({ definition }) => definition.unique)
|
|
@@ -169,9 +169,10 @@ function checkConnectionsUsed(entities, connections, declared, report) {
|
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
171
|
* The keys of an action's connections, each listed in `defineOntology({ connections })`, and the
|
|
172
|
-
* phases they require: `prepare` with connections
|
|
172
|
+
* phases they require: `prepare` with connections and only with them, except for built-in actions,
|
|
173
|
+
* and `prepare` or `handler` always.
|
|
173
174
|
*/
|
|
174
|
-
function resolveActionConnections(path, action, scope) {
|
|
175
|
+
function resolveActionConnections(path, action, scope, builtIn) {
|
|
175
176
|
const { report } = scope;
|
|
176
177
|
const declared = action.connections;
|
|
177
178
|
const keys = [];
|
|
@@ -200,7 +201,8 @@ function resolveActionConnections(path, action, scope) {
|
|
|
200
201
|
if (Array.isArray(declared) && declared.length > 0 && !hasPrepare) {
|
|
201
202
|
report("invalid-prepare", `${path}.prepare`, "an action with connections calls them in prepare, which runs before the transaction.");
|
|
202
203
|
}
|
|
203
|
-
|
|
204
|
+
// A built-in action's prepare reaches the platform, such as the workspace's agent, instead.
|
|
205
|
+
if (hasPrepare && !builtIn && Array.isArray(declared) && declared.length === 0) {
|
|
204
206
|
report("invalid-prepare", `${path}.prepare`, "prepare is for calling connections; an action without connections does its work in handler.");
|
|
205
207
|
}
|
|
206
208
|
if (!hasPrepare && !hasHandler) {
|
|
@@ -208,8 +210,8 @@ function resolveActionConnections(path, action, scope) {
|
|
|
208
210
|
}
|
|
209
211
|
return keys;
|
|
210
212
|
}
|
|
211
|
-
function collectEntities(config,
|
|
212
|
-
const entities = [
|
|
213
|
+
function collectEntities(config, builtIns, report) {
|
|
214
|
+
const entities = [...builtIns];
|
|
213
215
|
(config.entities ?? []).forEach((candidate, index) => {
|
|
214
216
|
if (!isEntity(candidate)) {
|
|
215
217
|
report("invalid-entity", `entities[${index}]`, "is not an entity; declare entities with entity(name, { … }).");
|
|
@@ -330,7 +332,7 @@ function resolveEntity(candidate, scope) {
|
|
|
330
332
|
else if (!properties.some((property) => property.name === definition.title)) {
|
|
331
333
|
report("invalid-title", `${name}.title`, `"${definition.title}" is not a declared property of ${name}.`);
|
|
332
334
|
}
|
|
333
|
-
const builtIn =
|
|
335
|
+
const builtIn = scope.builtIns.has(candidate.definition);
|
|
334
336
|
return {
|
|
335
337
|
entity: candidate,
|
|
336
338
|
name,
|
|
@@ -342,11 +344,15 @@ function resolveEntity(candidate, scope) {
|
|
|
342
344
|
permissions: entityPermissions(name, builtIn || definition.source !== undefined, relationships),
|
|
343
345
|
};
|
|
344
346
|
}
|
|
345
|
-
/**
|
|
347
|
+
/**
|
|
348
|
+
* Reads need `<entity>:read`; each generated action needs the permission named after it. Built-in
|
|
349
|
+
* entities hold the platform's records: only `agent_chat` has a generated action, `update`, for the
|
|
350
|
+
* properties members edit.
|
|
351
|
+
*/
|
|
346
352
|
function entityPermissions(name, builtIn, relationships) {
|
|
347
353
|
const read = permissionOf(name, READ_VERB);
|
|
348
354
|
if (builtIn) {
|
|
349
|
-
return { read };
|
|
355
|
+
return name === AgentChat.name ? { read, update: permissionOf(name, "update") } : { read };
|
|
350
356
|
}
|
|
351
357
|
const many = relationships.some(({ relationship }) => relationship.cardinality === "many");
|
|
352
358
|
return {
|
|
@@ -628,7 +634,7 @@ function resolveActions(candidate, scope) {
|
|
|
628
634
|
action,
|
|
629
635
|
permission: resolvePermission(path, permissionOf(candidate.name, name), action, scope),
|
|
630
636
|
input: resolveInput(path, action.input, scope),
|
|
631
|
-
connections: resolveActionConnections(path, action, scope),
|
|
637
|
+
connections: resolveActionConnections(path, action, scope, scope.builtIns.has(candidate.definition)),
|
|
632
638
|
hints: resolveHints(path, action, report),
|
|
633
639
|
});
|
|
634
640
|
}
|
|
@@ -762,7 +768,9 @@ function checkEntityIdentities(entities, report) {
|
|
|
762
768
|
const existingName = names.get(candidate.name);
|
|
763
769
|
const existingId = ids.get(definition.id);
|
|
764
770
|
if (existingName !== undefined) {
|
|
765
|
-
const builtIn = existingName.builtIn
|
|
771
|
+
const builtIn = existingName.builtIn
|
|
772
|
+
? ` (the built-in ${existingName.entity.definition.label})`
|
|
773
|
+
: "";
|
|
766
774
|
report("duplicate-name", candidate.name, `another entity is named ${candidate.name}${builtIn}.`);
|
|
767
775
|
}
|
|
768
776
|
else {
|
|
@@ -869,7 +877,9 @@ function knownVerbs(entities) {
|
|
|
869
877
|
/** Why a role's permission matches nothing in the ontology, or `undefined` when it matches. */
|
|
870
878
|
function unknownPermission(value, { entity, verb }, verbs) {
|
|
871
879
|
if (entity === WILDCARD) {
|
|
872
|
-
|
|
880
|
+
// A wildcard entity does not reach the entities only a named permission covers.
|
|
881
|
+
const matched = verb === WILDCARD ||
|
|
882
|
+
[...verbs].some(([name, own]) => !EXPLICIT_GRANT_ENTITIES.has(name) && own.has(verb));
|
|
873
883
|
return matched ? undefined : `${value} matches nothing: no entity has ${verb}.`;
|
|
874
884
|
}
|
|
875
885
|
const own = verbs.get(entity);
|
|
@@ -961,7 +971,8 @@ function checkPermissions(entities, roles, report) {
|
|
|
961
971
|
name: candidate.name,
|
|
962
972
|
permissions: Array.isArray(candidate.permissions) ? candidate.permissions : [],
|
|
963
973
|
}));
|
|
964
|
-
|
|
974
|
+
// Built-in actions are granted by the workspace's roles when it wants them.
|
|
975
|
+
for (const candidate of entities.filter((entity) => !entity.builtIn)) {
|
|
965
976
|
for (const { name, permission } of candidate.actions) {
|
|
966
977
|
const parsed = parsePermission(permission);
|
|
967
978
|
// An invalid name or permission is already an error; it needs no warning on top.
|
package/dist/authoring.d.ts
CHANGED
|
@@ -4,9 +4,12 @@ import { type AnyEntity } from "./entity.js";
|
|
|
4
4
|
import type { Principal } from "./runtime.js";
|
|
5
5
|
type Report = (code: DiagnosticCode, path: string, message: string) => void;
|
|
6
6
|
export declare function principalFromWire(principal: Principal): Principal;
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Canonical definitions feed REST, storage and future transports; the public config stays camelCase.
|
|
9
|
+
* The built-in entities, which every ontology includes, are normalized with the declared ones.
|
|
10
|
+
*/
|
|
11
|
+
export declare function authoringToWire(config: OntologyConfig, builtIns: ReadonlyArray<AnyEntity>, report: Report): {
|
|
9
12
|
readonly config: OntologyConfig;
|
|
10
|
-
readonly
|
|
13
|
+
readonly builtIns: ReadonlyArray<AnyEntity>;
|
|
11
14
|
};
|
|
12
15
|
export {};
|
package/dist/authoring.js
CHANGED
|
@@ -217,8 +217,11 @@ class AuthoringEncoder {
|
|
|
217
217
|
return normalized;
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
/**
|
|
221
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Canonical definitions feed REST, storage and future transports; the public config stays camelCase.
|
|
222
|
+
* The built-in entities, which every ontology includes, are normalized with the declared ones.
|
|
223
|
+
*/
|
|
224
|
+
export function authoringToWire(config, builtIns, report) {
|
|
222
225
|
const encoder = new AuthoringEncoder(report);
|
|
223
226
|
const normalized = {
|
|
224
227
|
...config,
|
|
@@ -233,5 +236,5 @@ export function authoringToWire(config, user, report) {
|
|
|
233
236
|
permissions: config.member.permissions.map(permissionToWire),
|
|
234
237
|
};
|
|
235
238
|
}
|
|
236
|
-
return { config: normalized,
|
|
239
|
+
return { config: normalized, builtIns: builtIns.map((entity) => encoder.entity(entity)) };
|
|
237
240
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export type { SignInCheck, SignInMethod, SignInOptions, SignInPolicy } from "./s
|
|
|
34
34
|
export { OntologyReads, OntologyRuntime, OntologyWrites } from "./runtime.js";
|
|
35
35
|
export type { FieldCondition, OntologyRuntimeService, FindOptions, GetOptions, Principal, RecordWhere, StoredRecord, StoredValues, UpsertResult, } from "./runtime.js";
|
|
36
36
|
export { User } from "./user.js";
|
|
37
|
+
export { AgentChannel, AgentChat } from "./agent-chat.js";
|
|
38
|
+
export type { AgentMessage } from "./agent-chat.js";
|
|
37
39
|
export { DEFAULT_QUERY_LIMIT, entityQueryResultSchema, entityQuerySchema, entityQuerySchemas, entityRecordSchema, FILTER_OPERATORS, MANY_QUANTIFIERS, MAX_IN_VALUES, MAX_QUERY_LIMIT, OrderBy, Query, queryFields, queryJsonSchema, queryLinks, QueryResult, queryResultJsonSchema, queryResultSchema, SortDirection, WHERE_COMBINATORS, } from "./query.js";
|
|
38
40
|
export type { EntityQuerySchemas, FieldFilter, FilterOperator, JsonRecord, ManyFilter, QueryField, QueryLink, StorageType, Where, } from "./query.js";
|
|
39
41
|
export { InvalidQuery, InvalidValues, MemberNotFound, PrincipalRejected, StoredRecordMismatch, UnknownAction, UnknownRole, ValidationIssue, WriteOutsideAction, } from "./runtime/errors.js";
|
|
@@ -52,11 +54,14 @@ export type { DeriveApiOptions, OntologyApi } from "./service/api.js";
|
|
|
52
54
|
export { CurrentPrincipal } from "./service/authentication.js";
|
|
53
55
|
export { deployedConnections, LocalConnectionContextError, localConnections, OIDC_TOKEN_HEADER, parseLocalConnectionContext, readLocalConnectionContext, RUNTIME_CONTEXT_FD, } from "./service/connections.js";
|
|
54
56
|
export type { ConnectionAudit, ConnectionTransportFactory, LocalConnection, LocalConnectionContext, } from "./service/connections.js";
|
|
55
|
-
export { configFromEnv, ENVIRONMENT_VARIABLES, NEON_AUTH_ENVIRONMENT_VARIABLES, OntologyServiceConfigError, } from "./service/config.js";
|
|
57
|
+
export { configFromEnv, AGENT_HOST_VARIABLE, ENVIRONMENT_VARIABLES, NEON_AUTH_ENVIRONMENT_VARIABLES, OntologyServiceConfigError, RUNTIME_IDENTITY_ENVIRONMENT_VARIABLES, } from "./service/config.js";
|
|
56
58
|
export type { ActionCommitted, AfterCommitHook, NeonAuthEnvironment, OntologyEnvironment, OntologyServiceConfig, } from "./service/config.js";
|
|
57
59
|
export { NotInvited, OntologyBackend } from "./service/runtime-contract.js";
|
|
58
|
-
export type { GeneratedActionsProvider, IdentityProvider, OAuthClient, OAuthClientAuthMethod, OAuthGrant, OAuthRequest, OAuthStore, OAuthToken, OAuthTokenKind, OntologyServiceRuntime, RunActionInput, SignInIdentity, UsedOAuthToken, } from "./service/runtime-contract.js";
|
|
60
|
+
export type { AgentIdentity, AgentMember, AgentPairing, AgentStore, SeenChat, GeneratedActionsProvider, IdentityProvider, OAuthClient, OAuthClientAuthMethod, OAuthGrant, OAuthRequest, OAuthStore, OAuthToken, OAuthTokenKind, OntologyServiceRuntime, RunActionInput, SignInIdentity, UsedOAuthToken, } from "./service/runtime-contract.js";
|
|
59
61
|
export { createOntologyService, ONTOLOGY_HASH_HEADER, REQUEST_ID_HEADER, } from "./service/service.js";
|
|
62
|
+
export { AGENT_PATH } from "./service/agent/server.js";
|
|
63
|
+
export { ACT_AS_HEADER } from "./service/runtime-identity.js";
|
|
64
|
+
export type { RuntimeIdentityConfig } from "./service/runtime-identity.js";
|
|
60
65
|
export { MCP_PATH, MCP_PROTOCOL_VERSIONS } from "./service/mcp/server.js";
|
|
61
66
|
export { MCP_TOOL_NAME_MAX_LENGTH, mcpToolName } from "./names.js";
|
|
62
67
|
export type { OntologyService } from "./service/service.js";
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { member, role } from "./role.js";
|
|
|
17
17
|
export { DEFAULT_SIGN_IN_POLICY, SIGN_IN_METHODS, SIGN_IN_POLICIES } from "./sign-in.js";
|
|
18
18
|
export { OntologyReads, OntologyRuntime, OntologyWrites } from "./runtime.js";
|
|
19
19
|
export { User } from "./user.js";
|
|
20
|
+
export { AgentChannel, AgentChat } from "./agent-chat.js";
|
|
20
21
|
// The query language of `find` and of the service's list, query and traversal routes.
|
|
21
22
|
export { DEFAULT_QUERY_LIMIT, entityQueryResultSchema, entityQuerySchema, entityQuerySchemas, entityRecordSchema, FILTER_OPERATORS, MANY_QUANTIFIERS, MAX_IN_VALUES, MAX_QUERY_LIMIT, OrderBy, Query, queryFields, queryJsonSchema, queryLinks, QueryResult, queryResultJsonSchema, queryResultSchema, SortDirection, WHERE_COMBINATORS, } from "./query.js";
|
|
22
23
|
// The SQL runtime: data access, the action pipeline, generated actions, history and principals.
|
|
@@ -31,9 +32,12 @@ export { makeOntologyRuntime } from "./runtime/sql-runtime.js";
|
|
|
31
32
|
export { BASE_PATH, deriveApi } from "./service/api.js";
|
|
32
33
|
export { CurrentPrincipal } from "./service/authentication.js";
|
|
33
34
|
export { deployedConnections, LocalConnectionContextError, localConnections, OIDC_TOKEN_HEADER, parseLocalConnectionContext, readLocalConnectionContext, RUNTIME_CONTEXT_FD, } from "./service/connections.js";
|
|
34
|
-
export { configFromEnv, ENVIRONMENT_VARIABLES, NEON_AUTH_ENVIRONMENT_VARIABLES, OntologyServiceConfigError, } from "./service/config.js";
|
|
35
|
+
export { configFromEnv, AGENT_HOST_VARIABLE, ENVIRONMENT_VARIABLES, NEON_AUTH_ENVIRONMENT_VARIABLES, OntologyServiceConfigError, RUNTIME_IDENTITY_ENVIRONMENT_VARIABLES, } from "./service/config.js";
|
|
35
36
|
export { NotInvited, OntologyBackend } from "./service/runtime-contract.js";
|
|
36
37
|
export { createOntologyService, ONTOLOGY_HASH_HEADER, REQUEST_ID_HEADER, } from "./service/service.js";
|
|
38
|
+
// The app's own agent: pairing channel accounts with members, and acting as them.
|
|
39
|
+
export { AGENT_PATH } from "./service/agent/server.js";
|
|
40
|
+
export { ACT_AS_HEADER } from "./service/runtime-identity.js";
|
|
37
41
|
// The MCP server the service serves beside the HTTP API.
|
|
38
42
|
export { MCP_PATH, MCP_PROTOCOL_VERSIONS } from "./service/mcp/server.js";
|
|
39
43
|
export { MCP_TOOL_NAME_MAX_LENGTH, mcpToolName } from "./names.js";
|
package/dist/migrate/apply.js
CHANGED
|
@@ -7,7 +7,7 @@ import { planMigration } from "./plan.js";
|
|
|
7
7
|
import { REPLICATION_DDL, REPLICATION_SCHEMA_VERSION, replicationEntityDdl, } from "../replication/schema.js";
|
|
8
8
|
import { createReplicationBaseline } from "../replication/baseline.js";
|
|
9
9
|
import { resolveReplicationSelection } from "../replication/model.js";
|
|
10
|
-
import { API_KEYS_TABLE, CATALOG_TABLE, OAUTH_TABLES, USER_ROLES_TABLE, quoteIdentifier, quoteLiteral, } from "./storage.js";
|
|
10
|
+
import { AGENT_TABLES, API_KEYS_TABLE, CATALOG_TABLE, OAUTH_TABLES, USER_ROLES_TABLE, quoteIdentifier, quoteLiteral, } from "./storage.js";
|
|
11
11
|
const CATALOG = quoteIdentifier(CATALOG_TABLE);
|
|
12
12
|
/** Serializes migrations of one database; transaction-scoped, so it ends with the transaction. */
|
|
13
13
|
const MIGRATION_LOCK = "segmentstream.ontology.migration";
|
|
@@ -171,6 +171,7 @@ export const RUNTIME_TABLES = [
|
|
|
171
171
|
USER_ROLES_TABLE,
|
|
172
172
|
API_KEYS_TABLE,
|
|
173
173
|
...OAUTH_TABLES,
|
|
174
|
+
...AGENT_TABLES,
|
|
174
175
|
];
|
|
175
176
|
/**
|
|
176
177
|
* The runtime tables the database has, for `planMigration`'s `existingTables`: a database that an
|