@hue-run/sdk 0.11.2 → 0.11.4
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/CLI.md +62 -25
- package/README.md +10 -7
- package/dist/cli/eval.js +16 -10
- package/dist/cli/listen.js +21 -2
- package/dist/cli/login.js +2 -1
- package/dist/cli/mcp.d.ts +5 -4
- package/dist/cli/mcp.js +480 -48
- package/dist/environment/world.d.ts +4 -2
- package/dist/environment/world.js +13 -4
- package/dist/evals/checkpoint.d.ts +5 -0
- package/dist/evals/checkpoint.js +17 -1
- package/dist/evals/local-worker.js +9 -5
- package/dist/evals/simulation.js +7 -5
- package/dist/provider-tools.d.ts +3 -1
- package/dist/provider-tools.js +4 -2
- package/dist/tool-definitions.d.ts +14 -3
- package/dist/tool-definitions.js +635 -53
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CLI.md
CHANGED
|
@@ -42,7 +42,7 @@ project manifest are refused because managers can update ancestor locks; Python
|
|
|
42
42
|
The generated `hue.setup.mjs` or `hue_setup.py` always selects `captureContent: false` /
|
|
43
43
|
`capture_content=False`. For a supported application, setup installs the dependency and adds the
|
|
44
44
|
managed import and middleware registration to the existing entrypoint; an unreferenced helper is
|
|
45
|
-
not a completed integration. TypeScript uses `@hue-run/sdk@0.11.
|
|
45
|
+
not a completed integration. TypeScript uses `@hue-run/sdk@0.11.4`, `@opentelemetry/api@1.9.1` and
|
|
46
46
|
`@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
|
|
47
47
|
Content capture requires an ordinary account-managed key and a later explicit application decision.
|
|
48
48
|
|
|
@@ -141,7 +141,7 @@ uses the observed predecessor for compare-and-swap. A conflict refreshes status
|
|
|
141
141
|
At most 32 distinct handoff IDs exist per installation; `SETUP_HANDOFF_LIMIT` is terminal. Replacing a
|
|
142
142
|
handoff does not create a trial, reset quota or rerun business work.
|
|
143
143
|
|
|
144
|
-
##
|
|
144
|
+
## Store project keys
|
|
145
145
|
|
|
146
146
|
`hue login` stores keys that a person creates in Hue; it never mints one, because setup
|
|
147
147
|
credentials are deliberately isolated from ordinary project keys. It prints the key settings page
|
|
@@ -185,14 +185,27 @@ for staging). Hue's Settings page does not show these snippets; this command kee
|
|
|
185
185
|
|
|
186
186
|
`--auth` chooses how the client authenticates:
|
|
187
187
|
|
|
188
|
-
- `key` (the default, except for `conductor`) references the `HUE_MCP_KEY` environment variable,
|
|
189
|
-
which `hue login` stores in `.env.hue`. A key value is never written.
|
|
190
188
|
- `oauth` configures the URL only. The client opens Hue in a browser, where you sign in and
|
|
191
189
|
approve the connection; no key is involved. A new sign-in connection has **Read and
|
|
192
|
-
write** access to every active project in the organization
|
|
193
|
-
agent first calls `list_projects` and passes the chosen project's id as `project_id`.
|
|
194
|
-
|
|
195
|
-
`
|
|
190
|
+
write** access to every active project in the organization the consent page names, within your
|
|
191
|
+
role, so an agent first calls `list_projects` and passes the chosen project's id as `project_id`.
|
|
192
|
+
The approval lasts 30 days; then sign in again. Sign-in covers the MCP server only: tracing and
|
|
193
|
+
`hue eval` still use a project key as `HUE_API_KEY`. It is available for `claude-code`, `codex`
|
|
194
|
+
and `conductor`. Hue does not yet accept Cursor's sign-in callback, so `cursor` and the other
|
|
195
|
+
clients use a key.
|
|
196
|
+
- `key` references the `HUE_MCP_KEY` environment variable, which `hue login` stores in
|
|
197
|
+
`.env.hue`. A key value is never written. Use a key for read-only access (a **Read** key, which
|
|
198
|
+
only `hue login --keys coding-agent` accepts), for a run that cannot complete a browser sign-in
|
|
199
|
+
(CI, `claude -p`), or for access that must outlast 30 days.
|
|
200
|
+
|
|
201
|
+
Without `--auth`, `claude-code`, `codex` and `conductor` sign in and the other clients use a key.
|
|
202
|
+
`--read-only` (or `read_only=true` in `--url`) selects a key. Otherwise `claude-code` and `codex`
|
|
203
|
+
keep the choice of the server's existing entry, in `./.mcp.json` or as `codex mcp get` reports it,
|
|
204
|
+
so a rerun that changes `--toolsets` keeps a key and its `read_only`; for a new entry they use a
|
|
205
|
+
key when `HUE_MCP_KEY` is set (only its presence is checked). When `codex` is on `PATH` but `codex mcp get` fails
|
|
206
|
+
for another reason, the command asks for `--auth` rather than guess which to keep. `conductor` keeps signing in, since
|
|
207
|
+
its agents read the login-shell environment Conductor captures rather than your terminal's. The
|
|
208
|
+
command names its choice on stderr; pass `--auth` to replace an entry as given.
|
|
196
209
|
|
|
197
210
|
`--project <id-or-slug>` pins either kind of connection to one project. The command adds or
|
|
198
211
|
replaces `?project=<value>` while keeping `toolsets`, and names the server `hue-<value>` so it can
|
|
@@ -203,10 +216,11 @@ The printed sign-in Codex TOML keeps its URL bare and sends `X-Hue-MCP-Project`
|
|
|
203
216
|
credential cannot reach it, Hue returns HTTP `404`; change or remove `--project`, because signing
|
|
204
217
|
in again does not change the credential's reach.
|
|
205
218
|
|
|
206
|
-
`--read-only` adds `?read_only=true` to
|
|
219
|
+
`--read-only` selects a key and adds `?read_only=true` to its URL, so Hue hides and rejects write
|
|
207
220
|
tools whatever the key allows. A project key, with or without it, keeps reaching its one project.
|
|
208
|
-
With `--auth oauth` it is refused
|
|
209
|
-
**Read**
|
|
221
|
+
With `--auth oauth` it is refused, as is any `read_only` in a sign-in `--url` (Hue reads only
|
|
222
|
+
`true` or `1`): a sign-in connection has **Read and write** access, so use a **Read** project key
|
|
223
|
+
for read-only access.
|
|
210
224
|
|
|
211
225
|
`--toolsets <names>` chooses the tools the connection lists: `all`; the profiles `observe`
|
|
212
226
|
(production reads), `author` (author evaluations) and `evaluate` (run an agent against a case);
|
|
@@ -233,9 +247,9 @@ reviewed cases `case_id`, environment versions `environment_version_id`, and tra
|
|
|
233
247
|
|
|
234
248
|
| Client | Key (`--auth key`) | Sign-in (`--auth oauth`) |
|
|
235
249
|
| --- | --- | --- |
|
|
236
|
-
| `claude-code` | Merges `mcpServers.hue` into `./.mcp.json`. `--scope user` runs `claude mcp add --transport http --scope user hue URL --header 'Authorization: Bearer ${HUE_MCP_KEY}'` when `claude` is on `PATH`, otherwise prints it. | Merges `mcpServers.hue` with only `type` and `url` into `./.mcp.json`; `--scope user` runs `claude mcp add --transport http --scope user hue URL`. Then
|
|
237
|
-
| `codex` | Runs `codex mcp add hue --url URL --bearer-token-env-var HUE_MCP_KEY`, or prints the `[mcp_servers.hue]` TOML block for `~/.codex/config.toml`. | Runs `codex mcp add hue --url URL`, which
|
|
238
|
-
| `conductor` | Runs the Claude Code user-scope and Codex key commands above. |
|
|
250
|
+
| `claude-code` | Merges `mcpServers.hue` into `./.mcp.json`. `--scope user` runs `claude mcp add --transport http --scope user hue URL --header 'Authorization: Bearer ${HUE_MCP_KEY}'` when `claude` is on `PATH`, otherwise prints it. | Merges `mcpServers.hue` with only `type` and `url` into `./.mcp.json`; `--scope user` runs `claude mcp add --transport http --scope user hue URL`. Then restart Claude Code, run `/mcp`, select `hue` and choose **Authenticate**. |
|
|
251
|
+
| `codex` | Runs `codex mcp add hue --url URL --bearer-token-env-var HUE_MCP_KEY`, or prints the `[mcp_servers.hue]` TOML block for `~/.codex/config.toml`. | Runs `codex mcp add hue --url URL`, which signs in at once and waits up to 5 minutes for the browser; without `--auth` and outside a terminal (CI, an agent's shell tool) the command prints it instead. A sign-in that does not finish leaves the server registered, and `codex mcp login hue` signs in. Then start a new Codex session. The printed TOML block keeps its URL bare and sends the selection as the `X-Hue-MCP-Toolsets` header. |
|
|
252
|
+
| `conductor` | Runs the Claude Code user-scope and Codex key commands above. | Runs `claude mcp add --transport http --scope user hue URL` and `codex mcp add hue --url URL` (printed outside a terminal, as for `codex`). Then use `hue`'s authentication action in Conductor's MCP status (the plug icon or `/mcp-status`). |
|
|
239
253
|
| `cursor` | Merges `mcpServers.hue` into `./.cursor/mcp.json` with `${env:HUE_MCP_KEY}`. | Refused. |
|
|
240
254
|
| `vscode` | Merges `servers.hue` and the `hue-mcp-key` password input into `./.vscode/mcp.json`. | Refused. |
|
|
241
255
|
| `windsurf` | Prints the `serverUrl` snippet for `~/.codeium/windsurf/mcp_config.json`; nothing is written to the home directory. | Refused. |
|
|
@@ -255,17 +269,39 @@ again with `--auth key`.
|
|
|
255
269
|
JSON files are parsed and merged: other servers, inputs and top-level fields are kept, only the
|
|
256
270
|
selected server entry is replaced (`hue`, or `hue-<value>` with `--project`), and invalid JSON
|
|
257
271
|
(including comments) is refused together with the snippet
|
|
258
|
-
to add by hand. Files are written
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
272
|
+
to add by hand. Files are written through a temporary file and an atomic rename. A new file is
|
|
273
|
+
owner-only (mode `0600`, narrowed further by the umask), because other servers' literal tokens often
|
|
274
|
+
end up in it; a replaced file keeps its read, write and execute permissions, so a file kept at
|
|
275
|
+
`0600` is never widened, except that other accounts lose write access (they could add a command for
|
|
276
|
+
the client to run). It keeps its group, and its owner when root runs the command (if root cannot
|
|
277
|
+
give the file back, the write fails and the old file stays); when the group cannot be kept, the
|
|
278
|
+
group and other accounts get only the access both had. Setuid, setgid and sticky bits are dropped,
|
|
279
|
+
and an access control list is not kept: the replacement has only the mode bits, whose group bits are
|
|
280
|
+
the list's mask, plus any default list the directory gives new files, whose entries then get up to
|
|
281
|
+
the group's access. Restore a list afterwards if you use one. A symlinked file, or a symlinked
|
|
282
|
+
`.cursor` or `.vscode` directory, is refused; the current directory itself may be reached through a
|
|
283
|
+
symlink. The directory is checked before the file is read and again before the rename, and a
|
|
284
|
+
different file found in place of the one read is refused, but not atomically: an account that can
|
|
285
|
+
write the project directory and races the command can still swap in a link between the last check
|
|
286
|
+
and the rename. `--dry-run` prints the resulting file content or commands without writing or
|
|
287
|
+
running. In printed file content, every header and `env` value, and each other credential it
|
|
288
|
+
recognizes (a known token prefix, the value of an option such as `--api-key VALUE`, `-H VALUE`,
|
|
289
|
+
`-e NAME=VALUE` or `--token=VALUE`, a field or assignment such as `clientSecret`, `key` or
|
|
290
|
+
`STRIPE_KEY=`, a token-like URL path segment, a URL's query values, which read `%5Bredacted%5D`), is
|
|
291
|
+
shown as `[redacted]` unless the whole value is a variable or input reference such as
|
|
292
|
+
`Bearer ${HUE_MCP_KEY}`; the file keeps the values. Hue's own URL shows its `toolsets`, `project`
|
|
293
|
+
and `read_only` selections, with every other query value and any token-like path segment as
|
|
294
|
+
`[redacted]`, there and in the lines that report what the command did; a command printed for you to
|
|
295
|
+
run keeps `--url` as given. `--print` prints only the snippet. Client CLIs run without a shell, so
|
|
296
|
+
the `${HUE_MCP_KEY}` reference reaches them literally; the printed commands use single quotes for
|
|
297
|
+
the same reason, and quote a URL with a query such as `?read_only=true`, whose `?` is a zsh glob.
|
|
263
298
|
|
|
264
299
|
After a key installation the command reminds you to export `HUE_MCP_KEY` in the shell that starts
|
|
265
300
|
the client (VS Code prompts for the key instead). A desktop app started from the Dock or a launcher
|
|
266
301
|
does not see that shell's variables, and Conductor's agents read the login-shell environment that
|
|
267
|
-
Conductor captures, so sign-in is the simpler choice there. After a sign-in installation it
|
|
268
|
-
the client
|
|
302
|
+
Conductor captures, so sign-in is the simpler choice there. After a sign-in installation it says
|
|
303
|
+
how the client loads the server (restart Claude Code, or a new Codex session) and names its
|
|
304
|
+
authentication action. Both end with the prompt that verifies the connection:
|
|
269
305
|
`Use the Hue MCP: call list_projects and confirm which project to inspect. Then call get_project_context and show that project's traces from the last 24 hours that need attention or have errors, with links; if there are none, show its 5 most recent traces. For an organization connection, pass the project's id or slug as project_id on each call after list_projects.` A pinned installation instead verifies `connection.pinned: true` and omits `project_id`. `list_projects` returns a project key's one project too. Exit codes:
|
|
270
306
|
`0` done or printed, `1` failed, `2` usage error.
|
|
271
307
|
|
|
@@ -300,8 +336,9 @@ a later one is a timeout. A failing answer with `x-slack-no-retry: 1` is reporte
|
|
|
300
336
|
retrying it. Only a successful URL verification answer's body (at most 4 KiB, for its challenge) is
|
|
301
337
|
sent to Hue; any other answer body stays on this machine. Each pull waits for at most 20 seconds and
|
|
302
338
|
leases up to `--max` deliveries (1 to 10, default 10), which are forwarded concurrently; the next
|
|
303
|
-
pull starts once they are acknowledged, and a delivery beyond `--max` is never forwarded. A
|
|
304
|
-
that
|
|
339
|
+
pull starts once they are acknowledged, and a delivery beyond `--max` is never forwarded. A waiting
|
|
340
|
+
pull that forwarded something is not repeated until 50 ms after it began, and one that answers at
|
|
341
|
+
once with nothing new to forward is not repeated for a second. A provider retry of
|
|
305
342
|
an event is a new delivery and is forwarded again with its `X-Slack-Retry-Num` and
|
|
306
343
|
`X-Slack-Retry-Reason`, so the bot deduplicates by `event_id` as it does with Slack. A delivery Hue
|
|
307
344
|
hands out a second time is not sent again; its recorded answer is repeated. An acknowledgement Hue
|
|
@@ -432,7 +469,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
|
|
|
432
469
|
# Set project to an existing supported fixture; use the same directory on resume.
|
|
433
470
|
project=/absolute/path/to/supported-fixture
|
|
434
471
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
435
|
-
--archive .artifacts/typescript/hue-run-sdk-0.11.
|
|
472
|
+
--archive .artifacts/typescript/hue-run-sdk-0.11.4.tgz \
|
|
436
473
|
--origin https://STAGING_ORIGIN \
|
|
437
474
|
--project "$project" --command setup \
|
|
438
475
|
--evidence .context/setup-staging-before-claim.json
|
|
@@ -443,7 +480,7 @@ the private local handoff and finish the real browser claim, then reconcile the
|
|
|
443
480
|
|
|
444
481
|
```sh
|
|
445
482
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
446
|
-
--archive .artifacts/typescript/hue-run-sdk-0.11.
|
|
483
|
+
--archive .artifacts/typescript/hue-run-sdk-0.11.4.tgz \
|
|
447
484
|
--origin https://STAGING_ORIGIN \
|
|
448
485
|
--project "$project" --command claim \
|
|
449
486
|
--evidence .context/setup-staging-after-claim.json
|
package/README.md
CHANGED
|
@@ -82,11 +82,12 @@ account linkage; the original request evidence is retained and business work is
|
|
|
82
82
|
claim. Setup never enables content capture or creates a simulation, Hue Run, evaluation, source capture
|
|
83
83
|
or remote execution. See the [setup CLI contract](./CLI.md) for the supported shapes and release gates.
|
|
84
84
|
|
|
85
|
-
For an existing account, `hue
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
For an existing account, `hue mcp install --client claude-code` (or `codex`, `conductor`,
|
|
86
|
+
`cursor`, `vscode`, `windsurf`, `gemini`) writes the Hue MCP configuration. For Claude Code, Codex
|
|
87
|
+
and Conductor it holds only the URL, and you sign in with Hue in the client; for the other clients,
|
|
88
|
+
`--read-only` and `--auth key` it references `HUE_MCP_KEY` (VS Code prompts for the key).
|
|
89
|
+
`hue login` validates keys created in Hue Settings and stores them in `.env.hue` without printing
|
|
90
|
+
them. See [Store project keys](./CLI.md#store-project-keys) and
|
|
90
91
|
[Install the MCP for your coding agent](./CLI.md#install-the-mcp-for-your-coding-agent).
|
|
91
92
|
|
|
92
93
|
`checkConnection()` rejects with `HueConnectionError`: its fixed message is safe to log, `status`
|
|
@@ -165,8 +166,10 @@ call carries `error.type` (`mcp_error`, the provider's status or error code) and
|
|
|
165
166
|
text, credentials scrubbed (URL userinfo, query values and fragments, or the whole URL for a scheme
|
|
166
167
|
other than `http(s)`, `ws(s)` and `ftp`; tokens with a known credential prefix such as `hue_sk_`,
|
|
167
168
|
`sk-` or `xoxb-`; authorization-scheme credentials and an `Authorization` header's whole value;
|
|
168
|
-
the value of a credential-named `key=value` or `key
|
|
169
|
-
|
|
169
|
+
the value of a credential-named `key=value`, `key: value` or `key => value` pair, quoted, bare or
|
|
170
|
+
a whole `[…]` or `{…}` (to the end of the text when it does not close), a quote that does not close
|
|
171
|
+
on its line running to its end; a JSON or `%` escape such as `\n` or `%20` ends a word) and cut to
|
|
172
|
+
1,024 characters and a `…`. An `mcp_list_tools`
|
|
170
173
|
item becomes a `tools/list` child span with that server's `gen_ai.tool.definitions`; with
|
|
171
174
|
`captureContent: false` it carries only `hue.tool.names` and `hue.tool.definitions.sha256`, the
|
|
172
175
|
summary described below. Pass the request so each server's host is recorded as `server.address`
|
package/dist/cli/eval.js
CHANGED
|
@@ -8,10 +8,10 @@ import { parseArgs } from "node:util";
|
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { createHue } from "../client.js";
|
|
10
10
|
import { createEnvironmentClient } from "../environment/client.js";
|
|
11
|
-
import { agentEnvironment, isHueControlPlaneCredential, stripHueControlPlaneCredentials, writeMcpConfig, } from "../environment/world.js";
|
|
11
|
+
import { agentEnvironment, isHueControlPlaneCredential, stripHueControlPlaneCredentials, withoutWorldVariables, writeMcpConfig, } from "../environment/world.js";
|
|
12
12
|
import { EvaluationClient, HueApiError } from "../evals/client.js";
|
|
13
13
|
import { TargetResult } from "../evals/types.js";
|
|
14
|
-
import { CheckpointIdentityError, CheckpointStore } from "../evals/checkpoint.js";
|
|
14
|
+
import { CheckpointIdentityError, CheckpointStore, checkpointPath } from "../evals/checkpoint.js";
|
|
15
15
|
import { onForcedExit, runForcedExitCleanups } from "../evals/exit-cleanup.js";
|
|
16
16
|
import { safeFilename } from "../evals/files.js";
|
|
17
17
|
import { digest } from "../evals/json.js";
|
|
@@ -583,11 +583,13 @@ function redacting(adapter) {
|
|
|
583
583
|
};
|
|
584
584
|
}
|
|
585
585
|
/** The parent environment an agent child starts from: without Hue control-plane credentials
|
|
586
|
-
* unless `--allow-hue-credentials` was passed. */
|
|
586
|
+
* unless `--allow-hue-credentials` was passed, and without any world variable. */
|
|
587
587
|
function parentEnvironment(allowHueCredentials) {
|
|
588
|
-
|
|
588
|
+
// A world variable left in this process's environment belongs to another world: only the
|
|
589
|
+
// current case's world sets them, whether or not the case has one.
|
|
590
|
+
return withoutWorldVariables(allowHueCredentials
|
|
589
591
|
? Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined))
|
|
590
|
-
: stripHueControlPlaneCredentials(process.env);
|
|
592
|
+
: stripHueControlPlaneCredentials(process.env));
|
|
591
593
|
}
|
|
592
594
|
function commandAdapter(command, timeoutSeconds, options) {
|
|
593
595
|
return async (inputs, context) => {
|
|
@@ -810,9 +812,13 @@ export function explain(error) {
|
|
|
810
812
|
}
|
|
811
813
|
/** `.hue/eval/<agent-key>/<project>/<leaf>` unless overridden; the SDK binds each store to one
|
|
812
814
|
* project and origin, so switching keys or deployments must not collide. */
|
|
813
|
-
async function prepareCheckpointDirectory(explicit, agentKey, projectId, leaf) {
|
|
815
|
+
async function prepareCheckpointDirectory(explicit, agentKey, projectId, ...leaf) {
|
|
816
|
+
const segments = [
|
|
817
|
+
[projectId, "project id"],
|
|
818
|
+
...leaf.map((part) => [part, "checkpoint kind"]),
|
|
819
|
+
];
|
|
814
820
|
if (explicit)
|
|
815
|
-
return
|
|
821
|
+
return checkpointPath(explicit, ...segments);
|
|
816
822
|
const root = resolve(".hue", "eval");
|
|
817
823
|
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
818
824
|
const ignore = join(root, ".gitignore");
|
|
@@ -822,7 +828,7 @@ async function prepareCheckpointDirectory(explicit, agentKey, projectId, leaf) {
|
|
|
822
828
|
catch {
|
|
823
829
|
await writeFile(ignore, "*\n", { flag: "wx", mode: 0o600 }).catch(() => undefined);
|
|
824
830
|
}
|
|
825
|
-
return join(root, agentKey,
|
|
831
|
+
return checkpointPath(join(root, agentKey), ...segments);
|
|
826
832
|
}
|
|
827
833
|
/** Worker-side client that reports registration and claims without changing the worker. */
|
|
828
834
|
class ObservedClient extends EvaluationClient {
|
|
@@ -1210,7 +1216,7 @@ async function runDirect(run, connection) {
|
|
|
1210
1216
|
client,
|
|
1211
1217
|
hue: run.hue,
|
|
1212
1218
|
experimentId,
|
|
1213
|
-
checkpointDirectory:
|
|
1219
|
+
checkpointDirectory: checkpointPath(store.directory, [experimentId, "experiment id"]),
|
|
1214
1220
|
// Outputs, error messages and explanations are stored unless opted out; --content governs
|
|
1215
1221
|
// only the telemetry.
|
|
1216
1222
|
persistResultContent: !values["no-output"],
|
|
@@ -1268,7 +1274,7 @@ async function runWorker(values, connection, adapter, agent, hue, output, signal
|
|
|
1268
1274
|
? undefined
|
|
1269
1275
|
: integer("max-runs", values["max-runs"], 1, 1, 1_000_000);
|
|
1270
1276
|
const project = await client.checkConnection();
|
|
1271
|
-
const checkpointDirectory = await prepareCheckpointDirectory(values["checkpoint-dir"], agent.key, project.id,
|
|
1277
|
+
const checkpointDirectory = await prepareCheckpointDirectory(values["checkpoint-dir"], agent.key, project.id, "worker", slug(agent.revision) || "dev");
|
|
1272
1278
|
let current;
|
|
1273
1279
|
client.onRegistered = (registered) => {
|
|
1274
1280
|
output.log(`Registered agent ${registered.key} (revision ${registered.revision}) with ${connection.baseUrl}; waiting for runs launched from Hue${maxRuns ? ` (stops after ${maxRuns})` : " (Ctrl+C to stop)"}`);
|
package/dist/cli/listen.js
CHANGED
|
@@ -35,6 +35,12 @@ const MAX_RETRY_AFTER_MS = 60_000;
|
|
|
35
35
|
const REMEMBERED_DELIVERIES = 1_024;
|
|
36
36
|
/** The least time between pulls that forwarded nothing new, so repeats cannot spin. */
|
|
37
37
|
const IDLE_PULL_FLOOR_MS = 1_000;
|
|
38
|
+
/** The least time between the starts of pulls that forwarded something, so a Hue that answers
|
|
39
|
+
* every pull at once with new deliveries is pulled at most 20 times a second. */
|
|
40
|
+
const BUSY_PULL_FLOOR_MS = 50;
|
|
41
|
+
/** The most of a text from elsewhere (an error message, a URL) that is scrubbed and printed, in
|
|
42
|
+
* code points, as of a provider's error text; a longer one is cut there and `…` marks the cut. */
|
|
43
|
+
const MAX_FOREIGN_TEXT = 16_384;
|
|
38
44
|
/** A stop request this soon after the first is the same one: under `npx`, npm forwards the
|
|
39
45
|
* terminal's SIGINT to a process group that already received it. */
|
|
40
46
|
const REPEATED_STOP_MS = 1_000;
|
|
@@ -491,7 +497,16 @@ export async function runListenCommand(argv, io = {}) {
|
|
|
491
497
|
out = out.replaceAll(secret, "[redacted]");
|
|
492
498
|
return out.replace(/[^\P{Cc}\n\t]|\p{Cf}/gu, " ");
|
|
493
499
|
};
|
|
494
|
-
const foreign = (text) =>
|
|
500
|
+
const foreign = (text) => {
|
|
501
|
+
let kept = "";
|
|
502
|
+
let seen = 0;
|
|
503
|
+
for (const character of text) {
|
|
504
|
+
if (seen++ === MAX_FOREIGN_TEXT)
|
|
505
|
+
return `${scrubCredentialText(kept, true)}…`;
|
|
506
|
+
kept += character;
|
|
507
|
+
}
|
|
508
|
+
return scrubCredentialText(text);
|
|
509
|
+
};
|
|
495
510
|
const out = (line) => void stdout.write(`${clean(line)}\n`);
|
|
496
511
|
const warn = (line) => void stderr.write(`${clean(line)}\n`);
|
|
497
512
|
let subscription;
|
|
@@ -809,8 +824,12 @@ export async function runListenCommand(argv, io = {}) {
|
|
|
809
824
|
// A pull that could wait but answered at once with nothing new to forward (none due, only
|
|
810
825
|
// deliveries already answered, or ones this client skips) is not repeated at once, so a
|
|
811
826
|
// Hue that hands them out again or refuses their acknowledgements is not pulled in a loop.
|
|
812
|
-
|
|
827
|
+
// One that forwarded something is repeated no sooner than BUSY_PULL_FLOOR_MS after it began.
|
|
828
|
+
const elapsed = performance.now() - pulledAt;
|
|
829
|
+
if (waited && !forwarded.includes(true) && elapsed < IDLE_PULL_FLOOR_MS)
|
|
813
830
|
await sleep(IDLE_PULL_FLOOR_MS, stopping.signal);
|
|
831
|
+
else if (waited && elapsed < BUSY_PULL_FLOOR_MS)
|
|
832
|
+
await sleep(BUSY_PULL_FLOOR_MS - elapsed, stopping.signal);
|
|
814
833
|
}
|
|
815
834
|
}
|
|
816
835
|
finally {
|
package/dist/cli/login.js
CHANGED
|
@@ -742,7 +742,8 @@ export async function runLoginCommand(argv, io = {}) {
|
|
|
742
742
|
out("Next steps:");
|
|
743
743
|
if (stored.includes("coding-agent"))
|
|
744
744
|
// `hue mcp install` defaults to production; a non-default origin needs its own endpoint.
|
|
745
|
-
|
|
745
|
+
// Without --auth it configures sign-in unless HUE_MCP_KEY is exported, so name the key stored.
|
|
746
|
+
out(` hue mcp install --client claude-code --auth key${mcpUrl === mcpUrlForOrigin(DEFAULT_ORIGIN) ? "" : ` --url ${mcpUrl}`}`);
|
|
746
747
|
if (stored.includes("evaluations"))
|
|
747
748
|
out(` hue eval --case "<name>" ./hue-agent.ts --env-file ${envDisplay}`);
|
|
748
749
|
return 0;
|
package/dist/cli/mcp.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `hue mcp install`: writes or prints the coding-agent configuration for Hue's MCP server. The
|
|
3
3
|
* shapes are those of Hue's published connection guide (https://docs.hue.run/agents/mcp-server),
|
|
4
|
-
* kept as a separate copy here.
|
|
5
|
-
* variable (or a VS Code password input)
|
|
6
|
-
*
|
|
4
|
+
* kept as a separate copy here. Sign-in configurations hold no credential; key configurations
|
|
5
|
+
* reference the `HUE_MCP_KEY` environment variable (or a VS Code password input). A key value is
|
|
6
|
+
* never written. Without `--auth`, a client that can sign in does unless a key is asked for or
|
|
7
|
+
* already set up (`defaultAuth`).
|
|
7
8
|
*/
|
|
8
9
|
/** Streams, environment and working directory for {@link runMcpCommand}; tests inject these. */
|
|
9
10
|
export interface McpCommandIo {
|
|
@@ -30,7 +31,7 @@ export declare const MCP_TOOLSETS: readonly ["all", "observe", "author", "evalua
|
|
|
30
31
|
export declare const TOOLSETS_HEADER = "X-Hue-MCP-Toolsets";
|
|
31
32
|
/** The header form of `?project=`, used by the sign-in Codex TOML to keep its URL bare. */
|
|
32
33
|
export declare const PROJECT_HEADER = "X-Hue-MCP-Project";
|
|
33
|
-
export declare const MCP_USAGE = "Usage: hue mcp install --client <claude-code|codex|conductor|cursor|vscode|windsurf|gemini>\n [--auth key|oauth] [--read-only] [--project ID-OR-SLUG]\n [--toolsets NAMES] [--url URL]\n [--scope project|user] [--dry-run] [--print]\n\nConfigure a coding agent to use the Hue MCP server. A
|
|
34
|
+
export declare const MCP_USAGE = "Usage: hue mcp install --client <claude-code|codex|conductor|cursor|vscode|windsurf|gemini>\n [--auth key|oauth] [--read-only] [--project ID-OR-SLUG]\n [--toolsets NAMES] [--url URL]\n [--scope project|user] [--dry-run] [--print]\n\nConfigure a coding agent to use the Hue MCP server. A sign-in configuration holds only the\nURL: the client opens Hue in a browser, where you approve access to the projects of one\norganization. A key configuration references the HUE_MCP_KEY environment variable; a key value\nis never written.\n\nOptions:\n --client NAME Coding agent to configure (required)\n --auth MODE oauth: URL only, sign in with Hue in the client (claude-code, codex and\n conductor; their default)\n key: reference HUE_MCP_KEY (the default for the other clients and with\n --read-only). Without --auth, claude-code and codex keep the choice of\n an existing entry, and use a key for a new one when HUE_MCP_KEY is set\n --read-only Add ?read_only=true to a key configuration's URL so write tools are\n hidden; selects a key without --auth. A sign-in connection has Read and\n write access; use a Read key for read-only\n --project VALUE Pin the connection to one project by id or slug. Its tools omit project_id,\n and the server is named hue-<value> so it can coexist with an unpinned hue\n --toolsets NAMES\n Tools to list, comma-separated: all; the observe, author or evaluate\n profiles; or project, traces, eval_sets, runs, judges, cases, runners,\n environments, intents or docs. Added to the URL as ?toolsets=.\n Defaults: all for claude-code, codex and conductor,\n which search their own tools; observe for cursor. Without a selection\n Hue lists the production reads and search_hue_tools, which reaches\n every other tool\n --url URL Hue MCP endpoint (default https://mcp.hue.run/mcp)\n --scope SCOPE claude-code only: project writes .mcp.json (default); user runs\n `claude mcp add --scope user`\n --dry-run Print the resulting file content or commands without writing or running\n --print Print the configuration snippet only\n -h, --help Show this help\n\nFiles: claude-code .mcp.json, cursor .cursor/mcp.json, vscode .vscode/mcp.json (relative to the\ncurrent directory). codex and gemini use their own CLI when it is on PATH; windsurf prints the\nsnippet for its user configuration file. conductor registers the server for its Claude Code\n(user scope) and Codex agents with their CLIs, printing the command for a CLI not on PATH.\ncodex mcp add signs in at once and waits up to 5 minutes for the browser, so without --auth\nand outside a terminal the command prints it instead of running it.";
|
|
34
35
|
/**
|
|
35
36
|
* A URL as a POSIX shell word for printed commands: `?` is a zsh glob and `&` ends a command, so a
|
|
36
37
|
* URL with a query (such as `?read_only=true`) is single-quoted. Executed commands pass argv.
|