/**
 * Agent Orchestrator
 *
 * Manages the Pi-Mono agent instance and coordinates with workspace managers.
 * Loads skills, handles system prompts, and manages conversation flow.
 */
import { Agent } from '@mariozechner/pi-agent-core';
import { WorkspaceManager } from '../core/workspace-manager.js';
import { WorkspaceConfig } from '../core/types.js';
import { GitUser } from '../services/git.js';
import { type MutationEvent } from './tools/graph-tools.js';
import { AuthService } from '../services/auth-service.js';
import type { AuthUser } from '../services/auth-service.js';
import type { CommentService } from '../services/comment-service.js';
import type { ChatSessionService } from '../services/chat-session-service.js';
import { AgentPool } from './agent-pool.js';
import { type InferenceUsageSummary } from '../services/cloud-inference-billing.js';
export interface AgentConfig {
    workspacePath: string;
    workspaceConfig: WorkspaceConfig;
    gitUser: GitUser;
    /** Additional skill directories from installed apps (highest-priority first). */
    skillsDirs?: string[];
    /** Additional custom tool directories from installed apps (highest-priority first). */
    toolsDirs?: string[];
    /** Recent daily memory content (today + yesterday) injected into system prompt. */
    recentMemory?: string;
    /** Shared WorkspaceManager instance (used by server to share state with agent). */
    workspaceManager?: WorkspaceManager;
    /**
     * Shared AuthService — pass the route-level instance so the
     * orchestrator participates in the same OAuth-mutation pubsub and uses
     * the master-key-wired crypto path for `oauth_tokens` reads.
     *
     * Without this, the orchestrator constructs a fresh AuthService that
     * (a) has no master key, so reads of encrypted oauth_tokens rows
     * throw; (b) subscribes to a different pubsub channel than the one
     * `connectors-api.ts` fires, so OAuth-mutation invalidations never
     * reach the agent pool. Phase 8 (May 11 2026) wired crypto into the
     * route-level AuthService but missed this instance — latent until
     * the 2026-05-13 per-user OAuth runtime wiring made the orchestrator
     * actually read encrypted oauth_tokens.
     *
     * Tests and CLI surfaces still construct their own — back-compat
     * fallback below.
     */
    authService?: AuthService;
    /**
     * Optional CommentService — when supplied, the agent's generic read tools
     * (`get_entity`/`list_entities`/`get_related`) can resolve the virtual
     * `comment` entity type, giving the in-app agent the same read parity the
     * MCP surface already has. Omitted by CLI/test callers (comments simply
     * don't appear there).
     */
    commentService?: CommentService;
    /**
     * Optional ChatSessionService — lets the agent's `save_file_to_assets` tool
     * promote a conversation-scratch image into the curated asset library. Omitted
     * by CLI/test callers (the tool then resolves no scratch images and no-ops).
     */
    chatSessionService?: ChatSessionService;
}
export interface AgentResponse {
    content: string;
    toolCalls?: Array<{
        tool: string;
        input: any;
        output: any;
    }>;
    usage?: InferenceUsageSummary;
}
export declare class AgentOrchestrator {
    private workspacePath;
    private workspaceManager;
    private pool;
    private config;
    private gitUser;
    private skillsDirs;
    private toolsDirs;
    private registryRef;
    private authService;
    private commentService;
    private chatSessionService;
    private recentMemory?;
    private sessionsDir;
    private _currentUser;
    private _highlightCallback;
    private _mutationCallback;
    private currentSessionId;
    private _sessionUserId;
    constructor(config: AgentConfig);
    /**
     * Create Pi-Mono agent with tools and system prompt.
     * Custom tools and skills are loaded asynchronously via initAsync().
     */
    private createAgentInstance;
    /**
     * Load custom JS tools from app/workspace tool dirs and append to the agent.
     * Runs async after construction — custom tools may not be available for the
     * very first message if initialisation is extremely fast, but in practice
     * the first user message arrives well after startup.
     *
     * `userId` is forwarded into `ConnectorManager.buildTools` so per-user MCP
     * OAuth tokens are resolved at tool-build time. Without it the runtime
     * silently falls back to workspace-shared static headers (the deferred
     * gap before 2026-05-13). The agent pool keys agents per-session and
     * supplies the session's userId on creation; CLI / non-interactive
     * spawns pass `undefined` and intentionally land on the workspace
     * fallback.
     */
    private loadCustomToolsAsync;
    /**
     * Build the full system prompt: base prompt + eager skill content + skill index.
     */
    private buildSystemPrompt;
    /**
     * Enumerate skill entities across all folders the user can access. Each
     * skill becomes a SkillMeta with a `graph://<repo>/<id>` sentinel
     * entrypoint. Priority order: user's private folder first, then shared
     * folders alphabetically. First occurrence of a given name wins.
     */
    private getPromptRepoScope;
    private enumerateGraphSkills;
    /**
     * Load the markdown body of a skill entity by (repo, id).
     */
    private readGraphSkillBody;
    /**
     * Build a workspace identity section from graph entities.
     * Reads organization, person, practice-area, and guideline entries
     * to compose a concise identity summary for the system prompt.
     * Returns null if no organization entries exist.
     */
    private buildWorkspaceIdentity;
    /**
     * Per-repo subfolder summary for the system prompt. Lists each accessible
     * repo with up to MAX_PER_REPO subfolder paths so the agent can route new
     * entries into existing folders without calling list_folders for every
     * decision. Refresh cadence is per-session (system prompt is built once).
     */
    private buildFolderInventory;
    /**
     * Fallback system prompt when prompts/system.md is not found.
     */
    private getDefaultSystemPrompt;
    /**
     * Send a message to the agent
     */
    /**
     * Send a message and await the full agent response.
     * Uses the current session (set via switchSession) or a default session.
     */
    chat(userMessage: string): Promise<AgentResponse>;
    /**
     * Send a message with an explicit session ID and context.
     * Preferred over the legacy setCurrentUser + switchSession + chat pattern.
     */
    chatInSession(sessionId: string, userMessage: string, opts?: {
        user?: AuthUser | null;
        userId?: number;
        fallbackHistory?: Array<{
            role: string;
            content: string;
        }>;
    }): Promise<AgentResponse>;
    /**
     * Get workspace manager (for tools)
     */
    getWorkspaceManager(): WorkspaceManager;
    getModelSelection(): {
        provider: string;
        modelId: string;
    };
    /** Can the active chat model accept image input? Drives the composer's
     *  vision-attach affordance (disable + tooltip when false). The Studiograph
     *  Credits gateway is vision-capable (A6); otherwise resolve from the model. */
    supportsVision(): boolean;
    /**
     * Delete the agent-pool's persisted state for a chat session — evicts the warm
     * agent + unlinks its on-disk JSON, which holds base64 copies of any vision
     * images the agent saw. Called when a chat is deleted so those copies don't
     * outlive the conversation (composer-vision cleanup). `rawSessionId` is the
     * client session id; the pool keys by the resolved `user:{id}:{raw}` form.
     */
    deleteChatSessionState(userId: number, rawSessionId: string): void;
    /** Delete agent-pool state for ALL of a user's chats (reset / delete-all). */
    deleteAllChatSessionState(userId: number): void;
    /**
     * One-shot, tool-free model call that names a conversation from its opening
     * exchange (the ChatGPT pattern). Used by POST /api/chat/title after the
     * first assistant reply. Deliberately lightweight: no agent pool, no tools,
     * no system context — just the configured model and a tight instruction.
     *
     * Returns null (caller keeps the truncated first-message title) when there's
     * no usable title, no API key, or we're in Studiograph Credits gateway mode —
     * the gateway routes inference through its own client (cloud-inference-billing),
     * not pi-ai's complete(), so a direct completeSimple() can't reach it. Titles
     * via the gateway are a follow-up if we want them.
     */
    generateConversationTitle(firstUserMessage: string, assistantReply: string): Promise<{
        title: string;
        usage?: InferenceUsageSummary;
    } | null>;
    /**
     * Get workspace summary
     */
    getSummary(): {
        totalRepos: number;
        totalEntities: number;
        entitiesByType: Record<string, number>;
        entitiesByRepo: Record<string, number>;
    };
    /**
     * Get repository names
     */
    getRepoNames(): string[];
    /**
     * Set the current authenticated user for per-folder access filtering.
     * Called per-request by the chat endpoint. Null = full access (API key / open mode).
     */
    setCurrentUser(user: AuthUser | null): void;
    /**
     * Set a callback for highlight_entities tool events (used by SSE streaming).
     */
    setHighlightCallback(cb: ((ids: string[]) => void) | null): void;
    /**
     * Emit a highlight event (called by graph-tools highlight_entities).
     */
    emitHighlight(ids: string[]): void;
    /**
     * Set a callback for entity mutation events (used by SSE streaming).
     */
    setMutationCallback(cb: ((m: MutationEvent) => void) | null): void;
    /**
     * Emit a mutation event (called by graph-tools create/update/delete).
     */
    emitMutation(m: MutationEvent): void;
    /**
     * Expose the raw pi-agent-core Agent instance for a session (for SSE streaming).
     * Returns the default session agent if no sessionId specified.
     */
    getAgent(sessionId?: string): Agent | null;
    /** Get the agent pool (for advanced callers that need direct pool access). */
    getPool(): AgentPool;
    /** Abort the agent for a session. */
    abort(sessionId?: string): void;
    /**
     * Get the current session ID (null if none active).
     */
    getCurrentSessionId(): string | null;
    /**
     * Switch session (backward compat — sets state for next chat() call).
     * With the pool, actual session loading happens lazily in withAgent().
     */
    switchSession(sessionId: string, fallbackHistory?: Array<{
        role: string;
        content: string;
    }>, userId?: number): Promise<void>;
    /**
     * Save session (backward compat — pool auto-saves after each withAgent call).
     */
    saveSession(): void;
    /**
     * Clear all saved sessions from disk.
     */
    clearSessions(): number;
    /** Shut down the agent pool (save all sessions, stop idle reaper). */
    shutdown(): Promise<void>;
}
