export interface HostContext {
    hostname: string;
    lanIPv4: string | null;
    adminUrl: string;
    tunnelUrl: string | null;
}
/** Where a spawn reads its identity files. Task 638 — admin reads
 *  `agents/admin` and needs no slug; a public agent reads `agents/<agentSlug>`
 *  (the slug, never the literal role), so the public arm carries the slug. The
 *  directory itself is resolved by the shared `resolveAgentIdentityDir` so the
 *  reachability audit reads the byte-identical path. */
export type IdentitySources = {
    accountDir: string;
    role: 'admin';
} | {
    accountDir: string;
    role: 'public';
    agentSlug: string;
};
/** Per-spawn owner-profile body resolved by the caller before /spawn fires.
 *
 *  ok:true  — `body` is the `formatProfileSummary` output (verbatim) — two
 *             prose lines: the known facts (or `NOTHING`) and the standing
 *             MAXIMISE imperative.
 *  ok:false — `reason` is a short token the agent surfaces to the operator,
 *             e.g. `neo4j-unreachable`, `profile-not-found`, `missing-user-id`.
 *             The composer renders the same two-line prose body with the
 *             reason inline-coded inside line one, so the absent signal is
 *             visible end-to-end and the MAXIMISE imperative still reaches
 *             the agent on every turn.
 *
 *  When the field is omitted entirely (undefined), the sentinel is suppressed.
 *  This is the contract for callers that have no owner concept (role=public
 *  visitor flows that do not query a profile). */
export type OwnerProfileBlock = {
    ok: true;
    body: string;
} | {
    ok: false;
    reason: string;
};
export interface DormantPlugin {
    /** Plugin directory name under platform/plugins/ — the same key used in
     *  account.json's `enabledPlugins` array. */
    name: string;
    /** One-line description from PLUGIN.md frontmatter. Verbatim, no trimming
     *  beyond what the caller did. */
    description: string;
}
export interface ActivePluginTool {
    name: string;
    /** Optional one-line description. When absent, the manifest renders the
     *  tool name only — descriptions live in the plugin's MCP source as
     *  `server.tool("name", "description", …)` and the caller scrapes them
     *  from the compiled dist. */
    description?: string;
}
export interface ActivePluginSkill {
    name: string;
    /** Optional one-line description sourced from the skill's SKILL.md
     *  frontmatter `description:` field. */
    description?: string;
}
export interface ActivePlugin {
    /** Plugin directory name — same key as DormantPlugin / enabledPlugins. */
    name: string;
    /** PLUGIN.md frontmatter `description:` field, verbatim. */
    description: string;
    tools: readonly ActivePluginTool[];
    skills: readonly ActivePluginSkill[];
}
export interface SpecialistDomainTool {
    name: string;
    description?: string;
}
export interface SpecialistDomain {
    /** Specialist agent name — the value passed as `subagent_type:
     *  specialists:<name>`. */
    name: string;
    /** YAML frontmatter `description:` of the specialist agent .md file. */
    description: string;
    tools: readonly SpecialistDomainTool[];
}
export interface ComposeExtras {
    aboutOwner?: OwnerProfileBlock;
    dormantPlugins?: readonly DormantPlugin[];
    activePlugins?: readonly ActivePlugin[];
    specialistDomains?: readonly SpecialistDomain[];
    /** Visitor session_key for role=public spawns. Surfaced as the
     *  `<chat-session>` sentinel so skills emitting first-party links
     *  (e.g. the property-recommender card's click-redirect URL) can
     *  correlate the click log line back to the recommend-fire log.
     *  Undefined on admin spawns — sentinel suppressed. */
    chatSessionKey?: string;
}
export type AppendBlockResult = {
    ok: true;
    block: string;
    identityBytes: number;
    soulBytes: number;
    knowledgeBytes: number;
    aboutOwnerBytes: number;
    dormantPluginsBytes: number;
    pluginManifestBytes: number;
    specialistDomainsBytes: number;
    chatSessionBytes: number;
} | {
    ok: false;
    reason: 'host-unresolved' | 'identity-unresolved' | 'soul-empty' | 'knowledge-missing' | 'knowledge-empty';
    detail: string;
};
export declare function composeAppendSystemPrompt(host: HostContext, sources: IdentitySources, extras?: ComposeExtras): AppendBlockResult;
export type IdentityForbiddenViolation = {
    class: string;
    token: string;
};
export declare function runIdentityForbiddenTokenCheck(identityContent: string): {
    ok: true;
} | {
    ok: false;
    violations: IdentityForbiddenViolation[];
};
/** Boot-time IDENTITY-vs-composer drift assertion. Parses IDENTITY.md at
 *  `<accountDir>/agents/admin/IDENTITY.md`, extracts every `<sentinel-name>`
 *  reference the prose makes, and confirms the composer would emit each one
 *  for a sample admin session. The synthetic inputs exercise every optional
 *  sentinel so a missing emit is visible regardless of operator state.
 *
 *  Returns ok:false with the first sentinel IDENTITY.md names that the
 *  composer does not emit. The caller LOUD-FAILs the manager start. */
export declare function runIdentityDriftAssertion(identityContent: string): {
    ok: true;
    sentinelsChecked: string[];
} | {
    ok: false;
    missing: string;
    sentinelsChecked: string[];
};
/** Boot-time self-test: render the block with synthetic inputs that exercise
 *  every optional sentinel, and assert the expected tags appear. Returns
 *  ok:true on success, ok:false with the first missing tag on drift. The
 *  caller logs `[startup-self-test] system-prompt-sentinels=ok` or LOUD-FAILs
 *  so future refactors that drop a sentinel surface at manager start, not at
 *  runtime against a real spawn. */
export declare function runSystemPromptSelfTest(): {
    ok: true;
} | {
    ok: false;
    missing: string;
};
//# sourceMappingURL=system-prompt.d.ts.map