@nacre.work/api 0.33.0 → 0.35.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.
@@ -0,0 +1,52 @@
1
+ import type { Pool } from 'pg';
2
+ import type { AccessSubject } from './access.js';
3
+ import type { AuthContext } from './auth.js';
4
+ export declare const isUuid: (value: string) => boolean;
5
+ export type PrincipalKind = 'person' | 'group' | 'service_account';
6
+ export interface NamedLayer {
7
+ readonly id: string;
8
+ readonly slug: string;
9
+ readonly workspaceId: string;
10
+ }
11
+ export declare class AdminNames {
12
+ private readonly pool;
13
+ private readonly role;
14
+ constructor(pool: Pool, role: string);
15
+ private inOrg;
16
+ /** A principal named by email, name or id, in this organization — or a refusal naming the reference. */
17
+ principal(auth: AuthContext, args: Readonly<Record<string, unknown>>, kinds?: readonly PrincipalKind[]): Promise<AccessSubject>;
18
+ lookup(auth: AuthContext, kind: PrincipalKind, ref: string): Promise<AccessSubject | undefined>;
19
+ /** A layer by slug or id. */
20
+ layer(auth: AuthContext, ref: string): Promise<NamedLayer>;
21
+ /** A workspace by slug or id. */
22
+ workspace(auth: AuthContext, ref: string): Promise<{
23
+ readonly id: string;
24
+ readonly slug: string;
25
+ }>;
26
+ /**
27
+ * A name beside every id: an email for a person, a name for a group or a
28
+ * service account, a slug for a layer or a workspace. Ids the organization
29
+ * does not have come back unnamed rather than refused — a deleted layer is
30
+ * still in last month's log.
31
+ */
32
+ names(auth: AuthContext, ids: Iterable<string>): Promise<ReadonlyMap<string, string>>;
33
+ /** One grant with its names, or `undefined` when this organization has none by that id. */
34
+ grant(auth: AuthContext, id: string): Promise<{
35
+ readonly principalType: string;
36
+ readonly principalId: string;
37
+ readonly scopeType: string;
38
+ readonly scopeId: string;
39
+ readonly permission: string;
40
+ readonly effect: string;
41
+ } | undefined>;
42
+ /** Counts a person reads before deleting something that holds things. */
43
+ counts(auth: AuthContext, of: {
44
+ readonly group?: string;
45
+ readonly layer?: string;
46
+ }): Promise<{
47
+ readonly members: number;
48
+ readonly grants: number;
49
+ readonly documents: number;
50
+ }>;
51
+ }
52
+ //# sourceMappingURL=admin-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-names.d.ts","sourceRoot":"","sources":["../src/admin-names.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAA;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAiB5C,eAAO,MAAM,MAAM,GAAI,OAAO,MAAM,KAAG,OAA2B,CAAA;AAElE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,iBAAiB,CAAA;AAElE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM;IAG/B,OAAO,CAAC,KAAK;IAIb,wGAAwG;IAClG,SAAS,CACb,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACvC,KAAK,GAAE,SAAS,aAAa,EAA2C,GACvE,OAAO,CAAC,aAAa,CAAC;IAYzB,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAiB/F,6BAA6B;IACvB,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAYhE,iCAAiC;IAC3B,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAYxG;;;;;OAKG;IACH,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAgBrF,2FAA2F;IAC3F,KAAK,CACH,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,GACT,OAAO,CACN;QACE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;QAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;QAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;QAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KACxB,GACD,SAAS,CACZ;IA6BD,yEAAyE;IACzE,MAAM,CACJ,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAkB9F"}
@@ -0,0 +1,127 @@
1
+ import { McpToolRefusal, withOrg } from '@nacre.work/core';
2
+ /**
3
+ * Names an administrator uses, resolved to the ids the ports take.
4
+ *
5
+ * An agent on the administrative surface says "dana@example.com" and "the
6
+ * handbook layer", not a uuid — and so does the person reading what it
7
+ * proposes. Every lookup is inside the caller's organization and names it in
8
+ * its own literal, so a name in another tenant resolves to nothing rather than
9
+ * to somebody else's row, whatever the connection's role.
10
+ *
11
+ * Shared by the MCP process, which proposes, and by the API, which applies from
12
+ * the console — one answer to "who is dana@example.com" on both sides.
13
+ */
14
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
15
+ export const isUuid = (value) => UUID.test(value);
16
+ export class AdminNames {
17
+ pool;
18
+ role;
19
+ constructor(pool, role) {
20
+ this.pool = pool;
21
+ this.role = role;
22
+ }
23
+ inOrg(auth, run) {
24
+ return withOrg(this.pool, auth.orgId, run, { role: this.role });
25
+ }
26
+ /** A principal named by email, name or id, in this organization — or a refusal naming the reference. */
27
+ async principal(auth, args, kinds = ['person', 'group', 'service_account']) {
28
+ const given = kinds.filter((k) => typeof args[k] === 'string' && args[k] !== '');
29
+ if (given.length !== 1) {
30
+ throw new McpToolRefusal(`Name exactly one of ${kinds.join(', ').replace(/, ([^,]*)$/, ' or $1')}.`);
31
+ }
32
+ const kind = given[0];
33
+ const ref = args[kind].trim();
34
+ const found = await this.lookup(auth, kind, ref);
35
+ if (found === undefined)
36
+ throw new McpToolRefusal(`No ${kind.replace('_', ' ')} "${ref}" in this organization.`);
37
+ return found;
38
+ }
39
+ lookup(auth, kind, ref) {
40
+ return this.inOrg(auth, async (client) => {
41
+ const byId = UUID.test(ref);
42
+ const [table, column, type] = kind === 'person'
43
+ ? ['users', 'lower(email)', 'user']
44
+ : kind === 'group'
45
+ ? ['groups', 'name', 'group']
46
+ : ['service_accounts', 'name', 'service_account'];
47
+ const { rows } = await client.query(`SELECT id FROM ${table} WHERE org_id = $1 AND ${byId ? 'id = $2::uuid' : `${column} = $2`} LIMIT 2`, [auth.orgId, byId ? ref : kind === 'person' ? ref.toLowerCase() : ref]);
48
+ return rows.length === 1 && rows[0] !== undefined ? { type, id: rows[0].id } : undefined;
49
+ });
50
+ }
51
+ /** A layer by slug or id. */
52
+ async layer(auth, ref) {
53
+ const found = await this.inOrg(auth, async (client) => {
54
+ const { rows } = await client.query(`SELECT id, slug, workspace_id FROM layers WHERE org_id = $1 AND deleted_at IS NULL AND ${UUID.test(ref) ? 'id = $2::uuid' : 'slug = $2'}`, [auth.orgId, ref]);
55
+ return rows[0];
56
+ });
57
+ if (found === undefined)
58
+ throw new McpToolRefusal(`No layer "${ref}" in this organization.`);
59
+ return { id: found.id, slug: found.slug, workspaceId: found.workspace_id };
60
+ }
61
+ /** A workspace by slug or id. */
62
+ async workspace(auth, ref) {
63
+ const found = await this.inOrg(auth, async (client) => {
64
+ const { rows } = await client.query(`SELECT id, slug FROM workspaces WHERE org_id = $1 AND ${UUID.test(ref) ? 'id = $2::uuid' : 'slug = $2'}`, [auth.orgId, ref]);
65
+ return rows[0];
66
+ });
67
+ if (found === undefined)
68
+ throw new McpToolRefusal(`No workspace "${ref}" in this organization.`);
69
+ return found;
70
+ }
71
+ /**
72
+ * A name beside every id: an email for a person, a name for a group or a
73
+ * service account, a slug for a layer or a workspace. Ids the organization
74
+ * does not have come back unnamed rather than refused — a deleted layer is
75
+ * still in last month's log.
76
+ */
77
+ names(auth, ids) {
78
+ const wanted = [...new Set([...ids].filter((id) => UUID.test(id)))];
79
+ if (wanted.length === 0)
80
+ return Promise.resolve(new Map());
81
+ return this.inOrg(auth, async (client) => {
82
+ const { rows } = await client.query(`SELECT id::text, email AS name FROM users WHERE org_id = $1 AND id = ANY($2::uuid[])
83
+ UNION ALL SELECT id::text, name FROM service_accounts WHERE org_id = $1 AND id = ANY($2::uuid[])
84
+ UNION ALL SELECT id::text, name FROM groups WHERE org_id = $1 AND id = ANY($2::uuid[])
85
+ UNION ALL SELECT id::text, slug FROM layers WHERE org_id = $1 AND id = ANY($2::uuid[])
86
+ UNION ALL SELECT id::text, slug FROM workspaces WHERE org_id = $1 AND id = ANY($2::uuid[])`, [auth.orgId, wanted]);
87
+ return new Map(rows.map((r) => [r.id, r.name]));
88
+ });
89
+ }
90
+ /** One grant with its names, or `undefined` when this organization has none by that id. */
91
+ grant(auth, id) {
92
+ if (!UUID.test(id))
93
+ return Promise.resolve(undefined);
94
+ return this.inOrg(auth, async (client) => {
95
+ const { rows } = await client.query(`SELECT principal_type, principal_id, scope_type, scope_id, permission, effect
96
+ FROM grants WHERE org_id = $1 AND id = $2::uuid`, [auth.orgId, id]);
97
+ const row = rows[0];
98
+ return row === undefined
99
+ ? undefined
100
+ : {
101
+ principalType: row.principal_type,
102
+ principalId: row.principal_id,
103
+ scopeType: row.scope_type,
104
+ scopeId: row.scope_id,
105
+ permission: row.permission,
106
+ effect: row.effect,
107
+ };
108
+ });
109
+ }
110
+ /** Counts a person reads before deleting something that holds things. */
111
+ counts(auth, of) {
112
+ return this.inOrg(auth, async (client) => {
113
+ const { rows } = await client.query(`SELECT (SELECT count(*) FROM group_members WHERE org_id = $1 AND group_id = $2::uuid) AS members,
114
+ (SELECT count(*) FROM grants WHERE org_id = $1
115
+ AND ((principal_type = 'group' AND principal_id = $2::uuid)
116
+ OR (scope_type = 'layer' AND scope_id = $3::uuid))) AS grants,
117
+ (SELECT count(*) FROM documents WHERE org_id = $1 AND layer_id = $3::uuid AND deleted_at IS NULL) AS documents`, [auth.orgId, of.group ?? null, of.layer ?? null]);
118
+ const row = rows[0];
119
+ return {
120
+ members: Number(row?.members ?? 0),
121
+ grants: Number(row?.grants ?? 0),
122
+ documents: Number(row?.documents ?? 0),
123
+ };
124
+ });
125
+ }
126
+ }
127
+ //# sourceMappingURL=admin-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-names.js","sourceRoot":"","sources":["../src/admin-names.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { type AuditWriter, type McpReadTool, type McpWriteTool } from '@nacre.work/core';
2
+ import type { AdminNames } from './admin-names.js';
3
+ import { type NotificationStore } from './notifications.js';
4
+ /**
5
+ * Notifications and alert rules on the administrative MCP. docs/mcp-admin.md,
6
+ * "Notifications".
7
+ *
8
+ * Built only where the installation has a mail relay — a tool that queues a
9
+ * message nothing will send is a tool that says it did something it did not —
10
+ * and composed by both processes that hold the core's writes: the MCP process,
11
+ * which proposes, and the API, which applies from the console.
12
+ *
13
+ * ## T35
14
+ *
15
+ * A recipient is named by address or id and resolved to an **active user of
16
+ * the caller's organization**, or the call is refused naming what did not
17
+ * resolve — before anything is stored, which is "refused before a message is
18
+ * composed". The address is used to find a person and then dropped: what is
19
+ * stored is the id, and the worker reads the address again at sending time.
20
+ * Applying resolves the ids once more, because a person disabled in the ten
21
+ * minutes between is somebody the organization has just decided should not be
22
+ * reached.
23
+ */
24
+ export interface NotifyPorts {
25
+ readonly audit: AuditWriter;
26
+ readonly names: AdminNames;
27
+ readonly notifications: NotificationStore;
28
+ }
29
+ export declare function notificationTools(ports: NotifyPorts): readonly (McpReadTool | McpWriteTool)[];
30
+ //# sourceMappingURL=admin-notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notify.d.ts","sourceRoot":"","sources":["../src/admin-notify.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEhB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAGlD,OAAO,EAAmC,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAE5F;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAA;CAC1C;AAmFD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CA8Q7F"}
@@ -0,0 +1,335 @@
1
+ import { ALERT_KINDS, ALERT_WINDOW, isAlertKind, McpToolRefusal, NOTIFY_MAX_RECIPIENTS, NOTIFY_PER_HOUR, notificationText, ruleSentence, } from '@nacre.work/core';
2
+ import { isUuid } from './admin-names.js';
3
+ import { MAX_ALERT_RULES } from './notifications.js';
4
+ const auth = (call) => call.auth;
5
+ const RECIPIENT_PROPS = {
6
+ people: {
7
+ type: 'array',
8
+ items: { type: 'string' },
9
+ maxItems: NOTIFY_MAX_RECIPIENTS,
10
+ description: 'People in this organization, by email address or id. Nobody else can be named: there is no way to ' +
11
+ 'send to an address outside the organization.',
12
+ },
13
+ org_admins: { type: 'boolean', description: "Every active organization administrator, whoever they are when it is sent." },
14
+ };
15
+ const count = (n, one, many = `${one}s`) => `${String(n)} ${n === 1 ? one : many}`;
16
+ /** The references a call named, bounded, before anything is looked up. */
17
+ function references(args) {
18
+ const people = args.people;
19
+ if (people !== undefined && !Array.isArray(people))
20
+ throw new McpToolRefusal("'people' is a list of addresses or ids.");
21
+ const refs = (people ?? []);
22
+ if (refs.some((r) => typeof r !== 'string' || r.trim() === '' || r.length > 320)) {
23
+ throw new McpToolRefusal("Each of 'people' is one person's address or id.");
24
+ }
25
+ if (refs.length > NOTIFY_MAX_RECIPIENTS) {
26
+ throw new McpToolRefusal(`At most ${String(NOTIFY_MAX_RECIPIENTS)} people by name; 'org_admins' reaches every administrator.`);
27
+ }
28
+ if (args.org_admins !== undefined && typeof args.org_admins !== 'boolean')
29
+ throw new McpToolRefusal("'org_admins' is true or false.");
30
+ const toOrgAdmins = args.org_admins === true;
31
+ if (refs.length === 0 && !toOrgAdmins)
32
+ throw new McpToolRefusal("Name the people it goes to, or set 'org_admins'.");
33
+ return { refs: refs, toOrgAdmins };
34
+ }
35
+ /**
36
+ * Who it goes to, as ids and as the sentence a person reads — or a refusal
37
+ * naming every reference that is not an active person here. T35.
38
+ */
39
+ async function recipients(ports, a, args) {
40
+ const { refs, toOrgAdmins } = references(args);
41
+ const { found, unknown } = await ports.notifications.people(a, refs);
42
+ if (unknown.length > 0) {
43
+ throw new McpToolRefusal(`${unknown.map((r) => `"${r}"`).join(', ')} ${unknown.length === 1 ? 'is' : 'are'} not an active person in this ` +
44
+ 'organization. A notification goes only to people here, named by their address — nobody outside it, ' +
45
+ 'and nobody disabled.');
46
+ }
47
+ const people = [...new Map([...found.values()].map((p) => [p.id, p])).values()];
48
+ const emails = people.map((p) => p.email);
49
+ const admins = toOrgAdmins ? await ports.notifications.orgAdmins(a) : 0;
50
+ const parts = [...emails, ...(toOrgAdmins ? [`every organization administrator (${String(admins)} now)`] : [])];
51
+ const words = parts.length <= 1 ? (parts[0] ?? '') : `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1] ?? ''}`;
52
+ return { ids: people.map((p) => p.id), emails, toOrgAdmins, words };
53
+ }
54
+ /** Ids a stored proposal carries, still active — or the refusal saying who is not. */
55
+ async function stillActive(ports, a, ids) {
56
+ const { unknown } = await ports.notifications.people(a, ids);
57
+ if (unknown.length > 0) {
58
+ throw new McpToolRefusal(`${count(unknown.length, 'person', 'people')} it was addressed to ${unknown.length === 1 ? 'is' : 'are'} no longer active ` +
59
+ 'in this organization. Propose it again to the people who are.');
60
+ }
61
+ }
62
+ const storedIds = (input, key) => {
63
+ const value = input[key];
64
+ if (!Array.isArray(value) || value.some((v) => typeof v !== 'string' || !isUuid(v))) {
65
+ throw new Error(`a stored proposal is missing ${key}`);
66
+ }
67
+ return value;
68
+ };
69
+ export function notificationTools(ports) {
70
+ const record = async (call, action, result, target, detail = {}) => {
71
+ const a = auth(call);
72
+ if (Object.keys(target).length === 0)
73
+ throw new Error(`${action.action} was recorded with no target`);
74
+ await ports.audit.write({
75
+ orgId: a.orgId,
76
+ actor: `${a.principal.type}:${a.principal.id}`,
77
+ ...action,
78
+ result,
79
+ surface: 'mcp-admin',
80
+ target: { ...target },
81
+ detail: { ...detail, ...(call.proposal === undefined ? {} : { proposal: call.proposal.id, through: call.proposal.through }) },
82
+ requestId: call.requestId,
83
+ });
84
+ };
85
+ const describeRule = async (a, rule) => {
86
+ const named = await ports.names.names(a, [...rule.recipients, rule.createdBy]);
87
+ return {
88
+ id: rule.id,
89
+ when: ruleSentence(rule),
90
+ kind: rule.kind,
91
+ ...(rule.layerSlug === null ? {} : { layer: rule.layerSlug }),
92
+ ...(rule.threshold === null ? {} : { threshold: rule.threshold, window_minutes: rule.windowMinutes }),
93
+ notifies: [
94
+ ...rule.recipients.map((id) => named.get(id) ?? id),
95
+ ...(rule.toOrgAdmins ? ['every organization administrator'] : []),
96
+ ],
97
+ created_by: named.get(rule.createdBy) ?? rule.createdBy,
98
+ created_at: rule.createdAt,
99
+ last_fired_at: rule.lastFiredAt,
100
+ };
101
+ };
102
+ return [
103
+ {
104
+ kind: 'write',
105
+ name: 'send_notification',
106
+ title: 'Send a notification',
107
+ description: 'Propose an email to people in this organization — by address, or every organization administrator. ' +
108
+ 'A subject and a plain-text body, no links. It says an agent wrote it and who approved it. Only people ' +
109
+ `in this organization can be named. At most ${String(NOTIFY_PER_HOUR)} an hour per organization.`,
110
+ inputSchema: {
111
+ type: 'object',
112
+ properties: {
113
+ ...RECIPIENT_PROPS,
114
+ subject: { type: 'string', maxLength: 200, description: 'One line.' },
115
+ body: { type: 'string', maxLength: 4000, description: 'Plain text; a blank line starts a paragraph. No links.' },
116
+ },
117
+ required: ['subject', 'body'],
118
+ additionalProperties: false,
119
+ },
120
+ async propose(call, args) {
121
+ const a = auth(call);
122
+ // Recipients first, and refused before the text is even looked at: the
123
+ // question T35 asks is answered before anything is composed.
124
+ const to = await recipients(ports, a, args);
125
+ const text = notificationText(args.subject, args.body);
126
+ if ('refused' in text)
127
+ throw new McpToolRefusal(text.refused);
128
+ const consentId = a.delegation?.id;
129
+ if (consentId === undefined)
130
+ throw new McpToolRefusal('A notification is proposed through an administrative connection.');
131
+ return {
132
+ summary: `Email ${to.words}: "${text.subject}".`,
133
+ details: [
134
+ { label: 'to', value: to.words },
135
+ { label: 'subject', value: text.subject },
136
+ { label: 'body', value: text.body },
137
+ { label: 'note', value: 'The message says an agent wrote it, through which connection, and that you approved it.' },
138
+ ],
139
+ input: { recipients: to.ids, to_org_admins: to.toOrgAdmins, subject: text.subject, body: text.body, consent_id: consentId },
140
+ };
141
+ },
142
+ async apply(call, input) {
143
+ const a = auth(call);
144
+ const ids = storedIds(input, 'recipients');
145
+ const consentId = typeof input.consent_id === 'string' && isUuid(input.consent_id) ? input.consent_id : undefined;
146
+ if (consentId === undefined)
147
+ throw new Error('a stored proposal is missing consent_id');
148
+ const text = notificationText(input.subject, input.body);
149
+ try {
150
+ if ('refused' in text)
151
+ throw new McpToolRefusal(text.refused);
152
+ await stillActive(ports, a, ids);
153
+ }
154
+ catch (error) {
155
+ await record(call, { action: 'send_notification' }, 'deny', { connection: consentId }, { recipients: ids.length });
156
+ throw error;
157
+ }
158
+ const queued = await ports.notifications.enqueue(a, {
159
+ recipients: ids,
160
+ toOrgAdmins: input.to_org_admins === true,
161
+ subject: text.subject,
162
+ body: text.body,
163
+ consentId,
164
+ });
165
+ if (queued.kind === 'limited') {
166
+ await record(call, { action: 'send_notification' }, 'deny', { connection: consentId }, { outcome: 'rate_limited' });
167
+ throw new McpToolRefusal(`This organization has queued ${String(NOTIFY_PER_HOUR)} notifications in the last hour, which is the limit. ` +
168
+ 'Try again later.');
169
+ }
170
+ await record(call, { action: 'send_notification' }, 'allow', { notification: queued.id, connection: consentId }, { recipients: ids.length, to_org_admins: input.to_org_admins === true });
171
+ return { notification: queued.id, status: 'queued: the worker sends it within a minute or two' };
172
+ },
173
+ },
174
+ {
175
+ kind: 'write',
176
+ name: 'create_alert_rule',
177
+ title: 'Create an alert rule',
178
+ description: 'Propose a standing rule the worker checks every minute, emailing people in this organization when it ' +
179
+ 'fires: a skill version written by an agent (skill_by_agent), one adding scripts (skill_scripts), an ' +
180
+ 'administrative connection approved (admin_connection), one principal denied at least `threshold` ' +
181
+ 'times in `window_minutes` (denial_spike), or at least `threshold` documents failing to index in ' +
182
+ '`window_minutes` (documents_failed). A counting rule fires at most once per window.',
183
+ inputSchema: {
184
+ type: 'object',
185
+ properties: {
186
+ kind: { type: 'string', enum: Object.keys(ALERT_KINDS) },
187
+ layer: { type: 'string', description: "A layer's slug, to watch only it — for the skill kinds and documents_failed." },
188
+ threshold: { type: 'integer', minimum: 1, maximum: 100000, description: 'For denial_spike and documents_failed.' },
189
+ window_minutes: {
190
+ type: 'integer',
191
+ minimum: ALERT_WINDOW.min,
192
+ maximum: ALERT_WINDOW.max,
193
+ description: `For denial_spike and documents_failed; ${String(ALERT_WINDOW.default)} by default.`,
194
+ },
195
+ ...RECIPIENT_PROPS,
196
+ },
197
+ required: ['kind'],
198
+ additionalProperties: false,
199
+ },
200
+ async propose(call, args) {
201
+ const a = auth(call);
202
+ if (!isAlertKind(args.kind))
203
+ throw new McpToolRefusal(`'kind' is one of ${Object.keys(ALERT_KINDS).join(', ')}.`);
204
+ const kind = args.kind;
205
+ const shape = ALERT_KINDS[kind];
206
+ const to = await recipients(ports, a, args);
207
+ let layer = null;
208
+ if (typeof args.layer === 'string' && args.layer !== '') {
209
+ if (!shape.layered)
210
+ throw new McpToolRefusal(`A ${kind} rule watches the whole organization; it takes no layer.`);
211
+ layer = await ports.names.layer(a, args.layer);
212
+ }
213
+ let threshold = null;
214
+ let windowMinutes = null;
215
+ if (shape.counts) {
216
+ if (typeof args.threshold !== 'number' || !Number.isInteger(args.threshold) || args.threshold < 1 || args.threshold > 100000) {
217
+ throw new McpToolRefusal(`A ${kind} rule needs a 'threshold': a whole number from 1.`);
218
+ }
219
+ threshold = args.threshold;
220
+ const w = args.window_minutes ?? ALERT_WINDOW.default;
221
+ if (typeof w !== 'number' || !Number.isInteger(w) || w < ALERT_WINDOW.min || w > ALERT_WINDOW.max) {
222
+ throw new McpToolRefusal(`'window_minutes' is from ${String(ALERT_WINDOW.min)} to ${String(ALERT_WINDOW.max)}.`);
223
+ }
224
+ windowMinutes = w;
225
+ }
226
+ else if (args.threshold !== undefined || args.window_minutes !== undefined) {
227
+ throw new McpToolRefusal(`A ${kind} rule fires on each one; it takes no threshold or window.`);
228
+ }
229
+ const when = ruleSentence({ kind, layerSlug: layer?.slug ?? null, threshold, windowMinutes });
230
+ return {
231
+ summary: `Email ${to.words} whenever ${when}.`,
232
+ details: [
233
+ { label: 'when', value: when },
234
+ { label: 'to', value: to.words },
235
+ { label: 'checked', value: 'every minute, by the worker, whether or not an agent is connected' },
236
+ ],
237
+ input: {
238
+ kind,
239
+ layer_id: layer?.id ?? null,
240
+ threshold,
241
+ window_minutes: windowMinutes,
242
+ recipients: to.ids,
243
+ to_org_admins: to.toOrgAdmins,
244
+ },
245
+ };
246
+ },
247
+ async apply(call, input) {
248
+ const a = auth(call);
249
+ if (!isAlertKind(input.kind))
250
+ throw new Error('a stored proposal is missing kind');
251
+ const ids = storedIds(input, 'recipients');
252
+ const layerId = typeof input.layer_id === 'string' && isUuid(input.layer_id) ? input.layer_id : null;
253
+ try {
254
+ await stillActive(ports, a, ids);
255
+ if (layerId !== null)
256
+ await ports.names.layer(a, layerId);
257
+ }
258
+ catch (error) {
259
+ await record(call, { action: 'create_alert_rule' }, 'deny', { kind: input.kind });
260
+ throw error;
261
+ }
262
+ const created = await ports.notifications.createRule(a, {
263
+ kind: input.kind,
264
+ layerId,
265
+ threshold: typeof input.threshold === 'number' ? input.threshold : null,
266
+ windowMinutes: typeof input.window_minutes === 'number' ? input.window_minutes : null,
267
+ recipients: ids,
268
+ toOrgAdmins: input.to_org_admins === true,
269
+ });
270
+ if (created.kind === 'full') {
271
+ await record(call, { action: 'create_alert_rule' }, 'deny', { kind: input.kind }, { outcome: 'limit' });
272
+ throw new McpToolRefusal(`This organization keeps ${String(MAX_ALERT_RULES)} rules, which is the limit. Remove one first.`);
273
+ }
274
+ await record(call, { action: 'create_alert_rule' }, 'allow', { alert_rule: created.id, kind: input.kind }, {
275
+ recipients: ids.length,
276
+ to_org_admins: input.to_org_admins === true,
277
+ ...(layerId === null ? {} : { layer_id: layerId }),
278
+ });
279
+ return { alert_rule: created.id };
280
+ },
281
+ },
282
+ {
283
+ kind: 'write',
284
+ name: 'remove_alert_rule',
285
+ title: 'Remove an alert rule',
286
+ description: 'Propose removing an alert rule, by the id list_alert_rules gives. It stops firing once applied.',
287
+ inputSchema: {
288
+ type: 'object',
289
+ properties: { rule: { type: 'string', description: 'The rule id, from list_alert_rules.' } },
290
+ required: ['rule'],
291
+ additionalProperties: false,
292
+ },
293
+ async propose(call, args) {
294
+ const a = auth(call);
295
+ const id = typeof args.rule === 'string' ? args.rule.trim() : '';
296
+ const rule = id === '' ? undefined : await ports.notifications.rule(a, id);
297
+ if (rule === undefined)
298
+ throw new McpToolRefusal(`No alert rule "${id}" in this organization.`);
299
+ const described = await describeRule(a, rule);
300
+ const to = described.notifies.join(', ');
301
+ return {
302
+ summary: `Stop emailing ${to} whenever ${ruleSentence(rule)}.`,
303
+ details: [
304
+ { label: 'when', value: ruleSentence(rule) },
305
+ { label: 'to', value: to },
306
+ ],
307
+ input: { rule_id: rule.id },
308
+ };
309
+ },
310
+ async apply(call, input) {
311
+ const id = typeof input.rule_id === 'string' && isUuid(input.rule_id) ? input.rule_id : undefined;
312
+ if (id === undefined)
313
+ throw new Error('a stored proposal is missing rule_id');
314
+ const removed = await ports.notifications.removeRule(auth(call), id);
315
+ await record(call, { action: 'remove_alert_rule' }, removed ? 'allow' : 'deny', { alert_rule: id });
316
+ if (!removed)
317
+ throw new McpToolRefusal('That rule is already gone.');
318
+ return { removed: true };
319
+ },
320
+ },
321
+ {
322
+ kind: 'read',
323
+ name: 'list_alert_rules',
324
+ title: 'List alert rules',
325
+ description: 'The alert rules this organization keeps: what each watches, whom it emails, and when it last fired.',
326
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
327
+ async run(call) {
328
+ const a = auth(call);
329
+ const rules = await ports.notifications.rules(a);
330
+ return { rules: await Promise.all(rules.map((r) => describeRule(a, r))) };
331
+ },
332
+ },
333
+ ];
334
+ }
335
+ //# sourceMappingURL=admin-notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-notify.js","sourceRoot":"","sources":["../src/admin-notify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type AuditWriter, type McpWriteTool } from '@nacre.work/core';
2
+ import type { Pool } from 'pg';
3
+ import type { OAuthConsents } from './oauth-store.js';
4
+ import type { Groups, Users } from './principals.js';
5
+ import type { Grants, Layers, Workspaces } from './server.js';
6
+ import type { Skills } from './skills.js';
7
+ /**
8
+ * The core's writes on the administrative MCP. docs/mcp-admin.md, "Tools".
9
+ *
10
+ * Every one is two halves, which is the whole design: `propose` resolves what
11
+ * the model named into ids and writes the sentence a person reads, and `apply`
12
+ * makes the change — only ever called when that person presses Apply, in the
13
+ * panel or on the console's Proposals screen. A module's write registered
14
+ * through `registerMcpTools` has exactly this shape, so the core's and a
15
+ * module's go past a person the same way.
16
+ *
17
+ * `apply` goes through the port the REST surface uses for the same change, as
18
+ * the person deciding — so every check that endpoint makes is made again here,
19
+ * ten minutes later at most, and a role lost in between is what decides. It
20
+ * records the same action the REST handler records, on the administrative
21
+ * surface, naming the proposal.
22
+ *
23
+ * What is not here, and why, is in the document: passwords and keys (a
24
+ * plaintext returned to a conversation stays in it), second factors (never on
25
+ * anybody's behalf), and anything above the organization.
26
+ */
27
+ export interface AdminWritePorts {
28
+ readonly pool: Pool;
29
+ readonly role: string;
30
+ readonly audit: AuditWriter;
31
+ readonly grants: Grants;
32
+ readonly groups: Groups;
33
+ readonly users: Users;
34
+ readonly workspaces: Workspaces;
35
+ readonly layers: Layers;
36
+ readonly skills: Skills;
37
+ readonly consents: OAuthConsents;
38
+ }
39
+ export declare function coreAdminWrites(ports: AdminWritePorts): readonly McpWriteTool[];
40
+ //# sourceMappingURL=admin-writes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-writes.d.ts","sourceRoot":"","sources":["../src/admin-writes.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,WAAW,EAGhB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAI9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,KAAK,EAAc,MAAM,EAAE,MAAM,aAAa,CAAA;AAErD;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;CACjC;AA8CD,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,YAAY,EAAE,CAswB/E"}