@capybara-hq/kernel 0.1.0-rc.11 → 0.1.0-rc.13
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/dist/admin/admin.schemas.d.ts +13 -0
- package/dist/agents/agent-lookup.d.ts +5 -0
- package/dist/agents/agent-lookup.js +15 -0
- package/dist/agents/agents.schemas.d.ts +3 -0
- package/dist/agents/agents.service.js +11 -1
- package/dist/agents/definition.d.ts +1 -0
- package/dist/agents/definition.js +7 -3
- package/dist/agents/org-templates.js +4 -0
- package/dist/agents/pending-connectors.service.js +1 -1
- package/dist/background/background.module.js +2 -2
- package/dist/background/launch-lock.d.ts +1 -1
- package/dist/background/launch-lock.js +1 -1
- package/dist/background/poll.job.d.ts +2 -2
- package/dist/background/poll.job.js +10 -10
- package/dist/background/stop.d.ts +1 -1
- package/dist/background/stop.js +3 -3
- package/dist/background/store.d.ts +5 -5
- package/dist/background/store.js +5 -5
- package/dist/background/tasks.service.d.ts +6 -6
- package/dist/background/tasks.service.js +13 -13
- package/dist/catalog/component-scope.controller.d.ts +18 -6
- package/dist/catalog/component-scope.controller.js +36 -13
- package/dist/catalog/component-scope.service.d.ts +11 -0
- package/dist/catalog/component-scope.service.js +4 -0
- package/dist/catalog/component-state.d.ts +8 -0
- package/dist/catalog/component-state.js +19 -11
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/plugin-import.js +4 -1
- package/dist/channels/channels.module.js +2 -2
- package/dist/channels/inbound.d.ts +8 -8
- package/dist/channels/inbound.js +31 -31
- package/dist/channels/phrases.d.ts +27 -27
- package/dist/channels/phrases.js +27 -27
- package/dist/config/docs.js +1 -1
- package/dist/config/schema.d.ts +2 -2
- package/dist/config/schema.js +21 -21
- package/dist/conversations/conversations.controller.d.ts +11 -11
- package/dist/conversations/conversations.controller.js +15 -15
- package/dist/conversations/conversations.module.d.ts +1 -1
- package/dist/conversations/conversations.module.js +7 -7
- package/dist/conversations/conversations.schemas.js +1 -1
- package/dist/conversations/conversations.service.d.ts +23 -14
- package/dist/conversations/conversations.service.js +28 -21
- package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
- package/dist/conversations/{environment-status.js → device-status.js} +18 -18
- package/dist/conversations/event-log.d.ts +1 -1
- package/dist/conversations/event-log.js +1 -1
- package/dist/conversations/events.d.ts +2 -1
- package/dist/conversations/events.js +2 -2
- package/dist/ctx/create-ctx.d.ts +10 -1
- package/dist/ctx/create-ctx.js +86 -25
- package/dist/db/kernel-schema.d.ts +39 -23
- package/dist/{environments → devices}/access.d.ts +19 -19
- package/dist/{environments → devices}/access.js +28 -28
- package/dist/devices/conversation-devices.d.ts +75 -0
- package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
- package/dist/devices/devices.controller.d.ts +36 -0
- package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
- package/dist/devices/devices.module.d.ts +5 -13
- package/dist/devices/devices.module.js +16 -29
- package/dist/devices/devices.schemas.d.ts +3 -0
- package/dist/devices/devices.schemas.js +14 -0
- package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
- package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
- package/dist/devices/dto.d.ts +13 -54
- package/dist/devices/dto.js +7 -39
- package/dist/devices/label.d.ts +11 -0
- package/dist/{environments → devices}/label.js +7 -7
- package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
- package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
- package/dist/errors.d.ts +27 -25
- package/dist/errors.js +27 -25
- package/dist/evals/recorded-tools.d.ts +2 -2
- package/dist/evals/recorded-tools.js +6 -6
- package/dist/extensions/admin-ops.js +1 -0
- package/dist/extensions/admin-view.js +2 -0
- package/dist/extensions/effective-config.d.ts +6 -0
- package/dist/extensions/effective-config.js +18 -7
- package/dist/extensions/host.d.ts +13 -2
- package/dist/extensions/host.js +24 -3
- package/dist/extensions/instance.d.ts +12 -1
- package/dist/extensions/instance.js +13 -0
- package/dist/extensions/permission-view.d.ts +10 -1
- package/dist/files/previewers.d.ts +1 -1
- package/dist/files/previewers.js +1 -1
- package/dist/http/api.module.js +10 -6
- package/dist/http/gateway-routes.d.ts +1 -1
- package/dist/http/gateway-routes.js +1 -1
- package/dist/identity/settings.d.ts +32 -0
- package/dist/identity/settings.js +26 -2
- package/dist/infra/config/kernel-config.service.js +4 -4
- package/dist/kernel.module.js +3 -3
- package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
- package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
- package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
- package/dist/{devices → local-devices}/device-auth.js +51 -53
- package/dist/local-devices/device-auth.module.d.ts +7 -0
- package/dist/{devices → local-devices}/device-auth.module.js +5 -5
- package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
- package/dist/{devices → local-devices}/device-routes.js +2 -2
- package/dist/local-devices/dto.d.ts +66 -0
- package/dist/local-devices/dto.js +44 -0
- package/dist/{devices → local-devices}/keys.d.ts +11 -11
- package/dist/{devices → local-devices}/keys.js +11 -11
- package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
- package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
- package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
- package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
- package/dist/local-devices/local-devices.module.d.ts +16 -0
- package/dist/local-devices/local-devices.module.js +44 -0
- package/dist/{devices → local-devices}/machines.d.ts +26 -26
- package/dist/{devices → local-devices}/machines.js +71 -71
- package/dist/{devices → local-devices}/relay.d.ts +15 -15
- package/dist/{devices → local-devices}/relay.js +55 -55
- package/dist/{devices → local-devices}/targets.d.ts +8 -8
- package/dist/{devices → local-devices}/targets.js +14 -14
- package/dist/orgs/offboarding.d.ts +6 -6
- package/dist/orgs/offboarding.js +12 -12
- package/dist/orgs/settings.service.d.ts +28 -0
- package/dist/orgs/settings.service.js +20 -1
- package/dist/platform/exclusions.js +48 -3
- package/dist/platform/operation.js +6 -2
- package/dist/platform/ops/admin.ops.js +8 -2
- package/dist/platform/ops/agents.ops.js +19 -6
- package/dist/platform/ops/conversations.ops.d.ts +3 -3
- package/dist/platform/ops/conversations.ops.js +9 -9
- package/dist/platform/ops/devices.ops.d.ts +26 -0
- package/dist/platform/ops/devices.ops.js +311 -0
- package/dist/platform/ops/org-settings.ops.js +2 -2
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/ui.ops.d.ts +8 -0
- package/dist/platform/ops/ui.ops.js +41 -0
- package/dist/platform/platform-ops.module.d.ts +3 -2
- package/dist/platform/platform-ops.module.js +8 -5
- package/dist/platform/route-schemas.js +6 -3
- package/dist/roles/gateway-role.d.ts +3 -3
- package/dist/roles/gateway-role.js +4 -4
- package/dist/roles/gateway.module.d.ts +1 -1
- package/dist/roles/gateway.module.js +5 -5
- package/dist/roles/worker.module.js +1 -1
- package/dist/runtime/context/build.d.ts +1 -1
- package/dist/runtime/context/build.js +6 -6
- package/dist/runtime/context/compaction.js +1 -1
- package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
- package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
- package/dist/runtime/maintenance.d.ts +1 -1
- package/dist/runtime/subagents/definition.d.ts +3 -2
- package/dist/runtime/subagents/definition.js +2 -1
- package/dist/sandbox/jobs.d.ts +13 -13
- package/dist/sandbox/jobs.js +20 -20
- package/dist/sandbox/leases.d.ts +9 -9
- package/dist/sandbox/leases.js +14 -14
- package/dist/sandbox/providers.d.ts +8 -8
- package/dist/sandbox/providers.js +8 -8
- package/dist/sandbox/sandbox.module.d.ts +1 -1
- package/dist/sandbox/sandbox.module.js +1 -1
- package/dist/sandbox/sandbox.service.d.ts +76 -76
- package/dist/sandbox/sandbox.service.js +230 -230
- package/dist/tools/builtins/builtin-tools.d.ts +2 -2
- package/dist/tools/builtins/builtin-tools.js +5 -5
- package/dist/tools/builtins/platform.js +1 -1
- package/dist/tools/builtins/sandbox/bash.js +6 -6
- package/dist/tools/builtins/sandbox/common.d.ts +25 -25
- package/dist/tools/builtins/sandbox/common.js +51 -51
- package/dist/tools/builtins/sandbox/edit.js +2 -2
- package/dist/tools/builtins/sandbox/index.d.ts +1 -1
- package/dist/tools/builtins/sandbox/index.js +1 -1
- package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
- package/dist/tools/builtins/sandbox/task-output.js +10 -10
- package/dist/tools/builtins/sandbox/task-stop.js +2 -2
- package/dist/tools/builtins/sandbox/write.js +2 -2
- package/dist/tools/builtins/skill-load.d.ts +1 -1
- package/dist/tools/builtins/skill-load.js +2 -2
- package/dist/tools/connector-account-policies.d.ts +51 -0
- package/dist/tools/connector-account-policies.js +165 -0
- package/dist/tools/connectors.controller.d.ts +35 -4
- package/dist/tools/connectors.controller.js +66 -7
- package/dist/tools/connectors.schemas.d.ts +11 -0
- package/dist/tools/connectors.schemas.js +11 -0
- package/dist/tools/connectors.service.d.ts +2 -0
- package/dist/tools/connectors.service.js +1 -0
- package/dist/tools/pipeline.d.ts +1 -1
- package/dist/tools/pipeline.js +4 -3
- package/dist/tools/policy.d.ts +23 -1
- package/dist/tools/policy.js +99 -24
- package/dist/tools/registry.d.ts +2 -2
- package/dist/tools/tools.module.js +6 -3
- package/dist/ui/admin-ui.controller.d.ts +56 -0
- package/dist/ui/admin-ui.controller.js +192 -0
- package/dist/ui/arrangement.d.ts +74 -0
- package/dist/ui/arrangement.js +98 -0
- package/dist/ui/embed-token.d.ts +2 -2
- package/dist/ui/embed-token.js +30 -5
- package/dist/ui/registry.d.ts +29 -14
- package/dist/ui/registry.js +31 -10
- package/dist/ui/slots.controller.d.ts +11 -0
- package/dist/ui/slots.controller.js +25 -0
- package/dist/ui/ui-stream.d.ts +20 -3
- package/dist/ui/ui-stream.js +25 -7
- package/dist/ui/ui.schemas.d.ts +66 -0
- package/dist/ui/ui.schemas.js +65 -0
- package/dist/ui/views.controller.d.ts +35 -49
- package/dist/ui/views.controller.js +48 -70
- package/dist/ui/views.d.ts +84 -18
- package/dist/ui/views.js +201 -38
- package/package.json +3 -3
- package/src/migrations/0104_connector_tool_account_policies.sql +16 -0
- package/src/migrations/0105_devices_rename.sql +136 -0
- package/dist/devices/device-auth.module.d.ts +0 -7
- package/dist/environments/conversation-environments.d.ts +0 -75
- package/dist/environments/dto.d.ts +0 -25
- package/dist/environments/dto.js +0 -12
- package/dist/environments/environments.controller.d.ts +0 -36
- package/dist/environments/environments.module.d.ts +0 -8
- package/dist/environments/environments.module.js +0 -31
- package/dist/environments/environments.schemas.d.ts +0 -3
- package/dist/environments/environments.schemas.js +0 -14
- package/dist/environments/label.d.ts +0 -11
- package/dist/platform/ops/environments.ops.d.ts +0 -26
- package/dist/platform/ops/environments.ops.js +0 -311
|
@@ -13,7 +13,7 @@ import { sql } from "kysely";
|
|
|
13
13
|
import { AgentLookup, visibleTo } from "../agents/agent-lookup.js";
|
|
14
14
|
import { BackgroundTaskStopper } from "../background/stop.js";
|
|
15
15
|
import { unbindConversation } from "../channels/threads.js";
|
|
16
|
-
import {
|
|
16
|
+
import { ConversationDevices, } from "../devices/conversation-devices.js";
|
|
17
17
|
import { ApiError } from "../errors.js";
|
|
18
18
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
19
19
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
@@ -69,7 +69,7 @@ const COLUMNS = [
|
|
|
69
69
|
"parent_conversation_id",
|
|
70
70
|
"subagent_type",
|
|
71
71
|
"archived_at",
|
|
72
|
-
"
|
|
72
|
+
"device_id",
|
|
73
73
|
];
|
|
74
74
|
function toView(r) {
|
|
75
75
|
return {
|
|
@@ -86,7 +86,7 @@ function toView(r) {
|
|
|
86
86
|
parentConversationId: r.parent_conversation_id,
|
|
87
87
|
subagentType: r.subagent_type,
|
|
88
88
|
archivedAt: r.archived_at,
|
|
89
|
-
|
|
89
|
+
deviceId: r.device_id,
|
|
90
90
|
activity: r.activity ?? null,
|
|
91
91
|
};
|
|
92
92
|
}
|
|
@@ -129,7 +129,7 @@ let ConversationsService = class ConversationsService {
|
|
|
129
129
|
attachments;
|
|
130
130
|
invoker;
|
|
131
131
|
background;
|
|
132
|
-
|
|
132
|
+
devices;
|
|
133
133
|
txHost;
|
|
134
134
|
constructor(db, eventLog, agents, access,
|
|
135
135
|
/** Starts and stops the conversation's runs. */
|
|
@@ -144,8 +144,8 @@ let ConversationsService = class ConversationsService {
|
|
|
144
144
|
invoker,
|
|
145
145
|
/** Stops a deleted conversation's background tasks. */
|
|
146
146
|
background,
|
|
147
|
-
/** The
|
|
148
|
-
|
|
147
|
+
/** The device a new conversation is bound to (environments spec §4.1 item 1). */
|
|
148
|
+
devices, txHost) {
|
|
149
149
|
this.db = db;
|
|
150
150
|
this.eventLog = eventLog;
|
|
151
151
|
this.agents = agents;
|
|
@@ -157,7 +157,7 @@ let ConversationsService = class ConversationsService {
|
|
|
157
157
|
this.attachments = attachments;
|
|
158
158
|
this.invoker = invoker;
|
|
159
159
|
this.background = background;
|
|
160
|
-
this.
|
|
160
|
+
this.devices = devices;
|
|
161
161
|
this.txHost = txHost;
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
@@ -170,14 +170,14 @@ let ConversationsService = class ConversationsService {
|
|
|
170
170
|
.catch((err) => this.log.warn("ensureRun on conversation open failed", { conversationId, err }));
|
|
171
171
|
}
|
|
172
172
|
/**
|
|
173
|
-
* Creates an empty conversation bound to its
|
|
174
|
-
* {@link
|
|
175
|
-
* `agent.not_found`,
|
|
176
|
-
* `
|
|
173
|
+
* Creates an empty conversation bound to its device (`pick`, see
|
|
174
|
+
* {@link ConversationDevices.forNew}); an agent the account cannot use →
|
|
175
|
+
* `agent.not_found`, a device it may not use → `device.not_found` /
|
|
176
|
+
* `device.unavailable`, and nothing is created.
|
|
177
177
|
*/
|
|
178
178
|
async create(orgId, accountId, agentId, pick = {}) {
|
|
179
179
|
await this.agents.get(orgId, agentId, accountId);
|
|
180
|
-
const
|
|
180
|
+
const deviceId = await this.devices.forNew(orgId, accountId, pick);
|
|
181
181
|
const row = await this.db
|
|
182
182
|
.insertInto("conversations")
|
|
183
183
|
.values({
|
|
@@ -185,7 +185,7 @@ let ConversationsService = class ConversationsService {
|
|
|
185
185
|
org_id: orgId,
|
|
186
186
|
agent_id: agentId,
|
|
187
187
|
account_id: accountId,
|
|
188
|
-
|
|
188
|
+
device_id: deviceId,
|
|
189
189
|
})
|
|
190
190
|
.returning(COLUMNS)
|
|
191
191
|
.executeTakeFirstOrThrow();
|
|
@@ -205,12 +205,12 @@ let ConversationsService = class ConversationsService {
|
|
|
205
205
|
const prior = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
|
|
206
206
|
if (prior)
|
|
207
207
|
return prior;
|
|
208
|
-
const
|
|
208
|
+
const deviceId = await this.devices.forNew(orgId, accountId, pick);
|
|
209
209
|
const next = await this.planSwitch(orgId, agent.definition, NO_SELECTION, input);
|
|
210
210
|
const extra = await this.resolveExtra(orgId, accountId, input);
|
|
211
211
|
const invoked = await this.prepareSkill(orgId, agentId, undefined, agent.definition, input);
|
|
212
212
|
const id = randomUUID();
|
|
213
|
-
const all = await this.createIn({ id, orgId, accountId, agentId,
|
|
213
|
+
const all = await this.createIn({ id, orgId, accountId, agentId, deviceId }, input, next, extra, invoked);
|
|
214
214
|
const written = all?.at(-1);
|
|
215
215
|
if (!all || !written) {
|
|
216
216
|
const raced = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
|
|
@@ -233,7 +233,7 @@ let ConversationsService = class ConversationsService {
|
|
|
233
233
|
org_id: orgId,
|
|
234
234
|
agent_id: conv.agentId,
|
|
235
235
|
account_id: accountId,
|
|
236
|
-
|
|
236
|
+
device_id: conv.deviceId,
|
|
237
237
|
create_request_id: input.clientMessageId,
|
|
238
238
|
})
|
|
239
239
|
.onConflict((oc) => oc
|
|
@@ -482,16 +482,16 @@ let ConversationsService = class ConversationsService {
|
|
|
482
482
|
LIMIT ${opts.limit + 1}
|
|
483
483
|
)
|
|
484
484
|
SELECT page.agent_id, page.latest_at, v.definition ->> 'name' AS agent_name,
|
|
485
|
-
cs.id, cs.org_id, cs.account_id, cs.title, cs.title_source, cs.selection, cs.last_seq,
|
|
485
|
+
a.profile_avatar, a.profile_updated_at, cs.id, cs.org_id, cs.account_id, cs.title, cs.title_source, cs.selection, cs.last_seq,
|
|
486
486
|
cs.created_at, cs.updated_at, cs.parent_conversation_id, cs.subagent_type,
|
|
487
|
-
cs.archived_at, cs.
|
|
487
|
+
cs.archived_at, cs.device_id, cs.activity
|
|
488
488
|
FROM page
|
|
489
489
|
JOIN capybara.agents a ON a.id = page.agent_id
|
|
490
490
|
JOIN capybara.agent_versions v ON v.agent_id = a.id AND v.version = a.current_version
|
|
491
491
|
CROSS JOIN LATERAL (
|
|
492
492
|
SELECT c.id, c.org_id, c.account_id, c.title, c.title_source, c.selection, c.last_seq,
|
|
493
493
|
c.created_at, c.updated_at, c.parent_conversation_id, c.subagent_type,
|
|
494
|
-
c.archived_at, c.
|
|
494
|
+
c.archived_at, c.device_id, ${activitySql("c")} AS activity
|
|
495
495
|
FROM capybara.conversations c
|
|
496
496
|
WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
|
|
497
497
|
AND c.deleted_at IS NULL AND ${archivedFilter} ${searchFilter} ${stateFilter}
|
|
@@ -506,7 +506,14 @@ let ConversationsService = class ConversationsService {
|
|
|
506
506
|
for (const r of rows) {
|
|
507
507
|
if (current?.agent.id !== r.agent_id) {
|
|
508
508
|
current = {
|
|
509
|
-
agent: {
|
|
509
|
+
agent: {
|
|
510
|
+
id: r.agent_id,
|
|
511
|
+
name: r.agent_name ?? "",
|
|
512
|
+
profile: {
|
|
513
|
+
avatar: r.profile_avatar,
|
|
514
|
+
updatedAt: r.profile_updated_at.toISOString(),
|
|
515
|
+
},
|
|
516
|
+
},
|
|
510
517
|
latestAt: r.latest_at,
|
|
511
518
|
conversations: [],
|
|
512
519
|
more: false,
|
|
@@ -952,7 +959,7 @@ ConversationsService = __decorate([
|
|
|
952
959
|
__param(8, Inject(AttachmentsService)),
|
|
953
960
|
__param(9, Inject(SkillInvoker)),
|
|
954
961
|
__param(10, Inject(BackgroundTaskStopper)),
|
|
955
|
-
__param(11, Inject(
|
|
962
|
+
__param(11, Inject(ConversationDevices)),
|
|
956
963
|
__param(12, Inject(TransactionHost))
|
|
957
964
|
], ConversationsService);
|
|
958
965
|
export { ConversationsService };
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DevicesService, type DeviceView } from "../devices/devices.service.js";
|
|
2
2
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
3
|
import { ConversationAccess } from "./access.js";
|
|
4
4
|
import { ConversationsService } from "./conversations.service.js";
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
6
|
+
* The device a conversation works in, for its page (environments spec §9: shown read only):
|
|
7
7
|
* a conversation the viewer may not see → `conversation.not_found`. A sub-agent shows its root
|
|
8
|
-
* conversation's
|
|
8
|
+
* conversation's device. Null while the conversation is unbound, or when its device is
|
|
9
9
|
* deleted or no longer its owner's (its sandbox tools then fail).
|
|
10
10
|
*/
|
|
11
|
-
export declare class
|
|
11
|
+
export declare class ConversationDeviceStatus {
|
|
12
12
|
/** Who may see the conversation (the rule of its event stream). */
|
|
13
13
|
private readonly access;
|
|
14
|
-
/** The conversation whose
|
|
14
|
+
/** The conversation whose device it works in: a sub-agent's root, else itself. */
|
|
15
15
|
private readonly conversations;
|
|
16
|
-
private readonly
|
|
16
|
+
private readonly devices;
|
|
17
17
|
private readonly db;
|
|
18
18
|
constructor(
|
|
19
19
|
/** Who may see the conversation (the rule of its event stream). */
|
|
20
20
|
access: Pick<ConversationAccess, "isAccessible">,
|
|
21
|
-
/** The conversation whose
|
|
22
|
-
conversations: Pick<ConversationsService, "sandboxConversationOf">,
|
|
23
|
-
get(orgId: string, conversationId: string, viewerAccountId: string): Promise<
|
|
21
|
+
/** The conversation whose device it works in: a sub-agent's root, else itself. */
|
|
22
|
+
conversations: Pick<ConversationsService, "sandboxConversationOf">, devices: Pick<DevicesService, "get">, db: KernelDb);
|
|
23
|
+
get(orgId: string, conversationId: string, viewerAccountId: string): Promise<DeviceView | null>;
|
|
24
24
|
}
|
|
@@ -8,31 +8,31 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
|
-
import {
|
|
11
|
+
import { DevicesService } from "../devices/devices.service.js";
|
|
12
12
|
import { ApiError } from "../errors.js";
|
|
13
13
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
14
14
|
import { UUID_RE } from "../ui/registry.js";
|
|
15
15
|
import { ConversationAccess } from "./access.js";
|
|
16
16
|
import { ConversationsService } from "./conversations.service.js";
|
|
17
17
|
/**
|
|
18
|
-
* The
|
|
18
|
+
* The device a conversation works in, for its page (environments spec §9: shown read only):
|
|
19
19
|
* a conversation the viewer may not see → `conversation.not_found`. A sub-agent shows its root
|
|
20
|
-
* conversation's
|
|
20
|
+
* conversation's device. Null while the conversation is unbound, or when its device is
|
|
21
21
|
* deleted or no longer its owner's (its sandbox tools then fail).
|
|
22
22
|
*/
|
|
23
|
-
let
|
|
23
|
+
let ConversationDeviceStatus = class ConversationDeviceStatus {
|
|
24
24
|
access;
|
|
25
25
|
conversations;
|
|
26
|
-
|
|
26
|
+
devices;
|
|
27
27
|
db;
|
|
28
28
|
constructor(
|
|
29
29
|
/** Who may see the conversation (the rule of its event stream). */
|
|
30
30
|
access,
|
|
31
|
-
/** The conversation whose
|
|
32
|
-
conversations,
|
|
31
|
+
/** The conversation whose device it works in: a sub-agent's root, else itself. */
|
|
32
|
+
conversations, devices, db) {
|
|
33
33
|
this.access = access;
|
|
34
34
|
this.conversations = conversations;
|
|
35
|
-
this.
|
|
35
|
+
this.devices = devices;
|
|
36
36
|
this.db = db;
|
|
37
37
|
}
|
|
38
38
|
async get(orgId, conversationId, viewerAccountId) {
|
|
@@ -40,32 +40,32 @@ let ConversationEnvironmentStatus = class ConversationEnvironmentStatus {
|
|
|
40
40
|
!(await this.access.isAccessible(orgId, conversationId, viewerAccountId))) {
|
|
41
41
|
throw new ApiError("conversation.not_found", "Conversation not found");
|
|
42
42
|
}
|
|
43
|
-
// a sub-agent works in its root conversation's
|
|
43
|
+
// a sub-agent works in its root conversation's device (at every depth)
|
|
44
44
|
const root = await this.conversations.sandboxConversationOf(orgId, conversationId);
|
|
45
45
|
const conv = await this.db
|
|
46
46
|
.selectFrom("conversations")
|
|
47
|
-
.select(["account_id", "
|
|
47
|
+
.select(["account_id", "device_id"])
|
|
48
48
|
.where("id", "=", root)
|
|
49
49
|
.where("org_id", "=", orgId)
|
|
50
50
|
.executeTakeFirst();
|
|
51
|
-
if (!conv?.
|
|
51
|
+
if (!conv?.device_id)
|
|
52
52
|
return null;
|
|
53
53
|
try {
|
|
54
|
-
// the
|
|
55
|
-
return await this.
|
|
54
|
+
// the device as its owner sees it (only the owner may use a personal device)
|
|
55
|
+
return await this.devices.get(orgId, conv.account_id, conv.device_id);
|
|
56
56
|
}
|
|
57
57
|
catch (e) {
|
|
58
|
-
if (e instanceof ApiError && e.code === "
|
|
58
|
+
if (e instanceof ApiError && e.code === "device.not_found")
|
|
59
59
|
return null;
|
|
60
60
|
throw e;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
|
-
|
|
64
|
+
ConversationDeviceStatus = __decorate([
|
|
65
65
|
Injectable(),
|
|
66
66
|
__param(0, Inject(ConversationAccess)),
|
|
67
67
|
__param(1, Inject(ConversationsService)),
|
|
68
|
-
__param(2, Inject(
|
|
68
|
+
__param(2, Inject(DevicesService)),
|
|
69
69
|
__param(3, Inject(KernelDb))
|
|
70
|
-
],
|
|
71
|
-
export {
|
|
70
|
+
], ConversationDeviceStatus);
|
|
71
|
+
export { ConversationDeviceStatus };
|
|
@@ -132,7 +132,7 @@ export declare class EventLog {
|
|
|
132
132
|
findByIdem(conversationId: string, idemKey: string): Promise<ConversationEvent | undefined>;
|
|
133
133
|
/**
|
|
134
134
|
* The conversation's latest event of `type` (by seq); undefined without one. `skills_catalog`
|
|
135
|
-
* (0025) and `
|
|
135
|
+
* (0025) and `device_paths` (0094) have partial indexes that answer it directly; other
|
|
136
136
|
* types scan the conversation's events backwards on the (conversation_id, seq) key, the whole
|
|
137
137
|
* conversation when it has none.
|
|
138
138
|
*/
|
|
@@ -246,7 +246,7 @@ let EventLog = class EventLog {
|
|
|
246
246
|
}
|
|
247
247
|
/**
|
|
248
248
|
* The conversation's latest event of `type` (by seq); undefined without one. `skills_catalog`
|
|
249
|
-
* (0025) and `
|
|
249
|
+
* (0025) and `device_paths` (0094) have partial indexes that answer it directly; other
|
|
250
250
|
* types scan the conversation's events backwards on the (conversation_id, seq) key, the whole
|
|
251
251
|
* conversation when it has none.
|
|
252
252
|
*/
|
|
@@ -516,7 +516,7 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
516
516
|
removed: z.ZodArray<z.ZodString>;
|
|
517
517
|
}, z.core.$strip>;
|
|
518
518
|
}, z.core.$strip>, z.ZodObject<{
|
|
519
|
-
type: z.ZodLiteral<"
|
|
519
|
+
type: z.ZodLiteral<"device_paths">;
|
|
520
520
|
payload: z.ZodObject<{
|
|
521
521
|
roots: z.ZodObject<{
|
|
522
522
|
workspace: z.ZodString;
|
|
@@ -809,6 +809,7 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
809
809
|
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
810
810
|
}, z.core.$strict>>;
|
|
811
811
|
toolPolicies: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
812
|
+
allow: "allow";
|
|
812
813
|
ask: "ask";
|
|
813
814
|
deny: "deny";
|
|
814
815
|
}>>>;
|
|
@@ -447,10 +447,10 @@ export const ConversationEventSchema = z.discriminatedUnion("type", [
|
|
|
447
447
|
* The directories of the instance a sandbox tool call entered, when they differ from the
|
|
448
448
|
* defaults (environments spec §13.1: a connected machine's real paths). Written once per
|
|
449
449
|
* conversation and roots, before that call's result. **Model-visible**: rendered at its position
|
|
450
|
-
* as a system message (`
|
|
450
|
+
* as a system message (`devicePathsText`); a summary covering it is followed by the latest
|
|
451
451
|
* one again.
|
|
452
452
|
*/
|
|
453
|
-
ev("
|
|
453
|
+
ev("device_paths", z.object({
|
|
454
454
|
roots: z.object({
|
|
455
455
|
workspace: z.string().min(1).max(4096),
|
|
456
456
|
skills: z.string().min(1).max(4096),
|
package/dist/ctx/create-ctx.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ExtensionContext, type ExtensionManifest, type ExtensionPointRef, type Logger, type ToolHandler, type ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type ExtensionContext, type ExtensionManifest, type ExtensionPointRef, type Logger, resolveUiSlots, type ToolHandler, type ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { Redis } from "ioredis";
|
|
3
3
|
import { Kysely } from "kysely";
|
|
4
4
|
import { type RegisteredHook } from "../extensions/hooks.js";
|
|
@@ -72,8 +72,15 @@ export interface ExtensionPointAccess {
|
|
|
72
72
|
* required bootstrap plugins, see `extensions/enabled.ts`), not cached.
|
|
73
73
|
*/
|
|
74
74
|
export declare function queryEnabledPlugins(db: Kysely<any>, orgId: string): Promise<Set<string>>;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the account is, right now, an active super admin (one query): what a platform embed
|
|
77
|
+
* token and the platform refresh stream re-check.
|
|
78
|
+
*/
|
|
79
|
+
export declare function currentPlatformAccount(db: KernelDeps["db"], accountId: string): Promise<boolean>;
|
|
75
80
|
/** 插件的 PG schema 名:`plugin_<name>`,name 中的 `-` 换成 `_`。 */
|
|
76
81
|
export declare function pluginSchemaName(name: string): string;
|
|
82
|
+
/** What {@link resolveUiSlots} returns for a manifest. */
|
|
83
|
+
export type UiResolution = ReturnType<typeof resolveUiSlots>;
|
|
77
84
|
/** 内核视角的插件 ctx:插件契约之外还带宿主要用的登记信息与关闭方法。 */
|
|
78
85
|
export type KernelExtensionContext = ExtensionContext & {
|
|
79
86
|
/** 本实例的登记账(C17):每次登记记一笔,撤销时逐项撤销。 */
|
|
@@ -92,6 +99,8 @@ export type KernelExtensionContext = ExtensionContext & {
|
|
|
92
99
|
registeredTools: RegisteredTool[];
|
|
93
100
|
/** Hooks registered through `ctx.hooks.on` (registration order). */
|
|
94
101
|
registeredHooks: RegisteredHook[];
|
|
102
|
+
/** The manifest's `ui` resolved against the slot catalog once per load (UI slots spec §3.2). */
|
|
103
|
+
uiResolution: UiResolution;
|
|
95
104
|
/** 经 `ctx.ui.view` 注册的视图(按贡献 id)。 */
|
|
96
105
|
uiViews: ReadonlyMap<string, UiRender>;
|
|
97
106
|
/** 经 `ctx.ui.action` 注册的动作处理函数(按贡献 id)。 */
|
package/dist/ctx/create-ctx.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EXTENSION_POINTS, ExtensionPermissionError, ExtensionPointUnavailableError, FILE_PREVIEW_EXTENSION_POINT, OBJECT_STORE_EXTENSION_POINT, parseExtensionPoint, validateFilePreviewProvider, validateToolSpec, } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { EXTENSION_POINTS, ExtensionPermissionError, ExtensionPointUnavailableError, FILE_PREVIEW_EXTENSION_POINT, OBJECT_STORE_EXTENSION_POINT, parseExtensionPoint, resolveUiSlots, validateFilePreviewProvider, validateToolSpec, } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { Command, ConnectionBuilder, ControlledTransactionBuilder, Kysely, TransactionBuilder, } from "kysely";
|
|
3
3
|
import { conversationTopic } from "../conversations/event-log.js";
|
|
4
4
|
import { kernelDb } from "../db/kernel-schema.js";
|
|
@@ -7,7 +7,7 @@ import { checkHookRegistration } from "../extensions/hooks.js";
|
|
|
7
7
|
import { RegistrationLedger } from "../extensions/ledger.js";
|
|
8
8
|
import { audit } from "../identity/audit.js";
|
|
9
9
|
import { PrefixedStorage } from "../storage/storage.service.js";
|
|
10
|
-
import {
|
|
10
|
+
import { canUseSlot, orgUiTopic, PLATFORM_UI_TOPIC, PluginUiRegistration, } from "../ui/registry.js";
|
|
11
11
|
import { createBus } from "./bus.js";
|
|
12
12
|
import { createKv } from "./kv.js";
|
|
13
13
|
import { createLock } from "./lock.js";
|
|
@@ -39,6 +39,20 @@ async function currentEmbedRole(db, plugin, orgId, accountId) {
|
|
|
39
39
|
.executeTakeFirst();
|
|
40
40
|
return row?.role;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether the account is, right now, an active super admin (one query): what a platform embed
|
|
44
|
+
* token and the platform refresh stream re-check.
|
|
45
|
+
*/
|
|
46
|
+
export async function currentPlatformAccount(db, accountId) {
|
|
47
|
+
const row = await kernelDb(db)
|
|
48
|
+
.selectFrom("accounts")
|
|
49
|
+
.select("accounts.id")
|
|
50
|
+
.where("accounts.id", "=", accountId)
|
|
51
|
+
.where("accounts.status", "=", "active")
|
|
52
|
+
.where("accounts.is_super_admin", "=", true)
|
|
53
|
+
.executeTakeFirst();
|
|
54
|
+
return row !== undefined;
|
|
55
|
+
}
|
|
42
56
|
/** `ui.invalidate` 目标的一条查询:插件在该组织启用,且(带对话时)对话属于该组织。 */
|
|
43
57
|
async function invalidateTargetOk(db, plugin, orgId, conversationId) {
|
|
44
58
|
const k = kernelDb(db);
|
|
@@ -248,7 +262,10 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
248
262
|
]);
|
|
249
263
|
const uiReg = new PluginUiRegistration(manifest, deny);
|
|
250
264
|
const embedTokens = deps.embedTokens;
|
|
251
|
-
const
|
|
265
|
+
const uiResolution = resolveUiSlots(manifest.ui);
|
|
266
|
+
const uiSlots = new Map(uiResolution.resolved.map((r) => [r.contribution.id, r.slot]));
|
|
267
|
+
/** Ids of the extension's `platform.page` contributions (their invalidate takes no orgId). */
|
|
268
|
+
const platformUi = new Set([...uiSlots].filter(([, s]) => s.id === "platform.page").map(([id]) => id));
|
|
252
269
|
/** 校验能力可取用(非 brokered、已在清单声明),返回其登记信息。 */
|
|
253
270
|
const checkExtensionPoint = (ref) => {
|
|
254
271
|
const capName = parseExtensionPoint(ref).name;
|
|
@@ -482,9 +499,15 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
482
499
|
});
|
|
483
500
|
},
|
|
484
501
|
// 目标先核对(一条查询):插件在 orgId 启用;带对话时对话属于 orgId。不满足就只记 debug、不发
|
|
485
|
-
// (插件拿着过期或别的组织的 id
|
|
502
|
+
// (插件拿着过期或别的组织的 id 调用不是错误,但不能借它往别的组织的流里发刷新)。A `platform.page`
|
|
503
|
+
// contribution takes no orgId and refreshes the platform stream (super admins only)
|
|
486
504
|
async invalidate(target) {
|
|
487
|
-
const t = uiReg.target(target);
|
|
505
|
+
const t = uiReg.target(target, platformUi);
|
|
506
|
+
const msg = { kind: "ui", plugin: name, contribution: t.contribution };
|
|
507
|
+
if (t.scope === "platform") {
|
|
508
|
+
await kernelBus.publish(PLATFORM_UI_TOPIC, msg);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
488
511
|
if (!(await invalidateTargetOk(deps.db, name, t.orgId, t.conversationId))) {
|
|
489
512
|
deps.log.debug?.("plugin ui invalidate ignored: plugin not enabled or conversation not in org", {
|
|
490
513
|
plugin: name,
|
|
@@ -494,22 +517,33 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
494
517
|
});
|
|
495
518
|
return;
|
|
496
519
|
}
|
|
497
|
-
const msg = { kind: "ui", plugin: name, contribution: t.contribution };
|
|
498
520
|
await kernelBus.publish(t.conversationId !== undefined
|
|
499
521
|
? conversationTopic(t.conversationId)
|
|
500
522
|
: orgUiTopic(t.orgId), msg);
|
|
501
523
|
},
|
|
502
524
|
// 只认签给本插件、未过期、未篡改的令牌(见 ui/embed-token.ts);再用一条查询核对此刻:本插件仍在该组织
|
|
503
525
|
// 启用、组织未删除、账号 active 且仍是成员(每次查 PG、不缓存:停用插件、离职、停用账号后已签出的令牌立即失效)。
|
|
504
|
-
// 返回的 role
|
|
526
|
+
// 返回的 role 是此刻的角色;令牌在此刻的角色低于贡献所在位置的 minRole 时失效(A38, UI slots spec §7)。
|
|
527
|
+
// A platform token: the contribution is a `platform.page` and the account is still an active
|
|
528
|
+
// super admin. A token of one scope never verifies for the other's slots. 其余一律 null
|
|
505
529
|
async verifyEmbedToken(token) {
|
|
506
530
|
const claims = embedTokens.verify(token, name);
|
|
507
531
|
if (claims === null)
|
|
508
532
|
return null;
|
|
509
|
-
const
|
|
510
|
-
if (
|
|
533
|
+
const slot = uiSlots.get(claims.contribution);
|
|
534
|
+
if (claims.scope === "platform") {
|
|
535
|
+
if (slot?.id !== "platform.page")
|
|
536
|
+
return null;
|
|
537
|
+
return (await currentPlatformAccount(deps.db, claims.accountId)) ? claims : null;
|
|
538
|
+
}
|
|
539
|
+
if (claims.scope !== "org" || claims.orgId === undefined)
|
|
511
540
|
return null;
|
|
512
|
-
|
|
541
|
+
// the contribution's slot as loaded now: never `platform.page` through an org token, and the
|
|
542
|
+
// current role still meets the slot's minRole (a demoted caller's token stops working)
|
|
543
|
+
if (slot === undefined || slot.id === "platform.page")
|
|
544
|
+
return null;
|
|
545
|
+
const role = await currentEmbedRole(deps.db, name, claims.orgId, claims.accountId);
|
|
546
|
+
if (role === undefined || !canUseSlot(slot, role))
|
|
513
547
|
return null;
|
|
514
548
|
return { ...claims, role };
|
|
515
549
|
},
|
|
@@ -521,14 +555,15 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
521
555
|
if (provided.has(extensionPoint)) {
|
|
522
556
|
throw new Error(`plugin ${name}: extension point ${extensionPoint} provided more than once`);
|
|
523
557
|
}
|
|
524
|
-
|
|
525
|
-
checkFilePreviewProvider(manifest, impl)
|
|
526
|
-
|
|
558
|
+
const served = extensionPoint === FILE_PREVIEW_EXTENSION_POINT
|
|
559
|
+
? checkFilePreviewProvider(manifest, impl, uiResolution, deps.log)
|
|
560
|
+
: impl;
|
|
561
|
+
provided.set(extensionPoint, served);
|
|
527
562
|
ledger.add({
|
|
528
563
|
kind: "provide",
|
|
529
564
|
key: extensionPoint,
|
|
530
565
|
dispose: async () => {
|
|
531
|
-
if (provided.get(extensionPoint) ===
|
|
566
|
+
if (provided.get(extensionPoint) === served)
|
|
532
567
|
provided.delete(extensionPoint);
|
|
533
568
|
},
|
|
534
569
|
});
|
|
@@ -547,6 +582,7 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
547
582
|
provided,
|
|
548
583
|
registeredTools,
|
|
549
584
|
registeredHooks,
|
|
585
|
+
uiResolution,
|
|
550
586
|
uiViews: uiReg.views,
|
|
551
587
|
uiActions: uiReg.actions,
|
|
552
588
|
ledger,
|
|
@@ -601,21 +637,46 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
601
637
|
/**
|
|
602
638
|
* A `file-preview@1` provider must be valid when it is provided (setup fails otherwise): the shape
|
|
603
639
|
* ({@link validateFilePreviewProvider}), and every `embed` names a ui contribution of this extension
|
|
604
|
-
* with `kind: "embed"`
|
|
640
|
+
* with `kind: "embed"` that resolved to the slot `file.preview` (from `slot` or the deprecated
|
|
641
|
+
* `place`). A previewer whose embed names an embed contribution dropped at load (unknown slot or
|
|
642
|
+
* slot version, UI slots spec §3.2) is left out instead, with one warn per load: the returned
|
|
643
|
+
* provider is what the kernel serves.
|
|
605
644
|
*/
|
|
606
|
-
function checkFilePreviewProvider(manifest, impl) {
|
|
645
|
+
function checkFilePreviewProvider(manifest, impl, ui, log) {
|
|
607
646
|
const problems = validateFilePreviewProvider(impl);
|
|
608
|
-
if (problems.length
|
|
609
|
-
|
|
610
|
-
.filter((c) => c.kind === "embed" && c.place === "file-preview")
|
|
611
|
-
.map((c) => c.id));
|
|
612
|
-
impl.previewers.forEach((p, i) => {
|
|
613
|
-
if (p.embed !== undefined && !embeds.has(p.embed)) {
|
|
614
|
-
problems.push(`previewers[${i}].embed "${p.embed}" is not a ui contribution { kind: "embed", place: "file-preview" }`);
|
|
615
|
-
}
|
|
616
|
-
});
|
|
647
|
+
if (problems.length > 0) {
|
|
648
|
+
throw new Error(`plugin ${manifest.name}: invalid ${FILE_PREVIEW_EXTENSION_POINT} provider: ${problems.join("; ")}`);
|
|
617
649
|
}
|
|
650
|
+
const embeds = new Set(ui.resolved
|
|
651
|
+
.filter((r) => r.contribution.kind === "embed" && r.slot.id === "file.preview")
|
|
652
|
+
.map((r) => r.contribution.id));
|
|
653
|
+
const droppedEmbeds = new Map(ui.dropped
|
|
654
|
+
.filter((d) => manifest.ui?.some((c) => c.id === d.id && c.kind === "embed"))
|
|
655
|
+
.map((d) => [d.id, d]));
|
|
656
|
+
const provider = impl;
|
|
657
|
+
const kept = [];
|
|
658
|
+
const dropped = [];
|
|
659
|
+
provider.previewers.forEach((p, i) => {
|
|
660
|
+
if (p.embed === undefined || embeds.has(p.embed)) {
|
|
661
|
+
kept.push(p);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
const d = droppedEmbeds.get(p.embed);
|
|
665
|
+
if (d !== undefined) {
|
|
666
|
+
dropped.push({ previewer: p.id, embed: p.embed, ref: d.ref, reason: d.reason });
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
problems.push(`previewers[${i}].embed "${p.embed}" is not a ui contribution { kind: "embed", slot: "file.preview" }`);
|
|
670
|
+
});
|
|
618
671
|
if (problems.length > 0) {
|
|
619
672
|
throw new Error(`plugin ${manifest.name}: invalid ${FILE_PREVIEW_EXTENSION_POINT} provider: ${problems.join("; ")}`);
|
|
620
673
|
}
|
|
674
|
+
if (dropped.length === 0)
|
|
675
|
+
return impl;
|
|
676
|
+
log.warn("plugin file previewers dropped", {
|
|
677
|
+
plugin: manifest.name,
|
|
678
|
+
version: manifest.version,
|
|
679
|
+
previewers: dropped,
|
|
680
|
+
});
|
|
681
|
+
return { ...provider, previewers: kept };
|
|
621
682
|
}
|