humanish 0.114.0 → 0.116.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 -1
- package/README.md +7 -6
- package/TELEMETRY.md +6 -0
- package/dist/actors/closing-report-limits.d.ts +11 -0
- package/dist/actors/closing-report-limits.js +13 -0
- package/dist/actors/closing-report.d.ts +68 -0
- package/dist/actors/closing-report.js +55 -0
- package/dist/actors/codex/app-server-ui.js +11 -4
- package/dist/actors/codex/restricted-participant-policy.d.ts +4 -47
- package/dist/actors/codex/restricted-participant-policy.js +12 -40
- package/dist/actors/computer-use/loop/debrief.d.ts +1 -10
- package/dist/actors/computer-use/loop/debrief.js +68 -70
- package/dist/actors/computer-use/loop/impressions.d.ts +2 -2
- package/dist/actors/computer-use/loop/impressions.js +6 -1
- package/dist/actors/computer-use/loop.d.ts +0 -1
- package/dist/actors/computer-use/loop.js +3 -3
- package/dist/actors/computer-use/openai-conversation.d.ts +1 -1
- package/dist/actors/computer-use/openai-conversation.js +3 -29
- package/dist/actors/computer-use/openai-provider.js +11 -5
- package/dist/actors/contract.d.ts +2 -20
- package/dist/actors/contract.js +0 -13
- package/dist/actors/scripted-browser/steps.js +6 -5
- package/dist/analysis/admission.d.ts +14 -4
- package/dist/analysis/admission.js +14 -9
- package/dist/analysis/analysis-limits.d.ts +35 -0
- package/dist/analysis/analysis-limits.js +41 -0
- package/dist/analysis/execute.d.ts +5 -0
- package/dist/analysis/execute.js +3 -0
- package/dist/analysis/finding-lead.d.ts +27 -0
- package/dist/analysis/finding-lead.js +23 -0
- package/dist/analysis/service.js +5 -3
- package/dist/analysis/validation.js +14 -21
- package/dist/cli/commands/analyze.js +2 -1
- package/dist/cli/commands/notes.js +5 -3
- package/dist/cli/commands/run-signals.js +5 -1
- package/dist/cli/commands/runs.js +2 -1
- package/dist/cli/commands/setup.js +3 -3
- package/dist/cli/commands/study-format.d.ts +5 -2
- package/dist/cli/commands/study-format.js +29 -9
- package/dist/cli/commands/study-route-computer-use.js +2 -2
- package/dist/cli/commands/study-route-scripted.js +1 -1
- package/dist/cli/commands/tui.js +59 -17
- package/dist/cli/doctor.js +9 -2
- package/dist/cli/findings-text.d.ts +5 -0
- package/dist/cli/findings-text.js +169 -0
- package/dist/cli/findings.d.ts +0 -4
- package/dist/cli/findings.js +11 -195
- package/dist/cli/io.js +10 -8
- package/dist/cli/telemetry.js +10 -22
- package/dist/cli/update-check.js +10 -19
- package/dist/cli/user-config.d.ts +11 -0
- package/dist/cli/user-config.js +25 -0
- package/dist/comms/connections.js +4 -3
- package/dist/comms/email-catch.js +7 -31
- package/dist/comms/setup.js +18 -12
- package/dist/evidence/encoded-text.d.ts +12 -11
- package/dist/evidence/encoded-text.js +81 -70
- package/dist/evidence/redaction.d.ts +11 -0
- package/dist/evidence/redaction.js +30 -11
- package/dist/evidence/secret-scrub.d.ts +26 -3
- package/dist/evidence/secret-scrub.js +172 -38
- package/dist/feedback/draft.d.ts +1 -1
- package/dist/feedback/draft.js +13 -16
- package/dist/feedback/export-bundle.js +21 -17
- package/dist/feedback/export.js +32 -46
- package/dist/feedback/feedback.js +31 -26
- package/dist/guest/desktop-keys.js +59 -5
- package/dist/keys/key-resolution.d.ts +9 -0
- package/dist/keys/key-resolution.js +62 -55
- package/dist/keys/key-status.d.ts +2 -0
- package/dist/keys/key-status.js +9 -16
- package/dist/observer/artifact.d.ts +1 -1
- package/dist/observer/http.d.ts +7 -0
- package/dist/observer/http.js +19 -1
- package/dist/observer/notes-route.d.ts +0 -1
- package/dist/observer/notes-route.js +24 -44
- package/dist/observer/pinned-files.js +4 -19
- package/dist/observer/render.js +6 -4
- package/dist/observer/run-routes.js +13 -28
- package/dist/observer/serve.js +2 -1
- package/dist/observer-app.html +7 -7
- package/dist/routes/computer-use/bundle-parts.d.ts +4 -4
- package/dist/routes/computer-use/bundle-parts.js +2 -2
- package/dist/routes/computer-use/bundle.d.ts +4 -5
- package/dist/routes/computer-use/bundle.js +1 -3
- package/dist/routes/computer-use/fanout-records.js +10 -11
- package/dist/routes/computer-use/live-phase.d.ts +1 -1
- package/dist/routes/computer-use/live-phase.js +0 -1
- package/dist/routes/computer-use/participant-execution.d.ts +3 -2
- package/dist/routes/computer-use/participant-runs.d.ts +6 -2
- package/dist/routes/computer-use/participant-runs.js +9 -5
- package/dist/routes/computer-use/plan.d.ts +3 -0
- package/dist/routes/computer-use/plan.js +14 -11
- package/dist/routes/computer-use/rerun-selection.js +2 -2
- package/dist/routes/computer-use/result.js +3 -6
- package/dist/routes/computer-use/setup.d.ts +4 -6
- package/dist/routes/computer-use/setup.js +2 -6
- package/dist/routes/computer-use/single-bundle.d.ts +4 -5
- package/dist/routes/computer-use/single-bundle.js +22 -25
- package/dist/routes/computer-use/subject-projection.d.ts +4 -8
- package/dist/routes/computer-use/subject-projection.js +9 -31
- package/dist/routes/computer-use/types.d.ts +4 -41
- package/dist/routes/scripted/scenario.js +28 -12
- package/dist/routes/scripted/session-result.js +6 -3
- package/dist/routes/shared-world/bundle-records.js +2 -8
- package/dist/routes/shared-world/participant-specs.d.ts +0 -2
- package/dist/routes/shared-world/participant-specs.js +1 -20
- package/dist/routes/shared-world/plan.d.ts +3 -0
- package/dist/routes/shared-world/plan.js +23 -3
- package/dist/routes/shared-world/setup.js +2 -2
- package/dist/routes/terminal/ledger.d.ts +9 -9
- package/dist/routes/terminal/ledger.js +63 -40
- package/dist/routes/terminal/live-finish.js +7 -10
- package/dist/routes/terminal/plan.d.ts +3 -0
- package/dist/routes/terminal/plan.js +8 -5
- package/dist/routes/terminal/token-usage.d.ts +1 -2
- package/dist/routes/terminal/token-usage.js +4 -6
- package/dist/routes/terminal/types.d.ts +9 -1
- package/dist/run/bundle-shape.js +6 -1
- package/dist/run/bundle.d.ts +5 -3
- package/dist/run/contained-output.d.ts +59 -10
- package/dist/run/contained-output.js +160 -68
- package/dist/run/detail.d.ts +2 -1
- package/dist/run/detail.js +12 -7
- package/dist/run/escape-sequences.d.ts +11 -0
- package/dist/run/escape-sequences.js +93 -0
- package/dist/run/evidence-files.d.ts +8 -1
- package/dist/run/evidence-files.js +24 -18
- package/dist/run/locate.d.ts +32 -3
- package/dist/run/locate.js +45 -40
- package/dist/run/note-files.d.ts +26 -45
- package/dist/run/note-files.js +86 -58
- package/dist/run/note-shape.d.ts +35 -0
- package/dist/run/note-shape.js +20 -0
- package/dist/run/notes.d.ts +3 -14
- package/dist/run/notes.js +23 -75
- package/dist/run/outcomes.d.ts +24 -0
- package/dist/run/outcomes.js +19 -0
- package/dist/run/participant-records.d.ts +2 -0
- package/dist/run/participant-records.js +17 -2
- package/dist/run/paths.d.ts +2 -0
- package/dist/run/paths.js +5 -1
- package/dist/run/preflight-receipts.js +13 -8
- package/dist/run/project.js +7 -4
- package/dist/run/reclaim-outcomes.d.ts +8 -6
- package/dist/run/reclaim-outcomes.js +10 -6
- package/dist/run/reclaim.d.ts +5 -0
- package/dist/run/reclaim.js +43 -16
- package/dist/run/run-clock.d.ts +50 -0
- package/dist/run/run-clock.js +66 -0
- package/dist/run/run.js +5 -2
- package/dist/run/sandbox-ids.d.ts +7 -2
- package/dist/run/sandbox-ids.js +27 -11
- package/dist/run/secrets.d.ts +2 -1
- package/dist/run/secrets.js +18 -22
- package/dist/run/stored-runs.js +14 -7
- package/dist/run/transient-comms-secrets.d.ts +6 -5
- package/dist/run/transient-comms-secrets.js +23 -39
- package/dist/run-study.js +22 -13
- package/dist/study/adapter-scorer-loader.js +6 -4
- package/dist/study/discover.js +9 -6
- package/dist/study/init-paths.js +7 -5
- package/dist/study/persona-resolve.d.ts +2 -2
- package/dist/study/persona-resolve.js +9 -5
- package/dist/study/plan-types.d.ts +2 -2
- package/dist/study/plan.d.ts +5 -3
- package/dist/study/plan.js +18 -9
- package/dist/study/preflight-probes.js +11 -3
- package/dist/study/preflight.js +9 -5
- package/dist/study/summary.d.ts +12 -7
- package/dist/study/summary.js +24 -24
- package/dist/study/warnings.d.ts +19 -12
- package/dist/study/warnings.js +39 -17
- package/dist/substrates/e2b/connection.d.ts +12 -0
- package/dist/substrates/e2b/connection.js +35 -0
- package/dist/substrates/e2b/desktop-executor.d.ts +1 -3
- package/dist/substrates/e2b/desktop-executor.js +42 -9
- package/dist/substrates/e2b/lifetime.d.ts +24 -4
- package/dist/substrates/e2b/lifetime.js +41 -4
- package/dist/substrates/e2b/sandbox.d.ts +4 -3
- package/dist/substrates/e2b/sandbox.js +13 -18
- package/dist/substrates/e2b/sdk.d.ts +20 -9
- package/dist/substrates/local/capacity.d.ts +18 -2
- package/dist/substrates/local/capacity.js +21 -2
- package/dist/substrates/local/runtime-host.js +21 -14
- package/dist/substrates/local/runtime.js +3 -11
- package/dist/tui/contract.d.ts +30 -5
- package/dist/tui/contract.js +4 -1
- package/dist/tui-app.js +149 -147
- package/dist/verify/artifacts.d.ts +9 -2
- package/dist/verify/artifacts.js +37 -13
- package/dist/verify/liveness.js +19 -5
- package/dist/verify/verify.js +54 -27
- package/docs/architecture/actor-contract.md +3 -3
- package/docs/architecture/guest-desktop.md +7 -2
- package/docs/architecture/restricted-codex-analysis.md +1 -1
- package/docs/architecture/state-driven-executor.md +5 -3
- package/docs/contracts/run-bundle.md +25 -13
- package/docs/contracts/schemas.md +38 -29
- package/docs/contracts/study-analysis.md +22 -5
- package/docs/product/automatic-analysis.md +7 -1
- package/package.json +8 -7
- package/skills/humanish/SKILL.md +1 -2
package/dist/analysis/execute.js
CHANGED
|
@@ -145,6 +145,7 @@ export function estimateAnalysisAdmission(input, config) {
|
|
|
145
145
|
outputTokenAllowance: 0,
|
|
146
146
|
estimatedCostUsd: null,
|
|
147
147
|
worstCaseCostUsd: null,
|
|
148
|
+
admittedCostUsd: null,
|
|
148
149
|
maxCostUsd: null,
|
|
149
150
|
ratesAsOf: null,
|
|
150
151
|
});
|
|
@@ -178,6 +179,7 @@ export function estimateAnalysisAdmission(input, config) {
|
|
|
178
179
|
outputTokenAllowance: null,
|
|
179
180
|
estimatedCostUsd: null,
|
|
180
181
|
worstCaseCostUsd: null,
|
|
182
|
+
admittedCostUsd: null,
|
|
181
183
|
maxCostUsd: null,
|
|
182
184
|
ratesAsOf: null,
|
|
183
185
|
};
|
|
@@ -207,6 +209,7 @@ export function estimateAnalysisAdmission(input, config) {
|
|
|
207
209
|
outputTokenAllowance: config.maxOutputTokens,
|
|
208
210
|
estimatedCostUsd: cost.expectedCostUsd,
|
|
209
211
|
worstCaseCostUsd: cost.worstCaseCostUsd,
|
|
212
|
+
admittedCostUsd: cost.admittedCostUsd,
|
|
210
213
|
maxCostUsd: config.maxCostUsd,
|
|
211
214
|
ratesAsOf: rate.asOf,
|
|
212
215
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AnalysisCorrection, AnalysisResult } from "./types.js";
|
|
2
|
+
type Severity = NonNullable<AnalysisResult["designFindings"]>[number]["severity"];
|
|
3
|
+
export interface FindingLead {
|
|
4
|
+
/** The reviewer's amended claim, or the plain headline. */
|
|
5
|
+
headline: string;
|
|
6
|
+
/**
|
|
7
|
+
* The line under the headline: the plain experience, or after an amendment a sentence saying the
|
|
8
|
+
* reviewer's claim replaced the headline and account. Null when the analysis wrote no experience.
|
|
9
|
+
*/
|
|
10
|
+
account: string | null;
|
|
11
|
+
/** The headline is a reviewer's amended claim. */
|
|
12
|
+
corrected: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The headline and account a finding leads with, given its latest human review note. An amendment
|
|
16
|
+
* replaces both, since they describe the claim the reviewer replaced. Null for an analysis written
|
|
17
|
+
* before headlines, which leads with the finding's title.
|
|
18
|
+
*/
|
|
19
|
+
export declare function findingLead(finding: {
|
|
20
|
+
headline?: string | null;
|
|
21
|
+
experience?: string | null;
|
|
22
|
+
}, correction: Pick<AnalysisCorrection, "status" | "replacementClaim"> | null | undefined): FindingLead | null;
|
|
23
|
+
/** Design findings most severe first, in a new array; equal severity keeps the analysis order. */
|
|
24
|
+
export declare function bySeverity<T extends {
|
|
25
|
+
severity: Severity;
|
|
26
|
+
}>(findings: readonly T[]): T[];
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// How a finding opens for a reader, the same in `review`, a feedback draft and the Observer. The
|
|
2
|
+
// Observer bundles this module, so it imports types only (observer/tests/contract-lock.test.ts).
|
|
3
|
+
/**
|
|
4
|
+
* The headline and account a finding leads with, given its latest human review note. An amendment
|
|
5
|
+
* replaces both, since they describe the claim the reviewer replaced. Null for an analysis written
|
|
6
|
+
* before headlines, which leads with the finding's title.
|
|
7
|
+
*/
|
|
8
|
+
export function findingLead(finding, correction) {
|
|
9
|
+
if (finding.headline === undefined || finding.headline === null)
|
|
10
|
+
return null;
|
|
11
|
+
if (correction?.status === "amended" && correction.replacementClaim)
|
|
12
|
+
return {
|
|
13
|
+
headline: correction.replacementClaim,
|
|
14
|
+
account: "Corrected in human review. The reviewer's claim replaces the original headline and account.",
|
|
15
|
+
corrected: true,
|
|
16
|
+
};
|
|
17
|
+
return { headline: finding.headline, account: finding.experience ?? null, corrected: false };
|
|
18
|
+
}
|
|
19
|
+
const SEVERITY_ORDER = ["major", "moderate", "minor"];
|
|
20
|
+
/** Design findings most severe first, in a new array; equal severity keeps the analysis order. */
|
|
21
|
+
export function bySeverity(findings) {
|
|
22
|
+
return findings.toSorted((a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity));
|
|
23
|
+
}
|
package/dist/analysis/service.js
CHANGED
|
@@ -17,7 +17,8 @@ import { appendAnalysisCorrection, assertAnalysisPublicationCapacity, listAnalys
|
|
|
17
17
|
import { beginAnalysisExecution, writeAnalysisExecutionReceipt } from "./store-executions.js";
|
|
18
18
|
import { keepRejectedAnalysisOutput } from "./diagnostics.js";
|
|
19
19
|
import { loadAnalysis } from "./load.js";
|
|
20
|
-
import {
|
|
20
|
+
import { costRefusal, refusedCost } from "./admission.js";
|
|
21
|
+
import { cli } from "../cli/invocation.js";
|
|
21
22
|
import { hashAnalysisValue } from "./validation.js";
|
|
22
23
|
import { ANALYSIS_CORRECTION_SCHEMA, } from "./types.js";
|
|
23
24
|
import { RUN_BUNDLE_FILE } from "../run/bundle.js";
|
|
@@ -191,13 +192,14 @@ function analyzeConfigRefusal(config) {
|
|
|
191
192
|
function admissionOnlyResult(base, config) {
|
|
192
193
|
if (!base.admission.allowed) {
|
|
193
194
|
const cost = refusedCost(base.admission);
|
|
195
|
+
const refusal = cost && costRefusal(cost, `--run ${base.run}`, cli);
|
|
194
196
|
return {
|
|
195
197
|
...base,
|
|
196
198
|
ok: false,
|
|
197
199
|
error: {
|
|
198
200
|
code: base.admission.error ?? "analysis_admission_denied",
|
|
199
|
-
message:
|
|
200
|
-
? `${
|
|
201
|
+
message: refusal
|
|
202
|
+
? `${refusal.text} ${refusal.command}`
|
|
201
203
|
: "The analysis input or configuration did not pass admission. No provider request was sent.",
|
|
202
204
|
},
|
|
203
205
|
};
|
|
@@ -2,12 +2,13 @@ import { validStoredCodexAnalysisConfig } from "./codex-config.js";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { ACTOR_STATUSES, ACTOR_STOP_CAUSES } from "../actors/contract.js";
|
|
5
|
+
import { ANALYSIS_LIMITS, hasControlCharacter, hasRevisionFields } from "./analysis-limits.js";
|
|
5
6
|
import { SHA256_HEX_PATTERN, ACTION_CAPTURE_VERSION, ANALYSIS_SCHEMA, ANALYSIS_CORRECTION_SCHEMA, } from "./types.js";
|
|
6
7
|
const text = (max) => z
|
|
7
8
|
.string()
|
|
8
9
|
.max(max)
|
|
9
|
-
.refine((value) =>
|
|
10
|
-
const label = text(
|
|
10
|
+
.refine((value) => !hasControlCharacter(value), "Invalid text.");
|
|
11
|
+
const label = text(ANALYSIS_LIMITS.labelChars).min(1);
|
|
11
12
|
const id = z
|
|
12
13
|
.string()
|
|
13
14
|
.min(1)
|
|
@@ -17,7 +18,7 @@ const sourceId = text(256).min(1);
|
|
|
17
18
|
const digest = z.string().regex(SHA256_HEX_PATTERN);
|
|
18
19
|
const timestamp = z.string().datetime({ offset: true });
|
|
19
20
|
const sourceIds = z.array(sourceId).max(128);
|
|
20
|
-
const refs = z.array(id).min(1).max(
|
|
21
|
+
const refs = z.array(id).min(1).max(ANALYSIS_LIMITS.evidenceRefs);
|
|
21
22
|
const limitations = z.array(text(2000).min(1)).max(100);
|
|
22
23
|
const observationSchema = z
|
|
23
24
|
.object({
|
|
@@ -32,9 +33,9 @@ const designFindingSchema = z
|
|
|
32
33
|
id,
|
|
33
34
|
headline: label,
|
|
34
35
|
screen: label,
|
|
35
|
-
notice: text(
|
|
36
|
-
whyItMatters: text(
|
|
37
|
-
suggestion: text(
|
|
36
|
+
notice: text(ANALYSIS_LIMITS.noticeChars).min(1),
|
|
37
|
+
whyItMatters: text(ANALYSIS_LIMITS.whyItMattersChars).min(1),
|
|
38
|
+
suggestion: text(ANALYSIS_LIMITS.suggestionChars).min(1),
|
|
38
39
|
severity: z.enum(["minor", "moderate", "major"]),
|
|
39
40
|
confidence: z.enum(["low", "medium", "high"]),
|
|
40
41
|
seenByStreamIds: sourceIds.min(1),
|
|
@@ -66,7 +67,7 @@ const analysisResultSchema = z
|
|
|
66
67
|
id,
|
|
67
68
|
title: label,
|
|
68
69
|
headline: label.optional(),
|
|
69
|
-
experience: text(
|
|
70
|
+
experience: text(ANALYSIS_LIMITS.experienceChars).min(1).optional(),
|
|
70
71
|
summary: text(4000).min(1),
|
|
71
72
|
impact: z.enum(["blocked_task", "friction", "recovery", "uncertain"]),
|
|
72
73
|
affectedStreamIds: sourceIds.min(1),
|
|
@@ -79,8 +80,8 @@ const analysisResultSchema = z
|
|
|
79
80
|
priorityReason: text(2000).min(1),
|
|
80
81
|
})
|
|
81
82
|
.strict())
|
|
82
|
-
.max(
|
|
83
|
-
designFindings: z.array(designFindingSchema).max(
|
|
83
|
+
.max(ANALYSIS_LIMITS.findings),
|
|
84
|
+
designFindings: z.array(designFindingSchema).max(ANALYSIS_LIMITS.designFindings).optional(),
|
|
84
85
|
concernReviews: z
|
|
85
86
|
.array(observationSchema
|
|
86
87
|
.extend({
|
|
@@ -89,7 +90,7 @@ const analysisResultSchema = z
|
|
|
89
90
|
reason: text(2000).min(1),
|
|
90
91
|
})
|
|
91
92
|
.strict())
|
|
92
|
-
.max(
|
|
93
|
+
.max(ANALYSIS_LIMITS.concernReviews)
|
|
93
94
|
.optional(),
|
|
94
95
|
limitations,
|
|
95
96
|
})
|
|
@@ -100,8 +101,8 @@ export const analysisResponseSchema = analysisResultSchema
|
|
|
100
101
|
.extend({
|
|
101
102
|
findings: z
|
|
102
103
|
.array(analysisResultSchema.shape.findings.element.required({ headline: true, experience: true }))
|
|
103
|
-
.max(
|
|
104
|
-
designFindings: z.array(designFindingSchema).max(
|
|
104
|
+
.max(ANALYSIS_LIMITS.findings),
|
|
105
|
+
designFindings: z.array(designFindingSchema).max(ANALYSIS_LIMITS.designFindings),
|
|
105
106
|
})
|
|
106
107
|
.required({ concernReviews: true });
|
|
107
108
|
export const analysisResultJsonSchema = z.toJSONSchema(analysisResponseSchema);
|
|
@@ -469,15 +470,7 @@ export function validateAnalysisArtifact(value) {
|
|
|
469
470
|
(artifact.status === "complete" && !coverage.complete))
|
|
470
471
|
throw new Error("ANALYSIS_STATUS_INVALID");
|
|
471
472
|
if (artifact.result !== null) {
|
|
472
|
-
|
|
473
|
-
// findings with revision 7. Keep each boundary stable when the prompt version advances; a later
|
|
474
|
-
// prompt must not regain legacy omissions.
|
|
475
|
-
const revision = Number(/^study-evidence-(\d+)$/.exec(artifact.promptVersion)?.[1] ?? 0);
|
|
476
|
-
const result = artifact.result;
|
|
477
|
-
if ((revision >= 5 && result.concernReviews === undefined) ||
|
|
478
|
-
(revision >= 7 &&
|
|
479
|
-
(result.designFindings === undefined ||
|
|
480
|
-
result.findings.some((finding) => finding.headline === undefined || finding.experience === undefined))))
|
|
473
|
+
if (!hasRevisionFields(artifact.promptVersion, artifact.result))
|
|
481
474
|
throw new Error("ANALYSIS_RESULT_SCHEMA_INVALID");
|
|
482
475
|
validateAnalysisResult({ ...artifact, images: [] }, artifact.result);
|
|
483
476
|
}
|
|
@@ -6,7 +6,8 @@ import { listAnalyses } from "../../analysis/store.js";
|
|
|
6
6
|
import { listAnalysisExecutions } from "../../analysis/store-executions.js";
|
|
7
7
|
import { resolveRunPath } from "../../run/locate.js";
|
|
8
8
|
import { CWD_OPTION_DESCRIPTION, discoverCliKeys, JSON_OPTION_DESCRIPTION, RUN_OPTION_DESCRIPTION, writeResult, humanError, wantsJson, } from "../io.js";
|
|
9
|
-
import { analysisFindings,
|
|
9
|
+
import { analysisFindings, missingRunFindings, readRunAnalysis, } from "../findings.js";
|
|
10
|
+
import { formatFindings } from "../findings-text.js";
|
|
10
11
|
import { resolvePhysicalCwd } from "../../run/paths.js";
|
|
11
12
|
/** Commander may collect a shared flag on the parent; only explicit values override leaf defaults. */
|
|
12
13
|
function analysisSelection(options, command) {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { renderObserver } from "../../observer/render.js";
|
|
2
2
|
import { loadRunBundlePrepared, resolveRunPath } from "../../run/locate.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { readRunNotes } from "../../run/note-files.js";
|
|
4
|
+
import { byRunTime } from "../../run/note-shape.js";
|
|
5
|
+
import { addRunNote } from "../../run/notes.js";
|
|
6
|
+
import { runParticipantCaptions, streamParticipantIdOf } from "../../run/participant-records.js";
|
|
5
7
|
import { resolvePhysicalCwd, runIdOf } from "../../run/paths.js";
|
|
8
|
+
import { formatRunTime } from "../../run/run-clock.js";
|
|
6
9
|
import { runNotFoundMessage } from "../../run/run-not-found.js";
|
|
7
10
|
import { cli } from "../invocation.js";
|
|
8
11
|
import { CWD_OPTION_DESCRIPTION, humanError, JSON_OPTION_DESCRIPTION, writeResult, } from "../io.js";
|
|
@@ -42,7 +45,6 @@ function parseRunTime(value) {
|
|
|
42
45
|
const match = /^(\d{1,4}):([0-5]\d)$/.exec(value.trim());
|
|
43
46
|
return match ? (Number(match[1]) * 60 + Number(match[2])) * 1000 : null;
|
|
44
47
|
}
|
|
45
|
-
const byRunTime = (notes) => [...notes].sort((left, right) => left.atMs - right.atMs || left.createdAt.localeCompare(right.createdAt));
|
|
46
48
|
async function handleNotes(io, run, text, options, command) {
|
|
47
49
|
const base = {
|
|
48
50
|
schema: NOTES_RESULT_SCHEMA,
|
|
@@ -134,7 +134,11 @@ async function stopActiveRuns(io, signal, stopping, options) {
|
|
|
134
134
|
// not swept; verify grades a file that names a receipt's id local_only.
|
|
135
135
|
Promise.all(stopping
|
|
136
136
|
.filter((stop) => stop.report !== null)
|
|
137
|
-
.map((stop) => scrubRunSandboxIds(stop.run.paths).catch(() =>
|
|
137
|
+
.map((stop) => scrubRunSandboxIds(stop.run.paths).catch((error) => {
|
|
138
|
+
// A file the sweep refused to read may still name a sandbox; say which.
|
|
139
|
+
if (error instanceof Error)
|
|
140
|
+
say(io, `humanish: ${signal}: ${error.message}`);
|
|
141
|
+
}))));
|
|
138
142
|
const finished = await withDeadline(work, deadlineMs, options.deadline);
|
|
139
143
|
for (const stop of stopping) {
|
|
140
144
|
if (stop.report === null)
|
|
@@ -11,7 +11,8 @@ import { addRunOptions, handleRun } from "./run-command.js";
|
|
|
11
11
|
import { applyEnvFileOption, CWD_OPTION_DESCRIPTION, DOTENV_OPTION_DESCRIPTION, dotenvPathOf, envFileAliasOption, JSON_OPTION_DESCRIPTION, parsePositiveInteger, RUN_OPTION_DESCRIPTION, writeResult, humanError, } from "../io.js";
|
|
12
12
|
import { plural } from "../../run/text.js";
|
|
13
13
|
import { warnAndQueue } from "../deprecations.js";
|
|
14
|
-
import {
|
|
14
|
+
import { readRunFindings } from "../findings.js";
|
|
15
|
+
import { formatFindings } from "../findings-text.js";
|
|
15
16
|
import { cli } from "../invocation.js";
|
|
16
17
|
export function registerRunCommand(parent, io) {
|
|
17
18
|
addRunOptions(parent
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { listUserKeys, resolveKeyName, setUserKey, unsetUserKey, userKeyStorePath, } from "../../keys/key-resolution.js";
|
|
2
|
+
import { KEY_VENDORS, listUserKeys, resolveKeyName, setUserKey, unsetUserKey, userKeyStorePath, } from "../../keys/key-resolution.js";
|
|
3
3
|
import { askForMissingKeys, formatKeyStatus, keyStatus, otherStoredKeys, } from "../../keys/key-status.js";
|
|
4
4
|
import { promptSecret } from "../secret-prompt.js";
|
|
5
5
|
import { runInit } from "../../study/init.js";
|
|
@@ -270,7 +270,7 @@ export function registerKeysCommand(parent, io) {
|
|
|
270
270
|
.action(async (_options, command) => writeKeyStatus(command, io));
|
|
271
271
|
keys
|
|
272
272
|
.command("set")
|
|
273
|
-
.argument("[vendor-or-name]",
|
|
273
|
+
.argument("[vendor-or-name]", `A vendor alias (${KEY_VENDORS.join(", ")}) or a raw ENV_NAME. Without one, a terminal asks for each missing key in turn.`)
|
|
274
274
|
.description("Store one provider key in the user store (0600), prompted with hidden input. With no key named, ask for each missing key in turn.")
|
|
275
275
|
.option("--stdin", "Read the value from stdin instead of prompting (for agents/pipes).")
|
|
276
276
|
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
@@ -288,7 +288,7 @@ export function registerKeysCommand(parent, io) {
|
|
|
288
288
|
action: "set",
|
|
289
289
|
store: storePath,
|
|
290
290
|
names: [],
|
|
291
|
-
message: `Not a vendor alias or valid env name: ${vendorOrName}. Vendors:
|
|
291
|
+
message: `Not a vendor alias or valid env name: ${vendorOrName}. Vendors: ${KEY_VENDORS.join(", ")}.`,
|
|
292
292
|
};
|
|
293
293
|
writeResult(command, io, result, formatKeysHuman);
|
|
294
294
|
io.setExitCode(2);
|
|
@@ -3,7 +3,10 @@ import type { CuaActorStudyResult } from "../../routes/computer-use/types.js";
|
|
|
3
3
|
import type { ScriptedBrowserStudyResult } from "../../routes/scripted/types.js";
|
|
4
4
|
import type { TerminalProductStudyResult } from "../../routes/terminal/types.js";
|
|
5
5
|
import type { ConcurrentSharedWorldStudyResult } from "../../routes/shared-world/types.js";
|
|
6
|
+
import type { StudySubject } from "../../study/types.js";
|
|
6
7
|
export declare function formatConcurrentSharedWorldStudyHuman(result: ConcurrentSharedWorldStudyResult): HumanOutput;
|
|
7
8
|
export declare function formatTerminalStudyHuman(result: TerminalProductStudyResult): HumanOutput;
|
|
8
|
-
|
|
9
|
-
export declare function
|
|
9
|
+
/** `subject` is the study's declared subject, which names it when the result has no URL. */
|
|
10
|
+
export declare function formatScriptedStudyHuman(result: ScriptedBrowserStudyResult, subject: StudySubject): HumanOutput;
|
|
11
|
+
/** `subject` is the study's declared subject, which names it when the result has no URL. */
|
|
12
|
+
export declare function formatCuaStudyHuman(result: CuaActorStudyResult, subject: StudySubject): HumanOutput;
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { participantCaption } from "../../run/participant-caption.js";
|
|
2
2
|
import { formatCuaDiagnostics, formatCuaStopCause } from "../../routes/computer-use/diagnostics.js";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* A run's first lines: the command that ran, whether it was a dry run, how it ended, and its route.
|
|
5
|
+
* `runOk` is the run's own ok, which automaticAnalysisEnvelope keeps when an analysis that did not
|
|
6
|
+
* complete turns `ok` false. That run finished, and the line says so.
|
|
7
|
+
*/
|
|
4
8
|
function runHeader(result, route) {
|
|
5
9
|
const kind = result.dryRun === true ? "dry run" : result.dryRun === false ? "live run" : "run";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
const ending = result.ok
|
|
11
|
+
? "finished"
|
|
12
|
+
: result.runOk === true
|
|
13
|
+
? "finished; the analysis did not complete"
|
|
14
|
+
: "failed";
|
|
15
|
+
return [`humanish run ${result.studyId}: ${kind} ${ending}`, `route: ${route}`];
|
|
10
16
|
}
|
|
11
17
|
/**
|
|
12
18
|
* A run's human output: its lines on stdout and, when it failed with an error, that error on
|
|
@@ -31,6 +37,18 @@ function sandboxIdsLine(result) {
|
|
|
31
37
|
? [`sandbox ids: .humanish/runs/${result.runId}/sandbox-receipts.ndjson`]
|
|
32
38
|
: [];
|
|
33
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* How a run's output names its subject: by the URL its participants open. A product studied at a
|
|
42
|
+
* desktop has no URL, so it is named with its source, as in `humanish (desktop-cli)`. A refusal
|
|
43
|
+
* made before the route resolved a URL has none either, so it names the URL the study declares.
|
|
44
|
+
*/
|
|
45
|
+
function subjectName(appUrl, subject) {
|
|
46
|
+
if (appUrl !== "")
|
|
47
|
+
return appUrl;
|
|
48
|
+
if (subject.product !== undefined)
|
|
49
|
+
return `${subject.product.name} (${subject.source})`;
|
|
50
|
+
return subject.appUrl ?? subject.serve?.url ?? subject.source;
|
|
51
|
+
}
|
|
34
52
|
/** A participant's status in words: a dry run's placeholder status says that nothing ran live. */
|
|
35
53
|
function participantStatus(status) {
|
|
36
54
|
return status === "contract_proof_only" ? "dry run, nothing ran live" : status;
|
|
@@ -83,12 +101,13 @@ export function formatTerminalStudyHuman(result) {
|
|
|
83
101
|
...result.warnings.map((warning) => `warning: ${warning}`),
|
|
84
102
|
]);
|
|
85
103
|
}
|
|
86
|
-
|
|
104
|
+
/** `subject` is the study's declared subject, which names it when the result has no URL. */
|
|
105
|
+
export function formatScriptedStudyHuman(result, subject) {
|
|
87
106
|
return withError(result.error, [
|
|
88
107
|
...runHeader(result, "scripted"),
|
|
89
108
|
`run: ${result.runId}`,
|
|
90
109
|
`actor: ${result.actor}`,
|
|
91
|
-
`subject: ${result.appUrl}`,
|
|
110
|
+
`subject: ${subjectName(result.appUrl, subject)}`,
|
|
92
111
|
...(result.scenario
|
|
93
112
|
? [
|
|
94
113
|
`scenario: ${result.scenario.id} @ ${result.scenario.sourceDigest.slice(0, 12)} (${result.scenario.source}, ${result.scenario.steps} step${result.scenario.steps === 1 ? "" : "s"})`,
|
|
@@ -108,12 +127,13 @@ export function formatScriptedStudyHuman(result) {
|
|
|
108
127
|
...result.warnings.map((warning) => `warning: ${warning}`),
|
|
109
128
|
]);
|
|
110
129
|
}
|
|
111
|
-
|
|
130
|
+
/** `subject` is the study's declared subject, which names it when the result has no URL. */
|
|
131
|
+
export function formatCuaStudyHuman(result, subject) {
|
|
112
132
|
return withError(result.error, [
|
|
113
133
|
...runHeader(result, "computer-use"),
|
|
114
134
|
`run: ${result.runId}`,
|
|
115
135
|
`actor: ${result.actor}`,
|
|
116
|
-
`subject: ${result.appUrl}`,
|
|
136
|
+
`subject: ${subjectName(result.appUrl, subject)}`,
|
|
117
137
|
...(result.subject?.source === "clone"
|
|
118
138
|
? [
|
|
119
139
|
`repo: ${result.subject.repo}${result.subject.commit ? `@${result.subject.commit.slice(0, 12)}` : ""}${result.subject.envNames && result.subject.envNames.length > 0 ? ` env=[${result.subject.envNames.join(", ")}]` : ""}`,
|
|
@@ -57,7 +57,7 @@ function refuseCua(args, dryRun, code, message) {
|
|
|
57
57
|
dryRun,
|
|
58
58
|
runId: args.options.runId ?? "not-created",
|
|
59
59
|
});
|
|
60
|
-
writeResult(args.command, args.io, result, formatCuaStudyHuman);
|
|
60
|
+
writeResult(args.command, args.io, result, (value) => formatCuaStudyHuman(value, args.config.subject));
|
|
61
61
|
args.io.setExitCode(2);
|
|
62
62
|
}
|
|
63
63
|
/** Parses the options, or writes the refusal and returns undefined. */
|
|
@@ -236,7 +236,7 @@ async function reportCuaRun(args, prepared, result, live) {
|
|
|
236
236
|
],
|
|
237
237
|
};
|
|
238
238
|
}
|
|
239
|
-
writeResult(args.command, args.io, output, formatCuaStudyHuman);
|
|
239
|
+
writeResult(args.command, args.io, output, (value) => formatCuaStudyHuman(value, args.config.subject));
|
|
240
240
|
args.io.setExitCode(result.ok && automaticAnalysisSucceeded(result) ? 0 : 2);
|
|
241
241
|
await writeRunFindings(args.command, args.io, args.options.cwd, result);
|
|
242
242
|
if (server && (result.observer?.ok || attachedObserver)) {
|
|
@@ -34,7 +34,7 @@ export function scriptedRouteRun(args) {
|
|
|
34
34
|
throw new Error(`Expected the scripted route, got ${outcome.route}.`);
|
|
35
35
|
}
|
|
36
36
|
const result = outcome.result;
|
|
37
|
-
writeResult(args.command, args.io, result, formatScriptedStudyHuman);
|
|
37
|
+
writeResult(args.command, args.io, result, (value) => formatScriptedStudyHuman(value, args.config.subject));
|
|
38
38
|
args.io.setExitCode(result.ok && automaticAnalysisSucceeded(result) ? 0 : 2);
|
|
39
39
|
await writeRunFindings(args.command, args.io, args.options.cwd, result);
|
|
40
40
|
// Watch mode serves the Observer the route rendered through its finished run.
|
package/dist/cli/commands/tui.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
-
import { setUserKey } from "../../keys/key-resolution.js";
|
|
4
|
+
import { LISTED_KEYS, setUserKey } from "../../keys/key-resolution.js";
|
|
5
|
+
import { keyStatus, keyStatusLine } from "../../keys/key-status.js";
|
|
5
6
|
import { saveCommsConnection } from "../../comms/connections.js";
|
|
6
7
|
import { readCommsSetup } from "../../comms/setup.js";
|
|
7
8
|
import { checkCommsConnection, configureCommsStudy, } from "../../comms/setup.js";
|
|
@@ -160,13 +161,11 @@ async function runTuiSession(loaded, runtime, cwd, discoveredKeys) {
|
|
|
160
161
|
runIndexCache: new RunIndexCache(),
|
|
161
162
|
observerSession: createTuiObserverSession(cwd),
|
|
162
163
|
};
|
|
163
|
-
let
|
|
164
|
+
let returnTo;
|
|
164
165
|
try {
|
|
165
166
|
for (;;) {
|
|
166
167
|
const outcome = await loaded.startTui({
|
|
167
|
-
...
|
|
168
|
-
? {}
|
|
169
|
-
: { initialScreen: "connections", connectionNotice }),
|
|
168
|
+
...returnTo,
|
|
170
169
|
cwd,
|
|
171
170
|
version: { cli: CLI_VERSION },
|
|
172
171
|
capabilities: tuiCapabilities(session),
|
|
@@ -175,9 +174,17 @@ async function runTuiSession(loaded, runtime, cwd, discoveredKeys) {
|
|
|
175
174
|
});
|
|
176
175
|
if (typeof outcome === "number")
|
|
177
176
|
return outcome;
|
|
178
|
-
|
|
177
|
+
returnTo =
|
|
178
|
+
outcome.action === "agentmail-key"
|
|
179
|
+
? { initialScreen: "connections", connectionNotice: await storeAgentmailKey(session) }
|
|
180
|
+
: outcome.action === "provider-key"
|
|
181
|
+
? {
|
|
182
|
+
initialScreen: "keys",
|
|
183
|
+
connectionNotice: await storeProviderKey(session, outcome.name),
|
|
184
|
+
}
|
|
185
|
+
: undefined;
|
|
186
|
+
if (returnTo === undefined)
|
|
179
187
|
return 1;
|
|
180
|
-
connectionNotice = await storeAgentmailKey(session);
|
|
181
188
|
}
|
|
182
189
|
}
|
|
183
190
|
finally {
|
|
@@ -197,6 +204,14 @@ function connectionEnv(session) {
|
|
|
197
204
|
function tuiCapabilities(session) {
|
|
198
205
|
const { cwd, runtime, sessionEnv, runIndexCache, observerSession } = session;
|
|
199
206
|
return {
|
|
207
|
+
keys: {
|
|
208
|
+
// The project's overlay and the stores, as `humanish keys` reads them from this directory.
|
|
209
|
+
status: async () => (await keyStatus({ cwd, env: connectionEnv(session) })).map((row) => ({
|
|
210
|
+
name: row.name,
|
|
211
|
+
set: row.source !== null,
|
|
212
|
+
line: keyStatusLine(row),
|
|
213
|
+
})),
|
|
214
|
+
},
|
|
200
215
|
comms: {
|
|
201
216
|
read: async () => ({
|
|
202
217
|
...(await readCommsSetup(cwd, connectionEnv(session))),
|
|
@@ -270,21 +285,48 @@ function tuiCapabilities(session) {
|
|
|
270
285
|
},
|
|
271
286
|
};
|
|
272
287
|
}
|
|
288
|
+
const ENTRY_CANCELLED = "Key entry cancelled. Nothing was changed.";
|
|
289
|
+
const ENTRY_FAILED = "Could not store the key. Use a single non-empty line and check key-store permissions.";
|
|
290
|
+
/**
|
|
291
|
+
* Asks for one key with hidden input, after the surface has unmounted, and stores it in the user
|
|
292
|
+
* store. Only the host reads the credential. A value that discovery filled into this session is
|
|
293
|
+
* dropped so the stored one is read; an explicit env or file value still wins.
|
|
294
|
+
*/
|
|
295
|
+
async function promptAndStoreKey(session, name, label) {
|
|
296
|
+
const { runtime, sessionEnv } = session;
|
|
297
|
+
const value = await runtime.promptSecret(label, runtime.stdin, runtime.stdout);
|
|
298
|
+
if (value === null || value === "")
|
|
299
|
+
return "cancelled";
|
|
300
|
+
setUserKey(name, value, sessionEnv);
|
|
301
|
+
if (session.discoveredKeys.has(name))
|
|
302
|
+
delete sessionEnv[name];
|
|
303
|
+
return "stored";
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* A key from the keys screen, asked for and stored as `humanish keys set <vendor>` does. Resolves to
|
|
307
|
+
* the notice the remounted keys screen shows.
|
|
308
|
+
*/
|
|
309
|
+
async function storeProviderKey(session, name) {
|
|
310
|
+
if (!LISTED_KEYS.some((key) => key.name === name))
|
|
311
|
+
return `${name} is not a key humanish keys lists. Nothing was changed.`;
|
|
312
|
+
try {
|
|
313
|
+
return (await promptAndStoreKey(session, name, `Value for ${name}`)) === "stored"
|
|
314
|
+
? `${name} stored.`
|
|
315
|
+
: ENTRY_CANCELLED;
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
return ENTRY_FAILED;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
273
321
|
/**
|
|
274
322
|
* Prompts for the AgentMail key after the surface has unmounted, stores it and checks it. Resolves
|
|
275
323
|
* to the notice the remounted connections screen shows.
|
|
276
324
|
*/
|
|
277
325
|
async function storeAgentmailKey(session) {
|
|
278
|
-
const { cwd, runtime
|
|
279
|
-
// startTui has unmounted: only the host reads the credential, then remounts the view.
|
|
280
|
-
const value = await runtime.promptSecret("AgentMail API key", runtime.stdin, runtime.stdout);
|
|
281
|
-
if (value === null || value === "")
|
|
282
|
-
return "Key entry cancelled. Nothing was changed.";
|
|
326
|
+
const { cwd, runtime } = session;
|
|
283
327
|
try {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (session.discoveredKeys.has("AGENTMAIL_API_KEY"))
|
|
287
|
-
delete sessionEnv.AGENTMAIL_API_KEY;
|
|
328
|
+
if ((await promptAndStoreKey(session, "AGENTMAIL_API_KEY", "AgentMail API key")) !== "stored")
|
|
329
|
+
return ENTRY_CANCELLED;
|
|
288
330
|
const saved = await saveCommsConnection(cwd);
|
|
289
331
|
const storedNotice = saved.ok
|
|
290
332
|
? "Key stored. Project connection saved."
|
|
@@ -301,6 +343,6 @@ async function storeAgentmailKey(session) {
|
|
|
301
343
|
: `${storedNotice} ${check.message}`;
|
|
302
344
|
}
|
|
303
345
|
catch {
|
|
304
|
-
return
|
|
346
|
+
return ENTRY_FAILED;
|
|
305
347
|
}
|
|
306
348
|
}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -2,8 +2,9 @@ import { readFile, readdir } from "node:fs/promises";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { probeKeySources, } from "../keys/key-resolution.js";
|
|
5
|
+
import { DOCTOR_KEYS, probeKeySources, } from "../keys/key-resolution.js";
|
|
6
6
|
import { desktopPeerAdvice, humanishInstall, } from "../substrates/e2b/peer-install.js";
|
|
7
|
+
import { describeSandboxCeiling, sandboxCeiling } from "../substrates/e2b/lifetime.js";
|
|
7
8
|
import { nodeSupportsTui, terminalSurfaceMessage, TUI_BUNDLE_URL } from "../tui/contract.js";
|
|
8
9
|
import { detectLocalAgents, localAgentDoctorMessage, localAgentReady, NO_LOCAL_AGENT_MESSAGE, } from "../actors/local-agent/cli.js";
|
|
9
10
|
import { leftoverClaudeTranscripts } from "../actors/local-agent/claude-participant.js";
|
|
@@ -95,6 +96,7 @@ export async function doctor(cwdInput, options = {}) {
|
|
|
95
96
|
const checks = [
|
|
96
97
|
...(await projectChecks(projectRoot)),
|
|
97
98
|
await desktopSdkCheck(setup),
|
|
99
|
+
sandboxCeilingCheck(env),
|
|
98
100
|
terminalSurfaceCheck(),
|
|
99
101
|
...versionCheck(env),
|
|
100
102
|
...localAgentChecks(agents),
|
|
@@ -191,6 +193,11 @@ async function runtimeIgnoreCheck(projectRoot) {
|
|
|
191
193
|
: `.gitignore does not list .humanish/; run ${cli("init --yes")}`,
|
|
192
194
|
};
|
|
193
195
|
}
|
|
196
|
+
/** The longest sandbox lifetime the E2B planners allow, and where that number came from. */
|
|
197
|
+
function sandboxCeilingCheck(env) {
|
|
198
|
+
const ceiling = sandboxCeiling(env);
|
|
199
|
+
return { name: "e2b sandbox ceiling", ok: ceiling.ok, message: describeSandboxCeiling(ceiling) };
|
|
200
|
+
}
|
|
194
201
|
/**
|
|
195
202
|
* The optional peer dep every live browser and terminal route needs. `npx -y humanish` does not
|
|
196
203
|
* pull optional peers, so without this check an adopter's first live run fails on it, safely and at
|
|
@@ -266,7 +273,7 @@ function versionCheck(env) {
|
|
|
266
273
|
}
|
|
267
274
|
/** Probes the live-run keys, plus the receiving-email key a real-comms study names. */
|
|
268
275
|
async function probeDoctorKeys(cwd, env, study, keyDeps, dotenv) {
|
|
269
|
-
const keyNames = new Set(
|
|
276
|
+
const keyNames = new Set(DOCTOR_KEYS);
|
|
270
277
|
let receivingKey = null;
|
|
271
278
|
if (study) {
|
|
272
279
|
const { resolveStudyManifest } = await import("../study/discover.js");
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnalysisFindings } from "./findings.js";
|
|
2
|
+
/** The full findings block: the analysis, every finding with its evidence, and its limitations. */
|
|
3
|
+
export declare function formatFindings(view: AnalysisFindings): string[];
|
|
4
|
+
/** At most `limit` findings, one line each, and the command that prints all of them. */
|
|
5
|
+
export declare function formatFindingsSummary(view: AnalysisFindings, fullCommand: string, limit?: number): string[];
|