import type { CreatedBy } from "../../../../../lib/graph-write/dist/index.js"; /** Task 1567 — the name-required invariant for every owner-creating seed path. * A name is a placeholder (and so must be refused, never persisted) when, after * trimming, it is empty or equal to the literal seed placeholder "Owner". A * real name that merely contains the word — "Owner Jones" — is not a * placeholder. This is the single predicate the sub-account seed, account_create * and admin-add all route through, and the same condition the standing * placeholder-owner audit detects on already-persisted nodes. */ export declare function isPlaceholderOwnerName(name: string): boolean; /** Task 1647 — the name-required invariant for the client business itself. A * business name is refused only when, after trimming, it is empty. Unlike the * owner name there is no literal placeholder to reject: the seed never wrote a * default business name (it left lb.name unset), so blank/whitespace is the * sole failure. This is the single predicate the sub-account seed and * account_create both route through for the business name. */ export declare function isBlankBusinessName(name: string): boolean; /** Task 1685 — the timezone-required invariant for account creation. A timezone * is valid iff it is a non-blank IANA zone the runtime recognises. Dependency- * free: `Intl.DateTimeFormat` throws `RangeError` on an unknown `timeZone`, so * a successful construction is the recognition test. This is the single * predicate account_create and the seed both route through, so the account * owner's `UserProfile.timezone` is the operator's real zone at birth rather * than the box's server default backfilled later (Task 1685). */ export declare function isValidTimeZone(tz: string): boolean; export interface AccountSummary { accountId: string; role: string; isHouse: boolean; } /** Enumerate the registry: every UUID dir with a parseable account.json, with * its role. An unparseable account.json is excluded (corruption discipline, * mirrors account-enumeration). */ export declare function listAccountsFromDir(accountsDir: string): AccountSummary[]; /** True iff `id` is the house account. Used to refuse delete/purge of the house. */ export declare function isHouseAccount(accountsDir: string, id: string): boolean; /** Returns the install's house account summary, or null if none is found * (should not happen on a correctly-provisioned install — callers treat * null as "no house configured yet" rather than throwing). */ export declare function findHouseAccount(accountsDir: string): AccountSummary | null; export interface AdminAddClientRefusal { /** stderr diagnostic line. */ logLine: string; /** operator-facing refusal message (prefixed with the tool TAG by the caller). */ message: string; } /** Task 1568 — admin-add mints a per-account operator seat (users.json PIN, * account.json admins[], graph AdminUser/Person). Since Task 999 admin access is * install-wide: a house admin reaches every sub-account with no seat on it * (canAccessAdmin never consults admins[]). On a client sub-account a seat * therefore grants nothing and only fragments identity, so admin-add is refused * there. The house account is unaffected: it is where operators legitimately * hold PIN logins and admins[] rows. Returns null on the house (proceed). */ export declare function refuseClientAdminAdd(isHouse: boolean, accountId: string): AdminAddClientRefusal | null; /** Task 1311 — reads the install-wide WhatsApp/Telegram channel-routing * pointer off the house account's account.json. Returns null when absent * (default: inbound routes to house, unchanged from pre-1311 behavior) or * when no house account exists. Does not validate the pointer against the * live account set — resolvePlatformAccountId() does that validation at * read time, where it has the enumeration already in hand. */ export declare function getChannelRoutingAccountId(accountsDir: string): string | null; /** Task 1311 — repoints the install-wide WhatsApp/Telegram channel-routing * pointer to `targetAccountId` by writing it onto the house account's * account.json. Throws if no house account exists (should not happen on a * correctly-provisioned install) or if the house account.json cannot be * read/parsed. Preserves every other field on the house account.json. */ export declare function setChannelRoutingAccountId(accountsDir: string, targetAccountId: string): void; /** Task 1500 — the house account's admin userIds, the set an operator may * choose from when naming a new client sub-account's managing admin. Reads the * house account.json `admins[]` (each entry keyed on `userId`). Returns [] when * no house account exists or its `admins[]` is empty/absent. Client `admins[]` * are never house admins, so a client-only registry yields []. */ export declare function readHouseAdminUserIds(accountsDir: string): string[]; /** Task 1500 — record the managing house admin on a client sub-account by * writing `managingAdminUserId` onto its account.json, preserving every other * field. Throws if the sub-account.json cannot be read/parsed (the caller turns * it into a tool error). The caller validates `adminUserId` against * readHouseAdminUserIds first — this writer does not re-validate. */ export declare function setManagingAdmin(accountsDir: string, accountId: string, adminUserId: string): void; /** Task 1501 — every client sub-account whose `managingAdminUserId` equals * `adminUserId`, sorted. Only role:"client" accounts count: a managing admin is * always a house admin, so the house account is never a managed sub-account even * if its account.json happens to carry the field. Filesystem-only; a dir with no * account.json, or an unparseable one, is excluded (corruption discipline). * * Enumeration decides membership solely by a parseable account.json — no UUID_RE * prefilter — so the guard sees every account that carries the pointer. Drives * the admin-remove reconciliation guard (Task 1501): removing an admin this * returns non-empty for would orphan those accounts, so the removal is refused. * (Passive intake no longer depends on this pointer being present — an absent or * dangling managing admin falls back to the primary admin, Task 1630 — but the * removal guard still runs so a reassign stays the operator's explicit choice.) */ export declare function findClientAccountsManagedBy(accountsDir: string, adminUserId: string): string[]; export type ReassignResult = { ok: true; previousAdminUserId: string; } | { ok: false; reason: "no-account" | "not-client" | "unknown-admin"; }; /** Task 1501 — repoint a client sub-account's managing admin to another live * house admin. Refuses when the account is not registered (`no-account`), is not * a client sub-account (`not-client` — the house has no managing admin), or when * `adminUserId` is not a current house admin (`unknown-admin`). On success writes * the new pointer (preserving every other field) and returns the previous * `managingAdminUserId` (`""` when the account had none — the absent-pointer * repair path). Reassigning to the same admin is allowed and idempotent. The * caller (account_reassign_admin) turns the result into a tool response and the * op=reassign log line; this function is pure of logging so its branches are * unit-testable. */ export declare function reassignManagingAdmin(accountsDir: string, accountId: string, adminUserId: string): ReassignResult; /** Move a live account dir into `data/accounts/.trash/-/`. Graph rows are * retained (purge is a separate, typed-confirmation step). Returns the trash * path and whether the live dir is now absent (the verified post-condition). */ export declare function archiveAccountDir(accountsDir: string, id: string, ts: string): { trashPath: string; dirAbsent: boolean; }; /** Scaffold a new client account dir by invoking the shared * `provision_account_dir` shell function (the same path setup-account.sh uses * for the house). Synchronous — throws on non-zero exit, which the caller turns * into a tool error. */ export declare function provisionClientAccount(opts: { platformRoot: string; accountsDir: string; accountId: string; usersFile: string; }): string; /** Minimal Neo4j session surface the root seed needs — the admin plugin's * `getSession()` satisfies it; tests pass an in-memory fake. */ export interface SeedSessionLike { run(query: string, params?: Record): Promise<{ records: Array<{ get(key: string): unknown; }>; }>; } /** The provenance the root seed represents — matches the properties the cypher * below stamps (`createdByAgent='system'`, `createdBySource='installer'`), the * same shape `seed-neo4j.sh` writes for the house root. `agent:"system"` is the * convention `checkLabelOriginGate` exempts. The seed runs as raw cypher through * the admin session (not the memory-write chokepoint), so the gate never * actually evaluates it in production; this constant is not the write's * provenance carrier — it exists so a unit test can assert the gate *would* * permit this origin. */ export declare const SEED_ORIGIN: CreatedBy; /** Task 1359 — seed a client account's graph root: a bare :LocalBusiness{accountId} * plus its owner :AdminUser (fresh per-account userId) joined by :ADMIN_OF, in * one statement, then read both node existences back. Without this a created * client account is rootless: `checkGraphWriteGate` refuses every non-root * write and `checkLabelOriginGate` refuses the agent's attempt to create the * root, so the graph is unpopulatable. * * Raw cypher through the admin session bypasses the memory-write gates by * construction (they guard only the memory-write chokepoint), mirroring * `seed-neo4j.sh`. The owner AdminUser carries a fresh `ownerUserId` because * AdminUser is MERGE-keyed on `userId` and COALESCEs `accountId`; reusing the * install owner's userId would keep `accountId=house` and leave the gate's * `MATCH (au:AdminUser {accountId: })` empty. Single-shot per account: * MERGE on `LocalBusiness.accountId` (a UNIQUE key) makes a replay against the * same accountId safe, but it is NOT replay-idempotent under a different * `ownerUserId` (that would MERGE a second owner AdminUser); the sole caller * mints a new accountId per create and never replays. Returns the two * read-back booleans; the caller treats `businessSeeded && adminSeeded` as the * verified post-condition. */ export declare function seedAccountGraphRoot(args: { session: SeedSessionLike; accountId: string; ownerUserId: string; ownerName: string; businessName: string; timezone: string; createdAt: string; }): Promise<{ businessSeeded: boolean; adminSeeded: boolean; profileSeeded: boolean; }>; /** Cypher for the account-scoped node count and the irreversible purge. The * purge deletes every node carrying the accountId (account scope is a property, * never an edge — see CLAUDE.md), detaching its relationships. */ export declare const ACCOUNT_NODE_COUNT_CYPHER = "MATCH (n) WHERE n.accountId = $id RETURN count(n) AS c"; export declare const ACCOUNT_PURGE_CYPHER = "MATCH (n) WHERE n.accountId = $id DETACH DELETE n"; /** Task 1623 — the managing-admin status for a client sub-account. `absent` when * no managingAdminUserId is set; `dangling` when set but not held by a live * house admin; `assigned` otherwise. Mirrors reconcileAccountAdminCensus's * classification so the roster tool and the standing census never disagree. */ export declare function classifyManagingAdmin(managingAdminUserId: string | undefined, liveHouseAdminUserIds: string[]): "assigned" | "absent" | "dangling"; /** Task 1623 — a client owner is `named` iff its owned Person carries a * non-empty givenName (the Task 1570 name source); otherwise `nameless`. */ export declare function ownerNameStatus(givenName: string | null | undefined): "named" | "nameless"; /** Task 1623 — read a sub-account's managingAdminUserId off its account.json. * Returns undefined when absent/empty/unparseable (corruption discipline). */ export declare function readManagingAdminUserId(accountsDir: string, accountId: string): string | undefined; /** Task 1623 — name a client sub-account's business owner. The name lives on the * owner's owned Person (Task 1570 name source), NOT AdminUser.name. Refuses an * empty/placeholder name (isPlaceholderOwnerName) before any write. Renames the * owner's EXISTING owned Person if it has one (the nameless-owner repair case); * otherwise creates one and binds it via OWNS. Does not route through * writeAdminUserAndPerson: that function name-matches to reuse/create a Person, * and against an already-nameless owned Person it would add a SECOND Person * rather than rename the existing one. Sets embedding=null so the standing * reindex re-embeds the renamed Person (raw cypher carries no embedding). */ export declare function setOwnerName(args: { session: SeedSessionLike; accountId: string; name: string; }): Promise<{ personRenamed: boolean; personCreated: boolean; givenName: string; familyName: string | null; }>; //# sourceMappingURL=account-lifecycle.d.ts.map