@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
package/dist/skills/usable.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { skillDisabled } from "../agents/definition.js";
|
|
2
|
+
import { componentOn, platformBuiltin } from "../catalog/component-state.js";
|
|
2
3
|
/** The `source` prefix of a platform skill version in a snapshot or an invocation record. */
|
|
3
4
|
export const PLATFORM_SOURCE = "platform:";
|
|
4
5
|
/** The snapshot `source` of a platform skill version stored at `prefix` (in `deployment/`). */
|
|
@@ -38,6 +39,9 @@ async function platformRowsOn(db, orgId, ids) {
|
|
|
38
39
|
"c.id as item_id",
|
|
39
40
|
"c.disabled as item_disabled",
|
|
40
41
|
"c.builtin as item_builtin",
|
|
42
|
+
"c.owner_org_id as item_owner",
|
|
43
|
+
"p.owner_org_id",
|
|
44
|
+
"p.shareable",
|
|
41
45
|
eb
|
|
42
46
|
.exists(eb
|
|
43
47
|
.selectFrom("plugin_installs as a")
|
|
@@ -66,18 +70,28 @@ async function platformRowsOn(db, orgId, ids) {
|
|
|
66
70
|
.where("p.disabled", "=", false);
|
|
67
71
|
if (ids !== undefined)
|
|
68
72
|
q = q.where("p.id", "in", [...ids]);
|
|
73
|
+
// the share gate (ruling P3-1, as `usableBy` in catalog/ownership.ts): another org's skill
|
|
74
|
+
// while its owner shares it, or while this org is associated with it (listed, its plugin
|
|
75
|
+
// installed here, or switched on here: an org keeps what it has when sharing stops, P3-16)
|
|
76
|
+
const gate = (r) => r.owner_org_id === null ||
|
|
77
|
+
r.owner_org_id === orgId ||
|
|
78
|
+
r.shareable ||
|
|
79
|
+
Boolean(r.listed) ||
|
|
80
|
+
Boolean(r.installed) ||
|
|
81
|
+
r.switch === true;
|
|
69
82
|
// the predicate of component scope (catalog/component-state.ts); `disabled` is filtered above
|
|
70
|
-
return (await q.execute()).filter((r) =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
return (await q.execute()).filter((r) => gate(r) &&
|
|
84
|
+
componentOn({ scopeMode: r.scope_mode, scope: r.scope, disabled: false, itemId: r.catalog_item_id }, r.catalog_item_id === null
|
|
85
|
+
? null
|
|
86
|
+
: {
|
|
87
|
+
live: r.item_id !== null,
|
|
88
|
+
disabled: r.item_disabled === true,
|
|
89
|
+
builtin: platformBuiltin({ builtin: r.item_builtin, owner_org_id: r.item_owner }),
|
|
90
|
+
// the plugin's own scope and list do not decide whether a component is on
|
|
91
|
+
scope: "optional",
|
|
92
|
+
listed: false,
|
|
93
|
+
installed: Boolean(r.installed),
|
|
94
|
+
}, Boolean(r.listed), r.switch ?? null));
|
|
81
95
|
}
|
|
82
96
|
/**
|
|
83
97
|
* The ids among `ids` of the platform skills on in the org (see {@link platformOn}), whatever the
|
|
@@ -114,7 +128,9 @@ export async function pinnedSkillLinksServed(db, orgId, ids) {
|
|
|
114
128
|
* - `own`: `wantedOn` over the skill's own scope, the org's list membership and its switch.
|
|
115
129
|
* One query for the skills with their plugin and org state, one for the org's names.
|
|
116
130
|
*/
|
|
117
|
-
async function platformOn(db, orgId, ids, log
|
|
131
|
+
async function platformOn(db, orgId, ids, log,
|
|
132
|
+
/** Ids nobody selected (builtin skills): their shadowing is logged at `debug`. */
|
|
133
|
+
quiet = new Set()) {
|
|
118
134
|
const rows = await platformRowsOn(db, orgId, ids);
|
|
119
135
|
if (rows.length === 0)
|
|
120
136
|
return [];
|
|
@@ -143,12 +159,12 @@ async function platformOn(db, orgId, ids, log) {
|
|
|
143
159
|
const sameComponent = o.platform_skill_id === r.id ||
|
|
144
160
|
(o.catalog_item_id !== null && o.catalog_item_id === r.catalog_item_id);
|
|
145
161
|
if (!sameComponent) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
162
|
+
const fields = { orgId, name: r.name, platformSkillId: r.id, skillId: o.id };
|
|
163
|
+
const msg = "platform skill shadowed by an org skill of the same name";
|
|
164
|
+
if (quiet.has(r.id))
|
|
165
|
+
log?.debug?.(msg, fields);
|
|
166
|
+
else
|
|
167
|
+
log?.warn(msg, fields);
|
|
152
168
|
}
|
|
153
169
|
continue;
|
|
154
170
|
}
|
|
@@ -187,7 +203,9 @@ async function platformOn(db, orgId, ids, log) {
|
|
|
187
203
|
* while the platform skill is on in the org; a pinned copy as its own version, while its platform
|
|
188
204
|
* skill is on in the org (ruling P2-18); a platform skill id while it is on in the org.
|
|
189
205
|
*/
|
|
190
|
-
export async function skillVersionsById(db, orgId, ids, log
|
|
206
|
+
export async function skillVersionsById(db, orgId, ids, log,
|
|
207
|
+
/** Ids nobody selected (builtin skills): a name clash of theirs is logged at `debug`. */
|
|
208
|
+
quiet = new Set()) {
|
|
191
209
|
const unique = [...new Set(ids)];
|
|
192
210
|
const out = new Map();
|
|
193
211
|
if (unique.length === 0)
|
|
@@ -222,7 +240,7 @@ export async function skillVersionsById(db, orgId, ids, log) {
|
|
|
222
240
|
...linked.map((r) => r.platform_skill_id),
|
|
223
241
|
]),
|
|
224
242
|
];
|
|
225
|
-
const platform = new Map((await platformOn(db, orgId, platformIds, log)).map((p) => [p.id, p]));
|
|
243
|
+
const platform = new Map((await platformOn(db, orgId, platformIds, log, quiet)).map((p) => [p.id, p]));
|
|
226
244
|
const owned = await ownedPlatformIds(db, orgId, linked.map((r) => r.platform_skill_id));
|
|
227
245
|
// ruling P2-18: a pinned copy serves its own version while its platform skill is on in the org
|
|
228
246
|
const pinnedOn = await pinnedSkillLinksServed(db, orgId, pinned.map((r) => r.platform_skill_id));
|
|
@@ -285,33 +303,149 @@ async function ownedPlatformIds(db, orgId, platformIds) {
|
|
|
285
303
|
return new Set(rows.map((r) => r.id));
|
|
286
304
|
}
|
|
287
305
|
/**
|
|
288
|
-
* The
|
|
289
|
-
*
|
|
290
|
-
* the
|
|
291
|
-
*
|
|
306
|
+
* The skill ids no agent can turn off (skill authoring spec §5.7, skills default-on spec §3): the
|
|
307
|
+
* live platform skills of the platform's builtin plugins (`catalog_items.builtin`, platform-owned),
|
|
308
|
+
* plus the org's pinned copies of them (a pinned copy shadows its platform skill by name, ruling
|
|
309
|
+
* P2-18). Whether each is on in the org (a component switch, the deployment's disable) is left to
|
|
310
|
+
* the resolvers.
|
|
292
311
|
*/
|
|
293
|
-
export async function
|
|
294
|
-
const
|
|
312
|
+
export async function builtinSkillIds(db, orgId) {
|
|
313
|
+
const platform = await db
|
|
314
|
+
.selectFrom("platform_skills as p")
|
|
315
|
+
.innerJoin("catalog_items as c", "c.id", "p.catalog_item_id")
|
|
316
|
+
.select("p.id")
|
|
317
|
+
.where("c.builtin", "=", true)
|
|
318
|
+
.where("c.owner_org_id", "is", null)
|
|
319
|
+
.where("c.deleted_at", "is", null)
|
|
320
|
+
.where("p.owner_org_id", "is", null)
|
|
321
|
+
.where("p.deleted_at", "is", null)
|
|
322
|
+
.where("p.disabled", "=", false)
|
|
323
|
+
.execute();
|
|
324
|
+
if (platform.length === 0)
|
|
325
|
+
return [];
|
|
326
|
+
const ids = platform.map((r) => r.id);
|
|
327
|
+
const pinnedCopies = await db
|
|
295
328
|
.selectFrom("skills")
|
|
296
329
|
.select("id")
|
|
297
|
-
.where("org_id", "=",
|
|
298
|
-
.where("scope", "=", "
|
|
299
|
-
.where("
|
|
330
|
+
.where("org_id", "=", orgId)
|
|
331
|
+
.where("scope", "=", "org")
|
|
332
|
+
.where("deleted_at", "is", null)
|
|
333
|
+
.where("pinned_version", "is not", null)
|
|
334
|
+
.where("platform_skill_id", "in", ids)
|
|
335
|
+
.execute();
|
|
336
|
+
return [...ids, ...pinnedCopies.map((r) => r.id)];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* The names of the platform's builtin plugins (`catalog_items.builtin`, platform-owned, live): their
|
|
340
|
+
* skills are always on, and an agent cannot turn them off (skills default-on spec §3).
|
|
341
|
+
*/
|
|
342
|
+
export async function builtinSkillPlugins(db) {
|
|
343
|
+
const rows = await db
|
|
344
|
+
.selectFrom("catalog_items")
|
|
345
|
+
.select("name")
|
|
346
|
+
.where("builtin", "=", true)
|
|
347
|
+
.where("owner_org_id", "is", null)
|
|
300
348
|
.where("deleted_at", "is", null)
|
|
301
349
|
.execute();
|
|
302
|
-
|
|
350
|
+
return new Set(rows.map((r) => r.name));
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The ids of the skills an agent always has, whatever its `disabledSkills` say: its own skills
|
|
354
|
+
* (`scope = 'agent'`) and the org skills it shared (`origin_agent_id`: the agent keeps a shared
|
|
355
|
+
* skill without a definition entry).
|
|
356
|
+
*/
|
|
357
|
+
export async function agentOwnSkillIds(db, a) {
|
|
358
|
+
const rows = await db
|
|
303
359
|
.selectFrom("skills")
|
|
304
|
-
.select("id")
|
|
360
|
+
.select(["id", "scope"])
|
|
305
361
|
.where("org_id", "=", a.orgId)
|
|
362
|
+
.where("deleted_at", "is", null)
|
|
363
|
+
.where((eb) => eb.or([
|
|
364
|
+
eb.and([eb("scope", "=", "agent"), eb("agent_id", "=", a.agentId)]),
|
|
365
|
+
eb.and([eb("scope", "=", "org"), eb("origin_agent_id", "=", a.agentId)]),
|
|
366
|
+
]))
|
|
367
|
+
.execute();
|
|
368
|
+
return {
|
|
369
|
+
own: rows.filter((r) => r.scope === "agent").map((r) => r.id),
|
|
370
|
+
shared: rows.filter((r) => r.scope === "org").map((r) => r.id),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* The org's copies linked to the platform skills `platformIds` without a pin (ruling B2), by
|
|
375
|
+
* platform skill id (the first by id when there are several): such a copy keeps its own id in an
|
|
376
|
+
* agent's catalog, as when it was selected, so the snapshot does not change when a copy gets
|
|
377
|
+
* linked.
|
|
378
|
+
*/
|
|
379
|
+
export async function linkedCopyIds(db, orgId, platformIds) {
|
|
380
|
+
const linked = new Map();
|
|
381
|
+
if (platformIds.length === 0)
|
|
382
|
+
return linked;
|
|
383
|
+
const copies = await db
|
|
384
|
+
.selectFrom("skills")
|
|
385
|
+
.select(["id", "platform_skill_id"])
|
|
386
|
+
.where("org_id", "=", orgId)
|
|
306
387
|
.where("scope", "=", "org")
|
|
307
|
-
.where("origin_agent_id", "=", a.agentId)
|
|
308
388
|
.where("deleted_at", "is", null)
|
|
389
|
+
.where("pinned_version", "is", null)
|
|
390
|
+
.where("platform_skill_id", "in", [...platformIds])
|
|
391
|
+
.orderBy("id")
|
|
309
392
|
.execute();
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
393
|
+
for (const c of copies) {
|
|
394
|
+
const pid = c.platform_skill_id;
|
|
395
|
+
if (!linked.has(pid))
|
|
396
|
+
linked.set(pid, c.id);
|
|
397
|
+
}
|
|
398
|
+
return linked;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* The ids a disabled skill entry may name for `r`: its own, the platform skill it serves and the
|
|
402
|
+
* org's linked copy listed under it (`linked`, from {@link linkedCopyIds}).
|
|
403
|
+
*/
|
|
404
|
+
export function skillIdsOf(r, linked) {
|
|
405
|
+
const ids = new Set([r.id]);
|
|
406
|
+
if (r.platformSkillId !== undefined)
|
|
407
|
+
ids.add(r.platformSkillId);
|
|
408
|
+
const copy = linked.get(r.id);
|
|
409
|
+
if (copy !== undefined)
|
|
410
|
+
ids.add(copy);
|
|
411
|
+
return [...ids];
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* The skills an agent has (skills default-on spec §3), sorted by id: its own skills, the org
|
|
415
|
+
* skills it shared, and every skill on in the org ({@link usableSkillVersions}) minus what
|
|
416
|
+
* `disabledSkills` turns off. A plugin entry matches a skill's `plugin` and name, a skill entry its
|
|
417
|
+
* id (or the platform skill a copy serves). The skills of builtin plugins, the agent's own and its
|
|
418
|
+
* shared ones are never turned off. Not deduplicated by name (`resolveSkills` ranks).
|
|
419
|
+
*/
|
|
420
|
+
export async function agentSkills(db, a, log) {
|
|
421
|
+
const { own, shared } = await agentOwnSkillIds(db, a);
|
|
422
|
+
const always = await skillVersionsById(db, a.orgId, [...own, ...shared], log);
|
|
423
|
+
const out = new Map();
|
|
424
|
+
for (const r of always.values()) {
|
|
425
|
+
if (r.scope === "agent" ? r.agentId === a.agentId : true)
|
|
426
|
+
out.set(r.id, r);
|
|
427
|
+
}
|
|
428
|
+
const usable = await usableSkillVersions(db, a.orgId, log);
|
|
429
|
+
// a linked copy keeps its own id in the catalog (see linkedCopyIds)
|
|
430
|
+
const linked = await linkedCopyIds(db, a.orgId, usable.flatMap((r) => (r.origin === "platform" ? [r.id] : [])));
|
|
431
|
+
const builtin = a.disabledSkills.length === 0 ? new Set() : await builtinSkillPlugins(db);
|
|
432
|
+
const builtinIds = a.disabledSkills.length === 0 ? new Set() : new Set(await builtinSkillIds(db, a.orgId));
|
|
433
|
+
for (const r of usable) {
|
|
434
|
+
if (out.has(r.id))
|
|
435
|
+
continue;
|
|
436
|
+
const ids = skillIdsOf(r, linked);
|
|
437
|
+
const fixed = (r.plugin !== null && builtin.has(r.plugin) && r.origin === "platform") ||
|
|
438
|
+
ids.some((id) => builtinIds.has(id));
|
|
439
|
+
if (!fixed && skillDisabled(a.disabledSkills, { plugin: r.plugin, name: r.name, ids })) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
const copy = linked.get(r.id);
|
|
443
|
+
if (copy === undefined)
|
|
444
|
+
out.set(r.id, r);
|
|
445
|
+
else if (!out.has(copy))
|
|
446
|
+
out.set(copy, { ...r, id: copy });
|
|
447
|
+
}
|
|
448
|
+
return [...out.values()].sort((x, y) => (x.id < y.id ? -1 : x.id > y.id ? 1 : 0));
|
|
315
449
|
}
|
|
316
450
|
/**
|
|
317
451
|
* The org's usable skills, sorted by name: its own live org skills (`scope = 'org'`, unlinked, or
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type Principal } from "../identity/principal.js";
|
|
3
3
|
import { type OrgContext } from "../orgs/org.guard.js";
|
|
4
|
-
import { type
|
|
4
|
+
import { type ApprovalPublicRecord, ApprovalsService, type ApprovalView, type ToolGrantView } from "./approvals.service.js";
|
|
5
5
|
declare const ListQuery: z.ZodObject<{
|
|
6
6
|
status: z.ZodOptional<z.ZodEnum<{
|
|
7
7
|
approved: "approved";
|
|
@@ -9,6 +9,7 @@ declare const ListQuery: z.ZodObject<{
|
|
|
9
9
|
denied: "denied";
|
|
10
10
|
expired: "expired";
|
|
11
11
|
pending: "pending";
|
|
12
|
+
skipped: "skipped";
|
|
12
13
|
}>>;
|
|
13
14
|
}, z.core.$strict>;
|
|
14
15
|
declare const DecisionBody: z.ZodObject<{
|
|
@@ -23,6 +24,7 @@ declare const DecisionBody: z.ZodObject<{
|
|
|
23
24
|
}>>;
|
|
24
25
|
note: z.ZodOptional<z.ZodString>;
|
|
25
26
|
reason: z.ZodOptional<z.ZodString>;
|
|
27
|
+
hosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
28
|
}, z.core.$strict>;
|
|
27
29
|
/**
|
|
28
30
|
* 工具调用审批(`/api/v1/orgs/:orgId/approvals`,`approvals.decide`:访客起、仅限用户主体——agent 不能审批,R2)。
|
|
@@ -38,7 +40,7 @@ export declare class ApprovalsController {
|
|
|
38
40
|
approval: ApprovalView;
|
|
39
41
|
}>;
|
|
40
42
|
decide(principal: Principal, org: OrgContext, id: string, body: z.infer<typeof DecisionBody>): Promise<{
|
|
41
|
-
approval:
|
|
43
|
+
approval: ApprovalPublicRecord;
|
|
42
44
|
}>;
|
|
43
45
|
}
|
|
44
46
|
/** 本人的「以后都允许」授权(`/api/v1/orgs/:orgId/me/tool-grants`):查看与撤销,只动自己的。 */
|
|
@@ -9,13 +9,14 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
9
9
|
};
|
|
10
10
|
import { Controller, Delete, Get, HttpCode, Inject, Param, Post } from "@nestjs/common";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
+
import { NETWORK_ASK_MAX_HOSTS, NETWORK_HOST_MAX_CHARS } from "../conversations/events.js";
|
|
12
13
|
import { CurrentPrincipal, patTokenId, requireUser, } from "../identity/principal.js";
|
|
13
14
|
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
14
15
|
import { ZBody, ZQuery } from "../infra/validation/zod-params.js";
|
|
15
16
|
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
16
17
|
import { CONTROL_OR_HIDDEN_RE } from "../text.js";
|
|
17
18
|
import { ApprovalsListQuerySchema } from "./approvals.schemas.js";
|
|
18
|
-
import { APPROVAL_DENY_REASON_MAX_CHARS, ApprovalsService, } from "./approvals.service.js";
|
|
19
|
+
import { APPROVAL_DENY_REASON_MAX_CHARS, ApprovalsService, publicRecord, } from "./approvals.service.js";
|
|
19
20
|
const ListQuery = ApprovalsListQuerySchema;
|
|
20
21
|
const DecisionBody = z
|
|
21
22
|
.object({
|
|
@@ -32,11 +33,24 @@ const DecisionBody = z
|
|
|
32
33
|
.max(APPROVAL_DENY_REASON_MAX_CHARS)
|
|
33
34
|
.refine((r) => !CONTROL_OR_HIDDEN_RE.test(r.replace(/[\n\t]/g, " ")), "reason must not contain control or format characters")
|
|
34
35
|
.optional(),
|
|
36
|
+
/**
|
|
37
|
+
* A network ask only: the hosts allowed, a subset of those asked (absent: all of them); the
|
|
38
|
+
* others count as denied.
|
|
39
|
+
*/
|
|
40
|
+
hosts: z
|
|
41
|
+
.array(z.string().min(1).max(NETWORK_HOST_MAX_CHARS))
|
|
42
|
+
.min(1)
|
|
43
|
+
.max(NETWORK_ASK_MAX_HOSTS)
|
|
44
|
+
.optional(),
|
|
35
45
|
})
|
|
36
46
|
.strict()
|
|
37
47
|
.refine((b) => b.reason === undefined || b.decision === "deny", {
|
|
38
48
|
path: ["reason"],
|
|
39
49
|
message: "A reason can only be given with deny",
|
|
50
|
+
})
|
|
51
|
+
.refine((b) => b.hosts === undefined || b.decision === "approve", {
|
|
52
|
+
path: ["hosts"],
|
|
53
|
+
message: "Hosts can only be given with approve",
|
|
40
54
|
});
|
|
41
55
|
class ApprovalsListQuery extends createZodDto(ListQuery, "ApprovalsListQuery") {
|
|
42
56
|
}
|
|
@@ -61,8 +75,9 @@ let ApprovalsController = class ApprovalsController {
|
|
|
61
75
|
}
|
|
62
76
|
async decide(principal, org, id, body) {
|
|
63
77
|
const p = requireUser(principal);
|
|
78
|
+
// a network ask's kept command result stays in the kernel
|
|
64
79
|
return {
|
|
65
|
-
approval: await this.approvals.decide(org.orgId, id, p.account.id, body, patTokenId(p)),
|
|
80
|
+
approval: publicRecord(await this.approvals.decide(org.orgId, id, p.account.id, body, patTokenId(p), "web")),
|
|
66
81
|
};
|
|
67
82
|
}
|
|
68
83
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/** `GET /approvals?status=`: absent lists every status. */
|
|
3
3
|
export const ApprovalsListQuerySchema = z
|
|
4
|
-
.object({
|
|
4
|
+
.object({
|
|
5
|
+
status: z.enum(["pending", "approved", "denied", "expired", "cancelled", "skipped"]).optional(),
|
|
6
|
+
})
|
|
5
7
|
.strict();
|
|
6
8
|
/** `:id` of `GET /approvals/:id`, as the route takes it (`@Param`). */
|
|
7
9
|
export const ApprovalPathParams = z.object({ id: z.string() });
|
|
@@ -1,16 +1,68 @@
|
|
|
1
|
-
import { type LocalizedName } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type LocalizedName, type ToolDisplay } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { EventLog } from "../conversations/event-log.js";
|
|
3
|
+
import { type ToolView } from "../conversations/events.js";
|
|
3
4
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
4
5
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
6
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
7
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
8
|
+
import { LockService } from "../infra/lock/lock.service.js";
|
|
9
|
+
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
7
10
|
import { type OrgRole } from "../orgs/roles.js";
|
|
11
|
+
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
12
|
+
import { ScheduledTaskStore } from "../schedules/store.js";
|
|
8
13
|
import type { ApprovalPolicySource } from "./pipeline.js";
|
|
14
|
+
import { ToolRegistry } from "./registry.js";
|
|
9
15
|
export { APPROVAL_DENY_REASON_MAX_CHARS } from "../conversations/events.js";
|
|
10
16
|
/** `list` 一次最多返回的审批数(收件箱据此判断这个来源是否取满)。 */
|
|
11
17
|
export declare const APPROVALS_LIST_MAX = 100;
|
|
12
|
-
export type ApprovalStatus = "pending" | "approved" | "denied" | "expired" | "cancelled";
|
|
18
|
+
export type ApprovalStatus = "pending" | "approved" | "denied" | "expired" | "cancelled" | "skipped";
|
|
13
19
|
export type ApprovalScope = "once" | "session" | "always";
|
|
20
|
+
/** What an approval asks: a tool call, or the device owner about refused network hosts. */
|
|
21
|
+
export type ApprovalKind = "tool" | "network";
|
|
22
|
+
/**
|
|
23
|
+
* A network ask's `detail` (network ask-to-allow spec §6.1, P-D2): the device, the refused hosts,
|
|
24
|
+
* the command and its kept `tool_result` (written with a note when the ask settles; the command is
|
|
25
|
+
* never run again), and once approved what took effect.
|
|
26
|
+
*/
|
|
27
|
+
export interface NetworkAskDetail {
|
|
28
|
+
deviceId: string;
|
|
29
|
+
hosts: {
|
|
30
|
+
host: string;
|
|
31
|
+
port: number;
|
|
32
|
+
}[];
|
|
33
|
+
command: string;
|
|
34
|
+
description: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* The formatted `tool_result` of the command (≤ 16 KB; larger output already archived), and the
|
|
37
|
+
* display-only fields the pipeline writes with it when the ask settles.
|
|
38
|
+
*/
|
|
39
|
+
result: {
|
|
40
|
+
content: string;
|
|
41
|
+
isError: boolean;
|
|
42
|
+
outputId?: string;
|
|
43
|
+
contentTypes?: string[];
|
|
44
|
+
display?: ToolDisplay;
|
|
45
|
+
view?: ToolView;
|
|
46
|
+
};
|
|
47
|
+
/** The hosts the decision allowed (every approved decision writes them; none: nothing). */
|
|
48
|
+
grantedHosts?: string[];
|
|
49
|
+
/** What took effect: a run grant, the device's policy, or a run grant because the device did not apply it. */
|
|
50
|
+
/**
|
|
51
|
+
* What took effect: a run grant (`once`), the device's policy (`always`), a run grant because
|
|
52
|
+
* the device could not be reached or lacks the feature (`once_fallback`), or nothing because the
|
|
53
|
+
* device refused the change (`refused`: its owner takes no remote grants; no run grant either).
|
|
54
|
+
*/
|
|
55
|
+
outcome?: "once" | "always" | "once_fallback" | "refused";
|
|
56
|
+
/** Why 「一直允许」 did not reach the device's policy. */
|
|
57
|
+
fallback?: "offline" | "refused" | "unsupported";
|
|
58
|
+
}
|
|
59
|
+
/** A network ask's detail as the API shows it: without the kept command result. */
|
|
60
|
+
export type NetworkAskView = Omit<NetworkAskDetail, "result">;
|
|
61
|
+
/**
|
|
62
|
+
* The model-visible note written after a network ask's kept result when its run is stopped before
|
|
63
|
+
* the device owner answered (spec §6.7).
|
|
64
|
+
*/
|
|
65
|
+
export declare const NETWORK_ASK_CANCELLED_NOTE = "[network] The run was stopped before the user answered; access stays blocked.";
|
|
14
66
|
/** 一条审批记录。 */
|
|
15
67
|
export interface ApprovalRecord {
|
|
16
68
|
id: string;
|
|
@@ -20,6 +72,10 @@ export interface ApprovalRecord {
|
|
|
20
72
|
callId: string;
|
|
21
73
|
/** 同一调用的第几次审批(从 1 起;工具定义变化后重新审批时递增)。 */
|
|
22
74
|
attempt: number;
|
|
75
|
+
/** `network`: a network ask (its tool key is `Bash`, its facts in {@link detail}). */
|
|
76
|
+
kind: ApprovalKind;
|
|
77
|
+
/** A network ask's hosts, command, kept result and outcome; null for a tool approval. */
|
|
78
|
+
detail: NetworkAskDetail | null;
|
|
23
79
|
toolKey: string;
|
|
24
80
|
specHash: string;
|
|
25
81
|
connectorId: string | null;
|
|
@@ -45,8 +101,14 @@ export interface ApprovalRecord {
|
|
|
45
101
|
expiresAt: string;
|
|
46
102
|
createdAt: string;
|
|
47
103
|
}
|
|
104
|
+
/** An approval as a route answers it: a network ask's detail without the kept command result. */
|
|
105
|
+
export interface ApprovalPublicRecord extends Omit<ApprovalRecord, "detail"> {
|
|
106
|
+
detail: NetworkAskView | null;
|
|
107
|
+
}
|
|
108
|
+
/** The record without a network ask's kept command result (what routes and cards may show). */
|
|
109
|
+
export declare function publicRecord(r: ApprovalRecord): ApprovalPublicRecord;
|
|
48
110
|
/** 审批详情(列表与详情接口返回)。 */
|
|
49
|
-
export interface ApprovalView extends
|
|
111
|
+
export interface ApprovalView extends ApprovalPublicRecord {
|
|
50
112
|
/** 来源内的工具原名(连接器工具为连接器里的名字;否则同 `toolKey`)。 */
|
|
51
113
|
toolName: string;
|
|
52
114
|
connector: {
|
|
@@ -108,7 +170,30 @@ export interface DecisionInput {
|
|
|
108
170
|
note?: string | undefined;
|
|
109
171
|
/** Only with `deny` (trimmed, 1–500 characters; checked by the route). */
|
|
110
172
|
reason?: string | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* A network ask's approval only: the hosts allowed, a subset of those asked (absent: all); the
|
|
175
|
+
* unchecked ones count as denied (P-D5).
|
|
176
|
+
*/
|
|
177
|
+
hosts?: string[] | undefined;
|
|
178
|
+
}
|
|
179
|
+
/** Where a decision was made (the device's local audit line and `device.network_*`). */
|
|
180
|
+
export type DecisionVia = "web" | "im";
|
|
181
|
+
/** A new network ask (network ask-to-allow spec §6.2), made by the tool pipeline. */
|
|
182
|
+
export interface NetworkAskInput {
|
|
183
|
+
orgId: string;
|
|
184
|
+
conversationId: string;
|
|
185
|
+
runId: string;
|
|
186
|
+
/** The call's step (written to `approval_requested`). */
|
|
187
|
+
step: number;
|
|
188
|
+
callId: string;
|
|
189
|
+
/** The device owner: the only person who may decide. */
|
|
190
|
+
requestedFor: string;
|
|
191
|
+
detail: Omit<NetworkAskDetail, "grantedHosts" | "outcome" | "fallback">;
|
|
192
|
+
/** `["once", "always"]`, or `["once"]` when the device takes no remote grants. */
|
|
193
|
+
scopes: ("once" | "always")[];
|
|
111
194
|
}
|
|
195
|
+
/** The tool key a network ask is recorded under (the command ran in `Bash`). */
|
|
196
|
+
export declare const NETWORK_ASK_TOOL_KEY = "Bash";
|
|
112
197
|
/** 「以后都允许」授权(本人可撤销)。 */
|
|
113
198
|
export interface ToolGrantView {
|
|
114
199
|
agentId: string;
|
|
@@ -132,13 +217,39 @@ export declare class ApprovalsService {
|
|
|
132
217
|
private readonly eventLog;
|
|
133
218
|
private readonly events;
|
|
134
219
|
readonly txHost: TransactionHost;
|
|
135
|
-
|
|
220
|
+
private readonly schedules;
|
|
221
|
+
/** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
|
|
222
|
+
private readonly registry;
|
|
223
|
+
/** 「这台设备一直允许」 sends the `policy` op to the device before the decision is recorded. */
|
|
224
|
+
private readonly policies;
|
|
225
|
+
/** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
|
|
226
|
+
readonly lockService: LockService;
|
|
227
|
+
/** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
|
|
228
|
+
private readonly sandbox;
|
|
229
|
+
constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost, schedules: Pick<ScheduledTaskStore, "addGrant">,
|
|
230
|
+
/** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
|
|
231
|
+
registry: Pick<ToolRegistry, "forAgent">,
|
|
232
|
+
/** 「这台设备一直允许」 sends the `policy` op to the device before the decision is recorded. */
|
|
233
|
+
policies: Pick<DevicePolicyService, "apply" | "summary">,
|
|
234
|
+
/** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
|
|
235
|
+
lockService: LockService,
|
|
236
|
+
/** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
|
|
237
|
+
sandbox: Pick<SandboxService, "ceilingLimits">);
|
|
136
238
|
/**
|
|
137
239
|
* 新建 pending 审批(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `approval_requested`,同一事务。
|
|
138
240
|
* 按 (run_id, call_id, attempt) 幂等:已存在则返回已有记录(不覆盖),事件按 `approval_requested:<approvalId>` 幂等。
|
|
139
241
|
*/
|
|
140
242
|
request(input: ApprovalRequestInput): Promise<ApprovalRecord>;
|
|
141
243
|
private requestIn;
|
|
244
|
+
/**
|
|
245
|
+
* A network ask (network ask-to-allow spec §6.2): a pending approval of kind `network` for the
|
|
246
|
+
* device owner, expiring like a tool approval (`tools.approvalTimeoutHours`), and its
|
|
247
|
+
* `approval_requested` (tool key `Bash`, the display-only `network.hosts`), in one transaction.
|
|
248
|
+
* It takes the attempt after the call's other approvals (P-D4: the command ran, so no tool
|
|
249
|
+
* approval of the call follows it). One per call: a second request returns the first.
|
|
250
|
+
*/
|
|
251
|
+
requestNetwork(input: NetworkAskInput): Promise<ApprovalRecord>;
|
|
252
|
+
private requestNetworkIn;
|
|
142
253
|
/** 当前用户能处理的审批(新的在前,最多 {@link APPROVALS_LIST_MAX} 条);`status` 缺省为全部。 */
|
|
143
254
|
list(orgId: string, accountId: string, status?: ApprovalStatus): Promise<ApprovalView[]>;
|
|
144
255
|
/** 详情;当前用户不能处理的一律 `approval.not_found`(不泄露存在)。 */
|
|
@@ -149,15 +260,61 @@ export declare class ApprovalsService {
|
|
|
149
260
|
* `always` → 发起人 × agent × 工具)、`approval_decided` 事件(幂等键 `approval_decided:<id>`)与审计;
|
|
150
261
|
* 提交后发布事件并调用 `onDecided`。
|
|
151
262
|
*/
|
|
152
|
-
decide(orgId: string, id: string, accountId: string, body: DecisionInput, actorTokenId?: string): Promise<ApprovalRecord>;
|
|
263
|
+
decide(orgId: string, id: string, accountId: string, body: DecisionInput, actorTokenId?: string, via?: DecisionVia): Promise<ApprovalRecord>;
|
|
264
|
+
/**
|
|
265
|
+
* A network ask's decision (network ask-to-allow spec §5.2), one at a time per ask across
|
|
266
|
+
* replicas: only the device owner (`requested_for`) decides, in a signed-in session (a personal
|
|
267
|
+
* access token is refused; the route already refuses service and embed tokens); the allowed
|
|
268
|
+
* hosts are a subset of those asked. 「这台设备一直允许」 sends the `policy` op first, outside any
|
|
269
|
+
* transaction; when the device cannot be reached or lacks the feature the decision is recorded
|
|
270
|
+
* as a run grant with the reason, and when the device refuses (its owner takes no remote grants)
|
|
271
|
+
* as `always` with outcome `refused` and nothing granted. Then {@link decideIn} records what took
|
|
272
|
+
* effect and the run resumes after the commit; a change the device applied after the ask closed
|
|
273
|
+
* is audited (`late`) and answered with a 409 that says so.
|
|
274
|
+
*/
|
|
275
|
+
private decideNetwork;
|
|
276
|
+
/** `device.network_allowed` of a policy change that reached the device after the ask closed. */
|
|
277
|
+
private auditLateAllow;
|
|
278
|
+
/** The owner of a live device of the org (null: none, deleted, or of another org). */
|
|
279
|
+
private deviceOwnerOf;
|
|
280
|
+
/**
|
|
281
|
+
* Adds the hosts to the device's local policy (the `policy` op; never inside a transaction).
|
|
282
|
+
* Null when it was applied, else why not: the decision falls back to a run grant.
|
|
283
|
+
*/
|
|
284
|
+
private applyAlways;
|
|
285
|
+
/** After the commit: the decision's events reach the page, the run's waits and IM delivery. */
|
|
286
|
+
private announceDecided;
|
|
287
|
+
/** An account's display name (null when unknown). */
|
|
288
|
+
private displayNameOf;
|
|
153
289
|
private decideIn;
|
|
290
|
+
/**
|
|
291
|
+
* A trial run of a scheduled task (scheduled tasks spec §7): an approval its creator granted, in
|
|
292
|
+
* any scope, is also added to the task's grants with the approved definition's hash, so its
|
|
293
|
+
* unattended runs do not ask again. Kept only by the rule `addGrants` applies
|
|
294
|
+
* (`grantRefusalOf`, against the task agent's tools now): never a role-escalated approval
|
|
295
|
+
* (`requestedFor` null, it stays `once`), one another account decided, one whose scopes never
|
|
296
|
+
* allowed remembering (an org / platform operation: "once" only), a per-call key
|
|
297
|
+
* (`Platform:<operation>`, a skill change among them), a fixed ask, a tool the org forbids (its key or the
|
|
298
|
+
* call's), or a tool the task's agent does not have. In the decision's transaction.
|
|
299
|
+
*/
|
|
300
|
+
private keepTrialGrant;
|
|
301
|
+
/**
|
|
302
|
+
* A trial run of a scheduled task (network ask-to-allow spec §8.1): the hosts its creator allowed
|
|
303
|
+
* for this run (「这次允许」) on a network ask about one of their devices become the task's
|
|
304
|
+
* `network:<host>` grants, each only when `networkGrantRefusal` allows it (exact host, within the
|
|
305
|
+
* org's effective egress, the device takes remote grants). Never for an ask another account
|
|
306
|
+
* decided or one asked of someone else. In the decision's transaction.
|
|
307
|
+
*/
|
|
308
|
+
private keepTrialNetworkGrant;
|
|
154
309
|
/**
|
|
155
310
|
* 详情,不做处理人校验(内核内部用:渠道出站投递组装 IM 审批卡片);不存在返回 null。
|
|
156
311
|
* 不得直接回给请求方——HTTP 接口用 {@link get}。
|
|
157
312
|
*/
|
|
158
313
|
describe(id: string): Promise<ApprovalView | null>;
|
|
159
|
-
/**
|
|
314
|
+
/** 某个工具调用最近一次的工具审批(attempt 最大者,网络询问除外);没有则返回 null。 */
|
|
160
315
|
forCall(runId: string, callId: string): Promise<ApprovalRecord | null>;
|
|
316
|
+
/** The network ask of a tool call (at most one per call); null when it has none. */
|
|
317
|
+
networkForCall(runId: string, callId: string): Promise<ApprovalRecord | null>;
|
|
161
318
|
/**
|
|
162
319
|
* 超时(等待机制的超时任务调用):仍 pending 且已到期 → 置 expired、写 `approval_decided{expired}`(处理人为 null)。
|
|
163
320
|
* 与人工决定都锁同一行,恰好一个生效。返回 `expired`(本次生效)/ `not_due`(尚未到期)/
|
|
@@ -182,7 +339,25 @@ export declare class ApprovalsService {
|
|
|
182
339
|
records: ApprovalRecord[];
|
|
183
340
|
seqs: Map<string, number>;
|
|
184
341
|
}>;
|
|
185
|
-
/**
|
|
342
|
+
/**
|
|
343
|
+
* In the caller's transaction (`mandatory`; it wrote `authorAccountId`'s message in
|
|
344
|
+
* `conversationId` and holds that conversation's row lock): the message skips the pending
|
|
345
|
+
* network asks it closes (network ask-to-allow spec §6.7), as it skips questions: the asks of
|
|
346
|
+
* active runs `authorAccountId` started, in the conversation and in its sub-agents'
|
|
347
|
+
* conversations at any depth (their asks are shown in the root). Each becomes `skipped` with
|
|
348
|
+
* `approval_decided{skipped}` by the author; access stays blocked. Returns the skipped records and
|
|
349
|
+
* the highest seq written per conversation; the caller notifies and emits
|
|
350
|
+
* `approval.decided{skipped}` after the commit, so the runs resume.
|
|
351
|
+
*/
|
|
352
|
+
skipNetworkIn(conversationId: string, authorAccountId: string): Promise<{
|
|
353
|
+
records: ApprovalRecord[];
|
|
354
|
+
seqs: Map<string, number>;
|
|
355
|
+
}>;
|
|
356
|
+
/** `conversationId` and its sub-agents' conversations at any depth (at most 16 levels). */
|
|
357
|
+
private subtreeOf;
|
|
358
|
+
/**
|
|
359
|
+
* 系统写的 `approval_decided`(超时 / 取消,处理人为 null;跳过,处理人为消息作者),在当前事务内;返回事件 seq。
|
|
360
|
+
*/
|
|
186
361
|
private decidedEvent;
|
|
187
362
|
/** 本人在该组织的「以后都允许」授权。 */
|
|
188
363
|
listGrants(orgId: string, accountId: string): Promise<ToolGrantView[]>;
|