humanish 0.110.1 → 0.110.3
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/ARCHITECTURE.md +11 -16
- package/CHANGELOG.md +26 -0
- package/CONTEXT.md +1 -1
- package/README.md +8 -7
- package/dist/actors/computer-use/actor.js +2 -2
- package/dist/actors/computer-use/loop/ending.js +1 -1
- package/dist/actors/local-agent/cli.d.ts +6 -0
- package/dist/actors/local-agent/cli.js +8 -0
- package/dist/actors/local-agent/readiness.js +2 -1
- package/dist/actors/registry.js +1 -1
- package/dist/analysis/automatic-completion.js +8 -0
- package/dist/cli/commands/observe.js +18 -4
- package/dist/cli/commands/run-signals.js +13 -4
- package/dist/cli/commands/runs.js +36 -13
- package/dist/cli/commands/setup.js +23 -14
- package/dist/cli/commands/study-route-computer-use.js +7 -3
- package/dist/cli/commands/study-route-open.js +1 -0
- package/dist/cli/commands/study-route-preview.js +1 -0
- package/dist/cli/commands/study-route-shared-world.js +4 -2
- package/dist/cli/commands/study-run.js +9 -19
- package/dist/cli/commands/watch.js +14 -4
- package/dist/cli/doctor.js +9 -6
- package/dist/cli/findings.d.ts +5 -0
- package/dist/cli/findings.js +28 -6
- package/dist/cli/first-run-path.d.ts +27 -7
- package/dist/cli/first-run-path.js +112 -75
- package/dist/cli/invocation.d.ts +7 -0
- package/dist/cli/invocation.js +32 -0
- package/dist/cli/io.d.ts +5 -2
- package/dist/cli/io.js +9 -16
- package/dist/cli/observer-follow.d.ts +24 -0
- package/dist/cli/observer-follow.js +48 -3
- package/dist/cli/orientation.js +16 -14
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +11 -0
- package/dist/evidence/encoded-text.d.ts +9 -6
- package/dist/evidence/encoded-text.js +84 -19
- package/dist/evidence/redaction.d.ts +2 -0
- package/dist/evidence/redaction.js +134 -21
- package/dist/feedback/export-bundle.js +2 -2
- package/dist/keys/key-resolution.js +6 -5
- package/dist/observer/exposure.js +1 -1
- package/dist/observer/library.js +3 -2
- package/dist/observer/render.js +2 -1
- package/dist/observer/run-routes.js +8 -1
- package/dist/observer/serve.js +2 -1
- package/dist/routes/computer-use/participant-runs.d.ts +6 -9
- package/dist/routes/computer-use/participant-runs.js +16 -36
- package/dist/routes/computer-use/plan.d.ts +2 -2
- package/dist/routes/computer-use/plan.js +6 -1
- package/dist/routes/computer-use/route.d.ts +5 -18
- package/dist/routes/computer-use/route.js +17 -78
- package/dist/routes/computer-use/setup.d.ts +3 -2
- package/dist/routes/computer-use/types.d.ts +6 -10
- package/dist/routes/scripted/route.d.ts +4 -17
- package/dist/routes/scripted/route.js +10 -42
- package/dist/routes/scripted/types.d.ts +1 -6
- package/dist/routes/shared-world/plan.js +5 -1
- package/dist/routes/shared-world/route.d.ts +5 -11
- package/dist/routes/shared-world/route.js +24 -63
- package/dist/routes/shared-world/types.d.ts +2 -6
- package/dist/routes/terminal/bundle.js +2 -1
- package/dist/routes/terminal/credentials.d.ts +5 -5
- package/dist/routes/terminal/credentials.js +14 -6
- package/dist/routes/terminal/plan.js +8 -0
- package/dist/routes/terminal/route.d.ts +3 -13
- package/dist/routes/terminal/route.js +24 -63
- package/dist/routes/terminal/runtime-auth.d.ts +6 -0
- package/dist/routes/terminal/runtime-auth.js +5 -0
- package/dist/routes/terminal/session.d.ts +2 -3
- package/dist/routes/terminal/session.js +2 -3
- package/dist/routes/terminal/types.d.ts +2 -7
- package/dist/run/dry-run.js +2 -1
- package/dist/run/judge.js +2 -1
- package/dist/run/local-only-files.d.ts +13 -0
- package/dist/run/local-only-files.js +35 -0
- package/dist/run/reclaim.d.ts +5 -0
- package/dist/run/reclaim.js +34 -2
- package/dist/run/route-shell.d.ts +60 -0
- package/dist/run/route-shell.js +51 -0
- package/dist/run/run-not-found.js +3 -2
- package/dist/run/run.d.ts +6 -1
- package/dist/run/run.js +11 -2
- package/dist/run/sandbox-creates.js +5 -3
- package/dist/run/sandbox-receipts.d.ts +6 -5
- package/dist/run/sandbox-receipts.js +12 -7
- package/dist/run/status.d.ts +2 -0
- package/dist/run/status.js +5 -0
- package/dist/study/agents-section.d.ts +21 -0
- package/dist/study/agents-section.js +86 -0
- package/dist/study/composition-rules.js +1 -1
- package/dist/study/doctor.js +5 -4
- package/dist/study/init-templates.d.ts +1 -1
- package/dist/study/init-templates.js +43 -129
- package/dist/study/init.js +32 -35
- package/dist/study/local-browser-host.d.ts +11 -0
- package/dist/study/local-browser-host.js +42 -0
- package/dist/study/parse/actors.js +9 -1
- package/dist/study/parse/subject.js +13 -1
- package/dist/study/parse/url-credentials.d.ts +9 -0
- package/dist/study/parse/url-credentials.js +50 -0
- package/dist/study/preflight-probes.js +2 -1
- package/dist/study/types.d.ts +8 -6
- package/dist/study/url-credentials.d.ts +6 -0
- package/dist/study/url-credentials.js +34 -0
- package/dist/substrates/e2b/sandbox.js +33 -15
- package/dist/substrates/local/firecracker-desktop.js +3 -2
- package/dist/substrates/local/runtime-host.js +5 -4
- package/dist/substrates/local/runtime.js +5 -4
- package/dist/tui/contract.js +5 -4
- package/dist/verify/liveness.js +2 -1
- package/dist/verify/verify.js +2 -1
- package/docs/architecture/actor-contract.md +1 -1
- package/docs/architecture/external-public-shared-world.md +1 -1
- package/docs/architecture/terminal-product-route.md +26 -24
- package/docs/contracts/run-bundle.md +20 -4
- package/docs/contracts/schemas.md +6 -5
- package/docs/contracts/study-analysis.md +5 -2
- package/docs/product/automatic-analysis.md +1 -1
- package/package.json +4 -2
- package/skills/humanish/SKILL.md +22 -18
|
@@ -3,26 +3,124 @@ import { realpathSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { PNG } from "pngjs";
|
|
5
5
|
import { SCREENSHOT_MAX_WIDTH_CAP, pngDecodeRefusal } from "./image.js";
|
|
6
|
+
import { isRecord } from "../run/type-guards.js";
|
|
6
7
|
// Single source of truth for public-safety redaction patterns. Producers and the verify gate
|
|
7
8
|
// both use these, so the denylist cannot drift between them. See docs/contracts/policy.md for
|
|
8
9
|
// the enforcement-scope policy.
|
|
10
|
+
// A whole value that stands in for a secret rather than being one, as written or percent-encoded:
|
|
11
|
+
// a redaction marker, a shell or template variable, a placeholder in angle brackets, or a mask of
|
|
12
|
+
// asterisks. A context pattern skips a value only when one of these is all of it, up to a
|
|
13
|
+
// character that ends that pattern's value, so a password that merely starts with `*` or `$`
|
|
14
|
+
// still counts.
|
|
15
|
+
const PLACEHOLDER = [
|
|
16
|
+
String.raw `\[REDACTED_[A-Z0-9_]{1,40}\]`,
|
|
17
|
+
String.raw `%5[Bb]REDACTED_[A-Z0-9_]{1,40}%5[Dd]`,
|
|
18
|
+
String.raw `\$\{[A-Za-z_][A-Za-z0-9_]{0,63}\}`,
|
|
19
|
+
String.raw `%24%7[Bb][A-Za-z_][A-Za-z0-9_]{0,63}%7[Dd]`,
|
|
20
|
+
String.raw `\$[A-Za-z_][A-Za-z0-9_]{0,63}`,
|
|
21
|
+
String.raw `%24[A-Za-z_][A-Za-z0-9_]{0,63}`,
|
|
22
|
+
String.raw `\{\{[A-Za-z0-9_. -]{1,64}\}\}`,
|
|
23
|
+
String.raw `<[A-Za-z0-9_. -]{1,64}>`,
|
|
24
|
+
String.raw `%3[Cc][A-Za-z0-9_.-]{1,64}%3[Ee]`,
|
|
25
|
+
String.raw `\*{3,64}`,
|
|
26
|
+
String.raw `(?:%2[Aa]){3,64}`,
|
|
27
|
+
].join("|");
|
|
28
|
+
const notAPlaceholder = (valueEnd) => String.raw `(?!(?:${PLACEHOLDER})(?:${valueEnd}|$))`;
|
|
29
|
+
// An optional quote around a name or a value, also as JSON writes it inside other JSON strings,
|
|
30
|
+
// four levels deep (\", \\\", ...).
|
|
31
|
+
const QUOTE = String.raw `(?:\\{0,15}["'])?`;
|
|
32
|
+
// A JSON number, which a credential-named key can hold and redaction must not turn into a string.
|
|
33
|
+
// The bounds are far past any number a run writes and keep the lookahead linear.
|
|
34
|
+
const NOT_A_NUMBER = String.raw `(?!-?[0-9]{1,512}(?:\.[0-9]{0,512})?(?:[eE][+-]?[0-9]{1,64})?(?:[\s,}\]]|$))`;
|
|
35
|
+
// The characters that end each context pattern's value.
|
|
36
|
+
const USERINFO_END = String.raw `[\s@/?#"'<>\\]`;
|
|
37
|
+
const PARAMETER_END = String.raw `[\s&#"'<>\\]`;
|
|
38
|
+
const VARIABLE_END = String.raw `[\s"'\\,;&]`;
|
|
39
|
+
// Query and fragment parameters that carry a credential. `page_token` and other cursors match too;
|
|
40
|
+
// their values are long and opaque, so a reader cannot tell them from a credential either.
|
|
41
|
+
const CREDENTIAL_PARAMETER = [
|
|
42
|
+
String.raw `(?:[a-z0-9]{1,24}[_-]){0,2}token`,
|
|
43
|
+
String.raw `api[_-]?key`,
|
|
44
|
+
String.raw `(?:client[_-])?secret`,
|
|
45
|
+
"password",
|
|
46
|
+
"passwd",
|
|
47
|
+
"pwd",
|
|
48
|
+
"sig",
|
|
49
|
+
"signature",
|
|
50
|
+
String.raw `session[_-]?id`,
|
|
51
|
+
"jwt",
|
|
52
|
+
"x-vercel-protection-bypass",
|
|
53
|
+
"_vercel_share",
|
|
54
|
+
String.raw `x-amz-(?:signature|security-token|credential)`,
|
|
55
|
+
String.raw `x-goog-(?:signature|credential)`,
|
|
56
|
+
"auth",
|
|
57
|
+
].join("|");
|
|
58
|
+
// Upper-case variable names that hold a credential, as an env file, a shell or a JSON dump writes
|
|
59
|
+
// them: GITHUB_TOKEN, VERCEL_TOKEN, AWS_SECRET_ACCESS_KEY, DATABASE_PASSWORD.
|
|
60
|
+
const CREDENTIAL_VARIABLE = String.raw `\b(?:[A-Z][A-Z0-9_]{0,48}_)?(?:TOKEN|SECRET|SECRET_KEY|PASSWORD|PASSWD|API_KEY|APIKEY|ACCESS_KEY|PRIVATE_KEY)`;
|
|
61
|
+
// The public E2B pages its errors link to, which name no sandbox and carry no credential: the
|
|
62
|
+
// dashboard as the SDK's missing-key error names it (https://e2b.dev/dashboard?tab=keys), with no
|
|
63
|
+
// path below /dashboard and no query but one short `tab`, and a docs page as the API's 401 names
|
|
64
|
+
// it (https://docs.e2b.dev/api-key), whose path segments hold only letters and hyphens and which
|
|
65
|
+
// has no query. Neither has user info, and only closing punctuation, ")" included, may follow
|
|
66
|
+
// before the next space or the end of the text.
|
|
67
|
+
const E2B_PUBLIC_PAGE = [
|
|
68
|
+
String.raw `(?:www\.)?e2b\.dev\/dashboard\/?(?:\?tab=[a-z]{1,16})?`,
|
|
69
|
+
String.raw `(?:docs\.e2b\.dev|(?:www\.)?e2b\.dev\/docs)(?:\/[a-z][a-z-]{0,39}){0,8}\/?`,
|
|
70
|
+
].join("|");
|
|
71
|
+
// A pattern that matches a credential by its context puts the context in a `keep` group, which
|
|
72
|
+
// redaction leaves in place: `?token=[REDACTED_SECRET]`. Context goes in a group and not in a
|
|
73
|
+
// lookbehind, because a pattern that starts with a lookbehind is tried at every position of the
|
|
74
|
+
// text and scans many times slower. An open-ended repeat is written `[...]{n}[...]*`: V8 runs
|
|
75
|
+
// `{n,}` with a backtrack entry per character and overflows its stack on a run of several
|
|
76
|
+
// megabytes, and a star loop does not.
|
|
9
77
|
const SECRET_PATTERNS = [
|
|
10
|
-
/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20
|
|
11
|
-
/\bsk-ant-[A-Za-z0-9_-]{20
|
|
12
|
-
/\be2b_[A-Za-z0-9]{16
|
|
13
|
-
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20
|
|
14
|
-
/\bgithub_pat_[A-Za-z0-9_]{20
|
|
15
|
-
/\
|
|
16
|
-
/\
|
|
17
|
-
|
|
18
|
-
/\
|
|
19
|
-
/\
|
|
20
|
-
/\
|
|
78
|
+
/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20}[A-Za-z0-9_-]*\b/g,
|
|
79
|
+
/\bsk-ant-[A-Za-z0-9_-]{20}[A-Za-z0-9_-]*\b/g,
|
|
80
|
+
/\be2b_[A-Za-z0-9]{16}[A-Za-z0-9]*\b/g,
|
|
81
|
+
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20}[A-Za-z0-9_]*\b/g,
|
|
82
|
+
/\bgithub_pat_[A-Za-z0-9_]{20}[A-Za-z0-9_]*\b/g,
|
|
83
|
+
/\bglpat-[A-Za-z0-9_-]{20}[A-Za-z0-9_-]*/g,
|
|
84
|
+
/\bnpm_[A-Za-z0-9]{36}\b/g,
|
|
85
|
+
// Vercel personal, integration, app access, app refresh and API key tokens.
|
|
86
|
+
/\bvc[pciark]_[A-Za-z0-9]{24}[A-Za-z0-9]*\b/g,
|
|
87
|
+
/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
|
|
88
|
+
/\bAIza[0-9A-Za-z_-]{20}[0-9A-Za-z_-]*\b/g,
|
|
89
|
+
/\bya29\.[0-9A-Za-z_-]{20}[0-9A-Za-z_-]*/g,
|
|
90
|
+
/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16}[A-Za-z0-9]*\b/g,
|
|
91
|
+
/\bwhsec_[A-Za-z0-9]{24}[A-Za-z0-9]*\b/g,
|
|
92
|
+
/\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g,
|
|
93
|
+
/\bhf_[A-Za-z0-9]{30}[A-Za-z0-9]*\b/g,
|
|
94
|
+
/\bxox[abeoprs]-[A-Za-z0-9-]{20}[A-Za-z0-9-]*\b/g,
|
|
95
|
+
/\bxapp-[0-9]-[A-Za-z0-9-]{20}[A-Za-z0-9-]*\b/g,
|
|
96
|
+
/\bhooks\.slack\.com\/(?:services|workflows|triggers)\/[A-Za-z0-9_/-]{20}[A-Za-z0-9_/-]*/g,
|
|
97
|
+
// A JWT starts only where a token run starts, so a run of `eyJ-` repeated is read once.
|
|
98
|
+
/eyJ(?<![A-Za-z0-9_-]eyJ)[A-Za-z0-9_-]{20}[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{20}[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{10}[A-Za-z0-9_-]*\b/g,
|
|
21
99
|
/-----BEGIN [A-Z ]*PRIVATE KEY-----/g,
|
|
22
|
-
/\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/[^:@/\s]+:[^@/\s]
|
|
23
|
-
/_authToken\s*=\s*[A-Za-z0-9._~+/=-]{20
|
|
24
|
-
/\bBearer\s+[A-Za-z0-9._~+/-]{24
|
|
25
|
-
|
|
100
|
+
/\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/[^:@/\s"'<>\\]+:[^@/\s"'<>\\]+@[^\s"'<>\\]+/g,
|
|
101
|
+
/_authToken\s*=\s*[A-Za-z0-9._~+/=-]{20}[A-Za-z0-9._~+/=-]*/g,
|
|
102
|
+
/\bBearer\s+[A-Za-z0-9._~+/-]{24}[A-Za-z0-9._~+/-]*\b/g,
|
|
103
|
+
new RegExp(String.raw `(?<keep>\bAuthorization${QUOTE}[ \t]{0,4}[:=][ \t]{0,4}${QUOTE}Basic[ \t]{1,4})[A-Za-z0-9+/]{16}[A-Za-z0-9+/]*={0,2}`, "gi"),
|
|
104
|
+
// The password in a URL's userinfo, any scheme, with or without a user name:
|
|
105
|
+
// https://user:password@host, https://:password@host.
|
|
106
|
+
new RegExp(String.raw `(?<keep>\b[a-z][a-z0-9+.-]{0,31}:\/\/[^\s:@/?#"'<>\\[\]]{0,256}:)${notAPlaceholder(USERINFO_END)}[^\s@/?#"'<>\\]{1,256}(?=@)`, "gi"),
|
|
107
|
+
// A credential-named query or fragment parameter's value, up to the next delimiter, so a
|
|
108
|
+
// password with punctuation in it counts whole. `;` also follows an HTML `&`.
|
|
109
|
+
new RegExp(String.raw `(?<keep>[?&#;](?:${CREDENTIAL_PARAMETER})=)${notAPlaceholder(PARAMETER_END)}[^\s&#"'<>\\]{16}[^\s&#"'<>\\]*`, "gi"),
|
|
110
|
+
// An Amazon Web Services secret access key next to its name, as a credentials file, an env
|
|
111
|
+
// line or a temporary-credentials JSON response writes it. The key is 40 characters, no prefix.
|
|
112
|
+
new RegExp(String.raw `(?<keep>\b(?:aws_?)?secret_?access_?key${QUOTE}[ \t]{0,4}[:=][ \t]{0,4}${QUOTE})${NOT_A_NUMBER}[A-Za-z0-9/+]{40}(?![A-Za-z0-9/+=])`, "gi"),
|
|
113
|
+
// A credential-named variable set to a value of 16 characters or more, up to the next space,
|
|
114
|
+
// quote or separator, with a digit and a letter in its first 256 characters. Words, placeholders
|
|
115
|
+
// such as humanish-egress-auth-placeholder and JSON numbers do not count. The lookaheads are
|
|
116
|
+
// bounded, so a long run of `TOKEN=` costs linear time.
|
|
117
|
+
new RegExp(String.raw `(?<keep>${CREDENTIAL_VARIABLE}${QUOTE}[ \t]{0,4}[:=][ \t]{0,4}${QUOTE})${notAPlaceholder(VARIABLE_END)}${NOT_A_NUMBER}(?=[^\s"'\\,;&]{0,255}[0-9])(?=[^\s"'\\,;&]{0,255}[A-Za-z])[^\s"'\\,;&]{16}[^\s"'\\,;&]*`, "g"),
|
|
118
|
+
// Any URL on an E2B host: a sandbox host names its sandbox, and a stream URL carries its auth
|
|
119
|
+
// key. The slashes may be JSON-escaped (`https:\/\/`). User info runs to its last `@`, as a URL
|
|
120
|
+
// parser reads it, and may hold any character but a slash or space. The host part stops at a
|
|
121
|
+
// backslash, quote or angle bracket, so a URL followed by an escaped line break and an `E2B_...`
|
|
122
|
+
// variable in JSON text is not read as an E2B host. A public page followed by `)@` is user info.
|
|
123
|
+
new RegExp(String.raw `(?!https:\/\/(?:${E2B_PUBLIC_PAGE})[.,;:!?'")]*(?:\s|$))https?:\\?\/\\?\/(?:[^/\s@]*@)*[^/\s\\"'<>@]*e2b[^)\s]+`, "gi"),
|
|
26
124
|
/BEGIN (RSA|OPENSSH|PRIVATE) KEY/gi,
|
|
27
125
|
];
|
|
28
126
|
const LOCAL_PATH_PATTERNS = [
|
|
@@ -32,6 +130,12 @@ const LOCAL_PATH_PATTERNS = [
|
|
|
32
130
|
[/\/tmp\/[^\s"'`<>)]*/g, "[REDACTED_LOCAL_PATH]"],
|
|
33
131
|
[/\/Users\/[A-Za-z0-9._-]+(?:\/[^\s"'`<>)]*)?/g, "[REDACTED_LOCAL_PATH]"],
|
|
34
132
|
[/\/home\/[A-Za-z0-9._-]+(?:\/[^\s"'`<>)]*)?/g, "[REDACTED_RUNTIME_PATH]"],
|
|
133
|
+
// A Windows profile path, with `\`, `\\` (JSON-escaped) or `/` between segments. A match never
|
|
134
|
+
// ends in a backslash, so it cannot swallow the escape of a closing quote.
|
|
135
|
+
[
|
|
136
|
+
/\b[A-Za-z]:(?:\\\\|\\|\/)Users(?:\\\\|\\|\/)[^\\/\s"'`<>)]+[^\s"'`<>)]*/g,
|
|
137
|
+
"[REDACTED_LOCAL_PATH]",
|
|
138
|
+
],
|
|
35
139
|
];
|
|
36
140
|
/** Every pattern containsSensitive tests. A test pins each to ASCII, which scanEncodedText relies on. */
|
|
37
141
|
export function sensitivePatterns() {
|
|
@@ -43,10 +147,13 @@ function matchesPattern(pattern, text) {
|
|
|
43
147
|
pattern.lastIndex = 0;
|
|
44
148
|
return pattern.test(text);
|
|
45
149
|
}
|
|
150
|
+
/** True if the text contains a secret-shaped token. Local paths do not count. */
|
|
151
|
+
export function containsSecret(text) {
|
|
152
|
+
return SECRET_PATTERNS.some((pattern) => matchesPattern(pattern, text));
|
|
153
|
+
}
|
|
46
154
|
/** True if the text contains any secret-shaped token or known local path. */
|
|
47
155
|
export function containsSensitive(text) {
|
|
48
|
-
return (
|
|
49
|
-
LOCAL_PATH_PATTERNS.some(([pattern]) => matchesPattern(pattern, text)));
|
|
156
|
+
return (containsSecret(text) || LOCAL_PATH_PATTERNS.some(([pattern]) => matchesPattern(pattern, text)));
|
|
50
157
|
}
|
|
51
158
|
// A local-path match may end in the backslashes escaping its closing quote in
|
|
52
159
|
// serialized JSON (including JSON nested inside a terminal event). Keep that
|
|
@@ -142,15 +249,21 @@ export function redactSandboxIds(value) {
|
|
|
142
249
|
entries.some(([key, child]) => child !== value[key]);
|
|
143
250
|
return changed ? Object.fromEntries(entries) : value;
|
|
144
251
|
}
|
|
252
|
+
/** Each secret replaced by [REDACTED_SECRET], with the context a pattern keeps left in place. */
|
|
253
|
+
function redactSecrets(text) {
|
|
254
|
+
return SECRET_PATTERNS.reduce((current, pattern) => current.replace(pattern, (...args) => {
|
|
255
|
+
const groups = args.at(-1);
|
|
256
|
+
const keep = isRecord(groups) && typeof groups.keep === "string" ? groups.keep : "";
|
|
257
|
+
return `${keep}[REDACTED_SECRET]`;
|
|
258
|
+
}), text);
|
|
259
|
+
}
|
|
145
260
|
/** Redact secrets to [REDACTED_SECRET] and local paths to their path labels. */
|
|
146
261
|
export function redactText(text) {
|
|
147
|
-
|
|
148
|
-
return redactLocalPaths(withoutSecrets);
|
|
262
|
+
return redactLocalPaths(redactSecrets(text));
|
|
149
263
|
}
|
|
150
264
|
/** Redact every sensitive match (secrets and paths) to a single [REDACTED_SECRET] label. */
|
|
151
265
|
export function redactToSecretLabel(text) {
|
|
152
|
-
|
|
153
|
-
return redactLocalPaths(withoutSecrets, "[REDACTED_SECRET]");
|
|
266
|
+
return redactLocalPaths(redactSecrets(text), "[REDACTED_SECRET]");
|
|
154
267
|
}
|
|
155
268
|
function canonicalizePath(value) {
|
|
156
269
|
const resolved = path.resolve(value);
|
|
@@ -18,6 +18,7 @@ import { loadRunBundlePrepared, resolveRunPath } from "../run/locate.js";
|
|
|
18
18
|
import {} from "../run/bundle.js";
|
|
19
19
|
import { runIdOf, isPathInside, prepareRunArtifactPaths, validatePreparedRunArtifactPaths, } from "../run/paths.js";
|
|
20
20
|
import { isAnalysisRecordPath } from "../analysis/sharing.js";
|
|
21
|
+
import { LOCAL_ONLY_RUN_FILES } from "../run/local-only-files.js";
|
|
21
22
|
import { assertPreparedSelectedOutputDirectory, prepareManagedHumanishOutputDirectory, prepareSelectedOutputDirectory, readContainedRegularFile, writeContainedOutputFile, } from "../run/contained-output.js";
|
|
22
23
|
const DERIVATION_SCHEMA = "humanish.redacted-derivation.v1";
|
|
23
24
|
const MAX_FILES = 10_000;
|
|
@@ -36,8 +37,7 @@ const OMITTED = new Map([
|
|
|
36
37
|
["observer/index.html", "regenerated Observer"],
|
|
37
38
|
["observer/observer-data.json", "regenerated Observer projection"],
|
|
38
39
|
["observer/study-analysis.json", "analysis must be regenerated against derivative evidence"],
|
|
39
|
-
|
|
40
|
-
["sandbox-receipts.ndjson", "operational journal does not confer a derivative resource lease"],
|
|
40
|
+
...LOCAL_ONLY_RUN_FILES,
|
|
41
41
|
]);
|
|
42
42
|
const PNG_MAGIC = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
43
43
|
function hash(bytes) {
|
|
@@ -21,6 +21,7 @@ import { chmodSync, closeSync, constants as fsConstants, existsSync, ftruncateSy
|
|
|
21
21
|
import { homedir } from "node:os";
|
|
22
22
|
import path from "node:path";
|
|
23
23
|
import { loadEnvFile } from "./env-file.js";
|
|
24
|
+
import { cli } from "../cli/invocation.js";
|
|
24
25
|
/** The only names implicit discovery may fill (and `humanish keys set` may store). Everything
|
|
25
26
|
* else in an overlay/store file is ignored-and-named: a repo-planted NODE_OPTIONS/LD_PRELOAD
|
|
26
27
|
* must never enter process env off a file the operator did not explicitly pass (an explicit
|
|
@@ -235,16 +236,16 @@ function storeLabel(env, deps) {
|
|
|
235
236
|
export function missingKeyHint(name) {
|
|
236
237
|
switch (name) {
|
|
237
238
|
case "E2B_API_KEY":
|
|
238
|
-
return
|
|
239
|
+
return `run \`e2b auth login\`, or \`${cli("keys set e2b")}\``;
|
|
239
240
|
case "GH_TOKEN":
|
|
240
241
|
case "GITHUB_TOKEN":
|
|
241
|
-
return
|
|
242
|
+
return `run \`gh auth login\`, or \`${cli("keys set github")}\``;
|
|
242
243
|
case "OPENAI_API_KEY":
|
|
243
|
-
return
|
|
244
|
+
return `run \`${cli("keys set openai")}\``;
|
|
244
245
|
case "ANTHROPIC_API_KEY":
|
|
245
|
-
return
|
|
246
|
+
return `run \`${cli("keys set anthropic")}\``;
|
|
246
247
|
default:
|
|
247
|
-
return `run
|
|
248
|
+
return `run \`${cli(`keys set ${name}`)}\``;
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
/** One shared suffix for *_KEYS_MISSING messages: where discovery looked, and what fills each
|
|
@@ -73,7 +73,7 @@ export function validateExposure(surface, request, live) {
|
|
|
73
73
|
// admit nothing and expose nothing: watch --expose always requires edge auth, and the live-follow
|
|
74
74
|
// preconditions are checked first so `--dry-run`/`--detach`/`--json` fail with the clearer reason.
|
|
75
75
|
if (live && (live.dryRun || live.detach || live.json)) {
|
|
76
|
-
return fail("EXPOSE_REQUIRES_LIVE_FOLLOW", "watch --expose streams a live desktop over an attached follow channel
|
|
76
|
+
return fail("EXPOSE_REQUIRES_LIVE_FOLLOW", "watch --expose streams a live desktop over an attached follow channel. It cannot combine with --dry-run, --detach or --json, and without an interactive terminal it needs --serve.");
|
|
77
77
|
}
|
|
78
78
|
// --safe is a share_ready library filter for `serve`; watch streams a single live run that is
|
|
79
79
|
// never share_ready, so --safe would silently do nothing here. Reject it rather than ignore it.
|
package/dist/observer/library.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cli } from "../cli/invocation.js";
|
|
1
2
|
// The pip color for each RunDisplay tone; a run with no display gets the neutral gray.
|
|
2
3
|
const DISPLAY_TONES = {
|
|
3
4
|
pass: "#3fb970",
|
|
@@ -8,8 +9,8 @@ const DISPLAY_TONES = {
|
|
|
8
9
|
};
|
|
9
10
|
export function renderLibraryHtml(history, opts) {
|
|
10
11
|
const emptyState = opts.safe
|
|
11
|
-
?
|
|
12
|
-
:
|
|
12
|
+
? `No share_ready runs yet. Run \`${cli("verify")}\` to see why.`
|
|
13
|
+
: `No runs yet. Run \`${cli("run first-run")}\` to create one.`;
|
|
13
14
|
return `<!doctype html>
|
|
14
15
|
<html lang="en" data-theme="dark">
|
|
15
16
|
<head>
|
package/dist/observer/render.js
CHANGED
|
@@ -20,6 +20,7 @@ import { buildServeSecurityHeaders, hostAllowed, parsePublicOrigin } from "./htt
|
|
|
20
20
|
import { listenOnLoopback } from "./listen.js";
|
|
21
21
|
import { pinDirectChildDirectory, pinDirectory, readContainedFile, } from "./pinned-files.js";
|
|
22
22
|
import { buildHistoryIndex, matchRunRoute, serveRunPath, writeResponse, } from "./run-routes.js";
|
|
23
|
+
import { cli } from "../cli/invocation.js";
|
|
23
24
|
const OBSERVER_SCHEMA = "humanish.observer-result.v1";
|
|
24
25
|
const observerRuntimeStreamUrls = new WeakMap();
|
|
25
26
|
const observerPreparedRunPaths = new WeakMap();
|
|
@@ -136,7 +137,7 @@ export async function renderObserver(cwdInput, runInput, options = {}) {
|
|
|
136
137
|
loaded.bundle.mode === "live"
|
|
137
138
|
? "Observer renders verified local evidence artifacts; runtime stream auth URLs are not persisted."
|
|
138
139
|
: "This is a dry run: its participants are synthetic and no product behavior was tested.",
|
|
139
|
-
|
|
140
|
+
`Before filing public feedback, use \`${cli("feedback issue")}\` so redaction and public-safety checks gate the payload.`,
|
|
140
141
|
...(openResult.warning ? [openResult.warning] : []),
|
|
141
142
|
],
|
|
142
143
|
};
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { isAnalysisRecordPath, projectShareCheckedAnalysis } from "../analysis/sharing.js";
|
|
6
6
|
import { loadAnalysis } from "../analysis/load.js";
|
|
7
|
+
import { isLocalOnlyRunFile, isLocalOnlyRunFileIdentity } from "../run/local-only-files.js";
|
|
7
8
|
import { listRuns } from "../run/stored-runs.js";
|
|
8
9
|
import { readAnalysisAccounting } from "../run/costs.js";
|
|
9
10
|
import { bindExistingRunArtifactPaths, isPathInside, isSafeRunIdSegment, } from "../run/paths.js";
|
|
@@ -70,9 +71,11 @@ export async function serveRunPath(runRoot, relativePath, response, runtimeStrea
|
|
|
70
71
|
// file serving bypass its current-content checks or a warmed source-only admission cache.
|
|
71
72
|
const derivedRoot = cleanedRelativePath.split("/")[0];
|
|
72
73
|
const derivedLeaf = path.posix.basename(cleanedRelativePath);
|
|
74
|
+
// Local-only files (raw sandbox ids, the recording pid) are read in process, never served.
|
|
73
75
|
if (derivedRoot === ".analysis-lock" ||
|
|
74
76
|
derivedLeaf.startsWith(".humanish-write-") ||
|
|
75
|
-
isAnalysisRecordPath(cleanedRelativePath)
|
|
77
|
+
isAnalysisRecordPath(cleanedRelativePath) ||
|
|
78
|
+
isLocalOnlyRunFile(cleanedRelativePath)) {
|
|
76
79
|
writeResponse(response, 404, "Not found", "text/plain; charset=utf-8");
|
|
77
80
|
return;
|
|
78
81
|
}
|
|
@@ -103,6 +106,10 @@ export async function serveRunPath(runRoot, relativePath, response, runtimeStrea
|
|
|
103
106
|
await serveContainedMedia(runRoot, filePath, response, request);
|
|
104
107
|
return;
|
|
105
108
|
}
|
|
109
|
+
if (await isLocalOnlyRunFileIdentity(root, filePath)) {
|
|
110
|
+
writeResponse(response, 404, "Not found", "text/plain; charset=utf-8");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
106
113
|
try {
|
|
107
114
|
const body = await readContainedFile(runRoot, filePath);
|
|
108
115
|
if (!body) {
|
package/dist/observer/serve.js
CHANGED
|
@@ -10,6 +10,7 @@ import { buildServeSecurityHeaders, hostAllowed, parsePublicOrigin, } from "./ht
|
|
|
10
10
|
import { isSafeRunIdSegment } from "../run/paths.js";
|
|
11
11
|
import { listRuns } from "../run/stored-runs.js";
|
|
12
12
|
import { verifyRun } from "../verify/verify.js";
|
|
13
|
+
import { cli } from "../cli/invocation.js";
|
|
13
14
|
export const SERVE_SCHEMA = "humanish.serve-result.v1";
|
|
14
15
|
export function createShareSafetyAdmission(cwd, options = {}) {
|
|
15
16
|
const verifyImpl = options.verifyImpl ?? verifyRun;
|
|
@@ -196,7 +197,7 @@ export async function serveObserverLibrary(cwdInput, options) {
|
|
|
196
197
|
ok: false,
|
|
197
198
|
error: {
|
|
198
199
|
code: "HUMANISH_RUN_NOT_FOUND",
|
|
199
|
-
message:
|
|
200
|
+
message: `No run library found under .humanish/runs. Run \`${cli("run first-run")}\` to create the first run.`,
|
|
200
201
|
},
|
|
201
202
|
};
|
|
202
203
|
}
|
|
@@ -3,7 +3,7 @@ import { type StudyConfig } from "../../study/types.js";
|
|
|
3
3
|
import { type ResolvedPersona } from "../../study/persona.js";
|
|
4
4
|
import { type RunRerunLineage } from "../../run/bundle.js";
|
|
5
5
|
import type { PreparedSelectedOutputDirectory } from "../../run/contained-output.js";
|
|
6
|
-
import { type CuaActorStudyErrorCode, type CuaParticipantPlan, type CuaParticipantPlanEntry, type DesktopParticipantRun } from "./types.js";
|
|
6
|
+
import { type CuaActorStudyErrorCode, type CuaParticipantPlan, type CuaParticipantPlanEntry, type DesktopParticipantRun, type ParticipantRunsAndPlan } from "./types.js";
|
|
7
7
|
export declare function defaultSessionTimeoutMs(config: StudyConfig): number;
|
|
8
8
|
/** Per-participant sandbox deadline (each one owns its own desktop). Mirrors the
|
|
9
9
|
* single-participant formula verbatim so N=1 stays byte-stable: explicit sandboxTimeoutMs, else session budget + (clone
|
|
@@ -21,17 +21,14 @@ export declare function resolveParticipantSandboxMs(config: StudyConfig): number
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function boundedConcurrency(declared: number | undefined, participantCount: number): number;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* marked $0 (dryRun: true).
|
|
24
|
+
* Build the participant runs and the public plan from a computer-use plan (pure). The same plan
|
|
25
|
+
* appears in dry-run and live, marked $0 for a dry run. `env` may lower the planned concurrency;
|
|
26
|
+
* `personas` are the compiled committed personas the prompts carry.
|
|
28
27
|
*/
|
|
29
|
-
export declare function
|
|
30
|
-
countOverride?: number;
|
|
28
|
+
export declare function participantRunsAndPlan(plan: ComputerUsePlan, opts?: {
|
|
31
29
|
env?: Record<string, string | undefined>;
|
|
32
|
-
dryRun?: boolean;
|
|
33
30
|
personas?: Map<string, ResolvedPersona>;
|
|
34
|
-
}):
|
|
31
|
+
}): ParticipantRunsAndPlan;
|
|
35
32
|
/** Print the participant plan to stderr before any sandbox/provider call (public-safe: ids, devices,
|
|
36
33
|
* digests, and budgets only; no prompt text, no secrets). */
|
|
37
34
|
export declare function emitPreflightPlan(participantPlan: CuaParticipantPlan, studyId: string): void;
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// compiles the committed personas first and narrows a rerun to its selected participants.
|
|
5
5
|
import { isLocalBrowserStudy, LOCAL_BROWSER_LIFETIME_MS, } from "../../substrates/local/runtime-config.js";
|
|
6
6
|
import { DEFAULT_STATE_STEP_TIMEOUT_MS } from "../../subject/state.js";
|
|
7
|
-
import { computerUseParticipants, } from "../../study/plan-participants.js";
|
|
8
7
|
import { resolveParticipant } from "../../run/participant.js";
|
|
9
8
|
import {} from "../../study/types.js";
|
|
10
9
|
import { scrubPersonaBrief } from "../../study/persona.js";
|
|
@@ -71,23 +70,17 @@ function envLoweredConcurrency(planned, participantCount, env) {
|
|
|
71
70
|
}
|
|
72
71
|
return { bound: planned };
|
|
73
72
|
}
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
sessionBudgetMs: config.execution?.timeoutMs ?? defaultSessionTimeoutMs(config),
|
|
81
|
-
sandboxMs: resolveParticipantSandboxMs(config),
|
|
82
|
-
desktopCli: config.subject.source === "desktop-cli",
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/** Build the participant runs and the public plan from the planned participants (pure). */
|
|
86
|
-
function participantRunsAndPlan(planned, opts = {}) {
|
|
73
|
+
/**
|
|
74
|
+
* Build the participant runs and the public plan from a computer-use plan (pure). The same plan
|
|
75
|
+
* appears in dry-run and live, marked $0 for a dry run. `env` may lower the planned concurrency;
|
|
76
|
+
* `personas` are the compiled committed personas the prompts carry.
|
|
77
|
+
*/
|
|
78
|
+
export function participantRunsAndPlan(plan, opts = {}) {
|
|
87
79
|
const env = opts.env ?? {};
|
|
88
80
|
// Who each participant is (id, persona, focus, device, limits) comes from the plan; this adds
|
|
89
81
|
// what the route derives from it: the prompt, bundle ids and artifact paths.
|
|
90
|
-
const { participants } =
|
|
82
|
+
const { participants } = plan.runner;
|
|
83
|
+
const desktopCli = plan.runner.subject.kind === "desktop-cli";
|
|
91
84
|
const participantCount = participants.length;
|
|
92
85
|
const runs = participants.map((participant) => {
|
|
93
86
|
const mission = participant.assignment.mission ?? DEFAULT_MISSION;
|
|
@@ -102,7 +95,7 @@ function participantRunsAndPlan(planned, opts = {}) {
|
|
|
102
95
|
...(resolvedPersona === undefined ? {} : { resolvedPersona }),
|
|
103
96
|
...(focus === undefined ? {} : { instruction: focus }),
|
|
104
97
|
device: { name: device.name, preset: device.preset },
|
|
105
|
-
...(
|
|
98
|
+
...(desktopCli ? { surface: "desktop-cli" } : {}),
|
|
106
99
|
});
|
|
107
100
|
const run = resolveParticipant(participant, {
|
|
108
101
|
persona: composed.persona,
|
|
@@ -119,9 +112,9 @@ function participantRunsAndPlan(planned, opts = {}) {
|
|
|
119
112
|
traceArtifactPath: participantCount === 1 ? "actor.json" : `actors/${run.streamId}.json`,
|
|
120
113
|
};
|
|
121
114
|
});
|
|
122
|
-
const resolved = envLoweredConcurrency(
|
|
115
|
+
const resolved = envLoweredConcurrency(plan.concurrency, participantCount, env);
|
|
123
116
|
const concurrency = resolved.bound;
|
|
124
|
-
const { sessionBudgetMs, sandboxMs } =
|
|
117
|
+
const { sessionBudgetMs, sandboxMs } = plan;
|
|
125
118
|
const participantPlan = {
|
|
126
119
|
strategy: CUA_FANOUT_STRATEGY,
|
|
127
120
|
laneCount: participantCount,
|
|
@@ -132,7 +125,7 @@ function participantRunsAndPlan(planned, opts = {}) {
|
|
|
132
125
|
waves: Math.ceil(participantCount / concurrency),
|
|
133
126
|
perLaneSessionBudgetMs: sessionBudgetMs,
|
|
134
127
|
worstCaseSandboxMinutes: Math.round((participantCount * sandboxMs) / 60_000),
|
|
135
|
-
dryRun:
|
|
128
|
+
dryRun: plan.dryRun,
|
|
136
129
|
lanes: runs.map((spec) => ({
|
|
137
130
|
id: spec.planned.id,
|
|
138
131
|
...(spec.planned.labels.actorType === undefined
|
|
@@ -162,16 +155,6 @@ function participantRunsAndPlan(planned, opts = {}) {
|
|
|
162
155
|
};
|
|
163
156
|
return { runs, participantPlan };
|
|
164
157
|
}
|
|
165
|
-
/**
|
|
166
|
-
* Pure pre-flight plan resolver (runs in dry-run and live). Returns the participant table, the
|
|
167
|
-
* effective concurrency, the wave count, the per-participant session budget, and the worst-case total
|
|
168
|
-
* sandbox-minutes, before any sandbox or provider call. The same plan appears in dry-run,
|
|
169
|
-
* marked $0 (dryRun: true).
|
|
170
|
-
*/
|
|
171
|
-
export function resolveCuaParticipantPlan(config, opts = {}) {
|
|
172
|
-
const { countOverride, ...rest } = opts;
|
|
173
|
-
return participantRunsAndPlan(plannedParticipantsOf(config, countOverride), rest).participantPlan;
|
|
174
|
-
}
|
|
175
158
|
/** Print the participant plan to stderr before any sandbox/provider call (public-safe: ids, devices,
|
|
176
159
|
* digests, and budgets only; no prompt text, no secrets). */
|
|
177
160
|
export function emitPreflightPlan(participantPlan, studyId) {
|
|
@@ -216,13 +199,10 @@ export async function loadCuaParticipants(args) {
|
|
|
216
199
|
// Compile any committed personas before planning, so the plan builder stays pure and each participant's
|
|
217
200
|
// prompt carries real behavioral directives rather than a bare `Persona: <id>.` label.
|
|
218
201
|
const personas = await compileParticipantPersonas(args.projectRoot, participants.map((participant) => participant.personaId));
|
|
219
|
-
const { runs: participantRuns, participantPlan } = participantRunsAndPlan({
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
sandboxMs: plan.sandboxMs,
|
|
224
|
-
desktopCli: plan.runner.subject.kind === "desktop-cli",
|
|
225
|
-
}, { env: args.env, dryRun: plan.dryRun, personas });
|
|
202
|
+
const { runs: participantRuns, participantPlan } = participantRunsAndPlan(plan, {
|
|
203
|
+
env: args.env,
|
|
204
|
+
personas,
|
|
205
|
+
});
|
|
226
206
|
const rerun = plan.rerun;
|
|
227
207
|
if (!rerun)
|
|
228
208
|
return { ok: true, participantRuns, participantPlan };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type CuaActorDescriptor } from "../../actors/registry.js";
|
|
2
2
|
import type { ComputerUsePlan, ComputerUseRunner, RoutePlanResult, RouteRefusal } from "../../study/plan-types.js";
|
|
3
3
|
import type { StudyConfig, StudySubjectState } from "../../study/types.js";
|
|
4
|
-
import { type
|
|
4
|
+
import { type ComputerUseRunInput, type CuaActorStudyErrorCode } from "./types.js";
|
|
5
5
|
/** The error a computer-use study returns before a run starts. */
|
|
6
6
|
export interface ComputerUseRefusal extends RouteRefusal<"computer-use", CuaActorStudyErrorCode> {
|
|
7
7
|
/**
|
|
@@ -44,5 +44,5 @@ export declare function planComputerUseStudy(config: StudyConfig, input: {
|
|
|
44
44
|
/** Which of the caller's driving homes are set; neither when absent. */
|
|
45
45
|
readonly driving?: CallerDriving;
|
|
46
46
|
readonly countOverride?: number;
|
|
47
|
-
readonly rerun?:
|
|
47
|
+
readonly rerun?: ComputerUseRunInput["rerun"];
|
|
48
48
|
}): ComputerUsePlanResult;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { resolveAutomaticAnalysis } from "../../analysis/automatic-config.js";
|
|
6
6
|
import { actorRegistry, isCuaActorDescriptor, } from "../../actors/registry.js";
|
|
7
7
|
import { isHttpUrl, isLoopbackUrl } from "../../study/parse/subject.js";
|
|
8
|
+
import { studyUrlCredentialReason } from "../../study/url-credentials.js";
|
|
8
9
|
import { subjectStateInvalidReason } from "../../study/parse/subject-state.js";
|
|
9
10
|
import { brainOf, callerBrainOf, capsOf, desktopRequirements, planBase, provisionedSubject, } from "../../study/plan-base.js";
|
|
10
11
|
import { computerUseParticipants, declaredTargets } from "../../study/plan-participants.js";
|
|
@@ -134,6 +135,10 @@ function subjectStructureReason(config, subjectRoute) {
|
|
|
134
135
|
*/
|
|
135
136
|
function entryTargetReason(config, subjectRoute) {
|
|
136
137
|
const { desktopCliRoute, provisionedRoute, localAppSubject, appUrl } = subjectRoute;
|
|
138
|
+
// A library caller's config skips the parser, which refuses these first.
|
|
139
|
+
const credential = studyUrlCredentialReason(config);
|
|
140
|
+
if (credential)
|
|
141
|
+
return { code: "HUMANISH_COMPUTER_USE_SUBJECT_UNSAFE", message: credential };
|
|
137
142
|
if (desktopCliRoute)
|
|
138
143
|
return undefined;
|
|
139
144
|
const allowPublicTargets = config.policies?.allowPublicTargets === true;
|
|
@@ -227,7 +232,7 @@ export function planComputerUseStudy(config, input) {
|
|
|
227
232
|
// participants. It comes after the rules above, so a shared-world config that breaks one of them,
|
|
228
233
|
// which runStudyWith sends here, still gets that rule's message.
|
|
229
234
|
if (config.subject.topology === "shared-world")
|
|
230
|
-
return refuse("in-scope", "HUMANISH_COMPUTER_USE_SUBJECT_INVALID", "route: shared-world studies run every participant against one shared app on the shared-world route; this route runs independent participants.
|
|
235
|
+
return refuse("in-scope", "HUMANISH_COMPUTER_USE_SUBJECT_INVALID", "route: shared-world studies run every participant against one shared app on the shared-world route; this route runs independent participants. The shared-world route takes subject.source clone or local-tree, or app-url with policies.allowPublicTargets: true, on execution.target e2b-desktop.", actor);
|
|
231
236
|
// The in-process route drives subject.appUrl on this machine and creates no desktop, so it would
|
|
232
237
|
// skip the subject a clone, local-tree or desktop-cli study declares.
|
|
233
238
|
const source = config.subject.source;
|
|
@@ -1,33 +1,20 @@
|
|
|
1
|
+
import { type RefusedStudy } from "../../run/route-shell.js";
|
|
1
2
|
import type { ComputerUsePlan } from "../../study/plan-types.js";
|
|
2
3
|
import type { AdmittedPlan } from "../../run-study.js";
|
|
3
4
|
import type { StudyConfig } from "../../study/types.js";
|
|
4
5
|
import { type ComputerUseRefusal } from "./plan.js";
|
|
5
|
-
import { type ComputerUseRunInput, type CuaActorStudyResult
|
|
6
|
-
/**
|
|
7
|
-
* Plans and runs a computer-use study in one call. It is not exported from src/index.ts; tests call
|
|
8
|
-
* it. It plans the config with planComputerUseStudy and runs
|
|
9
|
-
* the plan with runComputerUsePlan, whose run scope and withTransientCommsSecrets wrapper cover the
|
|
10
|
-
* run and its analysis.
|
|
11
|
-
*/
|
|
12
|
-
export declare function runCuaActorStudy(options: RunCuaActorStudyOptions): Promise<CuaActorStudyResult>;
|
|
6
|
+
import { type ComputerUseRunInput, type CuaActorStudyResult } from "./types.js";
|
|
13
7
|
/**
|
|
14
8
|
* A refused computer-use study's result, at the refusal's stage: a before-scope refusal has its own
|
|
15
9
|
* envelope and no analysis record; the others come after the cwd checks, and the participant cap after
|
|
16
10
|
* the personas are read.
|
|
17
11
|
*/
|
|
18
|
-
export declare function computerUseStudyRefusal(options:
|
|
12
|
+
export declare function computerUseStudyRefusal(options: ComputerUseRunInput & RefusedStudy, refusal: ComputerUseRefusal): Promise<CuaActorStudyResult>;
|
|
19
13
|
/**
|
|
20
14
|
* runStudyWith's step for a computer-use plan. It runs the plan's local checks (admitCuaRun: the
|
|
21
15
|
* participants, the preflight plan, the keys, the local agent, subject env and caps, and the
|
|
22
16
|
* local-tree archive) before any run scope opens, so the CLI can present their refusal before it
|
|
23
|
-
* loads a declared scorer, and returns the run that continues from them with that scorer.
|
|
17
|
+
* loads a declared scorer, and returns the run that continues from them with that scorer. The
|
|
18
|
+
* run and its analysis share one scope for the email secrets it registers.
|
|
24
19
|
*/
|
|
25
20
|
export declare function admitComputerUsePlan(plan: ComputerUsePlan, input: ComputerUseRunInput, config: StudyConfig): Promise<AdmittedPlan<"computer-use">>;
|
|
26
|
-
/**
|
|
27
|
-
* Run a computer-use plan: its local checks, then the run. The run scope in runAdmittedCuaRun
|
|
28
|
-
* finalizes any status record the run opened, on every exit, so a test or library caller does not
|
|
29
|
-
* leave the 5 s status cadence writing into a directory something else is deleting (an unrelated
|
|
30
|
-
* ENOTEMPTY). Participants, the brain, the subject, the caps and the residual config come from the
|
|
31
|
-
* plan. `config` is what the caller's createProvider and inProcess executor receive.
|
|
32
|
-
*/
|
|
33
|
-
export declare function runComputerUsePlan(plan: ComputerUsePlan, input: ComputerUseRunInput, config: StudyConfig): Promise<CuaActorStudyResult>;
|