humanish 0.48.0 → 0.50.0
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/README.md +65 -6
- package/dist/actor-contract.d.ts +14 -0
- package/dist/actor-contract.js.map +1 -1
- package/dist/computer-use-actor.d.ts +3 -7
- package/dist/computer-use.d.ts +4 -7
- package/dist/computer-use.js +39 -3
- package/dist/computer-use.js.map +1 -1
- package/dist/concurrent-shared-world-lab.d.ts +5 -0
- package/dist/concurrent-shared-world-lab.js +30 -1
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +7 -0
- package/dist/cua-actor-lab.js +50 -9
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/e2b-terminal-lab.d.ts +7 -0
- package/dist/e2b-terminal-lab.js +54 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/init-templates.js +7 -2
- package/dist/init-templates.js.map +1 -1
- package/dist/key-resolution.d.ts +67 -0
- package/dist/key-resolution.js +373 -0
- package/dist/key-resolution.js.map +1 -0
- package/dist/lab-engine.d.ts +11 -0
- package/dist/lab-engine.js +18 -0
- package/dist/lab-engine.js.map +1 -1
- package/dist/openai-responses-cu.d.ts +1 -1
- package/dist/openai-responses-cu.js +8 -2
- package/dist/openai-responses-cu.js.map +1 -1
- package/dist/oss-lab.d.ts +3 -0
- package/dist/oss-lab.js.map +1 -1
- package/dist/oss-meta-lab.d.ts +3 -0
- package/dist/oss-meta-lab.js +13 -0
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/pricing.d.ts +20 -0
- package/dist/pricing.js +109 -17
- package/dist/pricing.js.map +1 -1
- package/dist/program.d.ts +18 -1
- package/dist/program.js +291 -17
- package/dist/program.js.map +1 -1
- package/dist/run-detail.d.ts +59 -0
- package/dist/run-detail.js +108 -0
- package/dist/run-detail.js.map +1 -0
- package/dist/run-index.d.ts +71 -0
- package/dist/run-index.js +206 -0
- package/dist/run-index.js.map +1 -0
- package/dist/run-paths.js +16 -1
- package/dist/run-paths.js.map +1 -1
- package/dist/run-projection.d.ts +182 -0
- package/dist/run-projection.js +349 -0
- package/dist/run-projection.js.map +1 -0
- package/dist/run-status.d.ts +139 -0
- package/dist/run-status.js +218 -0
- package/dist/run-status.js.map +1 -0
- package/dist/run.d.ts +15 -0
- package/dist/run.js +115 -6
- package/dist/run.js.map +1 -1
- package/dist/scripted-browser-lab.d.ts +5 -0
- package/dist/scripted-browser-lab.js +29 -1
- package/dist/scripted-browser-lab.js.map +1 -1
- package/dist/shared-world-lab.d.ts +5 -0
- package/dist/shared-world-lab.js +29 -1
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/tui-app.js +402 -0
- package/dist/tui-contract.d.ts +73 -0
- package/dist/tui-contract.js +32 -0
- package/dist/tui-contract.js.map +1 -0
- package/dist/tui-launch.d.ts +48 -0
- package/dist/tui-launch.js +159 -0
- package/dist/tui-launch.js.map +1 -0
- package/docs/contracts/run-bundle.md +8 -0
- package/docs/contracts/schemas.md +85 -7
- package/docs/goals/current.md +16 -7
- package/docs/ramp/README.md +1 -1
- package/package.json +8 -5
- package/skills/humanish/SKILL.md +19 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { LabListResult } from "./labs.js";
|
|
2
|
+
import type { RunDetail } from "./run-detail.js";
|
|
3
|
+
import type { ReadRunIndexOptions, RunIndexResult } from "./run-index.js";
|
|
4
|
+
import type { LaunchRunOptions, LaunchRunResult } from "./tui-launch.js";
|
|
5
|
+
/** The humanish version string shown in the frame, so a screenshot in a bug report is datable. */
|
|
6
|
+
export interface TuiVersionInfo {
|
|
7
|
+
cli: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* What the surface may do to the project. Deliberately a small, explicit list rather than a handle
|
|
11
|
+
* to the whole library: the set of verbs a stakeholder surface can perform should be readable in
|
|
12
|
+
* one place, and anything absent here is something the TUI simply cannot do.
|
|
13
|
+
*/
|
|
14
|
+
export interface TuiCapabilities {
|
|
15
|
+
/** Read every run in the project, cheapest source first. */
|
|
16
|
+
readRunIndex(cwd: string, options?: ReadRunIndexOptions): Promise<RunIndexResult>;
|
|
17
|
+
/**
|
|
18
|
+
* The labs DECLARED in this project. Listed separately from run history because neither side is
|
|
19
|
+
* the whole truth: a fresh project has manifests and no runs, and a long-lived one has runs from
|
|
20
|
+
* manifests since renamed or deleted.
|
|
21
|
+
*/
|
|
22
|
+
listLabs(cwd: string): Promise<LabListResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Start a run and return once it is running. The run is DETACHED: it outlives this surface, so
|
|
25
|
+
* quitting the TUI — or losing the connection it runs over — does not kill a study that costs
|
|
26
|
+
* real money. The surface then follows it through `status.json` like any other reader.
|
|
27
|
+
*/
|
|
28
|
+
startRun(options: Omit<LaunchRunOptions, "spawn" | "cliPath" | "now">): Promise<LaunchRunResult>;
|
|
29
|
+
/** The tail of a launch log: the only account of a run that died before writing evidence. */
|
|
30
|
+
readLaunchLog(logPath: string): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Who is in ONE run and what they are thinking. Opens that run's bundle, which the index
|
|
33
|
+
* deliberately does not — affordable because it is asked only for the run being watched.
|
|
34
|
+
* `null` when the run has not written a bundle yet.
|
|
35
|
+
*/
|
|
36
|
+
readRunDetail(cwd: string, runId: string): Promise<RunDetail | null>;
|
|
37
|
+
}
|
|
38
|
+
export interface TuiOptions {
|
|
39
|
+
/** The project the surface is reading. Already resolved by the CLI. */
|
|
40
|
+
cwd: string;
|
|
41
|
+
version: TuiVersionInfo;
|
|
42
|
+
capabilities: TuiCapabilities;
|
|
43
|
+
/**
|
|
44
|
+
* Terminal streams, injected so a test can drive the surface without a real TTY. The CLI passes
|
|
45
|
+
* the real ones; both are known to be TTYs by the time this is called, because the command
|
|
46
|
+
* refuses to start otherwise.
|
|
47
|
+
*/
|
|
48
|
+
stdin: NodeJS.ReadStream;
|
|
49
|
+
stdout: NodeJS.WriteStream;
|
|
50
|
+
/** Test seam: render one frame and resolve, instead of waiting for the operator to quit. */
|
|
51
|
+
exitAfterFirstFrame?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start the surface. Resolves with the process exit code when the operator quits — the TUI owns the
|
|
55
|
+
* screen until then, so the CLI must not write to stdout while this is pending.
|
|
56
|
+
*/
|
|
57
|
+
export type StartTui = (options: TuiOptions) => Promise<number>;
|
|
58
|
+
/** The shape `dist/tui-app.js` exports. Asserted at the load boundary in program.ts. */
|
|
59
|
+
export interface TuiModule {
|
|
60
|
+
startTui: StartTui;
|
|
61
|
+
}
|
|
62
|
+
/** Node version the Ink runtime requires (ink@7 declares `engines.node >= 22`). */
|
|
63
|
+
export declare const TUI_MIN_NODE_MAJOR = 22;
|
|
64
|
+
/**
|
|
65
|
+
* Whether this runtime can host the surface. Kept here, beside the reason, so the CLI's refusal and
|
|
66
|
+
* `doctor`'s readiness row can never disagree about the answer.
|
|
67
|
+
*/
|
|
68
|
+
export declare function nodeSupportsTui(versionString?: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Where the bundle sits relative to the compiled CLI. Shared so the command that loads it and the
|
|
71
|
+
* readiness row that reports it can never look in different places.
|
|
72
|
+
*/
|
|
73
|
+
export declare function tuiBundleUrl(baseUrl: string): URL;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The boundary between the CLI and the terminal UI (#455).
|
|
2
|
+
//
|
|
3
|
+
// `humanish tui` loads a PRE-BUILT bundle (dist/tui-app.js) that contains Ink, React and the
|
|
4
|
+
// screens — and nothing else. Everything the surface needs to KNOW is passed across this interface
|
|
5
|
+
// by the CLI, which imports it from the same modules every other command uses.
|
|
6
|
+
//
|
|
7
|
+
// The reason for the seam: a terminal UI that reads the filesystem itself would become a second
|
|
8
|
+
// implementation of "what is a run, which lab does it belong to, is it alive" — one that ships
|
|
9
|
+
// minified, is invisible to the root test suite, and drifts from `humanish runs` the first time
|
|
10
|
+
// either side changes. Injection keeps exactly one implementation, already unit-tested, and leaves
|
|
11
|
+
// the bundle a view layer that can be reasoned about as one.
|
|
12
|
+
//
|
|
13
|
+
// It also makes the UI testable without a terminal: a test hands `startTui` a fake index and reads
|
|
14
|
+
// the frames back.
|
|
15
|
+
/** Node version the Ink runtime requires (ink@7 declares `engines.node >= 22`). */
|
|
16
|
+
export const TUI_MIN_NODE_MAJOR = 22;
|
|
17
|
+
/**
|
|
18
|
+
* Whether this runtime can host the surface. Kept here, beside the reason, so the CLI's refusal and
|
|
19
|
+
* `doctor`'s readiness row can never disagree about the answer.
|
|
20
|
+
*/
|
|
21
|
+
export function nodeSupportsTui(versionString = process.version) {
|
|
22
|
+
const major = Number.parseInt(versionString.replace(/^v/, "").split(".")[0] ?? "", 10);
|
|
23
|
+
return Number.isFinite(major) && major >= TUI_MIN_NODE_MAJOR;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Where the bundle sits relative to the compiled CLI. Shared so the command that loads it and the
|
|
27
|
+
* readiness row that reports it can never look in different places.
|
|
28
|
+
*/
|
|
29
|
+
export function tuiBundleUrl(baseUrl) {
|
|
30
|
+
return new URL("./tui-app.js", baseUrl);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=tui-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-contract.js","sourceRoot":"","sources":["../src/tui-contract.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,6FAA6F;AAC7F,mGAAmG;AACnG,+EAA+E;AAC/E,EAAE;AACF,gGAAgG;AAChG,+FAA+F;AAC/F,gGAAgG;AAChG,mGAAmG;AACnG,6DAA6D;AAC7D,EAAE;AACF,mGAAmG;AACnG,mBAAmB;AAqEnB,mFAAmF;AACnF,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,gBAAwB,OAAO,CAAC,OAAO;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACvF,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,kBAAkB,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,IAAI,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export declare function isSafeLabHandle(value: string): boolean;
|
|
3
|
+
export interface LaunchRunOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
/** The manifest handle (filename stem), as `humanish lab run` takes it. */
|
|
6
|
+
lab: string;
|
|
7
|
+
mode: "dry-run" | "live";
|
|
8
|
+
/** Injected in tests; defaults to the real spawn. */
|
|
9
|
+
spawn?: typeof spawn;
|
|
10
|
+
/** Injected in tests; defaults to the CLI beside this module. */
|
|
11
|
+
cliPath?: string;
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
/** Injected clock so a test can pin the log filename. */
|
|
14
|
+
now?: () => Date;
|
|
15
|
+
}
|
|
16
|
+
export interface LaunchedRun {
|
|
17
|
+
/** The spawned CLI's pid. The run's `status.json` stamps the same value. */
|
|
18
|
+
pid: number;
|
|
19
|
+
/**
|
|
20
|
+
* When the launch happened. A pid ALONE cannot identify a run: pids are recycled by the OS and a
|
|
21
|
+
* finished run keeps its pid in `status.json` forever, so a week-old record can carry the pid the
|
|
22
|
+
* kernel just handed this child. Anything matching on pid must also require the record to be
|
|
23
|
+
* newer than this.
|
|
24
|
+
*/
|
|
25
|
+
launchedAt: string;
|
|
26
|
+
/** Absolute path to the launch log; the only diagnosis when a run dies before writing evidence. */
|
|
27
|
+
logPath: string;
|
|
28
|
+
/** Exactly what was executed, so a failure can be reproduced by hand. */
|
|
29
|
+
command: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
export type LaunchRunResult = {
|
|
32
|
+
ok: true;
|
|
33
|
+
run: LaunchedRun;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
error: {
|
|
37
|
+
code: LaunchErrorCode;
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export type LaunchErrorCode = "HUMANISH_LAUNCH_INVALID_LAB" | "HUMANISH_LAUNCH_FAILED";
|
|
42
|
+
/**
|
|
43
|
+
* Start a run and return as soon as it is running. Never throws: a launch that cannot happen is a
|
|
44
|
+
* result the surface can render, not an exception that would tear down the screen.
|
|
45
|
+
*/
|
|
46
|
+
export declare function launchRun(options: LaunchRunOptions): Promise<LaunchRunResult>;
|
|
47
|
+
/** Read the tail of a launch log — the only account of a run that died before writing evidence. */
|
|
48
|
+
export declare function readLaunchLogTail(logPath: string, maxBytes?: number): Promise<string>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Starting a run from the terminal surface (#455).
|
|
2
|
+
//
|
|
3
|
+
// THE RUN MUST OUTLIVE THE SURFACE. A study can take minutes and costs real money, so a run
|
|
4
|
+
// started from the TUI cannot be a child of the TUI's event loop: closing the surface, or losing
|
|
5
|
+
// the SSH session it is running over, must not kill it. So this spawns the CLI the same way a
|
|
6
|
+
// person would type it — detached, in its own process group, with its output going to a file
|
|
7
|
+
// rather than to a terminal that may be about to disappear.
|
|
8
|
+
//
|
|
9
|
+
// The surface then learns what happened the same way any other reader does: from `status.json`.
|
|
10
|
+
// It does not hold a handle to the run, parse its stdout, or track it in memory. That is what
|
|
11
|
+
// makes the surface restartable — quit the TUI mid-run, reopen it, and the run is still there,
|
|
12
|
+
// because the filesystem was always the source of truth rather than a process handle.
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
import { constants as fsConstants } from "node:fs";
|
|
15
|
+
import { open } from "node:fs/promises";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { prepareManagedHumanishOutputDirectory } from "./selected-output-paths.js";
|
|
19
|
+
/**
|
|
20
|
+
* A lab handle is the manifest FILENAME, which is what `humanish lab run` resolves. Restricted to
|
|
21
|
+
* characters a manifest name can actually contain, and — the part that matters — never allowed to
|
|
22
|
+
* begin with `-`, because argv is positional: a lab called `--json` would otherwise be handed to
|
|
23
|
+
* the CLI as a flag. There is no shell involved, so this is the whole injection surface.
|
|
24
|
+
*
|
|
25
|
+
* A leading underscore IS allowed: `_wip.yaml` is an ordinary way to name a work-in-progress
|
|
26
|
+
* manifest, `humanish lab run _wip` resolves it, and refusing it here would leave the surface
|
|
27
|
+
* listing a lab it will not start. A leading dot stays out — that names a hidden file, not a lab.
|
|
28
|
+
*/
|
|
29
|
+
const SAFE_LAB_HANDLE = /^[A-Za-z0-9_][A-Za-z0-9._:-]*$/;
|
|
30
|
+
export function isSafeLabHandle(value) {
|
|
31
|
+
return SAFE_LAB_HANDLE.test(value) && value.length <= 128;
|
|
32
|
+
}
|
|
33
|
+
/** Where the CLI lives, relative to this compiled module. */
|
|
34
|
+
function defaultCliPath() {
|
|
35
|
+
return fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Start a run and return as soon as it is running. Never throws: a launch that cannot happen is a
|
|
39
|
+
* result the surface can render, not an exception that would tear down the screen.
|
|
40
|
+
*/
|
|
41
|
+
export async function launchRun(options) {
|
|
42
|
+
if (!isSafeLabHandle(options.lab)) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: {
|
|
46
|
+
code: "HUMANISH_LAUNCH_INVALID_LAB",
|
|
47
|
+
message: `"${options.lab}" is not a usable lab handle. Run it by path with \`humanish lab run <path>\` instead.`
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const cwd = path.resolve(options.cwd);
|
|
52
|
+
const now = options.now ?? (() => new Date());
|
|
53
|
+
const spawnFn = options.spawn ?? spawn;
|
|
54
|
+
const launchedAt = now().toISOString();
|
|
55
|
+
let logPath;
|
|
56
|
+
let handle;
|
|
57
|
+
try {
|
|
58
|
+
// Contained under `.humanish/`, through the same guard every other output path uses, so a
|
|
59
|
+
// symlinked directory cannot redirect the log somewhere outside the project.
|
|
60
|
+
const logDir = await prepareManagedHumanishOutputDirectory(cwd, "launches");
|
|
61
|
+
const stamp = launchedAt.replace(/[:.]/g, "-");
|
|
62
|
+
logPath = path.join(logDir.physicalPath, `${stamp}-${options.lab}.log`);
|
|
63
|
+
// O_NOFOLLOW so a symlink planted at this path cannot redirect a run's output — which may carry
|
|
64
|
+
// provider error text — outside the project, and cannot defeat the 0600 mode by pointing at a
|
|
65
|
+
// file that already exists with looser permissions. O_CREAT|O_APPEND keeps ordinary reuse
|
|
66
|
+
// working; only a symlink is refused (ELOOP).
|
|
67
|
+
handle = await open(logPath, fsConstants.O_CREAT | fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_NOFOLLOW, 0o600);
|
|
68
|
+
}
|
|
69
|
+
catch (cause) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: {
|
|
73
|
+
code: "HUMANISH_LAUNCH_FAILED",
|
|
74
|
+
message: `Could not open a launch log: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// `--json` because nothing reads this stream interactively; `--no-open` because the surface owns
|
|
79
|
+
// the operator's attention and must not have a browser thrown over it.
|
|
80
|
+
const args = [
|
|
81
|
+
options.cliPath ?? defaultCliPath(),
|
|
82
|
+
"lab",
|
|
83
|
+
"run",
|
|
84
|
+
"--cwd",
|
|
85
|
+
cwd,
|
|
86
|
+
"--json",
|
|
87
|
+
"--no-open",
|
|
88
|
+
...(options.mode === "dry-run" ? ["--dry-run"] : []),
|
|
89
|
+
// `--` ends option parsing, so the handle can only ever be read as the positional argument.
|
|
90
|
+
"--",
|
|
91
|
+
options.lab
|
|
92
|
+
];
|
|
93
|
+
const spawnOptions = {
|
|
94
|
+
cwd,
|
|
95
|
+
env: options.env ?? process.env,
|
|
96
|
+
// A new process group, so the SIGHUP that arrives when a terminal closes is not delivered here.
|
|
97
|
+
detached: true,
|
|
98
|
+
// stdin closed, output to the log: a detached process must never hold the terminal, and
|
|
99
|
+
// inheriting a pipe nobody reads is how a run blocks forever on a full buffer.
|
|
100
|
+
stdio: ["ignore", handle.fd, handle.fd]
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
const child = spawnFn(process.execPath, args, spawnOptions);
|
|
104
|
+
// A spawn failure is delivered ASYNCHRONOUSLY as an 'error' event (EAGAIN, EMFILE, ENOMEM, a
|
|
105
|
+
// vanished node binary). An 'error' event with no listener is re-thrown by EventEmitter as an
|
|
106
|
+
// uncaught exception — which would tear down the whole surface, the one thing this module
|
|
107
|
+
// promises never to do. The run is already unref'd and unobserved, so recording it is all that
|
|
108
|
+
// is available; the operator learns about it from the launch log and the missing record.
|
|
109
|
+
child.on("error", () => {
|
|
110
|
+
// Deliberately empty: see above. The failure surfaces as a run that never reports in.
|
|
111
|
+
});
|
|
112
|
+
if (child.pid === undefined) {
|
|
113
|
+
await handle.close();
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
error: { code: "HUMANISH_LAUNCH_FAILED", message: "The run process did not start." }
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Release the surface's hold: the parent can now exit whenever it likes and the run continues,
|
|
120
|
+
// reparented to init.
|
|
121
|
+
child.unref();
|
|
122
|
+
// The child owns the descriptor now.
|
|
123
|
+
await handle.close();
|
|
124
|
+
return {
|
|
125
|
+
ok: true,
|
|
126
|
+
run: { pid: child.pid, launchedAt, logPath, command: [process.execPath, ...args] }
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (cause) {
|
|
130
|
+
await handle.close().catch(() => undefined);
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
error: {
|
|
134
|
+
code: "HUMANISH_LAUNCH_FAILED",
|
|
135
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Read the tail of a launch log — the only account of a run that died before writing evidence. */
|
|
141
|
+
export async function readLaunchLogTail(logPath, maxBytes = 4_000) {
|
|
142
|
+
try {
|
|
143
|
+
const handle = await open(logPath, "r");
|
|
144
|
+
try {
|
|
145
|
+
const { size } = await handle.stat();
|
|
146
|
+
const start = Math.max(0, size - maxBytes);
|
|
147
|
+
const buffer = Buffer.alloc(Math.min(size, maxBytes));
|
|
148
|
+
await handle.read(buffer, 0, buffer.length, start);
|
|
149
|
+
return buffer.toString("utf8").trim();
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await handle.close();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=tui-launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-launch.js","sourceRoot":"","sources":["../src/tui-launch.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,10 @@ runId: "<core run id>"
|
|
|
21
21
|
mode: "dry-run|live"
|
|
22
22
|
simCount: 1
|
|
23
23
|
createdAt: "<ISO timestamp>"
|
|
24
|
+
lab: # optional, additive: which manifest produced this run
|
|
25
|
+
id: "<lab id>"
|
|
26
|
+
path: "humanish/labs/<lab id>.yaml"
|
|
27
|
+
origin: "committed|ignored|explicit"
|
|
24
28
|
cwd: "[target-cwd]"
|
|
25
29
|
artifactRoot: ".humanish/runs/<run-id>"
|
|
26
30
|
source:
|
|
@@ -360,6 +364,10 @@ History entries use:
|
|
|
360
364
|
schema: humanish.run-history-entry.v1
|
|
361
365
|
runId: "<run-id>"
|
|
362
366
|
createdAt: "<ISO timestamp>"
|
|
367
|
+
lab: # optional, additive: which manifest produced this run
|
|
368
|
+
id: "<lab id>"
|
|
369
|
+
path: "humanish/labs/<lab id>.yaml"
|
|
370
|
+
origin: "committed|ignored|explicit"
|
|
363
371
|
mode: "dry-run|live"
|
|
364
372
|
path: ".humanish/runs/<run-id>"
|
|
365
373
|
```
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Date: 2026-06-02 (current-state note updated 2026-07-14)
|
|
4
4
|
|
|
5
5
|
Status: reference map for the major contracts shipped through source version
|
|
6
|
-
`0.
|
|
6
|
+
`0.50.0`; it is not an exhaustive inventory of command/result envelopes. Exported types,
|
|
7
7
|
schema constants, parsers, and validators in `src/` are authoritative. Rows
|
|
8
8
|
marked "reserved" name layering intent only — no code emits or validates them
|
|
9
9
|
yet. Do not emit a reserved schema.
|
|
@@ -371,6 +371,14 @@ Core-owned fields:
|
|
|
371
371
|
- `artifacts`
|
|
372
372
|
- `review`
|
|
373
373
|
- `feedbackCandidates`
|
|
374
|
+
- `lab` (optional, additive): which manifest produced the run —
|
|
375
|
+
`{ id, path?, origin? }`, where `origin` is `committed` (humanish/labs),
|
|
376
|
+
`ignored` (a local overlay), or `explicit` (a path the operator passed).
|
|
377
|
+
Absent on bundles written before this contract and on library callers who
|
|
378
|
+
hand a `LabConfig` directly — the run is then honestly lab-less rather than
|
|
379
|
+
guessed. Readers wanting attribution for an older bundle may fall back to
|
|
380
|
+
`inferLegacyLabId`, which reads only the historical
|
|
381
|
+
`persona.source = "lab:<id>"` convention and nothing else.
|
|
374
382
|
- `subject` (optional, additive): structured subject provenance —
|
|
375
383
|
`{ source: clone | app-url | local-tree, repo?, commit?, archiveSha256?,
|
|
376
384
|
dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
|
|
@@ -724,7 +732,11 @@ Core-owned fields:
|
|
|
724
732
|
budget AFTER productive activity — status `passed`, a NON-FAILURE completion,
|
|
725
733
|
distinct from `timed_out`, which stays reserved for a zero-progress deadline
|
|
726
734
|
hit and remains a failure)
|
|
727
|
-
- `ids`, `counts`, `items[]`, optional `tokenUsage`, `capabilities`.
|
|
735
|
+
- `ids`, `counts`, `items[]`, optional `tokenUsage`, `capabilities`. `tokenUsage`
|
|
736
|
+
may carry `cacheWriteInput` (tokens billed at the provider's cache-write rate,
|
|
737
|
+
OpenAI 5.6+) and `turns[]` (per provider-request usage, the recorded fact
|
|
738
|
+
long-context tier pricing needs) — both additive and honestly absent on
|
|
739
|
+
producers that do not report them (#334). Items may
|
|
728
740
|
carry `at` (ISO-8601 recording stamp from the loop's clock) and, on
|
|
729
741
|
click-like `ui_action` items, structured `coord` (`x`/`y`) — both additive
|
|
730
742
|
(#441): absent on older bundles and non-stamping producers, and absence means
|
|
@@ -875,6 +887,67 @@ measure) and never grant a green pass (they surface as unmeasured). `verifyRun`
|
|
|
875
887
|
fails closed when a live bundle lacks the cost ledger or no-spend proof, when the
|
|
876
888
|
proof claims zero on a `null` line, or when known spend exceeds the declared cap.
|
|
877
889
|
|
|
890
|
+
## Run Status (identity + liveness index)
|
|
891
|
+
|
|
892
|
+
`humanish.run-status.v1` — `status.json`, written inside each run directory by
|
|
893
|
+
every backend at run start, refreshed on a fixed cadence while the run is
|
|
894
|
+
alive, and finalized when it ends: `{ schema, runId, state: running |
|
|
895
|
+
finished, mode, lab?, pid, startedAt, updatedAt, completedAt?, outcome? }`.
|
|
896
|
+
|
|
897
|
+
It answers two questions the filesystem could not answer before: **which lab**
|
|
898
|
+
a run belongs to, and **whether it is still alive** — including for runs an
|
|
899
|
+
agent launched (`lab run --json`) or that were detached, which previously wrote
|
|
900
|
+
nothing at all until they completed.
|
|
901
|
+
|
|
902
|
+
It is a DERIVED INDEX, not evidence. `run.json` remains the evidence-of-record;
|
|
903
|
+
`verify` never gates on `status.json`, nothing in it is a claim about what a
|
|
904
|
+
participant did, and when the two disagree the bundle wins and the record is
|
|
905
|
+
rebuildable from it. A `running` record whose `updatedAt` is older than three
|
|
906
|
+
touch intervals is INTERRUPTED, not alive — a dropped connection or a killed
|
|
907
|
+
terminal leaves exactly that shape, and reading it as interrupted is the honest
|
|
908
|
+
outcome (`classifyRunStatus` is the one shared definition). Fields are
|
|
909
|
+
public-safe by construction: no hostname and no user paths, because a run
|
|
910
|
+
directory may be shared.
|
|
911
|
+
|
|
912
|
+
## Run Index, Run Detail, And The Terminal Surface (#455)
|
|
913
|
+
|
|
914
|
+
Three derived projections that exist so a surface can list, classify and watch
|
|
915
|
+
runs without opening evidence for all of them. None is authoritative: `run.json`
|
|
916
|
+
remains the evidence-of-record, `verify` gates on none of these, and nothing in
|
|
917
|
+
them is a claim about what a participant did.
|
|
918
|
+
|
|
919
|
+
`humanish.run-index.v1` — the listing projection. One entry per run, read
|
|
920
|
+
cheapest-source-first: the `status.json` record, else the bundle, else the run
|
|
921
|
+
directory alone. `{ runId, derivedFrom: status | bundle | directory, liveness,
|
|
922
|
+
mode?, pid?, lab?, startedAt?, updatedAt?, completedAt?, verdict?,
|
|
923
|
+
participants?, estimatedCostUsd?, durationMs? }`. The point is cost: walking
|
|
924
|
+
every run tree and parsing every bundle measured 167ms on a 25-run project,
|
|
925
|
+
against 16ms cold and 2.8ms warm here, which is what makes a surface that
|
|
926
|
+
refreshes on a cadence affordable. `derivedFrom` is reported so a surprising row
|
|
927
|
+
can be traced to the file it came from. A run with receipts and no outcome is
|
|
928
|
+
`interrupted` — the shape a dropped connection leaves — and so is an
|
|
929
|
+
IN-PROGRESS bundle reached without a status record, because there is no
|
|
930
|
+
freshness to judge and "it started and nothing here says it finished" is the
|
|
931
|
+
honest reading.
|
|
932
|
+
|
|
933
|
+
`humanish.run-detail.v1` — the watching projection, for ONE run. Who is in it
|
|
934
|
+
and what they are thinking: `{ runId, participants: [{ id, label, personaId?,
|
|
935
|
+
traits, status?, completionReason?, thought?, turns?, actions?, thoughts?,
|
|
936
|
+
estimatedCostUsd? }], observerPath? }`. It reads the actor trace from
|
|
937
|
+
`stream.liveActor` while a run is in flight and `stream.actor` once it has
|
|
938
|
+
finished, preferring the live one, so a single screen renders a run the whole
|
|
939
|
+
way through. A reasoning item still being written is skipped rather than quoted
|
|
940
|
+
half-finished, and the text is carried verbatim — a surface may wrap it, nothing
|
|
941
|
+
paraphrases it. Unlike the index this DOES open the bundle, which is affordable
|
|
942
|
+
only because it is asked for the one run being watched.
|
|
943
|
+
|
|
944
|
+
`humanish.tui-result.v1` — what `humanish tui` emits when it refuses:
|
|
945
|
+
`{ schema, ok: false, error: { code, message } }` with `HUMANISH_TUI_REQUIRES_TTY`,
|
|
946
|
+
`HUMANISH_TUI_UNSUPPORTED_NODE`, or `HUMANISH_TUI_BUNDLE_MISSING`. Every other
|
|
947
|
+
command is built for an agent to drive; this one takes the screen and waits for
|
|
948
|
+
a person, so a non-interactive stdin or stdout fails closed naming the commands
|
|
949
|
+
that DO answer the question rather than rendering escape codes into a pipe.
|
|
950
|
+
|
|
878
951
|
## Run Cost Summary And Estimated Actor Cost
|
|
879
952
|
|
|
880
953
|
The computer-use (CUA) lane surfaces an ADVISORY, additive cost ESTIMATE. It is
|
|
@@ -887,11 +960,16 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
|
|
|
887
960
|
dated per-model input/output USD-per-token rates and an E2B desktop
|
|
888
961
|
USD-per-minute rate, each with a public pricing-page `source` and an `asOf`
|
|
889
962
|
date. A prominent banner says these are estimates to update when providers
|
|
890
|
-
change pricing.
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
963
|
+
change pricing. Model rates may carry a `cacheWriteUsdPerToken` (OpenAI 5.6+
|
|
964
|
+
bills cache writes at 1.25x input as the total rate for written tokens) and a
|
|
965
|
+
`longContext` tier (a per-request input threshold that re-prices the whole
|
|
966
|
+
request; priced exactly only from the trace's per-request `turns` ledger —
|
|
967
|
+
totals alone never re-tier, which is the under-estimate direction). Some
|
|
968
|
+
entries are `placeholder: true` stand-ins (the E2B desktop rate, pending a
|
|
969
|
+
live RAM-spec confirmation) — an operator MUST confirm them before trusting
|
|
970
|
+
the magnitude; the flag propagates into every estimate so a stand-in is never
|
|
971
|
+
mistaken for a live rate. An UNKNOWN model/desktop rate is DECLARED ABSENT
|
|
972
|
+
(`estimatedCostUsd: null` + a `reason`), never guessed.
|
|
895
973
|
- `humanish.actor-estimated-cost.v1` — `ActorTrace.estimatedCost`: one lane's
|
|
896
974
|
token-derived model cost, with `estimatedCostUsd` (or `null` + `reason`
|
|
897
975
|
`no_rate_for_model`/`no_token_usage`), `ratesAsOf`, `source`, `modelId`,
|
package/docs/goals/current.md
CHANGED
|
@@ -29,7 +29,7 @@ study completed, reproduced, and produced a real accessibility finding via a
|
|
|
29
29
|
keyboard-first participant
|
|
30
30
|
([docs/goals/email-gated-signup/receipts/](email-gated-signup/receipts/)).
|
|
31
31
|
|
|
32
|
-
## Current Program Truth (source `0.
|
|
32
|
+
## Current Program Truth (source `0.50.0`)
|
|
33
33
|
|
|
34
34
|
The package source and repository implementation in this tree agree on these
|
|
35
35
|
points:
|
|
@@ -45,7 +45,8 @@ The immutable 2026-06-10 proof-roadmap packet is paired with a
|
|
|
45
45
|
| Subject sources/routes | Six declared sources: `this-repo`, `clone`, `app-url`, `local-app`, `terminal-product`, and `local-tree`; support is route-specific and `this-repo` remains dry-run-only | One centralized run/resource lifecycle boundary across all routes |
|
|
46
46
|
| Public proof | A legible four-persona Observer hero from a verified real public-application study (commit-pinned drawDB) shipped in the npm payload (`0.16.0`) | Coverage beyond a single studied subject; the stratified breadth panel remains unbuilt |
|
|
47
47
|
| OSS meta-lab | Dry-run contract and separate disposable smoke harness | Live meta-lab execution; disabled until repository instructions and actor credentials have an isolated boundary |
|
|
48
|
-
| Observer serving | `watch`/`observe` loopback servers plus `serve` — the run-library surface with loopback default, capability-link exposure, `share_ready`-gated open mode, and optional operator-run tunnel; streams never served remotely | A remote live-stream (`--live-streams`) design; a persistent capability-link store
|
|
48
|
+
| Observer serving | `watch`/`observe` loopback servers plus `serve` — the run-library surface with loopback default, capability-link exposure, `share_ready`-gated open mode, and optional operator-run tunnel; streams never served remotely | A remote live-stream (`--live-streams`) design; a persistent capability-link store |
|
|
49
|
+
| Stakeholder terminal surface | `humanish tui` (`0.50.0`): labs -> lab -> run, arrow-key navigation, and starting a dry or live run from the lab screen. The run is DETACHED and outlives the terminal — live-proven by killing the terminal 42s into a real run that then ran on for ~3.5 minutes and finished `pass` at $0.639751. The run screen leads with the participant and their recorded thinking, live-proven mid-flight against a real computer-use run. Ships as one bundled file loaded on demand; refuses a non-interactive stdin/stdout naming the JSON commands instead | Cancelling a run from the surface; per-user persisted config (#470); export (#471); aggregate stats (#472); agent-authored labs (#473); a reusable persona panel (#474) |
|
|
49
50
|
| Off-app comms | Vendor-neutral in-sandbox email/SMS catch, a minimal persona inbox surface, and digest-only `humanish.comms-thread.v1` evidence; wired into the computer-use and shared-world routes over both HTTP and SMTP; live-proven end to end on 2026-08-08 — a persona signed up for a public app, read the emailed link in its inbox, and reached the signed-in product (`docs/goals/email-gated-signup/receipts/signup-verify-live-2026-08-08.md`); the adopter-hosted / app-url ingress plane is wired on the CUA and concurrent external-public routes (#387/#380, 2026-08-11) | Real-provider delivery; a live adopter-hosted receipt |
|
|
50
51
|
|
|
51
52
|
Capability proof and adopter replacement are different gates. A deterministic
|
|
@@ -487,17 +488,25 @@ Deep links landed in `0.48.0` (#464): every participant and frame is
|
|
|
487
488
|
addressable (`#/lane/<id>/f/<n>`), Back/Forward restore the view, and a
|
|
488
489
|
reload or shared link lands on the exact moment — #441 closed entirely.
|
|
489
490
|
|
|
491
|
+
The npx-first-try adoption cluster closed in `0.49.0`, operator-prompted and
|
|
492
|
+
adversarially red-teamed before merge (both arcs): provider keys now resolve
|
|
493
|
+
through each vendor's native chain (#436 — the documented project overlay,
|
|
494
|
+
`e2b auth login`'s store, `gh auth token`, and a `humanish keys set` user
|
|
495
|
+
store; fills announced by name and source, never value; `HUMANISH_STRICT_KEYS=1`
|
|
496
|
+
opts out) and #346 closed on its receipts. The computer-use default moved to
|
|
497
|
+
`gpt-5.6-sol` with the whole 5.6 family priced, and the cost estimate now
|
|
498
|
+
models the two billing mechanics 5.6 introduced — cache writes at 1.25x and
|
|
499
|
+
long-context re-tiering — exactly, from a new per-request usage ledger on the
|
|
500
|
+
trace (#334). Both spend caps price through the same tier-aware estimator.
|
|
501
|
+
|
|
490
502
|
The standing queue, in rough order:
|
|
491
503
|
|
|
492
504
|
1. registry promotions: the wordmark (#431), the participant card, and the two
|
|
493
505
|
vendored Base UI wrappers (drawer, popover) once a second surface consumes
|
|
494
506
|
them;
|
|
495
|
-
2.
|
|
496
|
-
desktop peer-dep gap (#346) — the two biggest remaining "npx works
|
|
497
|
-
first-try" violations;
|
|
498
|
-
3. shared-world honesty, remaining half: per-action evidence (#365) and
|
|
507
|
+
2. shared-world honesty, remaining half: per-action evidence (#365) and
|
|
499
508
|
exposure-flag coverage (#446);
|
|
500
|
-
|
|
509
|
+
3. the stakeholder TUI (#455): research + token-translated mocks on the
|
|
501
510
|
operator review surface, design sign-off gated before any code.
|
|
502
511
|
|
|
503
512
|
The depth-axis deletion front (an adopter's bespoke terminal-product sim,
|
package/docs/ramp/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Status: public-safe contributor and agent ramp.
|
|
4
4
|
|
|
5
|
-
Package/source version in this tree: `0.
|
|
5
|
+
Package/source version in this tree: `0.50.0` (2026-08-20). The Observer is phone-usable as a stated requirement (observer/AGENTS.md); interactive primitives start from Base UI. The Observer renderer is the observer/ workspace artifact only; the legacy string-concat renderer was deleted at cutover (#426), and rollback is a version pin to 0.42.0. The containment boundary introduced in
|
|
6
6
|
`0.15.1` remains in force: managed run and output paths bind to validated
|
|
7
7
|
physical filesystem identities, and stored provider IDs are evidence, not
|
|
8
8
|
cleanup authority. The bundled OSS meta-lab is dry-run only until
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "humanish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Open-source-safe CLI for persona simulation, observer review, and public-safe feedback drafts.",
|
|
5
5
|
"author": "Daniel G Wilson <daniel@danielgwilson.com>",
|
|
6
6
|
"keywords": [
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
|
-
"build": "tsc -p tsconfig.build.json && pnpm --filter humanish-observer build &&
|
|
53
|
-
"check": "pnpm typecheck && pnpm test && pnpm build",
|
|
52
|
+
"build": "tsc -p tsconfig.build.json && pnpm --filter humanish-observer build && pnpm --filter humanish-tui build && node scripts/finish-build.mjs",
|
|
53
|
+
"check": "pnpm typecheck && pnpm test && pnpm tui:test && pnpm build && pnpm tui:smoke",
|
|
54
54
|
"humanish": "tsx src/cli.ts",
|
|
55
55
|
"pack:dry-run": "npm pack --dry-run",
|
|
56
56
|
"prepack": "pnpm build",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"release:check": "pnpm check && pnpm public-surface:scan && pnpm skill:check && npm pack --dry-run",
|
|
59
59
|
"skill:check": "DISABLE_TELEMETRY=1 npx skills add . --list",
|
|
60
60
|
"test": "vitest run",
|
|
61
|
-
"typecheck": "tsc -p tsconfig.json --noEmit && pnpm --filter humanish-observer typecheck",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && pnpm --filter humanish-observer typecheck && pnpm --filter humanish-tui typecheck",
|
|
62
62
|
"humanish:doctor": "pnpm humanish -- doctor",
|
|
63
63
|
"humanish:run": "pnpm humanish -- run --dry-run",
|
|
64
64
|
"humanish:watch": "pnpm humanish -- watch",
|
|
@@ -69,7 +69,10 @@
|
|
|
69
69
|
"humanish:lab:list": "pnpm humanish -- lab list",
|
|
70
70
|
"humanish:lab:oss": "pnpm humanish -- lab run oss",
|
|
71
71
|
"humanish:lab:oss:ci": "pnpm humanish -- lab run oss --dry-run --json --no-open",
|
|
72
|
-
"humanish:lab:oss:smoke": "pnpm humanish -- lab run oss-smoke"
|
|
72
|
+
"humanish:lab:oss:smoke": "pnpm humanish -- lab run oss-smoke",
|
|
73
|
+
"tui:typecheck": "pnpm --filter humanish-tui typecheck",
|
|
74
|
+
"tui:smoke": "node scripts/tui-smoke.mjs",
|
|
75
|
+
"tui:test": "pnpm --filter humanish-tui test"
|
|
73
76
|
},
|
|
74
77
|
"repository": {
|
|
75
78
|
"type": "git",
|
package/skills/humanish/SKILL.md
CHANGED
|
@@ -21,6 +21,25 @@ assume broad inherited job env is safe. Stop before live provider spend,
|
|
|
21
21
|
hosted execution, deploys, public tunnels, or GitHub mutation unless the user
|
|
22
22
|
explicitly approves that exact action.
|
|
23
23
|
|
|
24
|
+
## Not For You: `humanish tui`
|
|
25
|
+
|
|
26
|
+
`humanish tui` is a human-only surface. It takes over the terminal and waits for
|
|
27
|
+
keystrokes, so it will block you and produce nothing you can read. It refuses a
|
|
28
|
+
non-interactive stdin or stdout with `HUMANISH_TUI_REQUIRES_TTY` rather than
|
|
29
|
+
rendering escape codes into your transcript — but do not invoke it at all.
|
|
30
|
+
|
|
31
|
+
Everything it shows has a machine-readable equivalent, which is what you want:
|
|
32
|
+
|
|
33
|
+
| Instead of the TUI | Use |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| browsing labs | `npx humanish lab list --json` |
|
|
36
|
+
| browsing runs | `npx humanish runs --json` |
|
|
37
|
+
| starting a run | `npx humanish lab run <lab> --json --no-open` |
|
|
38
|
+
| a run's outcome | `npx humanish review --run <id> --json` |
|
|
39
|
+
|
|
40
|
+
If a human asks you to "open the TUI", tell them the command to type; do not run
|
|
41
|
+
it on their behalf.
|
|
42
|
+
|
|
24
43
|
## Setup Workflow
|
|
25
44
|
|
|
26
45
|
1. Inspect public target-repo files only: `package.json`, docs, route/app
|