humanish 0.104.2 → 0.105.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 (44) hide show
  1. package/README.md +29 -6
  2. package/dist/actor-contract.d.ts +8 -0
  3. package/dist/actor-contract.js.map +1 -1
  4. package/dist/comms-catch-host.d.ts +2 -0
  5. package/dist/comms-catch-host.js +4 -2
  6. package/dist/comms-catch-host.js.map +1 -1
  7. package/dist/comms-sandbox-catch.d.ts +1 -1
  8. package/dist/comms-sandbox-catch.js +10 -8
  9. package/dist/comms-sandbox-catch.js.map +1 -1
  10. package/dist/computer-use.js +3 -1
  11. package/dist/computer-use.js.map +1 -1
  12. package/dist/concurrent-shared-world-lab.js +2 -0
  13. package/dist/concurrent-shared-world-lab.js.map +1 -1
  14. package/dist/cua-actor-lab.js +7 -9
  15. package/dist/cua-actor-lab.js.map +1 -1
  16. package/dist/e2b-terminal-lab.d.ts +1 -13
  17. package/dist/e2b-terminal-lab.js +8 -49
  18. package/dist/e2b-terminal-lab.js.map +1 -1
  19. package/dist/init-templates.js +0 -1
  20. package/dist/init-templates.js.map +1 -1
  21. package/dist/labs.d.ts +7 -0
  22. package/dist/labs.js +17 -1
  23. package/dist/labs.js.map +1 -1
  24. package/dist/observer-app.html +4 -4
  25. package/dist/persona-resolve.d.ts +2 -2
  26. package/dist/persona-resolve.js +15 -25
  27. package/dist/persona-resolve.js.map +1 -1
  28. package/dist/persona.d.ts +17 -13
  29. package/dist/persona.js +118 -115
  30. package/dist/persona.js.map +1 -1
  31. package/dist/program.js +17 -0
  32. package/dist/program.js.map +1 -1
  33. package/dist/run.js +4 -2
  34. package/dist/run.js.map +1 -1
  35. package/dist/shared-world-lab.js +2 -0
  36. package/dist/shared-world-lab.js.map +1 -1
  37. package/docs/contracts/schemas.md +32 -1
  38. package/docs/goals/current.md +10 -4
  39. package/docs/principles/actor-fidelity.md +12 -21
  40. package/docs/ramp/README.md +9 -1
  41. package/docs/release/0.104.3-local-smtp-catch.md +15 -0
  42. package/docs/release/0.105.0-participant-backgrounds.md +50 -0
  43. package/package.json +1 -1
  44. package/skills/humanish/SKILL.md +40 -23
@@ -5,8 +5,8 @@ export declare const PERSONA_ID_PATTERN: RegExp;
5
5
  /** Title-case an id for the fallback display name (`skeptical-power-user` -> `Skeptical Power User`). */
6
6
  export declare function personaTitleFromId(personaId: string): string;
7
7
  /**
8
- * Resolve ONE committed persona. Returns `null` (never a throw, never a guess) when the id is
9
- * unsafe or no file exists, so a lane always runs.
8
+ * Resolve ONE committed persona. Returns `null` with a warning when the id is
9
+ * unsafe or no file exists. Invalid rich backgrounds reject the study before execution.
10
10
  */
11
11
  export declare function resolveCommittedPersona(projectRoot: PreparedSelectedOutputDirectory, personaId: string): Promise<{
12
12
  persona: ResolvedPersona | null;
@@ -1,24 +1,9 @@
1
- // Resolve committed persona files into compiled personas, for EVERY lane (#381).
2
- //
3
- // #308 gave the terminal lane this: read `humanish/personas/<id>.yaml`, compile the traits into
4
- // behavioral directives, and record truthfully which traits were applied. The computer-use lanes
5
- // never got it — they composed a bare `Persona: <id>.` line and hardcoded `traitsApplied: []`, so
6
- // on browser routes the persona axis was a label rather than a behavior. A live two-lane contrast
7
- // (an impatient expert vs a patient newcomer, same app, same mission) came back with near-identical
8
- // action profiles, which read like evidence that personas do not matter and was actually evidence
9
- // that personas were never applied.
10
- //
11
- // This module is the shared implementation so the two routes cannot drift again: one containment
12
- // rule, one compiler, one fallback polarity.
13
- //
14
- // FAIL-SAFE, not fail-closed — deliberately the opposite polarity to the scorer loader. A persona
15
- // that declared nothing must never be given fabricated traits, so an unsafe id, a missing file, or
16
- // unparseable YAML falls back to the bare id line with a truthful EMPTY traitsApplied (warning only
17
- // when the file existed but could not be read). Silence here is honest; invention would not be.
1
+ // Shared contained persona resolution for browser and terminal participants.
18
2
  import { parse as parseYaml } from "yaml";
19
3
  import path from "node:path";
20
- import { parseResolvedPersona } from "./persona.js";
4
+ import { parseResolvedPersona, PersonaConfigError } from "./persona.js";
21
5
  import { prepareSelectedOutputDirectory, readContainedRegularFile } from "./selected-output-paths.js";
6
+ import { digestText, redactText } from "./redaction.js";
22
7
  import { realpath } from "node:fs/promises";
23
8
  /** Persona ids are file-name segments, never paths: the same grammar the terminal lane enforces. */
24
9
  export const PERSONA_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
@@ -31,12 +16,12 @@ export function personaTitleFromId(personaId) {
31
16
  .join(" ");
32
17
  }
33
18
  /**
34
- * Resolve ONE committed persona. Returns `null` (never a throw, never a guess) when the id is
35
- * unsafe or no file exists, so a lane always runs.
19
+ * Resolve ONE committed persona. Returns `null` with a warning when the id is
20
+ * unsafe or no file exists. Invalid rich backgrounds reject the study before execution.
36
21
  */
37
22
  export async function resolveCommittedPersona(projectRoot, personaId) {
38
23
  if (!PERSONA_ID_PATTERN.test(personaId)) {
39
- return { persona: null, warnings: [] };
24
+ return { persona: null, warnings: ["Persona id is not a safe filename; using the id only (no persona context)."] };
40
25
  }
41
26
  for (const candidate of [
42
27
  path.posix.join("humanish", "personas", `${personaId}.yaml`),
@@ -55,9 +40,12 @@ export async function resolveCommittedPersona(projectRoot, personaId) {
55
40
  warnings: [`${candidate} could not be parsed as YAML; the lane ran with the persona id only (no traits applied).`]
56
41
  };
57
42
  }
58
- return { persona: parseResolvedPersona(raw, { id: personaId, name: personaTitleFromId(personaId) }), warnings: [] };
43
+ const warnings = [];
44
+ const persona = parseResolvedPersona(raw, { id: personaId, name: personaTitleFromId(personaId) }, warnings);
45
+ persona.sourceDigest = digestText(bytes.toString("utf8"));
46
+ return { persona, warnings: warnings.map(warning => `${candidate}: ${warning}`) };
59
47
  }
60
- return { persona: null, warnings: [] };
48
+ return { persona: null, warnings: [`Persona ${redactText(personaId)} has no readable file under humanish/personas; using the id only (no persona context).`] };
61
49
  }
62
50
  /**
63
51
  * Resolve every distinct persona id a run will use, once, before lane specs are built. Returning a
@@ -102,8 +90,10 @@ export async function resolveCommittedPersonasForCwd(cwd, personaIds) {
102
90
  const projectRoot = await prepareSelectedOutputDirectory(path.dirname(physical), physical);
103
91
  return await resolveCommittedPersonas(projectRoot, personaIds);
104
92
  }
105
- catch {
106
- return { personas: new Map(), warnings: [] };
93
+ catch (error) {
94
+ if (error instanceof PersonaConfigError)
95
+ throw error;
96
+ return { personas: new Map(), warnings: ["Persona directory could not be read; no persona context was loaded."] };
107
97
  }
108
98
  }
109
99
  //# sourceMappingURL=persona-resolve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"persona-resolve.js","sourceRoot":"","sources":["../src/persona-resolve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"persona-resolve.js","sourceRoot":"","sources":["../src/persona-resolve.ts"],"names":[],"mappings":"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"}
package/dist/persona.d.ts CHANGED
@@ -1,11 +1,20 @@
1
+ import type { ActorPersonaRef } from "./actor-contract.js";
1
2
  export type PersonaLevel = "low" | "medium" | "high";
3
+ export declare const PERSONA_BACKGROUND_MAX_BYTES: number;
4
+ export declare const PERSONA_COMPILER_VERSION = 2;
5
+ export declare class PersonaConfigError extends Error {
6
+ constructor(message: string);
7
+ }
2
8
  export interface ResolvedPersona {
3
9
  id: string;
4
10
  name: string;
5
11
  summary?: string;
12
+ background?: string;
13
+ /** Digest of the source file, when resolved from disk. */
14
+ sourceDigest?: string;
6
15
  traits: {
7
- patience: PersonaLevel;
8
- skill: PersonaLevel;
16
+ patience?: PersonaLevel;
17
+ skill?: PersonaLevel;
9
18
  accessibilityNeeds?: string;
10
19
  };
11
20
  constraints: string[];
@@ -15,21 +24,16 @@ export interface PersonaDirectives {
15
24
  skillBias: string;
16
25
  accessibilityBehavior?: string;
17
26
  constraints: string[];
27
+ /** Directives included in the prompt, not proof of behavioral adherence. */
18
28
  traitsApplied: string[];
19
29
  }
20
- /**
21
- * Parse a YAML-parsed persona object into a normalized ResolvedPersona. `fallback`
22
- * supplies id/name when the document omits them (or is not a record at all).
23
- */
30
+ /** Legacy short fields keep their bounds, but every discarded value is diagnosed. */
24
31
  export declare function parseResolvedPersona(raw: unknown, fallback: {
25
32
  id: string;
26
33
  name: string;
27
- }): ResolvedPersona;
34
+ }, warnings?: string[]): ResolvedPersona;
28
35
  export declare function personaToDirectives(persona: ResolvedPersona): PersonaDirectives;
29
- /**
30
- * Render the persona section injected into an actor prompt. Replaces the bare
31
- * `Persona: <name>.` line so the persona's friction tolerance, skill, and
32
- * accessibility behavior actually shape the run. Returns a single space-joined
33
- * string to match the existing prompt-builder style.
34
- */
35
36
  export declare function renderPersonaPromptSection(persona: ResolvedPersona): string;
37
+ /** Only the authored persona section; excludes task criteria, runtime grants and inbox URLs. */
38
+ export declare function personaBrief(persona: ResolvedPersona, scrub?: (text: string) => string): NonNullable<ActorPersonaRef["brief"]>;
39
+ export declare function scrubPersonaBrief(persona: ActorPersonaRef, scrub: (text: string) => string): ActorPersonaRef;
package/dist/persona.js CHANGED
@@ -1,141 +1,144 @@
1
- // Compiles a committed persona (humanish/personas/*.yaml) into actor-neutral
2
- // behavioral directives so persona traits actually drive how the actor uses the
3
- // product, instead of being inert metadata. See docs/architecture/actor-contract.md.
4
- //
5
- // Patience is modeled as FRICTION TOLERANCE, never a turn/tool-call budget: the
6
- // directive tells the actor when, in character, to stop and report the friction.
7
- const LEVELS = ["low", "medium", "high"];
8
- function normalizeLevel(value, fallback = "medium") {
9
- if (typeof value === "string") {
10
- const lowered = value.trim().toLowerCase();
11
- if (LEVELS.includes(lowered)) {
12
- return lowered;
13
- }
14
- }
15
- return fallback;
16
- }
17
- // Persona files are committed public-safe source, but they still flow into an
18
- // actor prompt, so replace control characters with spaces and cap length to
19
- // limit prompt-injection surface from a hand-authored persona.
20
- function stripControlChars(value) {
21
- let out = "";
22
- for (const ch of value) {
23
- const code = ch.codePointAt(0) ?? 0;
24
- out += code < 0x20 || code === 0x7f ? " " : ch;
25
- }
26
- return out;
27
- }
28
- function sanitizePersonaText(value, maxLength) {
29
- if (typeof value !== "string") {
30
- return undefined;
31
- }
32
- const cleaned = stripControlChars(value)
33
- // Neutralize the actor verdict/nonce markers so persona text injected into a
34
- // prompt cannot forge a verdict line an actor might echo back.
35
- .replace(/HUMANISH_ACTOR_(?:VERDICT|NONCE)/gi, "HUMANISH_ACTOR_[neutralized]")
36
- .replace(/\s+/g, " ")
37
- .trim();
38
- if (cleaned.length === 0) {
39
- return undefined;
40
- }
41
- return cleaned.length > maxLength ? `${cleaned.slice(0, maxLength - 1)}…` : cleaned;
1
+ import { digestText, redactText } from "./redaction.js";
2
+ export const PERSONA_BACKGROUND_MAX_BYTES = 32 * 1024;
3
+ export const PERSONA_COMPILER_VERSION = 2;
4
+ export class PersonaConfigError extends Error {
5
+ constructor(message) { super(message); this.name = "PersonaConfigError"; }
42
6
  }
7
+ const LEVELS = ["low", "medium", "high"];
8
+ const PERSONA_FIELDS = new Set(["schema", "id", "name", "summary", "background", "traits", "constraints"]);
9
+ const TRAIT_FIELDS = new Set(["patience", "technical_confidence", "accessibility_needs"]);
43
10
  function isRecord(value) {
44
11
  return typeof value === "object" && value !== null && !Array.isArray(value);
45
12
  }
46
- /**
47
- * Parse a YAML-parsed persona object into a normalized ResolvedPersona. `fallback`
48
- * supplies id/name when the document omits them (or is not a record at all).
49
- */
50
- export function parseResolvedPersona(raw, fallback) {
13
+ function cleanText(value) {
14
+ return value.replace(/\r\n?/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, " ")
15
+ .replace(/HUMANISH_ACTOR_(?:VERDICT|NONCE)/gi, "HUMANISH_ACTOR_[neutralized]").trim();
16
+ }
17
+ /** Legacy short fields keep their bounds, but every discarded value is diagnosed. */
18
+ export function parseResolvedPersona(raw, fallback, warnings = []) {
51
19
  const record = isRecord(raw) ? raw : {};
52
20
  const traits = isRecord(record.traits) ? record.traits : {};
53
- const id = sanitizePersonaText(record.id, 120) ?? fallback.id;
54
- const name = sanitizePersonaText(record.name, 120) ?? fallback.name;
55
- const summary = sanitizePersonaText(record.summary, 280);
56
- const accessibilityNeeds = sanitizePersonaText(traits.accessibility_needs, 80);
57
- const constraints = Array.isArray(record.constraints)
58
- ? record.constraints
59
- .map((entry) => sanitizePersonaText(entry, 160))
60
- .filter((entry) => entry !== undefined)
61
- .slice(0, 8)
62
- : [];
21
+ if (!isRecord(raw))
22
+ warnings.push("Persona must be a mapping; using the legacy defaults.");
23
+ for (const field of Object.keys(record)) {
24
+ if (!PERSONA_FIELDS.has(field))
25
+ warnings.push(`Unsupported persona field ${redactText(JSON.stringify(field))}; it is not sent to the participant. Use background for relevant context.`);
26
+ }
27
+ for (const field of Object.keys(traits)) {
28
+ if (!TRAIT_FIELDS.has(field))
29
+ warnings.push(`Unsupported persona trait ${redactText(JSON.stringify(field))}; it is not sent to the participant.`);
30
+ }
31
+ const short = (value, max, field) => {
32
+ if (value === undefined)
33
+ return undefined;
34
+ if (typeof value !== "string") {
35
+ warnings.push(`${field} must be text; ignored.`);
36
+ return undefined;
37
+ }
38
+ const cleaned = cleanText(value).replace(/\s+/g, " ");
39
+ if (!cleaned)
40
+ return undefined;
41
+ if (cleaned.length > max)
42
+ warnings.push(`${field} exceeds ${max} characters and was shortened; move detailed context to background.`);
43
+ return cleaned.length > max ? `${cleaned.slice(0, max - 1)}…` : cleaned;
44
+ };
45
+ let background;
46
+ if (record.background !== undefined) {
47
+ if (typeof record.background !== "string")
48
+ throw new PersonaConfigError("Persona background must be text.");
49
+ background = cleanText(record.background);
50
+ if (!background)
51
+ throw new PersonaConfigError("Persona background must not be empty.");
52
+ if (Buffer.byteLength(record.background, "utf8") > PERSONA_BACKGROUND_MAX_BYTES || Buffer.byteLength(background, "utf8") > PERSONA_BACKGROUND_MAX_BYTES) {
53
+ throw new PersonaConfigError(`Persona background exceeds ${PERSONA_BACKGROUND_MAX_BYTES} UTF-8 bytes; shorten it explicitly. No background was truncated.`);
54
+ }
55
+ }
56
+ const level = (field) => {
57
+ const value = traits[field];
58
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : undefined;
59
+ if (LEVELS.includes(normalized))
60
+ return normalized;
61
+ if (value !== undefined)
62
+ warnings.push(`traits.${field} must be low, medium or high; ${background ? "ignored" : "using medium"}.`);
63
+ return background ? undefined : "medium";
64
+ };
65
+ if (record.traits !== undefined && !isRecord(record.traits))
66
+ warnings.push("Persona traits must be a mapping; invalid traits ignored.");
67
+ if (background && Object.keys(traits).some(field => field === "patience" || field === "technical_confidence")) {
68
+ warnings.push("Background and explicit trait directives are both sent to the participant. Review the brief for conflicting instructions; prose conflicts are not automatically detected.");
69
+ }
70
+ let accessibilityNeeds = short(traits.accessibility_needs, 80, "traits.accessibility_needs");
71
+ if (accessibilityNeeds && /^(none[ _-]declared|none|not[ _-]applicable)$/i.test(accessibilityNeeds))
72
+ accessibilityNeeds = undefined;
73
+ if (record.constraints !== undefined && !Array.isArray(record.constraints))
74
+ warnings.push("Persona constraints must be a list; ignored.");
75
+ const allConstraints = Array.isArray(record.constraints) ? record.constraints : [];
76
+ if (allConstraints.length > 8)
77
+ warnings.push("Only the first eight persona constraints are used; move detailed context to background.");
78
+ const constraints = allConstraints.slice(0, 8).map(value => short(value, 160, "constraint")).filter((value) => value !== undefined);
79
+ const patience = level("patience"), skill = level("technical_confidence");
80
+ const summary = short(record.summary, 280, "summary");
63
81
  return {
64
- id,
65
- name,
82
+ id: short(record.id, 120, "id") ?? fallback.id,
83
+ name: short(record.name, 120, "name") ?? fallback.name,
66
84
  ...(summary === undefined ? {} : { summary }),
67
- traits: {
68
- patience: normalizeLevel(traits.patience),
69
- // Persona YAML uses `technical_confidence`; the directive model calls it skill.
70
- skill: normalizeLevel(traits.technical_confidence),
71
- ...(accessibilityNeeds === undefined ? {} : { accessibilityNeeds })
72
- },
85
+ ...(background === undefined ? {} : { background }),
86
+ traits: { ...(patience === undefined ? {} : { patience }), ...(skill === undefined ? {} : { skill }), ...(accessibilityNeeds === undefined ? {} : { accessibilityNeeds }) },
73
87
  constraints
74
88
  };
75
89
  }
76
90
  const FRICTION_TOLERANCE = {
77
- low: "You are impatient: if you hit repeated friction (errors, dead-ends, confusing UI) or stop making progress toward your goal, stop and report exactly what blocked you instead of pushing through.",
78
- medium: "You have moderate patience: work through some friction, but if you repeatedly fail to make progress toward your goal, stop and report what blocked you.",
79
- high: "You are determined: push through friction and try recovery paths, but if you reach an unrecoverable dead-end, stop and report it."
91
+ low: "You are impatient: if you hit repeated friction or stop making progress toward your goal, you are likely to stop. Describe what you actually encountered.",
92
+ medium: "You have moderate patience: you will work through some friction, but may stop if further effort no longer seems worthwhile.",
93
+ high: "You are determined: you are willing to spend effort on recovery when the goal matters to you, but may stop at an unrecoverable dead-end."
80
94
  };
81
- // Skill describes how COMPETENTLY an actor works the surfaces its population actually uses — it
82
- // must not name an affordance that escapes those surfaces. The `high` directive used to say
83
- // "inspect configuration, and try the recovery paths an expert would", which a computer-use actor
84
- // can honestly read as license to open devtools or type a `javascript:` URL: a human-persona run
85
- // then routes around the very friction the study exists to measure and still returns green. Skill
86
- // is now modality-neutral (an expert human uses advanced UI and settings; an expert agent uses the
87
- // documented API), so which affordances are in scope is a property of the declared population,
88
- // not a side effect of a trait table.
89
95
  const SKILL_BIAS = {
90
- low: "You are not technically confident: prefer obvious UI affordances over CLI flags or config, and narrate your confusion whenever the interface is ambiguous.",
91
- medium: "You have moderate technical confidence: use straightforward paths and try one recovery step before giving up.",
92
- high: "You are technically confident: use keyboard shortcuts, the product's own advanced options and settings, and the recovery paths an expert would try on the surfaces this product gives you."
96
+ low: "You are not technically confident and generally rely on familiar, visible controls. Describe confusion only when you actually encounter it.",
97
+ medium: "You have moderate technical confidence and generally use straightforward paths through the product.",
98
+ high: "You are technically confident and comfortable with keyboard shortcuts and advanced options on the surfaces this product gives you."
93
99
  };
94
100
  function accessibilityBehavior(needs) {
95
- const lowered = needs.toLowerCase();
96
- if (lowered.includes("keyboard")) {
97
- return "Navigate using the keyboard; treat a control that requires a mouse and offers no keyboard path as a defect and report it.";
98
- }
99
- if (lowered.includes("terminal") || lowered.includes("output")) {
100
- return "You rely on clear terminal output; flag noisy, unreadable, or ambiguous output as a defect.";
101
- }
102
- return `You have the accessibility need "${needs}"; treat violations of it as defects and report them.`;
101
+ if (/keyboard[ _-]first/i.test(needs))
102
+ return "You prefer keyboard navigation, but can use a pointer when needed. Describe any difficulty you encounter switching between them.";
103
+ if (/keyboard[ _-]only/i.test(needs))
104
+ return "You can only use the keyboard. If an essential control has no discoverable keyboard path, describe where you could not proceed.";
105
+ if (/terminal|output/i.test(needs))
106
+ return "You rely on clear terminal output. Describe any difficulty understanding the output you actually encounter.";
107
+ return `Your accessibility requirement is: ${needs}. Use the available interface within this requirement and describe any difficulty you encounter.`;
103
108
  }
104
109
  export function personaToDirectives(persona) {
105
- const traitsApplied = [`patience:${persona.traits.patience}`, `skill:${persona.traits.skill}`];
106
- const accessibility = persona.traits.accessibilityNeeds
107
- ? accessibilityBehavior(persona.traits.accessibilityNeeds)
108
- : undefined;
109
- if (persona.traits.accessibilityNeeds) {
110
- traitsApplied.push(`accessibility:${persona.traits.accessibilityNeeds}`);
111
- }
112
- if (persona.constraints.length > 0) {
110
+ const { patience, skill, accessibilityNeeds } = persona.traits;
111
+ const traitsApplied = [...(patience ? [`patience:${patience}`] : []), ...(skill ? [`skill:${skill}`] : [])];
112
+ if (accessibilityNeeds)
113
+ traitsApplied.push(`accessibility:${accessibilityNeeds}`);
114
+ if (persona.constraints.length)
113
115
  traitsApplied.push(`constraints:${persona.constraints.length}`);
114
- }
115
116
  return {
116
- frictionTolerance: FRICTION_TOLERANCE[persona.traits.patience],
117
- skillBias: SKILL_BIAS[persona.traits.skill],
118
- ...(accessibility === undefined ? {} : { accessibilityBehavior: accessibility }),
119
- constraints: persona.constraints,
120
- traitsApplied
117
+ frictionTolerance: patience ? FRICTION_TOLERANCE[patience] : "",
118
+ skillBias: skill ? SKILL_BIAS[skill] : "",
119
+ ...(accessibilityNeeds ? { accessibilityBehavior: accessibilityBehavior(accessibilityNeeds) } : {}),
120
+ constraints: persona.constraints, traitsApplied
121
121
  };
122
122
  }
123
- /**
124
- * Render the persona section injected into an actor prompt. Replaces the bare
125
- * `Persona: <name>.` line so the persona's friction tolerance, skill, and
126
- * accessibility behavior actually shape the run. Returns a single space-joined
127
- * string to match the existing prompt-builder style.
128
- */
129
123
  export function renderPersonaPromptSection(persona) {
130
124
  const directives = personaToDirectives(persona);
131
- const parts = [
132
- `Persona: ${persona.name}.`,
133
- ...(persona.summary ? [persona.summary] : []),
134
- directives.frictionTolerance,
135
- directives.skillBias,
136
- ...(directives.accessibilityBehavior ? [directives.accessibilityBehavior] : []),
137
- ...(directives.constraints.length > 0 ? [`Honor these constraints: ${directives.constraints.join("; ")}.`] : [])
138
- ];
139
- return parts.join(" ");
125
+ return [
126
+ `Persona: ${persona.name}.`, persona.summary,
127
+ persona.background ? `Background:\n${persona.background}` : undefined,
128
+ directives.frictionTolerance, directives.skillBias, directives.accessibilityBehavior,
129
+ persona.constraints.length ? `Honor these constraints: ${persona.constraints.join("; ")}.` : undefined
130
+ ].filter(Boolean).join("\n\n");
131
+ }
132
+ /** Only the authored persona section; excludes task criteria, runtime grants and inbox URLs. */
133
+ export function personaBrief(persona, scrub = text => text) {
134
+ const original = renderPersonaPromptSection(persona), text = redactText(scrub(original));
135
+ return { compilerVersion: PERSONA_COMPILER_VERSION, text, digest: digestText(original), redacted: text !== original,
136
+ ...(persona.sourceDigest ? { sourceDigest: persona.sourceDigest } : {}) };
137
+ }
138
+ export function scrubPersonaBrief(persona, scrub) {
139
+ if (!persona.brief)
140
+ return persona;
141
+ const text = redactText(scrub(persona.brief.text));
142
+ return { ...persona, brief: { ...persona.brief, text, redacted: persona.brief.redacted || text !== persona.brief.text } };
140
143
  }
141
144
  //# sourceMappingURL=persona.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"persona.js","sourceRoot":"","sources":["../src/persona.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"persona.js","sourceRoot":"","sources":["../src/persona.ts"],"names":[],"mappings":"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"}
package/dist/program.js CHANGED
@@ -1351,6 +1351,7 @@ function registerCommsCommands(parent, io) {
1351
1351
  .option("--port <port>", "Port for capture + inbox (default 8025).", String(DEFAULT_SANDBOX_CATCH_PORT))
1352
1352
  .option("--dir <path>", "Directory for the deliveries log and rendered inbox.", ".humanish/comms-catch")
1353
1353
  .option("--token <value>", "Require this bearer token on GET /deliveries (recommended when reachable off-host).")
1354
+ .option("--smtp-port <port>", "Also capture SMTP mail on 127.0.0.1:<port>. Point your app's SMTP transport here.")
1354
1355
  .option("--inbox-port <port>", "Also serve a READ-ONLY inbox listener on 0.0.0.0:<port>, so a persona on another machine can open /inbox. Without it the catch stays loopback-only.")
1355
1356
  .option("--recipient <address>", "Only render mail sent to this address (repeatable). Default: render whatever the app actually mailed.", (value, previous = []) => [...previous, value])
1356
1357
  .action(async (options) => {
@@ -1374,11 +1375,26 @@ function registerCommsCommands(parent, io) {
1374
1375
  return;
1375
1376
  }
1376
1377
  }
1378
+ let smtpPort;
1379
+ if (options.smtpPort !== undefined) {
1380
+ smtpPort = Number(options.smtpPort);
1381
+ if (!Number.isInteger(smtpPort) || smtpPort <= 0 || smtpPort > 65_534) {
1382
+ io.writeErr("--smtp-port must be an integer between 1 and 65534.\n");
1383
+ io.setExitCode(2);
1384
+ return;
1385
+ }
1386
+ if (smtpPort === port || smtpPort === inboxPort) {
1387
+ io.writeErr("--smtp-port must differ from --port and --inbox-port.\n");
1388
+ io.setExitCode(2);
1389
+ return;
1390
+ }
1391
+ }
1377
1392
  await runCommsCatchHost({
1378
1393
  port,
1379
1394
  dir: options.dir,
1380
1395
  ...(options.token ? { token: options.token } : {}),
1381
1396
  ...(inboxPort === undefined ? {} : { inboxPort }),
1397
+ ...(smtpPort === undefined ? {} : { smtpPort }),
1382
1398
  ...(options.recipient && options.recipient.length > 0 ? { recipients: options.recipient } : {})
1383
1399
  }, io);
1384
1400
  });
@@ -3680,6 +3696,7 @@ function formatLabInspectHuman(result) {
3680
3696
  ...(result.path ? [`path: ${result.path}`] : []),
3681
3697
  ...(result.origin ? [`origin: ${result.origin}`] : []),
3682
3698
  ...(config.subject.repos?.length ? [`repos: ${config.subject.repos.join(", ")}`] : []),
3699
+ ...(result.personas ?? []).map(persona => `persona ${persona.id}: ${persona.brief ? `authored context (before runtime additions)\n${persona.brief.text}` : "unresolved; id only"}`),
3683
3700
  ...result.warnings.map((warning) => `warning: ${warning}`)
3684
3701
  ].join("\n") + "\n";
3685
3702
  }