@capybara-hq/kernel 0.1.0-rc.21 → 0.1.0-rc.23
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.controller.d.ts +3 -3
- package/dist/admin/admin.controller.js +6 -2
- package/dist/admin/admin.schemas.d.ts +13 -0
- package/dist/agents/admin-agents.d.ts +16 -3
- package/dist/agents/admin-agents.js +65 -7
- package/dist/agents/agents.schemas.d.ts +24 -6
- package/dist/agents/agents.service.d.ts +4 -3
- package/dist/agents/agents.service.js +39 -26
- package/dist/agents/definition.d.ts +44 -6
- package/dist/agents/definition.js +86 -18
- package/dist/agents/from-template.d.ts +6 -3
- package/dist/agents/from-template.js +57 -44
- package/dist/agents/org-templates.js +97 -41
- package/dist/agents/templates.d.ts +6 -0
- package/dist/agents/templates.js +15 -1
- package/dist/background/launch.d.ts +43 -5
- package/dist/background/launch.js +88 -8
- package/dist/background/session.d.ts +14 -2
- package/dist/background/session.js +26 -4
- package/dist/background/status.d.ts +1 -1
- package/dist/background/status.js +3 -3
- package/dist/background/stop.d.ts +0 -2
- package/dist/background/stop.js +1 -21
- package/dist/catalog/admin-catalog.controller.d.ts +2 -4
- package/dist/catalog/admin-catalog.controller.js +2 -6
- package/dist/catalog/catalog-content.d.ts +5 -0
- package/dist/catalog/catalog-content.js +1 -0
- package/dist/catalog/catalog.module.js +3 -0
- package/dist/catalog/claude/report.d.ts +9 -2
- package/dist/catalog/claude/report.js +1 -0
- package/dist/catalog/claude/translate.js +1 -1
- package/dist/catalog/component-promotion.js +1 -2
- package/dist/catalog/component-state.d.ts +13 -1
- package/dist/catalog/component-state.js +14 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/marketplaces.service.js +2 -1
- package/dist/catalog/org-catalog.controller.d.ts +13 -3
- package/dist/catalog/org-catalog.controller.js +26 -4
- package/dist/catalog/org-catalog.schemas.d.ts +13 -0
- package/dist/catalog/org-catalog.schemas.js +34 -0
- package/dist/catalog/org-plugin-uploads.controller.js +0 -1
- package/dist/catalog/org-plugin-uploads.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.js +0 -3
- package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
- package/dist/catalog/platform-components.d.ts +0 -1
- package/dist/catalog/platform-components.js +3 -8
- package/dist/catalog/plugin-import.d.ts +0 -8
- package/dist/catalog/plugin-import.js +4 -35
- package/dist/catalog/plugin-install.d.ts +2 -2
- package/dist/catalog/plugin-install.js +2 -2
- package/dist/catalog/plugin-package.d.ts +7 -0
- package/dist/catalog/plugin-package.js +27 -1
- package/dist/catalog/plugin-upgrade.d.ts +5 -6
- package/dist/catalog/plugin-upgrade.js +5 -23
- package/dist/catalog/requests.service.d.ts +66 -10
- package/dist/catalog/requests.service.js +196 -15
- package/dist/channels/actions.d.ts +2 -0
- package/dist/channels/actions.js +19 -4
- package/dist/channels/cards.d.ts +16 -2
- package/dist/channels/cards.js +40 -7
- package/dist/channels/channels.module.js +3 -0
- package/dist/channels/outbound.d.ts +22 -1
- package/dist/channels/outbound.js +116 -7
- package/dist/channels/phrases.d.ts +25 -0
- package/dist/channels/phrases.js +19 -0
- package/dist/channels/threads.d.ts +12 -0
- package/dist/channels/threads.js +34 -0
- package/dist/config/schema.d.ts +23 -1
- package/dist/config/schema.js +107 -5
- package/dist/conversations/conversations.service.d.ts +42 -9
- package/dist/conversations/conversations.service.js +57 -25
- package/dist/conversations/events.d.ts +106 -11
- package/dist/conversations/events.js +121 -18
- package/dist/conversations/message-context.d.ts +6 -3
- package/dist/conversations/message-context.js +14 -10
- package/dist/conversations/queue.d.ts +15 -2
- package/dist/conversations/queue.js +22 -3
- package/dist/conversations/queue.service.d.ts +12 -6
- package/dist/conversations/queue.service.js +36 -13
- package/dist/db/kernel-schema.d.ts +105 -3
- package/dist/devices/conversation-devices.d.ts +8 -1
- package/dist/devices/conversation-devices.js +8 -0
- package/dist/devices/devices.controller.d.ts +21 -4
- package/dist/devices/devices.controller.js +46 -8
- package/dist/devices/devices.schemas.d.ts +9 -1
- package/dist/devices/devices.schemas.js +11 -3
- package/dist/devices/devices.service.d.ts +176 -4
- package/dist/devices/devices.service.js +347 -6
- package/dist/devices/dto.d.ts +56 -3
- package/dist/devices/dto.js +51 -1
- package/dist/devices/public-suffix.d.ts +6 -0
- package/dist/devices/public-suffix.js +79 -0
- package/dist/errors.d.ts +50 -5
- package/dist/errors.js +50 -5
- package/dist/evals/reconstruct.d.ts +18 -13
- package/dist/evals/reconstruct.js +50 -36
- package/dist/evals/recorded-tools.d.ts +7 -7
- package/dist/evals/recorded-tools.js +4 -3
- package/dist/evals/trajectory.d.ts +6 -1
- package/dist/evals/trajectory.js +7 -0
- package/dist/extensions/package.js +2 -2
- package/dist/http/api.module.js +5 -0
- package/dist/identity/settings.d.ts +53 -3
- package/dist/identity/settings.js +92 -9
- package/dist/inbox/inbox.module.js +2 -1
- package/dist/inbox/inbox.service.d.ts +19 -4
- package/dist/inbox/inbox.service.js +14 -3
- package/dist/infra/config/kernel-config.service.d.ts +7 -0
- package/dist/infra/config/kernel-config.service.js +31 -0
- package/dist/infra/events/kernel-events.d.ts +2 -1
- package/dist/local-devices/device-policy.service.d.ts +65 -0
- package/dist/local-devices/device-policy.service.js +217 -0
- package/dist/local-devices/local-device-gateway.d.ts +4 -2
- package/dist/local-devices/local-device-gateway.js +39 -11
- package/dist/local-devices/local-device-provider.d.ts +40 -3
- package/dist/local-devices/local-device-provider.js +109 -7
- package/dist/local-devices/local-devices.module.d.ts +2 -1
- package/dist/local-devices/local-devices.module.js +19 -3
- package/dist/local-devices/machines.d.ts +30 -3
- package/dist/local-devices/machines.js +79 -3
- package/dist/local-devices/network-grants.d.ts +104 -0
- package/dist/local-devices/network-grants.js +288 -0
- package/dist/local-devices/policy-summary.d.ts +20 -0
- package/dist/local-devices/policy-summary.js +63 -0
- package/dist/orgs/orgs.module.js +4 -10
- package/dist/orgs/roles.d.ts +1 -1
- package/dist/orgs/roles.js +4 -0
- package/dist/orgs/settings.controller.js +4 -3
- package/dist/orgs/settings.service.d.ts +172 -14
- package/dist/orgs/settings.service.js +229 -27
- package/dist/platform/exclusions.js +11 -1
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin-items.ops.js +1 -1
- package/dist/platform/ops/admin.ops.js +32 -7
- package/dist/platform/ops/agent-skills.ops.js +2 -2
- package/dist/platform/ops/agent-templates.ops.js +2 -2
- package/dist/platform/ops/agents.ops.js +11 -15
- package/dist/platform/ops/devices.ops.js +3 -1
- package/dist/platform/ops/inbox.ops.js +19 -0
- package/dist/platform/ops/org-settings.ops.js +15 -6
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/platform.ops.d.ts +9 -1
- package/dist/platform/ops/platform.ops.js +34 -12
- package/dist/platform/ops/requests.ops.js +3 -2
- package/dist/platform/ops/schedules.ops.d.ts +21 -0
- package/dist/platform/ops/schedules.ops.js +470 -0
- package/dist/platform/ops/skills.ops.js +3 -3
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +3 -0
- package/dist/runtime/context/build.js +13 -4
- package/dist/runtime/context/compaction.js +6 -3
- package/dist/runtime/context/device-paths.d.ts +43 -21
- package/dist/runtime/context/device-paths.js +104 -21
- package/dist/runtime/context/run-context.d.ts +9 -0
- package/dist/runtime/context/run-context.js +8 -0
- package/dist/runtime/context/skills.d.ts +14 -1
- package/dist/runtime/context/skills.js +36 -5
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +16 -5
- package/dist/runtime/run-finish.d.ts +6 -1
- package/dist/runtime/run-finish.js +16 -2
- package/dist/runtime/run-queue.js +21 -2
- package/dist/runtime/run-worker.js +14 -33
- package/dist/runtime/runtime.module.js +3 -0
- package/dist/runtime/subagents/definition.d.ts +32 -14
- package/dist/runtime/subagents/definition.js +106 -109
- package/dist/runtime/subagents/dispatch.js +4 -3
- package/dist/runtime/subagents/preamble.d.ts +2 -1
- package/dist/runtime/subagents/preamble.js +2 -1
- package/dist/runtime/waits.js +14 -4
- package/dist/sandbox/platform-tiers.d.ts +86 -0
- package/dist/sandbox/platform-tiers.js +97 -0
- package/dist/sandbox/sandbox.service.d.ts +121 -11
- package/dist/sandbox/sandbox.service.js +351 -30
- package/dist/sandbox/tiers.d.ts +98 -0
- package/dist/sandbox/tiers.js +104 -0
- package/dist/schedules/effective-settings.d.ts +16 -0
- package/dist/schedules/effective-settings.js +17 -0
- package/dist/schedules/errors.d.ts +8 -0
- package/dist/schedules/errors.js +8 -0
- package/dist/schedules/fire.d.ts +118 -0
- package/dist/schedules/fire.js +625 -0
- package/dist/schedules/grant-rules.d.ts +94 -0
- package/dist/schedules/grant-rules.js +126 -0
- package/dist/schedules/job-ids.d.ts +32 -0
- package/dist/schedules/job-ids.js +27 -0
- package/dist/schedules/pause.d.ts +9 -0
- package/dist/schedules/pause.js +33 -0
- package/dist/schedules/run-tracker.d.ts +81 -0
- package/dist/schedules/run-tracker.js +333 -0
- package/dist/schedules/scan.job.d.ts +64 -0
- package/dist/schedules/scan.job.js +303 -0
- package/dist/schedules/schedule-math.d.ts +31 -0
- package/dist/schedules/schedule-math.js +223 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
- package/dist/schedules/scheduled-tasks.service.js +1193 -0
- package/dist/schedules/schedules.controller.d.ts +68 -0
- package/dist/schedules/schedules.controller.js +242 -0
- package/dist/schedules/schedules.module.d.ts +16 -0
- package/dist/schedules/schedules.module.js +65 -0
- package/dist/schedules/schedules.schemas.d.ts +316 -0
- package/dist/schedules/schedules.schemas.js +186 -0
- package/dist/schedules/store.d.ts +81 -0
- package/dist/schedules/store.js +390 -0
- package/dist/schedules/store.module.d.ts +6 -0
- package/dist/schedules/store.module.js +21 -0
- package/dist/schedules/types.d.ts +108 -0
- package/dist/schedules/types.js +1 -0
- package/dist/schedules/unattended.d.ts +117 -0
- package/dist/schedules/unattended.js +228 -0
- package/dist/setup/oauth-token-probe.js +8 -4
- package/dist/skills/agent-skills.service.d.ts +4 -8
- package/dist/skills/agent-skills.service.js +18 -53
- package/dist/skills/catalog.d.ts +65 -62
- package/dist/skills/catalog.js +109 -206
- package/dist/skills/conversation-catalog.d.ts +3 -3
- package/dist/skills/conversation-catalog.js +5 -12
- package/dist/skills/invoke.d.ts +2 -2
- package/dist/skills/skills.controller.d.ts +3 -1
- package/dist/skills/skills.service.d.ts +38 -37
- package/dist/skills/skills.service.js +168 -107
- package/dist/skills/usable.d.ts +57 -8
- package/dist/skills/usable.js +172 -38
- package/dist/tools/approvals.controller.d.ts +4 -2
- package/dist/tools/approvals.controller.js +17 -2
- package/dist/tools/approvals.schemas.d.ts +1 -0
- package/dist/tools/approvals.schemas.js +3 -1
- package/dist/tools/approvals.service.d.ts +182 -7
- package/dist/tools/approvals.service.js +597 -13
- package/dist/tools/builtin-names.d.ts +25 -4
- package/dist/tools/builtin-names.js +45 -4
- package/dist/tools/builtins/builtin-tools.d.ts +8 -0
- package/dist/tools/builtins/builtin-tools.js +23 -2
- package/dist/tools/builtins/sandbox/bash.js +16 -7
- package/dist/tools/builtins/sandbox/common.d.ts +1 -1
- package/dist/tools/builtins/sandbox/common.js +33 -10
- package/dist/tools/builtins/sandbox/edit.js +7 -3
- package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
- package/dist/tools/builtins/sandbox/file-state.js +33 -3
- package/dist/tools/builtins/sandbox/write.js +8 -4
- package/dist/tools/builtins/schedules.d.ts +107 -0
- package/dist/tools/builtins/schedules.js +789 -0
- package/dist/tools/elicitation.d.ts +2 -0
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/fixed-ask.d.ts +7 -0
- package/dist/tools/fixed-ask.js +9 -0
- package/dist/tools/network-asks.d.ts +109 -0
- package/dist/tools/network-asks.js +221 -0
- package/dist/tools/pipeline.d.ts +53 -6
- package/dist/tools/pipeline.js +202 -13
- package/dist/tools/policy.d.ts +21 -8
- package/dist/tools/policy.js +63 -9
- package/dist/tools/registry.d.ts +10 -1
- package/dist/tools/tools.module.js +12 -0
- package/package.json +2 -2
- package/src/migrations/0113_scheduled_tasks.sql +108 -0
- package/src/migrations/0114_network_asks.sql +23 -0
- package/src/migrations/0115_network_requests.sql +28 -0
- package/src/migrations/0116_skills_default_on.sql +37 -0
- package/src/migrations/0117_sandbox_tiers.sql +11 -0
|
@@ -7,8 +7,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { validateAllowHost } from "@capybara-hq/plugin-sdk";
|
|
11
|
-
import { Inject, Injectable } from "@nestjs/common";
|
|
10
|
+
import { SANDBOX_TIERS, tierAtMost, validateAllowHost, } from "@capybara-hq/plugin-sdk";
|
|
11
|
+
import { Inject, Injectable, Module, Optional } from "@nestjs/common";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { ApiError } from "../errors.js";
|
|
14
14
|
import { audit } from "../identity/audit.js";
|
|
@@ -17,10 +17,13 @@ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
|
17
17
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
18
18
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
19
19
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
20
|
+
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
21
|
+
import { readPlatformTiers } from "../sandbox/platform-tiers.js";
|
|
22
|
+
import { effectiveSandboxPolicy } from "../sandbox/tiers.js";
|
|
20
23
|
import { assertThemeOnIn } from "../themes/theme-state.js";
|
|
21
24
|
import { IanaTimeZone } from "../time-zone.js";
|
|
22
25
|
import { mergeArrangement, OrgUiArrangementPatchSchema, UiArrangementSchema, } from "../ui/arrangement.js";
|
|
23
|
-
import { OrgLock } from "./org-lock.js";
|
|
26
|
+
import { OrgLock, OrgLockModule } from "./org-lock.js";
|
|
24
27
|
/**
|
|
25
28
|
* 组织设置(`capybara.orgs.settings` jsonb)。读取宽松:缺失或非法的字段按默认值,未知键保留不动
|
|
26
29
|
* (更新时与存量合并),新版本加字段不需要迁移。
|
|
@@ -30,7 +33,10 @@ import { OrgLock } from "./org-lock.js";
|
|
|
30
33
|
* `Read` / `Glob` / `Grep` 外都需要审批(执行时实时读取)。
|
|
31
34
|
* - `wakeOnBackgroundTask`:组织对 `runtime.wakeOnBackgroundTask` 的覆盖(缺省跟随部署),见 {@link effectiveWakeOnBackgroundTask}。
|
|
32
35
|
* - `sandboxEgress`:组织的沙箱出站(Ruling M19 / D6),缺省跟随部署的 `sandbox.limits.egress` + `allowHosts`。
|
|
33
|
-
*
|
|
36
|
+
* See `baseSandboxEgress` (sandbox/tiers.ts). 存量值非法时按 `none` 读(宁可断网,不悄悄放宽到部署默认)。
|
|
37
|
+
* - `sandboxTier`: the org's sandbox tier ceiling, default and unsandboxed switch (sandbox dev tiers
|
|
38
|
+
* spec §4.1), each absent = follow the platform (`sandbox.tiers`); never above the platform. An
|
|
39
|
+
* invalid stored value reads as `{ max: "strict" }` (narrower, never wider).
|
|
34
40
|
* - `platformTool`: the `Platform` builtin for the whole org (Platform tool spec §3): `on`,
|
|
35
41
|
* `read-only` (writes listed as unavailable) or `off` (default `on`).
|
|
36
42
|
* - `timeZone`: the org's default time zone (IANA; absent = UTC) for senders without one of their
|
|
@@ -65,6 +71,19 @@ export const SandboxEgressSettingSchema = z
|
|
|
65
71
|
.optional(),
|
|
66
72
|
})
|
|
67
73
|
.strict();
|
|
74
|
+
const TierSchema = z.enum(["strict", "dev", "open"]);
|
|
75
|
+
/**
|
|
76
|
+
* The org's sandbox tier setting (sandbox dev tiers spec §4.1): `max` the highest tier its devices
|
|
77
|
+
* may reach, `default` the tier of a device that chose none, `allowUnsandboxed` false forbids
|
|
78
|
+
* unsandboxed local devices. Each absent = follow the platform's `sandbox.tiers`.
|
|
79
|
+
*/
|
|
80
|
+
export const SandboxTierSettingSchema = z
|
|
81
|
+
.object({
|
|
82
|
+
max: TierSchema.optional(),
|
|
83
|
+
default: TierSchema.optional(),
|
|
84
|
+
allowUnsandboxed: z.boolean().optional(),
|
|
85
|
+
})
|
|
86
|
+
.strict();
|
|
68
87
|
/**
|
|
69
88
|
* The org's theme setting (themes spec §7): `id` a theme on in the org (a `platform_themes` id),
|
|
70
89
|
* null = follow the platform's default; `personal: "locked"` hides the personal picker in the org
|
|
@@ -78,12 +97,56 @@ export const OrgThemeSettingSchema = z
|
|
|
78
97
|
.strict();
|
|
79
98
|
/** The theme setting of an org that has none: follow the platform, personal picks allowed. */
|
|
80
99
|
export const DEFAULT_ORG_THEME = { id: null, personal: "allowed" };
|
|
100
|
+
const posInt = (max, def) => z.number().int().min(1).max(max).default(def).catch(def);
|
|
101
|
+
/** A stored `forbiddenTools` entry worth keeping: a tool key of the length the PATCH accepts. */
|
|
102
|
+
const isForbiddenToolEntry = (v) => typeof v === "string" && v.length >= 1 && v.length <= 200;
|
|
103
|
+
/**
|
|
104
|
+
* Org settings of scheduled tasks (spec §9); every field reads leniently (invalid = default,
|
|
105
|
+
* except `forbiddenTools`, which keeps its valid entries).
|
|
106
|
+
*/
|
|
107
|
+
export const ScheduledTasksSettingsSchema = z.object({
|
|
108
|
+
enabled: z.boolean().default(true).catch(true),
|
|
109
|
+
minIntervalMinutes: posInt(1_000_000, 60),
|
|
110
|
+
maxTasksPerMember: posInt(1_000_000, 20),
|
|
111
|
+
maxTasksPerOrg: posInt(1_000_000, 200),
|
|
112
|
+
maxRunsPerHour: posInt(1_000_000, 60),
|
|
113
|
+
defaultExpiryDays: z.number().int().min(1).max(36_500).nullable().default(null).catch(null),
|
|
114
|
+
catchUpWindowMinutes: posInt(1_000_000, 60),
|
|
115
|
+
unattendedAsk: z.enum(["fail", "wait"]).default("fail").catch("fail"),
|
|
116
|
+
pauseAfterFailures: posInt(1000, 5),
|
|
117
|
+
// fail-safe: an invalid entry is dropped, the valid ones stay forbidden (never reset to []);
|
|
118
|
+
// past the PATCH's 500 the list is truncated
|
|
119
|
+
forbiddenTools: z
|
|
120
|
+
.preprocess((v) => (Array.isArray(v) ? v.filter(isForbiddenToolEntry).slice(0, 500) : v), z.array(z.string()).max(500).default([]))
|
|
121
|
+
.catch([]),
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* The PATCH form: a partial merge into the group (fields left out keep their stored value; see
|
|
125
|
+
* `OrgSettingsService.update`). A field given replaces that field whole, `forbiddenTools` too.
|
|
126
|
+
*/
|
|
127
|
+
export const ScheduledTasksPatchSchema = z
|
|
128
|
+
.object({
|
|
129
|
+
enabled: z.boolean(),
|
|
130
|
+
minIntervalMinutes: z.number().int().min(1).max(1_000_000),
|
|
131
|
+
maxTasksPerMember: z.number().int().min(1).max(1_000_000),
|
|
132
|
+
maxTasksPerOrg: z.number().int().min(1).max(1_000_000),
|
|
133
|
+
maxRunsPerHour: z.number().int().min(1).max(1_000_000),
|
|
134
|
+
defaultExpiryDays: z.number().int().min(1).max(36_500).nullable(),
|
|
135
|
+
catchUpWindowMinutes: z.number().int().min(1).max(1_000_000),
|
|
136
|
+
unattendedAsk: z.enum(["fail", "wait"]),
|
|
137
|
+
pauseAfterFailures: z.number().int().min(1).max(1000),
|
|
138
|
+
forbiddenTools: z.array(z.string().min(1).max(200)).max(500),
|
|
139
|
+
})
|
|
140
|
+
.partial()
|
|
141
|
+
.strict();
|
|
81
142
|
export const OrgSettingsSchema = z.object({
|
|
82
143
|
toolSearch: z.enum(["on", "off"]).default("on").catch("on"),
|
|
83
144
|
locale: z.enum(["zh", "en"]).default("en").catch("en"),
|
|
84
145
|
sandboxToolPolicy: z.enum(["allow", "ask"]).default("allow").catch("allow"),
|
|
85
146
|
platformTool: z.enum(["on", "read-only", "off"]).default("on").catch("on"),
|
|
86
147
|
sandboxEgress: SandboxEgressSettingSchema.optional().catch({ mode: "none" }),
|
|
148
|
+
/** The org's sandbox tiers (sandbox dev tiers spec §4.1); absent follows the platform. */
|
|
149
|
+
sandboxTier: SandboxTierSettingSchema.optional().catch({ max: "strict" }),
|
|
87
150
|
/** Overrides `runtime.wakeOnBackgroundTask`; absent follows the deployment. */
|
|
88
151
|
wakeOnBackgroundTask: z.boolean().optional().catch(undefined),
|
|
89
152
|
/** The org's default time zone (run context spec §3.3); absent = UTC. */
|
|
@@ -92,6 +155,8 @@ export const OrgSettingsSchema = z.object({
|
|
|
92
155
|
theme: OrgThemeSettingSchema.optional().catch(undefined),
|
|
93
156
|
/** Hidden and ordered extension contributions per slot; absent = follow the platform. */
|
|
94
157
|
uiArrangement: UiArrangementSchema.optional().catch(undefined),
|
|
158
|
+
/** Scheduled tasks (spec §9); the platform bounds clamp it, see `effectiveScheduleSettings`. */
|
|
159
|
+
scheduledTasks: ScheduledTasksSettingsSchema.prefault({}).catch(ScheduledTasksSettingsSchema.parse({})),
|
|
95
160
|
});
|
|
96
161
|
/** PATCH 体:只改给出的字段;未知字段 → 400。 */
|
|
97
162
|
export const OrgSettingsPatchSchema = z
|
|
@@ -104,6 +169,11 @@ export const OrgSettingsPatchSchema = z
|
|
|
104
169
|
wakeOnBackgroundTask: z.boolean().nullable().optional(),
|
|
105
170
|
/** null 清除(恢复跟随部署默认)。 */
|
|
106
171
|
sandboxEgress: SandboxEgressSettingSchema.nullable().optional(),
|
|
172
|
+
/**
|
|
173
|
+
* The org's sandbox tiers, replaced whole (fields left out follow the platform); never above
|
|
174
|
+
* the platform (`org.sandbox_tier_not_allowed`). null clears it.
|
|
175
|
+
*/
|
|
176
|
+
sandboxTier: SandboxTierSettingSchema.nullable().optional(),
|
|
107
177
|
/** The org's default time zone (IANA); null clears it (UTC). */
|
|
108
178
|
timeZone: IanaTimeZone.nullable().optional(),
|
|
109
179
|
/**
|
|
@@ -116,19 +186,10 @@ export const OrgSettingsPatchSchema = z
|
|
|
116
186
|
* null removes a slot's entry (it follows the platform again). `platform.page` is refused.
|
|
117
187
|
*/
|
|
118
188
|
uiArrangement: OrgUiArrangementPatchSchema.optional(),
|
|
189
|
+
/** Fields to change in the scheduled-tasks group; the rest keep their value. */
|
|
190
|
+
scheduledTasks: ScheduledTasksPatchSchema.optional(),
|
|
119
191
|
})
|
|
120
192
|
.strict();
|
|
121
|
-
/**
|
|
122
|
-
* 组织实际生效的沙箱出站:没有组织设置 → 部署默认;`none` → 只有回环;`allowlist` → 组织的白名单
|
|
123
|
-
* (省略时用部署的),但部署是 `none` 时仍为 `none`(组织永远不比部署放得更开,部署改成 none 后存量设置也随之收紧)。
|
|
124
|
-
*/
|
|
125
|
-
export function effectiveSandboxEgress(deployment, org) {
|
|
126
|
-
if (org === undefined)
|
|
127
|
-
return { egress: deployment.egress, allowHosts: [...deployment.allowHosts] };
|
|
128
|
-
if (org.mode === "none" || deployment.egress === "none")
|
|
129
|
-
return { egress: "none", allowHosts: [] };
|
|
130
|
-
return { egress: "allowlist", allowHosts: [...(org.allowHosts ?? deployment.allowHosts)] };
|
|
131
|
-
}
|
|
132
193
|
/** Whether a finished background task wakes an idle conversation: the org's setting, else the deployment's. */
|
|
133
194
|
export function effectiveWakeOnBackgroundTask(deployment, org) {
|
|
134
195
|
return org ?? deployment;
|
|
@@ -162,25 +223,55 @@ export async function readOrgSettings(db, orgId) {
|
|
|
162
223
|
.executeTakeFirst();
|
|
163
224
|
return parseOrgSettings(row?.settings);
|
|
164
225
|
}
|
|
226
|
+
const sandboxTierNotAllowed = (message) => new ApiError("org.sandbox_tier_not_allowed", message);
|
|
227
|
+
/** Whether a stored settings object holds a `sandboxTier` this version reads as `{ max: "strict" }`. */
|
|
228
|
+
export function invalidStoredSandboxTier(raw) {
|
|
229
|
+
const v = asObject(raw).sandboxTier;
|
|
230
|
+
return v !== undefined && !SandboxTierSettingSchema.safeParse(v).success;
|
|
231
|
+
}
|
|
232
|
+
/** A setting's tier for intent comparison: absent (follow the platform) ranks above every tier. */
|
|
233
|
+
const intentRank = (t) => t === undefined ? SANDBOX_TIERS.length : SANDBOX_TIERS.indexOf(t);
|
|
165
234
|
/** 组织设置服务:读取与修改(修改需 `org.update`,锁内实时复核角色,写审计)。 */
|
|
166
235
|
let OrgSettingsService = class OrgSettingsService {
|
|
167
236
|
db;
|
|
168
237
|
txHost;
|
|
169
238
|
orgLock;
|
|
170
239
|
config;
|
|
240
|
+
log;
|
|
171
241
|
constructor(db, txHost, orgLock,
|
|
172
242
|
/** The deployment config (`sandboxEgress` may not exceed the deployment's). */
|
|
173
|
-
config) {
|
|
243
|
+
config, log) {
|
|
174
244
|
this.db = db;
|
|
175
245
|
this.txHost = txHost;
|
|
176
246
|
this.orgLock = orgLock;
|
|
177
247
|
this.config = config;
|
|
248
|
+
this.log = log;
|
|
249
|
+
}
|
|
250
|
+
/** Parses stored settings, warning when an invalid `sandboxTier` narrows to `{ max: "strict" }`. */
|
|
251
|
+
parseStored(orgId, raw) {
|
|
252
|
+
if (invalidStoredSandboxTier(raw)) {
|
|
253
|
+
this.log?.warn("org settings: stored sandboxTier is invalid; it reads as max strict", {
|
|
254
|
+
orgId,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return parseOrgSettings(raw);
|
|
258
|
+
}
|
|
259
|
+
/** The platform's effective sandbox tiers (the startup config over the admin console's choice). */
|
|
260
|
+
platformTiers() {
|
|
261
|
+
return readPlatformTiers(this.db.db, this.config, this.log);
|
|
178
262
|
}
|
|
179
263
|
/** 部署默认值(设置页展示「跟随部署」时的实际取值)。 */
|
|
180
|
-
deployment() {
|
|
264
|
+
async deployment() {
|
|
181
265
|
const lim = this.config.get().sandbox.limits;
|
|
266
|
+
const tiers = await this.platformTiers();
|
|
182
267
|
return {
|
|
183
268
|
sandboxEgress: { mode: lim.egress, allowHosts: [...lim.allowHosts] },
|
|
269
|
+
sandboxTiers: {
|
|
270
|
+
max: tiers.max,
|
|
271
|
+
default: tiers.default,
|
|
272
|
+
allowUnsandboxed: tiers.allowUnsandboxed,
|
|
273
|
+
devHosts: [...lim.devHosts],
|
|
274
|
+
},
|
|
184
275
|
};
|
|
185
276
|
}
|
|
186
277
|
/** 组织实际生效的沙箱 cpu / 内存(只读;由超级管理员在部署设置里覆盖)。 */
|
|
@@ -202,22 +293,99 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
202
293
|
.executeTakeFirst();
|
|
203
294
|
if (!row)
|
|
204
295
|
throw new ApiError("org.not_found", "Organization not found");
|
|
205
|
-
return
|
|
296
|
+
return this.parseStored(orgId, row.settings);
|
|
206
297
|
}
|
|
207
298
|
/** The refusals of {@link update} that need no read of the org (the Platform tool's precheck). */
|
|
208
|
-
check(patch) {
|
|
299
|
+
async check(patch) {
|
|
209
300
|
if (patch.sandboxEgress?.mode === "allowlist" &&
|
|
210
|
-
this.
|
|
301
|
+
this.config.get().sandbox.limits.egress === "none") {
|
|
211
302
|
throw new ApiError("org.sandbox_egress_not_allowed", "This deployment disables sandbox network access (sandbox.limits.egress is none); an organization can only set sandboxEgress to none");
|
|
212
303
|
}
|
|
304
|
+
const t = patch.sandboxTier;
|
|
305
|
+
if (t != null) {
|
|
306
|
+
const platform = await this.platformTiers();
|
|
307
|
+
if (t.max !== undefined && !tierAtMost(t.max, platform.max)) {
|
|
308
|
+
throw sandboxTierNotAllowed(`The platform allows sandbox tiers up to ${platform.max}; sandboxTier.max cannot be ${t.max}`);
|
|
309
|
+
}
|
|
310
|
+
const max = t.max ?? platform.max;
|
|
311
|
+
if (t.default !== undefined && !tierAtMost(t.default, max)) {
|
|
312
|
+
throw sandboxTierNotAllowed(`sandboxTier.default (${t.default}) cannot be above the organization's highest tier (${max})`);
|
|
313
|
+
}
|
|
314
|
+
if (t.allowUnsandboxed === true && !platform.allowUnsandboxed) {
|
|
315
|
+
throw sandboxTierNotAllowed("The platform does not allow unsandboxed devices (sandbox.tiers.allowUnsandboxed is false); sandboxTier.allowUnsandboxed cannot be true");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
213
318
|
}
|
|
214
319
|
async update(orgId, patch, actorAccountId, actorTokenId,
|
|
215
320
|
/** Merged into the audit detail (the Platform tool's `via: "agent"`). */
|
|
216
|
-
auditDetail
|
|
217
|
-
|
|
218
|
-
|
|
321
|
+
auditDetail,
|
|
322
|
+
/**
|
|
323
|
+
* `onlyNarrows` (the Platform tool, API tokens): refuse a `sandboxTier` or `sandboxEgress`
|
|
324
|
+
* change that widens (see {@link assertOnlyNarrows}); only a person in a session widens.
|
|
325
|
+
*/
|
|
326
|
+
opts) {
|
|
327
|
+
await this.check(patch);
|
|
328
|
+
return this.updateIn(orgId, patch, actorAccountId, actorTokenId, auditDetail, opts);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* The effective sandbox policy for the org's settings and a device's own tier (null = none);
|
|
332
|
+
* `tiers`: the platform's tiers when the caller already read them.
|
|
333
|
+
*/
|
|
334
|
+
async sandboxPolicy(settings, deviceTier, tiers) {
|
|
335
|
+
const deployment = {
|
|
336
|
+
limits: this.config.get().sandbox.limits,
|
|
337
|
+
tiers: tiers ?? (await this.platformTiers()),
|
|
338
|
+
};
|
|
339
|
+
return effectiveSandboxPolicy(deployment, settings, deviceTier);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Refuses a patch that widens the org's sandbox (sandbox dev tiers spec §2, §7.2: only a person
|
|
343
|
+
* widens it, never the Platform tool or an API token):
|
|
344
|
+
* - `sandboxTier`: the effective ceiling, default or unsandboxed switch rises, or a stored cap
|
|
345
|
+
* loosens (an absent `max` / `default` follows the platform, so it ranks above every tier and
|
|
346
|
+
* clearing a cap that equals today's platform value is a widening; an explicit
|
|
347
|
+
* `allowUnsandboxed: false` stays false). A cleared `default` under a new `max` no higher than
|
|
348
|
+
* the old default is no loosening: the default never exceeds the ceiling. The value is
|
|
349
|
+
* replaced whole, so the refusal names the fields the patch would clear;
|
|
350
|
+
* - `sandboxEgress`: the base list grows: from none to anything else, or to hosts (the
|
|
351
|
+
* deployment's when the org gives none) not all in the current list.
|
|
352
|
+
*/
|
|
353
|
+
async assertOnlyNarrows(before, patch) {
|
|
354
|
+
if (patch.sandboxTier !== undefined) {
|
|
355
|
+
const next = patch.sandboxTier ?? undefined;
|
|
356
|
+
const prev = before.sandboxTier;
|
|
357
|
+
const tiers = await this.platformTiers();
|
|
358
|
+
const b = await this.sandboxPolicy(before, null, tiers);
|
|
359
|
+
const a = await this.sandboxPolicy({ ...before, sandboxTier: next }, null, tiers);
|
|
360
|
+
// a cleared default follows the platform, capped by the ceiling: `max` bounds it then
|
|
361
|
+
const nextDefault = next?.default === undefined && next?.max !== undefined ? next.max : next?.default;
|
|
362
|
+
if (!tierAtMost(a.ceiling, b.ceiling) ||
|
|
363
|
+
!tierAtMost(a.defaultTier, b.defaultTier) ||
|
|
364
|
+
(a.allowUnsandboxed && !b.allowUnsandboxed) ||
|
|
365
|
+
intentRank(next?.max) > intentRank(prev?.max) ||
|
|
366
|
+
intentRank(nextDefault) > intentRank(prev?.default) ||
|
|
367
|
+
(prev?.allowUnsandboxed === false && next?.allowUnsandboxed !== false)) {
|
|
368
|
+
const cleared = ["max", "default", "allowUnsandboxed"].filter((f) => prev?.[f] !== undefined && next?.[f] === undefined);
|
|
369
|
+
throw sandboxTierNotAllowed("Raising the sandbox level needs a person in the organization settings." +
|
|
370
|
+
(cleared.length === 0
|
|
371
|
+
? ""
|
|
372
|
+
: ` sandboxTier is replaced whole: this change clears ${cleared.join(", ")}` +
|
|
373
|
+
" (which would then follow the platform); give the values to keep again."));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (patch.sandboxEgress !== undefined) {
|
|
377
|
+
const deploymentHosts = this.config.get().sandbox.limits.allowHosts;
|
|
378
|
+
const prev = before.sandboxEgress;
|
|
379
|
+
const next = patch.sandboxEgress ?? undefined;
|
|
380
|
+
const hostsOf = (e) => e?.allowHosts ?? deploymentHosts;
|
|
381
|
+
const widens = next?.mode !== "none" &&
|
|
382
|
+
(prev?.mode === "none" || !hostsOf(next).every((h) => hostsOf(prev).includes(h)));
|
|
383
|
+
if (widens) {
|
|
384
|
+
throw new ApiError("org.sandbox_egress_not_allowed", "Widening the sandbox network needs a person in the organization settings.");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
219
387
|
}
|
|
220
|
-
async updateIn(orgId, patch, actorAccountId, actorTokenId, auditDetail) {
|
|
388
|
+
async updateIn(orgId, patch, actorAccountId, actorTokenId, auditDetail, opts) {
|
|
221
389
|
await this.orgLock.lockAndAuthorize(orgId, actorAccountId, "org.update");
|
|
222
390
|
const row = await this.db
|
|
223
391
|
.selectFrom("orgs")
|
|
@@ -225,14 +393,26 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
225
393
|
.where("id", "=", orgId)
|
|
226
394
|
.executeTakeFirstOrThrow();
|
|
227
395
|
const stored = asObject(row.settings);
|
|
228
|
-
const before =
|
|
396
|
+
const before = this.parseStored(orgId, stored);
|
|
397
|
+
// again under the org lock: a hot config reload may have lowered the platform since
|
|
398
|
+
await this.check(patch);
|
|
399
|
+
// under the org lock, so the comparison is with the value the write replaces
|
|
400
|
+
if (opts?.onlyNarrows)
|
|
401
|
+
await this.assertOnlyNarrows(before, patch);
|
|
229
402
|
const changes = Object.fromEntries(Object.entries(patch)
|
|
230
403
|
.filter(([, v]) => v !== undefined)
|
|
231
404
|
// null clears sandboxEgress / wakeOnBackgroundTask: follow the deployment default again
|
|
232
405
|
.map(([k, v]) => [k, v === null ? undefined : v]));
|
|
233
406
|
const arrangementPatch = patch.uiArrangement;
|
|
234
407
|
delete changes.uiArrangement;
|
|
408
|
+
delete changes.scheduledTasks;
|
|
235
409
|
const after = { ...before, ...changes };
|
|
410
|
+
if (patch.scheduledTasks !== undefined) {
|
|
411
|
+
after.scheduledTasks = ScheduledTasksSettingsSchema.parse({
|
|
412
|
+
...before.scheduledTasks,
|
|
413
|
+
...Object.fromEntries(Object.entries(patch.scheduledTasks).filter(([, v]) => v !== undefined)),
|
|
414
|
+
});
|
|
415
|
+
}
|
|
236
416
|
if (arrangementPatch !== undefined) {
|
|
237
417
|
after.uiArrangement = mergeArrangement(before.uiArrangement, arrangementPatch);
|
|
238
418
|
if (Object.keys(after.uiArrangement).length === 0)
|
|
@@ -240,6 +420,12 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
240
420
|
}
|
|
241
421
|
if (after.sandboxEgress === undefined)
|
|
242
422
|
delete after.sandboxEgress;
|
|
423
|
+
// an empty sandboxTier is the same as none: follow the platform
|
|
424
|
+
if (after.sandboxTier !== undefined && Object.keys(after.sandboxTier).length === 0) {
|
|
425
|
+
delete after.sandboxTier;
|
|
426
|
+
}
|
|
427
|
+
if (after.sandboxTier === undefined)
|
|
428
|
+
delete after.sandboxTier;
|
|
243
429
|
if (after.wakeOnBackgroundTask === undefined)
|
|
244
430
|
delete after.wakeOnBackgroundTask;
|
|
245
431
|
if (after.timeZone === undefined)
|
|
@@ -256,7 +442,7 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
256
442
|
await assertThemeOnIn(this.db.db, orgId, patch.theme.id);
|
|
257
443
|
}
|
|
258
444
|
// 与存量合并:保留本版本不认识的键(清除的 sandboxEgress 不留在存量里)
|
|
259
|
-
const { sandboxEgress: _old, wakeOnBackgroundTask: _oldWake, timeZone: _oldZone, theme: _oldTheme, uiArrangement: _oldArrangement, ...rest } = stored;
|
|
445
|
+
const { sandboxEgress: _old, sandboxTier: _oldTier, wakeOnBackgroundTask: _oldWake, timeZone: _oldZone, theme: _oldTheme, uiArrangement: _oldArrangement, scheduledTasks: _oldScheduled, ...rest } = stored;
|
|
260
446
|
await this.db
|
|
261
447
|
.updateTable("orgs")
|
|
262
448
|
.set({ settings: JSON.stringify({ ...rest, ...after }) })
|
|
@@ -281,6 +467,22 @@ OrgSettingsService = __decorate([
|
|
|
281
467
|
__param(0, Inject(KernelDb)),
|
|
282
468
|
__param(1, Inject(TransactionHost)),
|
|
283
469
|
__param(2, Inject(OrgLock)),
|
|
284
|
-
__param(3, Inject(KernelConfigService))
|
|
470
|
+
__param(3, Inject(KernelConfigService)),
|
|
471
|
+
__param(4, Optional()),
|
|
472
|
+
__param(4, Inject(KERNEL_LOGGER))
|
|
285
473
|
], OrgSettingsService);
|
|
286
474
|
export { OrgSettingsService };
|
|
475
|
+
/**
|
|
476
|
+
* The one {@link OrgSettingsService}: the orgs module re-exports it, and the catalog imports it
|
|
477
|
+
* directly (approving a network request writes `sandboxEgress`) without a cycle through OrgsModule.
|
|
478
|
+
*/
|
|
479
|
+
let OrgSettingsModule = class OrgSettingsModule {
|
|
480
|
+
};
|
|
481
|
+
OrgSettingsModule = __decorate([
|
|
482
|
+
Module({
|
|
483
|
+
imports: [OrgLockModule],
|
|
484
|
+
providers: [OrgSettingsService],
|
|
485
|
+
exports: [OrgSettingsService],
|
|
486
|
+
})
|
|
487
|
+
], OrgSettingsModule);
|
|
488
|
+
export { OrgSettingsModule };
|
|
@@ -153,6 +153,11 @@ export const PLATFORM_EXCLUSIONS = [
|
|
|
153
153
|
category: "secret",
|
|
154
154
|
reason: "Body carries connector credentials and client secrets entered by a person",
|
|
155
155
|
},
|
|
156
|
+
{
|
|
157
|
+
route: "POST /api/v1/orgs/:orgId/catalog-requests/network",
|
|
158
|
+
category: "human-only",
|
|
159
|
+
reason: "A member asks org admins to allow a domain from a blocked command's card; agents never file network requests",
|
|
160
|
+
},
|
|
156
161
|
{
|
|
157
162
|
route: "POST /api/v1/orgs/:orgId/catalog-requests/:id/decide",
|
|
158
163
|
category: "human-only",
|
|
@@ -281,6 +286,11 @@ export const PLATFORM_EXCLUSIONS = [
|
|
|
281
286
|
category: "human-only",
|
|
282
287
|
reason: "Disconnects a person's own machine (revokes its credential); a trust decision taken on the device's page",
|
|
283
288
|
},
|
|
289
|
+
{
|
|
290
|
+
route: "POST /api/v1/orgs/:orgId/devices/:id/policy",
|
|
291
|
+
category: "human-only",
|
|
292
|
+
reason: "A person changes their own machine's network policy; agents never widen a device's network",
|
|
293
|
+
},
|
|
284
294
|
{
|
|
285
295
|
route: "POST /api/v1/device/authorize",
|
|
286
296
|
category: "transport",
|
|
@@ -560,7 +570,7 @@ export const PLATFORM_EXCLUSIONS = [
|
|
|
560
570
|
{
|
|
561
571
|
route: "POST /api/v1/orgs/:orgId/approvals/:id/decision",
|
|
562
572
|
category: "human-only",
|
|
563
|
-
reason: "Decides a tool permission approval; only the person decides (spec §6.4); reading what waits is approvals.waiting / approvals.get",
|
|
573
|
+
reason: "Decides a tool permission approval or a network ask; only the person decides (spec §6.4); a person must decide network access on their own device; reading what waits is approvals.waiting / approvals.get",
|
|
564
574
|
},
|
|
565
575
|
{
|
|
566
576
|
route: "GET /api/v1/orgs/:orgId/me/tool-grants",
|
|
@@ -87,6 +87,10 @@ export const PLATFORM_DOMAINS = [
|
|
|
87
87
|
id: "devices",
|
|
88
88
|
description: "Your own cloud devices (the workspaces your conversations run in): list, read, create, rename, delete, wipe the files.",
|
|
89
89
|
},
|
|
90
|
+
{
|
|
91
|
+
id: "schedules",
|
|
92
|
+
description: "Your scheduled tasks (an agent runs a prompt on a schedule, as you, unattended): list, read, create, edit, pause, resume, delete, run now or as a trial, run history, and the tools approved for a task.",
|
|
93
|
+
},
|
|
90
94
|
{
|
|
91
95
|
id: "setup",
|
|
92
96
|
description: "Plugin setup guides: list the guides you may help with, read one as Markdown, the state of its checks for a channel connection or a connector, and running one check. Entering secrets, connecting an account and confirming the app was updated stay with the person on the web; read the docs topic `setup` first.",
|
|
@@ -96,7 +96,7 @@ let AdminItemsOperations = class AdminItemsOperations {
|
|
|
96
96
|
}),
|
|
97
97
|
defineOperation({
|
|
98
98
|
id: "admin.agent-get",
|
|
99
|
-
description: "Read one agent of this org as the admin console shows it (super admin): its definition with the instructions in full, members, profile with quick action labels, versions count, the skills and connectors it
|
|
99
|
+
description: "Read one agent of this org as the admin console shows it (super admin): its definition with the instructions in full, members, profile with quick action labels, versions count, the skills and connectors it has by name, and how many of the org's skills it turns off (skillsOff). Never its conversations or credentials.",
|
|
100
100
|
input: AgentIdInput,
|
|
101
101
|
example: { agentId: "7c1a5b0e-9a53-4c6b-8d64-2f5a1b0c3e77" },
|
|
102
102
|
authz: { superAdmin: true },
|
|
@@ -43,6 +43,7 @@ const SettingsInput = DEPLOYMENT_SETTINGS_PATCH.pick({
|
|
|
43
43
|
uiTheme: true,
|
|
44
44
|
uiArrangement: true,
|
|
45
45
|
clientExtensions: true,
|
|
46
|
+
sandboxTiers: true,
|
|
46
47
|
}).strict();
|
|
47
48
|
const ProviderId = ModelProviderParams.shape.provider.describe("The model provider (models.platform-list), e.g. the plugin name of its extension.");
|
|
48
49
|
/** `provider/model` of a purpose target, or null. */
|
|
@@ -182,17 +183,17 @@ let AdminOperations = class AdminOperations {
|
|
|
182
183
|
}),
|
|
183
184
|
defineOperation({
|
|
184
185
|
id: "admin.settings",
|
|
185
|
-
description: "Read the deployment settings (super admin): who may create orgs (orgCreation), the price currency, whether orgs may create their own connectors and skills, whether org admins may browse the marketplaces,
|
|
186
|
+
description: "Read the deployment settings (super admin): who may create orgs (orgCreation), the price currency, whether orgs may create their own connectors and skills, whether org admins may browse the marketplaces, the per-org limit overrides keyed by org id (orgs.get reads one org's), and the platform's sandbox levels in effect (sandboxTiers: max, default, allowUnsandboxed, with sources: env, file or nacos are set in the startup config and read-only, db chosen in the admin console, default).",
|
|
186
187
|
input: z.object({}).strict(),
|
|
187
188
|
example: {},
|
|
188
189
|
authz: { superAdmin: true },
|
|
189
190
|
mirrors: ["GET /api/v1/admin/settings"],
|
|
190
191
|
confirm: "read",
|
|
191
|
-
run: () => this.settings.
|
|
192
|
+
run: () => this.settings.view(),
|
|
192
193
|
}),
|
|
193
194
|
defineOperation({
|
|
194
195
|
id: "admin.update-settings",
|
|
195
|
-
description: "Change deployment settings (super admin); give only the keys to change: orgCreation (anyone or super_admin), priceCurrency (ISO 4217 code), customConnectors / customSkills (allow or deny: whether orgs may create their own instead of installing from the catalog), orgMarketplaceBrowse (true or false), uiTheme ({ id }: the platform default theme, a platform theme whose scope is all, or null for the built-in theme; it is what every org falls back to and what the sign-in pages show), uiArrangement ({ <slot>: { hidden, order } } for a list slot, { <slot>: { use } } for a single slot: the platform default arrangement of UI slots, with contribution keys <extension>/<contribution>; hidden ones are not listed but still open by URL; use names the contribution that replaces the built-in, null for the built-in; null for a slot removes its entry), clientExtensions ({ enabled?, disabled? }: the kill switch of plugin client code; enabled false stops every plugin's interface code in every browser, disabled is the whole list of plugins whose interface code alone is stopped while their tools keep working; read the current list from admin.settings first). Per-org limits are orgs.set-limits.",
|
|
196
|
+
description: "Change deployment settings (super admin); give only the keys to change: orgCreation (anyone or super_admin), priceCurrency (ISO 4217 code), customConnectors / customSkills (allow or deny: whether orgs may create their own instead of installing from the catalog), orgMarketplaceBrowse (true or false), uiTheme ({ id }: the platform default theme, a platform theme whose scope is all, or null for the built-in theme; it is what every org falls back to and what the sign-in pages show), uiArrangement ({ <slot>: { hidden, order } } for a list slot, { <slot>: { use } } for a single slot: the platform default arrangement of UI slots, with contribution keys <extension>/<contribution>; hidden ones are not listed but still open by URL; use names the contribution that replaces the built-in, null for the built-in; null for a slot removes its entry), clientExtensions ({ enabled?, disabled? }: the kill switch of plugin client code; enabled false stops every plugin's interface code in every browser, disabled is the whole list of plugins whose interface code alone is stopped while their tools keep working; read the current list from admin.settings first), sandboxTiers ({ max?, default?, allowUnsandboxed? }: the platform's sandbox levels strict < dev < open; null clears a field back to its default; through this tool they may only be lowered, never raised, and fields set in the startup config are read-only). Per-org limits are orgs.set-limits.",
|
|
196
197
|
input: SettingsInput.refine((b) => Object.keys(b).length > 0, {
|
|
197
198
|
message: "at least one setting is required",
|
|
198
199
|
}),
|
|
@@ -207,6 +208,14 @@ let AdminOperations = class AdminOperations {
|
|
|
207
208
|
code: "validation_failed",
|
|
208
209
|
next: "Fix the values as the message says (admin.settings shows the current ones).",
|
|
209
210
|
},
|
|
211
|
+
{
|
|
212
|
+
code: "deployment.sandbox_tier_not_allowed",
|
|
213
|
+
next: "Through this tool the platform's sandbox levels may only be lowered; to raise them, ask a super admin to change them in the admin console settings on the web.",
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
code: "deployment.setting_readonly",
|
|
217
|
+
next: "That sandbox level is set in the deployment's startup config (environment, file or Nacos; admin.settings shows the source); it can only be changed there, by the operator.",
|
|
218
|
+
},
|
|
210
219
|
{
|
|
211
220
|
code: "theme.not_available",
|
|
212
221
|
next: "The platform default must be a platform theme whose scope is all, not disabled, and on in every organization. When the details list orgs, the theme is off in those organizations: tell the person their names, then choose another theme (plugins.components with kind theme lists them) or have it switched on in those organizations first; { id: null } sets the built-in theme.",
|
|
@@ -214,13 +223,27 @@ let AdminOperations = class AdminOperations {
|
|
|
214
223
|
],
|
|
215
224
|
precheck: async (i) => {
|
|
216
225
|
const r = DeploymentSettingsBody.safeParse(i);
|
|
217
|
-
|
|
226
|
+
if (!r.success) {
|
|
227
|
+
return {
|
|
228
|
+
ok: false,
|
|
229
|
+
code: "validation_failed",
|
|
230
|
+
message: r.error.issues[0]?.message ?? "",
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const tiers = i.sandboxTiers;
|
|
234
|
+
return tiers === undefined
|
|
218
235
|
? { ok: true }
|
|
219
|
-
:
|
|
236
|
+
: precheckBy(() => this.settings.checkSandboxTiers(tiers, { onlyNarrows: true }));
|
|
220
237
|
},
|
|
221
238
|
summary: async (i) => {
|
|
222
|
-
const { uiTheme, uiArrangement, clientExtensions, ...rest } = i;
|
|
239
|
+
const { uiTheme, uiArrangement, clientExtensions, sandboxTiers, ...rest } = i;
|
|
223
240
|
const parts = Object.entries(rest).map(([k, v]) => `${k}=${String(v)}`);
|
|
241
|
+
if (sandboxTiers !== undefined) {
|
|
242
|
+
parts.push(`sandboxTiers=${Object.entries(sandboxTiers)
|
|
243
|
+
.filter(([, v]) => v !== undefined)
|
|
244
|
+
.map(([k, v]) => `${k}:${String(v)}`)
|
|
245
|
+
.join(" ")}`);
|
|
246
|
+
}
|
|
224
247
|
const zh = [...parts];
|
|
225
248
|
const en = [...parts];
|
|
226
249
|
if (uiTheme !== undefined) {
|
|
@@ -253,7 +276,9 @@ let AdminOperations = class AdminOperations {
|
|
|
253
276
|
en: `Change the deployment settings: ${en.join(", ")}`,
|
|
254
277
|
};
|
|
255
278
|
},
|
|
256
|
-
run: (input, ctx) => this.settings.update(input, ctx.actorAccountId, undefined, viaAgent("admin.update-settings", ctx)
|
|
279
|
+
run: (input, ctx) => this.settings.update(input, ctx.actorAccountId, undefined, viaAgent("admin.update-settings", ctx),
|
|
280
|
+
// a person raises the platform's sandbox levels, never an agent (dev tiers spec §2)
|
|
281
|
+
{ onlyNarrows: true }),
|
|
257
282
|
}),
|
|
258
283
|
defineOperation({
|
|
259
284
|
id: "models.platform-list",
|
|
@@ -38,7 +38,7 @@ const SHARE_FILES_SHOWN = 10;
|
|
|
38
38
|
/** The model-facing result line of `agents.share-skill`. */
|
|
39
39
|
function sharedText(o, name) {
|
|
40
40
|
return o.kind === "shared"
|
|
41
|
-
? `Shared skill "${name}" with the organization: it is now an org skill (the person maintains it) that
|
|
41
|
+
? `Shared skill "${name}" with the organization: it is now an org skill (the person maintains it) that every agent of the org has unless it turns it off; this agent keeps using it.`
|
|
42
42
|
: `Asked the org admins to share skill "${name}" with the organization; it waits for their decision in the inbox. Until then it stays this agent's own skill.`;
|
|
43
43
|
}
|
|
44
44
|
/** The model-facing result line of `agents.register-skill`. */
|
|
@@ -367,7 +367,7 @@ let AgentSkillsOperations = class AgentSkillsOperations {
|
|
|
367
367
|
}),
|
|
368
368
|
defineOperation({
|
|
369
369
|
id: "agents.share-skill",
|
|
370
|
-
description: "Share one of an agent's own skills with the organization, so
|
|
370
|
+
description: "Share one of an agent's own skills with the organization, so every agent of the org has it unless it turns it off; the agent keeps using it and the person becomes its maintainer (later versions they register on their agents are proposed to the org admins). An org admin shares at once; anyone else's ask waits for an org admin's decision in the inbox. Only the agent's owner can; takes the agent id from agents.list and the skill id from agents.skills.",
|
|
371
371
|
input: z.object({ agentId: AgentId, skillId: AgentSkillPathParams.shape.skillId }).strict(),
|
|
372
372
|
example: {
|
|
373
373
|
agentId: "7c1a5b0e-9a53-4c6b-8d64-2f5a1b0c3e77",
|
|
@@ -50,7 +50,7 @@ let AgentTemplatesOperations = class AgentTemplatesOperations {
|
|
|
50
50
|
}),
|
|
51
51
|
defineOperation({
|
|
52
52
|
id: "agent-templates.get",
|
|
53
|
-
description: "Read one agent template with its definition (model tier, tools, skills, connectors it needs). Takes the template id from agent-templates.list.",
|
|
53
|
+
description: "Read one agent template with its definition (model tier, tools, the skills an agent made from it loads first, the skills it turns off (disallowedSkills), connectors it needs). Takes the template id from agent-templates.list.",
|
|
54
54
|
input: z.object({ templateId: AgentTemplateIdParams.shape.id }).strict(),
|
|
55
55
|
example: { templateId: "5d0c7a52-3a0e-4f0a-bd0a-0f8c3d2a9e10" },
|
|
56
56
|
authz: { orgAction: "agents.create" },
|
|
@@ -63,7 +63,7 @@ let AgentTemplatesOperations = class AgentTemplatesOperations {
|
|
|
63
63
|
}),
|
|
64
64
|
defineOperation({
|
|
65
65
|
id: "agent-templates.save",
|
|
66
|
-
description: "Save an agent of the org as an org agent template under a new name, so members can create their own copy from it. Credentials and per-person settings are not carried over; the result lists what was omitted.",
|
|
66
|
+
description: "Save an agent of the org as an org agent template under a new name, so members can create their own copy from it. The skills the agent turns off are saved as the template's disallowedSkills. Credentials and per-person settings are not carried over; the result lists what was omitted.",
|
|
67
67
|
input: SaveAsAgentIdParams.extend({
|
|
68
68
|
name: SaveAsTemplateBody.shape.name.min(1).max(80),
|
|
69
69
|
description: SaveAsTemplateBody.shape.description.max(1024),
|