@capybara-hq/kernel 0.1.0-rc.7 → 0.1.0-rc.9
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/background/finish.js +2 -1
- package/dist/background/launch.d.ts +13 -7
- package/dist/background/launch.js +15 -8
- package/dist/background/poll.job.d.ts +1 -1
- package/dist/background/poll.job.js +8 -3
- package/dist/background/session.d.ts +2 -2
- package/dist/background/session.js +3 -2
- package/dist/background/status.d.ts +1 -1
- package/dist/background/status.js +1 -1
- package/dist/background/stop.js +4 -3
- package/dist/background/store.d.ts +6 -1
- package/dist/background/store.js +5 -1
- package/dist/background/tasks.service.d.ts +1 -1
- package/dist/background/tasks.service.js +4 -4
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/config/docs.js +2 -0
- package/dist/config/schema.d.ts +7 -0
- package/dist/config/schema.js +45 -0
- package/dist/conversations/event-log.d.ts +4 -3
- package/dist/conversations/event-log.js +4 -3
- package/dist/conversations/events.d.ts +9 -0
- package/dist/conversations/events.js +14 -0
- package/dist/db/kernel-schema.d.ts +26 -1
- package/dist/devices/device-auth.controller.d.ts +42 -0
- package/dist/devices/device-auth.controller.js +116 -0
- package/dist/devices/device-auth.d.ts +119 -0
- package/dist/devices/device-auth.js +403 -0
- package/dist/devices/device-auth.module.d.ts +7 -0
- package/dist/devices/device-auth.module.js +25 -0
- package/dist/devices/device-gateway.d.ts +128 -0
- package/dist/devices/device-gateway.js +530 -0
- package/dist/devices/device-provider.d.ts +89 -0
- package/dist/devices/device-provider.js +151 -0
- package/dist/devices/device-routes.d.ts +36 -0
- package/dist/devices/device-routes.js +90 -0
- package/dist/devices/devices.module.d.ts +16 -0
- package/dist/devices/devices.module.js +44 -0
- package/dist/devices/dto.d.ts +66 -0
- package/dist/devices/dto.js +44 -0
- package/dist/devices/keys.d.ts +25 -0
- package/dist/devices/keys.js +25 -0
- package/dist/devices/machines.d.ts +106 -0
- package/dist/devices/machines.js +301 -0
- package/dist/devices/relay.d.ts +100 -0
- package/dist/devices/relay.js +373 -0
- package/dist/devices/targets.d.ts +28 -0
- package/dist/devices/targets.js +73 -0
- package/dist/environments/access.d.ts +11 -6
- package/dist/environments/access.js +23 -8
- package/dist/environments/conversation-environments.d.ts +20 -6
- package/dist/environments/conversation-environments.js +70 -9
- package/dist/environments/environments.controller.d.ts +4 -1
- package/dist/environments/environments.controller.js +15 -1
- package/dist/environments/environments.module.js +3 -1
- package/dist/environments/environments.service.d.ts +54 -7
- package/dist/environments/environments.service.js +104 -20
- package/dist/environments/legacy-cleanup.job.d.ts +1 -1
- package/dist/environments/legacy-cleanup.job.js +4 -4
- package/dist/errors.d.ts +24 -0
- package/dist/errors.js +24 -0
- package/dist/evals/recorded-tools.d.ts +9 -0
- package/dist/evals/recorded-tools.js +27 -0
- package/dist/http/api.module.js +4 -0
- package/dist/http/gateway-routes.d.ts +7 -3
- package/dist/http/gateway-routes.js +6 -3
- package/dist/identity/me.controller.d.ts +1 -3
- package/dist/identity/me.controller.js +1 -9
- package/dist/identity/me.schemas.d.ts +14 -0
- package/dist/identity/me.schemas.js +17 -0
- package/dist/infra/config/kernel-config.service.js +2 -0
- package/dist/infra/throttler/named-throttlers.d.ts +5 -0
- package/dist/infra/throttler/named-throttlers.js +38 -0
- package/dist/kernel.module.js +3 -0
- package/dist/lifecycle/kernel-lifecycle.js +5 -0
- package/dist/platform/exclusions.js +33 -10
- package/dist/platform/operation.js +1 -1
- package/dist/platform/ops/environments.ops.js +21 -3
- package/dist/platform/ops/platform.ops.d.ts +7 -2
- package/dist/platform/ops/platform.ops.js +50 -3
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/gateway-role.d.ts +14 -3
- package/dist/roles/gateway-role.js +25 -5
- package/dist/roles/gateway.module.d.ts +2 -1
- package/dist/roles/gateway.module.js +6 -3
- package/dist/runtime/context/build.d.ts +2 -0
- package/dist/runtime/context/build.js +14 -0
- package/dist/runtime/context/compaction.js +1 -0
- package/dist/runtime/context/environment-paths.d.ts +40 -0
- package/dist/runtime/context/environment-paths.js +48 -0
- package/dist/runtime/model-call.js +2 -0
- package/dist/sandbox/providers.d.ts +29 -7
- package/dist/sandbox/providers.js +43 -8
- package/dist/sandbox/sandbox.service.d.ts +70 -18
- package/dist/sandbox/sandbox.service.js +176 -59
- package/dist/tools/builtins/sandbox/bash.d.ts +2 -2
- package/dist/tools/builtins/sandbox/bash.js +39 -27
- package/dist/tools/builtins/sandbox/common.d.ts +60 -25
- package/dist/tools/builtins/sandbox/common.js +121 -30
- package/dist/tools/builtins/sandbox/deliver.js +9 -6
- package/dist/tools/builtins/sandbox/edit.js +40 -37
- package/dist/tools/builtins/sandbox/glob.js +13 -8
- package/dist/tools/builtins/sandbox/grep.js +10 -6
- package/dist/tools/builtins/sandbox/index.d.ts +6 -4
- package/dist/tools/builtins/sandbox/index.js +6 -4
- package/dist/tools/builtins/sandbox/read.js +9 -6
- package/dist/tools/builtins/sandbox/task-output.d.ts +7 -3
- package/dist/tools/builtins/sandbox/task-output.js +14 -10
- package/dist/tools/builtins/sandbox/task-stop.js +3 -3
- package/dist/tools/builtins/sandbox/write.js +33 -30
- package/dist/tools/builtins/skill-load.d.ts +8 -5
- package/dist/tools/builtins/skill-load.js +21 -15
- package/dist/tools/builtins/skill-write.js +2 -2
- package/dist/tools/pipeline.js +2 -0
- package/dist/tools/registry.d.ts +21 -4
- package/dist/tools/registry.js +10 -2
- package/package.json +6 -3
- package/src/migrations/0093_connected_environments.sql +37 -0
- package/src/migrations/0094_environment_paths_idx.sql +7 -0
|
@@ -10,6 +10,8 @@ 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 { AgentsService } from "../../agents/agents.service.js";
|
|
13
|
+
import { AccountsService } from "../../identity/accounts.js";
|
|
14
|
+
import { PreferencesSchema } from "../../identity/me.schemas.js";
|
|
13
15
|
import { can } from "../../orgs/roles.js";
|
|
14
16
|
import { effectiveSandboxEgress, OrgSettingsService } from "../../orgs/settings.service.js";
|
|
15
17
|
import { SANDBOX_TOOL_NAMES } from "../../tools/builtin-names.js";
|
|
@@ -34,19 +36,24 @@ const FAMILIES = [
|
|
|
34
36
|
"Plugin and connector tools, named plugin__<plugin>__<tool> and mcp__<connector>__<tool>.",
|
|
35
37
|
];
|
|
36
38
|
const EMPTY = z.object({}).strict();
|
|
37
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* The `platform` domain: orientation and what this agent is (spec §7, synthetic), and the
|
|
41
|
+
* person's own preferences (`/me/preferences`).
|
|
42
|
+
*/
|
|
38
43
|
let PlatformOperationsProvider = class PlatformOperationsProvider {
|
|
39
44
|
registry;
|
|
40
45
|
actor;
|
|
41
46
|
agents;
|
|
42
47
|
settings;
|
|
43
48
|
tools;
|
|
44
|
-
|
|
49
|
+
accounts;
|
|
50
|
+
constructor(registry, actor, agents, settings, tools, accounts) {
|
|
45
51
|
this.registry = registry;
|
|
46
52
|
this.actor = actor;
|
|
47
53
|
this.agents = agents;
|
|
48
54
|
this.settings = settings;
|
|
49
55
|
this.tools = tools;
|
|
56
|
+
this.accounts = accounts;
|
|
50
57
|
}
|
|
51
58
|
/**
|
|
52
59
|
* The agent's tools by the keys the model sees, as the registry resolves them for a run (tools
|
|
@@ -143,6 +150,45 @@ let PlatformOperationsProvider = class PlatformOperationsProvider {
|
|
|
143
150
|
};
|
|
144
151
|
},
|
|
145
152
|
}),
|
|
153
|
+
defineOperation({
|
|
154
|
+
id: "platform.preferences",
|
|
155
|
+
description: "Read your own preferences: your time zone (an IANA name such as Asia/Shanghai), or null when it follows the time zone of the browser you send messages from. The run context already tells you the zone in effect for this message.",
|
|
156
|
+
input: EMPTY,
|
|
157
|
+
example: {},
|
|
158
|
+
// any signed-in person may call the route; every run's actor is a member of this org
|
|
159
|
+
authz: { orgAction: "agents.use" },
|
|
160
|
+
mirrors: ["GET /api/v1/me/preferences"],
|
|
161
|
+
confirm: "read",
|
|
162
|
+
run: async (_input, ctx) => this.accounts.preferences(ctx.actorAccountId),
|
|
163
|
+
}),
|
|
164
|
+
defineOperation({
|
|
165
|
+
id: "platform.set-preferences",
|
|
166
|
+
description: "Set your own time zone (an IANA name such as Europe/Berlin), or null to follow the time zone of the browser you send messages from. It applies to the messages you send afterwards, on the web and in IM; the current run keeps its zone.",
|
|
167
|
+
input: PreferencesSchema,
|
|
168
|
+
example: { timeZone: "Asia/Shanghai" },
|
|
169
|
+
authz: { orgAction: "agents.use" },
|
|
170
|
+
mirrors: ["PUT /api/v1/me/preferences"],
|
|
171
|
+
confirm: "personal",
|
|
172
|
+
errors: [
|
|
173
|
+
{
|
|
174
|
+
code: "validation_failed",
|
|
175
|
+
next: "Use an IANA time zone name such as Asia/Shanghai or America/New_York, or null.",
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
summary: async (input, ctx) => {
|
|
179
|
+
const now = (await this.accounts.preferences(ctx.actorAccountId)).timeZone;
|
|
180
|
+
const from = { zh: now ?? "跟随浏览器", en: now ?? "follow the browser" };
|
|
181
|
+
const to = {
|
|
182
|
+
zh: input.timeZone ?? "跟随浏览器",
|
|
183
|
+
en: input.timeZone ?? "follow the browser",
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
zh: `把你的时区从「${from.zh}」改为「${to.zh}」`,
|
|
187
|
+
en: `Change your time zone from "${from.en}" to "${to.en}"`,
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
run: async (input, ctx) => this.accounts.setPreferences(ctx.actorAccountId, { timeZone: input.timeZone }),
|
|
191
|
+
}),
|
|
146
192
|
]);
|
|
147
193
|
}
|
|
148
194
|
};
|
|
@@ -152,6 +198,7 @@ PlatformOperationsProvider = __decorate([
|
|
|
152
198
|
__param(1, Inject(PlatformActor)),
|
|
153
199
|
__param(2, Inject(AgentsService)),
|
|
154
200
|
__param(3, Inject(OrgSettingsService)),
|
|
155
|
-
__param(4, Inject(ToolRegistry))
|
|
201
|
+
__param(4, Inject(ToolRegistry)),
|
|
202
|
+
__param(5, Inject(AccountsService))
|
|
156
203
|
], PlatformOperationsProvider);
|
|
157
204
|
export { PlatformOperationsProvider };
|
|
@@ -15,6 +15,7 @@ import { ENVIRONMENT_ROUTES } from "../environments/environments.schemas.js";
|
|
|
15
15
|
import { EVAL_ROUTES } from "../evals/evals.schemas.js";
|
|
16
16
|
import { EXTENSION_POINT_ROUTES } from "../extensions/point-defaults.schemas.js";
|
|
17
17
|
import { FILE_PREVIEWER_ROUTES } from "../files/previewers.schemas.js";
|
|
18
|
+
import { ME_ROUTES } from "../identity/me.schemas.js";
|
|
18
19
|
import { INBOX_ROUTES } from "../inbox/inbox.schemas.js";
|
|
19
20
|
import { ORG_MODEL_ROUTES } from "../models/org-models.schemas.js";
|
|
20
21
|
import { ORG_ROUTES, ORG_SETTINGS_ROUTES } from "../orgs/orgs.schemas.js";
|
|
@@ -57,4 +58,6 @@ export const ROUTE_SCHEMAS = [
|
|
|
57
58
|
...FILE_PREVIEWER_ROUTES,
|
|
58
59
|
// Task 11b: environments
|
|
59
60
|
...ENVIRONMENT_ROUTES,
|
|
61
|
+
// personal preferences (run context spec §3.4)
|
|
62
|
+
...ME_ROUTES,
|
|
60
63
|
];
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { ChannelSupervisor } from "../channels/supervisor.js";
|
|
3
3
|
import { ChannelWebhooks } from "../channels/webhook.js";
|
|
4
|
+
import { DeviceGateway } from "../devices/device-gateway.js";
|
|
4
5
|
import { ExtensionHost } from "../extensions/host.js";
|
|
5
6
|
import type { GatewayRoutes } from "../http/gateway-routes.js";
|
|
6
7
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
7
8
|
/**
|
|
8
9
|
* The gateway role's parts, driven by `KernelLifecycle` at fixed points of its start order and
|
|
9
10
|
* stop phases: the channel long-connection supervisor (leader election and share) and the webhook
|
|
10
|
-
* inbound (its raw Fastify route, {@link routes}, and its runtime cache's invalidation)
|
|
11
|
+
* inbound (its raw Fastify route, {@link routes}, and its runtime cache's invalidation), and the
|
|
12
|
+
* device connections ({@link DeviceGateway}, `GET /device/connect`). Provided
|
|
11
13
|
* only by `GatewayModule`, so a process without the gateway role has none of them.
|
|
12
14
|
*/
|
|
13
15
|
export declare class GatewayRole {
|
|
@@ -16,6 +18,7 @@ export declare class GatewayRole {
|
|
|
16
18
|
private readonly bus;
|
|
17
19
|
private readonly supervisor;
|
|
18
20
|
private readonly webhooks;
|
|
21
|
+
private readonly devices;
|
|
19
22
|
/** The supervisor's subscription to extension instance changes. */
|
|
20
23
|
private unwatchPlugins;
|
|
21
24
|
private unwatchWebhooks;
|
|
@@ -23,8 +26,11 @@ export declare class GatewayRole {
|
|
|
23
26
|
private unsubscribeWebhooks;
|
|
24
27
|
/** The supervisor was started: the runs phase stops it. */
|
|
25
28
|
private supervisorStarted;
|
|
26
|
-
constructor(config: KernelConfigService, host: ExtensionHost, bus: Pick<ExtensionContext["bus"], "subscribe">, supervisor: ChannelSupervisor, webhooks: ChannelWebhooks);
|
|
27
|
-
/**
|
|
29
|
+
constructor(config: KernelConfigService, host: ExtensionHost, bus: Pick<ExtensionContext["bus"], "subscribe">, supervisor: ChannelSupervisor, webhooks: ChannelWebhooks, devices: DeviceGateway);
|
|
30
|
+
/**
|
|
31
|
+
* The gateway role's HTTP routes (the channel webhook, the device connection), registered when
|
|
32
|
+
* the app is built.
|
|
33
|
+
*/
|
|
28
34
|
routes(): GatewayRoutes;
|
|
29
35
|
/**
|
|
30
36
|
* Starts the supervisor and the webhook cache invalidation (after the extension load).
|
|
@@ -35,6 +41,11 @@ export declare class GatewayRole {
|
|
|
35
41
|
unwatch(): void;
|
|
36
42
|
/** Ends the webhook cache's `channel.changed` subscription. */
|
|
37
43
|
unsubscribe(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Closes the device connections (1001) and hands their leases back, before the HTTP server
|
|
46
|
+
* closes (the machines reconnect to another gateway replica).
|
|
47
|
+
*/
|
|
48
|
+
stopDevices(): Promise<void>;
|
|
38
49
|
/**
|
|
39
50
|
* The runs phase: the supervisor aborts its connects and hands back its leases (undefined: never
|
|
40
51
|
* started).
|
|
@@ -11,13 +11,15 @@ import { Inject, Injectable } from "@nestjs/common";
|
|
|
11
11
|
import { CHANNEL_CHANGED_TOPIC } from "../channels/connections.service.js";
|
|
12
12
|
import { ChannelSupervisor } from "../channels/supervisor.js";
|
|
13
13
|
import { ChannelWebhooks } from "../channels/webhook.js";
|
|
14
|
+
import { DeviceGateway } from "../devices/device-gateway.js";
|
|
14
15
|
import { ExtensionHost } from "../extensions/host.js";
|
|
15
16
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
16
17
|
import { KERNEL_BUS } from "../infra/tokens.js";
|
|
17
18
|
/**
|
|
18
19
|
* The gateway role's parts, driven by `KernelLifecycle` at fixed points of its start order and
|
|
19
20
|
* stop phases: the channel long-connection supervisor (leader election and share) and the webhook
|
|
20
|
-
* inbound (its raw Fastify route, {@link routes}, and its runtime cache's invalidation)
|
|
21
|
+
* inbound (its raw Fastify route, {@link routes}, and its runtime cache's invalidation), and the
|
|
22
|
+
* device connections ({@link DeviceGateway}, `GET /device/connect`). Provided
|
|
21
23
|
* only by `GatewayModule`, so a process without the gateway role has none of them.
|
|
22
24
|
*/
|
|
23
25
|
let GatewayRole = class GatewayRole {
|
|
@@ -26,6 +28,7 @@ let GatewayRole = class GatewayRole {
|
|
|
26
28
|
bus;
|
|
27
29
|
supervisor;
|
|
28
30
|
webhooks;
|
|
31
|
+
devices;
|
|
29
32
|
/** The supervisor's subscription to extension instance changes. */
|
|
30
33
|
unwatchPlugins;
|
|
31
34
|
unwatchWebhooks;
|
|
@@ -33,18 +36,27 @@ let GatewayRole = class GatewayRole {
|
|
|
33
36
|
unsubscribeWebhooks;
|
|
34
37
|
/** The supervisor was started: the runs phase stops it. */
|
|
35
38
|
supervisorStarted = false;
|
|
36
|
-
constructor(config, host, bus, supervisor, webhooks) {
|
|
39
|
+
constructor(config, host, bus, supervisor, webhooks, devices) {
|
|
37
40
|
this.config = config;
|
|
38
41
|
this.host = host;
|
|
39
42
|
this.bus = bus;
|
|
40
43
|
this.supervisor = supervisor;
|
|
41
44
|
this.webhooks = webhooks;
|
|
45
|
+
this.devices = devices;
|
|
42
46
|
}
|
|
43
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The gateway role's HTTP routes (the channel webhook, the device connection), registered when
|
|
49
|
+
* the app is built.
|
|
50
|
+
*/
|
|
44
51
|
routes() {
|
|
52
|
+
const config = this.config.get();
|
|
45
53
|
return {
|
|
46
54
|
webhooks: this.webhooks,
|
|
47
|
-
webhookMaxBodyBytes:
|
|
55
|
+
webhookMaxBodyBytes: config.channels.webhookMaxBodyBytes,
|
|
56
|
+
devices: {
|
|
57
|
+
gateway: this.devices,
|
|
58
|
+
maxFrameBytes: config.environments.device.maxFrameBytes,
|
|
59
|
+
},
|
|
48
60
|
};
|
|
49
61
|
}
|
|
50
62
|
/**
|
|
@@ -79,6 +91,13 @@ let GatewayRole = class GatewayRole {
|
|
|
79
91
|
unsubscribe() {
|
|
80
92
|
return this.unsubscribeWebhooks?.() ?? Promise.resolve();
|
|
81
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Closes the device connections (1001) and hands their leases back, before the HTTP server
|
|
96
|
+
* closes (the machines reconnect to another gateway replica).
|
|
97
|
+
*/
|
|
98
|
+
stopDevices() {
|
|
99
|
+
return this.devices.stop();
|
|
100
|
+
}
|
|
82
101
|
/**
|
|
83
102
|
* The runs phase: the supervisor aborts its connects and hands back its leases (undefined: never
|
|
84
103
|
* started).
|
|
@@ -93,6 +112,7 @@ GatewayRole = __decorate([
|
|
|
93
112
|
__param(1, Inject(ExtensionHost)),
|
|
94
113
|
__param(2, Inject(KERNEL_BUS)),
|
|
95
114
|
__param(3, Inject(ChannelSupervisor)),
|
|
96
|
-
__param(4, Inject(ChannelWebhooks))
|
|
115
|
+
__param(4, Inject(ChannelWebhooks)),
|
|
116
|
+
__param(5, Inject(DeviceGateway))
|
|
97
117
|
], GatewayRole);
|
|
98
118
|
export { GatewayRole };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The gateway role (nest kernel spec §2), imported by `KernelModule` only when `roles` includes
|
|
3
3
|
* `gateway`: the channel long-connection supervisor, the webhook inbound (served as a raw Fastify
|
|
4
|
-
* route, `http/gateway-routes.ts`)
|
|
4
|
+
* route, `http/gateway-routes.ts`), the device connections ({@link DeviceGateway}, `GET
|
|
5
|
+
* /device/connect`) and {@link GatewayRole}, which `KernelLifecycle` drives. It runs
|
|
5
6
|
* no cron and no queue consumer (those are the worker role's).
|
|
6
7
|
*/
|
|
7
8
|
export declare class GatewayModule {
|
|
@@ -8,20 +8,23 @@ import { Module } from "@nestjs/common";
|
|
|
8
8
|
import { ChannelsModule } from "../channels/channels.module.js";
|
|
9
9
|
import { ChannelSupervisor } from "../channels/supervisor.js";
|
|
10
10
|
import { ChannelWebhooks } from "../channels/webhook.js";
|
|
11
|
+
import { DeviceGateway } from "../devices/device-gateway.js";
|
|
12
|
+
import { DevicesModule } from "../devices/devices.module.js";
|
|
11
13
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
12
14
|
import { GatewayRole } from "./gateway-role.js";
|
|
13
15
|
/**
|
|
14
16
|
* The gateway role (nest kernel spec §2), imported by `KernelModule` only when `roles` includes
|
|
15
17
|
* `gateway`: the channel long-connection supervisor, the webhook inbound (served as a raw Fastify
|
|
16
|
-
* route, `http/gateway-routes.ts`)
|
|
18
|
+
* route, `http/gateway-routes.ts`), the device connections ({@link DeviceGateway}, `GET
|
|
19
|
+
* /device/connect`) and {@link GatewayRole}, which `KernelLifecycle` drives. It runs
|
|
17
20
|
* no cron and no queue consumer (those are the worker role's).
|
|
18
21
|
*/
|
|
19
22
|
let GatewayModule = class GatewayModule {
|
|
20
23
|
};
|
|
21
24
|
GatewayModule = __decorate([
|
|
22
25
|
Module({
|
|
23
|
-
imports: [ChannelsModule, ExtensionsModule],
|
|
24
|
-
providers: [ChannelSupervisor, ChannelWebhooks, GatewayRole],
|
|
26
|
+
imports: [ChannelsModule, ExtensionsModule, DevicesModule],
|
|
27
|
+
providers: [ChannelSupervisor, ChannelWebhooks, DeviceGateway, GatewayRole],
|
|
25
28
|
exports: [GatewayRole],
|
|
26
29
|
})
|
|
27
30
|
], GatewayModule);
|
|
@@ -233,6 +233,8 @@ export declare function summaryText(summary: string): string;
|
|
|
233
233
|
* `user_message` / `assistant_message`(content 原样,含 opaque;aborted 的也回放;媒体引用按
|
|
234
234
|
* {@link resolveMedia} 换成 base64 或文字占位)/ `reminder`
|
|
235
235
|
* (`role: "system"`,放在它所在的位置)/ `tools_changed` (`role: "system"`, the text of {@link toolsChangedText}, at its position) /
|
|
236
|
+
* `environment_paths` (`role: "system"` at its position, `environmentPathsText`; covered by the
|
|
237
|
+
* summary: the latest one follows the summary) /
|
|
236
238
|
* `run_context` (`role: "system"` at its position, `runContextText`; covered by the summary: the
|
|
237
239
|
* current context follows the summary whole; a user message's sender line by `senderLines`) /
|
|
238
240
|
* `background_task_finished`(`role: "system"`, the text of
|
|
@@ -4,6 +4,7 @@ import { renderBackgroundFinished } from "../../background/render.js";
|
|
|
4
4
|
import { isAbsorbable, } from "../../conversations/events.js";
|
|
5
5
|
import { catalogText, isAlwaysLoaded } from "../../tools/builtins/tool-search.js";
|
|
6
6
|
import { renderSubagentDelivery } from "../subagents/render.js";
|
|
7
|
+
import { environmentPathsAt, environmentPathsText } from "./environment-paths.js";
|
|
7
8
|
import { LEGACY_CATALOG_HEADER, LEGACY_SKILL_TEXTS, usesLegacyToolNames } from "./legacy-text.js";
|
|
8
9
|
import { currentRunContext, runContextEvents, runContextFullText, runContextText, senderLines, } from "./run-context.js";
|
|
9
10
|
import { invokedSkillNames, invokedSkillsText, SKILL_TEXTS, skillsCatalogEvents, snapshotChangeText, snapshotCurrentText, snapshotSystemText, systemSkillsCatalog, } from "./skills.js";
|
|
@@ -336,6 +337,8 @@ export function summaryText(summary) {
|
|
|
336
337
|
* `user_message` / `assistant_message`(content 原样,含 opaque;aborted 的也回放;媒体引用按
|
|
337
338
|
* {@link resolveMedia} 换成 base64 或文字占位)/ `reminder`
|
|
338
339
|
* (`role: "system"`,放在它所在的位置)/ `tools_changed` (`role: "system"`, the text of {@link toolsChangedText}, at its position) /
|
|
340
|
+
* `environment_paths` (`role: "system"` at its position, `environmentPathsText`; covered by the
|
|
341
|
+
* summary: the latest one follows the summary) /
|
|
339
342
|
* `run_context` (`role: "system"` at its position, `runContextText`; covered by the summary: the
|
|
340
343
|
* current context follows the summary whole; a user message's sender line by `senderLines`) /
|
|
341
344
|
* `background_task_finished`(`role: "system"`, the text of
|
|
@@ -547,6 +550,12 @@ export function buildModelRequest(input) {
|
|
|
547
550
|
content: runContextFullText(currentRunContext(events, after)),
|
|
548
551
|
});
|
|
549
552
|
}
|
|
553
|
+
// environments spec §13.1: the instance's directories told before the boundary follow the
|
|
554
|
+
// summary again (the model keeps using the real paths)
|
|
555
|
+
const paths = environmentPathsAt(events, after);
|
|
556
|
+
if (paths) {
|
|
557
|
+
messages.push({ role: "system", content: environmentPathsText(paths.payload.roots) });
|
|
558
|
+
}
|
|
550
559
|
// 被摘要覆盖的变更提醒不再发送:摘要时生效的目录与 system 里的不同 → 紧随摘要补上完整的当前目录
|
|
551
560
|
const covered = skillSnaps.filter((e) => e.seq <= after).at(-1);
|
|
552
561
|
// (phase B: plus the current pinned skills and tool-name map when they differ from system's)
|
|
@@ -620,6 +629,11 @@ export function buildModelRequest(input) {
|
|
|
620
629
|
case "run_context":
|
|
621
630
|
messages.push({ role: "system", content: runContextText(events, e) });
|
|
622
631
|
break;
|
|
632
|
+
// the instance's directories (environments spec §13.1): logged during a step's tool call,
|
|
633
|
+
// so rendered after that step's tool results
|
|
634
|
+
case "environment_paths":
|
|
635
|
+
messages.push({ role: "system", content: environmentPathsText(e.payload.roots) });
|
|
636
|
+
break;
|
|
623
637
|
// hook_denied is not model-visible: the model sees only the denied tool call's tool_result
|
|
624
638
|
// (and nothing for a denied model request), so request building skips it
|
|
625
639
|
case "hook_denied":
|
|
@@ -134,6 +134,7 @@ function transcriptLine(e) {
|
|
|
134
134
|
// phase D: a forked skill's report is user content of the request (build.ts)
|
|
135
135
|
case "skill_fork_result":
|
|
136
136
|
return `User: ${e.payload.text}`;
|
|
137
|
+
// environment_paths is left out too: the latest one follows the summary (build.ts)
|
|
137
138
|
// run_context is left out: its current values follow the summary anyway (build.ts), and
|
|
138
139
|
// stale names should not be summarised as current
|
|
139
140
|
case "tool_result":
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SandboxRoots } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import type { ConversationEvent } from "../../conversations/events.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `environment_paths` event (environments spec §13.1, ruling R22): the first sandbox tool call
|
|
5
|
+
* of every conversation records the instance's roots (the defaults included), and a later call
|
|
6
|
+
* records them again whenever they differ from the conversation's latest recorded ones (a
|
|
7
|
+
* connected machine's real paths, a mode switch back to the defaults). Model-visible: rendered at
|
|
8
|
+
* its position as a system message; in the log, so it needs no `DriftReason`.
|
|
9
|
+
*/
|
|
10
|
+
/** The system line of an `environment_paths` event (model-visible, English). */
|
|
11
|
+
export declare function environmentPathsText(r: SandboxRoots): string;
|
|
12
|
+
/** Whether two sets of roots are the same paths. */
|
|
13
|
+
export declare const sameRoots: (a: SandboxRoots, b: SandboxRoots) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The idempotency key of an `environment_paths` event for `r` that follows the conversation's
|
|
16
|
+
* event at `prevSeq` (0: none). Unique per change (A → B → A gets three events), while parallel
|
|
17
|
+
* calls that saw the same latest event write it once.
|
|
18
|
+
*/
|
|
19
|
+
export declare const environmentPathsIdemKey: (r: SandboxRoots, prevSeq: number) => string;
|
|
20
|
+
/**
|
|
21
|
+
* The `environment_paths` event to append when a sandbox call holds an instance with `roots` and
|
|
22
|
+
* the conversation's latest such event is `latest`; null when nothing changed.
|
|
23
|
+
*/
|
|
24
|
+
export declare function environmentPathsChange(latest: {
|
|
25
|
+
seq: number;
|
|
26
|
+
payload: {
|
|
27
|
+
roots: SandboxRoots;
|
|
28
|
+
};
|
|
29
|
+
} | undefined, roots: SandboxRoots, runId: string | undefined): {
|
|
30
|
+
type: "environment_paths";
|
|
31
|
+
runId?: string;
|
|
32
|
+
idemKey: string;
|
|
33
|
+
payload: {
|
|
34
|
+
roots: SandboxRoots;
|
|
35
|
+
};
|
|
36
|
+
} | null;
|
|
37
|
+
/** The latest `environment_paths` event at or before `seq`; undefined without one. */
|
|
38
|
+
export declare function environmentPathsAt(events: readonly ConversationEvent[], seq: number): Extract<ConversationEvent, {
|
|
39
|
+
type: "environment_paths";
|
|
40
|
+
}> | undefined;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* The `environment_paths` event (environments spec §13.1, ruling R22): the first sandbox tool call
|
|
4
|
+
* of every conversation records the instance's roots (the defaults included), and a later call
|
|
5
|
+
* records them again whenever they differ from the conversation's latest recorded ones (a
|
|
6
|
+
* connected machine's real paths, a mode switch back to the defaults). Model-visible: rendered at
|
|
7
|
+
* its position as a system message; in the log, so it needs no `DriftReason`.
|
|
8
|
+
*/
|
|
9
|
+
/** The system line of an `environment_paths` event (model-visible, English). */
|
|
10
|
+
export function environmentPathsText(r) {
|
|
11
|
+
return `This environment's directories: workspace ${r.workspace}, skills ${r.skills} (read only), temporary files ${r.tmp}. Use these paths in commands and tool calls.`;
|
|
12
|
+
}
|
|
13
|
+
/** Whether two sets of roots are the same paths. */
|
|
14
|
+
export const sameRoots = (a, b) => a.workspace === b.workspace && a.skills === b.skills && a.tmp === b.tmp;
|
|
15
|
+
/**
|
|
16
|
+
* The idempotency key of an `environment_paths` event for `r` that follows the conversation's
|
|
17
|
+
* event at `prevSeq` (0: none). Unique per change (A → B → A gets three events), while parallel
|
|
18
|
+
* calls that saw the same latest event write it once.
|
|
19
|
+
*/
|
|
20
|
+
export const environmentPathsIdemKey = (r, prevSeq) => `environment_paths:${createHash("sha256")
|
|
21
|
+
.update(JSON.stringify([r.workspace, r.skills, r.tmp]))
|
|
22
|
+
.digest("hex")
|
|
23
|
+
.slice(0, 16)}:${prevSeq}`;
|
|
24
|
+
/**
|
|
25
|
+
* The `environment_paths` event to append when a sandbox call holds an instance with `roots` and
|
|
26
|
+
* the conversation's latest such event is `latest`; null when nothing changed.
|
|
27
|
+
*/
|
|
28
|
+
export function environmentPathsChange(latest, roots, runId) {
|
|
29
|
+
if (latest && sameRoots(latest.payload.roots, roots))
|
|
30
|
+
return null;
|
|
31
|
+
return {
|
|
32
|
+
type: "environment_paths",
|
|
33
|
+
...(runId !== undefined && { runId }),
|
|
34
|
+
idemKey: environmentPathsIdemKey(roots, latest?.seq ?? 0),
|
|
35
|
+
payload: { roots: { workspace: roots.workspace, skills: roots.skills, tmp: roots.tmp } },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** The latest `environment_paths` event at or before `seq`; undefined without one. */
|
|
39
|
+
export function environmentPathsAt(events, seq) {
|
|
40
|
+
let found;
|
|
41
|
+
for (const e of events) {
|
|
42
|
+
if (e.seq > seq)
|
|
43
|
+
break;
|
|
44
|
+
if (e.type === "environment_paths")
|
|
45
|
+
found = e;
|
|
46
|
+
}
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
@@ -163,6 +163,8 @@ wrapUpReason) {
|
|
|
163
163
|
subagents: input.agent.subagents,
|
|
164
164
|
child: input.parent !== undefined || input.parentRunId !== undefined || input.subagent === true,
|
|
165
165
|
forkChild: input.parent?.forSkill === true,
|
|
166
|
+
sandboxConversationId: input.parent?.rootConversationId ?? input.conversationId,
|
|
167
|
+
replay: input.conversationKind === "eval",
|
|
166
168
|
skillLoad: skillLoadOffered(events.findLast((e) => e.type === "skills_catalog"), hasInvocation(events)),
|
|
167
169
|
});
|
|
168
170
|
const tools = {
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import { type SandboxProvider } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
3
|
-
/**
|
|
3
|
+
/** The kernel-internal provider of connected machines (`device` environments). */
|
|
4
|
+
export declare const DEVICE_PROVIDER = "@device";
|
|
5
|
+
/**
|
|
6
|
+
* A kernel-internal provider name: it starts with `@`, which no extension name can
|
|
7
|
+
* (`EXTENSION_NAME_RE`), so it never collides with a loaded `sandbox@2` plugin.
|
|
8
|
+
*/
|
|
9
|
+
export declare const isInternalProvider: (name: string) => boolean;
|
|
10
|
+
/** A sandbox provider: its name (plugin name, or an internal `@…` name) and implementation. */
|
|
4
11
|
export interface SandboxProviderEntry {
|
|
5
12
|
plugin: string;
|
|
6
13
|
impl: SandboxProvider;
|
|
7
14
|
}
|
|
8
15
|
/**
|
|
9
|
-
* The
|
|
10
|
-
* call
|
|
16
|
+
* The sandbox providers of this process. The cloud provider is the loaded `sandbox@2` extension,
|
|
17
|
+
* read from the {@link ExtensionRegistry} at every call (providers are ready only after the
|
|
18
|
+
* extension load, and change with installs). Kernel-internal providers (`@device`) are registered
|
|
19
|
+
* by their module on init, in every replica: they are code, not shared state.
|
|
11
20
|
*/
|
|
12
21
|
export declare class SandboxProviders {
|
|
13
22
|
private readonly registry;
|
|
23
|
+
private readonly internal;
|
|
14
24
|
constructor(registry: Pick<ExtensionRegistry, "list">);
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
|
|
25
|
+
/** Registers a kernel-internal provider (an `@…` name); a later registration replaces it. */
|
|
26
|
+
register(entry: SandboxProviderEntry): void;
|
|
27
|
+
/** The cloud provider (the loaded `sandbox@2` extension); undefined without one. */
|
|
28
|
+
cloud(): SandboxProviderEntry | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* A provider by name: a registered internal provider, else a still-loaded `sandbox@2` extension
|
|
31
|
+
* of that plugin name (to destroy an earlier provider's resources).
|
|
32
|
+
*/
|
|
33
|
+
forName(name: string): SandboxProviderEntry | undefined;
|
|
19
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The provider an environment works with, from its `environments.provider`: an internal one
|
|
37
|
+
* (`@device`) by that name only, never falling back to the cloud provider; any other environment
|
|
38
|
+
* is a cloud environment and uses the current cloud provider (files kept by another provider are
|
|
39
|
+
* refused by the sandbox service, `environment.unavailable`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function providerForEnvironment(providers: Pick<SandboxProviders, "cloud" | "forName">, provider: string | null): SandboxProviderEntry | undefined;
|
|
@@ -10,25 +10,49 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
10
10
|
import { SANDBOX_EXTENSION_POINT } from "@capybara-hq/plugin-sdk";
|
|
11
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
12
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
13
|
+
/** The kernel-internal provider of connected machines (`device` environments). */
|
|
14
|
+
export const DEVICE_PROVIDER = "@device";
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* A kernel-internal provider name: it starts with `@`, which no extension name can
|
|
17
|
+
* (`EXTENSION_NAME_RE`), so it never collides with a loaded `sandbox@2` plugin.
|
|
18
|
+
*/
|
|
19
|
+
export const isInternalProvider = (name) => name.startsWith("@");
|
|
20
|
+
/**
|
|
21
|
+
* The sandbox providers of this process. The cloud provider is the loaded `sandbox@2` extension,
|
|
22
|
+
* read from the {@link ExtensionRegistry} at every call (providers are ready only after the
|
|
23
|
+
* extension load, and change with installs). Kernel-internal providers (`@device`) are registered
|
|
24
|
+
* by their module on init, in every replica: they are code, not shared state.
|
|
16
25
|
*/
|
|
17
26
|
let SandboxProviders = class SandboxProviders {
|
|
18
27
|
registry;
|
|
28
|
+
internal = new Map();
|
|
19
29
|
constructor(registry) {
|
|
20
30
|
this.registry = registry;
|
|
21
31
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
32
|
+
/** Registers a kernel-internal provider (an `@…` name); a later registration replaces it. */
|
|
33
|
+
register(entry) {
|
|
34
|
+
if (!isInternalProvider(entry.plugin)) {
|
|
35
|
+
throw new Error(`an internal sandbox provider name starts with @: ${entry.plugin}`);
|
|
36
|
+
}
|
|
37
|
+
this.internal.set(entry.plugin, entry);
|
|
38
|
+
}
|
|
39
|
+
/** The cloud provider (the loaded `sandbox@2` extension); undefined without one. */
|
|
40
|
+
cloud() {
|
|
24
41
|
const e = this.registry.list("sandbox").find((x) => x.capability === SANDBOX_EXTENSION_POINT);
|
|
25
42
|
return e ? { plugin: e.plugin, impl: e.impl } : undefined;
|
|
26
43
|
}
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
/**
|
|
45
|
+
* A provider by name: a registered internal provider, else a still-loaded `sandbox@2` extension
|
|
46
|
+
* of that plugin name (to destroy an earlier provider's resources).
|
|
47
|
+
*/
|
|
48
|
+
forName(name) {
|
|
49
|
+
const own = this.internal.get(name);
|
|
50
|
+
if (own)
|
|
51
|
+
return own;
|
|
52
|
+
const e = this.registry
|
|
30
53
|
.list("sandbox")
|
|
31
|
-
.find((x) => x.capability === SANDBOX_EXTENSION_POINT && x.plugin ===
|
|
54
|
+
.find((x) => x.capability === SANDBOX_EXTENSION_POINT && x.plugin === name);
|
|
55
|
+
return e ? { plugin: e.plugin, impl: e.impl } : undefined;
|
|
32
56
|
}
|
|
33
57
|
};
|
|
34
58
|
SandboxProviders = __decorate([
|
|
@@ -36,3 +60,14 @@ SandboxProviders = __decorate([
|
|
|
36
60
|
__param(0, Inject(ExtensionRegistry))
|
|
37
61
|
], SandboxProviders);
|
|
38
62
|
export { SandboxProviders };
|
|
63
|
+
/**
|
|
64
|
+
* The provider an environment works with, from its `environments.provider`: an internal one
|
|
65
|
+
* (`@device`) by that name only, never falling back to the cloud provider; any other environment
|
|
66
|
+
* is a cloud environment and uses the current cloud provider (files kept by another provider are
|
|
67
|
+
* refused by the sandbox service, `environment.unavailable`).
|
|
68
|
+
*/
|
|
69
|
+
export function providerForEnvironment(providers, provider) {
|
|
70
|
+
if (provider !== null && isInternalProvider(provider))
|
|
71
|
+
return providers.forName(provider);
|
|
72
|
+
return providers.cloud();
|
|
73
|
+
}
|