humanish 0.95.0 → 0.96.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.
Files changed (87) hide show
  1. package/README.md +4 -2
  2. package/dist/comms-agentmail.d.ts +25 -0
  3. package/dist/comms-agentmail.js +418 -0
  4. package/dist/comms-agentmail.js.map +1 -0
  5. package/dist/comms-connections.d.ts +4 -2
  6. package/dist/comms-connections.js +3 -3
  7. package/dist/comms-connections.js.map +1 -1
  8. package/dist/comms-lease-store.d.ts +90 -0
  9. package/dist/comms-lease-store.js +489 -0
  10. package/dist/comms-lease-store.js.map +1 -0
  11. package/dist/comms-receiving-evidence.d.ts +46 -0
  12. package/dist/comms-receiving-evidence.js +35 -0
  13. package/dist/comms-receiving-evidence.js.map +1 -0
  14. package/dist/comms-receiving-inbox.d.ts +16 -0
  15. package/dist/comms-receiving-inbox.js +410 -0
  16. package/dist/comms-receiving-inbox.js.map +1 -0
  17. package/dist/comms-receiving-runtime.d.ts +25 -0
  18. package/dist/comms-receiving-runtime.js +45 -0
  19. package/dist/comms-receiving-runtime.js.map +1 -0
  20. package/dist/comms-receiving-types.d.ts +98 -0
  21. package/dist/comms-receiving-types.js +2 -0
  22. package/dist/comms-receiving-types.js.map +1 -0
  23. package/dist/comms-receiving.d.ts +58 -0
  24. package/dist/comms-receiving.js +554 -0
  25. package/dist/comms-receiving.js.map +1 -0
  26. package/dist/comms-setup.d.ts +40 -0
  27. package/dist/comms-setup.js +121 -0
  28. package/dist/comms-setup.js.map +1 -0
  29. package/dist/concurrent-shared-world-lab.js +662 -624
  30. package/dist/concurrent-shared-world-lab.js.map +1 -1
  31. package/dist/cua-actor-lab.d.ts +4 -1
  32. package/dist/cua-actor-lab.js +98 -17
  33. package/dist/cua-actor-lab.js.map +1 -1
  34. package/dist/doctor-lab.js +11 -0
  35. package/dist/doctor-lab.js.map +1 -1
  36. package/dist/e2b-terminal-lab.js +3 -0
  37. package/dist/e2b-terminal-lab.js.map +1 -1
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/lab-config.d.ts +20 -3
  42. package/dist/lab-config.js +59 -6
  43. package/dist/lab-config.js.map +1 -1
  44. package/dist/lab-engine.js +8 -3
  45. package/dist/lab-engine.js.map +1 -1
  46. package/dist/lab-summary.d.ts +1 -0
  47. package/dist/lab-summary.js +8 -2
  48. package/dist/lab-summary.js.map +1 -1
  49. package/dist/observer-app.html +1 -1
  50. package/dist/observer-data.js +14 -1
  51. package/dist/observer-data.js.map +1 -1
  52. package/dist/oss-lab.d.ts +1 -1
  53. package/dist/oss-lab.js.map +1 -1
  54. package/dist/oss-meta-lab.d.ts +1 -1
  55. package/dist/oss-meta-lab.js.map +1 -1
  56. package/dist/program.d.ts +2 -0
  57. package/dist/program.js +100 -4
  58. package/dist/program.js.map +1 -1
  59. package/dist/run-narration-secrets.d.ts +5 -0
  60. package/dist/run-narration-secrets.js +68 -0
  61. package/dist/run-narration-secrets.js.map +1 -0
  62. package/dist/run.d.ts +7 -2
  63. package/dist/run.js +25 -3
  64. package/dist/run.js.map +1 -1
  65. package/dist/scripted-browser-lab.js +7 -0
  66. package/dist/scripted-browser-lab.js.map +1 -1
  67. package/dist/shared-world-lab.js +4 -0
  68. package/dist/shared-world-lab.js.map +1 -1
  69. package/dist/study-analysis-engine.js +30 -1
  70. package/dist/study-analysis-engine.js.map +1 -1
  71. package/dist/study-analysis-evidence.js +9 -1
  72. package/dist/study-analysis-evidence.js.map +1 -1
  73. package/dist/tui-app.js +134 -134
  74. package/dist/tui-contract.d.ts +13 -0
  75. package/dist/tui-contract.js.map +1 -1
  76. package/dist/tui-launch.d.ts +2 -0
  77. package/dist/tui-launch.js +9 -1
  78. package/dist/tui-launch.js.map +1 -1
  79. package/docs/architecture/comms-inbox.md +4 -0
  80. package/docs/architecture/real-email-receiving.md +135 -0
  81. package/docs/contracts/run-bundle.md +9 -1
  82. package/docs/contracts/schemas.md +20 -3
  83. package/docs/goals/current.md +5 -5
  84. package/docs/ramp/README.md +9 -5
  85. package/docs/release/0.96.0-real-email-receiving.md +47 -0
  86. package/package.json +2 -1
  87. package/skills/humanish/SKILL.md +33 -13
@@ -0,0 +1,5 @@
1
+ /** Enclose both participant execution and its automatic analysis in one scope. Nested runs isolate too. */
2
+ export declare function withTransientCommsSecrets<T>(work: () => Promise<T>): Promise<T>;
3
+ export declare function registerTransientCommsSecrets(values: string[]): void;
4
+ /** Literal, longest-first replacement in one pass; replacement text is never matched recursively. */
5
+ export declare function scrubTransientCommsText(text: string): string;
@@ -0,0 +1,68 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const scopes = new AsyncLocalStorage();
3
+ const MAX_VALUES = 8192;
4
+ const MAX_BYTES = 1024 * 1024;
5
+ const MAX_VALUE_BYTES = 65_536;
6
+ function usable(scope) {
7
+ if (scope.closed)
8
+ throw new Error("TRANSIENT_NARRATION_SCOPE_CLOSED");
9
+ if (scope.failed)
10
+ throw new Error("TRANSIENT_NARRATION_SECRET_LIMIT");
11
+ }
12
+ function fail(scope) {
13
+ scope.failed = true;
14
+ scope.values.clear();
15
+ delete scope.pattern;
16
+ throw new Error("TRANSIENT_NARRATION_SECRET_LIMIT");
17
+ }
18
+ /** Enclose both participant execution and its automatic analysis in one scope. Nested runs isolate too. */
19
+ export async function withTransientCommsSecrets(work) {
20
+ const scope = { values: new Set(), bytes: 0, closed: false, failed: false };
21
+ try {
22
+ return await scopes.run(scope, work);
23
+ }
24
+ finally {
25
+ // Detached work can retain an async context after return. Clear its values and refuse future
26
+ // scrubbing/registration there; never turn an expired scope into an unprotected analysis.
27
+ scope.closed = true;
28
+ scope.values.clear();
29
+ scope.bytes = 0;
30
+ delete scope.pattern;
31
+ }
32
+ }
33
+ export function registerTransientCommsSecrets(values) {
34
+ const scope = scopes.getStore();
35
+ if (!scope)
36
+ return;
37
+ usable(scope);
38
+ for (const value of values) {
39
+ // Match the existing narration registry: short subjects such as "Hi" are ordinary prose.
40
+ // Received OTP extraction starts at four characters; management keys and addresses are longer.
41
+ if (value.length < 4 || scope.values.has(value))
42
+ continue;
43
+ const bytes = Buffer.byteLength(value);
44
+ if (bytes > MAX_VALUE_BYTES || scope.values.size >= MAX_VALUES || scope.bytes + bytes > MAX_BYTES)
45
+ fail(scope);
46
+ scope.values.add(value);
47
+ scope.bytes += bytes;
48
+ delete scope.pattern;
49
+ }
50
+ }
51
+ /** Literal, longest-first replacement in one pass; replacement text is never matched recursively. */
52
+ export function scrubTransientCommsText(text) {
53
+ const scope = scopes.getStore();
54
+ if (!scope)
55
+ return text;
56
+ usable(scope);
57
+ if (!scope.values.size)
58
+ return text;
59
+ try {
60
+ scope.pattern ??= new RegExp([...scope.values].sort((a, b) => b.length - a.length)
61
+ .map(value => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g");
62
+ return text.replace(scope.pattern, "[REDACTED_SECRET]");
63
+ }
64
+ catch {
65
+ return fail(scope);
66
+ }
67
+ }
68
+ //# sourceMappingURL=run-narration-secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-narration-secrets.js","sourceRoot":"","sources":["../src/run-narration-secrets.ts"],"names":[],"mappings":"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"}
package/dist/run.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CommsReceivingEvidence } from "./comms-receiving-types.js";
1
2
  import { type CodexAppServerTrace } from "./codex-app-server.js";
2
3
  import { type ActorStatus, type ActorTrace, type ActorTraceItem } from "./actor-contract.js";
3
4
  import { type CuaGoalSource } from "./actor-goal-source.js";
@@ -715,6 +716,10 @@ export interface SharedWorldEvidence {
715
716
  outcomes?: SharedWorldOutcome[];
716
717
  }
717
718
  export interface RunBundle {
719
+ publication?: {
720
+ restrictions: ["real-communications"];
721
+ };
722
+ commsReceiving?: CommsReceivingEvidence;
718
723
  schema: typeof RUN_BUNDLE_SCHEMA;
719
724
  runId: string;
720
725
  mode: "dry-run" | "live";
@@ -1065,7 +1070,7 @@ export interface RunResult {
1065
1070
  latestPath?: string;
1066
1071
  warnings: string[];
1067
1072
  error?: {
1068
- code: "HUMANISH_LAB_ANALYSIS_INVALID" | "HUMANISH_LAB_ANALYSIS_UNSUPPORTED" | "HUMANISH_LAB_TASKS_UNSUPPORTED" | "HUMANISH_ACTOR_FANOUT_UNIMPLEMENTED" | "HUMANISH_APP_URL_OPTION_CONFLICT" | "HUMANISH_BROWSER_APP_CAPTURE_FAILED" | "HUMANISH_CODEX_APP_SERVER_FAILED" | "HUMANISH_LIVE_RUN_UNIMPLEMENTED" | "HUMANISH_LOCAL_CODEX_EXEC_FAILED" | "HUMANISH_LOCAL_CODEX_TUI_FAILED" | "HUMANISH_INVALID_APP_URL" | "HUMANISH_INVALID_ACTOR_CONCURRENCY" | "HUMANISH_INVALID_CWD" | "HUMANISH_INVALID_SIM_COUNT" | "HUMANISH_INVALID_TIMEOUT" | "HUMANISH_INVALID_PORT" | "HUMANISH_UNSUPPORTED_ACTOR" | "HUMANISH_UNSUPPORTED_RERUN_FLAGS" | "HUMANISH_WATCH_OPTION_CONFLICT" | "HUMANISH_LAB_SCORER_BAD_REF" | "HUMANISH_LAB_SCORER_NOT_FOUND" | "HUMANISH_LAB_SCORER_LOAD_FAILED" | "HUMANISH_LAB_SCORER_NO_HOOKS" | "HUMANISH_LAB_SCORER_UNSUPPORTED_BACKEND";
1073
+ code: "HUMANISH_LAB_ANALYSIS_INVALID" | "HUMANISH_LAB_ANALYSIS_UNSUPPORTED" | "HUMANISH_LAB_TASKS_UNSUPPORTED" | "HUMANISH_LAB_COMMS_UNSUPPORTED" | "HUMANISH_ACTOR_FANOUT_UNIMPLEMENTED" | "HUMANISH_APP_URL_OPTION_CONFLICT" | "HUMANISH_BROWSER_APP_CAPTURE_FAILED" | "HUMANISH_CODEX_APP_SERVER_FAILED" | "HUMANISH_LIVE_RUN_UNIMPLEMENTED" | "HUMANISH_LOCAL_CODEX_EXEC_FAILED" | "HUMANISH_LOCAL_CODEX_TUI_FAILED" | "HUMANISH_INVALID_APP_URL" | "HUMANISH_INVALID_ACTOR_CONCURRENCY" | "HUMANISH_INVALID_CWD" | "HUMANISH_INVALID_SIM_COUNT" | "HUMANISH_INVALID_TIMEOUT" | "HUMANISH_INVALID_PORT" | "HUMANISH_UNSUPPORTED_ACTOR" | "HUMANISH_UNSUPPORTED_RERUN_FLAGS" | "HUMANISH_WATCH_OPTION_CONFLICT" | "HUMANISH_LAB_SCORER_BAD_REF" | "HUMANISH_LAB_SCORER_NOT_FOUND" | "HUMANISH_LAB_SCORER_LOAD_FAILED" | "HUMANISH_LAB_SCORER_NO_HOOKS" | "HUMANISH_LAB_SCORER_UNSUPPORTED_BACKEND";
1069
1074
  message: string;
1070
1075
  };
1071
1076
  }
@@ -1085,7 +1090,7 @@ export interface VerifyResult {
1085
1090
  shareSafety: {
1086
1091
  status: "share_ready" | "local_only" | "blocked";
1087
1092
  reasons: Array<{
1088
- code: "VERIFY_FAILED" | "PUBLIC_SAFETY_FINDINGS" | "ANALYSIS_UNVERIFIED" | "RAW_SCREENSHOTS";
1093
+ code: "VERIFY_FAILED" | "PUBLIC_SAFETY_FINDINGS" | "ANALYSIS_UNVERIFIED" | "RAW_SCREENSHOTS" | "REAL_COMMUNICATIONS";
1089
1094
  message: string;
1090
1095
  }>;
1091
1096
  };
package/dist/run.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isCommsReceivingEvidence } from "./comms-receiving-evidence.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { spawn } from "node:child_process";
3
4
  import { lstat, readdir, readFile, realpath, stat } from "node:fs/promises";
@@ -3158,7 +3159,19 @@ export async function doctor(cwdInput, options = {}) {
3158
3159
  };
3159
3160
  const env = options.env ?? process.env;
3160
3161
  const agents = await detectLocalAgents({ ...options.localAgents, env });
3161
- const probes = await probeKeySources(["OPENAI_API_KEY", "E2B_API_KEY", "GH_TOKEN", "CODEX_API_KEY"], { cwd, env });
3162
+ const keyNames = new Set(["OPENAI_API_KEY", "E2B_API_KEY", "GH_TOKEN", "CODEX_API_KEY"]);
3163
+ let receivingKey = null;
3164
+ if (options.lab) {
3165
+ const { resolveLabManifest } = await import("./labs.js");
3166
+ const resolved = await resolveLabManifest(cwd, options.lab);
3167
+ if (resolved.ok && resolved.config.comms?.email?.kind === "real") {
3168
+ const { receivingRequiredKey } = await import("./comms-setup.js");
3169
+ receivingKey = await receivingRequiredKey(cwd, resolved.config.comms.email.connection);
3170
+ if (receivingKey)
3171
+ keyNames.add(receivingKey);
3172
+ }
3173
+ }
3174
+ const probes = await probeKeySources([...keyNames], { cwd, env });
3162
3175
  const setup = options.lab ? await labSetupChecks({ cwd, lab: options.lab, env, agents,
3163
3176
  keyPresent: name => probes.some(probe => probe.name === name && probe.source !== null) }) : undefined;
3164
3177
  const checks = [
@@ -3255,6 +3268,9 @@ export async function doctor(cwdInput, options = {}) {
3255
3268
  ...await (async () => {
3256
3269
  return probes.map((probe) => {
3257
3270
  const present = probe.source !== null;
3271
+ const hint = probe.name === receivingKey
3272
+ ? `provide ${probe.name} through process env or --env-file`
3273
+ : probe.hint;
3258
3274
  // GH_TOKEN is needed only for private clone subjects, so its absence is informational.
3259
3275
  const required = setup ? setup.keys.includes(probe.name) : probe.name === "E2B_API_KEY"
3260
3276
  || probe.name === "OPENAI_API_KEY" && !agents.some(agent => agent.authStatus === "authenticated");
@@ -3264,8 +3280,8 @@ export async function doctor(cwdInput, options = {}) {
3264
3280
  message: present
3265
3281
  ? `supplied by ${probe.source}; presence only, validity not tested`
3266
3282
  : required
3267
- ? `missing from every source — ${probe.hint}`
3268
- : `not required for ${setup ? "the selected participant route" : "every route"}; ${probe.hint}`
3283
+ ? `missing from every source — ${hint}`
3284
+ : `not required for ${setup ? "the selected participant route" : "every route"}; ${hint}`
3269
3285
  };
3270
3286
  });
3271
3287
  })(),
@@ -4170,6 +4186,9 @@ function buildShareSafety(args) {
4170
4186
  message: "Text artifacts or public-proof paths matched known secret, token, local-path, browser-profile, or hosted-substrate URL patterns."
4171
4187
  });
4172
4188
  }
4189
+ if (args.bundle.publication !== undefined || args.bundle.commsReceiving !== undefined) {
4190
+ reasons.push({ code: "REAL_COMMUNICATIONS", message: "This study used real email. Message content may appear in recordings, narration or analysis. Local review is supported; screenshot blurring does not make it public-safe." });
4191
+ }
4173
4192
  const rawStreamIds = rawScreenshotStreamIds(args.bundle);
4174
4193
  if (rawStreamIds.length > 0) {
4175
4194
  reasons.push({
@@ -5186,6 +5205,9 @@ function redactSensitiveText(text) {
5186
5205
  function isRunBundle(value) {
5187
5206
  return isRecord(value)
5188
5207
  && value.schema === RUN_BUNDLE_SCHEMA
5208
+ && (value.commsReceiving === undefined || isCommsReceivingEvidence(value.commsReceiving))
5209
+ && (value.publication === undefined || (isRecord(value.publication) && Array.isArray(value.publication.restrictions)
5210
+ && value.publication.restrictions.length === 1 && value.publication.restrictions[0] === "real-communications"))
5189
5211
  && typeof value.runId === "string"
5190
5212
  && (value.mode === "dry-run" || value.mode === "live")
5191
5213
  && isPositiveSafeInteger(value.simCount)