@capybara-hq/kernel 0.1.0-rc.19 → 0.1.0-rc.20
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/bootstrap.json +25 -25
- package/dist/admin/admin-agents.controller.d.ts +28 -0
- package/dist/admin/admin-agents.controller.js +79 -0
- package/dist/admin/admin-connectors.controller.d.ts +29 -0
- package/dist/admin/admin-connectors.controller.js +79 -0
- package/dist/admin/admin-items.schemas.d.ts +42 -0
- package/dist/admin/admin-items.schemas.js +48 -0
- package/dist/admin/admin.controller.d.ts +11 -20
- package/dist/admin/admin.controller.js +28 -34
- package/dist/admin/admin.module.d.ts +4 -1
- package/dist/admin/admin.module.js +50 -4
- package/dist/admin/admin.schemas.d.ts +4 -0
- package/dist/admin/admin.schemas.js +4 -1
- package/dist/admin/audit.controller.d.ts +14 -0
- package/dist/admin/audit.controller.js +42 -0
- package/dist/admin/audit.schemas.d.ts +8 -0
- package/dist/admin/audit.schemas.js +17 -0
- package/dist/admin/health.service.d.ts +104 -0
- package/dist/admin/health.service.js +246 -0
- package/dist/admin/overview.controller.d.ts +31 -0
- package/dist/admin/overview.controller.js +78 -0
- package/dist/admin/overview.schemas.d.ts +8 -0
- package/dist/admin/overview.schemas.js +13 -0
- package/dist/admin/overview.service.d.ts +112 -0
- package/dist/admin/overview.service.js +223 -0
- package/dist/admin/platform-audit.service.d.ts +41 -0
- package/dist/admin/platform-audit.service.js +70 -0
- package/dist/admin/usage.service.d.ts +149 -0
- package/dist/admin/usage.service.js +321 -0
- package/dist/agents/admin-agents.d.ts +92 -0
- package/dist/agents/admin-agents.js +170 -0
- package/dist/agents/agents.module.d.ts +4 -3
- package/dist/agents/agents.module.js +7 -3
- package/dist/agents/agents.service.d.ts +41 -3
- package/dist/agents/agents.service.js +113 -27
- package/dist/agents/definition.d.ts +16 -0
- package/dist/agents/definition.js +32 -2
- package/dist/agents/org-templates.js +4 -0
- package/dist/background/launch.d.ts +1 -1
- package/dist/background/launch.js +3 -3
- package/dist/background/tasks.service.d.ts +35 -3
- package/dist/background/tasks.service.js +91 -36
- package/dist/catalog/catalog.module.js +8 -2
- package/dist/channels/channels.module.js +11 -2
- package/dist/channels/supervisor.d.ts +2 -0
- package/dist/channels/supervisor.js +4 -0
- package/dist/conversations/events.js +1 -1
- package/dist/conversations/titles.js +4 -8
- package/dist/db/kernel-schema.d.ts +8 -1
- package/dist/devices/devices.module.js +5 -2
- package/dist/errors.d.ts +22 -2
- package/dist/errors.js +22 -2
- package/dist/evals/evals.module.js +11 -2
- package/dist/extensions/permission-view.js +3 -3
- package/dist/http/api.module.js +10 -1
- package/dist/identity/settings.js +3 -2
- package/dist/identity/write-actor.d.ts +11 -0
- package/dist/identity/write-actor.js +7 -0
- package/dist/inbox/inbox.service.d.ts +2 -1
- package/dist/inbox/platform-inbox.service.d.ts +7 -1
- package/dist/inbox/platform-inbox.service.js +20 -2
- package/dist/infra/presence/presence.module.d.ts +6 -0
- package/dist/infra/presence/presence.module.js +22 -0
- package/dist/infra/presence/replica-presence.d.ts +87 -0
- package/dist/infra/presence/replica-presence.js +189 -0
- package/dist/kernel.module.js +3 -0
- package/dist/lifecycle/kernel-lifecycle.d.ts +3 -1
- package/dist/lifecycle/kernel-lifecycle.js +17 -3
- package/dist/local-devices/local-device-gateway.d.ts +6 -0
- package/dist/local-devices/local-device-gateway.js +12 -0
- package/dist/overrides.d.ts +7 -1
- package/dist/overrides.js +4 -0
- package/dist/platform/exclusions.js +15 -0
- package/dist/platform/operation.d.ts +1 -1
- package/dist/platform/operation.js +1 -1
- package/dist/platform/ops/admin-items.ops.d.ts +37 -0
- package/dist/platform/ops/admin-items.ops.js +278 -0
- package/dist/platform/ops/admin.ops.d.ts +10 -10
- package/dist/platform/ops/admin.ops.js +111 -42
- package/dist/platform/ops/agent-skills.ops.d.ts +29 -3
- package/dist/platform/ops/agent-skills.ops.js +290 -7
- package/dist/platform/ops/inbox.ops.js +7 -4
- package/dist/platform/ops/platform.ops.js +1 -1
- package/dist/platform/ops/skills.ops.js +1 -57
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +8 -0
- package/dist/roles/gateway-role.d.ts +4 -0
- package/dist/roles/gateway-role.js +8 -0
- package/dist/runtime/run-jobs.js +4 -2
- package/dist/sandbox/sandbox-file-reader.d.ts +35 -0
- package/dist/sandbox/sandbox-file-reader.js +98 -0
- package/dist/skills/agent-skills.controller.d.ts +48 -0
- package/dist/skills/agent-skills.controller.js +131 -0
- package/dist/skills/agent-skills.service.d.ts +262 -0
- package/dist/skills/agent-skills.service.js +996 -0
- package/dist/skills/catalog.d.ts +8 -6
- package/dist/skills/catalog.js +8 -4
- package/dist/skills/changes.controller.d.ts +5 -22
- package/dist/skills/changes.controller.js +7 -55
- package/dist/skills/changes.service.d.ts +69 -115
- package/dist/skills/changes.service.js +249 -599
- package/dist/skills/conversation-catalog.d.ts +53 -0
- package/dist/skills/conversation-catalog.js +149 -0
- package/dist/skills/invocation-keys.d.ts +1 -1
- package/dist/skills/invocation-keys.js +1 -1
- package/dist/skills/package.d.ts +0 -5
- package/dist/skills/package.js +0 -14
- package/dist/skills/skills.controller.js +1 -10
- package/dist/skills/skills.module.js +15 -2
- package/dist/skills/skills.schemas.d.ts +11 -15
- package/dist/skills/skills.schemas.js +27 -20
- package/dist/skills/skills.service.d.ts +37 -30
- package/dist/skills/skills.service.js +107 -59
- package/dist/skills/usable.d.ts +4 -3
- package/dist/skills/usable.js +13 -4
- package/dist/tools/admin-connectors.d.ts +69 -0
- package/dist/tools/admin-connectors.js +135 -0
- package/dist/tools/builtin-names.d.ts +9 -14
- package/dist/tools/builtin-names.js +13 -22
- package/dist/tools/builtins/builtin-tools.d.ts +2 -3
- package/dist/tools/builtins/builtin-tools.js +21 -26
- package/dist/tools/builtins/sandbox/bash.js +2 -2
- package/dist/tools/builtins/skill-load.js +1 -0
- package/dist/tools/connectors.controller.d.ts +5 -1
- package/dist/tools/connectors.controller.js +17 -3
- package/dist/tools/connectors.service.d.ts +20 -1
- package/dist/tools/connectors.service.js +61 -0
- package/dist/tools/legacy-keys.js +0 -1
- package/dist/tools/org-tool-switches.d.ts +3 -2
- package/dist/tools/org-tool-switches.js +4 -4
- package/dist/tools/policy.d.ts +7 -9
- package/dist/tools/policy.js +11 -22
- package/dist/tools/tools.module.js +3 -0
- package/package.json +1 -1
- package/src/migrations/0109_audit_platform_idx.sql +5 -0
- package/src/migrations/0110_usage_time_idx.sql +8 -0
- package/src/migrations/0111_agent_skill_sharing.sql +22 -0
- package/src/migrations/0112_skill_share_requests.sql +4 -0
- package/dist/tools/builtins/skill-write.d.ts +0 -59
- package/dist/tools/builtins/skill-write.js +0 -244
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { ToolError } from "@capybara-hq/plugin-sdk";
|
|
11
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { AdminAgentListQuery, AdminConnectorListQuery, AgentIdParams, ConnectorIdParams, ConnectorStatusBody, } from "../../admin/admin-items.schemas.js";
|
|
14
|
+
import { AdminAgents } from "../../agents/admin-agents.js";
|
|
15
|
+
import { AgentsService } from "../../agents/agents.service.js";
|
|
16
|
+
import { ApiError } from "../../errors.js";
|
|
17
|
+
import { KernelDb } from "../../infra/database/kernel-db.js";
|
|
18
|
+
import { MembersService } from "../../orgs/members.service.js";
|
|
19
|
+
import { rootConversationOf } from "../../runtime/subagents/child.js";
|
|
20
|
+
import { AdminConnectors } from "../../tools/admin-connectors.js";
|
|
21
|
+
import { ConnectorsService } from "../../tools/connectors.service.js";
|
|
22
|
+
import { defineOperation } from "../operation.js";
|
|
23
|
+
import { PlatformOperations } from "../registry.js";
|
|
24
|
+
import { viaAgent } from "./errors.js";
|
|
25
|
+
import { named, orElse } from "./summary.js";
|
|
26
|
+
/**
|
|
27
|
+
* Refusal of an item another org owns: the tool's results enter this org's event log, where
|
|
28
|
+
* another org's items do not belong (Platform tool spec §6.1, §14.5 item 1).
|
|
29
|
+
*/
|
|
30
|
+
export const OTHER_ORG_ITEM = "This belongs to another organization; the Platform tool acts only on this conversation's organization. Use the admin console.";
|
|
31
|
+
const AgentIdInput = AgentIdParams.strict();
|
|
32
|
+
const ConnectorIdInput = ConnectorIdParams.strict();
|
|
33
|
+
/**
|
|
34
|
+
* The super admin's agent and connector operations of the `Platform` tool (admin agents and
|
|
35
|
+
* connectors spec §4.5): the admin console's views and writes, run through the same services as
|
|
36
|
+
* the routes. Unlike the console they cover only the conversation's org: lists filter to it, and
|
|
37
|
+
* reads and writes of another org's item are refused (Platform tool spec §6.1). The owner transfer
|
|
38
|
+
* is excluded (human-only, as the org's own transfer route).
|
|
39
|
+
*/
|
|
40
|
+
let AdminItemsOperations = class AdminItemsOperations {
|
|
41
|
+
registry;
|
|
42
|
+
adminAgents;
|
|
43
|
+
agents;
|
|
44
|
+
adminConnectors;
|
|
45
|
+
connectors;
|
|
46
|
+
members;
|
|
47
|
+
kdb;
|
|
48
|
+
constructor(registry, adminAgents, agents, adminConnectors, connectors, members, kdb) {
|
|
49
|
+
this.registry = registry;
|
|
50
|
+
this.adminAgents = adminAgents;
|
|
51
|
+
this.agents = agents;
|
|
52
|
+
this.adminConnectors = adminConnectors;
|
|
53
|
+
this.connectors = connectors;
|
|
54
|
+
this.members = members;
|
|
55
|
+
this.kdb = kdb;
|
|
56
|
+
}
|
|
57
|
+
/** The agent's admin detail when it is the conversation org's; else {@link OTHER_ORG_ITEM}. */
|
|
58
|
+
async agentOf(agentId, ctx) {
|
|
59
|
+
const agent = await this.adminAgents.get(agentId);
|
|
60
|
+
if (agent.orgId !== ctx.orgId)
|
|
61
|
+
throw new ToolError(OTHER_ORG_ITEM);
|
|
62
|
+
return agent;
|
|
63
|
+
}
|
|
64
|
+
/** The connector's admin detail when it is the conversation org's; else {@link OTHER_ORG_ITEM}. */
|
|
65
|
+
async connectorOf(connectorId, ctx) {
|
|
66
|
+
const connector = await this.adminConnectors.get(connectorId);
|
|
67
|
+
if (connector.orgId !== ctx.orgId)
|
|
68
|
+
throw new ToolError(OTHER_ORG_ITEM);
|
|
69
|
+
return connector;
|
|
70
|
+
}
|
|
71
|
+
/** A precheck from a read: its `ApiError` code, or `notFound` for another org's item. */
|
|
72
|
+
async precheck(read, notFound) {
|
|
73
|
+
try {
|
|
74
|
+
await read();
|
|
75
|
+
return { ok: true };
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
if (e instanceof ToolError)
|
|
79
|
+
return { ok: false, code: notFound, message: e.message };
|
|
80
|
+
if (e instanceof ApiError)
|
|
81
|
+
return { ok: false, code: e.code, message: e.message };
|
|
82
|
+
throw e;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
onModuleInit() {
|
|
86
|
+
this.registry.register([
|
|
87
|
+
defineOperation({
|
|
88
|
+
id: "admin.agents-list",
|
|
89
|
+
description: "List this org's agents as the admin console shows them (super admin): private ones too, with owner, visibility, whether the agent needs a new owner and the template it was created from. q searches the name; pages by limit (default 50), pass the returned next as after. Other orgs' agents are only in the admin console.",
|
|
90
|
+
input: AdminAgentListQuery.pick({ q: true, after: true, limit: true }).strict(),
|
|
91
|
+
example: { limit: 50 },
|
|
92
|
+
authz: { superAdmin: true },
|
|
93
|
+
mirrors: ["GET /api/v1/admin/agents"],
|
|
94
|
+
confirm: "read",
|
|
95
|
+
run: (input, ctx) => this.adminAgents.list({ ...input, orgId: ctx.orgId }),
|
|
96
|
+
}),
|
|
97
|
+
defineOperation({
|
|
98
|
+
id: "admin.agent-get",
|
|
99
|
+
description: "Read one agent of this org as the admin console shows it (super admin): its definition with the instructions in full, members, profile with quick action labels, versions count, the skills and connectors it uses by name. Never its conversations or credentials.",
|
|
100
|
+
input: AgentIdInput,
|
|
101
|
+
example: { agentId: "7c1a5b0e-9a53-4c6b-8d64-2f5a1b0c3e77" },
|
|
102
|
+
authz: { superAdmin: true },
|
|
103
|
+
mirrors: ["GET /api/v1/admin/agents/:agentId"],
|
|
104
|
+
confirm: "read",
|
|
105
|
+
errors: [{ code: "agent.not_found", next: "Check the agent id with admin.agents-list." }],
|
|
106
|
+
run: async (input, ctx) => ({ agent: await this.agentOf(input.agentId, ctx) }),
|
|
107
|
+
}),
|
|
108
|
+
defineOperation({
|
|
109
|
+
id: "admin.agent-delete",
|
|
110
|
+
description: "Delete an agent of this org on the platform's behalf (super admin), whoever owns it: its conversations stay readable but it can no longer be used, its eval runs stop and its pending skill changes are cancelled. The org's audit log records that the platform acted. The agent you are running as cannot be deleted.",
|
|
111
|
+
input: AgentIdInput,
|
|
112
|
+
example: { agentId: "7c1a5b0e-9a53-4c6b-8d64-2f5a1b0c3e77" },
|
|
113
|
+
authz: { superAdmin: true },
|
|
114
|
+
mirrors: ["DELETE /api/v1/admin/agents/:agentId"],
|
|
115
|
+
confirm: "platform",
|
|
116
|
+
errors: [
|
|
117
|
+
{ code: "agent.not_found", next: "Check the agent id with admin.agents-list." },
|
|
118
|
+
{
|
|
119
|
+
code: "agent.delete_current",
|
|
120
|
+
next: "This is the agent you are running as: ask the person to delete it in the admin console, or delete a different agent.",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
precheck: async (i, ctx) => {
|
|
124
|
+
// the run's agent is the root conversation's: a sub-agent runs under its parent's agent
|
|
125
|
+
const rootId = await rootConversationOf(this.kdb.db, ctx.conversationId);
|
|
126
|
+
const root = await this.kdb.db
|
|
127
|
+
.selectFrom("conversations")
|
|
128
|
+
.select("agent_id")
|
|
129
|
+
.where("id", "=", rootId)
|
|
130
|
+
.executeTakeFirst();
|
|
131
|
+
if (i.agentId === ctx.agentId || i.agentId === root?.agent_id) {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
code: "agent.delete_current",
|
|
135
|
+
message: "The agent you are running as cannot be deleted from here.",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return this.precheck(() => this.agentOf(i.agentId, ctx), "agent.not_found");
|
|
139
|
+
},
|
|
140
|
+
summary: async (i, ctx) => {
|
|
141
|
+
const n = await orElse(async () => named((await this.agentOf(i.agentId, ctx)).name), i.agentId);
|
|
142
|
+
return {
|
|
143
|
+
zh: `以平台身份删除智能体「${n}」;它的对话仍可查看,但不能再使用`,
|
|
144
|
+
en: `Delete agent "${n}" on the platform's behalf; its conversations stay readable but it can no longer be used`,
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
run: async (input, ctx) => {
|
|
148
|
+
await this.agentOf(input.agentId, ctx);
|
|
149
|
+
await this.agents.softDeleteAsPlatform(input.agentId, { accountId: ctx.actorAccountId }, viaAgent("admin.agent-delete", ctx));
|
|
150
|
+
return { id: input.agentId, deleted: true };
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
defineOperation({
|
|
154
|
+
id: "admin.connectors-list",
|
|
155
|
+
description: "List this org's connectors as the admin console shows them (super admin): name, protocol, status, tool count, who created it and from which template. Never an endpoint, credential or whether anyone has one. status and q (name search) filter; pages by limit (default 50), pass the returned next as after. Other orgs' connectors are only in the admin console.",
|
|
156
|
+
input: AdminConnectorListQuery.pick({
|
|
157
|
+
status: true,
|
|
158
|
+
q: true,
|
|
159
|
+
after: true,
|
|
160
|
+
limit: true,
|
|
161
|
+
}).strict(),
|
|
162
|
+
example: { limit: 50 },
|
|
163
|
+
authz: { superAdmin: true },
|
|
164
|
+
mirrors: ["GET /api/v1/admin/connectors"],
|
|
165
|
+
confirm: "read",
|
|
166
|
+
run: (input, ctx) => this.adminConnectors.list({ ...input, orgId: ctx.orgId }),
|
|
167
|
+
}),
|
|
168
|
+
defineOperation({
|
|
169
|
+
id: "admin.connector-get",
|
|
170
|
+
description: "Read one connector of this org as the admin console shows it (super admin): its endpoint with large fields as size and hash only, the auth type and who holds the credential (the org or each person), and its tools with their state and default policy. Never a credential, token or secret, nor whether any member has one.",
|
|
171
|
+
input: ConnectorIdInput,
|
|
172
|
+
example: { connectorId: "5d2e8f1a-0b3c-4d5e-8f9a-1b2c3d4e5f60" },
|
|
173
|
+
authz: { superAdmin: true },
|
|
174
|
+
mirrors: ["GET /api/v1/admin/connectors/:connectorId"],
|
|
175
|
+
confirm: "read",
|
|
176
|
+
errors: [
|
|
177
|
+
{
|
|
178
|
+
code: "connector.not_found",
|
|
179
|
+
next: "Check the connector id with admin.connectors-list.",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
run: async (input, ctx) => ({
|
|
183
|
+
connector: await this.connectorOf(input.connectorId, ctx),
|
|
184
|
+
}),
|
|
185
|
+
}),
|
|
186
|
+
defineOperation({
|
|
187
|
+
id: "admin.connector-set-status",
|
|
188
|
+
description: "Disable or enable a connector of this org on the platform's behalf (super admin): status disabled takes its tools away from every agent until it is enabled again. Its endpoint and auth stay the org's to edit. The org's audit log records that the platform acted.",
|
|
189
|
+
input: z
|
|
190
|
+
.object({
|
|
191
|
+
connectorId: ConnectorIdParams.shape.connectorId,
|
|
192
|
+
status: ConnectorStatusBody.shape.status,
|
|
193
|
+
})
|
|
194
|
+
.strict(),
|
|
195
|
+
example: {
|
|
196
|
+
connectorId: "5d2e8f1a-0b3c-4d5e-8f9a-1b2c3d4e5f60",
|
|
197
|
+
status: "disabled",
|
|
198
|
+
},
|
|
199
|
+
authz: { superAdmin: true },
|
|
200
|
+
mirrors: ["PATCH /api/v1/admin/connectors/:connectorId"],
|
|
201
|
+
confirm: "platform",
|
|
202
|
+
errors: [
|
|
203
|
+
{
|
|
204
|
+
code: "connector.not_found",
|
|
205
|
+
next: "Check the connector id with admin.connectors-list.",
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
precheck: (i, ctx) => this.precheck(() => this.connectorOf(i.connectorId, ctx), "connector.not_found"),
|
|
209
|
+
summary: async (i, ctx) => {
|
|
210
|
+
const n = await orElse(async () => named((await this.connectorOf(i.connectorId, ctx)).name), i.connectorId);
|
|
211
|
+
return i.status === "disabled"
|
|
212
|
+
? {
|
|
213
|
+
zh: `以平台身份停用连接器「${n}」;智能体将失去其工具,直到重新启用`,
|
|
214
|
+
en: `Disable connector "${n}" on the platform's behalf; agents lose its tools until it is enabled again`,
|
|
215
|
+
}
|
|
216
|
+
: {
|
|
217
|
+
zh: `以平台身份启用连接器「${n}」`,
|
|
218
|
+
en: `Enable connector "${n}" on the platform's behalf`,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
run: async (input, ctx) => {
|
|
222
|
+
await this.connectorOf(input.connectorId, ctx);
|
|
223
|
+
await this.connectors.setStatusAsPlatform(input.connectorId, input.status, { accountId: ctx.actorAccountId }, viaAgent("admin.connector-set-status", ctx));
|
|
224
|
+
return { connector: await this.adminConnectors.get(input.connectorId) };
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
defineOperation({
|
|
228
|
+
id: "admin.connector-delete",
|
|
229
|
+
description: "Delete a connector of this org on the platform's behalf (super admin): its credentials are deleted too and agents that use it lose its tools. Creating it again needs its credentials entered on the web. The org's audit log records that the platform acted.",
|
|
230
|
+
input: ConnectorIdInput,
|
|
231
|
+
example: { connectorId: "5d2e8f1a-0b3c-4d5e-8f9a-1b2c3d4e5f60" },
|
|
232
|
+
authz: { superAdmin: true },
|
|
233
|
+
mirrors: ["DELETE /api/v1/admin/connectors/:connectorId"],
|
|
234
|
+
confirm: "platform",
|
|
235
|
+
errors: [
|
|
236
|
+
{
|
|
237
|
+
code: "connector.not_found",
|
|
238
|
+
next: "Check the connector id with admin.connectors-list.",
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
precheck: (i, ctx) => this.precheck(() => this.connectorOf(i.connectorId, ctx), "connector.not_found"),
|
|
242
|
+
summary: async (i, ctx) => {
|
|
243
|
+
const n = await orElse(async () => named((await this.connectorOf(i.connectorId, ctx)).name), i.connectorId);
|
|
244
|
+
return {
|
|
245
|
+
zh: `以平台身份删除连接器「${n}」及其全部凭据;使用它的智能体将失去其工具`,
|
|
246
|
+
en: `Delete connector "${n}" and all its credentials on the platform's behalf; agents that use it lose its tools`,
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
run: async (input, ctx) => {
|
|
250
|
+
await this.connectorOf(input.connectorId, ctx);
|
|
251
|
+
await this.connectors.removeAsPlatform(input.connectorId, { accountId: ctx.actorAccountId }, viaAgent("admin.connector-delete", ctx));
|
|
252
|
+
return { id: input.connectorId, deleted: true };
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
255
|
+
defineOperation({
|
|
256
|
+
id: "admin.org-members",
|
|
257
|
+
description: "List the members of this conversation's org as the admin console reads them (super admin): account id, display name, email, role, when they joined and whether the account is the org's anonymous guest.",
|
|
258
|
+
input: z.object({}).strict(),
|
|
259
|
+
example: {},
|
|
260
|
+
authz: { superAdmin: true },
|
|
261
|
+
mirrors: ["GET /api/v1/admin/orgs/:orgId/members"],
|
|
262
|
+
confirm: "read",
|
|
263
|
+
run: async (_input, ctx) => ({ members: await this.members.list(ctx.orgId) }),
|
|
264
|
+
}),
|
|
265
|
+
]);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
AdminItemsOperations = __decorate([
|
|
269
|
+
Injectable(),
|
|
270
|
+
__param(0, Inject(PlatformOperations)),
|
|
271
|
+
__param(1, Inject(AdminAgents)),
|
|
272
|
+
__param(2, Inject(AgentsService)),
|
|
273
|
+
__param(3, Inject(AdminConnectors)),
|
|
274
|
+
__param(4, Inject(ConnectorsService)),
|
|
275
|
+
__param(5, Inject(MembersService)),
|
|
276
|
+
__param(6, Inject(KernelDb))
|
|
277
|
+
], AdminItemsOperations);
|
|
278
|
+
export { AdminItemsOperations };
|
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
import { type OnModuleInit } from "@nestjs/common";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { AdminHealthService } from "../../admin/health.service.js";
|
|
3
|
+
import { AdminOverviewService } from "../../admin/overview.service.js";
|
|
4
|
+
import { PlatformAuditService } from "../../admin/platform-audit.service.js";
|
|
5
|
+
import { AdminUsageService } from "../../admin/usage.service.js";
|
|
4
6
|
import { ExtensionDisplayNames } from "../../extensions/display-name.js";
|
|
5
7
|
import { ExtensionRegistry } from "../../extensions/registry.js";
|
|
6
|
-
import { AccountsService } from "../../identity/accounts.js";
|
|
7
8
|
import { SettingsService } from "../../identity/settings.js";
|
|
8
9
|
import { ModelEnablement } from "../../models/enablement.js";
|
|
9
10
|
import { PricesService } from "../../models/prices.js";
|
|
10
11
|
import { ModelPurposesService } from "../../models/purposes.js";
|
|
11
|
-
import { OrgsService } from "../../orgs/orgs.service.js";
|
|
12
12
|
import { ProviderRegistry } from "../../runtime/providers.js";
|
|
13
13
|
import { ThemesService } from "../../themes/themes.service.js";
|
|
14
14
|
import { PlatformOperations } from "../registry.js";
|
|
15
15
|
/**
|
|
16
|
-
* Admin console operations of the `Platform` tool (spec §14.2): the dashboard
|
|
16
|
+
* Admin console operations of the `Platform` tool (spec §14.2): the dashboard numbers and attention lists, the platform audit log, the
|
|
17
17
|
* deployment settings, and the platform's models (the model catalog with the platform's disabled
|
|
18
18
|
* list, the price table, the default model per purpose). Super admin only, through their own
|
|
19
19
|
* private agent. The per-org overrides of models and purposes are the org's own pages.
|
|
20
20
|
*/
|
|
21
21
|
export declare class AdminOperations implements OnModuleInit {
|
|
22
22
|
private readonly registry;
|
|
23
|
-
private readonly extensions;
|
|
24
|
-
private readonly orgs;
|
|
25
|
-
private readonly accounts;
|
|
26
|
-
private readonly catalogRequests;
|
|
27
23
|
private readonly settings;
|
|
28
24
|
private readonly providers;
|
|
29
25
|
private readonly displayNames;
|
|
@@ -32,7 +28,11 @@ export declare class AdminOperations implements OnModuleInit {
|
|
|
32
28
|
private readonly prices;
|
|
33
29
|
private readonly purposes;
|
|
34
30
|
private readonly themes;
|
|
35
|
-
|
|
31
|
+
private readonly audit;
|
|
32
|
+
private readonly overview;
|
|
33
|
+
private readonly health;
|
|
34
|
+
private readonly usage;
|
|
35
|
+
constructor(registry: PlatformOperations, settings: SettingsService, providers: ProviderRegistry, displayNames: ExtensionDisplayNames, extensionRegistry: ExtensionRegistry, enablement: ModelEnablement, prices: PricesService, purposes: ModelPurposesService, themes: ThemesService, audit: PlatformAuditService, overview: AdminOverviewService, health: AdminHealthService, usage: AdminUsageService);
|
|
36
36
|
private assertProvider;
|
|
37
37
|
onModuleInit(): void;
|
|
38
38
|
}
|
|
@@ -10,20 +10,21 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
10
10
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { DeploymentSettingsBody, PurposeParams } from "../../admin/admin.schemas.js";
|
|
13
|
-
import {
|
|
13
|
+
import { AuditListQuery } from "../../admin/audit.schemas.js";
|
|
14
|
+
import { AdminHealthService } from "../../admin/health.service.js";
|
|
15
|
+
import { UsageQuery } from "../../admin/overview.schemas.js";
|
|
16
|
+
import { AdminOverviewService } from "../../admin/overview.service.js";
|
|
17
|
+
import { PlatformAuditService } from "../../admin/platform-audit.service.js";
|
|
18
|
+
import { AdminUsageService } from "../../admin/usage.service.js";
|
|
14
19
|
import { ApiError } from "../../errors.js";
|
|
15
|
-
import { ExtensionAdmin } from "../../extensions/admin-view.js";
|
|
16
20
|
import { ExtensionDisplayNames } from "../../extensions/display-name.js";
|
|
17
|
-
import { extensionNeedsAction } from "../../extensions/permission-view.js";
|
|
18
21
|
import { ExtensionRegistry } from "../../extensions/registry.js";
|
|
19
|
-
import { AccountsService } from "../../identity/accounts.js";
|
|
20
22
|
import { DEPLOYMENT_SETTINGS_PATCH, SettingsService } from "../../identity/settings.js";
|
|
21
23
|
import { ModelDisabledPatch, ModelProviderParams } from "../../models/enablement.controller.js";
|
|
22
24
|
import { ModelEnablement } from "../../models/enablement.js";
|
|
23
25
|
import { MODEL_PRICE_PUT, PricesService } from "../../models/prices.js";
|
|
24
26
|
import { purposeTargetOf } from "../../models/purposes.controller.js";
|
|
25
27
|
import { ModelPurposesService, PURPOSE_PUT, withDeclaredByNames } from "../../models/purposes.js";
|
|
26
|
-
import { OrgsService } from "../../orgs/orgs.service.js";
|
|
27
28
|
import { ProviderRegistry } from "../../runtime/providers.js";
|
|
28
29
|
import { ThemesService } from "../../themes/themes.service.js";
|
|
29
30
|
import { defineOperation } from "../operation.js";
|
|
@@ -46,18 +47,49 @@ const SettingsInput = DEPLOYMENT_SETTINGS_PATCH.pick({
|
|
|
46
47
|
const ProviderId = ModelProviderParams.shape.provider.describe("The model provider (models.platform-list), e.g. the plugin name of its extension.");
|
|
47
48
|
/** `provider/model` of a purpose target, or null. */
|
|
48
49
|
const refOf = (t) => t === null ? null : `${t.model.provider}/${t.model.model}`;
|
|
50
|
+
/** The attention lists as compact rows (`next`: the admin console page that fixes it). */
|
|
51
|
+
function attentionRows(a) {
|
|
52
|
+
return {
|
|
53
|
+
items: [
|
|
54
|
+
...a.extensions.map((e) => ({
|
|
55
|
+
kind: "extension",
|
|
56
|
+
id: e.name,
|
|
57
|
+
name: localized(e.displayName, e.name).en,
|
|
58
|
+
state: e.status,
|
|
59
|
+
reason: e.reason,
|
|
60
|
+
next: `Fix it on the extension's page in the admin console (/admin/extensions/${e.name}).`,
|
|
61
|
+
})),
|
|
62
|
+
...a.updates.map((u) => ({
|
|
63
|
+
kind: "update",
|
|
64
|
+
id: u.itemId,
|
|
65
|
+
name: localized(u.displayName, u.name).en,
|
|
66
|
+
state: "newer_in_marketplace",
|
|
67
|
+
reason: `${u.version} -> ${u.marketplaceVersion}`,
|
|
68
|
+
next: `Upgrade it on the plugin's page in the admin console (/admin/plugins/${u.itemId}); a newer version is never imported automatically.`,
|
|
69
|
+
})),
|
|
70
|
+
...a.providers.map((p) => ({
|
|
71
|
+
kind: "provider",
|
|
72
|
+
id: p.plugin,
|
|
73
|
+
name: localized(p.displayName, p.plugin).en,
|
|
74
|
+
state: p.problem === "list_failed" ? "error" : "ok",
|
|
75
|
+
reason: p.problem,
|
|
76
|
+
next: p.problem === "list_failed"
|
|
77
|
+
? `Check the provider's deployment config and connection on its extension page (/admin/extensions/${p.plugin}); the models page (/admin/models) shows the list.`
|
|
78
|
+
: "The provider lists no chat model that is switched on: switch one on in the admin console (/admin/models) or check the provider's config.",
|
|
79
|
+
})),
|
|
80
|
+
],
|
|
81
|
+
more: a.more,
|
|
82
|
+
unavailable: a.unavailable,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
49
85
|
/**
|
|
50
|
-
* Admin console operations of the `Platform` tool (spec §14.2): the dashboard
|
|
86
|
+
* Admin console operations of the `Platform` tool (spec §14.2): the dashboard numbers and attention lists, the platform audit log, the
|
|
51
87
|
* deployment settings, and the platform's models (the model catalog with the platform's disabled
|
|
52
88
|
* list, the price table, the default model per purpose). Super admin only, through their own
|
|
53
89
|
* private agent. The per-org overrides of models and purposes are the org's own pages.
|
|
54
90
|
*/
|
|
55
91
|
let AdminOperations = class AdminOperations {
|
|
56
92
|
registry;
|
|
57
|
-
extensions;
|
|
58
|
-
orgs;
|
|
59
|
-
accounts;
|
|
60
|
-
catalogRequests;
|
|
61
93
|
settings;
|
|
62
94
|
providers;
|
|
63
95
|
displayNames;
|
|
@@ -66,12 +98,12 @@ let AdminOperations = class AdminOperations {
|
|
|
66
98
|
prices;
|
|
67
99
|
purposes;
|
|
68
100
|
themes;
|
|
69
|
-
|
|
101
|
+
audit;
|
|
102
|
+
overview;
|
|
103
|
+
health;
|
|
104
|
+
usage;
|
|
105
|
+
constructor(registry, settings, providers, displayNames, extensionRegistry, enablement, prices, purposes, themes, audit, overview, health, usage) {
|
|
70
106
|
this.registry = registry;
|
|
71
|
-
this.extensions = extensions;
|
|
72
|
-
this.orgs = orgs;
|
|
73
|
-
this.accounts = accounts;
|
|
74
|
-
this.catalogRequests = catalogRequests;
|
|
75
107
|
this.settings = settings;
|
|
76
108
|
this.providers = providers;
|
|
77
109
|
this.displayNames = displayNames;
|
|
@@ -80,6 +112,10 @@ let AdminOperations = class AdminOperations {
|
|
|
80
112
|
this.prices = prices;
|
|
81
113
|
this.purposes = purposes;
|
|
82
114
|
this.themes = themes;
|
|
115
|
+
this.audit = audit;
|
|
116
|
+
this.overview = overview;
|
|
117
|
+
this.health = health;
|
|
118
|
+
this.usage = usage;
|
|
83
119
|
}
|
|
84
120
|
assertProvider(provider) {
|
|
85
121
|
if (!this.extensionRegistry.list("model-provider").some((e) => e.plugin === provider)) {
|
|
@@ -90,26 +126,59 @@ let AdminOperations = class AdminOperations {
|
|
|
90
126
|
this.registry.register([
|
|
91
127
|
defineOperation({
|
|
92
128
|
id: "admin.overview",
|
|
93
|
-
description: "The admin console's dashboard
|
|
129
|
+
description: "The admin console's dashboard numbers (super admin): orgs, accounts and attentionTotal, everything waiting for a super admin (the platform inbox total plus the extensions, plugin updates and model providers admin.attention lists); partial is true when one of those lists could not be read right now (attentionTotal then leaves it out).",
|
|
94
130
|
input: z.object({}).strict(),
|
|
95
131
|
example: {},
|
|
96
132
|
authz: { superAdmin: true },
|
|
97
133
|
mirrors: ["GET /api/v1/admin/overview"],
|
|
98
134
|
confirm: "read",
|
|
99
|
-
run:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
run: () => this.overview.stats(),
|
|
136
|
+
}),
|
|
137
|
+
defineOperation({
|
|
138
|
+
id: "admin.attention",
|
|
139
|
+
description: "What needs the super admin besides the platform inbox (super admin), one compact row each (kind, id, name, state, reason, next step): extensions (reason config, load_failed, approval, dependency, conflict, converging or legacy_format), platform plugins with a newer marketplace version (reason names both versions) and model providers (reason list_failed or no_chat_model). Each kind lists at most 20 rows; more counts the rest per kind. unavailable names a kind that could not be read right now (try again later). The platform inbox itself is inbox.platform-list.",
|
|
140
|
+
input: z.object({}).strict(),
|
|
141
|
+
example: {},
|
|
142
|
+
authz: { superAdmin: true },
|
|
143
|
+
mirrors: ["GET /api/v1/admin/overview/attention"],
|
|
144
|
+
confirm: "read",
|
|
145
|
+
run: async () => attentionRows(await this.overview.attention()),
|
|
146
|
+
}),
|
|
147
|
+
defineOperation({
|
|
148
|
+
id: "admin.health",
|
|
149
|
+
description: 'How the platform runs right now (super admin), to answer "is the worker up, is the run queue backing up": kernel.version; replicas (live kernel processes: id, roles, version, startedAt) and roles (live replicas per role: api serves requests, worker runs conversations and jobs, gateway holds channel and device connections; 0 workers means no run starts); queues (per kernel queue, e.g. cap-kernel-runs: waiting, active, delayed, failed and failedLastDay, counts only); channels (online, offline: active long connections no replica holds, webhook, disabled, error); devices (online / local); sandboxes (running, suspended, running per provider). unavailable names a source that did not answer (redis or database); its numbers are then null. Failure details are in the kernel logs, not here.',
|
|
150
|
+
input: z.object({}).strict(),
|
|
151
|
+
example: {},
|
|
152
|
+
authz: { superAdmin: true },
|
|
153
|
+
mirrors: ["GET /api/v1/admin/overview/health"],
|
|
154
|
+
confirm: "read",
|
|
155
|
+
run: () => this.health.read(),
|
|
156
|
+
}),
|
|
157
|
+
defineOperation({
|
|
158
|
+
id: "admin.usage",
|
|
159
|
+
description: "What the platform used over the last 7 days (super admin): today and the 6 days before, as calendar days in tz (an IANA zone such as Asia/Shanghai; use the person's own zone). days has one entry per day, oldest first: runs (every run, sub-agent and eval replay runs included), tokens (input, output, cacheRead, cacheWrite of every model call, and embedding) and cost. cost is an estimate from the model prices the super admin entered (models.prices), in currency; it is null when tokens were used and none of them had a price, and costPartial is true when some tokens had no price (the costs then count only the priced ones). orgs lists the 10 orgs that used the most tokens (runs, tokens, cost each); otherOrgs sums the rest (count, runs, tokens, cost). Totals only: never per account, agent or conversation. The answer may be up to 5 minutes old. admin.usage_unavailable means the database took too long: try again later.",
|
|
160
|
+
input: UsageQuery.strict(),
|
|
161
|
+
example: { tz: "Asia/Shanghai" },
|
|
162
|
+
authz: { superAdmin: true },
|
|
163
|
+
mirrors: ["GET /api/v1/admin/overview/usage"],
|
|
164
|
+
confirm: "read",
|
|
165
|
+
errors: [
|
|
166
|
+
{
|
|
167
|
+
code: "admin.usage_unavailable",
|
|
168
|
+
next: "The usage numbers took too long to compute; try again in a few minutes.",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
run: (input) => this.usage.week(input.tz),
|
|
172
|
+
}),
|
|
173
|
+
defineOperation({
|
|
174
|
+
id: "admin.audit",
|
|
175
|
+
description: "The platform-level audit log (super admin), newest first: deployment settings, accounts, marketplaces, catalog imports and scope, extensions, model prices and purposes, setup. Each event has its id, time, actor (id, name, email; null for the kernel itself), viaToken (done with a personal access token), viaAgent (done through the Platform tool), action and target; never the event's details, and never an org's own events. limit is 1 to 100 (default 20); pages by keyset: pass the returned next as params.after (null: no more).",
|
|
176
|
+
input: AuditListQuery.strict(),
|
|
177
|
+
example: { limit: 20 },
|
|
178
|
+
authz: { superAdmin: true },
|
|
179
|
+
mirrors: ["GET /api/v1/admin/audit"],
|
|
180
|
+
confirm: "read",
|
|
181
|
+
run: (input) => this.audit.list(input),
|
|
113
182
|
}),
|
|
114
183
|
defineOperation({
|
|
115
184
|
id: "admin.settings",
|
|
@@ -412,17 +481,17 @@ let AdminOperations = class AdminOperations {
|
|
|
412
481
|
AdminOperations = __decorate([
|
|
413
482
|
Injectable(),
|
|
414
483
|
__param(0, Inject(PlatformOperations)),
|
|
415
|
-
__param(1, Inject(
|
|
416
|
-
__param(2, Inject(
|
|
417
|
-
__param(3, Inject(
|
|
418
|
-
__param(4, Inject(
|
|
419
|
-
__param(5, Inject(
|
|
420
|
-
__param(6, Inject(
|
|
421
|
-
__param(7, Inject(
|
|
422
|
-
__param(8, Inject(
|
|
423
|
-
__param(9, Inject(
|
|
424
|
-
__param(10, Inject(
|
|
425
|
-
__param(11, Inject(
|
|
426
|
-
__param(12, Inject(
|
|
484
|
+
__param(1, Inject(SettingsService)),
|
|
485
|
+
__param(2, Inject(ProviderRegistry)),
|
|
486
|
+
__param(3, Inject(ExtensionDisplayNames)),
|
|
487
|
+
__param(4, Inject(ExtensionRegistry)),
|
|
488
|
+
__param(5, Inject(ModelEnablement)),
|
|
489
|
+
__param(6, Inject(PricesService)),
|
|
490
|
+
__param(7, Inject(ModelPurposesService)),
|
|
491
|
+
__param(8, Inject(ThemesService)),
|
|
492
|
+
__param(9, Inject(PlatformAuditService)),
|
|
493
|
+
__param(10, Inject(AdminOverviewService)),
|
|
494
|
+
__param(11, Inject(AdminHealthService)),
|
|
495
|
+
__param(12, Inject(AdminUsageService))
|
|
427
496
|
], AdminOperations);
|
|
428
497
|
export { AdminOperations };
|
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import { type OnModuleInit } from "@nestjs/common";
|
|
2
2
|
import { AgentsService } from "../../agents/agents.service.js";
|
|
3
|
+
import { KernelDb } from "../../infra/database/kernel-db.js";
|
|
4
|
+
import { SandboxFileReader } from "../../sandbox/sandbox-file-reader.js";
|
|
5
|
+
import { AgentSkillsService } from "../../skills/agent-skills.service.js";
|
|
3
6
|
import { SkillChangesService } from "../../skills/changes.service.js";
|
|
4
7
|
import { SkillInvoker } from "../../skills/invoke.js";
|
|
5
8
|
import { SkillsService } from "../../skills/skills.service.js";
|
|
6
9
|
import { PlatformOperations } from "../registry.js";
|
|
7
10
|
/**
|
|
8
11
|
* The skills of one agent and the skill change inbox detail (`Platform` tool, spec §14): reads of
|
|
9
|
-
* the agent's own skills and of the invocable ones,
|
|
10
|
-
* skill change as the inbox shows it. Deciding a
|
|
12
|
+
* the agent's own skills and of the invocable ones, registering, deleting and sharing a skill,
|
|
13
|
+
* and the detail of a skill change request as the inbox shows it. Deciding a request stays
|
|
14
|
+
* `human-only` (org admins on the web).
|
|
11
15
|
*/
|
|
12
16
|
export declare class AgentSkillsOperations implements OnModuleInit {
|
|
13
17
|
private readonly registry;
|
|
14
18
|
private readonly agents;
|
|
15
19
|
private readonly skills;
|
|
16
20
|
private readonly changes;
|
|
21
|
+
private readonly agentSkills;
|
|
17
22
|
private readonly invoker;
|
|
18
|
-
|
|
23
|
+
private readonly files;
|
|
24
|
+
private readonly db;
|
|
25
|
+
constructor(registry: PlatformOperations, agents: AgentsService, skills: SkillsService, changes: SkillChangesService, agentSkills: AgentSkillsService, invoker: SkillInvoker, files: SandboxFileReader, db: KernelDb);
|
|
26
|
+
/**
|
|
27
|
+
* Class of registering a skill on the run's agent: `personal` on a private agent the actor owns
|
|
28
|
+
* whose `skills.writePolicy` is not `ask`; otherwise `org` (asked every time, never remembered).
|
|
29
|
+
* Also `org` when the registration may apply a version to a shared org skill at once (the actor
|
|
30
|
+
* is an org admin maintaining one; the package name is not known before the card).
|
|
31
|
+
*/
|
|
32
|
+
private registerClass;
|
|
33
|
+
/**
|
|
34
|
+
* The refusals read before anything touches the sandbox (the reader checks only the device):
|
|
35
|
+
* a sub-agent conversation, then an actor who is not the agent's current owner.
|
|
36
|
+
*/
|
|
37
|
+
private registerCheck;
|
|
38
|
+
/**
|
|
39
|
+
* The refusals of sharing read before the card: a sub-agent conversation, an actor who is not
|
|
40
|
+
* the agent's current owner, a skill that is not one of the agent's own.
|
|
41
|
+
*/
|
|
42
|
+
private shareCheck;
|
|
43
|
+
/** The file count of a registered version (null for a proposal or when the row is gone). */
|
|
44
|
+
private filesOf;
|
|
19
45
|
/** Class of deleting a skill: your own private agent is personal; a shared agent is the org's. */
|
|
20
46
|
private deleteClass;
|
|
21
47
|
/** The route's refusals, read before the card: a visible agent, owned by the actor, with the skill. */
|