@capybara-hq/kernel 0.1.0-rc.21 → 0.1.0-rc.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin.controller.d.ts +3 -3
- package/dist/admin/admin.controller.js +6 -2
- package/dist/admin/admin.schemas.d.ts +13 -0
- package/dist/agents/admin-agents.d.ts +16 -3
- package/dist/agents/admin-agents.js +65 -7
- package/dist/agents/agents.schemas.d.ts +24 -6
- package/dist/agents/agents.service.d.ts +4 -3
- package/dist/agents/agents.service.js +39 -26
- package/dist/agents/definition.d.ts +44 -6
- package/dist/agents/definition.js +86 -18
- package/dist/agents/from-template.d.ts +6 -3
- package/dist/agents/from-template.js +57 -44
- package/dist/agents/org-templates.js +97 -41
- package/dist/agents/templates.d.ts +6 -0
- package/dist/agents/templates.js +15 -1
- package/dist/background/launch.d.ts +43 -5
- package/dist/background/launch.js +88 -8
- package/dist/background/session.d.ts +14 -2
- package/dist/background/session.js +26 -4
- package/dist/background/status.d.ts +1 -1
- package/dist/background/status.js +3 -3
- package/dist/background/stop.d.ts +0 -2
- package/dist/background/stop.js +1 -21
- package/dist/catalog/admin-catalog.controller.d.ts +2 -4
- package/dist/catalog/admin-catalog.controller.js +2 -6
- package/dist/catalog/catalog-content.d.ts +5 -0
- package/dist/catalog/catalog-content.js +1 -0
- package/dist/catalog/catalog.module.js +3 -0
- package/dist/catalog/claude/report.d.ts +9 -2
- package/dist/catalog/claude/report.js +1 -0
- package/dist/catalog/claude/translate.js +1 -1
- package/dist/catalog/component-promotion.js +1 -2
- package/dist/catalog/component-state.d.ts +13 -1
- package/dist/catalog/component-state.js +14 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/marketplaces.service.js +2 -1
- package/dist/catalog/org-catalog.controller.d.ts +13 -3
- package/dist/catalog/org-catalog.controller.js +26 -4
- package/dist/catalog/org-catalog.schemas.d.ts +13 -0
- package/dist/catalog/org-catalog.schemas.js +34 -0
- package/dist/catalog/org-plugin-uploads.controller.js +0 -1
- package/dist/catalog/org-plugin-uploads.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.js +0 -3
- package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
- package/dist/catalog/platform-components.d.ts +0 -1
- package/dist/catalog/platform-components.js +3 -8
- package/dist/catalog/plugin-import.d.ts +0 -8
- package/dist/catalog/plugin-import.js +4 -35
- package/dist/catalog/plugin-install.d.ts +2 -2
- package/dist/catalog/plugin-install.js +2 -2
- package/dist/catalog/plugin-package.d.ts +7 -0
- package/dist/catalog/plugin-package.js +27 -1
- package/dist/catalog/plugin-upgrade.d.ts +5 -6
- package/dist/catalog/plugin-upgrade.js +5 -23
- package/dist/catalog/requests.service.d.ts +66 -10
- package/dist/catalog/requests.service.js +196 -15
- package/dist/channels/actions.d.ts +2 -0
- package/dist/channels/actions.js +19 -4
- package/dist/channels/cards.d.ts +16 -2
- package/dist/channels/cards.js +40 -7
- package/dist/channels/channels.module.js +3 -0
- package/dist/channels/outbound.d.ts +22 -1
- package/dist/channels/outbound.js +116 -7
- package/dist/channels/phrases.d.ts +25 -0
- package/dist/channels/phrases.js +19 -0
- package/dist/channels/threads.d.ts +12 -0
- package/dist/channels/threads.js +34 -0
- package/dist/config/schema.d.ts +23 -1
- package/dist/config/schema.js +107 -5
- package/dist/conversations/conversations.service.d.ts +42 -9
- package/dist/conversations/conversations.service.js +57 -25
- package/dist/conversations/events.d.ts +106 -11
- package/dist/conversations/events.js +121 -18
- package/dist/conversations/message-context.d.ts +6 -3
- package/dist/conversations/message-context.js +14 -10
- package/dist/conversations/queue.d.ts +15 -2
- package/dist/conversations/queue.js +22 -3
- package/dist/conversations/queue.service.d.ts +12 -6
- package/dist/conversations/queue.service.js +36 -13
- package/dist/db/kernel-schema.d.ts +105 -3
- package/dist/devices/conversation-devices.d.ts +8 -1
- package/dist/devices/conversation-devices.js +8 -0
- package/dist/devices/devices.controller.d.ts +21 -4
- package/dist/devices/devices.controller.js +46 -8
- package/dist/devices/devices.schemas.d.ts +9 -1
- package/dist/devices/devices.schemas.js +11 -3
- package/dist/devices/devices.service.d.ts +176 -4
- package/dist/devices/devices.service.js +347 -6
- package/dist/devices/dto.d.ts +56 -3
- package/dist/devices/dto.js +51 -1
- package/dist/devices/public-suffix.d.ts +6 -0
- package/dist/devices/public-suffix.js +79 -0
- package/dist/errors.d.ts +50 -5
- package/dist/errors.js +50 -5
- package/dist/evals/reconstruct.d.ts +18 -13
- package/dist/evals/reconstruct.js +50 -36
- package/dist/evals/recorded-tools.d.ts +7 -7
- package/dist/evals/recorded-tools.js +4 -3
- package/dist/evals/trajectory.d.ts +6 -1
- package/dist/evals/trajectory.js +7 -0
- package/dist/extensions/package.js +2 -2
- package/dist/http/api.module.js +5 -0
- package/dist/identity/settings.d.ts +53 -3
- package/dist/identity/settings.js +92 -9
- package/dist/inbox/inbox.module.js +2 -1
- package/dist/inbox/inbox.service.d.ts +19 -4
- package/dist/inbox/inbox.service.js +14 -3
- package/dist/infra/config/kernel-config.service.d.ts +7 -0
- package/dist/infra/config/kernel-config.service.js +31 -0
- package/dist/infra/events/kernel-events.d.ts +2 -1
- package/dist/local-devices/device-policy.service.d.ts +65 -0
- package/dist/local-devices/device-policy.service.js +217 -0
- package/dist/local-devices/local-device-gateway.d.ts +4 -2
- package/dist/local-devices/local-device-gateway.js +39 -11
- package/dist/local-devices/local-device-provider.d.ts +40 -3
- package/dist/local-devices/local-device-provider.js +109 -7
- package/dist/local-devices/local-devices.module.d.ts +2 -1
- package/dist/local-devices/local-devices.module.js +19 -3
- package/dist/local-devices/machines.d.ts +30 -3
- package/dist/local-devices/machines.js +79 -3
- package/dist/local-devices/network-grants.d.ts +104 -0
- package/dist/local-devices/network-grants.js +288 -0
- package/dist/local-devices/policy-summary.d.ts +20 -0
- package/dist/local-devices/policy-summary.js +63 -0
- package/dist/orgs/orgs.module.js +4 -10
- package/dist/orgs/roles.d.ts +1 -1
- package/dist/orgs/roles.js +4 -0
- package/dist/orgs/settings.controller.js +4 -3
- package/dist/orgs/settings.service.d.ts +172 -14
- package/dist/orgs/settings.service.js +229 -27
- package/dist/platform/exclusions.js +11 -1
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin-items.ops.js +1 -1
- package/dist/platform/ops/admin.ops.js +32 -7
- package/dist/platform/ops/agent-skills.ops.js +2 -2
- package/dist/platform/ops/agent-templates.ops.js +2 -2
- package/dist/platform/ops/agents.ops.js +11 -15
- package/dist/platform/ops/devices.ops.js +3 -1
- package/dist/platform/ops/inbox.ops.js +19 -0
- package/dist/platform/ops/org-settings.ops.js +15 -6
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/platform.ops.d.ts +9 -1
- package/dist/platform/ops/platform.ops.js +34 -12
- package/dist/platform/ops/requests.ops.js +3 -2
- package/dist/platform/ops/schedules.ops.d.ts +21 -0
- package/dist/platform/ops/schedules.ops.js +470 -0
- package/dist/platform/ops/skills.ops.js +3 -3
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +3 -0
- package/dist/runtime/context/build.js +13 -4
- package/dist/runtime/context/compaction.js +6 -3
- package/dist/runtime/context/device-paths.d.ts +43 -21
- package/dist/runtime/context/device-paths.js +104 -21
- package/dist/runtime/context/run-context.d.ts +9 -0
- package/dist/runtime/context/run-context.js +8 -0
- package/dist/runtime/context/skills.d.ts +14 -1
- package/dist/runtime/context/skills.js +36 -5
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +16 -5
- package/dist/runtime/run-finish.d.ts +6 -1
- package/dist/runtime/run-finish.js +16 -2
- package/dist/runtime/run-queue.js +21 -2
- package/dist/runtime/run-worker.js +14 -33
- package/dist/runtime/runtime.module.js +3 -0
- package/dist/runtime/subagents/definition.d.ts +32 -14
- package/dist/runtime/subagents/definition.js +106 -109
- package/dist/runtime/subagents/dispatch.js +4 -3
- package/dist/runtime/subagents/preamble.d.ts +2 -1
- package/dist/runtime/subagents/preamble.js +2 -1
- package/dist/runtime/waits.js +14 -4
- package/dist/sandbox/platform-tiers.d.ts +86 -0
- package/dist/sandbox/platform-tiers.js +97 -0
- package/dist/sandbox/sandbox.service.d.ts +121 -11
- package/dist/sandbox/sandbox.service.js +351 -30
- package/dist/sandbox/tiers.d.ts +98 -0
- package/dist/sandbox/tiers.js +104 -0
- package/dist/schedules/effective-settings.d.ts +16 -0
- package/dist/schedules/effective-settings.js +17 -0
- package/dist/schedules/errors.d.ts +8 -0
- package/dist/schedules/errors.js +8 -0
- package/dist/schedules/fire.d.ts +118 -0
- package/dist/schedules/fire.js +625 -0
- package/dist/schedules/grant-rules.d.ts +94 -0
- package/dist/schedules/grant-rules.js +126 -0
- package/dist/schedules/job-ids.d.ts +32 -0
- package/dist/schedules/job-ids.js +27 -0
- package/dist/schedules/pause.d.ts +9 -0
- package/dist/schedules/pause.js +33 -0
- package/dist/schedules/run-tracker.d.ts +81 -0
- package/dist/schedules/run-tracker.js +333 -0
- package/dist/schedules/scan.job.d.ts +64 -0
- package/dist/schedules/scan.job.js +303 -0
- package/dist/schedules/schedule-math.d.ts +31 -0
- package/dist/schedules/schedule-math.js +223 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
- package/dist/schedules/scheduled-tasks.service.js +1193 -0
- package/dist/schedules/schedules.controller.d.ts +68 -0
- package/dist/schedules/schedules.controller.js +242 -0
- package/dist/schedules/schedules.module.d.ts +16 -0
- package/dist/schedules/schedules.module.js +65 -0
- package/dist/schedules/schedules.schemas.d.ts +316 -0
- package/dist/schedules/schedules.schemas.js +186 -0
- package/dist/schedules/store.d.ts +81 -0
- package/dist/schedules/store.js +390 -0
- package/dist/schedules/store.module.d.ts +6 -0
- package/dist/schedules/store.module.js +21 -0
- package/dist/schedules/types.d.ts +108 -0
- package/dist/schedules/types.js +1 -0
- package/dist/schedules/unattended.d.ts +117 -0
- package/dist/schedules/unattended.js +228 -0
- package/dist/setup/oauth-token-probe.js +8 -4
- package/dist/skills/agent-skills.service.d.ts +4 -8
- package/dist/skills/agent-skills.service.js +18 -53
- package/dist/skills/catalog.d.ts +65 -62
- package/dist/skills/catalog.js +109 -206
- package/dist/skills/conversation-catalog.d.ts +3 -3
- package/dist/skills/conversation-catalog.js +5 -12
- package/dist/skills/invoke.d.ts +2 -2
- package/dist/skills/skills.controller.d.ts +3 -1
- package/dist/skills/skills.service.d.ts +38 -37
- package/dist/skills/skills.service.js +168 -107
- package/dist/skills/usable.d.ts +57 -8
- package/dist/skills/usable.js +172 -38
- package/dist/tools/approvals.controller.d.ts +4 -2
- package/dist/tools/approvals.controller.js +17 -2
- package/dist/tools/approvals.schemas.d.ts +1 -0
- package/dist/tools/approvals.schemas.js +3 -1
- package/dist/tools/approvals.service.d.ts +182 -7
- package/dist/tools/approvals.service.js +597 -13
- package/dist/tools/builtin-names.d.ts +25 -4
- package/dist/tools/builtin-names.js +45 -4
- package/dist/tools/builtins/builtin-tools.d.ts +8 -0
- package/dist/tools/builtins/builtin-tools.js +23 -2
- package/dist/tools/builtins/sandbox/bash.js +16 -7
- package/dist/tools/builtins/sandbox/common.d.ts +1 -1
- package/dist/tools/builtins/sandbox/common.js +33 -10
- package/dist/tools/builtins/sandbox/edit.js +7 -3
- package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
- package/dist/tools/builtins/sandbox/file-state.js +33 -3
- package/dist/tools/builtins/sandbox/write.js +8 -4
- package/dist/tools/builtins/schedules.d.ts +107 -0
- package/dist/tools/builtins/schedules.js +789 -0
- package/dist/tools/elicitation.d.ts +2 -0
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/fixed-ask.d.ts +7 -0
- package/dist/tools/fixed-ask.js +9 -0
- package/dist/tools/network-asks.d.ts +109 -0
- package/dist/tools/network-asks.js +221 -0
- package/dist/tools/pipeline.d.ts +53 -6
- package/dist/tools/pipeline.js +202 -13
- package/dist/tools/policy.d.ts +21 -8
- package/dist/tools/policy.js +63 -9
- package/dist/tools/registry.d.ts +10 -1
- package/dist/tools/tools.module.js +12 -0
- package/package.json +2 -2
- package/src/migrations/0113_scheduled_tasks.sql +108 -0
- package/src/migrations/0114_network_asks.sql +23 -0
- package/src/migrations/0115_network_requests.sql +28 -0
- package/src/migrations/0116_skills_default_on.sql +37 -0
- package/src/migrations/0117_sandbox_tiers.sql +11 -0
|
@@ -34,18 +34,88 @@ export function newTaskId() {
|
|
|
34
34
|
out += BASE32[b & 31];
|
|
35
35
|
return out;
|
|
36
36
|
}
|
|
37
|
-
/** The programs
|
|
38
|
-
const
|
|
37
|
+
/** The programs a session launch and the later polls and stops need (Linux, sandbox-fake). */
|
|
38
|
+
const REQUIRED_SESSION = ["bash", "setsid", "nohup", "timeout", "pgrep", "pkill", "base64"];
|
|
39
|
+
/** The programs a process group launch and the later polls and stops need (macOS). */
|
|
40
|
+
const REQUIRED_GROUP = ["bash", "perl", "nohup", "base64"];
|
|
39
41
|
/** A task id ({@link newTaskId}). */
|
|
40
42
|
export const TASK_ID_RE = /^bt_[a-z2-7]{6}$/;
|
|
43
|
+
/**
|
|
44
|
+
* A shell fragment that sets how a task's processes are found and signalled by its sid, defining
|
|
45
|
+
* `tk_alive <sid>` (status 0 while the task has a process) and `tk_kill <SIGNAL> <sid>` (0: sent,
|
|
46
|
+
* 1: no process left, anything else: a failure), and `pg` to the mode:
|
|
47
|
+
* - `-s`, the session, where `pgrep` has `-s` (Linux procps and busybox, the sandbox-fake shims:
|
|
48
|
+
* `pgrep -s 0`, the own session, exits 0 or 1): `pgrep -s` and `pkill -s`;
|
|
49
|
+
* - `-g`, the process group, elsewhere (macOS). Its `pgrep` has no `-s`, and inside the OS sandbox
|
|
50
|
+
* (Seatbelt) a command can neither list processes nor signal those of another command. So the
|
|
51
|
+
* task's process group (its id is the sid) is watched by its launcher ({@link GROUP_LAUNCHER}),
|
|
52
|
+
* which signals it on request: `tk_kill` appends the signal to `.capybara/tasks/g<sid>.ctl`, and
|
|
53
|
+
* `tk_alive` asks `kill(0, -sid)`, where "not permitted" (another command's group) still means
|
|
54
|
+
* the group exists. A group signal is one system call, so it misses no process forked meanwhile.
|
|
55
|
+
* Relative to the workspace root. Every script that asks for or signals a task's processes starts
|
|
56
|
+
* with it; the launch picks its launcher with the same test.
|
|
57
|
+
*/
|
|
58
|
+
export const SESSION_MODE_SCRIPT = [
|
|
59
|
+
'if pgrep -s 0 >/dev/null 2>&1 || [ "$?" -eq 1 ]; then pg=-s',
|
|
60
|
+
'tk_alive() { pgrep -s "$1" >/dev/null 2>&1; }',
|
|
61
|
+
'tk_kill() { pkill -"$1" -s "$2"; }',
|
|
62
|
+
"else pg=-g",
|
|
63
|
+
`tk_alive() { perl -e 'exit((kill(0, -$ARGV[0]) || $!{EPERM}) ? 0 : 1)' "$1"; }`,
|
|
64
|
+
`tk_kill() { tk_alive "$2" || return 1; echo "$1" >> ${TASKS_DIR}/g"$2".ctl || return 2; }`,
|
|
65
|
+
"fi",
|
|
66
|
+
].join("\n");
|
|
67
|
+
/**
|
|
68
|
+
* The process group launcher (macOS: {@link SESSION_MODE_SCRIPT}'s `-g`), a perl program run as
|
|
69
|
+
* `perl -e <this> <seconds> <task path>` from the workspace root. It makes a session of its own
|
|
70
|
+
* (leaving the launch exec's process group), starts `bash <task>.sh` as the leader of a new process
|
|
71
|
+
* group (the task; its id, the sid, goes to `<task>.sid` through a rename) and watches it every
|
|
72
|
+
* 0.1 s, outside the group:
|
|
73
|
+
* - the command exits: its status as the shell gives it (`128 + signal` when killed) goes to
|
|
74
|
+
* `<task>.exit`, and what it left running in the group is SIGKILLed (the task ends when its
|
|
75
|
+
* command exits, as the poll does on Linux);
|
|
76
|
+
* - the time limit passes: 137 goes to `<task>.exit`, then the group is SIGKILLed (as
|
|
77
|
+
* `timeout -s KILL`);
|
|
78
|
+
* - a signal is requested in `g<sid>.ctl` ({@link SESSION_MODE_SCRIPT}'s `tk_kill`, read through
|
|
79
|
+
* a rename): the group gets it, and no exit file is written any more (a stop, as on Linux).
|
|
80
|
+
* It exits once the group is empty. Contains no single quote (it goes between single quotes).
|
|
81
|
+
*/
|
|
82
|
+
export const GROUP_LAUNCHER = [
|
|
83
|
+
"use POSIX;",
|
|
84
|
+
"my ($n, $t) = @ARGV;",
|
|
85
|
+
"my ($d) = $t =~ m{^(.*)/};",
|
|
86
|
+
'sub put { my ($f, $v) = @_; open(my $h, ">", "$f.tmp") or return; print $h "$v\\n"; close($h); rename("$f.tmp", $f) }',
|
|
87
|
+
'POSIX::setsid() or die "setsid: $!\\n";',
|
|
88
|
+
"my $c = fork();",
|
|
89
|
+
'defined $c or die "fork: $!\\n";',
|
|
90
|
+
'if ($c == 0) { setpgid(0, 0); exec("bash", "$t.sh") or POSIX::_exit(127) }',
|
|
91
|
+
"setpgid($c, $c);",
|
|
92
|
+
'put("$t.sid", $c);',
|
|
93
|
+
'my $ctl = "$d/g$c.ctl";',
|
|
94
|
+
"my $end = time() + $n;",
|
|
95
|
+
"my ($code, $done);",
|
|
96
|
+
'sub fin { put("$t.exit", $_[0]) unless $done; $done = 1 }',
|
|
97
|
+
"while (1) {",
|
|
98
|
+
' if (rename($ctl, "$ctl.$$")) { open(my $h, "<", "$ctl.$$"); my @s = grep { /^[A-Z0-9]+$/ } map { s/\\s+//gr } <$h>; close($h); unlink("$ctl.$$"); $done = 1; kill($_, -$c) for @s }',
|
|
99
|
+
' if (!defined $code && waitpid($c, WNOHANG) == $c) { $code = ($? & 127) ? 128 + ($? & 127) : $? >> 8; fin($code); kill("KILL", -$c) }',
|
|
100
|
+
' if (!defined $code && time() >= $end) { fin(137); kill("KILL", -$c) }',
|
|
101
|
+
" last if defined $code && !kill(0, -$c);",
|
|
102
|
+
" select(undef, undef, undef, 0.1);",
|
|
103
|
+
"}",
|
|
104
|
+
].join(" ");
|
|
41
105
|
/**
|
|
42
106
|
* The shell of the launch exec: writes `command` (base64 in the script, so nothing is quoted by
|
|
43
107
|
* hand) to `.capybara/tasks/<id>.sh` and starts it with bash (the shell a foreground `Bash` call
|
|
44
|
-
* runs its command in)
|
|
45
|
-
* status written to `<id>.exit` and its output to
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
108
|
+
* runs its command in), detached from the exec under a hard time limit of `seconds` that SIGKILLs
|
|
109
|
+
* every process of the task, with its exit status written to `<id>.exit` and its output to
|
|
110
|
+
* `<id>.out`; the task's sid goes to `<id>.sid` and is printed. Where `pgrep` has `-s`
|
|
111
|
+
* ({@link SESSION_MODE_SCRIPT}) the command runs in a session of its own under
|
|
112
|
+
* `setsid nohup sh -c 'timeout -s KILL <seconds> …'` and the sid is `$!`, the pid `setsid` started;
|
|
113
|
+
* elsewhere (macOS: no `setsid`, no `timeout`, no `pgrep -s`) {@link GROUP_LAUNCHER} runs it in a
|
|
114
|
+
* process group of its own, watched by the launcher, and writes the sid (the group id) itself,
|
|
115
|
+
* which the launch waits for (up to 10 s). An
|
|
116
|
+
* environment without one of the needed programs fails with a readable error on stderr and exit
|
|
117
|
+
* code 127, before anything starts. `id` must be a task id and `seconds` a positive integer (both
|
|
118
|
+
* go into the script unquoted).
|
|
49
119
|
*/
|
|
50
120
|
export function launchScript(id, command, seconds) {
|
|
51
121
|
if (!TASK_ID_RE.test(id))
|
|
@@ -55,15 +125,25 @@ export function launchScript(id, command, seconds) {
|
|
|
55
125
|
}
|
|
56
126
|
const b64 = Buffer.from(command, "utf8").toString("base64");
|
|
57
127
|
const t = `${TASKS_DIR}/${id}`;
|
|
128
|
+
const need = (list) => `for c in ${list.join(" ")}; do command -v "$c" >/dev/null 2>&1 || { echo "Background tasks need ${list.join(", ")} in this environment; $c is missing." >&2; exit 127; }; done`;
|
|
58
129
|
return [
|
|
59
130
|
"set -e",
|
|
60
|
-
|
|
131
|
+
SESSION_MODE_SCRIPT,
|
|
132
|
+
`if [ "$pg" = -s ]; then ${need(REQUIRED_SESSION)}; else ${need(REQUIRED_GROUP)}; fi`,
|
|
61
133
|
`mkdir -p ${TASKS_DIR}`,
|
|
62
134
|
`printf '%s' '${b64}' | base64 -d > ${t}.sh`,
|
|
135
|
+
`if [ "$pg" = -s ]; then`,
|
|
63
136
|
`setsid nohup sh -c 'timeout -s KILL ${seconds} bash ${t}.sh; echo $? > ${t}.exit' > ${t}.out 2>&1 < /dev/null &`,
|
|
64
137
|
// the session id goes to a file first: a launch that ends before its output comes back
|
|
65
138
|
// (aborted, timed out) still leaves the id of what it started
|
|
66
139
|
`echo $! > ${t}.sid`,
|
|
140
|
+
"else",
|
|
141
|
+
`rm -f ${t}.sid`,
|
|
142
|
+
`nohup perl -e '${GROUP_LAUNCHER}' ${seconds} ${t} > ${t}.out 2>&1 < /dev/null &`,
|
|
143
|
+
// the launcher writes the group id once the group exists
|
|
144
|
+
`i=0; while [ ! -s ${t}.sid ] && [ "$i" -lt 200 ]; do sleep 0.05; i=$((i+1)); done`,
|
|
145
|
+
`[ -s ${t}.sid ] || { echo "The background task did not start: no process group id after 10 s." >&2; exit 1; }`,
|
|
146
|
+
"fi",
|
|
67
147
|
`cat ${t}.sid`,
|
|
68
148
|
].join("\n");
|
|
69
149
|
}
|
|
@@ -4,7 +4,8 @@ import type { HeldProvider, TaggedProvider } from "../sandbox/sandbox.service.js
|
|
|
4
4
|
* A background task's session inside the sandbox (spec 2026-11-05-background-bash-design §4.2):
|
|
5
5
|
* every process of a task is in the session `setsid` started, so liveness is `pgrep -s <sid>` and
|
|
6
6
|
* stopping is `pkill -s <sid>` (never `kill -0` or `/proc`, which disagree between sandbox-fake and
|
|
7
|
-
* Linux).
|
|
7
|
+
* Linux). macOS has no `setsid` and its `pgrep` / `pkill` no `-s`: there the task is the process
|
|
8
|
+
* group the launch made (`launchScript`), signalled with `kill -- -<sid>` ({@link SESSION_MODE_SCRIPT}).
|
|
8
9
|
*/
|
|
9
10
|
/**
|
|
10
11
|
* `p` with every `exec` carrying `actor` (`SandboxService.ifRunning` tags execs but leaves the
|
|
@@ -27,7 +28,8 @@ export declare function assertTaskRef(id: string, sid: string): void;
|
|
|
27
28
|
* session is empty (pkill exits 1: no process left): a process may fork between pkill's listing
|
|
28
29
|
* and its signals, and the child would live on. Sets `ks` to 0 when the session was confirmed
|
|
29
30
|
* empty, else to pkill's failing status (3 when it was still not empty after 10 tries). Uses only
|
|
30
|
-
* the variables `ks`, `kc` and `ki`. `sid` must be
|
|
31
|
+
* the variables and functions of {@link SESSION_MODE_SCRIPT} (it starts with it), `ks`, `kc` and `ki`. `sid` must be
|
|
32
|
+
* a session id.
|
|
31
33
|
*/
|
|
32
34
|
export declare function killSessionScript(sid: string): string;
|
|
33
35
|
/**
|
|
@@ -37,6 +39,16 @@ export declare function killSessionScript(sid: string): string;
|
|
|
37
39
|
* empty; false when it could not be (an error is never thrown).
|
|
38
40
|
*/
|
|
39
41
|
export declare function killSession(p: Pick<HeldProvider, "exec">, h: SandboxHandle, sid: string): Promise<boolean>;
|
|
42
|
+
/** How long a stop waits after SIGTERM before SIGKILL (spec §2.3). */
|
|
43
|
+
export declare const STOP_GRACE_MS = 5000;
|
|
44
|
+
/** The time limit of the SIGTERM exec: the grace period plus some room. */
|
|
45
|
+
export declare const TERM_EXEC_TIMEOUT_MS: number;
|
|
46
|
+
/**
|
|
47
|
+
* Exit code 0 with `exited <code>` when the task had already finished (its exit file exists),
|
|
48
|
+
* else sends SIGTERM to the session and waits up to {@link STOP_GRACE_MS} for it to empty, then
|
|
49
|
+
* prints `term`. Relative to the workspace root.
|
|
50
|
+
*/
|
|
51
|
+
export declare function termScript(id: string, sid: string): string;
|
|
40
52
|
/** The longest `lastLines` of a finished task (spec §2.4). */
|
|
41
53
|
export declare const LAST_LINES_MAX_CHARS = 4000;
|
|
42
54
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { rootsOf } from "@capybara-hq/plugin-sdk";
|
|
2
|
-
import { TASK_ID_RE, TASKS_DIR } from "./launch.js";
|
|
2
|
+
import { SESSION_MODE_SCRIPT, TASK_ID_RE, TASKS_DIR } from "./launch.js";
|
|
3
3
|
/**
|
|
4
4
|
* A background task's session inside the sandbox (spec 2026-11-05-background-bash-design §4.2):
|
|
5
5
|
* every process of a task is in the session `setsid` started, so liveness is `pgrep -s <sid>` and
|
|
6
6
|
* stopping is `pkill -s <sid>` (never `kill -0` or `/proc`, which disagree between sandbox-fake and
|
|
7
|
-
* Linux).
|
|
7
|
+
* Linux). macOS has no `setsid` and its `pgrep` / `pkill` no `-s`: there the task is the process
|
|
8
|
+
* group the launch made (`launchScript`), signalled with `kill -- -<sid>` ({@link SESSION_MODE_SCRIPT}).
|
|
8
9
|
*/
|
|
9
10
|
/**
|
|
10
11
|
* `p` with every `exec` carrying `actor` (`SandboxService.ifRunning` tags execs but leaves the
|
|
@@ -35,12 +36,13 @@ export function assertTaskRef(id, sid) {
|
|
|
35
36
|
* session is empty (pkill exits 1: no process left): a process may fork between pkill's listing
|
|
36
37
|
* and its signals, and the child would live on. Sets `ks` to 0 when the session was confirmed
|
|
37
38
|
* empty, else to pkill's failing status (3 when it was still not empty after 10 tries). Uses only
|
|
38
|
-
* the variables `ks`, `kc` and `ki`. `sid` must be
|
|
39
|
+
* the variables and functions of {@link SESSION_MODE_SCRIPT} (it starts with it), `ks`, `kc` and `ki`. `sid` must be
|
|
40
|
+
* a session id.
|
|
39
41
|
*/
|
|
40
42
|
export function killSessionScript(sid) {
|
|
41
43
|
if (!SID_RE.test(sid))
|
|
42
44
|
throw new Error(`invalid background task session: ${JSON.stringify(sid)}`);
|
|
43
|
-
return
|
|
45
|
+
return `${SESSION_MODE_SCRIPT}\nks=3; for ki in 1 2 3 4 5 6 7 8 9 10; do tk_kill KILL ${sid}; kc=$?; if [ "$kc" -eq 1 ]; then ks=0; break; fi; if [ "$kc" -ne 0 ]; then ks=$kc; break; fi; sleep 0.2; done`;
|
|
44
46
|
}
|
|
45
47
|
/**
|
|
46
48
|
* Kills every process of the session `sid` ({@link killSessionScript}) in an exec of its own. Any
|
|
@@ -62,6 +64,26 @@ export async function killSession(p, h, sid) {
|
|
|
62
64
|
.catch(() => undefined);
|
|
63
65
|
return r?.exitCode === 0;
|
|
64
66
|
}
|
|
67
|
+
/** How long a stop waits after SIGTERM before SIGKILL (spec §2.3). */
|
|
68
|
+
export const STOP_GRACE_MS = 5000;
|
|
69
|
+
/** The time limit of the SIGTERM exec: the grace period plus some room. */
|
|
70
|
+
export const TERM_EXEC_TIMEOUT_MS = STOP_GRACE_MS + 5000;
|
|
71
|
+
/**
|
|
72
|
+
* Exit code 0 with `exited <code>` when the task had already finished (its exit file exists),
|
|
73
|
+
* else sends SIGTERM to the session and waits up to {@link STOP_GRACE_MS} for it to empty, then
|
|
74
|
+
* prints `term`. Relative to the workspace root.
|
|
75
|
+
*/
|
|
76
|
+
export function termScript(id, sid) {
|
|
77
|
+
const f = `${TASKS_DIR}/${id}`;
|
|
78
|
+
const tries = Math.ceil(STOP_GRACE_MS / 200);
|
|
79
|
+
return [
|
|
80
|
+
`if [ -s ${f}.exit ]; then echo "exited $(cat ${f}.exit)"; exit 0; fi`,
|
|
81
|
+
SESSION_MODE_SCRIPT,
|
|
82
|
+
`tk_kill TERM ${sid} >/dev/null 2>&1`,
|
|
83
|
+
`i=0; while [ "$i" -lt ${tries} ] && tk_alive ${sid}; do sleep 0.2; i=$((i+1)); done`,
|
|
84
|
+
"echo term",
|
|
85
|
+
].join("\n");
|
|
86
|
+
}
|
|
65
87
|
/** The most bytes of a task's output end a tail reads (the event keeps at most 4 000 characters). */
|
|
66
88
|
const TAIL_BYTES = 12_000;
|
|
67
89
|
/** The longest `lastLines` of a finished task (spec §2.4). */
|
|
@@ -5,7 +5,7 @@ export interface TaskReport {
|
|
|
5
5
|
/** The exit file's code; null while there is none. */
|
|
6
6
|
exitCode: number | null;
|
|
7
7
|
/**
|
|
8
|
-
* Whether the session still has a process: `pgrep -s <sid
|
|
8
|
+
* Whether the session still has a process: `pgrep -s <sid>` (`-g` on macOS), or, for a task whose command
|
|
9
9
|
* exited, whether its leftover processes could not be confirmed killed.
|
|
10
10
|
*/
|
|
11
11
|
alive: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TASK_ID_RE, TASKS_DIR } from "./launch.js";
|
|
1
|
+
import { SESSION_MODE_SCRIPT, TASK_ID_RE, TASKS_DIR } from "./launch.js";
|
|
2
2
|
import { decodeTail, killSessionScript, SID_RE, tailCommand } from "./session.js";
|
|
3
3
|
import { exitedAs } from "./stop.js";
|
|
4
4
|
/**
|
|
@@ -17,7 +17,7 @@ export function pollScript(tasks) {
|
|
|
17
17
|
}
|
|
18
18
|
const f = `${TASKS_DIR}/${id}`;
|
|
19
19
|
return [
|
|
20
|
-
`if
|
|
20
|
+
`if tk_alive ${sid}; then a=1; else a=0; fi`,
|
|
21
21
|
`if [ -s ${f}.exit ]; then c=$(tr -dc '0-9-' < ${f}.exit); else c=-; fi`,
|
|
22
22
|
`[ -n "$c" ] || c=-`,
|
|
23
23
|
`if [ "$c" != - ] && [ "$a" = 1 ]; then ${killSessionScript(sid)}; [ "$ks" = 0 ] && a=0; fi`,
|
|
@@ -25,7 +25,7 @@ export function pollScript(tasks) {
|
|
|
25
25
|
`echo "${id} $c $a \${t:--}"`,
|
|
26
26
|
].join("\n");
|
|
27
27
|
});
|
|
28
|
-
return lines.join("\n");
|
|
28
|
+
return [SESSION_MODE_SCRIPT, ...lines].join("\n");
|
|
29
29
|
}
|
|
30
30
|
/** Parses {@link pollScript}'s output; a task missing from it is absent from the map. */
|
|
31
31
|
export function parsePollOutput(stdout) {
|
|
@@ -5,8 +5,6 @@ import { type BackgroundFinish, BackgroundTaskFinisher } from "./finish.js";
|
|
|
5
5
|
import { type BackgroundTaskRow, BackgroundTaskStore } from "./store.js";
|
|
6
6
|
/** Why a task is stopped. */
|
|
7
7
|
export type StopReason = "tool" | "person" | "subagent_end" | "conversation_deleted";
|
|
8
|
-
/** How long a stop waits after SIGTERM before SIGKILL (spec §2.3). */
|
|
9
|
-
export declare const STOP_GRACE_MS = 5000;
|
|
10
8
|
/** How a task that exited by itself ends (spec §4.3): exit 137 past its deadline timed out. */
|
|
11
9
|
export declare function exitedAs(task: BackgroundTaskRow, code: number, now: Date): BackgroundFinish;
|
|
12
10
|
/** How a task whose sandbox is not running ends (`ifRunning`'s `not_running` status). */
|
package/dist/background/stop.js
CHANGED
|
@@ -13,30 +13,10 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
13
13
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
14
14
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
15
15
|
import { BackgroundTaskFinisher, } from "./finish.js";
|
|
16
|
-
import {
|
|
17
|
-
import { actingAs, assertTaskRef, decodeTail, killSession, tailCommand, taskActor, } from "./session.js";
|
|
16
|
+
import { actingAs, assertTaskRef, decodeTail, killSession, TERM_EXEC_TIMEOUT_MS, tailCommand, taskActor, termScript, } from "./session.js";
|
|
18
17
|
import { BackgroundTaskStore } from "./store.js";
|
|
19
|
-
/** How long a stop waits after SIGTERM before SIGKILL (spec §2.3). */
|
|
20
|
-
export const STOP_GRACE_MS = 5000;
|
|
21
|
-
/** The time limit of the SIGTERM exec: the grace period plus some room. */
|
|
22
|
-
const TERM_EXEC_TIMEOUT_MS = STOP_GRACE_MS + 5000;
|
|
23
18
|
/** The time limit of reading the last lines after a stop. */
|
|
24
19
|
const TAIL_EXEC_TIMEOUT_MS = 10_000;
|
|
25
|
-
/**
|
|
26
|
-
* Exit code 0 with `exited <code>` when the task had already finished (its exit file exists),
|
|
27
|
-
* else sends SIGTERM to the session and waits up to {@link STOP_GRACE_MS} for it to empty, then
|
|
28
|
-
* prints `term`. Relative to the workspace root.
|
|
29
|
-
*/
|
|
30
|
-
function termScript(id, sid) {
|
|
31
|
-
const f = `${TASKS_DIR}/${id}`;
|
|
32
|
-
const tries = Math.ceil(STOP_GRACE_MS / 200);
|
|
33
|
-
return [
|
|
34
|
-
`if [ -s ${f}.exit ]; then echo "exited $(cat ${f}.exit)"; exit 0; fi`,
|
|
35
|
-
`pkill -TERM -s ${sid} >/dev/null 2>&1`,
|
|
36
|
-
`i=0; while [ "$i" -lt ${tries} ] && pgrep -s ${sid} >/dev/null 2>&1; do sleep 0.2; i=$((i+1)); done`,
|
|
37
|
-
"echo term",
|
|
38
|
-
].join("\n");
|
|
39
|
-
}
|
|
40
20
|
/** How a task that exited by itself ends (spec §4.3): exit 137 past its deadline timed out. */
|
|
41
21
|
export function exitedAs(task, code, now) {
|
|
42
22
|
if (code === 0)
|
|
@@ -21,7 +21,6 @@ declare const ConfirmBody: z.ZodObject<{
|
|
|
21
21
|
optional: "optional";
|
|
22
22
|
}>>;
|
|
23
23
|
orgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
24
|
-
pinnedSuggestions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
25
24
|
}, z.core.$strict>;
|
|
26
25
|
declare const ConfirmPermissionsBody: z.ZodObject<{
|
|
27
26
|
permissionsHash: z.ZodString;
|
|
@@ -105,9 +104,8 @@ export declare class AdminCatalogController {
|
|
|
105
104
|
* by sha256). An unknown field (the removed `deployExtension`) -> 400 `validation_failed`. A foreseeable failure (a downgrade to a version with
|
|
106
105
|
* unrun migrations) is a 409 `extension.downgrade_unsupported` before anything is written.
|
|
107
106
|
* Staging missing or expired -> 404 `plugin.staging_not_found`; `reviewHash` differs or the
|
|
108
|
-
* extension on the deployment changed -> 409 `plugin.review_stale`. `pinnedSuggestions`
|
|
109
|
-
*
|
|
110
|
-
* `validation_failed`.
|
|
107
|
+
* extension on the deployment changed -> 409 `plugin.review_stale`. The removed `pinnedSuggestions`
|
|
108
|
+
* is an unknown field (400 `validation_failed`).
|
|
111
109
|
*/
|
|
112
110
|
confirm(principal: Principal, stagingId: string, body: z.infer<typeof ConfirmBody>): Promise<PluginConfirmResult>;
|
|
113
111
|
/**
|
|
@@ -31,8 +31,6 @@ const ConfirmBody = z
|
|
|
31
31
|
/** The new item's scope (default `optional`); `orgs` only with `named`. */
|
|
32
32
|
scope: Scope.optional(),
|
|
33
33
|
orgs: Orgs.optional(),
|
|
34
|
-
/** Skill names suggested as pinned (spec §9.1); omitted keeps the plugin's flags. */
|
|
35
|
-
pinnedSuggestions: z.array(z.string().min(1).max(64)).max(50).optional(),
|
|
36
34
|
})
|
|
37
35
|
.strict();
|
|
38
36
|
const ConfirmPermissionsBody = z.object({ permissionsHash: Hash }).strict();
|
|
@@ -160,9 +158,8 @@ let AdminCatalogController = class AdminCatalogController {
|
|
|
160
158
|
* by sha256). An unknown field (the removed `deployExtension`) -> 400 `validation_failed`. A foreseeable failure (a downgrade to a version with
|
|
161
159
|
* unrun migrations) is a 409 `extension.downgrade_unsupported` before anything is written.
|
|
162
160
|
* Staging missing or expired -> 404 `plugin.staging_not_found`; `reviewHash` differs or the
|
|
163
|
-
* extension on the deployment changed -> 409 `plugin.review_stale`. `pinnedSuggestions`
|
|
164
|
-
*
|
|
165
|
-
* `validation_failed`.
|
|
161
|
+
* extension on the deployment changed -> 409 `plugin.review_stale`. The removed `pinnedSuggestions`
|
|
162
|
+
* is an unknown field (400 `validation_failed`).
|
|
166
163
|
*/
|
|
167
164
|
confirm(principal, stagingId, body) {
|
|
168
165
|
const actor = requireUser(principal);
|
|
@@ -170,7 +167,6 @@ let AdminCatalogController = class AdminCatalogController {
|
|
|
170
167
|
reviewHash: body.reviewHash,
|
|
171
168
|
...(body.scope !== undefined && { scope: body.scope }),
|
|
172
169
|
...(body.orgs !== undefined && { orgs: body.orgs }),
|
|
173
|
-
...(body.pinnedSuggestions !== undefined && { pinnedSuggestions: body.pinnedSuggestions }),
|
|
174
170
|
}, actorOpts(actor));
|
|
175
171
|
}
|
|
176
172
|
/**
|
|
@@ -72,6 +72,11 @@ export interface OrgCatalogTemplate {
|
|
|
72
72
|
editableEndpoint: string[];
|
|
73
73
|
auth: Record<string, unknown>;
|
|
74
74
|
ownership: "org" | "personal" | null;
|
|
75
|
+
/**
|
|
76
|
+
* The template file says `defaultEnabled: false`: it starts switched off in every org, so the
|
|
77
|
+
* install wizard does not ask for it (an org admin sets it up when switching it on).
|
|
78
|
+
*/
|
|
79
|
+
defaultOff: boolean;
|
|
75
80
|
/**
|
|
76
81
|
* The template's setup guide in the item's latest version (`GET …/catalog/:itemId/guides/
|
|
77
82
|
* :guideId`), null without one (spec 2026-11-09-setup-guides-v2-design.md §9.3).
|
|
@@ -9,6 +9,7 @@ import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
|
9
9
|
import { SuperAdminLockModule } from "../identity/super-admin.js";
|
|
10
10
|
import { QueueModule } from "../infra/queue/queue.module.js";
|
|
11
11
|
import { OrgLockModule } from "../orgs/org-lock.js";
|
|
12
|
+
import { OrgSettingsModule } from "../orgs/settings.service.js";
|
|
12
13
|
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
13
14
|
import { SkillsModule } from "../skills/skills.module.js";
|
|
14
15
|
import { StorageModule } from "../storage/storage.module.js";
|
|
@@ -46,6 +47,8 @@ CatalogModule = __decorate([
|
|
|
46
47
|
ExtensionsModule,
|
|
47
48
|
SuperAdminLockModule,
|
|
48
49
|
OrgLockModule,
|
|
50
|
+
// approving a network request writes the org's sandboxEgress
|
|
51
|
+
OrgSettingsModule,
|
|
49
52
|
StorageModule,
|
|
50
53
|
SkillsModule,
|
|
51
54
|
ToolsModule,
|
|
@@ -27,7 +27,14 @@ export type TranslationCode = "skill_imported" | "agent_imported"
|
|
|
27
27
|
/** Phase D: a command that cannot become a skill (`detail.problem`). */
|
|
28
28
|
| "command_invalid"
|
|
29
29
|
/** Phase B only (agents were not imported); no longer emitted, kept for stored reports. */
|
|
30
|
-
| "agent_needs_subagents" | "agent_invalid" | "agent_field_ignored" | "hooks_not_run" | "hooks_module" | "mcp_unsupported_transport" | "mcp_invalid" | "skill_invalid" | "not_applicable" | "unknown_field" | "other_harness" | "skill_ignored_restrictions" | "skill_claude_variables"
|
|
30
|
+
| "agent_needs_subagents" | "agent_invalid" | "agent_field_ignored" | "hooks_not_run" | "hooks_module" | "mcp_unsupported_transport" | "mcp_invalid" | "skill_invalid" | "not_applicable" | "unknown_field" | "other_harness" | "skill_ignored_restrictions" | "skill_claude_variables"
|
|
31
|
+
/**
|
|
32
|
+
* The plugin runs a SessionStart hook (Capybara runs no hooks; its skills are in every agent's
|
|
33
|
+
* catalog; skills default-on spec §8).
|
|
34
|
+
*/
|
|
35
|
+
| "session_start_hook"
|
|
36
|
+
/** Before skills default-on (the pinned suggestion); no longer emitted, kept for stored reports. */
|
|
37
|
+
| "session_start_pin_hint" | "mcp_auth_none" | "mcp_type_assumed" | "connector_not_loaded" | "agent_tools_unmapped"
|
|
31
38
|
/** A pattern in `disallowedTools` disallows its whole tool (narrower, never wider). */
|
|
32
39
|
| "agent_tools_narrowed" | "agent_skills_truncated"
|
|
33
40
|
/** Phase D: the body has `!`command`` / ```! shell injections, which stay text. */
|
|
@@ -55,7 +62,7 @@ export interface TranslationReport {
|
|
|
55
62
|
format: "claude-code";
|
|
56
63
|
/** `.claude-plugin/plugin.json`, or null when the tree has none. */
|
|
57
64
|
manifestPath: string | null;
|
|
58
|
-
/** The plugin has a SessionStart hook
|
|
65
|
+
/** The plugin has a SessionStart hook (the report then has a `session_start_hook` item). */
|
|
59
66
|
sessionStart: boolean;
|
|
60
67
|
/** Items sorted by group order, then component, then name. At most {@link CLAUDE_REPORT_MAX_ITEMS} (then one "unknown_field" item named "…" with detail { more: n }). */
|
|
61
68
|
items: TranslationItem[];
|
|
@@ -42,6 +42,7 @@ export const TRANSLATION_CODE_GROUP = {
|
|
|
42
42
|
other_harness: "ignored",
|
|
43
43
|
skill_ignored_restrictions: "warnings",
|
|
44
44
|
skill_claude_variables: "warnings",
|
|
45
|
+
session_start_hook: "warnings",
|
|
45
46
|
session_start_pin_hint: "warnings",
|
|
46
47
|
mcp_auth_none: "warnings",
|
|
47
48
|
mcp_type_assumed: "warnings",
|
|
@@ -702,7 +702,7 @@ export function translateClaudePlugin(files, o) {
|
|
|
702
702
|
add(item("hooks_not_run", "hook", "…", { detail: { more: moreHooks } }));
|
|
703
703
|
const sessionStart = events.has("SessionStart");
|
|
704
704
|
if (sessionStart)
|
|
705
|
-
add(item("
|
|
705
|
+
add(item("session_start_hook", "hook", "SessionStart"));
|
|
706
706
|
// MCP servers: .mcp.json first, then each declaration; a later name replaces an earlier one.
|
|
707
707
|
// A Map: an untrusted "__proto__" key must never touch an object's prototype.
|
|
708
708
|
const servers = new Map();
|
|
@@ -172,7 +172,7 @@ let ComponentPromotion = class ComponentPromotion {
|
|
|
172
172
|
if (p.kind === "skill") {
|
|
173
173
|
const v = await this.db
|
|
174
174
|
.selectFrom("skill_versions")
|
|
175
|
-
.select(["manifest", "source_format", "
|
|
175
|
+
.select(["manifest", "source_format", "created_by"])
|
|
176
176
|
.where("skill_id", "=", row.id)
|
|
177
177
|
.where("version", "=", row.version)
|
|
178
178
|
.executeTakeFirstOrThrow();
|
|
@@ -198,7 +198,6 @@ let ComponentPromotion = class ComponentPromotion {
|
|
|
198
198
|
object_prefix: p.prefix,
|
|
199
199
|
catalog_version: null,
|
|
200
200
|
source_format: v.source_format,
|
|
201
|
-
suggest_pinned: v.suggest_pinned,
|
|
202
201
|
created_by: v.created_by,
|
|
203
202
|
})
|
|
204
203
|
.execute();
|
|
@@ -110,6 +110,15 @@ export declare function loadScopeState(db: KernelSchemaDb, kind: ComponentKind,
|
|
|
110
110
|
id: string;
|
|
111
111
|
itemId: string | null;
|
|
112
112
|
}[]): Promise<ScopeState>;
|
|
113
|
+
/**
|
|
114
|
+
* Whether a catalog item is builtin: marked so and owned by the platform. An org-owned item marked
|
|
115
|
+
* builtin (never so in practice) is an ordinary item: never on without an install, never locked,
|
|
116
|
+
* and its skills can be turned off (skills default-on spec §3).
|
|
117
|
+
*/
|
|
118
|
+
export declare const platformBuiltin: (item: {
|
|
119
|
+
builtin: boolean | null;
|
|
120
|
+
owner_org_id: string | null;
|
|
121
|
+
}) => boolean;
|
|
113
122
|
/** The plugin facts of `itemId` for one org from a {@link ScopeState} (null: no plugin). */
|
|
114
123
|
export declare function pluginFactsOf(st: ScopeState, itemId: string | null, orgId: string): PluginFacts | null;
|
|
115
124
|
/** The org's switch of a component from a {@link ScopeState}. */
|
|
@@ -123,7 +132,10 @@ export interface DefinitionRefs {
|
|
|
123
132
|
* one of `orgConnectors` it does not turn off as a whole.
|
|
124
133
|
*/
|
|
125
134
|
connectors: Set<string>;
|
|
126
|
-
/**
|
|
135
|
+
/**
|
|
136
|
+
* The skills the agent names: the inline sub-agent templates' `skills`. Having a skill on by
|
|
137
|
+
* default is not using it (skills default-on spec §11), so a plugin update can remove it.
|
|
138
|
+
*/
|
|
127
139
|
skills: Set<string>;
|
|
128
140
|
/** `subagents.templates`. */
|
|
129
141
|
templates: Set<string>;
|
|
@@ -193,12 +193,17 @@ export async function loadScopeState(db, kind, comps) {
|
|
|
193
193
|
if (itemIds.length > 0) {
|
|
194
194
|
const items = await db
|
|
195
195
|
.selectFrom("catalog_items")
|
|
196
|
-
.select(["id", "scope", "builtin", "disabled"])
|
|
196
|
+
.select(["id", "scope", "builtin", "disabled", "owner_org_id"])
|
|
197
197
|
.where("id", "in", itemIds)
|
|
198
198
|
.where("deleted_at", "is", null)
|
|
199
199
|
.execute();
|
|
200
|
-
for (const i of items)
|
|
201
|
-
state.plugins.set(i.id,
|
|
200
|
+
for (const i of items) {
|
|
201
|
+
state.plugins.set(i.id, {
|
|
202
|
+
scope: i.scope,
|
|
203
|
+
builtin: platformBuiltin(i),
|
|
204
|
+
disabled: i.disabled,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
202
207
|
const listed = await db
|
|
203
208
|
.selectFrom("catalog_item_scope_orgs")
|
|
204
209
|
.select(["item_id", "org_id"])
|
|
@@ -235,6 +240,12 @@ export async function loadScopeState(db, kind, comps) {
|
|
|
235
240
|
}
|
|
236
241
|
return state;
|
|
237
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Whether a catalog item is builtin: marked so and owned by the platform. An org-owned item marked
|
|
245
|
+
* builtin (never so in practice) is an ordinary item: never on without an install, never locked,
|
|
246
|
+
* and its skills can be turned off (skills default-on spec §3).
|
|
247
|
+
*/
|
|
248
|
+
export const platformBuiltin = (item) => item.builtin === true && item.owner_org_id === null;
|
|
238
249
|
/** The plugin facts of `itemId` for one org from a {@link ScopeState} (null: no plugin). */
|
|
239
250
|
export function pluginFactsOf(st, itemId, orgId) {
|
|
240
251
|
if (itemId === null)
|
|
@@ -287,8 +298,6 @@ export function definitionRefs(definition, orgConnectors = []) {
|
|
|
287
298
|
if (typeof x === "string")
|
|
288
299
|
into.add(x);
|
|
289
300
|
};
|
|
290
|
-
strings(def?.skills?.orgSkills, skills);
|
|
291
|
-
strings(def?.skills?.pinned, skills);
|
|
292
301
|
const inline = def?.subagents?.inline;
|
|
293
302
|
if (Array.isArray(inline))
|
|
294
303
|
for (const t of inline)
|
|
@@ -188,7 +188,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
|
|
|
188
188
|
/** The host versions (an entry's `minKernel` is judged against them). */
|
|
189
189
|
export declare const HOST_VERSIONS: {
|
|
190
190
|
readonly kernel: string;
|
|
191
|
-
readonly sdk: "0.1.0-rc.
|
|
191
|
+
readonly sdk: "0.1.0-rc.17";
|
|
192
192
|
};
|
|
193
193
|
/** Whether this deployment satisfies the entry's `minKernel`. */
|
|
194
194
|
export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
|
|
@@ -31,6 +31,7 @@ import { expandGitUrl, GitSourceError, gitErrorDetails, gitLocationIdentity, git
|
|
|
31
31
|
import { CatalogSourceLock } from "./locks.js";
|
|
32
32
|
import { fetchMarketplaceManifest, MarketplaceFetchError, MarketplaceInvalidError, marketplaceNetPolicy, marketplaceUrlProblem, } from "./marketplace-fetch.js";
|
|
33
33
|
import { checkMarketplaceIcon, fetchMarketplaceIcon, gitIconPaths, MARKETPLACE_ICON_CONCURRENCY, MARKETPLACE_ICON_FETCHES_MAX, MARKETPLACE_ICON_MAX_BYTES, MARKETPLACE_ICON_RETRY_MS, marketplaceIconUrl, wantedIcons, } from "./marketplace-icons.js";
|
|
34
|
+
import { withoutRemovedManifestFields } from "./plugin-package.js";
|
|
34
35
|
import { compareVersions, newestFirst, sameRelease } from "./versions.js";
|
|
35
36
|
/** 官方清单来源的 id。 */
|
|
36
37
|
export const OFFICIAL_MARKETPLACE_ID = "official";
|
|
@@ -925,7 +926,7 @@ let MarketplacesService = class MarketplacesService {
|
|
|
925
926
|
const pj = files.get(PLUGIN_MANIFEST_FILE);
|
|
926
927
|
let entry;
|
|
927
928
|
if (pj !== undefined) {
|
|
928
|
-
const m = parsePluginManifest(dec(pj));
|
|
929
|
+
const m = parsePluginManifest(withoutRemovedManifestFields(dec(pj)));
|
|
929
930
|
if ("errors" in m) {
|
|
930
931
|
throw new MarketplaceInvalidError(`the repository is a single plugin, but its ${PLUGIN_MANIFEST_FILE} is invalid: ${m.errors.slice(0, 5).join("; ")}`, m.errors);
|
|
931
932
|
}
|
|
@@ -7,7 +7,7 @@ import { type OrgContext } from "../orgs/org.guard.js";
|
|
|
7
7
|
import type { ConnectorDetail, ToolIssue } from "../tools/connectors.service.js";
|
|
8
8
|
import type { MarketplaceImportResult } from "./catalog.service.js";
|
|
9
9
|
import type { CatalogIcon, OrgCatalogDetail, OrgCatalogItem, OrgInstallResult } from "./catalog-content.js";
|
|
10
|
-
import { CatalogGuideQuery, CatalogMarketplaceRequestBody, CatalogRequestBody, CatalogRequestsListQuery, OrgPluginInstallBody, OrgPluginSwitchBody } from "./org-catalog.schemas.js";
|
|
10
|
+
import { CatalogGuideQuery, CatalogMarketplaceRequestBody, CatalogRequestBody, CatalogRequestsListQuery, NetworkRequestBody, OrgPluginInstallBody, OrgPluginSwitchBody } from "./org-catalog.schemas.js";
|
|
11
11
|
import { OrgCatalogService } from "./org-catalog.service.js";
|
|
12
12
|
import { PluginInstaller, type PluginInstallView, type UninstallPreview, type UninstallReport } from "./plugin-install.js";
|
|
13
13
|
import { CatalogRequestsService, type CatalogRequestView } from "./requests.service.js";
|
|
@@ -57,6 +57,8 @@ declare const DecideBody: z.ZodObject<{
|
|
|
57
57
|
* - `POST catalog/:itemId/requests` (`catalog.request`; `access` needs an org admin): requests
|
|
58
58
|
* under plugin scope (spec §5.1), `enable` into the org admins' inbox, `access` to the super
|
|
59
59
|
* admin; `POST marketplace-requests` (org admins): an `import` request for a marketplace entry;
|
|
60
|
+
* - `POST catalog-requests/network` (`catalog.request`, web sessions only): a member asks the org
|
|
61
|
+
* admins to allow hosts in the org's sandbox network (network ask-to-allow spec §5.3);
|
|
60
62
|
* - `PUT catalog/:itemId/switch` (scope phase 1): `{ enabled: true, inputs? }` installs -> 200 `{ item }`;
|
|
61
63
|
* `{ enabled: false, confirm? }` -> 200 `{ preview, confirm }`, then with the hash 200 `{ item }`;
|
|
62
64
|
* - `POST catalog/:itemId/install` (an alias of switching on, no longer creates requests): the route only requires `catalog.read`; the service re-checks
|
|
@@ -69,8 +71,8 @@ declare const DecideBody: z.ZodObject<{
|
|
|
69
71
|
* admin (`PUT /admin/catalog/items/:id/update`, ruling F2);
|
|
70
72
|
* - `POST connector-templates/:templateId/connectors` (`connectors.manage`): one more connector
|
|
71
73
|
* from a platform connector template on in the org (ruling P4-T6-8);
|
|
72
|
-
* - `POST catalog-requests/:id/decide` (`catalog.decide`, org admins; `enable
|
|
73
|
-
* `connector_setup` requests, `{ approve, reason? }`; a `connector_setup` request is only
|
|
74
|
+
* - `POST catalog-requests/:id/decide` (`catalog.decide`, org admins; `enable`,
|
|
75
|
+
* `network_allowlist` and `connector_setup` requests, `{ approve, reason? }`; a `connector_setup` request is only
|
|
74
76
|
* rejected here, ruling P4-T6-6), `POST catalog-requests/:id/dismiss` (the requester, a decided
|
|
75
77
|
* request).
|
|
76
78
|
*/
|
|
@@ -156,6 +158,14 @@ export declare class OrgCatalogController {
|
|
|
156
158
|
installs: PluginInstallView[];
|
|
157
159
|
}>;
|
|
158
160
|
uninstall(principal: Principal, org: OrgContext, installId: string): Promise<UninstallReport>;
|
|
161
|
+
/**
|
|
162
|
+
* A member asks the org admins to allow hosts in the org's sandbox egress (from a blocked
|
|
163
|
+
* command's card) -> 201 `{ request }` (an identical pending request of the member is returned).
|
|
164
|
+
* Web sessions only: a personal token, a service token or an agent never files one.
|
|
165
|
+
*/
|
|
166
|
+
networkRequest(principal: Principal, org: OrgContext, body: z.infer<typeof NetworkRequestBody>): Promise<{
|
|
167
|
+
request: CatalogRequestView;
|
|
168
|
+
}>;
|
|
159
169
|
decide(principal: Principal, org: OrgContext, id: string, body: z.infer<typeof DecideBody>): Promise<{
|
|
160
170
|
request: CatalogRequestView;
|
|
161
171
|
}>;
|