import { type Membership } from "../index.js"; import type { RunnerEvent, VaultClientSurface } from "./sync-runner.js"; interface IdentityClaims { sub?: string; email?: string; name?: string; given_name?: string; family_name?: string; /** * Entity-bound machine-principal claims (US-013, unblocks US-004). A * headless agent or Outpost authenticates with machine credentials; its * idToken carries the machine's own entity binding here. The `--personal` * target resolver reads agent claims to resolve the agent AS itself: when * `custom:entityType === "agent"`, the personal slot resolves to the * agent's OWN entity (`custom:entityUid`, `agt_*`) instead of the * person-only canonical pick. */ "custom:entityType"?: string; "custom:entityUid"?: string; } export interface RunnerTarget { uid: string; slug: string; name?: string; bucketName?: string; /** * Entity liveness as observed AT PLAN TIME, copied off the `entity.get` * result this plan was built from. Carried so a downstream consumer can make * a liveness decision without paying a second round trip. * * These deliberately do NOT gate SYNC. A suspended company still syncs — that * is user-visible behavior and not ours to change here. They exist so the * MANIFEST decision (see `manifest-reconcile.ts`) can be as strict as it was * when it re-fetched the entity itself. Absent fields mean "unknown", and * every consumer of them is required to fail closed. */ entityType?: string; entityStatus?: string; personalMode?: boolean; journalSlug?: string; } /** * Why a run cannot proceed to a fanout. * * - `no-memberships` — the caller resolved fine but belongs to no company. * Usually a joiner whose invite is still pending acceptance. * - `no-person-entity` — the caller is signed in but has no personal entity to * sync into, so even `--personal` has no target. */ export type SetupNeededReason = "no-memberships" | "no-person-entity"; export type MembershipResolution = { status: "setup-needed"; reason: SetupNeededReason; pendingInviteCount?: number; } | { status: "memberships"; memberships: RunnerMembership[]; }; /** A membership target carried into planning, including a safely unresolved slug. */ export type RunnerMembership = Pick & { /** A caller-namespace lookup proved this slug belongs to no accessible company. */ unresolvedSlug?: string; }; export declare function resolveMembershipsForRun(options: { personal: boolean; companies: boolean; company?: string; client: VaultClientSurface; claims: IdentityClaims | null; stderr: { write: (chunk: string) => boolean | void; }; /** Returns how many invites were still pending after the claim attempt. */ runClaimDance: (client: VaultClientSurface, claims: IdentityClaims, stderr: { write: (chunk: string) => boolean | void; }) => Promise; listMemberships: (client: VaultClientSurface) => Promise; }): Promise; /** * Company slugs the desktop app has paused via per-workspace Off toggles. * * `HQ_SYNC_SKIP_COMPANIES` is a comma-separated slug list (whitespace tolerated). * Empty / missing → no filter. Used by `--companies` fanout and the watch * scoped-drain so Sync Now / Auto-sync honor `workspaceSyncEnabled=false`. */ export declare function resolveSkipCompanies(envValue?: string | undefined): Set; export declare function buildFanoutPlan(options: { memberships: RunnerMembership[]; companies: boolean; personal: boolean; skipPersonal: boolean; client: VaultClientSurface; claims: IdentityClaims | null; resolveSkipPersonal: (flag: boolean) => boolean; /** Optional company-slug denylist (desktop per-workspace Off toggles). */ skipCompanies?: Set; /** Emits resolution outcomes through the runner's existing protocol seam. */ emit?: (event: RunnerEvent) => void; }): Promise<{ status: "setup-needed"; reason: SetupNeededReason; } | { status: "plan"; plan: RunnerTarget[]; deferredUnresolved: number; unresolvedCompanies?: number; skippedCompanies?: number; }>; export declare function emitFanoutPlan(emit: (event: RunnerEvent) => void, plan: RunnerTarget[]): void; export {}; //# sourceMappingURL=sync-runner-planning.d.ts.map