humanish 0.109.1 → 0.110.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.
Files changed (164) hide show
  1. package/ARCHITECTURE.md +14 -11
  2. package/CHANGELOG.md +24 -0
  3. package/CONTEXT.md +8 -6
  4. package/README.md +97 -48
  5. package/dist/actors/computer-use/loop/ending.js +4 -4
  6. package/dist/actors/local-agent/claude-participant.d.ts +74 -0
  7. package/dist/actors/local-agent/claude-participant.js +243 -0
  8. package/dist/actors/local-agent/claude-session.d.ts +16 -3
  9. package/dist/actors/local-agent/claude-session.js +64 -23
  10. package/dist/actors/local-agent/cli.d.ts +11 -0
  11. package/dist/actors/local-agent/cli.js +103 -24
  12. package/dist/actors/local-agent/readiness.d.ts +2 -2
  13. package/dist/actors/local-agent/readiness.js +18 -11
  14. package/dist/cli/commands/analyze.js +21 -5
  15. package/dist/cli/commands/comms.js +25 -7
  16. package/dist/cli/commands/observe.js +4 -2
  17. package/dist/cli/commands/run-command.js +4 -3
  18. package/dist/cli/commands/run-signals.d.ts +2 -0
  19. package/dist/cli/commands/run-signals.js +96 -44
  20. package/dist/cli/commands/runs.d.ts +5 -0
  21. package/dist/cli/commands/runs.js +89 -67
  22. package/dist/cli/commands/setup.js +19 -5
  23. package/dist/cli/commands/study-format.js +29 -2
  24. package/dist/cli/commands/study-route-computer-use.js +2 -0
  25. package/dist/cli/commands/study-route-scripted.js +2 -0
  26. package/dist/cli/commands/study-route-shared-world.js +2 -0
  27. package/dist/cli/commands/study-route-terminal.js +2 -0
  28. package/dist/cli/commands/study.js +4 -3
  29. package/dist/cli/commands/tui.js +4 -3
  30. package/dist/cli/commands/watch.js +2 -2
  31. package/dist/cli/doctor.d.ts +7 -2
  32. package/dist/cli/doctor.js +47 -8
  33. package/dist/cli/findings.d.ts +103 -0
  34. package/dist/cli/findings.js +316 -0
  35. package/dist/cli/first-run-path.d.ts +5 -4
  36. package/dist/cli/first-run-path.js +14 -7
  37. package/dist/cli/io.d.ts +17 -3
  38. package/dist/cli/io.js +22 -4
  39. package/dist/cli/product-sentence.d.ts +3 -3
  40. package/dist/cli/product-sentence.js +3 -3
  41. package/dist/comms/receiving-runtime.js +1 -1
  42. package/dist/comms/setup.js +1 -1
  43. package/dist/evidence/redaction.d.ts +7 -3
  44. package/dist/evidence/redaction.js +68 -24
  45. package/dist/feedback/export-bundle.js +6 -1
  46. package/dist/feedback/export.js +3 -0
  47. package/dist/keys/env-file.d.ts +4 -0
  48. package/dist/keys/env-file.js +17 -0
  49. package/dist/keys/key-resolution.d.ts +7 -0
  50. package/dist/keys/key-resolution.js +10 -8
  51. package/dist/observer/data.d.ts +14 -1
  52. package/dist/observer/data.js +19 -9
  53. package/dist/observer/library.d.ts +4 -0
  54. package/dist/observer/library.js +18 -15
  55. package/dist/observer/render.js +8 -2
  56. package/dist/observer/run-routes.d.ts +2 -0
  57. package/dist/observer/run-routes.js +43 -34
  58. package/dist/observer-app.html +3 -3
  59. package/dist/routes/computer-use/bundle.js +3 -1
  60. package/dist/routes/computer-use/fanout-records.js +1 -1
  61. package/dist/routes/computer-use/live-phase.d.ts +1 -0
  62. package/dist/routes/computer-use/participant-execution.js +3 -1
  63. package/dist/routes/computer-use/participant-model.js +5 -2
  64. package/dist/routes/computer-use/plan.js +4 -4
  65. package/dist/routes/computer-use/preflight.js +2 -2
  66. package/dist/routes/computer-use/result.js +17 -28
  67. package/dist/routes/computer-use/single-bundle.js +7 -1
  68. package/dist/routes/computer-use/types.d.ts +6 -0
  69. package/dist/routes/scripted/bundle.js +4 -2
  70. package/dist/routes/scripted/plan.js +2 -2
  71. package/dist/routes/scripted/result.d.ts +3 -5
  72. package/dist/routes/scripted/result.js +16 -22
  73. package/dist/routes/scripted/scenario.js +4 -4
  74. package/dist/routes/scripted/types.d.ts +1 -0
  75. package/dist/routes/shared-world/bundle-records.js +6 -1
  76. package/dist/routes/shared-world/bundle.js +2 -0
  77. package/dist/routes/shared-world/plan.js +1 -1
  78. package/dist/routes/shared-world/result.js +22 -22
  79. package/dist/routes/shared-world/route.js +1 -1
  80. package/dist/routes/shared-world/types.d.ts +2 -0
  81. package/dist/routes/terminal/bundle.js +4 -2
  82. package/dist/routes/terminal/credentials.js +1 -1
  83. package/dist/routes/terminal/dry-run.js +8 -12
  84. package/dist/routes/terminal/ledger.d.ts +3 -3
  85. package/dist/routes/terminal/ledger.js +6 -6
  86. package/dist/routes/terminal/live-finish.js +23 -17
  87. package/dist/routes/terminal/plan.js +4 -4
  88. package/dist/routes/terminal/result.d.ts +6 -7
  89. package/dist/routes/terminal/result.js +6 -22
  90. package/dist/routes/terminal/session.js +1 -1
  91. package/dist/routes/terminal/types.d.ts +3 -0
  92. package/dist/run/bundle-shape.d.ts +3 -1
  93. package/dist/run/bundle-shape.js +26 -1
  94. package/dist/run/bundle.d.ts +30 -0
  95. package/dist/run/display.d.ts +59 -0
  96. package/dist/run/display.js +106 -0
  97. package/dist/run/dry-run.js +11 -7
  98. package/dist/run/judge.d.ts +3 -2
  99. package/dist/run/judge.js +10 -0
  100. package/dist/run/pricing.js +1 -1
  101. package/dist/run/projection.d.ts +0 -2
  102. package/dist/run/projection.js +0 -11
  103. package/dist/run/reclaim-outcomes.d.ts +89 -0
  104. package/dist/run/reclaim-outcomes.js +213 -0
  105. package/dist/run/reclaim.d.ts +18 -14
  106. package/dist/run/reclaim.js +179 -105
  107. package/dist/run/results.d.ts +11 -18
  108. package/dist/run/results.js +2 -2
  109. package/dist/run/run-index.d.ts +11 -1
  110. package/dist/run/run-index.js +11 -0
  111. package/dist/run/run.d.ts +34 -18
  112. package/dist/run/run.js +177 -72
  113. package/dist/run/sandbox-creates.d.ts +59 -0
  114. package/dist/run/sandbox-creates.js +138 -0
  115. package/dist/run/sandbox-ids.d.ts +53 -0
  116. package/dist/run/sandbox-ids.js +198 -0
  117. package/dist/run/sandbox-receipts.d.ts +16 -0
  118. package/dist/run/sandbox-receipts.js +70 -0
  119. package/dist/run/stats.d.ts +5 -0
  120. package/dist/run/stats.js +8 -4
  121. package/dist/run/status.d.ts +6 -9
  122. package/dist/run/status.js +7 -20
  123. package/dist/run/stored-runs.d.ts +5 -14
  124. package/dist/run/stored-runs.js +31 -137
  125. package/dist/run/streams.d.ts +3 -2
  126. package/dist/run-study.js +6 -1
  127. package/dist/study/doctor.d.ts +1 -1
  128. package/dist/study/doctor.js +7 -4
  129. package/dist/study/init-templates.js +2 -2
  130. package/dist/study/init.js +14 -3
  131. package/dist/study/keys.d.ts +12 -0
  132. package/dist/study/keys.js +32 -0
  133. package/dist/study/persona.d.ts +4 -0
  134. package/dist/study/persona.js +4 -2
  135. package/dist/study/types.d.ts +1 -1
  136. package/dist/substrates/e2b/peer-install.d.ts +51 -0
  137. package/dist/substrates/e2b/peer-install.js +161 -0
  138. package/dist/substrates/e2b/sandbox.d.ts +34 -6
  139. package/dist/substrates/e2b/sandbox.js +128 -35
  140. package/dist/substrates/e2b/sdk.d.ts +33 -0
  141. package/dist/substrates/e2b/sdk.js +21 -21
  142. package/dist/substrates/shell.d.ts +2 -0
  143. package/dist/substrates/shell.js +4 -0
  144. package/dist/tui-app.js +133 -133
  145. package/dist/verify/artifacts.d.ts +4 -0
  146. package/dist/verify/artifacts.js +8 -0
  147. package/dist/verify/liveness.d.ts +19 -3
  148. package/dist/verify/liveness.js +53 -28
  149. package/dist/verify/verify.d.ts +4 -1
  150. package/dist/verify/verify.js +27 -3
  151. package/docs/architecture/actor-contract.md +2 -1
  152. package/docs/architecture/observer.md +22 -7
  153. package/docs/architecture/restricted-codex-analysis.md +1 -1
  154. package/docs/architecture/task-protocol-support.md +13 -13
  155. package/docs/architecture/terminal-product-route.md +1 -1
  156. package/docs/contracts/run-bundle.md +90 -7
  157. package/docs/contracts/schemas.md +39 -22
  158. package/docs/contracts/study-analysis.md +45 -0
  159. package/docs/contracts/study-costs.md +8 -0
  160. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +3 -0
  161. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +9 -3
  162. package/docs/principles/invariants-and-defaults.md +11 -8
  163. package/package.json +5 -3
  164. package/skills/humanish/SKILL.md +18 -4
@@ -6,6 +6,7 @@ import { redactText } from "../../evidence/redaction.js";
6
6
  import { cliAnalysisOptions } from "./analysis-signals.js";
7
7
  import { parseObserverPort, wantsJson, writeResult, } from "../io.js";
8
8
  import { followObserver, showObserver, staticObserverOpen, withObserverServer, } from "../observer-follow.js";
9
+ import { writeRunFindings } from "../findings.js";
9
10
  import { formatConcurrentSharedWorldStudyHuman } from "./study-format.js";
10
11
  import { resolveRouteShouldOpen, watchFinishedPlan } from "./study-route-open.js";
11
12
  import { rosterOf } from "../../study/parse/actors.js";
@@ -118,6 +119,7 @@ export function sharedWorldRouteRun(args) {
118
119
  }
119
120
  writeResult(args.command, args.io, output, formatConcurrentSharedWorldStudyHuman);
120
121
  args.io.setExitCode(result.ok && automaticAnalysisSucceeded(result) ? 0 : 2);
122
+ await writeRunFindings(args.command, args.io, args.options.cwd, result);
121
123
  if (server && output.observer?.ok) {
122
124
  await followObserver(args.io, output.observer, server);
123
125
  }
@@ -2,6 +2,7 @@ import { automaticAnalysisSucceeded } from "../../analysis/automatic-completion.
2
2
  import { Command } from "commander";
3
3
  import { cliAnalysisOptions } from "./analysis-signals.js";
4
4
  import { wantsJson, writeResult } from "../io.js";
5
+ import { writeRunFindings } from "../findings.js";
5
6
  import { showObserver } from "../observer-follow.js";
6
7
  import { formatTerminalStudyHuman } from "./study-format.js";
7
8
  import { observerOpen, resolveRouteShouldOpen, watchFinishedPlan } from "./study-route-open.js";
@@ -35,6 +36,7 @@ export function terminalRouteRun(args) {
35
36
  const result = outcome.result;
36
37
  writeResult(args.command, args.io, result, formatTerminalStudyHuman);
37
38
  args.io.setExitCode(result.ok && automaticAnalysisSucceeded(result) ? 0 : 2);
39
+ await writeRunFindings(args.command, args.io, args.options.cwd, result);
38
40
  if (finishedPlan !== undefined && result.ok && result.observer !== undefined) {
39
41
  await showObserver({
40
42
  command: args.command,
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
3
3
  import { Command, Option } from "commander";
4
4
  import { inspectStudyManifest, listStudyManifests } from "../../study/discover.js";
5
5
  import { runStudyPreflight, } from "../../study/preflight.js";
6
- import { applyEnvFileOption, CWD_OPTION_DESCRIPTION, ENV_FILE_OPTION_DESCRIPTION, JSON_OPTION_DESCRIPTION, parsePositiveInteger, writeResult, humanError, } from "../io.js";
6
+ import { applyEnvFileOption, CWD_OPTION_DESCRIPTION, DOTENV_OPTION_DESCRIPTION, dotenvPathOf, envFileAliasOption, JSON_OPTION_DESCRIPTION, parsePositiveInteger, writeResult, humanError, } from "../io.js";
7
7
  export function registerStudyCommands(parent, io) {
8
8
  const study = parent
9
9
  .command("study")
@@ -36,7 +36,8 @@ function checkCommand(command, io) {
36
36
  .choices(["metadata", "public-preview", "sandbox-loopback", "prepared-host"])
37
37
  .default("metadata"))
38
38
  .option("--timeout-ms <ms>", "Target reachability timeout.", String(30_000))
39
- .option("--env-file <path>", ENV_FILE_OPTION_DESCRIPTION)
39
+ .option("--dotenv <path>", DOTENV_OPTION_DESCRIPTION)
40
+ .addOption(envFileAliasOption())
40
41
  .option("--json", JSON_OPTION_DESCRIPTION)
41
42
  .action((name, options, command) => handleStudyCheck(io, name, options, command));
42
43
  }
@@ -54,7 +55,7 @@ async function handleStudyCheck(io, name, options, command) {
54
55
  if (!(await applyEnvFileOption({
55
56
  command,
56
57
  cwd: options.cwd,
57
- envFile: options.envFile,
58
+ envFile: dotenvPathOf(options, command, io),
58
59
  io,
59
60
  }))) {
60
61
  return;
@@ -19,7 +19,7 @@ import { readRunDetail } from "../../run/detail.js";
19
19
  import { launchRun, readLaunchLogTail } from "../../tui/launch.js";
20
20
  import { TUI_MIN_NODE_MAJOR, nodeSupportsTui, TUI_BUNDLE_URL, } from "../../tui/contract.js";
21
21
  import { detectAgentSession } from "../../actors/agent-session.js";
22
- import { applyEnvFileOption, CLI_VERSION, CWD_OPTION_DESCRIPTION, ENV_FILE_OPTION_DESCRIPTION, JSON_OPTION_DESCRIPTION, markInvocationEnvelopeWritten, wantsJson, } from "../io.js";
22
+ import { applyEnvFileOption, CLI_VERSION, CWD_OPTION_DESCRIPTION, DOTENV_OPTION_DESCRIPTION, dotenvPathOf, envFileAliasOption, JSON_OPTION_DESCRIPTION, markInvocationEnvelopeWritten, wantsJson, } from "../io.js";
23
23
  export const defaultTuiRuntime = {
24
24
  checkComms: checkCommsConnection,
25
25
  promptSecret,
@@ -59,7 +59,8 @@ export function registerTuiCommand(parent, io, runtime = defaultTuiRuntime) {
59
59
  .description("Browse studies and runs in an interactive terminal UI. It refuses detected agent sessions and non-TTY input or output; agents use humanish study list --json, humanish study show <study> --json and humanish runs --json.")
60
60
  .summary("Browse studies and runs in a terminal UI for people.")
61
61
  .option("--cwd <path>", CWD_OPTION_DESCRIPTION, ".")
62
- .option("--env-file <path>", ENV_FILE_OPTION_DESCRIPTION)
62
+ .option("--dotenv <path>", DOTENV_OPTION_DESCRIPTION)
63
+ .addOption(envFileAliasOption())
63
64
  .option("--force", "Open it anyway in a session that looks like an agent's.")
64
65
  .option("--json", JSON_OPTION_DESCRIPTION)
65
66
  .action((options, command) => handleTui(io, runtime, options, command));
@@ -89,7 +90,7 @@ async function handleTui(io, runtime, options, command) {
89
90
  if (!(await applyEnvFileOption({
90
91
  command,
91
92
  cwd: options.cwd,
92
- envFile: options.envFile,
93
+ envFile: dotenvPathOf(options, command, io),
93
94
  io,
94
95
  env: runtime.env,
95
96
  onDiscovered: (names) => names.forEach((name) => discoveredKeys.add(name)),
@@ -4,7 +4,7 @@ import { WATCH_SAFE_NOT_APPLICABLE_MESSAGE } from "../../observer/exposure.js";
4
4
  import { runDryRun } from "../../run/dry-run.js";
5
5
  import { runStudyCommand } from "./study-run.js";
6
6
  import { addRunOptions, studyOnlyFlags } from "./run-command.js";
7
- import { applyEnvFileOption, collectRepeated, formatRunHuman, parseObserverPort, parsePositiveInteger, wantsJson, writeResult, } from "../io.js";
7
+ import { applyEnvFileOption, collectRepeated, dotenvPathOf, formatRunHuman, parseObserverPort, parsePositiveInteger, wantsJson, writeResult, } from "../io.js";
8
8
  import { followObserver, formatObserverHuman, watchExposeRequested } from "../observer-follow.js";
9
9
  export function registerWatchCommand(parent, io) {
10
10
  // A run's flags come from addRunOptions, the helper `run` uses, so the two cannot drift.
@@ -59,7 +59,7 @@ async function handleWatch(io, named, parsed, command) {
59
59
  if (!(await applyEnvFileOption({
60
60
  command,
61
61
  cwd: options.cwd,
62
- envFile: options.envFile,
62
+ envFile: dotenvPathOf(options, command, io),
63
63
  io,
64
64
  // runStudyCommand discovers keys for a live study; a preview run needs none.
65
65
  discoverKeys: false,
@@ -1,4 +1,5 @@
1
- import { type KeyResolutionDeps } from "../keys/key-resolution.js";
1
+ import { type KeyResolutionDeps, type DotenvLoad } from "../keys/key-resolution.js";
2
+ import { type HumanishInstall } from "../substrates/e2b/peer-install.js";
2
3
  import { type DetectLocalAgentsOptions } from "../actors/local-agent/cli.js";
3
4
  import { type StudySetupCheckArgs } from "../study/doctor.js";
4
5
  declare const DOCTOR_SCHEMA = "humanish.doctor-result.v1";
@@ -35,7 +36,9 @@ export interface DoctorResult {
35
36
  */
36
37
  export declare const DESKTOP_SDK_FLOOR = "2.3.2";
37
38
  /** The advisory `doctor` attaches to an installed desktop SDK older than the floor, else undefined. */
38
- export declare function desktopSdkAdvisory(version: string | undefined): string | undefined;
39
+ export declare function desktopSdkAdvisory(version: string | undefined, install?: () => HumanishInstall): string | undefined;
40
+ /** The doctor line for an absent desktop SDK, with the install command for where humanish runs from. */
41
+ export declare function missingDesktopSdkMessage(install: HumanishInstall): string;
39
42
  /** A row as a check writes it. Doctor derives `status`; an advisory row sets "note" itself. */
40
43
  export type DoctorCheckDraft = Omit<DoctorResult["checks"][number], "status"> & {
41
44
  status?: "note";
@@ -48,5 +51,7 @@ export declare function doctor(cwdInput: string, options?: {
48
51
  keyDeps?: KeyResolutionDeps;
49
52
  /** The hosted Codex participant's operator handshake; tests replace it. */
50
53
  codexParticipantReadiness?: StudySetupCheckArgs["codexParticipantReadiness"];
54
+ /** What `--dotenv` loaded, so a key row names the file that supplied it. */
55
+ dotenv?: DotenvLoad;
51
56
  }): Promise<DoctorResult>;
52
57
  export {};
@@ -1,9 +1,12 @@
1
- import { readFile } from "node:fs/promises";
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import { homedir } from "node:os";
2
3
  import { existsSync } from "node:fs";
3
4
  import path from "node:path";
4
5
  import { probeKeySources, } from "../keys/key-resolution.js";
6
+ import { desktopPeerAdvice, humanishInstall, } from "../substrates/e2b/peer-install.js";
5
7
  import { nodeSupportsTui, terminalSurfaceMessage, TUI_BUNDLE_URL } from "../tui/contract.js";
6
8
  import { detectLocalAgents, localAgentDoctorMessage, NO_LOCAL_AGENT_MESSAGE, } from "../actors/local-agent/cli.js";
9
+ import { leftoverClaudeTranscripts } from "../actors/local-agent/claude-participant.js";
7
10
  import { studySetupChecks, studiesByRequiredKey, } from "../study/doctor.js";
8
11
  import { prepareSelectedOutputDirectory, } from "../run/contained-output.js";
9
12
  import { implicitProjectDirectoryExists, readImplicitProjectFile, validateCwd, } from "../run/project.js";
@@ -16,7 +19,7 @@ const DOCTOR_SCHEMA = "humanish.doctor-result.v1";
16
19
  */
17
20
  export const DESKTOP_SDK_FLOOR = "2.3.2";
18
21
  /** The advisory `doctor` attaches to an installed desktop SDK older than the floor, else undefined. */
19
- export function desktopSdkAdvisory(version) {
22
+ export function desktopSdkAdvisory(version, install = humanishInstall) {
20
23
  if (version === undefined)
21
24
  return undefined;
22
25
  const parse = (value) => value
@@ -31,9 +34,14 @@ export function desktopSdkAdvisory(version) {
31
34
  (have[0] === floor[0] &&
32
35
  (have[1] < floor[1] || (have[1] === floor[1] && have[2] < floor[2])));
33
36
  return older
34
- ? `@e2b/desktop ${version} is older than ${DESKTOP_SDK_FLOOR}, the supported floor for background command cleanup and stdin handles (older releases could keep the CLI alive for minutes after its result). Update with \`npm i -D @e2b/desktop@latest\`.`
37
+ ? `@e2b/desktop ${version} is older than ${DESKTOP_SDK_FLOOR}, the supported floor for background command cleanup and stdin handles (older releases could keep the CLI alive for minutes after its result). To update it: ${desktopPeerAdvice(install(), "@e2b/desktop@latest").advice}`
35
38
  : undefined;
36
39
  }
40
+ /** The doctor line for an absent desktop SDK, with the install command for where humanish runs from. */
41
+ export function missingDesktopSdkMessage(install) {
42
+ const { where, advice } = desktopPeerAdvice(install);
43
+ return `optional peer @e2b/desktop is not installed${where}: dry runs work, but a live run with a desktop participant fails when it starts. ${advice}`;
44
+ }
37
45
  /** The version of the @e2b/desktop that `import("@e2b/desktop")` resolves to from here, if readable. */
38
46
  async function installedDesktopSdkVersion() {
39
47
  try {
@@ -65,7 +73,7 @@ export async function doctor(cwdInput, options = {}) {
65
73
  }
66
74
  const env = options.env ?? process.env;
67
75
  const agents = await detectLocalAgents({ ...options.localAgents, env });
68
- const { probes, receivingKey } = await probeDoctorKeys(cwd, env, options.study, options.keyDeps);
76
+ const { probes, receivingKey } = await probeDoctorKeys(cwd, env, options.study, options.keyDeps, options.dotenv);
69
77
  const keyPresent = (name) => probes.some((probe) => probe.name === name && probe.source !== null);
70
78
  const setup = options.study
71
79
  ? await studySetupChecks({
@@ -86,6 +94,7 @@ export async function doctor(cwdInput, options = {}) {
86
94
  await desktopSdkCheck(setup),
87
95
  terminalSurfaceCheck(),
88
96
  ...localAgentChecks(agents),
97
+ await claudeTranscriptCheck(env),
89
98
  ...keyChecks(probes, receivingKey, setup, keyUsers),
90
99
  ...(setup?.checks ?? [
91
100
  {
@@ -205,7 +214,7 @@ async function desktopSdkCheck(setup) {
205
214
  ? `optional peer @e2b/desktop ${version ?? "(version unread)"} is installed; provider access is not tested${advisory === undefined ? "" : `. ${advisory}`}`
206
215
  : setup?.desktop === false
207
216
  ? "optional peer @e2b/desktop is absent; not required by the selected route"
208
- : "optional peer @e2b/desktop is not installed: dry runs work, but a live run with a desktop participant fails when it starts. Install it with `npm i -D @e2b/desktop`.",
217
+ : missingDesktopSdkMessage(humanishInstall()),
209
218
  };
210
219
  }
211
220
  /**
@@ -237,7 +246,7 @@ function terminalSurfaceCheck() {
237
246
  };
238
247
  }
239
248
  /** Probes the live-run keys, plus the receiving-email key a real-comms study names. */
240
- async function probeDoctorKeys(cwd, env, study, keyDeps) {
249
+ async function probeDoctorKeys(cwd, env, study, keyDeps, dotenv) {
241
250
  const keyNames = new Set(["OPENAI_API_KEY", "E2B_API_KEY", "GH_TOKEN", "CODEX_API_KEY"]);
242
251
  let receivingKey = null;
243
252
  if (study) {
@@ -254,6 +263,7 @@ async function probeDoctorKeys(cwd, env, study, keyDeps) {
254
263
  cwd,
255
264
  env,
256
265
  ...(keyDeps === undefined ? {} : { deps: keyDeps }),
266
+ ...(dotenv === undefined ? {} : { dotenv }),
257
267
  });
258
268
  return { probes, receivingKey };
259
269
  }
@@ -273,6 +283,35 @@ function localAgentChecks(agents) {
273
283
  message: localAgentDoctorMessage(agent),
274
284
  }));
275
285
  }
286
+ /**
287
+ * Earlier releases let Claude Code save a transcript for every participant, with each screenshot it
288
+ * read, under its projects folder and outside `.humanish/`. Participants now run with
289
+ * `--no-session-persistence`; this row counts what earlier runs left and prints the command that
290
+ * removes it. doctor never deletes anything.
291
+ */
292
+ async function claudeTranscriptCheck(env) {
293
+ const found = await leftoverClaudeTranscripts(env, env.HOME ?? homedir(), (directory) => readdir(directory));
294
+ const name = "claude participant transcripts";
295
+ if ("unreadable" in found)
296
+ return {
297
+ name,
298
+ ok: true,
299
+ status: "note",
300
+ message: `could not list ${found.directory} (${found.unreadable}), so transcripts from earlier Claude Code participants were not counted. Check the folder for names ending in -humanish-claude-session-XXXXXX.`,
301
+ };
302
+ const count = found.names.length;
303
+ const folders = `${count} transcript folder${count === 1 ? "" : "s"} from earlier Claude Code participants under ${found.directory} hold every screenshot those runs read, unblurred.`;
304
+ return {
305
+ name,
306
+ ok: true,
307
+ ...(count === 0 ? {} : { status: "note" }),
308
+ message: count === 0
309
+ ? `no transcript from an earlier Claude Code participant under ${found.directory}`
310
+ : found.removeCommand === undefined
311
+ ? `${folders} Remove these folders there: ${found.names.join(", ")}`
312
+ : `${folders} Remove them with: ${found.removeCommand}`,
313
+ };
314
+ }
276
315
  /** "try-live", "try-live and local-browser", "a, b and c". */
277
316
  function studyList(studies) {
278
317
  return studies.length < 2
@@ -281,7 +320,7 @@ function studyList(studies) {
281
320
  }
282
321
  /**
283
322
  * Provider-key discovery: which source supplies each live-run key, through the same
284
- * chain a live command resolves (env/--env-file, project overlay, vendor stores, the
323
+ * chain a live command resolves (env/--dotenv, project overlay, vendor stores, the
285
324
  * humanish user store). Values never appear; sources and fill commands do.
286
325
  *
287
326
  * With --study, a key the selected route requires fails when missing. Without it, `keyUsers` maps
@@ -292,7 +331,7 @@ function keyChecks(probes, receivingKey, setup, keyUsers) {
292
331
  return probes.map((probe) => {
293
332
  const present = probe.source !== null;
294
333
  const hint = probe.name === receivingKey
295
- ? `provide ${probe.name} through process env or --env-file`
334
+ ? `provide ${probe.name} through process env or --dotenv`
296
335
  : probe.hint;
297
336
  if (!setup) {
298
337
  const users = keyUsers?.get(probe.name) ?? [];
@@ -0,0 +1,103 @@
1
+ import type { Command } from "commander";
2
+ import type { AutomaticAnalysisOutcome } from "../analysis/job.js";
3
+ import type { AnalysisArtifact, LoadedAnalysis } from "../analysis/types.js";
4
+ import { type CliIo } from "./io.js";
5
+ export declare const ANALYSIS_FINDINGS_SCHEMA = "humanish.analysis-findings.v1";
6
+ type Finding = NonNullable<AnalysisArtifact["result"]>["findings"][number];
7
+ /** `ready` and `stale` carry findings; every other state carries none and says why in `message`. */
8
+ type FindingsState = "ready" | "stale" | "running" | "none" | "skipped" | "failed" | "dry_run" | "unavailable";
9
+ interface FindingEvidence {
10
+ id: string;
11
+ streamId: string;
12
+ kind: string;
13
+ /** How the finding's observations used this item: visual, action, participant_statement, inference. */
14
+ bases: string[];
15
+ /** Index of the latest retained capture at this item, from 0; null before any capture. */
16
+ frame: number | null;
17
+ /** Time since the first retained capture, not a video offset. */
18
+ elapsedMs: number | null;
19
+ at: string | null;
20
+ /** The capture file relative to the project directory, or null for nonvisual evidence. */
21
+ capture: string | null;
22
+ }
23
+ interface FindingView {
24
+ id: string;
25
+ title: string;
26
+ summary: string;
27
+ impact: Finding["impact"];
28
+ confidence: Finding["confidence"];
29
+ recovery: Finding["recovery"];
30
+ affected: {
31
+ streamId: string;
32
+ label: string;
33
+ }[];
34
+ exposedCount: number;
35
+ evidence: FindingEvidence[];
36
+ nextStep: string;
37
+ /** The latest human review note on this finding version (`analyze correct`), or null. */
38
+ correction: {
39
+ status: "confirmed" | "dismissed" | "amended";
40
+ reason: string;
41
+ replacementClaim: string | null;
42
+ } | null;
43
+ }
44
+ export interface AnalysisFindings {
45
+ schema: typeof ANALYSIS_FINDINGS_SCHEMA;
46
+ runId: string;
47
+ state: FindingsState;
48
+ /** The stable code behind a state other than ready, when one is recorded. */
49
+ reason: string | null;
50
+ message: string;
51
+ /** The command that gets findings for this run, or null when none can. */
52
+ next: string | null;
53
+ analysisId: string | null;
54
+ status: AnalysisArtifact["status"] | null;
55
+ provider: AnalysisArtifact["provider"] | null;
56
+ model: string | null;
57
+ completedAt: string | null;
58
+ estimatedCostUsd: number | null;
59
+ /** The run directory relative to the project directory; null when the run was not found. */
60
+ runPath: string | null;
61
+ /** analysis.json relative to the project directory. */
62
+ path: string | null;
63
+ summary: string | null;
64
+ /** Highest priority first, as the analysis ranked them. */
65
+ findings: FindingView[];
66
+ limitations: string[];
67
+ warnings: string[];
68
+ }
69
+ interface FindingsSource {
70
+ runId: string;
71
+ /** The bundle's mode; undefined when run.json could not be read. */
72
+ mode: string | undefined;
73
+ /** The run directory relative to the project directory. */
74
+ runRoot: string;
75
+ loaded: LoadedAnalysis;
76
+ /** ` --cwd <dir>` when the commands this view names need it, or "". */
77
+ cwdFlag: string;
78
+ }
79
+ /** The findings view of a run's selected analysis, or why it has none. */
80
+ export declare function analysisFindings(source: FindingsSource): AnalysisFindings;
81
+ /**
82
+ * The run's bundle mode and its selected analysis: `analysisId`, or the version `analyze show`
83
+ * defaults to. Null when the run is missing or its storage is unsafe.
84
+ */
85
+ export declare function readRunAnalysis(cwd: string, run: string, analysisId?: string): Promise<FindingsSource | null>;
86
+ /** The view of a run that could not be found. */
87
+ export declare function missingRunFindings(cwd: string, run: string): AnalysisFindings;
88
+ /** The findings of a run's selected analysis, or why it has none. */
89
+ export declare function readRunFindings(cwd: string, run: string, analysisId?: string): Promise<AnalysisFindings>;
90
+ /** The full findings block: the analysis, every finding with its evidence, and its limitations. */
91
+ export declare function formatFindings(view: AnalysisFindings): string[];
92
+ /** At most `limit` findings, one line each, and the command that prints all of them. */
93
+ export declare function formatFindingsSummary(view: AnalysisFindings, fullCommand: string, limit?: number): string[];
94
+ /**
95
+ * The end of a live run's human output, after its `analysis:` line: the findings of the analysis
96
+ * the run just completed. Prints nothing for --json, or when the run's analysis did not complete,
97
+ * since that line already says why.
98
+ */
99
+ export declare function writeRunFindings(command: Command, io: CliIo, cwd: string, result: {
100
+ runId?: string;
101
+ automaticAnalysis?: AutomaticAnalysisOutcome;
102
+ }): Promise<void>;
103
+ export {};