humanish 0.88.2 → 0.89.1
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 +8 -0
- package/dist/export-bundle.js +6 -0
- package/dist/export-bundle.js.map +1 -1
- package/dist/export.js +67 -10
- package/dist/export.js.map +1 -1
- package/dist/feedback.d.ts +10 -0
- package/dist/feedback.js +58 -1
- package/dist/feedback.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/observer-app.html +10 -9
- package/dist/observer.d.ts +2 -0
- package/dist/observer.js +52 -5
- package/dist/observer.js.map +1 -1
- package/dist/pricing.js +2 -2
- package/dist/pricing.js.map +1 -1
- package/dist/program.js +146 -3
- package/dist/program.js.map +1 -1
- package/dist/run.d.ts +3 -1
- package/dist/run.js +46 -18
- package/dist/run.js.map +1 -1
- package/dist/study-analysis-engine.d.ts +34 -0
- package/dist/study-analysis-engine.js +217 -0
- package/dist/study-analysis-engine.js.map +1 -0
- package/dist/study-analysis-evidence.d.ts +27 -0
- package/dist/study-analysis-evidence.js +420 -0
- package/dist/study-analysis-evidence.js.map +1 -0
- package/dist/study-analysis-provider.d.ts +38 -0
- package/dist/study-analysis-provider.js +140 -0
- package/dist/study-analysis-provider.js.map +1 -0
- package/dist/study-analysis-service.d.ts +45 -0
- package/dist/study-analysis-service.js +207 -0
- package/dist/study-analysis-service.js.map +1 -0
- package/dist/study-analysis-sharing.d.ts +11 -0
- package/dist/study-analysis-sharing.js +27 -0
- package/dist/study-analysis-sharing.js.map +1 -0
- package/dist/study-analysis-store.d.ts +30 -0
- package/dist/study-analysis-store.js +343 -0
- package/dist/study-analysis-store.js.map +1 -0
- package/dist/study-analysis-validation.d.ts +448 -0
- package/dist/study-analysis-validation.js +331 -0
- package/dist/study-analysis-validation.js.map +1 -0
- package/dist/study-analysis.d.ts +167 -0
- package/dist/study-analysis.js +4 -0
- package/dist/study-analysis.js.map +1 -0
- package/docs/architecture/observer.md +10 -1
- package/docs/contracts/feedback.md +9 -3
- package/docs/contracts/schemas.md +4 -1
- package/docs/contracts/study-analysis.md +150 -0
- package/docs/goals/current.md +4 -3
- package/docs/ramp/README.md +12 -2
- package/docs/release/0.89.0-study-findings.md +79 -0
- package/docs/release/0.89.1-analysis-finished-notice.md +24 -0
- package/package.json +3 -2
package/dist/run.d.ts
CHANGED
|
@@ -1071,6 +1071,8 @@ export interface RunResult {
|
|
|
1071
1071
|
export interface VerifyResult {
|
|
1072
1072
|
schema: typeof VERIFY_SCHEMA;
|
|
1073
1073
|
ok: boolean;
|
|
1074
|
+
/** Only present when source evidence verifies but derived analysis failed the public-safety scan. */
|
|
1075
|
+
recordingOk?: boolean;
|
|
1074
1076
|
cwd: string;
|
|
1075
1077
|
run: string;
|
|
1076
1078
|
bundlePath?: string;
|
|
@@ -1082,7 +1084,7 @@ export interface VerifyResult {
|
|
|
1082
1084
|
shareSafety: {
|
|
1083
1085
|
status: "share_ready" | "local_only" | "blocked";
|
|
1084
1086
|
reasons: Array<{
|
|
1085
|
-
code: "VERIFY_FAILED" | "PUBLIC_SAFETY_FINDINGS" | "RAW_SCREENSHOTS";
|
|
1087
|
+
code: "VERIFY_FAILED" | "PUBLIC_SAFETY_FINDINGS" | "ANALYSIS_UNVERIFIED" | "RAW_SCREENSHOTS";
|
|
1086
1088
|
message: string;
|
|
1087
1089
|
}>;
|
|
1088
1090
|
};
|
package/dist/run.js
CHANGED
|
@@ -22,6 +22,8 @@ import { screenshotEvidenceError } from "./image-evidence.js";
|
|
|
22
22
|
import { buildObserverData } from "./observer-data.js";
|
|
23
23
|
import { parseResolvedPersona, personaToDirectives, renderPersonaPromptSection } from "./persona.js";
|
|
24
24
|
import { round6 } from "./pricing.js";
|
|
25
|
+
import { loadStudyAnalysis, listStudyAnalysisExecutions } from "./study-analysis-store.js";
|
|
26
|
+
import { isStudyAnalysisRecordPath, studyAnalysisSharingProblems } from "./study-analysis-sharing.js";
|
|
25
27
|
import { containsSensitive, digestText, redactText, redactToSecretLabel, tailText } from "./redaction.js";
|
|
26
28
|
import { bindExistingRunArtifactPaths, RUNS_RELATIVE_ROOT, isSafeRunIdSegment, prepareRunArtifactPaths, resolveExistingRunDirectory, resolveLatestRunDirectory, resolveRunsRoot, validatePreparedRunArtifactPaths } from "./run-paths.js";
|
|
27
29
|
import { probeKeySources } from "./key-resolution.js";
|
|
@@ -2612,7 +2614,13 @@ async function verifyPreparedRun(cwd, runInput, runPaths) {
|
|
|
2612
2614
|
ok: reviewJson !== null && reviewMarkdown !== null,
|
|
2613
2615
|
message: "review.json and review.md must exist"
|
|
2614
2616
|
});
|
|
2615
|
-
const
|
|
2617
|
+
const derivedPublicSafetyFindings = [];
|
|
2618
|
+
const publicSafetyFindings = await scanRunPublicSafetyArtifacts(runPaths, derivedPublicSafetyFindings);
|
|
2619
|
+
// JSON escapes can hide a sensitive value from the byte scan while the
|
|
2620
|
+
// decoded recording exposes it to Observer, feedback, or analysis input.
|
|
2621
|
+
if (publicSafetyFindings.length < 50 && bundle !== null && containsSensitivePattern(JSON.stringify(bundle))) {
|
|
2622
|
+
publicSafetyFindings.push("sensitive decoded run.json");
|
|
2623
|
+
}
|
|
2616
2624
|
checks.push({
|
|
2617
2625
|
name: "public-safety scan",
|
|
2618
2626
|
ok: publicSafetyFindings.length === 0,
|
|
@@ -2716,6 +2724,10 @@ async function verifyPreparedRun(cwd, runInput, runPaths) {
|
|
|
2716
2724
|
? "cost figures are absent, or every claimed estimate carries its ratesAsOf date + source and the total matches its known lines (estimates never presented as exact)"
|
|
2717
2725
|
: `cost labeling findings: ${costFindings.join(", ")}`
|
|
2718
2726
|
});
|
|
2727
|
+
const recordingOk = checks.every((check) => check.ok);
|
|
2728
|
+
if (derivedPublicSafetyFindings.length > 0)
|
|
2729
|
+
checks.push({ name: "derived analysis public-safety scan", ok: false,
|
|
2730
|
+
message: "Derived analysis contains sensitive text or unsafe artifact paths; sharing is blocked, original recording remains independently verifiable." });
|
|
2719
2731
|
const ok = checks.every((check) => check.ok);
|
|
2720
2732
|
const warnings = isRunBundle(bundle)
|
|
2721
2733
|
? [...rawScreenshotPostureWarnings(bundle), ...undeclaredSubjectStateWarnings(bundle), ...desktopGeometryWarnings(bundle)]
|
|
@@ -2731,9 +2743,23 @@ async function verifyPreparedRun(cwd, runInput, runPaths) {
|
|
|
2731
2743
|
}
|
|
2732
2744
|
]
|
|
2733
2745
|
};
|
|
2746
|
+
// Interpretation validity is independent of run validity. Keep recordings usable,
|
|
2747
|
+
// while refusing to silently promote stale or malformed derived text into sharing.
|
|
2748
|
+
const analysis = await loadStudyAnalysis(runPaths);
|
|
2749
|
+
const analysisSharing = studyAnalysisSharingProblems(analysis);
|
|
2750
|
+
const executionHistory = await listStudyAnalysisExecutions(runPaths);
|
|
2751
|
+
if (analysisSharing.unverified || analysisSharing.sensitive || executionHistory.warnings.length > 0) {
|
|
2752
|
+
warnings.push("Some study analysis or correction records could not be validated against current evidence.");
|
|
2753
|
+
shareSafety.reasons.push({ code: "ANALYSIS_UNVERIFIED", message: "Derived analysis or corrections need review against the current evidence before sharing." });
|
|
2754
|
+
if (analysisSharing.sensitive)
|
|
2755
|
+
shareSafety.status = "blocked";
|
|
2756
|
+
else if (shareSafety.status === "share_ready")
|
|
2757
|
+
shareSafety.status = "local_only";
|
|
2758
|
+
}
|
|
2734
2759
|
return {
|
|
2735
2760
|
schema: VERIFY_SCHEMA,
|
|
2736
2761
|
ok,
|
|
2762
|
+
...(!ok && recordingOk ? { recordingOk: true } : {}),
|
|
2737
2763
|
cwd,
|
|
2738
2764
|
run: runInput,
|
|
2739
2765
|
bundlePath: path.relative(cwd, bundlePath),
|
|
@@ -5040,15 +5066,17 @@ const riskyPublicArtifactPathSegments = new Set([
|
|
|
5040
5066
|
"Secure Preferences",
|
|
5041
5067
|
"profiles"
|
|
5042
5068
|
]);
|
|
5043
|
-
async function scanRunPublicSafetyArtifacts(runPaths) {
|
|
5069
|
+
async function scanRunPublicSafetyArtifacts(runPaths, derivedFindings) {
|
|
5044
5070
|
const findings = [];
|
|
5045
5071
|
await validatePreparedRunArtifactPaths(runPaths);
|
|
5046
|
-
await scanRunPublicSafetyDirectory(runPaths, "", findings);
|
|
5072
|
+
await scanRunPublicSafetyDirectory(runPaths, "", findings, derivedFindings);
|
|
5047
5073
|
await validatePreparedRunArtifactPaths(runPaths);
|
|
5048
5074
|
return findings;
|
|
5049
5075
|
}
|
|
5050
|
-
async function scanRunPublicSafetyDirectory(runPaths, relativeDirectory, findings) {
|
|
5051
|
-
|
|
5076
|
+
async function scanRunPublicSafetyDirectory(runPaths, relativeDirectory, findings, derivedFindings) {
|
|
5077
|
+
// Each authority has its own finding budget. Derived files must never consume
|
|
5078
|
+
// the source scan's budget and make an unscanned recording appear verified.
|
|
5079
|
+
if (findings.length >= 50 && derivedFindings.length >= 50) {
|
|
5052
5080
|
return;
|
|
5053
5081
|
}
|
|
5054
5082
|
const current = relativeDirectory
|
|
@@ -5057,33 +5085,33 @@ async function scanRunPublicSafetyDirectory(runPaths, relativeDirectory, finding
|
|
|
5057
5085
|
const entries = await readdir(current).catch(() => []);
|
|
5058
5086
|
for (const entryName of entries) {
|
|
5059
5087
|
const relativePath = relativeDirectory ? `${relativeDirectory}/${entryName}` : entryName;
|
|
5088
|
+
const stats = await lstat(path.join(current, entryName), { bigint: true }).catch(() => null);
|
|
5089
|
+
const selectedFindings = !stats?.isDirectory()
|
|
5090
|
+
&& (relativePath === "observer/study-analysis.json" || isStudyAnalysisRecordPath(relativePath)) ? derivedFindings : findings;
|
|
5060
5091
|
if (isRiskyPublicArtifactPath(relativePath) || containsSensitivePattern(relativePath)) {
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
return;
|
|
5092
|
+
if (selectedFindings.length < 50)
|
|
5093
|
+
selectedFindings.push(`risky artifact path ${relativePath}`);
|
|
5064
5094
|
}
|
|
5065
|
-
const stats = await lstat(path.join(current, entryName), { bigint: true }).catch(() => null);
|
|
5066
5095
|
if (!stats || stats.isSymbolicLink() || (!stats.isDirectory() && !stats.isFile()) || (stats.isFile() && stats.nlink > 1n)) {
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
return;
|
|
5096
|
+
if (selectedFindings.length < 50)
|
|
5097
|
+
selectedFindings.push(`unsafe artifact leaf ${relativePath}`);
|
|
5070
5098
|
continue;
|
|
5071
5099
|
}
|
|
5072
5100
|
if (stats.isDirectory()) {
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5101
|
+
// A directory named analysis.json is not an owned record. Its children
|
|
5102
|
+
// can contain source evidence even after derived findings are saturated.
|
|
5103
|
+
await scanRunPublicSafetyDirectory(runPaths, relativePath, findings, derivedFindings);
|
|
5076
5104
|
continue;
|
|
5077
5105
|
}
|
|
5106
|
+
if (selectedFindings.length >= 50)
|
|
5107
|
+
continue;
|
|
5078
5108
|
if (!shouldScanTextArtifact(relativePath)) {
|
|
5079
5109
|
continue;
|
|
5080
5110
|
}
|
|
5081
5111
|
const bytes = await readSafeRunArtifactBytes(runPaths, relativePath);
|
|
5082
5112
|
const text = bytes?.toString("utf8") ?? null;
|
|
5083
5113
|
if (text !== null && containsSensitivePattern(text)) {
|
|
5084
|
-
|
|
5085
|
-
if (findings.length >= 50)
|
|
5086
|
-
return;
|
|
5114
|
+
selectedFindings.push(`sensitive text ${relativePath}`);
|
|
5087
5115
|
}
|
|
5088
5116
|
}
|
|
5089
5117
|
}
|