/** * Council manifest helpers: file-based multi-agent coordination primitives. * * Lives under .harnery/councils/ alongside heartbeats + journals. Council * lifecycle commands serialize manifest mutations through a shared flock; * round contribution files are per-member and don't need shared locking. */ export declare const COUNCIL_SCHEMA_VERSION: 2; export type CouncilStatus = "active" | "closed" | "archived"; export type CouncilRoundStatus = "open" | "collected"; export type CouncilRoundVisibility = "next_round" | "live"; export interface CouncilManifest { schema_version: typeof COUNCIL_SCHEMA_VERSION; council_id: string; created_at: string; /** Display name of the convener (denormalized, for human scan). * Canonical FK is `created_by_id`. */ created_by: string; /** Durable persona UUID of the convener (registry key). Authoritative. */ created_by_id: string; /** * Optional ongoing process-tender. Distinct from `created_by`, which is the * one-time act of creation; the steward is whoever drafts + maintains the * per-round prompts that route operator → contributor each round. Defaults * to `created_by` when omitted (set at create-time via --steward, or * retrofitted via direct manifest edit). Read by `agents council prompt` * to enforce write authority. * * Display name (denormalized). Canonical FK is `steward_id`. */ steward?: string; /** Durable persona UUID of the steward. */ steward_id?: string; objective: string; target_doc: string | null; /** Member display names (denormalized, parallel to `member_ids`). */ members: string[]; /** Canonical FK array of durable persona UUIDs of every member, parallel * to `members[]` (same length, same order). Used by contributors-in-round * lookups and contribution filenames (`round-N/.md`). */ member_ids: string[]; current_round: number; round_status: CouncilRoundStatus; status: CouncilStatus; auto_advance: boolean; round_visibility: CouncilRoundVisibility; closed_at?: string; archived_at?: string; } /** * Resolve the effective steward: explicit `steward` field if set, otherwise * fall back to `created_by`. Always returns a normalized `agent-Foo` name. */ export declare function effectiveSteward(manifest: CouncilManifest): string; /** Resolve `.harnery/councils/` (creates the dir lazily on first write). */ export declare function councilsDir(): string | null; /** Resolve `.harnery/councils/archive/`. */ export declare function councilsArchiveDir(): string | null; /** Manifest file path: `.harnery/councils/.json`. */ export declare function manifestPath(councilId: string): string | null; /** Council body dir: `.harnery/councils//` (holds invite.md + round-N/...). */ export declare function councilBodyDir(councilId: string): string | null; /** Normalize an `agent-Foo`/`Foo` reference to canonical `agent-Foo` form. */ export declare function normalizeAgentName(raw: string): string; /** Strip the `agent-` prefix for output to lookups that expect bare name. */ export declare function bareAgentName(raw: string): string; /** * Derive a kebab-case slug from objective text. Keeps the first 5 words after * lowercasing and stripping non-alphanumeric chars. */ export declare function deriveSlug(objective: string): string; /** * Build a council_id from objective + today's UTC date. * * Format: `--<4hex>`. The 4-hex suffix is sourced from a * crypto-strong random byte (not from a hash of the objective) to avoid * collisions when two councils share the same slug + date. */ export declare function buildCouncilId(objective: string, now?: Date): string; /** Hash an objective deterministically, used by tests for stable IDs. */ export declare function deterministicCouncilId(objective: string, now?: Date): string; /** Atomically write a manifest (write tmp → rename). */ export declare function writeManifest(manifest: CouncilManifest): void; /** Read a manifest by id. Returns null when the file is missing. */ export declare function readManifest(councilId: string): CouncilManifest | null; /** Read an archived manifest by id from `.harnery/councils/archive/.json`. * Symmetric to readManifest() but scoped to the archive dir, used by * `agents council unarchive` to load an archived council that is * (by definition) not in the active councils dir. */ export declare function readArchivedManifest(councilId: string): CouncilManifest | null; /** * Reassign the steward on an active or closed council. Atomic via * writeManifest's tmp+rename. Refuses to mutate archived councils * (those are read-only by convention). Pass `null` to clear the field * and revert to the default (the convener, via effectiveSteward). */ export declare function setCouncilSteward(councilId: string, steward: string | null): CouncilManifest; export interface KnownAgent { /** `agent-` canonical handle. */ name: string; /** `active` = currently has an authority-eligible V3 generation. `stale` = * recently ended (journal archived within the lookback window). */ state: "active" | "stale"; /** ISO timestamp of the most-recent signal. */ last_seen: string; } /** * Active V3 generations + recently-archived journals, deduped by name. * Used by `agents council set-steward` to refuse arbitrary names; * pass `--allow-unknown` to bypass when bootstrapping a new agent. */ export declare function listKnownAgents(): KnownAgent[]; /** List all council manifests in the active dir (skips archive/). */ export declare function listManifests(): CouncilManifest[]; /** * Move a council's manifest + body dir into the archive subdir. Idempotent: * archiving an already-archived council is a no-op (the source paths won't * exist). Used by `council archive` and by `council close --archive`. */ export declare function moveToArchive(councilId: string): void; /** * Reverse of moveToArchive: move a council's manifest + body dir back from * archive/ to the active councils dir. Idempotent: a missing archive path is * a no-op; an existing active path is left untouched and the archived copy * is dropped (mirrors moveToArchive's clobber-avoidance rule). Used by * `agents council unarchive` for testing the archive flow and as an undo * escape hatch. */ export declare function moveFromArchive(councilId: string): void; /** * Permanently remove an archived council: manifest + body dir under * .harnery/councils/archive/. Refuses to touch a council that's still * in the active dir (caller must archive first; the trash-can pattern). * Idempotent: missing paths are a no-op. Returns true when something was * actually deleted, false when both targets were already absent. * * NB: does NOT touch the council's target_doc (separate authored artifact), * close_handoff_path (separate authored artifact), or the canonical event * stream (immutable activity log). The delete is scoped to the manifest + * per-round member contributions. */ export declare function deleteArchivedCouncil(councilId: string): boolean; /** Resolve a member name (or partial id) to a council manifest from the active dir. */ export declare function findManifestByPartialId(partial: string): CouncilManifest | null; /** Build the invite.md body that ships alongside the manifest. */ export declare function buildInviteMarkdown(manifest: CouncilManifest): string; /** * Path to the contribution file for a member in a specific round. * Filename uses the agent's durable persona uuid (`.md`) so * a future rename doesn't break the link between manifest and on-disk * contribution. Resolves the name through the identity registry; mints a * new identity when the member name isn't registered yet (rare post- * migration; only happens for a brand-new persona that's never run). */ export declare function contributionPath(councilId: string, round: number, memberName: string): string | null; /** Path to a round's directory: `.harnery/councils//round-/`. */ export declare function roundDir(councilId: string, round: number): string | null; /** * Read the set of agent-Names that have contributed to a given round. * Returns display names ("agent-Maya"), not raw uuids; filenames on disk * are now `.md`, so we resolve each one through the registry * before returning. An identity that's been pruned (or never registered) * surfaces as `agent-<8-char-prefix>` so the value remains scannable. * * For UUID-keyed callers, use `contributorIdsInRound`. * Empty array when the round directory doesn't exist yet. */ export declare function contributorsInRound(councilId: string, round: number): string[]; /** Like contributorsInRound but returns the raw agent_id uuids: the * filenames on disk without the .md extension. */ export declare function contributorIdsInRound(councilId: string, round: number): string[]; /** * Return the IDs of active councils where this agent is a member AND has not * yet contributed to the current open round. Used by `agents status` to * surface a `council N pending` line in the status box, and by SessionStart * adapters to inject system reminders about pending invites. */ export declare function pendingCouncilsForMember(memberName: string): string[]; /** Write a contribution file (atomic). Creates the round directory lazily. */ export declare function writeContribution(councilId: string, round: number, memberName: string, body: string): string; /** * Path to the round-N prompts directory: `.harnery/councils//round-N/prompts/`. * Sibling to the contribution files. Holds one `.md` per non-self * council member, drafted by the steward, read by the operator (copy-paste * into each agent adapter) and the web UI (per-member panel). */ export declare function promptsDir(councilId: string, round: number): string | null; /** Path to a single member's prompt file in a given round. Like * contributionPath, filename uses the agent's durable persona uuid. */ export declare function promptPath(councilId: string, round: number, memberName: string): string | null; /** * Build the routing header prepended to every steward-drafted prompt. The * contributor skill scans inbound messages for this comment block; if the * `member:` line does not match the receiving agent's whoami, the agent * refuses to contribute (catches operator misrouting). HTML-comment so it * renders invisibly in markdown previews. */ export declare function buildRouteHeader(councilId: string, round: number, memberName: string): string; /** Strip a leading route header from a prompt body, if present. */ export declare function stripRouteHeader(body: string): string; /** * Build the submit footer appended to every steward-drafted prompt. This is the * load-bearing instruction that a contribution composed in chat is NOT recorded; * the agent must run the command below. It rides on the prompt (the one * artifact the operator always pastes) so it reaches every adapter regardless of * whether the convene-time invitation was delivered or a `/council` skill is * available. Without it, agents (esp. non-Claude adapters with no skill) write * their take as a reply and end the turn, leaving the council showing them as * still-pending. Visible markdown (not an HTML comment) so the agent reads it. */ export declare function buildSubmitFooter(councilId: string): string; /** Strip an appended submit footer from a prompt body, if present. */ export declare function stripSubmitFooter(body: string): string; /** Parse a route header from a string (the inbound user message). Returns * null when the comment is absent or malformed. Used by the /council * contribute skill to detect operator misrouting before contributing. */ export declare function parseRouteHeader(text: string): { council_id: string; council_round: number; member: string; } | null; /** Write a prompt file (atomic). Creates the prompts dir lazily. The body * is automatically prepended with a route header (see `buildRouteHeader`) so * the contributor skill can verify the operator routed the prompt to the * right agent. */ export declare function writePrompt(councilId: string, round: number, memberName: string, body: string): string; /** * Read a member's prompt for a given round. Returns null when the file * doesn't exist (steward hasn't drafted one yet). Includes a `completed` * boolean so the UI can mark the prompt deactivated once the contribution * has landed. */ export declare function readPrompt(councilId: string, round: number, memberName: string): { body: string; completed: boolean; } | null; /** * Visual/behavioral state of a per-member routing prompt within a round: * * - `contributed`: the member already submitted; the prompt is preserved for * audit but no longer actionable. UIs render it dimmed + struck-through. * - `active`: the first not-yet-contributed prompt in `manifest.members` * order. This is the one the operator should route next. UIs highlight it. * - `queued`: drafted but waiting for an earlier member to contribute first. * UIs render it dimmed with the Copy button disabled so the operator can't * route it out of order. */ export type CouncilPromptState = "contributed" | "active" | "queued"; /** * Read every member's prompt for a round, in `manifest.members` order * (which is the agreed round-robin sequence; alphabetical is wrong because * stewards typically build councils with a deliberate first-to-last order). * * Each entry carries `order` (1-indexed position within manifest.members, * skipping members whose prompts don't exist) + `state` (contributed / * active / queued) so the UI can render the three-state pattern without * duplicating the active-determination logic. */ export declare function readRoundPrompts(manifest: CouncilManifest, round: number): Array<{ member: string; body: string; completed: boolean; order: number; state: CouncilPromptState; }>; //# sourceMappingURL=index.d.ts.map