import { type ValidatorEnv } from "../lib/schema-validator.js"; export declare function ownerPersonEmbedText(givenName: string | null | undefined, familyName: string | null | undefined): string | null; interface ProfileUpdateParams { accountId: string; userId: string; category: string; key: string; value: string; source?: "explicit" | "behavioural"; mode?: "reinforce" | "update" | "contradict" | "merge"; sessionId?: string; profileFields?: Record; /** * first-class "this field doesn't apply to me" signal. * * When `true`, writes a Preference with `notApplicable: true` and * `confidence: 1.0`. The field is then counted as covered in the * sparse-state observability log (computeAbsentFields) so the agent * stops re-eliciting it, and the row is filtered from the user-facing * prose body produced by `formatProfileSummary` (no value to render). * * Validation: * - Mutually exclusive with non-empty `value` (rejects loudly — a * declination has no value to record). * - Rejects `mode: "merge"` and `mode: "contradict"` — declination * is immutable once set; the agent must call `profile-delete` if * the user reverses themselves later. * - Forces `confidence: 1.0` regardless of caller-supplied value, * and bypasses the reinforcement-curve update on existing rows. */ notApplicable?: boolean; /** * open Person properties for personal-profile elicitation. * The agent decides which Person properties best help it serve the * operator (identity, contact, context); the schema validator (synonym * pass + forbidden-property pass) is the central enforcement. * No allow-list, no enumeration. Writes route to the operator's * personal-profile Person via `(au:AdminUser)-[:OWNS]->(p:Person)`. * * Validation runs in `mode: "update"`: synonym + forbidden * checks fire (so `phone` rejects with "use telephone", `name` rejects * with the Forbidden Properties rationale), but the required-property * loop is skipped (the personal-profile Person was created with * givenName/familyName at PIN setup; partial SET should not re-validate * those). */ personFields?: Record; /** For mode: "merge" — preferenceIds of sources to merge into this preference */ mergeSourceIds?: string[]; /** * Schema validator environment — passed in by the MCP server (built once * at startup, see `index.ts`). Required when `personFields` is non-empty * so the central validator can enforce synonym + forbidden-property * rules on operator-identity writes. */ validator?: ValidatorEnv; /** * Task 1461 — cross-account owner write. When true, MERGE the owner's * `UserProfile` off the (seeded) owner `AdminUser` before any preference * write, so a preference-only write on a sub-account seeded with an * `AdminUser` but no `UserProfile` (Task 1359) lands instead of silently * no-oping on the `MATCH (up)` create path. Set ONLY by the MCP server's * cross-account path (resolved account ≠ boot account); own-account writes * never set it — their `UserProfile` is provisioned at PIN setup, so their * behaviour is byte-identical. */ bootstrapProfile?: boolean; /** * Task 1463 — cross-account owner `personFields` write. When true, the * `personFields` SET MERGEs the owner `Person` off the (seeded) owner * `AdminUser` via the `OWNS` edge before setting the fields, so a house * operator setting a sub-account owner's email/telephone lands instead of * loud-failing on the absent `OWNS`-`Person` (a sub-account is seeded with an * `AdminUser` but no owner `Person` — Task 1359; it never runs the PIN / * access-grant flow that would create one). The owner `Person` is keyed by * the `OWNS` edge + `accountId`, matching `writeAdminUserAndPerson`'s * identity `Person`; it is NEVER keyed on phone (that is `enrolPerson`'s * access-grant `Person`), so the bootstrap cannot fork a parallel identity. * Set ONLY by the MCP server's cross-account path (resolved account ≠ boot * account); own-account writes never set it — their `OWNS`-`Person` is a real * post-PIN invariant, so a missing one still loud-fails (byte-identical). */ bootstrapPerson?: boolean; } interface ProfileUpdateResult { preferenceId: string; action: "created" | "reinforced" | "updated" | "contradicted" | "merged"; confidence: number; mergedCount?: number; /** * Task 1486 — how the existing-preference lookup resolved. `exact-key`: the * classic `(category, key)` match (byte-identical old path). `semantic`: no * exact key, but a same-subject neighbour above `SAME_SUBJECT_COSINE` was * found over `preference_embedding` and the write landed on it. `none`: no * match, a fresh node was created. Absent on merge. */ reconciledVia?: "exact-key" | "semantic" | "none"; /** * Task 1486 — the nearest same-subject neighbour(s) the write was judged * against (top matches from the vector lookup, similarity-desc). Surfaced so * the agent can classify future writes (supersede / refine / corroborate) * against real prior state. Present only on the `semantic` path. */ matchedNeighbours?: Array<{ preferenceId: string; category: string; key: string; value: string; similarity: number; }>; } export declare function profileUpdate(params: ProfileUpdateParams): Promise; export {}; //# sourceMappingURL=profile-update.d.ts.map