@capybara-hq/kernel 0.1.0-rc.12 → 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.
Files changed (192) hide show
  1. package/dist/admin/admin.schemas.d.ts +13 -0
  2. package/dist/agents/agent-lookup.d.ts +5 -0
  3. package/dist/agents/agent-lookup.js +15 -0
  4. package/dist/background/background.module.js +2 -2
  5. package/dist/background/launch-lock.d.ts +1 -1
  6. package/dist/background/launch-lock.js +1 -1
  7. package/dist/background/poll.job.d.ts +2 -2
  8. package/dist/background/poll.job.js +10 -10
  9. package/dist/background/stop.d.ts +1 -1
  10. package/dist/background/stop.js +3 -3
  11. package/dist/background/store.d.ts +5 -5
  12. package/dist/background/store.js +5 -5
  13. package/dist/background/tasks.service.d.ts +6 -6
  14. package/dist/background/tasks.service.js +13 -13
  15. package/dist/catalog/marketplaces.service.d.ts +1 -1
  16. package/dist/channels/channels.module.js +2 -2
  17. package/dist/channels/inbound.d.ts +8 -8
  18. package/dist/channels/inbound.js +31 -31
  19. package/dist/channels/phrases.d.ts +27 -27
  20. package/dist/channels/phrases.js +27 -27
  21. package/dist/config/docs.js +1 -1
  22. package/dist/config/schema.d.ts +2 -2
  23. package/dist/config/schema.js +21 -21
  24. package/dist/conversations/conversations.controller.d.ts +11 -11
  25. package/dist/conversations/conversations.controller.js +15 -15
  26. package/dist/conversations/conversations.module.d.ts +1 -1
  27. package/dist/conversations/conversations.module.js +7 -7
  28. package/dist/conversations/conversations.schemas.js +1 -1
  29. package/dist/conversations/conversations.service.d.ts +14 -14
  30. package/dist/conversations/conversations.service.js +19 -19
  31. package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
  32. package/dist/conversations/{environment-status.js → device-status.js} +18 -18
  33. package/dist/conversations/event-log.d.ts +1 -1
  34. package/dist/conversations/event-log.js +1 -1
  35. package/dist/conversations/events.d.ts +1 -1
  36. package/dist/conversations/events.js +2 -2
  37. package/dist/ctx/create-ctx.d.ts +10 -1
  38. package/dist/ctx/create-ctx.js +86 -25
  39. package/dist/db/kernel-schema.d.ts +26 -23
  40. package/dist/{environments → devices}/access.d.ts +19 -19
  41. package/dist/{environments → devices}/access.js +28 -28
  42. package/dist/devices/conversation-devices.d.ts +75 -0
  43. package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
  44. package/dist/devices/devices.controller.d.ts +36 -0
  45. package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
  46. package/dist/devices/devices.module.d.ts +5 -13
  47. package/dist/devices/devices.module.js +16 -29
  48. package/dist/devices/devices.schemas.d.ts +3 -0
  49. package/dist/devices/devices.schemas.js +14 -0
  50. package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
  51. package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
  52. package/dist/devices/dto.d.ts +13 -54
  53. package/dist/devices/dto.js +7 -39
  54. package/dist/devices/label.d.ts +11 -0
  55. package/dist/{environments → devices}/label.js +7 -7
  56. package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
  57. package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
  58. package/dist/errors.d.ts +25 -25
  59. package/dist/errors.js +25 -25
  60. package/dist/evals/recorded-tools.d.ts +2 -2
  61. package/dist/evals/recorded-tools.js +6 -6
  62. package/dist/extensions/admin-ops.js +1 -0
  63. package/dist/extensions/admin-view.js +2 -0
  64. package/dist/extensions/effective-config.d.ts +6 -0
  65. package/dist/extensions/effective-config.js +18 -7
  66. package/dist/extensions/host.d.ts +13 -2
  67. package/dist/extensions/host.js +24 -3
  68. package/dist/extensions/instance.d.ts +12 -1
  69. package/dist/extensions/instance.js +13 -0
  70. package/dist/extensions/permission-view.d.ts +10 -1
  71. package/dist/files/previewers.d.ts +1 -1
  72. package/dist/files/previewers.js +1 -1
  73. package/dist/http/api.module.js +10 -6
  74. package/dist/http/gateway-routes.d.ts +1 -1
  75. package/dist/http/gateway-routes.js +1 -1
  76. package/dist/identity/settings.d.ts +32 -0
  77. package/dist/identity/settings.js +26 -2
  78. package/dist/infra/config/kernel-config.service.js +4 -4
  79. package/dist/kernel.module.js +3 -3
  80. package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
  81. package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
  82. package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
  83. package/dist/{devices → local-devices}/device-auth.js +51 -53
  84. package/dist/local-devices/device-auth.module.d.ts +7 -0
  85. package/dist/{devices → local-devices}/device-auth.module.js +5 -5
  86. package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
  87. package/dist/{devices → local-devices}/device-routes.js +2 -2
  88. package/dist/local-devices/dto.d.ts +66 -0
  89. package/dist/local-devices/dto.js +44 -0
  90. package/dist/{devices → local-devices}/keys.d.ts +11 -11
  91. package/dist/{devices → local-devices}/keys.js +11 -11
  92. package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
  93. package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
  94. package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
  95. package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
  96. package/dist/local-devices/local-devices.module.d.ts +16 -0
  97. package/dist/local-devices/local-devices.module.js +44 -0
  98. package/dist/{devices → local-devices}/machines.d.ts +26 -26
  99. package/dist/{devices → local-devices}/machines.js +71 -71
  100. package/dist/{devices → local-devices}/relay.d.ts +15 -15
  101. package/dist/{devices → local-devices}/relay.js +55 -55
  102. package/dist/{devices → local-devices}/targets.d.ts +8 -8
  103. package/dist/{devices → local-devices}/targets.js +14 -14
  104. package/dist/orgs/offboarding.d.ts +6 -6
  105. package/dist/orgs/offboarding.js +12 -12
  106. package/dist/orgs/settings.service.d.ts +28 -0
  107. package/dist/orgs/settings.service.js +20 -1
  108. package/dist/platform/exclusions.js +38 -3
  109. package/dist/platform/operation.js +6 -2
  110. package/dist/platform/ops/admin.ops.js +8 -2
  111. package/dist/platform/ops/conversations.ops.d.ts +3 -3
  112. package/dist/platform/ops/conversations.ops.js +9 -9
  113. package/dist/platform/ops/devices.ops.d.ts +26 -0
  114. package/dist/platform/ops/devices.ops.js +311 -0
  115. package/dist/platform/ops/org-settings.ops.js +2 -2
  116. package/dist/platform/ops/orgs.ops.js +1 -0
  117. package/dist/platform/ops/ui.ops.d.ts +8 -0
  118. package/dist/platform/ops/ui.ops.js +41 -0
  119. package/dist/platform/platform-ops.module.d.ts +3 -2
  120. package/dist/platform/platform-ops.module.js +8 -5
  121. package/dist/platform/route-schemas.js +6 -3
  122. package/dist/roles/gateway-role.d.ts +3 -3
  123. package/dist/roles/gateway-role.js +4 -4
  124. package/dist/roles/gateway.module.d.ts +1 -1
  125. package/dist/roles/gateway.module.js +5 -5
  126. package/dist/roles/worker.module.js +1 -1
  127. package/dist/runtime/context/build.d.ts +1 -1
  128. package/dist/runtime/context/build.js +6 -6
  129. package/dist/runtime/context/compaction.js +1 -1
  130. package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
  131. package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
  132. package/dist/runtime/maintenance.d.ts +1 -1
  133. package/dist/sandbox/jobs.d.ts +13 -13
  134. package/dist/sandbox/jobs.js +20 -20
  135. package/dist/sandbox/leases.d.ts +9 -9
  136. package/dist/sandbox/leases.js +14 -14
  137. package/dist/sandbox/providers.d.ts +8 -8
  138. package/dist/sandbox/providers.js +8 -8
  139. package/dist/sandbox/sandbox.module.d.ts +1 -1
  140. package/dist/sandbox/sandbox.module.js +1 -1
  141. package/dist/sandbox/sandbox.service.d.ts +76 -76
  142. package/dist/sandbox/sandbox.service.js +230 -230
  143. package/dist/tools/builtins/builtin-tools.d.ts +2 -2
  144. package/dist/tools/builtins/builtin-tools.js +5 -5
  145. package/dist/tools/builtins/platform.js +1 -1
  146. package/dist/tools/builtins/sandbox/bash.js +6 -6
  147. package/dist/tools/builtins/sandbox/common.d.ts +25 -25
  148. package/dist/tools/builtins/sandbox/common.js +51 -51
  149. package/dist/tools/builtins/sandbox/edit.js +2 -2
  150. package/dist/tools/builtins/sandbox/index.d.ts +1 -1
  151. package/dist/tools/builtins/sandbox/index.js +1 -1
  152. package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
  153. package/dist/tools/builtins/sandbox/task-output.js +10 -10
  154. package/dist/tools/builtins/sandbox/task-stop.js +2 -2
  155. package/dist/tools/builtins/sandbox/write.js +2 -2
  156. package/dist/tools/builtins/skill-load.d.ts +1 -1
  157. package/dist/tools/builtins/skill-load.js +2 -2
  158. package/dist/tools/pipeline.js +4 -3
  159. package/dist/tools/registry.d.ts +2 -2
  160. package/dist/tools/tools.module.js +3 -3
  161. package/dist/ui/admin-ui.controller.d.ts +56 -0
  162. package/dist/ui/admin-ui.controller.js +192 -0
  163. package/dist/ui/arrangement.d.ts +74 -0
  164. package/dist/ui/arrangement.js +98 -0
  165. package/dist/ui/embed-token.d.ts +2 -2
  166. package/dist/ui/embed-token.js +30 -5
  167. package/dist/ui/registry.d.ts +29 -14
  168. package/dist/ui/registry.js +31 -10
  169. package/dist/ui/slots.controller.d.ts +11 -0
  170. package/dist/ui/slots.controller.js +25 -0
  171. package/dist/ui/ui-stream.d.ts +20 -3
  172. package/dist/ui/ui-stream.js +25 -7
  173. package/dist/ui/ui.schemas.d.ts +66 -0
  174. package/dist/ui/ui.schemas.js +65 -0
  175. package/dist/ui/views.controller.d.ts +35 -49
  176. package/dist/ui/views.controller.js +48 -70
  177. package/dist/ui/views.d.ts +84 -18
  178. package/dist/ui/views.js +201 -38
  179. package/package.json +3 -3
  180. package/src/migrations/0105_devices_rename.sql +136 -0
  181. package/dist/devices/device-auth.module.d.ts +0 -7
  182. package/dist/environments/conversation-environments.d.ts +0 -75
  183. package/dist/environments/dto.d.ts +0 -25
  184. package/dist/environments/dto.js +0 -12
  185. package/dist/environments/environments.controller.d.ts +0 -36
  186. package/dist/environments/environments.module.d.ts +0 -8
  187. package/dist/environments/environments.module.js +0 -31
  188. package/dist/environments/environments.schemas.d.ts +0 -3
  189. package/dist/environments/environments.schemas.js +0 -14
  190. package/dist/environments/label.d.ts +0 -11
  191. package/dist/platform/ops/environments.ops.d.ts +0 -26
  192. package/dist/platform/ops/environments.ops.js +0 -311
@@ -1,66 +1,25 @@
1
1
  import { z } from "zod";
2
- /** `POST /api/v1/device/authorize`: the machine as the program describes it. */
3
- export declare const DeviceAuthorizeBody: z.ZodObject<{
2
+ /** A device's name: trimmed, 1-64 characters (unique per person, case-insensitive). */
3
+ export declare const DeviceName: z.ZodString;
4
+ export declare const DeviceNameBody: z.ZodObject<{
4
5
  name: z.ZodString;
5
- os: z.ZodEnum<{
6
- darwin: "darwin";
7
- linux: "linux";
8
- }>;
9
- arch: z.ZodString;
10
- programVersion: z.ZodString;
11
6
  }, z.core.$strict>;
12
- /** `POST /api/v1/device/token`: the secret device code from `authorize`. */
13
- export declare const DeviceTokenBody: z.ZodObject<{
14
- deviceCode: z.ZodString;
15
- }, z.core.$strict>;
16
- export declare const DeviceUserCodeParams: z.ZodObject<{
17
- userCode: z.ZodString;
7
+ export declare const DeviceParams: z.ZodObject<{
8
+ id: z.ZodString;
18
9
  }, z.core.$strip>;
19
- export declare const DeviceUserCodeBody: z.ZodObject<{
20
- userCode: z.ZodString;
21
- }, z.core.$strict>;
22
- /**
23
- * `POST /api/v1/device/confirm`: the org and exactly one of an existing device environment of the
24
- * person's (`environmentId`) or the name of a new one (`name`).
25
- */
26
- export declare const DeviceConfirmBody: z.ZodObject<{
27
- userCode: z.ZodString;
28
- orgId: z.ZodString;
29
- environmentId: z.ZodOptional<z.ZodUUID>;
30
- name: z.ZodOptional<z.ZodString>;
31
- }, z.core.$strict>;
32
- declare const DeviceAuthorizeBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
10
+ declare const DeviceCreateBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
33
11
  name: z.ZodString;
34
- os: z.ZodEnum<{
35
- darwin: "darwin";
36
- linux: "linux";
37
- }>;
38
- arch: z.ZodString;
39
- programVersion: z.ZodString;
40
12
  }, z.core.$strict>>;
41
- export declare class DeviceAuthorizeBodyDto extends DeviceAuthorizeBodyDto_base {
13
+ export declare class DeviceCreateBodyDto extends DeviceCreateBodyDto_base {
42
14
  }
43
- declare const DeviceTokenBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
44
- deviceCode: z.ZodString;
15
+ declare const DeviceRenameBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
16
+ name: z.ZodString;
45
17
  }, z.core.$strict>>;
46
- export declare class DeviceTokenBodyDto extends DeviceTokenBodyDto_base {
18
+ export declare class DeviceRenameBodyDto extends DeviceRenameBodyDto_base {
47
19
  }
48
- declare const DeviceUserCodeParamsDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
49
- userCode: z.ZodString;
20
+ declare const DeviceParamsDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
21
+ id: z.ZodString;
50
22
  }, z.core.$strip>>;
51
- export declare class DeviceUserCodeParamsDto extends DeviceUserCodeParamsDto_base {
52
- }
53
- declare const DeviceUserCodeBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
54
- userCode: z.ZodString;
55
- }, z.core.$strict>>;
56
- export declare class DeviceUserCodeBodyDto extends DeviceUserCodeBodyDto_base {
57
- }
58
- declare const DeviceConfirmBodyDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
59
- userCode: z.ZodString;
60
- orgId: z.ZodString;
61
- environmentId: z.ZodOptional<z.ZodUUID>;
62
- name: z.ZodOptional<z.ZodString>;
63
- }, z.core.$strict>>;
64
- export declare class DeviceConfirmBodyDto extends DeviceConfirmBodyDto_base {
23
+ export declare class DeviceParamsDto extends DeviceParamsDto_base {
65
24
  }
66
25
  export {};
@@ -1,44 +1,12 @@
1
1
  import { z } from "zod";
2
- import { EnvironmentName } from "../environments/dto.js";
3
2
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
4
- /** `POST /api/v1/device/authorize`: the machine as the program describes it. */
5
- export const DeviceAuthorizeBody = z
6
- .object({
7
- name: z.string().trim().min(1).max(64),
8
- os: z.enum(["darwin", "linux"]),
9
- arch: z.string().trim().min(1).max(32),
10
- programVersion: z.string().trim().min(1).max(64),
11
- })
12
- .strict();
13
- /** `POST /api/v1/device/token`: the secret device code from `authorize`. */
14
- export const DeviceTokenBody = z.object({ deviceCode: z.string().min(1).max(200) }).strict();
15
- /** A user code as typed (`XXXX-XXXX`; case, spaces and the dash are forgiven). */
16
- const UserCode = z.string().min(1).max(32);
17
- export const DeviceUserCodeParams = z.object({ userCode: UserCode });
18
- export const DeviceUserCodeBody = z.object({ userCode: UserCode }).strict();
19
- /**
20
- * `POST /api/v1/device/confirm`: the org and exactly one of an existing device environment of the
21
- * person's (`environmentId`) or the name of a new one (`name`).
22
- */
23
- export const DeviceConfirmBody = z
24
- .object({
25
- userCode: UserCode,
26
- orgId: z.string().min(1).max(100),
27
- environmentId: z.uuid().optional(),
28
- name: EnvironmentName.optional(),
29
- })
30
- .strict()
31
- .refine((b) => (b.environmentId === undefined) !== (b.name === undefined), {
32
- message: "Give exactly one of environmentId and name",
33
- path: ["environmentId"],
34
- });
35
- export class DeviceAuthorizeBodyDto extends createZodDto(DeviceAuthorizeBody, "DeviceAuthorizeBody") {
3
+ /** A device's name: trimmed, 1-64 characters (unique per person, case-insensitive). */
4
+ export const DeviceName = z.string().trim().min(1).max(64);
5
+ export const DeviceNameBody = z.object({ name: DeviceName }).strict();
6
+ export const DeviceParams = z.object({ id: z.string().min(1).max(100) });
7
+ export class DeviceCreateBodyDto extends createZodDto(DeviceNameBody, "DeviceCreateBody") {
36
8
  }
37
- export class DeviceTokenBodyDto extends createZodDto(DeviceTokenBody, "DeviceTokenBody") {
9
+ export class DeviceRenameBodyDto extends createZodDto(DeviceNameBody, "DeviceRenameBody") {
38
10
  }
39
- export class DeviceUserCodeParamsDto extends createZodDto(DeviceUserCodeParams, "DeviceUserCodeParams") {
40
- }
41
- export class DeviceUserCodeBodyDto extends createZodDto(DeviceUserCodeBody, "DeviceUserCodeBody") {
42
- }
43
- export class DeviceConfirmBodyDto extends createZodDto(DeviceConfirmBody, "DeviceConfirmBody") {
11
+ export class DeviceParamsDto extends createZodDto(DeviceParams, "DeviceParams") {
44
12
  }
@@ -0,0 +1,11 @@
1
+ import type { DeviceView } from "./devices.service.js";
2
+ /**
3
+ * A device's name for people outside the web (the IM `/device` list, the `Platform` tool): its
4
+ * own name, else 「个人设备」 / "Personal" for the default, else for a default demoted by a provider
5
+ * switch the default's name plus the provider's display name (its plugin id when it has none),
6
+ * e.g. 「个人设备(Docker 沙箱)」 / "Personal (Docker sandbox)", with " 2", " 3", … after the
7
+ * first of the same provider. The web builds the same label from its own messages.
8
+ */
9
+ export declare function deviceLabel(v: Pick<DeviceView, "name" | "demotedFrom">, locale: "zh" | "en"): string;
10
+ /** The words the default device answers to in `/device <name>` besides its label. */
11
+ export declare const DEFAULT_DEVICE_ALIASES: readonly string[];
@@ -1,12 +1,12 @@
1
- const PERSONAL = { zh: "个人环境", en: "Personal" };
1
+ const PERSONAL = { zh: "个人设备", en: "Personal" };
2
2
  /**
3
- * An environment's name for people outside the web (the IM `/env` list, the `Platform` tool): its
4
- * own name, else 「个人环境」 / "Personal" for the default, else for a default demoted by a provider
3
+ * A device's name for people outside the web (the IM `/device` list, the `Platform` tool): its
4
+ * own name, else 「个人设备」 / "Personal" for the default, else for a default demoted by a provider
5
5
  * switch the default's name plus the provider's display name (its plugin id when it has none),
6
- * e.g. 「个人环境(Docker 沙箱)」 / "Personal (Docker sandbox)", with " 2", " 3", … after the
6
+ * e.g. 「个人设备(Docker 沙箱)」 / "Personal (Docker sandbox)", with " 2", " 3", … after the
7
7
  * first of the same provider. The web builds the same label from its own messages.
8
8
  */
9
- export function environmentLabel(v, locale) {
9
+ export function deviceLabel(v, locale) {
10
10
  if (v.name !== null)
11
11
  return v.name;
12
12
  const d = v.demotedFrom;
@@ -18,8 +18,8 @@ export function environmentLabel(v, locale) {
18
18
  ? `${PERSONAL.zh}(${provider})${nth}`
19
19
  : `${PERSONAL.en} (${provider})${nth}`;
20
20
  }
21
- /** The words the default environment answers to in `/env <name>` besides its label. */
22
- export const DEFAULT_ENVIRONMENT_ALIASES = [
21
+ /** The words the default device answers to in `/device <name>` besides its label. */
22
+ export const DEFAULT_DEVICE_ALIASES = [
23
23
  PERSONAL.zh.toLowerCase(),
24
24
  PERSONAL.en.toLowerCase(),
25
25
  ];
@@ -3,15 +3,15 @@ import { KernelDb } from "../infra/database/kernel-db.js";
3
3
  import { LockService } from "../infra/lock/lock.service.js";
4
4
  import type { KernelLogger } from "../log.js";
5
5
  import { SandboxProviders } from "../sandbox/providers.js";
6
- /** The job's name (`@LockedInterval`, run lock `cron:environments-legacy-cleanup`). */
7
- export declare const LEGACY_CLEANUP_JOB = "environments-legacy-cleanup";
6
+ /** The job's name (`@LockedInterval`, run lock `cron:devices-legacy-cleanup`). */
7
+ export declare const LEGACY_CLEANUP_JOB = "devices-legacy-cleanup";
8
8
  /** What one run did: rows destroyed and deleted, rows kept because their provider is not loaded. */
9
9
  export interface LegacyCleanupResult {
10
10
  destroyed: number;
11
11
  kept: number;
12
12
  }
13
13
  /**
14
- * The cleanup of the per-conversation sandboxes of before environments (spec
14
+ * The cleanup of the per-conversation sandboxes of before devices (spec
15
15
  * 2026-11-07-environments-design §4.2 item 9): for up to {@link BATCH} rows of the legacy
16
16
  * `sandboxes` table per tick, the row's provider destroys the instance and the file system named
17
17
  * `cap-<conversationId>` (`destroyByName`, `destroyFileSystemByName`; also the instance of the
@@ -14,8 +14,8 @@ import { LockService } from "../infra/lock/lock.service.js";
14
14
  import { LockedInterval } from "../infra/schedule/locked-interval.js";
15
15
  import { KERNEL_LOGGER } from "../infra/tokens.js";
16
16
  import { SandboxProviders } from "../sandbox/providers.js";
17
- /** The job's name (`@LockedInterval`, run lock `cron:environments-legacy-cleanup`). */
18
- export const LEGACY_CLEANUP_JOB = "environments-legacy-cleanup";
17
+ /** The job's name (`@LockedInterval`, run lock `cron:devices-legacy-cleanup`). */
18
+ export const LEGACY_CLEANUP_JOB = "devices-legacy-cleanup";
19
19
  /** Every 5 minutes, once per tick across replicas. */
20
20
  const PERIOD_MS = 5 * 60_000;
21
21
  /** Rows handled per tick. */
@@ -23,7 +23,7 @@ const BATCH = 50;
23
23
  /** The time limit of one provider call. */
24
24
  const CALL_TIMEOUT_MS = 120_000;
25
25
  /**
26
- * The cleanup of the per-conversation sandboxes of before environments (spec
26
+ * The cleanup of the per-conversation sandboxes of before devices (spec
27
27
  * 2026-11-07-environments-design §4.2 item 9): for up to {@link BATCH} rows of the legacy
28
28
  * `sandboxes` table per tick, the row's provider destroys the instance and the file system named
29
29
  * `cap-<conversationId>` (`destroyByName`, `destroyFileSystemByName`; also the instance of the
package/dist/errors.d.ts CHANGED
@@ -283,40 +283,40 @@ export declare const ERROR_CODES: {
283
283
  readonly "attachment.too_many_pending": 429;
284
284
  readonly "sandbox.unavailable": 503;
285
285
  readonly "sandbox.busy": 409;
286
- /** The environment is missing, deleted, or not the caller's (never 403, so ids cannot be probed). */
287
- readonly "environment.not_found": 404;
288
- /** The environment's file system is gone at the provider; reset its files to start empty. */
289
- readonly "environment.files_lost": 409;
290
- /** The caller may see the environment but not use it (project environments, later phases). */
291
- readonly "environment.forbidden": 403;
292
- /** The environment's provider is not the deployment's current one, or there is none. */
293
- readonly "environment.unavailable": 503;
294
- /** The person already has a live environment with this name (case-insensitive). */
295
- readonly "environment.name_taken": 409;
296
- /** The person already has `environments.maxPersonal` live personal environments. */
297
- readonly "environment.limit_reached": 409;
286
+ /** The device is missing, deleted, or not the caller's (never 403, so ids cannot be probed). */
287
+ readonly "device.not_found": 404;
288
+ /** The cloud device's file system is gone at the provider; reset its files to start empty. */
289
+ readonly "device.files_lost": 409;
290
+ /** The caller may see the device but not use it (shared devices, later phases). */
291
+ readonly "device.forbidden": 403;
292
+ /** The device's provider is not the deployment's current one, or there is none. */
293
+ readonly "device.unavailable": 503;
294
+ /** The person already has a live device with this name (case-insensitive). */
295
+ readonly "device.name_taken": 409;
296
+ /** The person already has `devices.maxPersonal` live personal devices. */
297
+ readonly "device.limit_reached": 409;
298
298
  /**
299
- * A connected environment's files are on its machine: they cannot be reset from the platform;
300
- * run `capybara-device logout`, then `capybara-device login` into the environment, on the machine
299
+ * A local device's files are on its machine: they cannot be reset from the platform; run
300
+ * `capybara-device logout`, then `capybara-device login` into the device, on the machine
301
301
  * (ruling R24).
302
302
  */
303
- readonly "environment.files_on_device": 409;
304
- /** The environment's connected machine is not connected now (no replica holds its lease). */
305
- readonly "environment.offline": 503;
306
- /** The connected machine runs a device program too old for this kernel; update the program. */
307
- readonly "environment.program_outdated": 426;
303
+ readonly "device.files_on_device": 409;
304
+ /** The local device's machine is not connected now (no replica holds its lease). */
305
+ readonly "device.offline": 503;
306
+ /** The local device's machine runs a `capybara-device` too old for this kernel; update it. */
307
+ readonly "device.program_outdated": 426;
308
308
  /** Device sign-in (RFC 8628 style): the person has not approved the code yet; keep polling. */
309
- readonly "device.authorization_pending": 400;
309
+ readonly "device_sign_in.authorization_pending": 400;
310
310
  /** Device sign-in: the program polls faster than the interval; add 5 seconds and keep polling. */
311
- readonly "device.slow_down": 400;
311
+ readonly "device_sign_in.slow_down": 400;
312
312
  /** Device sign-in: the code expired before it was approved; start over. */
313
- readonly "device.expired": 400;
313
+ readonly "device_sign_in.expired": 400;
314
314
  /** Device sign-in: the person denied the code. */
315
- readonly "device.denied": 400;
315
+ readonly "device_sign_in.denied": 400;
316
316
  /** Device sign-in: no pending code matches the one given. */
317
- readonly "device.code_invalid": 404;
317
+ readonly "device_sign_in.code_invalid": 404;
318
318
  /** Device sign-in: the confirmation could not hand the credential to the program; start over. */
319
- readonly "device.unavailable": 503;
319
+ readonly "device_sign_in.unavailable": 503;
320
320
  /** The machine credential is unknown or revoked. */
321
321
  readonly "device.credential_invalid": 401;
322
322
  readonly "skill.not_found": 404;
package/dist/errors.js CHANGED
@@ -283,40 +283,40 @@ export const ERROR_CODES = {
283
283
  "attachment.too_many_pending": 429,
284
284
  "sandbox.unavailable": 503,
285
285
  "sandbox.busy": 409,
286
- /** The environment is missing, deleted, or not the caller's (never 403, so ids cannot be probed). */
287
- "environment.not_found": 404,
288
- /** The environment's file system is gone at the provider; reset its files to start empty. */
289
- "environment.files_lost": 409,
290
- /** The caller may see the environment but not use it (project environments, later phases). */
291
- "environment.forbidden": 403,
292
- /** The environment's provider is not the deployment's current one, or there is none. */
293
- "environment.unavailable": 503,
294
- /** The person already has a live environment with this name (case-insensitive). */
295
- "environment.name_taken": 409,
296
- /** The person already has `environments.maxPersonal` live personal environments. */
297
- "environment.limit_reached": 409,
286
+ /** The device is missing, deleted, or not the caller's (never 403, so ids cannot be probed). */
287
+ "device.not_found": 404,
288
+ /** The cloud device's file system is gone at the provider; reset its files to start empty. */
289
+ "device.files_lost": 409,
290
+ /** The caller may see the device but not use it (shared devices, later phases). */
291
+ "device.forbidden": 403,
292
+ /** The device's provider is not the deployment's current one, or there is none. */
293
+ "device.unavailable": 503,
294
+ /** The person already has a live device with this name (case-insensitive). */
295
+ "device.name_taken": 409,
296
+ /** The person already has `devices.maxPersonal` live personal devices. */
297
+ "device.limit_reached": 409,
298
298
  /**
299
- * A connected environment's files are on its machine: they cannot be reset from the platform;
300
- * run `capybara-device logout`, then `capybara-device login` into the environment, on the machine
299
+ * A local device's files are on its machine: they cannot be reset from the platform; run
300
+ * `capybara-device logout`, then `capybara-device login` into the device, on the machine
301
301
  * (ruling R24).
302
302
  */
303
- "environment.files_on_device": 409,
304
- /** The environment's connected machine is not connected now (no replica holds its lease). */
305
- "environment.offline": 503,
306
- /** The connected machine runs a device program too old for this kernel; update the program. */
307
- "environment.program_outdated": 426,
303
+ "device.files_on_device": 409,
304
+ /** The local device's machine is not connected now (no replica holds its lease). */
305
+ "device.offline": 503,
306
+ /** The local device's machine runs a `capybara-device` too old for this kernel; update it. */
307
+ "device.program_outdated": 426,
308
308
  /** Device sign-in (RFC 8628 style): the person has not approved the code yet; keep polling. */
309
- "device.authorization_pending": 400,
309
+ "device_sign_in.authorization_pending": 400,
310
310
  /** Device sign-in: the program polls faster than the interval; add 5 seconds and keep polling. */
311
- "device.slow_down": 400,
311
+ "device_sign_in.slow_down": 400,
312
312
  /** Device sign-in: the code expired before it was approved; start over. */
313
- "device.expired": 400,
313
+ "device_sign_in.expired": 400,
314
314
  /** Device sign-in: the person denied the code. */
315
- "device.denied": 400,
315
+ "device_sign_in.denied": 400,
316
316
  /** Device sign-in: no pending code matches the one given. */
317
- "device.code_invalid": 404,
317
+ "device_sign_in.code_invalid": 404,
318
318
  /** Device sign-in: the confirmation could not hand the credential to the program; start over. */
319
- "device.unavailable": 503,
319
+ "device_sign_in.unavailable": 503,
320
320
  /** The machine credential is unknown or revoked. */
321
321
  "device.credential_invalid": 401,
322
322
  "skill.not_found": 404,
@@ -25,13 +25,13 @@ export interface RecordedCall {
25
25
  /** The recorded call used release-candidate names; `toolKey` and `inputKey` are already the current ones. */
26
26
  legacy?: true;
27
27
  /**
28
- * The roots of the latest `environment_paths` event the original run had logged by this call's
28
+ * The roots of the latest `device_paths` event the original run had logged by this call's
29
29
  * step (environments spec §13.1, ruling R22): set on every recorded call from the step where an
30
30
  * event was logged onward. A replay that matches the call writes the event when the eval
31
31
  * conversation's latest one differs, so a candidate whose first sandbox call lands in another
32
32
  * step still gets the line, once.
33
33
  */
34
- environmentPaths?: SandboxRoots;
34
+ devicePaths?: SandboxRoots;
35
35
  }
36
36
  /**
37
37
  * The connectors and extensions an rc `<x>__<tool>` key can name, from positive evidence in the
@@ -1,4 +1,4 @@
1
- import { environmentPathsChange } from "../runtime/context/environment-paths.js";
1
+ import { devicePathsChange } from "../runtime/context/device-paths.js";
2
2
  import { assistantIdemKey } from "../runtime/model-call.js";
3
3
  import { MATCH_IGNORED_FIELDS } from "../tools/builtin-names.js";
4
4
  import { isLegacyPrefixedKey, legacyToolInput, legacyToolKey, } from "../tools/legacy-keys.js";
@@ -138,7 +138,7 @@ export function recordedCalls(s) {
138
138
  started.set(p.callId, p.specHash);
139
139
  }
140
140
  }
141
- // an environment_paths event belongs to the step of the assistant message before it, and holds
141
+ // a device_paths event belongs to the step of the assistant message before it, and holds
142
142
  // for that step's calls and every later step's (until the next one)
143
143
  const pathsOf = new Map();
144
144
  const assistants = own.filter((e) => e.type === "assistant_message").map((e) => e.seq);
@@ -148,7 +148,7 @@ export function recordedCalls(s) {
148
148
  const end = assistants[i + 1] ?? Number.POSITIVE_INFINITY;
149
149
  for (; next < own.length && own[next].seq < end; next++) {
150
150
  const e = own[next];
151
- if (e.type === "environment_paths" && e.seq > a) {
151
+ if (e.type === "device_paths" && e.seq > a) {
152
152
  current = e.payload.roots;
153
153
  }
154
154
  }
@@ -179,7 +179,7 @@ export function recordedCalls(s) {
179
179
  ...(r.loadedTools !== undefined && { loadedTools: r.loadedTools }),
180
180
  ...(r.contentTypes !== undefined && { contentTypes: r.contentTypes }),
181
181
  specHash: started.get(call.id) ?? "",
182
- ...(paths !== undefined && { environmentPaths: paths }),
182
+ ...(paths !== undefined && { devicePaths: paths }),
183
183
  });
184
184
  }
185
185
  }
@@ -235,10 +235,10 @@ export class RecordedToolPipeline {
235
235
  this.misses++;
236
236
  // the original run had told the model the instance's directories by this call: so does the
237
237
  // replay, when the eval conversation's latest record differs (as the sandbox tools write it)
238
- const paths = rec?.environmentPaths;
238
+ const paths = rec?.devicePaths;
239
239
  const change = paths === undefined
240
240
  ? null
241
- : environmentPathsChange(await eventLog.latestOfType(ctx.conversationId, "environment_paths"), paths, ctx.runId);
241
+ : devicePathsChange(await eventLog.latestOfType(ctx.conversationId, "device_paths"), paths, ctx.runId);
242
242
  await eventLog.append(ctx.conversationId, [
243
243
  ...(change ? [change] : []),
244
244
  {
@@ -519,6 +519,7 @@ function fallbackView(name, known, err) {
519
519
  enabledOrgs: 0,
520
520
  builtin: false,
521
521
  installSource: null,
522
+ uiDropped: [],
522
523
  error: `the change was saved, but its current status cannot be read: ${err instanceof Error ? err.message : String(err)}`,
523
524
  };
524
525
  }
@@ -440,6 +440,7 @@ let ExtensionAdmin = class ExtensionAdmin {
440
440
  required: install?.required === true,
441
441
  ...scopeInfo,
442
442
  installSource: install?.source ?? null,
443
+ uiDropped: this.host.uiDropped(s.name),
443
444
  error: s.broken,
444
445
  };
445
446
  }
@@ -493,6 +494,7 @@ let ExtensionAdmin = class ExtensionAdmin {
493
494
  required: install?.required === true,
494
495
  ...scopeInfo,
495
496
  installSource: install?.source ?? null,
497
+ uiDropped: this.host.uiDropped(s.name),
496
498
  };
497
499
  });
498
500
  }
@@ -92,6 +92,12 @@ export declare class EffectiveConfigs {
92
92
  /** Secret values of a copy read here are masked in the logs. */
93
93
  log: KernelLogger);
94
94
  private get db();
95
+ /**
96
+ * The config of `plugin` for work done for the platform (a `platform.page` view, UI slots spec
97
+ * §7): the running instance's deployment config, no org copy applies; `notRunning` when the
98
+ * extension is not running on this replica.
99
+ */
100
+ platform(plugin: string): EffectiveConfig;
95
101
  /**
96
102
  * The effective config of `plugin` for `orgId`. Following: the running platform config, `ok:
97
103
  * false` when it leaves a required overridable field empty (the host loaded it with those
@@ -37,6 +37,11 @@ const OVERRIDE_KEY_PREFIX = "plugin-override:";
37
37
  * distinct from the org-only `plugin-config:<plugin>:<path>` (spec A §5.8).
38
38
  */
39
39
  export const overrideSecretKey = (plugin, path) => `${OVERRIDE_KEY_PREFIX}${plugin}:${path}`;
40
+ const notRunning = () => ({
41
+ ok: false,
42
+ notRunning: true,
43
+ issues: [{ path: "", segments: [], message: "the extension is not running" }],
44
+ });
40
45
  /** The secret leaf paths of a deployment schema. */
41
46
  export function secretLeafPaths(schema) {
42
47
  return configFields(schema)
@@ -171,6 +176,17 @@ let EffectiveConfigs = class EffectiveConfigs {
171
176
  get db() {
172
177
  return this.kdb.db;
173
178
  }
179
+ /**
180
+ * The config of `plugin` for work done for the platform (a `platform.page` view, UI slots spec
181
+ * §7): the running instance's deployment config, no org copy applies; `notRunning` when the
182
+ * extension is not running on this replica.
183
+ */
184
+ platform(plugin) {
185
+ const inst = this.registry.current(plugin);
186
+ if (!inst)
187
+ return notRunning();
188
+ return { ok: true, config: inst.config, source: "platform" };
189
+ }
174
190
  /**
175
191
  * The effective config of `plugin` for `orgId`. Following: the running platform config, `ok:
176
192
  * false` when it leaves a required overridable field empty (the host loaded it with those
@@ -180,13 +196,8 @@ let EffectiveConfigs = class EffectiveConfigs {
180
196
  */
181
197
  async get(orgId, plugin) {
182
198
  const inst = this.registry.current(plugin);
183
- if (!inst) {
184
- return {
185
- ok: false,
186
- notRunning: true,
187
- issues: [{ path: "", segments: [], message: "the extension is not running" }],
188
- };
189
- }
199
+ if (!inst)
200
+ return notRunning();
190
201
  const schema = inst.manifest.config?.deployment;
191
202
  if (!schema)
192
203
  return { ok: true, config: inst.config, source: "platform" };
@@ -1,4 +1,4 @@
1
- import { type ExtensionManifest, type ExtensionPointRef, type LocalizedName, type ToolHandler, type ToolSpec, type UiContribution } from "@capybara-hq/plugin-sdk";
1
+ import { type DroppedUiContribution, type ExtensionManifest, type ExtensionPointRef, type LocalizedName, type ToolHandler, type ToolSpec, type UiContribution, type UiSlotSpec } from "@capybara-hq/plugin-sdk";
2
2
  import type { Redis } from "ioredis";
3
3
  import type { core } from "zod";
4
4
  import type { KernelAppOptions } from "../app.js";
@@ -24,10 +24,17 @@ import { type PermissionSet } from "./permission-set.js";
24
24
  import { type ExtensionPointEntry, ExtensionRegistry } from "./registry.js";
25
25
  import { type ExtensionSource, ReplicaStatusStore } from "./sources.js";
26
26
  export { deploymentConfigHash };
27
- /** One item of {@link ExtensionHost.uiContributions}: a UI contribution from the manifest with its registered view / action (embeds have none). */
27
+ /**
28
+ * One item of {@link ExtensionHost.uiContributions}: a UI contribution from the manifest whose slot
29
+ * resolved at load (dropped ones never appear), with its registered view / action (embeds have none).
30
+ */
28
31
  export interface LoadedUiContribution {
29
32
  plugin: string;
30
33
  contribution: UiContribution;
34
+ /** The catalog slot the contribution resolved to (from `slot`, or the deprecated `place`). */
35
+ slot: UiSlotSpec;
36
+ /** For a slot with anchors: the anchor it goes after (see `resolveUiSlots`). */
37
+ after?: string;
31
38
  render?: UiRender;
32
39
  action?: UiActionHandler;
33
40
  }
@@ -494,6 +501,8 @@ export declare class ExtensionHost {
494
501
  * 配置声明的插件启动时不设时限(见 {@link load})。
495
502
  */
496
503
  private setup;
504
+ /** Logs once per load what slot resolution left out or flagged (UI slots spec §3.2). */
505
+ private logUiResolution;
497
506
  /** 取能力实现:单选能力无需 providerName,多选能力必须指定。 */
498
507
  get<T>(capability: ExtensionPointRef, providerName?: string): T;
499
508
  /**
@@ -607,6 +616,8 @@ export declare class ExtensionHost {
607
616
  * 是否在某组织启用由调用方另查(每次查询,不缓存)。
608
617
  */
609
618
  uiContributions(): LoadedUiContribution[];
619
+ /** The UI contributions the loaded extension left out at load (UI slots spec §3.2); `[]` when not loaded. */
620
+ uiDropped(name: string): DroppedUiContribution[];
610
621
  /** 串行执行生命周期操作(unload / reload 互不交错);宿主关闭后拒绝。 */
611
622
  private serial;
612
623
  /**
@@ -1712,7 +1712,7 @@ let ExtensionHost = class ExtensionHost {
1712
1712
  },
1713
1713
  });
1714
1714
  if (timeoutMs === undefined)
1715
- return p;
1715
+ return p.then((inst) => this.logUiResolution(inst));
1716
1716
  let timer;
1717
1717
  let onClose = () => { };
1718
1718
  const stop = new Promise((resolve) => {
@@ -1725,8 +1725,9 @@ let ExtensionHost = class ExtensionHost {
1725
1725
  });
1726
1726
  try {
1727
1727
  const r = await Promise.race([p.then((inst) => ({ inst })), stop]);
1728
+ // a late instance that is disposed right away never logs its UI resolution
1728
1729
  if (typeof r === "object")
1729
- return r.inst;
1730
+ return this.logUiResolution(r.inst);
1730
1731
  // setup 迟迟不返回:关闭它的 ctx(此后它的登记立即撤销),迟到完成的实例直接撤销
1731
1732
  p.then((inst) => this.disposeQuietly(inst, 0), () => { });
1732
1733
  if (this.settingUp === ctx)
@@ -1743,6 +1744,20 @@ let ExtensionHost = class ExtensionHost {
1743
1744
  this.abort.signal.removeEventListener("abort", onClose);
1744
1745
  }
1745
1746
  }
1747
+ /** Logs once per load what slot resolution left out or flagged (UI slots spec §3.2). */
1748
+ logUiResolution(inst) {
1749
+ const at = { plugin: inst.name, version: inst.version };
1750
+ if (inst.uiDropped.length > 0) {
1751
+ this.log.warn("plugin ui contributions dropped", { ...at, dropped: inst.uiDropped });
1752
+ }
1753
+ if (inst.uiUnknownAnchors.length > 0) {
1754
+ this.log.warn("plugin ui anchors unknown", { ...at, anchors: inst.uiUnknownAnchors });
1755
+ }
1756
+ if (inst.uiDeprecatedPlace.length > 0) {
1757
+ this.log.info("plugin ui uses the deprecated place", { ...at, ids: inst.uiDeprecatedPlace });
1758
+ }
1759
+ return inst;
1760
+ }
1746
1761
  /** 取能力实现:单选能力无需 providerName,多选能力必须指定。 */
1747
1762
  get(capability, providerName) {
1748
1763
  const providers = this.registry.all().filter((i) => i.provided.has(capability));
@@ -2087,17 +2102,23 @@ let ExtensionHost = class ExtensionHost {
2087
2102
  * 是否在某组织启用由调用方另查(每次查询,不缓存)。
2088
2103
  */
2089
2104
  uiContributions() {
2090
- return this.registry.all().flatMap((i) => (i.manifest.ui ?? []).map((c) => {
2105
+ return this.registry.all().flatMap((i) => i.ui.map(({ contribution: c, slot, after }) => {
2091
2106
  const render = c.kind === "view" ? i.uiViews.get(c.id) : undefined;
2092
2107
  const action = c.kind === "view" ? i.uiActions.get(c.id) : undefined;
2093
2108
  return {
2094
2109
  plugin: i.name,
2095
2110
  contribution: c,
2111
+ slot,
2112
+ ...(after !== undefined && { after }),
2096
2113
  ...(render !== undefined && { render }),
2097
2114
  ...(action !== undefined && { action }),
2098
2115
  };
2099
2116
  }));
2100
2117
  }
2118
+ /** The UI contributions the loaded extension left out at load (UI slots spec §3.2); `[]` when not loaded. */
2119
+ uiDropped(name) {
2120
+ return [...(this.registry.current(name)?.uiDropped ?? [])];
2121
+ }
2101
2122
  /** 串行执行生命周期操作(unload / reload 互不交错);宿主关闭后拒绝。 */
2102
2123
  serial(fn) {
2103
2124
  const guarded = () => {
@@ -1,4 +1,4 @@
1
- import { type ExtensionManifest, type ExtensionPointRef, type StaticExtensionManifest } from "@capybara-hq/plugin-sdk";
1
+ import { type DroppedUiContribution, type ExtensionManifest, type ExtensionPointRef, type ResolvedUiContribution, type StaticExtensionManifest } from "@capybara-hq/plugin-sdk";
2
2
  import { type ExtensionPointAccess, type KernelDeps, type KernelExtensionContext, LiveConfig, type RegisteredTool, type Route } from "../ctx/create-ctx.js";
3
3
  import type { UiActionHandler, UiRender } from "../ui/registry.js";
4
4
  import type { RegisteredHook } from "./hooks.js";
@@ -44,6 +44,17 @@ export declare class ExtensionInstance {
44
44
  readonly generation: number;
45
45
  readonly manifest: ExtensionManifest;
46
46
  readonly permissions: PermissionSet;
47
+ /** The manifest's UI contributions whose slot the catalog knows (UI slots spec §3.2), in manifest order. */
48
+ readonly ui: ResolvedUiContribution[];
49
+ /** The manifest's UI contributions left out at load (unknown slot or slot version). */
50
+ readonly uiDropped: DroppedUiContribution[];
51
+ /** `after` values that are not anchors of their slot (the contribution keeps the default position). */
52
+ readonly uiUnknownAnchors: {
53
+ id: string;
54
+ after: string;
55
+ }[];
56
+ /** Ids of contributions that still name the deprecated `place`. */
57
+ readonly uiDeprecatedPlace: string[];
47
58
  private hash;
48
59
  private readonly live;
49
60
  readonly entryUrl: string;