humanish 0.112.0 → 0.114.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/CHANGELOG.md +41 -0
- package/CONTEXT.md +1 -0
- package/README.md +12 -1
- package/TELEMETRY.md +21 -0
- package/dist/actors/codex/restricted-participant-policy.d.ts +21 -1
- package/dist/actors/codex/restricted-participant-policy.js +20 -2
- package/dist/actors/codex/restricted-participant.d.ts +1 -1
- package/dist/actors/codex/restricted-participant.js +2 -1
- package/dist/actors/computer-use/loop/debrief.d.ts +12 -4
- package/dist/actors/computer-use/loop/debrief.js +57 -20
- package/dist/actors/computer-use/loop/ending.d.ts +5 -2
- package/dist/actors/computer-use/loop/impressions.d.ts +14 -0
- package/dist/actors/computer-use/loop/impressions.js +40 -0
- package/dist/actors/computer-use/loop/session.d.ts +3 -1
- package/dist/actors/computer-use/loop/session.js +2 -0
- package/dist/actors/computer-use/loop/trace.js +2 -0
- package/dist/actors/computer-use/loop/types.d.ts +8 -1
- package/dist/actors/computer-use/loop.d.ts +2 -1
- package/dist/actors/computer-use/loop.js +15 -3
- package/dist/actors/computer-use/openai-conversation.d.ts +9 -4
- package/dist/actors/computer-use/openai-conversation.js +60 -29
- package/dist/actors/computer-use/openai-provider.js +18 -12
- package/dist/actors/contract.d.ts +47 -5
- package/dist/actors/contract.js +13 -0
- package/dist/analysis/admission.d.ts +37 -0
- package/dist/analysis/admission.js +60 -0
- package/dist/analysis/automatic-completion.d.ts +0 -6
- package/dist/analysis/automatic-completion.js +3 -3
- package/dist/analysis/automatic-config.d.ts +10 -2
- package/dist/analysis/automatic-config.js +29 -14
- package/dist/analysis/automatic.js +7 -0
- package/dist/analysis/evidence-sources.d.ts +3 -1
- package/dist/analysis/evidence-sources.js +10 -1
- package/dist/analysis/evidence.js +2 -2
- package/dist/analysis/execute.d.ts +20 -6
- package/dist/analysis/execute.js +61 -28
- package/dist/analysis/image-tokens.d.ts +2 -0
- package/dist/analysis/image-tokens.js +1 -1
- package/dist/analysis/job.d.ts +9 -1
- package/dist/analysis/job.js +11 -2
- package/dist/analysis/service.js +6 -3
- package/dist/cli/commands/analyze.js +7 -2
- package/dist/cli/commands/notes.d.ts +3 -0
- package/dist/cli/commands/notes.js +144 -0
- package/dist/cli/commands/observe.js +1 -1
- package/dist/cli/commands/runs.js +33 -10
- package/dist/cli/commands/setup.js +95 -6
- package/dist/cli/commands/study-format.js +7 -4
- package/dist/cli/commands/study-run.js +2 -1
- package/dist/cli/commands/study.js +1 -1
- package/dist/cli/commands/tui.js +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.js +18 -0
- package/dist/cli/findings.js +4 -1
- package/dist/cli/io.d.ts +3 -0
- package/dist/cli/io.js +11 -12
- package/dist/cli/program.js +37 -1
- package/dist/cli/secret-prompt.d.ts +5 -1
- package/dist/cli/secret-prompt.js +6 -2
- package/dist/cli/update-check-worker.d.ts +1 -0
- package/dist/cli/update-check-worker.js +5 -0
- package/dist/cli/update-check.d.ts +46 -0
- package/dist/cli/update-check.js +203 -0
- package/dist/evidence/encoded-text.d.ts +15 -2
- package/dist/evidence/encoded-text.js +68 -4
- package/dist/evidence/secret-scrub.d.ts +1 -1
- package/dist/evidence/secret-scrub.js +23 -2
- package/dist/feedback/draft.d.ts +19 -0
- package/dist/feedback/draft.js +69 -1
- package/dist/feedback/export-bundle.js +2 -2
- package/dist/feedback/export.js +47 -3
- package/dist/feedback/feedback.js +42 -2
- package/dist/keys/key-resolution.js +2 -0
- package/dist/keys/key-status.d.ts +37 -0
- package/dist/keys/key-status.js +68 -0
- package/dist/observer/artifact.d.ts +5 -0
- package/dist/observer/artifact.js +18 -2
- package/dist/observer/notes-route.d.ts +21 -0
- package/dist/observer/notes-route.js +140 -0
- package/dist/observer/pinned-files.d.ts +8 -1
- package/dist/observer/pinned-files.js +32 -3
- package/dist/observer/render.js +38 -14
- package/dist/observer/run-routes.d.ts +8 -2
- package/dist/observer/run-routes.js +47 -6
- package/dist/observer/serve.d.ts +3 -0
- package/dist/observer/serve.js +28 -3
- package/dist/observer-app.html +7 -6
- package/dist/routes/computer-use/local-vm.js +9 -2
- package/dist/routes/computer-use/participant-desktop.d.ts +6 -0
- package/dist/routes/computer-use/participant-execution.js +9 -1
- package/dist/routes/computer-use/rerun-selection.js +23 -21
- package/dist/routes/computer-use/result.js +3 -2
- package/dist/routes/computer-use/self-report.js +7 -1
- package/dist/routes/computer-use/setup.js +14 -0
- package/dist/routes/computer-use/types.d.ts +4 -1
- package/dist/routes/shared-world/bundle-records.js +8 -7
- package/dist/routes/shared-world/bundle.js +1 -1
- package/dist/run/contained-output.d.ts +7 -0
- package/dist/run/contained-output.js +35 -1
- package/dist/run/detail.js +4 -4
- package/dist/run/note-files.d.ts +73 -0
- package/dist/run/note-files.js +184 -0
- package/dist/run/notes.d.ts +37 -0
- package/dist/run/notes.js +198 -0
- package/dist/run/participant-caption.d.ts +11 -1
- package/dist/run/participant-caption.js +30 -1
- package/dist/run/participant-records.d.ts +3 -1
- package/dist/run/participant-records.js +6 -0
- package/dist/run-study.js +17 -1
- package/dist/study/discover.js +6 -1
- package/dist/study/doctor.d.ts +2 -0
- package/dist/study/doctor.js +46 -8
- package/dist/study/init-templates.js +10 -5
- package/dist/study/plan.js +3 -2
- package/dist/study/preflight-result.d.ts +1 -0
- package/dist/study/preflight-result.js +3 -2
- package/dist/study/preflight.d.ts +2 -0
- package/dist/study/preflight.js +54 -14
- package/dist/study/summary.js +2 -2
- package/dist/study/warnings.d.ts +5 -0
- package/dist/study/warnings.js +79 -2
- package/dist/substrates/local/capacity.d.ts +75 -0
- package/dist/substrates/local/capacity.js +143 -0
- package/dist/substrates/local/firecracker-desktop.d.ts +8 -0
- package/dist/substrates/local/firecracker-desktop.js +34 -3
- package/dist/substrates/local/runtime-host.d.ts +16 -3
- package/dist/substrates/local/runtime-host.js +49 -4
- package/dist/substrates/local/runtime.d.ts +10 -0
- package/dist/substrates/local/runtime.js +44 -5
- package/dist/tui-app.js +58 -58
- package/docs/architecture/actor-contract.md +20 -6
- package/docs/contracts/errors.md +2 -2
- package/docs/contracts/feedback.md +8 -1
- package/docs/contracts/run-bundle.md +57 -0
- package/docs/contracts/schemas.md +62 -36
- package/docs/contracts/study-analysis.md +41 -8
- package/docs/principles/invariants-and-defaults.md +2 -2
- package/docs/product/automatic-analysis.md +22 -16
- package/package.json +2 -2
- package/skills/humanish/SKILL.md +26 -10
|
@@ -9,6 +9,7 @@ import { listRuns } from "../run/stored-runs.js";
|
|
|
9
9
|
import { readAnalysisAccounting } from "../run/costs.js";
|
|
10
10
|
import { bindExistingRunArtifactPaths, isPathInside, isSafeRunIdSegment, } from "../run/paths.js";
|
|
11
11
|
import { isRunOutcome } from "../run/bundle-shape.js";
|
|
12
|
+
import { decodeRunNote, listNoteEntries, MAX_NOTE_FILE_BYTES, noteListing, RUN_NOTES_DIR, runNoteFile, } from "../run/note-files.js";
|
|
12
13
|
import { analysisCostOf, runCost, runCostLabel } from "../run/run-cost.js";
|
|
13
14
|
import { RUN_STATUS_FILE, RUN_STATUS_STALE_MS, isRunStatusRecord, } from "../run/status.js";
|
|
14
15
|
import { renderObserverHtml } from "./artifact.js";
|
|
@@ -16,7 +17,7 @@ import { buildObserverData, recordedStreamEmbed, withObserverEndings, } from "./
|
|
|
16
17
|
import { buildArtifactSecurityHeaders } from "./http.js";
|
|
17
18
|
import { assertPinnedDirectory, openContainedFile, pinDirectChildDirectory, readContainedFile, relativeToRoot, sha256OfOpenedFile, } from "./pinned-files.js";
|
|
18
19
|
/** The run's artifact paths, only when they are still the pinned directory being served. */
|
|
19
|
-
async function servedRunPaths(runRoot) {
|
|
20
|
+
export async function servedRunPaths(runRoot) {
|
|
20
21
|
const runId = path.basename(runRoot.physicalPath);
|
|
21
22
|
const cwd = path.dirname(path.dirname(path.dirname(runRoot.physicalPath)));
|
|
22
23
|
const prepared = await bindExistingRunArtifactPaths(cwd, runId);
|
|
@@ -57,8 +58,40 @@ async function readServedAnalysisSpend(runRoot) {
|
|
|
57
58
|
return undefined;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
/**
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* The run's reviewer notes as served: notes/ listed, and each note file read within
|
|
63
|
+
* MAX_NOTE_FILE_BYTES through the root's checks, so a --safe root's admission and hash checks
|
|
64
|
+
* apply to every note. A file that is not a readable note of the run is skipped and counted.
|
|
65
|
+
*/
|
|
66
|
+
async function readServedNotes(runRoot) {
|
|
67
|
+
const runId = path.basename(runRoot.physicalPath);
|
|
68
|
+
let listing;
|
|
69
|
+
try {
|
|
70
|
+
await assertPinnedDirectory(runRoot);
|
|
71
|
+
listing = await listNoteEntries(path.join(runRoot.physicalPath, RUN_NOTES_DIR));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return { runId, notes: [], skipped: [`${RUN_NOTES_DIR} is not a plain folder.`] };
|
|
75
|
+
}
|
|
76
|
+
if (listing === null)
|
|
77
|
+
return { runId, notes: [], skipped: [] };
|
|
78
|
+
const { ids, skipped } = noteListing(listing);
|
|
79
|
+
const notes = [];
|
|
80
|
+
for (const id of ids) {
|
|
81
|
+
const bytes = await readContainedFile(runRoot, path.join(runRoot.physicalPath, runNoteFile(id)), { maxBytes: MAX_NOTE_FILE_BYTES });
|
|
82
|
+
const note = bytes === null ? null : decodeRunNote(bytes, runId, id);
|
|
83
|
+
if (note === null)
|
|
84
|
+
skipped.push(`${runNoteFile(id)} was skipped.`);
|
|
85
|
+
else
|
|
86
|
+
notes.push(note);
|
|
87
|
+
}
|
|
88
|
+
return { runId, notes, skipped };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* internal: consumed by src/observer/serve.ts. `notesToken` goes into the run's Observer page so
|
|
92
|
+
* it can add notes; a server passes it only for a run it takes notes for.
|
|
93
|
+
*/
|
|
94
|
+
export async function serveRunPath(runRoot, relativePath, response, runtimeStreamUrls = [], request, notesToken) {
|
|
62
95
|
const root = runRoot.physicalPath;
|
|
63
96
|
const filePath = path.resolve(root, relativePath === "" ? "observer/index.html" : relativePath);
|
|
64
97
|
if (!isPathInside(root, filePath)) {
|
|
@@ -87,7 +120,12 @@ export async function serveRunPath(runRoot, relativePath, response, runtimeStrea
|
|
|
87
120
|
return;
|
|
88
121
|
}
|
|
89
122
|
const analysis = await readObserverAnalysis(runRoot);
|
|
90
|
-
|
|
123
|
+
const notes = await readServedNotes(runRoot);
|
|
124
|
+
writeResponse(response, 200, renderObserverHtml(observerData, {
|
|
125
|
+
analysis,
|
|
126
|
+
notes,
|
|
127
|
+
...(notesToken === undefined ? {} : { notesToken }),
|
|
128
|
+
}), "text/html; charset=utf-8");
|
|
91
129
|
return;
|
|
92
130
|
}
|
|
93
131
|
if (cleanedRelativePath === "observer/observer-data.json") {
|
|
@@ -103,7 +141,10 @@ export async function serveRunPath(runRoot, relativePath, response, runtimeStrea
|
|
|
103
141
|
writeResponse(response, 200, JSON.stringify(await readObserverAnalysis(runRoot)), "application/json; charset=utf-8");
|
|
104
142
|
return;
|
|
105
143
|
}
|
|
106
|
-
|
|
144
|
+
// Every file under notes/ is read within the note limit, whatever its name, so media handling
|
|
145
|
+
// never streams one.
|
|
146
|
+
const notePath = derivedRoot === RUN_NOTES_DIR;
|
|
147
|
+
if (!notePath && path.extname(filePath).toLowerCase() === ".mp4") {
|
|
107
148
|
await serveContainedMedia(runRoot, filePath, response, request);
|
|
108
149
|
return;
|
|
109
150
|
}
|
|
@@ -112,7 +153,7 @@ export async function serveRunPath(runRoot, relativePath, response, runtimeStrea
|
|
|
112
153
|
return;
|
|
113
154
|
}
|
|
114
155
|
try {
|
|
115
|
-
const body = await readContainedFile(runRoot, filePath);
|
|
156
|
+
const body = await readContainedFile(runRoot, filePath, notePath ? { maxBytes: MAX_NOTE_FILE_BYTES } : {});
|
|
116
157
|
if (!body) {
|
|
117
158
|
writeResponse(response, 404, "Not found", "text/plain; charset=utf-8");
|
|
118
159
|
return;
|
package/dist/observer/serve.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type NotesWriter } from "./notes-route.js";
|
|
2
3
|
import type { PinnedDirectory } from "./pinned-files.js";
|
|
3
4
|
import { type AdmittedRun } from "./run-inventory.js";
|
|
4
5
|
import type { LibraryHistory } from "./library.js";
|
|
@@ -70,6 +71,8 @@ export declare function createShareSafetyAdmission(cwd: string, options?: {
|
|
|
70
71
|
}): ShareSafetyAdmission;
|
|
71
72
|
export interface ServeRequestHandlerOptions {
|
|
72
73
|
proofRoot: PinnedDirectory;
|
|
74
|
+
/** Takes reviewer notes on loopback; without it the library refuses them. */
|
|
75
|
+
notes?: NotesWriter;
|
|
73
76
|
safe: boolean;
|
|
74
77
|
admit: (runId: string) => Promise<AdmittedRun | null>;
|
|
75
78
|
hostAllowlist: ReadonlySet<string>;
|
package/dist/observer/serve.js
CHANGED
|
@@ -3,7 +3,9 @@ import { runNotFoundMessage } from "../run/run-not-found.js";
|
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { pinDirectChildDirectory, pinDirectory } from "./pinned-files.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createNotesWriter, NOTES_PATH } from "./notes-route.js";
|
|
7
|
+
import { renderObserver } from "./render.js";
|
|
8
|
+
import { buildHistoryIndex, matchRunRoute, servedRunPaths, serveRunPath } from "./run-routes.js";
|
|
7
9
|
import { hashRunInventory, inventoryRoot, readRunInventory, } from "./run-inventory.js";
|
|
8
10
|
import { renderLibraryHtml } from "./library.js";
|
|
9
11
|
import { buildServeSecurityHeaders, hostAllowed, parsePublicOrigin, } from "./http.js";
|
|
@@ -104,6 +106,13 @@ export function createServeRequestHandler(options) {
|
|
|
104
106
|
for (const [name, value] of Object.entries(buildServeSecurityHeaders())) {
|
|
105
107
|
response.setHeader(name, value);
|
|
106
108
|
}
|
|
109
|
+
if (new URL(request.url ?? "/", "http://127.0.0.1").pathname === NOTES_PATH) {
|
|
110
|
+
if (options.notes)
|
|
111
|
+
await options.notes.handle(request, response);
|
|
112
|
+
else
|
|
113
|
+
writeText(response, 405, "Method Not Allowed");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
107
116
|
const method = request.method ?? "GET";
|
|
108
117
|
if (method !== "GET" && method !== "HEAD") {
|
|
109
118
|
writeText(response, 405, "Method Not Allowed");
|
|
@@ -161,7 +170,7 @@ export function createServeRequestHandler(options) {
|
|
|
161
170
|
}
|
|
162
171
|
// The explicit empty runtimeStreamUrls keeps a future refactor from
|
|
163
172
|
// reintroducing auth-keyed stream injection on the serve surface.
|
|
164
|
-
await serveRunPath(targetRoot, runRoute.relativePath || "observer/index.html", response, [], request);
|
|
173
|
+
await serveRunPath(targetRoot, runRoute.relativePath || "observer/index.html", response, [], request, options.notes?.pageToken(request));
|
|
165
174
|
return;
|
|
166
175
|
}
|
|
167
176
|
writeText(response, 404, "Not found");
|
|
@@ -260,8 +269,24 @@ export async function serveObserverLibrary(cwdInput, options) {
|
|
|
260
269
|
}
|
|
261
270
|
const declaredOrigin = options.publicOrigin ? parsePublicOrigin(options.publicOrigin) : null;
|
|
262
271
|
const hostAllowlist = new Set();
|
|
272
|
+
let port = 0;
|
|
273
|
+
const notes = createNotesWriter({
|
|
274
|
+
exposed: mode !== "loopback",
|
|
275
|
+
port: () => port,
|
|
276
|
+
resolveRun: async (runId) => {
|
|
277
|
+
const pinned = await pinDirectChildDirectory(proofRoot, runId);
|
|
278
|
+
if (!pinned || (options.safe && (await admission.admit(runId)) === null))
|
|
279
|
+
return null;
|
|
280
|
+
return servedRunPaths(pinned);
|
|
281
|
+
},
|
|
282
|
+
refresh: (prepared) => renderObserver(cwd, path.basename(prepared.physicalRunRoot), {
|
|
283
|
+
open: false,
|
|
284
|
+
expectedRun: prepared,
|
|
285
|
+
}),
|
|
286
|
+
});
|
|
263
287
|
const handler = createServeRequestHandler({
|
|
264
288
|
proofRoot,
|
|
289
|
+
notes,
|
|
265
290
|
safe: options.safe,
|
|
266
291
|
admit: (runId) => admission.admit(runId),
|
|
267
292
|
hostAllowlist,
|
|
@@ -276,7 +301,6 @@ export async function serveObserverLibrary(cwdInput, options) {
|
|
|
276
301
|
const server = createServer((request, response) => {
|
|
277
302
|
void handler(request, response);
|
|
278
303
|
});
|
|
279
|
-
let port;
|
|
280
304
|
try {
|
|
281
305
|
port = await listenOnLoopback(server, options.port);
|
|
282
306
|
}
|
|
@@ -314,6 +338,7 @@ export async function serveObserverLibrary(cwdInput, options) {
|
|
|
314
338
|
hostAllowlist.add(parsed.host);
|
|
315
339
|
},
|
|
316
340
|
close: async () => {
|
|
341
|
+
await notes.settled();
|
|
317
342
|
const closed = closeServer(server);
|
|
318
343
|
// Keep-alive sockets would otherwise keep close() pending past the point
|
|
319
344
|
// the operator believes Ctrl-C tore the server down.
|