@capybara-hq/kernel 0.1.0-rc.20 → 0.1.0-rc.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bootstrap.json +10 -1
- 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/catalog/admin-catalog.controller.d.ts +2 -4
- package/dist/catalog/admin-catalog.controller.js +2 -6
- 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 +105 -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 +341 -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 +46 -5
- package/dist/errors.js +46 -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/bootstrap.d.ts +49 -10
- package/dist/extensions/bootstrap.js +104 -27
- package/dist/extensions/package.js +2 -2
- package/dist/http/api.module.js +5 -0
- 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.js +11 -0
- package/dist/infra/events/kernel-events.d.ts +2 -1
- package/dist/lifecycle/kernel-lifecycle.d.ts +5 -1
- package/dist/lifecycle/kernel-lifecycle.js +16 -4
- 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.js +4 -1
- 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 +14 -3
- package/dist/local-devices/machines.js +41 -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 +2 -1
- package/dist/orgs/settings.service.d.ts +164 -12
- package/dist/orgs/settings.service.js +212 -24
- 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/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 +14 -5
- 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/sandbox.service.d.ts +120 -10
- package/dist/sandbox/sandbox.service.js +335 -28
- package/dist/sandbox/tiers.d.ts +86 -0
- package/dist/sandbox/tiers.js +96 -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,12 @@ 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 { effectiveSandboxPolicy } from "../sandbox/tiers.js";
|
|
20
22
|
import { assertThemeOnIn } from "../themes/theme-state.js";
|
|
21
23
|
import { IanaTimeZone } from "../time-zone.js";
|
|
22
24
|
import { mergeArrangement, OrgUiArrangementPatchSchema, UiArrangementSchema, } from "../ui/arrangement.js";
|
|
23
|
-
import { OrgLock } from "./org-lock.js";
|
|
25
|
+
import { OrgLock, OrgLockModule } from "./org-lock.js";
|
|
24
26
|
/**
|
|
25
27
|
* 组织设置(`capybara.orgs.settings` jsonb)。读取宽松:缺失或非法的字段按默认值,未知键保留不动
|
|
26
28
|
* (更新时与存量合并),新版本加字段不需要迁移。
|
|
@@ -30,7 +32,10 @@ import { OrgLock } from "./org-lock.js";
|
|
|
30
32
|
* `Read` / `Glob` / `Grep` 外都需要审批(执行时实时读取)。
|
|
31
33
|
* - `wakeOnBackgroundTask`:组织对 `runtime.wakeOnBackgroundTask` 的覆盖(缺省跟随部署),见 {@link effectiveWakeOnBackgroundTask}。
|
|
32
34
|
* - `sandboxEgress`:组织的沙箱出站(Ruling M19 / D6),缺省跟随部署的 `sandbox.limits.egress` + `allowHosts`。
|
|
33
|
-
*
|
|
35
|
+
* See `baseSandboxEgress` (sandbox/tiers.ts). 存量值非法时按 `none` 读(宁可断网,不悄悄放宽到部署默认)。
|
|
36
|
+
* - `sandboxTier`: the org's sandbox tier ceiling, default and unsandboxed switch (sandbox dev tiers
|
|
37
|
+
* spec §4.1), each absent = follow the platform (`sandbox.tiers`); never above the platform. An
|
|
38
|
+
* invalid stored value reads as `{ max: "strict" }` (narrower, never wider).
|
|
34
39
|
* - `platformTool`: the `Platform` builtin for the whole org (Platform tool spec §3): `on`,
|
|
35
40
|
* `read-only` (writes listed as unavailable) or `off` (default `on`).
|
|
36
41
|
* - `timeZone`: the org's default time zone (IANA; absent = UTC) for senders without one of their
|
|
@@ -65,6 +70,19 @@ export const SandboxEgressSettingSchema = z
|
|
|
65
70
|
.optional(),
|
|
66
71
|
})
|
|
67
72
|
.strict();
|
|
73
|
+
const TierSchema = z.enum(["strict", "dev", "open"]);
|
|
74
|
+
/**
|
|
75
|
+
* The org's sandbox tier setting (sandbox dev tiers spec §4.1): `max` the highest tier its devices
|
|
76
|
+
* may reach, `default` the tier of a device that chose none, `allowUnsandboxed` false forbids
|
|
77
|
+
* unsandboxed local devices. Each absent = follow the platform's `sandbox.tiers`.
|
|
78
|
+
*/
|
|
79
|
+
export const SandboxTierSettingSchema = z
|
|
80
|
+
.object({
|
|
81
|
+
max: TierSchema.optional(),
|
|
82
|
+
default: TierSchema.optional(),
|
|
83
|
+
allowUnsandboxed: z.boolean().optional(),
|
|
84
|
+
})
|
|
85
|
+
.strict();
|
|
68
86
|
/**
|
|
69
87
|
* The org's theme setting (themes spec §7): `id` a theme on in the org (a `platform_themes` id),
|
|
70
88
|
* null = follow the platform's default; `personal: "locked"` hides the personal picker in the org
|
|
@@ -78,12 +96,56 @@ export const OrgThemeSettingSchema = z
|
|
|
78
96
|
.strict();
|
|
79
97
|
/** The theme setting of an org that has none: follow the platform, personal picks allowed. */
|
|
80
98
|
export const DEFAULT_ORG_THEME = { id: null, personal: "allowed" };
|
|
99
|
+
const posInt = (max, def) => z.number().int().min(1).max(max).default(def).catch(def);
|
|
100
|
+
/** A stored `forbiddenTools` entry worth keeping: a tool key of the length the PATCH accepts. */
|
|
101
|
+
const isForbiddenToolEntry = (v) => typeof v === "string" && v.length >= 1 && v.length <= 200;
|
|
102
|
+
/**
|
|
103
|
+
* Org settings of scheduled tasks (spec §9); every field reads leniently (invalid = default,
|
|
104
|
+
* except `forbiddenTools`, which keeps its valid entries).
|
|
105
|
+
*/
|
|
106
|
+
export const ScheduledTasksSettingsSchema = z.object({
|
|
107
|
+
enabled: z.boolean().default(true).catch(true),
|
|
108
|
+
minIntervalMinutes: posInt(1_000_000, 60),
|
|
109
|
+
maxTasksPerMember: posInt(1_000_000, 20),
|
|
110
|
+
maxTasksPerOrg: posInt(1_000_000, 200),
|
|
111
|
+
maxRunsPerHour: posInt(1_000_000, 60),
|
|
112
|
+
defaultExpiryDays: z.number().int().min(1).max(36_500).nullable().default(null).catch(null),
|
|
113
|
+
catchUpWindowMinutes: posInt(1_000_000, 60),
|
|
114
|
+
unattendedAsk: z.enum(["fail", "wait"]).default("fail").catch("fail"),
|
|
115
|
+
pauseAfterFailures: posInt(1000, 5),
|
|
116
|
+
// fail-safe: an invalid entry is dropped, the valid ones stay forbidden (never reset to []);
|
|
117
|
+
// past the PATCH's 500 the list is truncated
|
|
118
|
+
forbiddenTools: z
|
|
119
|
+
.preprocess((v) => (Array.isArray(v) ? v.filter(isForbiddenToolEntry).slice(0, 500) : v), z.array(z.string()).max(500).default([]))
|
|
120
|
+
.catch([]),
|
|
121
|
+
});
|
|
122
|
+
/**
|
|
123
|
+
* The PATCH form: a partial merge into the group (fields left out keep their stored value; see
|
|
124
|
+
* `OrgSettingsService.update`). A field given replaces that field whole, `forbiddenTools` too.
|
|
125
|
+
*/
|
|
126
|
+
export const ScheduledTasksPatchSchema = z
|
|
127
|
+
.object({
|
|
128
|
+
enabled: z.boolean(),
|
|
129
|
+
minIntervalMinutes: z.number().int().min(1).max(1_000_000),
|
|
130
|
+
maxTasksPerMember: z.number().int().min(1).max(1_000_000),
|
|
131
|
+
maxTasksPerOrg: z.number().int().min(1).max(1_000_000),
|
|
132
|
+
maxRunsPerHour: z.number().int().min(1).max(1_000_000),
|
|
133
|
+
defaultExpiryDays: z.number().int().min(1).max(36_500).nullable(),
|
|
134
|
+
catchUpWindowMinutes: z.number().int().min(1).max(1_000_000),
|
|
135
|
+
unattendedAsk: z.enum(["fail", "wait"]),
|
|
136
|
+
pauseAfterFailures: z.number().int().min(1).max(1000),
|
|
137
|
+
forbiddenTools: z.array(z.string().min(1).max(200)).max(500),
|
|
138
|
+
})
|
|
139
|
+
.partial()
|
|
140
|
+
.strict();
|
|
81
141
|
export const OrgSettingsSchema = z.object({
|
|
82
142
|
toolSearch: z.enum(["on", "off"]).default("on").catch("on"),
|
|
83
143
|
locale: z.enum(["zh", "en"]).default("en").catch("en"),
|
|
84
144
|
sandboxToolPolicy: z.enum(["allow", "ask"]).default("allow").catch("allow"),
|
|
85
145
|
platformTool: z.enum(["on", "read-only", "off"]).default("on").catch("on"),
|
|
86
146
|
sandboxEgress: SandboxEgressSettingSchema.optional().catch({ mode: "none" }),
|
|
147
|
+
/** The org's sandbox tiers (sandbox dev tiers spec §4.1); absent follows the platform. */
|
|
148
|
+
sandboxTier: SandboxTierSettingSchema.optional().catch({ max: "strict" }),
|
|
87
149
|
/** Overrides `runtime.wakeOnBackgroundTask`; absent follows the deployment. */
|
|
88
150
|
wakeOnBackgroundTask: z.boolean().optional().catch(undefined),
|
|
89
151
|
/** The org's default time zone (run context spec §3.3); absent = UTC. */
|
|
@@ -92,6 +154,8 @@ export const OrgSettingsSchema = z.object({
|
|
|
92
154
|
theme: OrgThemeSettingSchema.optional().catch(undefined),
|
|
93
155
|
/** Hidden and ordered extension contributions per slot; absent = follow the platform. */
|
|
94
156
|
uiArrangement: UiArrangementSchema.optional().catch(undefined),
|
|
157
|
+
/** Scheduled tasks (spec §9); the platform bounds clamp it, see `effectiveScheduleSettings`. */
|
|
158
|
+
scheduledTasks: ScheduledTasksSettingsSchema.prefault({}).catch(ScheduledTasksSettingsSchema.parse({})),
|
|
95
159
|
});
|
|
96
160
|
/** PATCH 体:只改给出的字段;未知字段 → 400。 */
|
|
97
161
|
export const OrgSettingsPatchSchema = z
|
|
@@ -104,6 +168,11 @@ export const OrgSettingsPatchSchema = z
|
|
|
104
168
|
wakeOnBackgroundTask: z.boolean().nullable().optional(),
|
|
105
169
|
/** null 清除(恢复跟随部署默认)。 */
|
|
106
170
|
sandboxEgress: SandboxEgressSettingSchema.nullable().optional(),
|
|
171
|
+
/**
|
|
172
|
+
* The org's sandbox tiers, replaced whole (fields left out follow the platform); never above
|
|
173
|
+
* the platform (`org.sandbox_tier_not_allowed`). null clears it.
|
|
174
|
+
*/
|
|
175
|
+
sandboxTier: SandboxTierSettingSchema.nullable().optional(),
|
|
107
176
|
/** The org's default time zone (IANA); null clears it (UTC). */
|
|
108
177
|
timeZone: IanaTimeZone.nullable().optional(),
|
|
109
178
|
/**
|
|
@@ -116,19 +185,10 @@ export const OrgSettingsPatchSchema = z
|
|
|
116
185
|
* null removes a slot's entry (it follows the platform again). `platform.page` is refused.
|
|
117
186
|
*/
|
|
118
187
|
uiArrangement: OrgUiArrangementPatchSchema.optional(),
|
|
188
|
+
/** Fields to change in the scheduled-tasks group; the rest keep their value. */
|
|
189
|
+
scheduledTasks: ScheduledTasksPatchSchema.optional(),
|
|
119
190
|
})
|
|
120
191
|
.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
192
|
/** Whether a finished background task wakes an idle conversation: the org's setting, else the deployment's. */
|
|
133
193
|
export function effectiveWakeOnBackgroundTask(deployment, org) {
|
|
134
194
|
return org ?? deployment;
|
|
@@ -162,25 +222,50 @@ export async function readOrgSettings(db, orgId) {
|
|
|
162
222
|
.executeTakeFirst();
|
|
163
223
|
return parseOrgSettings(row?.settings);
|
|
164
224
|
}
|
|
225
|
+
const sandboxTierNotAllowed = (message) => new ApiError("org.sandbox_tier_not_allowed", message);
|
|
226
|
+
/** Whether a stored settings object holds a `sandboxTier` this version reads as `{ max: "strict" }`. */
|
|
227
|
+
export function invalidStoredSandboxTier(raw) {
|
|
228
|
+
const v = asObject(raw).sandboxTier;
|
|
229
|
+
return v !== undefined && !SandboxTierSettingSchema.safeParse(v).success;
|
|
230
|
+
}
|
|
231
|
+
/** A setting's tier for intent comparison: absent (follow the platform) ranks above every tier. */
|
|
232
|
+
const intentRank = (t) => t === undefined ? SANDBOX_TIERS.length : SANDBOX_TIERS.indexOf(t);
|
|
165
233
|
/** 组织设置服务:读取与修改(修改需 `org.update`,锁内实时复核角色,写审计)。 */
|
|
166
234
|
let OrgSettingsService = class OrgSettingsService {
|
|
167
235
|
db;
|
|
168
236
|
txHost;
|
|
169
237
|
orgLock;
|
|
170
238
|
config;
|
|
239
|
+
log;
|
|
171
240
|
constructor(db, txHost, orgLock,
|
|
172
241
|
/** The deployment config (`sandboxEgress` may not exceed the deployment's). */
|
|
173
|
-
config) {
|
|
242
|
+
config, log) {
|
|
174
243
|
this.db = db;
|
|
175
244
|
this.txHost = txHost;
|
|
176
245
|
this.orgLock = orgLock;
|
|
177
246
|
this.config = config;
|
|
247
|
+
this.log = log;
|
|
248
|
+
}
|
|
249
|
+
/** Parses stored settings, warning when an invalid `sandboxTier` narrows to `{ max: "strict" }`. */
|
|
250
|
+
parseStored(orgId, raw) {
|
|
251
|
+
if (invalidStoredSandboxTier(raw)) {
|
|
252
|
+
this.log?.warn("org settings: stored sandboxTier is invalid; it reads as max strict", {
|
|
253
|
+
orgId,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return parseOrgSettings(raw);
|
|
178
257
|
}
|
|
179
258
|
/** 部署默认值(设置页展示「跟随部署」时的实际取值)。 */
|
|
180
259
|
deployment() {
|
|
181
|
-
const lim = this.config.get().sandbox
|
|
260
|
+
const { limits: lim, tiers } = this.config.get().sandbox;
|
|
182
261
|
return {
|
|
183
262
|
sandboxEgress: { mode: lim.egress, allowHosts: [...lim.allowHosts] },
|
|
263
|
+
sandboxTiers: {
|
|
264
|
+
max: tiers.max,
|
|
265
|
+
default: tiers.default,
|
|
266
|
+
allowUnsandboxed: tiers.allowUnsandboxed,
|
|
267
|
+
devHosts: [...lim.devHosts],
|
|
268
|
+
},
|
|
184
269
|
};
|
|
185
270
|
}
|
|
186
271
|
/** 组织实际生效的沙箱 cpu / 内存(只读;由超级管理员在部署设置里覆盖)。 */
|
|
@@ -202,7 +287,7 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
202
287
|
.executeTakeFirst();
|
|
203
288
|
if (!row)
|
|
204
289
|
throw new ApiError("org.not_found", "Organization not found");
|
|
205
|
-
return
|
|
290
|
+
return this.parseStored(orgId, row.settings);
|
|
206
291
|
}
|
|
207
292
|
/** The refusals of {@link update} that need no read of the org (the Platform tool's precheck). */
|
|
208
293
|
check(patch) {
|
|
@@ -210,14 +295,83 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
210
295
|
this.deployment().sandboxEgress.mode === "none") {
|
|
211
296
|
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
297
|
}
|
|
298
|
+
const t = patch.sandboxTier;
|
|
299
|
+
if (t != null) {
|
|
300
|
+
const platform = this.config.get().sandbox.tiers;
|
|
301
|
+
if (t.max !== undefined && !tierAtMost(t.max, platform.max)) {
|
|
302
|
+
throw sandboxTierNotAllowed(`The platform allows sandbox tiers up to ${platform.max}; sandboxTier.max cannot be ${t.max}`);
|
|
303
|
+
}
|
|
304
|
+
const max = t.max ?? platform.max;
|
|
305
|
+
if (t.default !== undefined && !tierAtMost(t.default, max)) {
|
|
306
|
+
throw sandboxTierNotAllowed(`sandboxTier.default (${t.default}) cannot be above the organization's highest tier (${max})`);
|
|
307
|
+
}
|
|
308
|
+
if (t.allowUnsandboxed === true && !platform.allowUnsandboxed) {
|
|
309
|
+
throw sandboxTierNotAllowed("The platform does not allow unsandboxed devices (sandbox.tiers.allowUnsandboxed is false); sandboxTier.allowUnsandboxed cannot be true");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
213
312
|
}
|
|
214
313
|
async update(orgId, patch, actorAccountId, actorTokenId,
|
|
215
314
|
/** Merged into the audit detail (the Platform tool's `via: "agent"`). */
|
|
216
|
-
auditDetail
|
|
315
|
+
auditDetail,
|
|
316
|
+
/**
|
|
317
|
+
* `onlyNarrows` (the Platform tool, API tokens): refuse a `sandboxTier` or `sandboxEgress`
|
|
318
|
+
* change that widens (see {@link assertOnlyNarrows}); only a person in a session widens.
|
|
319
|
+
*/
|
|
320
|
+
opts) {
|
|
217
321
|
this.check(patch);
|
|
218
|
-
return this.updateIn(orgId, patch, actorAccountId, actorTokenId, auditDetail);
|
|
322
|
+
return this.updateIn(orgId, patch, actorAccountId, actorTokenId, auditDetail, opts);
|
|
219
323
|
}
|
|
220
|
-
|
|
324
|
+
/** The effective sandbox policy for the org's settings and a device's own tier (null = none). */
|
|
325
|
+
sandboxPolicy(settings, deviceTier) {
|
|
326
|
+
return effectiveSandboxPolicy(this.config.get().sandbox, settings, deviceTier);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Refuses a patch that widens the org's sandbox (sandbox dev tiers spec §2, §7.2: only a person
|
|
330
|
+
* widens it, never the Platform tool or an API token):
|
|
331
|
+
* - `sandboxTier`: the effective ceiling, default or unsandboxed switch rises, or a stored cap
|
|
332
|
+
* loosens (an absent `max` / `default` follows the platform, so it ranks above every tier and
|
|
333
|
+
* clearing a cap that equals today's platform value is a widening; an explicit
|
|
334
|
+
* `allowUnsandboxed: false` stays false). A cleared `default` under a new `max` no higher than
|
|
335
|
+
* the old default is no loosening: the default never exceeds the ceiling. The value is
|
|
336
|
+
* replaced whole, so the refusal names the fields the patch would clear;
|
|
337
|
+
* - `sandboxEgress`: the base list grows: from none to anything else, or to hosts (the
|
|
338
|
+
* deployment's when the org gives none) not all in the current list.
|
|
339
|
+
*/
|
|
340
|
+
assertOnlyNarrows(before, patch) {
|
|
341
|
+
if (patch.sandboxTier !== undefined) {
|
|
342
|
+
const next = patch.sandboxTier ?? undefined;
|
|
343
|
+
const prev = before.sandboxTier;
|
|
344
|
+
const b = this.sandboxPolicy(before, null);
|
|
345
|
+
const a = this.sandboxPolicy({ ...before, sandboxTier: next }, null);
|
|
346
|
+
// a cleared default follows the platform, capped by the ceiling: `max` bounds it then
|
|
347
|
+
const nextDefault = next?.default === undefined && next?.max !== undefined ? next.max : next?.default;
|
|
348
|
+
if (!tierAtMost(a.ceiling, b.ceiling) ||
|
|
349
|
+
!tierAtMost(a.defaultTier, b.defaultTier) ||
|
|
350
|
+
(a.allowUnsandboxed && !b.allowUnsandboxed) ||
|
|
351
|
+
intentRank(next?.max) > intentRank(prev?.max) ||
|
|
352
|
+
intentRank(nextDefault) > intentRank(prev?.default) ||
|
|
353
|
+
(prev?.allowUnsandboxed === false && next?.allowUnsandboxed !== false)) {
|
|
354
|
+
const cleared = ["max", "default", "allowUnsandboxed"].filter((f) => prev?.[f] !== undefined && next?.[f] === undefined);
|
|
355
|
+
throw sandboxTierNotAllowed("Raising the sandbox level needs a person in the organization settings." +
|
|
356
|
+
(cleared.length === 0
|
|
357
|
+
? ""
|
|
358
|
+
: ` sandboxTier is replaced whole: this change clears ${cleared.join(", ")}` +
|
|
359
|
+
" (which would then follow the platform); give the values to keep again."));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (patch.sandboxEgress !== undefined) {
|
|
363
|
+
const deploymentHosts = this.config.get().sandbox.limits.allowHosts;
|
|
364
|
+
const prev = before.sandboxEgress;
|
|
365
|
+
const next = patch.sandboxEgress ?? undefined;
|
|
366
|
+
const hostsOf = (e) => e?.allowHosts ?? deploymentHosts;
|
|
367
|
+
const widens = next?.mode !== "none" &&
|
|
368
|
+
(prev?.mode === "none" || !hostsOf(next).every((h) => hostsOf(prev).includes(h)));
|
|
369
|
+
if (widens) {
|
|
370
|
+
throw new ApiError("org.sandbox_egress_not_allowed", "Widening the sandbox network needs a person in the organization settings.");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async updateIn(orgId, patch, actorAccountId, actorTokenId, auditDetail, opts) {
|
|
221
375
|
await this.orgLock.lockAndAuthorize(orgId, actorAccountId, "org.update");
|
|
222
376
|
const row = await this.db
|
|
223
377
|
.selectFrom("orgs")
|
|
@@ -225,14 +379,26 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
225
379
|
.where("id", "=", orgId)
|
|
226
380
|
.executeTakeFirstOrThrow();
|
|
227
381
|
const stored = asObject(row.settings);
|
|
228
|
-
const before =
|
|
382
|
+
const before = this.parseStored(orgId, stored);
|
|
383
|
+
// again under the org lock: a hot config reload may have lowered the platform since
|
|
384
|
+
this.check(patch);
|
|
385
|
+
// under the org lock, so the comparison is with the value the write replaces
|
|
386
|
+
if (opts?.onlyNarrows)
|
|
387
|
+
this.assertOnlyNarrows(before, patch);
|
|
229
388
|
const changes = Object.fromEntries(Object.entries(patch)
|
|
230
389
|
.filter(([, v]) => v !== undefined)
|
|
231
390
|
// null clears sandboxEgress / wakeOnBackgroundTask: follow the deployment default again
|
|
232
391
|
.map(([k, v]) => [k, v === null ? undefined : v]));
|
|
233
392
|
const arrangementPatch = patch.uiArrangement;
|
|
234
393
|
delete changes.uiArrangement;
|
|
394
|
+
delete changes.scheduledTasks;
|
|
235
395
|
const after = { ...before, ...changes };
|
|
396
|
+
if (patch.scheduledTasks !== undefined) {
|
|
397
|
+
after.scheduledTasks = ScheduledTasksSettingsSchema.parse({
|
|
398
|
+
...before.scheduledTasks,
|
|
399
|
+
...Object.fromEntries(Object.entries(patch.scheduledTasks).filter(([, v]) => v !== undefined)),
|
|
400
|
+
});
|
|
401
|
+
}
|
|
236
402
|
if (arrangementPatch !== undefined) {
|
|
237
403
|
after.uiArrangement = mergeArrangement(before.uiArrangement, arrangementPatch);
|
|
238
404
|
if (Object.keys(after.uiArrangement).length === 0)
|
|
@@ -240,6 +406,12 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
240
406
|
}
|
|
241
407
|
if (after.sandboxEgress === undefined)
|
|
242
408
|
delete after.sandboxEgress;
|
|
409
|
+
// an empty sandboxTier is the same as none: follow the platform
|
|
410
|
+
if (after.sandboxTier !== undefined && Object.keys(after.sandboxTier).length === 0) {
|
|
411
|
+
delete after.sandboxTier;
|
|
412
|
+
}
|
|
413
|
+
if (after.sandboxTier === undefined)
|
|
414
|
+
delete after.sandboxTier;
|
|
243
415
|
if (after.wakeOnBackgroundTask === undefined)
|
|
244
416
|
delete after.wakeOnBackgroundTask;
|
|
245
417
|
if (after.timeZone === undefined)
|
|
@@ -256,7 +428,7 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
256
428
|
await assertThemeOnIn(this.db.db, orgId, patch.theme.id);
|
|
257
429
|
}
|
|
258
430
|
// 与存量合并:保留本版本不认识的键(清除的 sandboxEgress 不留在存量里)
|
|
259
|
-
const { sandboxEgress: _old, wakeOnBackgroundTask: _oldWake, timeZone: _oldZone, theme: _oldTheme, uiArrangement: _oldArrangement, ...rest } = stored;
|
|
431
|
+
const { sandboxEgress: _old, sandboxTier: _oldTier, wakeOnBackgroundTask: _oldWake, timeZone: _oldZone, theme: _oldTheme, uiArrangement: _oldArrangement, scheduledTasks: _oldScheduled, ...rest } = stored;
|
|
260
432
|
await this.db
|
|
261
433
|
.updateTable("orgs")
|
|
262
434
|
.set({ settings: JSON.stringify({ ...rest, ...after }) })
|
|
@@ -281,6 +453,22 @@ OrgSettingsService = __decorate([
|
|
|
281
453
|
__param(0, Inject(KernelDb)),
|
|
282
454
|
__param(1, Inject(TransactionHost)),
|
|
283
455
|
__param(2, Inject(OrgLock)),
|
|
284
|
-
__param(3, Inject(KernelConfigService))
|
|
456
|
+
__param(3, Inject(KernelConfigService)),
|
|
457
|
+
__param(4, Optional()),
|
|
458
|
+
__param(4, Inject(KERNEL_LOGGER))
|
|
285
459
|
], OrgSettingsService);
|
|
286
460
|
export { OrgSettingsService };
|
|
461
|
+
/**
|
|
462
|
+
* The one {@link OrgSettingsService}: the orgs module re-exports it, and the catalog imports it
|
|
463
|
+
* directly (approving a network request writes `sandboxEgress`) without a cycle through OrgsModule.
|
|
464
|
+
*/
|
|
465
|
+
let OrgSettingsModule = class OrgSettingsModule {
|
|
466
|
+
};
|
|
467
|
+
OrgSettingsModule = __decorate([
|
|
468
|
+
Module({
|
|
469
|
+
imports: [OrgLockModule],
|
|
470
|
+
providers: [OrgSettingsService],
|
|
471
|
+
exports: [OrgSettingsService],
|
|
472
|
+
})
|
|
473
|
+
], OrgSettingsModule);
|
|
474
|
+
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 },
|
|
@@ -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),
|
|
@@ -35,11 +35,6 @@ const Def = AgentCreateDefinition.shape;
|
|
|
35
35
|
/** Every string the model sends is bounded (the route's tool refs and model strings are not). */
|
|
36
36
|
const Short = z.string().min(1).max(200);
|
|
37
37
|
const Model = Def.model.extend({ provider: Short, model: Short }).strict();
|
|
38
|
-
const RouteSkills = Def.skills.unwrap();
|
|
39
|
-
/** `writePolicy` stays the web's; `orgSkills` is required here (the route defaults it to []). */
|
|
40
|
-
const Skills = RouteSkills.pick({ pinned: true })
|
|
41
|
-
.extend({ orgSkills: RouteSkills.shape.orgSkills.unwrap() })
|
|
42
|
-
.strict();
|
|
43
38
|
/**
|
|
44
39
|
* Whether `next` sets an `allow` that `current` does not have (decision 2026-11-09: `allow` on a
|
|
45
40
|
* connector tool lets an agent call it without asking). Such a change is confirmed every time and
|
|
@@ -61,7 +56,8 @@ const Fields = {
|
|
|
61
56
|
*/
|
|
62
57
|
disabledTools: Def.disabledTools.unwrap(),
|
|
63
58
|
toolPolicies: z.record(Short, Def.toolPolicies.unwrap().valueType),
|
|
64
|
-
skills
|
|
59
|
+
/** Skills turned off (skills default-on spec §4); `skills.writePolicy` stays the web's. */
|
|
60
|
+
disabledSkills: Def.disabledSkills.unwrap(),
|
|
65
61
|
subagents: Def.subagents.unwrap(),
|
|
66
62
|
};
|
|
67
63
|
/**
|
|
@@ -189,7 +185,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
189
185
|
}),
|
|
190
186
|
defineOperation({
|
|
191
187
|
id: "agents.get",
|
|
192
|
-
description: "Read one agent you can see with its full definition (description, instructions, model, the tools it turns off (disabledTools: an agent has every tool the org offers except these), tool policies, skills and sub-agents) and its profile (avatar, description, quick actions). Takes the agent id from agents.list.",
|
|
188
|
+
description: "Read one agent you can see with its full definition (description, instructions, model, the tools it turns off (disabledTools: an agent has every tool the org offers except these), tool policies, the skills it turns off (disabledSkills: an agent has every skill on in the org except these), skills.writePolicy and sub-agents) and its profile (avatar, description, quick actions). Takes the agent id from agents.list.",
|
|
193
189
|
input: z.object({ agentId: AgentId }).strict(),
|
|
194
190
|
example: { agentId: "7c1a5b0e-9a53-4c6b-8d64-2f5a1b0c3e77" },
|
|
195
191
|
authz: { orgAction: "agents.use" },
|
|
@@ -201,7 +197,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
201
197
|
}),
|
|
202
198
|
defineOperation({
|
|
203
199
|
id: "agents.create",
|
|
204
|
-
description: 'Create a new private agent owned by you, with a name, a model and optionally instructions, disabledTools, tool policies,
|
|
200
|
+
description: 'Create a new private agent owned by you, with a name, a model and optionally instructions, disabledTools, tool policies, disabledSkills and sub-agents. An agent has every tool the org offers (all built-in tools, the tools of the extensions on in the org and of the org\'s connectors) unless disabledTools turns it off: { kind: "builtin", name }, { kind: "plugin", plugin, tools: "all" | [names] } or { kind: "connector", connectorId, tools: "all" | [names] }. Likewise, an agent has every skill on in the org unless disabledSkills turns it off: { kind: "plugin", plugin, skills: "all" | [names] } for skills of a plugin (skills.list gives the plugin of each), or { kind: "skill", skillId } for a skill without a plugin; the skills of builtin plugins (builtin in skills.list) and the own skills of the agent cannot be turned off. Agents created with the Platform tool are private; sharing an agent with the org or with members is done on the agents page of the web app.',
|
|
205
201
|
input: z
|
|
206
202
|
.object({
|
|
207
203
|
definition: z
|
|
@@ -211,7 +207,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
211
207
|
model: Fields.model,
|
|
212
208
|
disabledTools: Fields.disabledTools.optional(),
|
|
213
209
|
toolPolicies: Fields.toolPolicies.optional(),
|
|
214
|
-
|
|
210
|
+
disabledSkills: Fields.disabledSkills.optional(),
|
|
215
211
|
subagents: Fields.subagents.optional(),
|
|
216
212
|
})
|
|
217
213
|
.strict(),
|
|
@@ -248,7 +244,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
248
244
|
model: d.model,
|
|
249
245
|
...(d.disabledTools !== undefined && { disabledTools: d.disabledTools }),
|
|
250
246
|
...(d.toolPolicies !== undefined && { toolPolicies: d.toolPolicies }),
|
|
251
|
-
...(d.
|
|
247
|
+
...(d.disabledSkills !== undefined && { disabledSkills: d.disabledSkills }),
|
|
252
248
|
...(d.subagents !== undefined && { subagents: d.subagents }),
|
|
253
249
|
});
|
|
254
250
|
return this.agents.create(ctx.orgId, ctx.actorAccountId, {
|
|
@@ -259,7 +255,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
259
255
|
}),
|
|
260
256
|
defineOperation({
|
|
261
257
|
id: "agents.update",
|
|
262
|
-
description: 'Change an agent you may edit: your own, private or shared, and as an org admin any agent of the org. Send the version you read from agents.get and only the fields to change; the others keep their value. The description (one short paragraph, "" removes it) is saved with the definition; the avatar and quick actions are changed with agents.update-profile. disabledTools replaces the whole list of turned-off tools (send [] to turn every tool on). A version that is no longer current is refused, so read the agent again first. The visibility cannot be changed here (that is done on the agents page of the web app). A change to your own private agent may be remembered by the person; a change to a shared agent, or one that sets a tool policy to allow, is confirmed every time.',
|
|
258
|
+
description: 'Change an agent you may edit: your own, private or shared, and as an org admin any agent of the org. Send the version you read from agents.get and only the fields to change; the others keep their value. The description (one short paragraph, "" removes it) is saved with the definition; the avatar and quick actions are changed with agents.update-profile. disabledTools replaces the whole list of turned-off tools (send [] to turn every tool on). disabledSkills replaces the whole list of turned-off skills (send [] to turn every skill on); an agent has every skill on in the org unless disabledSkills turns it off: { kind: "plugin", plugin, skills: "all" | [names] } for skills of a plugin (skills.list gives the plugin of each), or { kind: "skill", skillId } for a skill without a plugin; the skills of builtin plugins (builtin in skills.list) and the own skills of the agent cannot be turned off. A version that is no longer current is refused, so read the agent again first. The visibility cannot be changed here (that is done on the agents page of the web app). A change to your own private agent may be remembered by the person; a change to a shared agent, or one that sets a tool policy to allow, is confirmed every time.',
|
|
263
259
|
input: z
|
|
264
260
|
.object({
|
|
265
261
|
agentId: AgentId,
|
|
@@ -270,7 +266,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
270
266
|
model: Fields.model.optional(),
|
|
271
267
|
disabledTools: Fields.disabledTools.optional(),
|
|
272
268
|
toolPolicies: Fields.toolPolicies.optional(),
|
|
273
|
-
|
|
269
|
+
disabledSkills: Fields.disabledSkills.optional(),
|
|
274
270
|
subagents: Fields.subagents.optional(),
|
|
275
271
|
})
|
|
276
272
|
.strict(),
|
|
@@ -292,7 +288,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
292
288
|
model: "definition.model",
|
|
293
289
|
disabledTools: "definition.disabledTools",
|
|
294
290
|
toolPolicies: "definition.toolPolicies",
|
|
295
|
-
|
|
291
|
+
disabledSkills: "definition.disabledSkills",
|
|
296
292
|
subagents: "definition.subagents",
|
|
297
293
|
},
|
|
298
294
|
confirm: "personal",
|
|
@@ -332,7 +328,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
332
328
|
...(patch.model !== undefined && { model: patch.model }),
|
|
333
329
|
...(patch.disabledTools !== undefined && { disabledTools: patch.disabledTools }),
|
|
334
330
|
...(patch.toolPolicies !== undefined && { toolPolicies: patch.toolPolicies }),
|
|
335
|
-
...(patch.
|
|
331
|
+
...(patch.disabledSkills !== undefined && { disabledSkills: patch.disabledSkills }),
|
|
336
332
|
...(patch.subagents !== undefined && { subagents: patch.subagents }),
|
|
337
333
|
};
|
|
338
334
|
return this.agents.update(ctx.orgId, agentId, ctx.actorAccountId, { definition, expectedVersion: version }, undefined, viaAgent("agents.update", ctx));
|
|
@@ -414,7 +410,7 @@ let AgentsOperations = class AgentsOperations {
|
|
|
414
410
|
}),
|
|
415
411
|
defineOperation({
|
|
416
412
|
id: "agents.preview-from-template",
|
|
417
|
-
description: "Preview creating an agent from an agent template: what carries over, what is pending (connectors the org has not set up) or unavailable, the model that would be used, and a previewHash. agents.create-from-template needs that hash.",
|
|
413
|
+
description: "Preview creating an agent from an agent template: what carries over, what is pending (connectors the org has not set up) or unavailable, the model that would be used, and a previewHash. Each skill the template names is carried when the org has it and unavailable otherwise; the new agent has every skill on in the org except those the template's disallowedSkills turns off. agents.create-from-template needs that hash.",
|
|
418
414
|
input: AgentFromTemplatePreviewBody,
|
|
419
415
|
example: { templateId: "5d0c7a52-3a0e-4f0a-bd0a-0f8c3d2a9e10" },
|
|
420
416
|
authz: { orgAction: "agents.create" },
|
|
@@ -131,7 +131,7 @@ let DevicesOperations = class DevicesOperations {
|
|
|
131
131
|
}),
|
|
132
132
|
defineOperation({
|
|
133
133
|
id: "devices.get",
|
|
134
|
-
description: "Read one of your devices with its source (cloud or local), provider, state, instance, the machine of a local one and whether it is online, limits and creation time. Takes the id from devices.list.",
|
|
134
|
+
description: "Read one of your devices with its source (cloud or local), provider, state, instance, the machine of a local one and whether it is online, limits, sandbox level (the tier chosen and the one in effect, the highest allowed and why it is lower, whether it runs without a sandbox) and creation time. Takes the id from devices.list. Only the person changes a device's sandbox level, on the web.",
|
|
135
135
|
input: DeviceId,
|
|
136
136
|
example: { deviceId: "4e8a0c1d-6b2f-4a37-8d19-3c5e7f9a1b20" },
|
|
137
137
|
authz: { orgAction: "agents.use" },
|
|
@@ -276,6 +276,8 @@ let DevicesOperations = class DevicesOperations {
|
|
|
276
276
|
authz: { orgAction: "agents.use" },
|
|
277
277
|
mirrors: ["PATCH /api/v1/orgs/:orgId/devices/:id"],
|
|
278
278
|
renames: { deviceId: "id" },
|
|
279
|
+
// a person decides their device's sandbox level (sandbox dev tiers spec §7.2)
|
|
280
|
+
omits: ["tier"],
|
|
279
281
|
confirm: "personal",
|
|
280
282
|
errors: [
|
|
281
283
|
{
|
|
@@ -109,6 +109,25 @@ function orgRow(i) {
|
|
|
109
109
|
summary: `Your ${r.kind} request was ${r.status}${r.reason ? `: ${r.reason}` : ""}.`,
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
|
+
case "schedule_attention": {
|
|
113
|
+
const a = i.attention;
|
|
114
|
+
const summary = a.reason === "missing_grants"
|
|
115
|
+
? `Scheduled task "${a.name}" ran without: ${a.missing.join(", ")}.${a.needsRole.length > 0 ? ` Needing a higher role than the owner has (cannot be pre-approved): ${a.needsRole.join(", ")}.` : ""}${a.needsAdmin.length > 0 ? ` Outside the organization's network allowlist (only an org admin can add them): ${a.needsAdmin.join(", ")}.` : ""}${a.deviceRefuses ? " The task's device does not accept network grants; its owner allows domains locally (capybara-device policy add allowedDomains <host>)." : ""}`
|
|
116
|
+
: a.reason === "failed"
|
|
117
|
+
? `Scheduled task "${a.name}"'s latest run failed${a.error ? `: ${a.error}` : ""}.`
|
|
118
|
+
: `Scheduled task "${a.name}" is paused (${a.reason}${a.pausedBy ? ` by ${a.pausedBy.name || a.pausedBy.id}` : ""}).`;
|
|
119
|
+
return {
|
|
120
|
+
...base,
|
|
121
|
+
name: a.name,
|
|
122
|
+
state: a.reason,
|
|
123
|
+
summary,
|
|
124
|
+
next: a.reason === "missing_grants"
|
|
125
|
+
? "Add the missing tools to the task's approved tools and connect the missing connectors on the web; tools that need a higher role cannot be pre-approved; network domains (network:<host>) are approved only by the person on the task page."
|
|
126
|
+
: a.reason === "failed"
|
|
127
|
+
? "Check the run's error and the task's settings; the next good run clears this, and repeated failures pause the task."
|
|
128
|
+
: "Resume the task once the cause is fixed.",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
112
131
|
default: {
|
|
113
132
|
const n = i.notice;
|
|
114
133
|
return {
|