humanish 0.110.1 → 0.110.2
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 +1 -1
- package/CHANGELOG.md +13 -0
- package/README.md +8 -7
- 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/cli/commands/observe.js +15 -2
- 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/watch.js +14 -4
- package/dist/cli/doctor.js +9 -6
- package/dist/cli/findings.js +7 -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 +3 -1
- package/dist/cli/io.js +6 -14
- package/dist/cli/observer-follow.d.ts +20 -0
- package/dist/cli/observer-follow.js +36 -1
- 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 +123 -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/plan.js +5 -0
- package/dist/routes/shared-world/plan.js +5 -1
- 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.js +5 -4
- package/dist/routes/terminal/runtime-auth.d.ts +6 -0
- package/dist/routes/terminal/runtime-auth.js +5 -0
- 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/run-not-found.js +3 -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/study/agents-section.d.ts +21 -0
- package/dist/study/agents-section.js +86 -0
- package/dist/study/doctor.js +5 -4
- package/dist/study/init-templates.js +0 -95
- package/dist/study/init.js +31 -34
- 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 +12 -0
- 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/terminal-product-route.md +14 -12
- package/docs/contracts/run-bundle.md +20 -4
- package/docs/contracts/schemas.md +6 -5
- package/package.json +4 -2
- package/skills/humanish/SKILL.md +22 -18
|
@@ -9,6 +9,7 @@ import { ownDesktopAllocation, } from "../desktop-session.js";
|
|
|
9
9
|
import { runtimeDocker, runtimeExec, usesLima } from "./runtime-host.js";
|
|
10
10
|
import { openLimaTunnel } from "./runtime-ssh.js";
|
|
11
11
|
import { guestMediaConfigSchema } from "../../guest/media-config.js";
|
|
12
|
+
import { cli } from "../../cli/invocation.js";
|
|
12
13
|
const docker = async (args) => (await runtimeDocker(args, {}, 60_000)).stdout.trim();
|
|
13
14
|
const readLogs = async (id) => {
|
|
14
15
|
const result = await runtimeDocker(["logs", "--tail", "100", id], {}, 10_000);
|
|
@@ -18,12 +19,12 @@ function checkDesktopOptions(options) {
|
|
|
18
19
|
if (options.media !== undefined) {
|
|
19
20
|
guestMediaConfigSchema.parse(options.media);
|
|
20
21
|
if (options.assets.media !== true)
|
|
21
|
-
throw new Error(
|
|
22
|
+
throw new Error(`This local runtime does not include media. Run ${cli("runtime setup --media")}.`);
|
|
22
23
|
}
|
|
23
24
|
if (options.recording !== undefined) {
|
|
24
25
|
desktopRecordingConfigSchema.parse(options.recording);
|
|
25
26
|
if (options.assets.media !== true)
|
|
26
|
-
throw new Error(
|
|
27
|
+
throw new Error(`This runtime does not include the recorder. Run ${cli("runtime setup --media")}.`);
|
|
27
28
|
}
|
|
28
29
|
let url;
|
|
29
30
|
try {
|
|
@@ -3,6 +3,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
+
import { cli } from "../../cli/invocation.js";
|
|
6
7
|
const exec = promisify(execFile);
|
|
7
8
|
export const LIMA_INSTANCE = "humanish-runtime";
|
|
8
9
|
export const usesLima = (options = {}) => (options.platform ?? process.platform) === "darwin";
|
|
@@ -86,11 +87,11 @@ export async function limaStatus(options = {}) {
|
|
|
86
87
|
version = (await hostExec("limactl", ["--version"], options)).stdout;
|
|
87
88
|
}
|
|
88
89
|
catch {
|
|
89
|
-
throw new Error(
|
|
90
|
+
throw new Error(`Install Lima 2.2 or newer (brew install lima), then run ${cli("runtime setup")}. Docker Desktop is not required.`);
|
|
90
91
|
}
|
|
91
92
|
const match = /\b(\d+)\.(\d+)\.\d+/.exec(version);
|
|
92
93
|
if (!match || Number(match[1]) < 2 || (Number(match[1]) === 2 && Number(match[2]) < 2))
|
|
93
|
-
throw new Error(
|
|
94
|
+
throw new Error(`Update Lima to 2.2 or newer (brew upgrade lima), then run ${cli("runtime setup")}.`);
|
|
94
95
|
const output = (await hostExec("limactl", ["list", "--json"], options)).stdout.trim();
|
|
95
96
|
const instance = output
|
|
96
97
|
.split("\n")
|
|
@@ -104,8 +105,8 @@ export async function limaStatus(options = {}) {
|
|
|
104
105
|
ready: instance?.status === "Running",
|
|
105
106
|
exists: !!instance,
|
|
106
107
|
message: instance
|
|
107
|
-
?
|
|
108
|
-
:
|
|
108
|
+
? `The humanish Lima host is stopped. Run ${cli("runtime setup")} to start it.`
|
|
109
|
+
: `Run ${cli("runtime setup")} to create the humanish Lima host and install the browser runtime.`,
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
112
|
export async function prepareLima(options = {}, progress) {
|
|
@@ -7,6 +7,7 @@ import { Readable, Transform } from "node:stream";
|
|
|
7
7
|
import { pipeline } from "node:stream/promises";
|
|
8
8
|
import { LOCAL_MEDIA_RUNTIME_RELEASES, LOCAL_RUNTIME_RELEASES } from "./runtime-release.js";
|
|
9
9
|
import { limaStatus, loadRuntimeArchive, prepareLima, runtimeArchitecture, runtimeDocker, runtimeExec, usesLima, } from "./runtime-host.js";
|
|
10
|
+
import { cli } from "../../cli/invocation.js";
|
|
10
11
|
const docker = async (args, options) => (await runtimeDocker(args, options)).stdout.trim();
|
|
11
12
|
const runtimeRelease = (options) => options.release ??
|
|
12
13
|
(options.media ? LOCAL_MEDIA_RUNTIME_RELEASES : LOCAL_RUNTIME_RELEASES)[runtimeArchitecture(options) ?? "amd64"];
|
|
@@ -80,7 +81,7 @@ export async function localRuntimeStatus(options = {}) {
|
|
|
80
81
|
installed: false,
|
|
81
82
|
message: lima
|
|
82
83
|
? "Docker is unavailable inside the humanish Lima host. Check limactl shell humanish-runtime -- sudo systemctl status docker; Docker Desktop is not used."
|
|
83
|
-
:
|
|
84
|
+
: `Docker is unavailable. Install/start Docker Engine and give your account access, then run ${cli("runtime setup")}.`,
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
const release = runtimeRelease(options);
|
|
@@ -108,7 +109,7 @@ export async function localRuntimeStatus(options = {}) {
|
|
|
108
109
|
: {
|
|
109
110
|
ok: true,
|
|
110
111
|
installed: false,
|
|
111
|
-
message:
|
|
112
|
+
message: `Local runtime will download before the first live run. Run ${cli("runtime setup")} to prepare it now.`,
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
115
|
const image = images[0];
|
|
@@ -161,7 +162,7 @@ export async function prepareLocalRuntime(options = {}) {
|
|
|
161
162
|
try {
|
|
162
163
|
const response = await fetch(release.url, { signal });
|
|
163
164
|
if (!response.ok || !response.body)
|
|
164
|
-
throw new Error(`Runtime download failed (HTTP ${response.status}). Run
|
|
165
|
+
throw new Error(`Runtime download failed (HTTP ${response.status}). Run ${cli("runtime setup")} to retry.`);
|
|
165
166
|
let bytes = 0;
|
|
166
167
|
const hash = createHash("sha256");
|
|
167
168
|
await pipeline(Readable.fromWeb(response.body), new Transform({
|
|
@@ -181,7 +182,7 @@ export async function prepareLocalRuntime(options = {}) {
|
|
|
181
182
|
await loadRuntimeArchive(archive, { ...options, signal });
|
|
182
183
|
const after = await localRuntimeStatus(options);
|
|
183
184
|
if (!after.assets)
|
|
184
|
-
throw new Error(
|
|
185
|
+
throw new Error(`Downloaded runtime did not provide the expected image. Run ${cli("runtime setup")} to retry.`);
|
|
185
186
|
return after.assets;
|
|
186
187
|
}
|
|
187
188
|
finally {
|
package/dist/tui/contract.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
//
|
|
13
13
|
// It also makes the UI testable without a terminal: a test hands `startTui` a fake index and reads
|
|
14
14
|
// the frames back.
|
|
15
|
+
import { cli } from "../cli/invocation.js";
|
|
15
16
|
/** Node version the Ink runtime requires (ink@7 declares `engines.node >= 22`). */
|
|
16
17
|
export const TUI_MIN_NODE_MAJOR = 22;
|
|
17
18
|
/**
|
|
@@ -41,12 +42,12 @@ export const TUI_BUNDLE_URL = new URL("../tui-app.js", import.meta.url);
|
|
|
41
42
|
*/
|
|
42
43
|
export function terminalSurfaceMessage(state) {
|
|
43
44
|
if (!state.supported) {
|
|
44
|
-
return
|
|
45
|
+
return `\`${cli("tui")}\` needs Node ${TUI_MIN_NODE_MAJOR}+ (this is ${state.nodeVersion}); every other command works here`;
|
|
45
46
|
}
|
|
46
47
|
if (!state.bundlePresent) {
|
|
47
|
-
return "
|
|
48
|
+
return `\`${cli("tui")}\` is not built in this checkout; run \`pnpm build\` (installed packages ship it prebuilt)`;
|
|
48
49
|
}
|
|
49
50
|
return state.interactive
|
|
50
|
-
? "
|
|
51
|
-
: "
|
|
51
|
+
? `\`${cli("tui")}\` opens the interactive surface for browsing studies and runs`
|
|
52
|
+
: `\`${cli("tui")}\` is the interactive screen a person uses to watch and start runs here. This shell is not a terminal, so pass it on to a person at one`;
|
|
52
53
|
}
|
package/dist/verify/liveness.js
CHANGED
|
@@ -11,6 +11,7 @@ import { runLiveness } from "../run/run-index.js";
|
|
|
11
11
|
import { parseSandboxReceipts, SANDBOX_RECEIPTS_ARTIFACT } from "../run/sandbox-receipts.js";
|
|
12
12
|
import { RUN_STATUS_FILE } from "../run/status.js";
|
|
13
13
|
import { plural } from "../run/text.js";
|
|
14
|
+
import { cli } from "../cli/invocation.js";
|
|
14
15
|
/** The stable code every RUN_NOT_FINISHED warning starts with. */
|
|
15
16
|
const RUN_NOT_FINISHED = "RUN_NOT_FINISHED";
|
|
16
17
|
const GONE_STATES = new Set(["killed", "already-gone"]);
|
|
@@ -55,7 +56,7 @@ async function reclaimState(runPaths, runId) {
|
|
|
55
56
|
// Matched by digest: a reclaim receipt names each sandbox by digest, and one written before it
|
|
56
57
|
// did holds the raw id, which is digested here.
|
|
57
58
|
const journaled = new Set((journal === null ? [] : parseSandboxReceipts(journal)).map((receipt) => sandboxIdDigest(receipt.sandboxId)));
|
|
58
|
-
const command =
|
|
59
|
+
const command = `\`${cli(`reclaim --run ${runId}`)}\``;
|
|
59
60
|
const receipt = reclaimReceipt(await readRunJsonIfExists(runPaths, RECLAIM_RECEIPT_ARTIFACT), runId);
|
|
60
61
|
if (receipt === undefined)
|
|
61
62
|
return {
|
package/dist/verify/verify.js
CHANGED
|
@@ -19,6 +19,7 @@ import { rerunLineageFindings } from "./rerun.js";
|
|
|
19
19
|
import { sharedWorldEvidenceFindings } from "./shared-world.js";
|
|
20
20
|
import { subjectStateFindings, undeclaredSubjectStateWarnings } from "./subject.js";
|
|
21
21
|
import { plural } from "../run/text.js";
|
|
22
|
+
import { cli } from "../cli/invocation.js";
|
|
22
23
|
export const VERIFY_SCHEMA = "humanish.verify-result.v1";
|
|
23
24
|
// The UNSCANNED_ARTIFACT reason names this many paths and counts the rest.
|
|
24
25
|
const MAX_LISTED_UNSCANNED = 10;
|
|
@@ -387,7 +388,7 @@ function buildShareSafety(args) {
|
|
|
387
388
|
];
|
|
388
389
|
reasons.push({
|
|
389
390
|
code: "RAW_SANDBOX_ID",
|
|
390
|
-
message: `Raw sandbox ids appear in ${files.sort().join(", ")}. Runs from 0.110 keep them only in sandbox-receipts.ndjson;
|
|
391
|
+
message: `Raw sandbox ids appear in ${files.sort().join(", ")}. Runs from 0.110 keep them only in sandbox-receipts.ndjson; \`${cli("export --format bundle --redact-screenshots")}\` writes a copy without them.`,
|
|
391
392
|
});
|
|
392
393
|
}
|
|
393
394
|
const rawStreamIds = rawScreenshotStreamIds(args.bundle, args.redactedShapeFrames);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Terminal-product route
|
|
2
2
|
|
|
3
3
|
The live terminal-product route has shipped since `0.8.0`, with the in-sandbox runtime,
|
|
4
|
-
|
|
4
|
+
explicit credential placement (an E2B egress proxy by default, or command-scoped), exact-id cleanup proof, an interventions ledger, a cost and
|
|
5
5
|
no-spend ledger, caps, and product scoring and feedback hooks. The
|
|
6
6
|
[original plan](https://github.com/danielgwilson/humanish/blob/main/docs/history/goals/terminal-product-lane/goal.md)
|
|
7
7
|
holds the build order and the safety contract.
|
|
@@ -30,7 +30,7 @@ fail-closed cross-validation, and forward-declared warnings.
|
|
|
30
30
|
| `subject.product` | `{ name, publicSurfaces[], install?, workdir?, upload? }`: the only world the agent sees |
|
|
31
31
|
| `execution.target` | `e2b-terminal` (or absent → implied) |
|
|
32
32
|
| `execution.terminal` | `{ transport: exec-stream, stdin: disabled }` |
|
|
33
|
-
| `execution.runtimeAuth` | `openai-
|
|
33
|
+
| `execution.runtimeAuth` | `openai-egress` (default) or opt-in `openai-env`; names-only durable evidence |
|
|
34
34
|
| `execution.runtime.version` | Optional exact `@openai/codex` version; observed before keyed execution |
|
|
35
35
|
| `actor.model` / `reasoningEffort` | Passed to Codex as `--model` (default `gpt-5.6-sol`) and `-c model_reasoning_effort`; declarations, not observed provider identity |
|
|
36
36
|
| `caps` | `{ maxUsd, maxJobs, maxMinutes }`: the blast-radius budget; `maxUsd > 0` is refused: no adopter cost source exists until issue 347 lands |
|
|
@@ -86,11 +86,8 @@ product's no-spend boundary still needs separate interpretation.
|
|
|
86
86
|
|
|
87
87
|
## Runtime auth: raw-key placement and remaining provider access
|
|
88
88
|
|
|
89
|
-
`execution.runtimeAuth: openai-
|
|
90
|
-
`
|
|
91
|
-
that was the host source. Child processes can read and use the raw key.
|
|
92
|
-
|
|
93
|
-
Opt in to keeping the raw key outside the sandbox:
|
|
89
|
+
`execution.runtimeAuth: openai-egress` is the default: a study that declares no
|
|
90
|
+
`runtimeAuth` keeps the raw key outside the sandbox, as if it declared:
|
|
94
91
|
|
|
95
92
|
```yaml
|
|
96
93
|
execution:
|
|
@@ -115,7 +112,7 @@ output and errors, including errors during sandbox creation.
|
|
|
115
112
|
This mode supports the default OpenAI endpoint only. humanish explicitly sets Codex's
|
|
116
113
|
built-in `openai` provider and `openai_base_url` to `https://api.openai.com/v1` for
|
|
117
114
|
that invocation. It does not support a custom provider, proxy base URL, or regional
|
|
118
|
-
endpoint under this mode
|
|
115
|
+
endpoint under this mode; a study that needs one declares `openai-env`. E2B
|
|
119
116
|
header rules are a public-beta capability;
|
|
120
117
|
`tests/routes/terminal/runtime-auth.test.ts` checks the local contract against the
|
|
121
118
|
installed Desktop SDK (the lockfile has `@e2b/desktop` 2.4.0, resolving `e2b` 2.49.0).
|
|
@@ -136,6 +133,11 @@ requests can fail. E2B domain allowlists are routing controls rather than strict
|
|
|
136
133
|
destination isolation on shared infrastructure. An existing exact OpenAI host
|
|
137
134
|
rule is rejected instead of silently overwritten.
|
|
138
135
|
|
|
136
|
+
`execution.runtimeAuth: openai-env` is the opt-in alternative. It supplies
|
|
137
|
+
`CODEX_API_KEY` command-scoped to Codex, with `OPENAI_API_KEY` also supplied when
|
|
138
|
+
that was the host source. Child processes can read and use the raw key, and so can
|
|
139
|
+
anything the agent runs or a page it reads asks it to run.
|
|
140
|
+
|
|
139
141
|
Evidence records the selected auth mode and the residual proxy capability.
|
|
140
142
|
Resolved live actor traces use `keyPlacement: external` in `openai-egress`; the
|
|
141
143
|
actor registry continues to describe the default `in-sandbox-command-scoped`
|
|
@@ -197,10 +199,10 @@ deadline.
|
|
|
197
199
|
|
|
198
200
|
## The original command-scoped safety contract
|
|
199
201
|
|
|
200
|
-
The
|
|
201
|
-
On the computer-use route the model's key stays _outside_ the sandbox;
|
|
202
|
-
agent-under-test runs _inside_ with a real `OPENAI_API_KEY`/`CODEX_API_KEY` and
|
|
203
|
-
|
|
202
|
+
The `openai-env` mode **inverts** the credential-placement default of every other E2B route.
|
|
203
|
+
On the computer-use route the model's key stays _outside_ the sandbox; under `openai-env` the
|
|
204
|
+
agent-under-test runs _inside_ with a real `OPENAI_API_KEY`/`CODEX_API_KEY` and is **presumed
|
|
205
|
+
exfiltratable**. The placement rule in invariants-and-defaults.md
|
|
204
206
|
applies: _keys live where the keyed process runs, and nowhere else;
|
|
205
207
|
blast radius is bounded by key scoping and budgets, not by hoping._
|
|
206
208
|
|
|
@@ -585,7 +585,9 @@ shareSafety:
|
|
|
585
585
|
bundle is safe to promote into a public issue. Public promotion should branch on
|
|
586
586
|
`shareSafety.status`:
|
|
587
587
|
|
|
588
|
-
- `share_ready`: feedback draft commands may render public issue payloads
|
|
588
|
+
- `share_ready`: feedback draft commands may render public issue payloads, and `observe --all --safe`
|
|
589
|
+
serves the run. The folder still holds `sandbox-receipts.ndjson` (raw sandbox ids) and `status.json`
|
|
590
|
+
(the recording pid); no Observer server serves them and bundle export omits them;
|
|
589
591
|
- `local_only`: keep the run local; only supported redaction-only cases can produce a shareable derivative;
|
|
590
592
|
`RAW_SANDBOX_ID` is one: a file other than `sandbox-receipts.ndjson` names one of the run's raw
|
|
591
593
|
sandbox ids, which verify reads from the receipts and from any raw `providerResources[].id` in
|
|
@@ -658,6 +660,16 @@ trace references outside `screenshots/`. An unregistered
|
|
|
658
660
|
`.mp4`, and a file or directory whose name contains `\`, are public-safety
|
|
659
661
|
findings and block the run.
|
|
660
662
|
|
|
663
|
+
The patterns live in `src/evidence/redaction.ts`: provider key formats (OpenAI,
|
|
664
|
+
Anthropic, E2B, GitHub, GitLab, npm, Vercel, AWS, Google, Stripe, Slack,
|
|
665
|
+
SendGrid, Hugging Face), JWTs, bearer and basic `Authorization` values, private
|
|
666
|
+
key headers, database URLs with a password, the password in any URL's user info,
|
|
667
|
+
credential query and fragment parameters with a value of 16 characters or more,
|
|
668
|
+
credential-named upper-case variables set to a value of 16 characters or more
|
|
669
|
+
with a digit and a letter, an AWS secret access key next to its name, E2B URLs, and Linux,
|
|
670
|
+
macOS and Windows local paths. `tests/verify/secret-formats.test.ts` lists the
|
|
671
|
+
formats and the ordinary values that must not match.
|
|
672
|
+
|
|
661
673
|
Before matching, the scan undoes JSON and JS escapes, percent-encoding and HTML
|
|
662
674
|
character references, the decoding bundle export applies (`decodeEscapes`), and
|
|
663
675
|
then JSON whitespace escapes and quoted-printable. It reads inside base64 runs of
|
|
@@ -673,9 +685,13 @@ wrapped across lines, and inside hex runs of 32 characters or more:
|
|
|
673
685
|
|
|
674
686
|
`observer/index.html` is exempt from that base64 rule: serve renders it from
|
|
675
687
|
`run.json`, export regenerates it, and it embeds the Observer's own base64 fonts.
|
|
676
|
-
Its text is still scanned.
|
|
677
|
-
|
|
678
|
-
|
|
688
|
+
Its text is still scanned. A base64 run of up to 4096 characters is also read one
|
|
689
|
+
slash-separated piece at a time, and from just after each slash in its first 64
|
|
690
|
+
characters, so a URL path before an encoded key cannot shift its decoding. Known
|
|
691
|
+
limits: base64 split across separate strings, nesting deeper than three levels,
|
|
692
|
+
base64 that holds a slash itself and starts more than 64 characters into a longer
|
|
693
|
+
run, and encodings a pattern scan cannot undo (such as encryption) still grade
|
|
694
|
+
`share_ready`. `tests/verify/encoding-coverage.test.ts`
|
|
679
695
|
records each case.
|
|
680
696
|
|
|
681
697
|
Real email receiving adds `publication.restrictions: [real-communications]` and
|
|
@@ -279,7 +279,7 @@ instruction?, target?, entry?, host?, reasoningEffort?, stopWhen?, dwell? }` bec
|
|
|
279
279
|
an interactive PTY would overstate the mechanism (a duplex PTY
|
|
280
280
|
transport does not ship). `terminal.stdin` defaults to `disabled`
|
|
281
281
|
(`sent`/assisted input is rejected until the interventions ledger + a
|
|
282
|
-
non-comparable marker exist). `runtimeAuth` (`openai-
|
|
282
|
+
non-comparable marker exist). `runtimeAuth` (`openai-egress`, the default, or `openai-env`)
|
|
283
283
|
declares the agent's runtime-auth channel, recorded as names only. On a live run, the engine
|
|
284
284
|
resolves the registered terminal descriptor and requires
|
|
285
285
|
`keyPlacement: in-sandbox-command-scoped` before creating a sandbox. With `openai-env`
|
|
@@ -1068,9 +1068,9 @@ contract.
|
|
|
1068
1068
|
## Terminal Cost Ledger And No-Spend Proof
|
|
1069
1069
|
|
|
1070
1070
|
The terminal-product route (`src/routes/terminal/route.ts`), under the default
|
|
1071
|
-
`runtimeAuth: openai-
|
|
1072
|
-
|
|
1073
|
-
|
|
1071
|
+
`runtimeAuth: openai-egress`, gives the in-sandbox agent command a placeholder and
|
|
1072
|
+
keeps the key in an external E2B header transform; `openai-env` passes a real
|
|
1073
|
+
provider key only to that command. Neither mode puts it in sandbox-global env or
|
|
1074
1074
|
metadata, so the no-spend claim must be derived from a ledger, never asserted. The
|
|
1075
1075
|
live run writes both to `terminal-ledgers.json` (a `cost` block + a
|
|
1076
1076
|
`noSpendProof` block, additive to `humanish.terminal-ledgers.v1`).
|
|
@@ -1186,7 +1186,8 @@ the CLI's run command when that signal stops a live run outside post-run
|
|
|
1186
1186
|
analysis. It first prints one line saying it is stopping and refuses any further sandbox
|
|
1187
1187
|
create. It then kills the E2B sandboxes the run's `sandbox-receipts.ndjson` names, each sandbox
|
|
1188
1188
|
a create in flight reports as its id arrives, and each one E2B lists with the run's owner tags,
|
|
1189
|
-
writes `reclaim-receipt.json` as `humanish reclaim` does,
|
|
1189
|
+
writes `reclaim-receipt.json` as `humanish reclaim` does, rewrites each run file that names one of
|
|
1190
|
+
the run's raw sandbox ids with the marker and digest, as a finished run's publish does, and exits
|
|
1190
1191
|
128+n. The bundle is not finished: run.json keeps its last live flush, with
|
|
1191
1192
|
`outcome: { state: interrupted, ok: false, signal, at }` when the run had
|
|
1192
1193
|
flushed one, and status.json gets no `outcome`. A `running` record that stopped refreshing still reads as
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "humanish",
|
|
3
|
-
"version": "0.110.
|
|
3
|
+
"version": "0.110.2",
|
|
4
4
|
"description": "Synthetic user research for apps, CLIs, and agent-facing product flows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -56,11 +56,12 @@
|
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsc -p tsconfig.build.json && pnpm --filter humanish-observer build && pnpm --filter humanish-tui build && node scripts/finish-build.mjs",
|
|
59
|
-
"check": "pnpm format:check && pnpm lint && pnpm knip && pnpm prose:check && pnpm vocabulary:check && pnpm docs:check && pnpm site-css:check && pnpm typecheck && pnpm test && pnpm tui:test && pnpm build && pnpm guest-media:startup:proof && pnpm cli:startup:test && pnpm cli:preflight:test && pnpm cli:dotenv:test && pnpm cli:peer:test && pnpm tui:smoke",
|
|
59
|
+
"check": "pnpm format:check && pnpm lint && pnpm knip && pnpm prose:check && pnpm vocabulary:check && pnpm docs:check && pnpm site-css:check && pnpm typecheck && pnpm test && pnpm tui:test && pnpm build && pnpm guest-media:startup:proof && pnpm cli:startup:test && pnpm cli:preflight:test && pnpm cli:dotenv:test && pnpm cli:peer:test && pnpm cli:skill:test && pnpm tui:smoke",
|
|
60
60
|
"cli:startup:test": "node scripts/terminal-startup-exit-proof.mjs",
|
|
61
61
|
"cli:preflight:test": "node scripts/task-route-preflight-proof.mjs",
|
|
62
62
|
"cli:dotenv:test": "node scripts/dotenv-proof.mjs",
|
|
63
63
|
"cli:peer:test": "node scripts/peer-install-proof.mjs",
|
|
64
|
+
"cli:skill:test": "node scripts/skill-first-run-proof.mjs",
|
|
64
65
|
"humanish": "tsx src/cli.ts",
|
|
65
66
|
"pack:dry-run": "npm pack --dry-run",
|
|
66
67
|
"prepack": "pnpm build",
|
|
@@ -74,6 +75,7 @@
|
|
|
74
75
|
"tui:smoke": "node scripts/tui-smoke.mjs",
|
|
75
76
|
"tui:test": "pnpm --filter humanish-tui test",
|
|
76
77
|
"release:dogfood": "node scripts/release-dogfood.mjs",
|
|
78
|
+
"bench": "tsx bench/run.ts",
|
|
77
79
|
"browser-control:proof": "node scripts/browser-control-proof.mjs",
|
|
78
80
|
"docs:generate": "tsx scripts/generate-cli-docs.ts",
|
|
79
81
|
"docs:check": "tsx scripts/generate-cli-docs.ts --check && tsx scripts/check-doc-paths.ts && tsx scripts/check-doc-symbols.ts && tsx scripts/check-doc-comments.ts && tsx scripts/check-doc-study-fields.ts",
|
package/skills/humanish/SKILL.md
CHANGED
|
@@ -289,7 +289,6 @@ Useful commands:
|
|
|
289
289
|
```bash
|
|
290
290
|
npx humanish study list
|
|
291
291
|
npx humanish study show first-run
|
|
292
|
-
npx humanish watch first-run
|
|
293
292
|
npx humanish run first-run --json --no-open
|
|
294
293
|
```
|
|
295
294
|
|
|
@@ -377,25 +376,30 @@ inbox page. See `docs/contracts/schemas.md` for the full `comms:` shape and
|
|
|
377
376
|
## First Proof Run
|
|
378
377
|
|
|
379
378
|
Run the no-credentials path first. It is a dry run: humanish writes a synthetic
|
|
380
|
-
run bundle and tests no target app behavior
|
|
379
|
+
run bundle and tests no target app behavior. Every command in this block
|
|
380
|
+
finishes and exits, needs no key and spends nothing; lines marked
|
|
381
|
+
`# live only` need keys and spend money, so run them only when the user has
|
|
382
|
+
approved a live run. `run` prints the run id; use it in place of `<id>`.
|
|
381
383
|
|
|
382
384
|
```bash
|
|
383
|
-
npx humanish doctor
|
|
384
|
-
npx humanish watch
|
|
385
|
-
npx humanish verify --run latest --json
|
|
386
|
-
npx humanish feedback issue --run latest --repo example/app --format markdown
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
For CI or non-interactive proof:
|
|
390
|
-
|
|
391
|
-
```bash
|
|
392
|
-
npx humanish watch --json --no-open
|
|
385
|
+
npx humanish doctor --json
|
|
393
386
|
npx humanish run first-run --json --no-open
|
|
387
|
+
npx humanish verify --run <id> --json
|
|
388
|
+
npx humanish review --run <id> --json
|
|
389
|
+
npx humanish observe --run <id>
|
|
390
|
+
npx humanish run try-live --json --no-open # live only
|
|
391
|
+
npx humanish feedback issue --run <id> --repo example/app --format markdown # live only
|
|
394
392
|
```
|
|
395
393
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
394
|
+
`observe` prints the path of the run's Observer page for the person you are
|
|
395
|
+
working for. Without an interactive terminal, `observe` and `watch` print the
|
|
396
|
+
result and exit; at a person's terminal they serve the Observer until Ctrl-C.
|
|
397
|
+
|
|
398
|
+
`feedback issue` drafts from a live run only and refuses a dry run with
|
|
399
|
+
`HUMANISH_FEEDBACK_REQUIRES_LIVE_RUN`. Its Markdown draft is checked for
|
|
400
|
+
secret, key, token and local-path shapes; it is not checked for personal data. It
|
|
401
|
+
must not call the GitHub API, require a token, update Projects, use provider
|
|
402
|
+
credits, or claim product behavior proof from a dry run.
|
|
399
403
|
|
|
400
404
|
When the target app can run locally, prove a known browser path with a
|
|
401
405
|
scripted-browser study after starting the app on loopback. The study replays a
|
|
@@ -414,8 +418,8 @@ no browser.
|
|
|
414
418
|
# in another terminal, start the target app on 127.0.0.1 or localhost
|
|
415
419
|
npx humanish study show <study> --json
|
|
416
420
|
npx humanish run <study> --json --no-open
|
|
417
|
-
npx humanish verify --run
|
|
418
|
-
npx humanish observe --run
|
|
421
|
+
npx humanish verify --run <id> --json
|
|
422
|
+
npx humanish observe --run <id> --open # for a person; at their terminal it serves until Ctrl-C
|
|
419
423
|
```
|
|
420
424
|
|
|
421
425
|
Do not use `humanish watch --count ...` as a substitute for a scripted-browser
|
|
@@ -442,7 +446,7 @@ Do not paste values into files, prompts, run bundles, issue drafts, or logs.
|
|
|
442
446
|
Load local values only at invocation time:
|
|
443
447
|
|
|
444
448
|
```bash
|
|
445
|
-
npx humanish
|
|
449
|
+
npx humanish run .humanish/studies/local-live.yaml --dotenv .humanish/local/provider.env --json --no-open
|
|
446
450
|
```
|
|
447
451
|
|
|
448
452
|
When choosing dogfood targets, prefer apps, CLIs, or agent-facing tools with a
|