/** * session-builder.ts * * Shared `createAgentSession()` factory — extracts the common agent * initialisation sequence from agent.ts (REPL), run-flow.ts (flow), and * serve.ts (WebSocket server) into a single reusable module. * * The shared setup pipeline: * 1. resolveSettings → driverType, provider, model, apiKey * 2. loadAgentManifest → manifest (model, maxTurns, tools, name) * 3. buildAgentResources → resourceManager, resourcePromptSection, mcpServers, dispose * 4. Load workspace config, build contextSection + environmentSection, load prompt-extensions * 5. assembleSystemPrompt (context + env + resources + extensions + additional sections) * 6. Resolve maxTurns (config override wins over manifest) * 7. createDriver (NOT started — caller calls driver.start() when ready) * * Agent identity (SOUL.md, RULES.md, knowledge/) is never assembled here. * It is rendered at install time by the renderers in asset-manager and read * natively by each backend driver at runtime. */ import type { AgentDriver, BridgeCapabilityHooks, CodexManagedRuntimeOptions, CodexDriverOptions } from "@skaile/workspaces/bridge"; import type { AgentManifest, RuntimeAssetsResult, SkWorkspaceConfig } from "@skaile/workspaces/core"; import type { TelemetryProvider, Trace } from "@skaile/workspaces/telemetry"; import type { SystemPromptSection } from "@skaile/workspaces/types"; import type { CapabilityRegistry } from "./capability-registry.js"; import { type ResolvedComposition } from "./composition/bootstrap.js"; import { type ResourceWatchCallbacks } from "./resources.js"; export type { ResourceWatchCallbacks }; /** * Configuration for creating an agent session. * * Controls the driver backend, LLM provider and model, agent identity, * workspace layout, resource lifecycle, and protocol v2 capability hooks. * * @docLink packages/runner/dev-guide#flow-execution-turn-based-model */ export interface AgentSessionConfig { /** Trusted programmatic admission only; wire feature advertisement remains independently gated. */ managedCodex?: { runtime: Omit; approvals: import("./codex-tools.js").CodexNativeApprovalHandlers; requiredTools: readonly string[]; approvalPolicy: NonNullable; sandboxMode: Exclude, "danger-full-access">; reasoningEffort: NonNullable; /** Managed subprompt tier mapping and authoritative models available to this account. */ subpromptModels?: { small: string; deep: string; available: readonly string[]; }; }; projectDir: string; /** * Agent working directory — where the agent reads/writes files. * Defaults to `projectDir` when omitted. Use this to separate the config * root (`projectDir` with `skaile.yaml`) from the actual working directory * (e.g. when the user is in a subdirectory or has no `skaile.yaml` in their CWD). */ cwd?: string; agentDir?: string; driver?: string; model?: string; provider?: string; /** * AI cloud transport for claude-sdk (`default|bedrock|vertex|azure|gateway`). * Explicit value wins over the settings cascade (skaile.yaml * `agent-config.default.cloud`); absent everywhere = `default`. * * @since 1.3.0 */ cloud?: string; /** * Non-secret cloud transport settings (camelCase). Explicit value wins over * the settings cascade. Must stay field-identical with the bridge's * `CloudConfig` / core's `CloudTransportConfig` (kept inline to avoid * widening the runner↔bridge type surface). * * @since 1.3.0 */ cloudConfig?: { region?: string; projectId?: string; resource?: string; baseUrl?: string; }; /** * Resolved credential env bundle for the cloud. serve.ts injects the * platform-resolved bundle (from `session_init` secrets, with the Vertex SA * JSON already materialized to a file path); CLI/standalone paths leave it * unset and the driver falls back to ambient `process.env` credentials. * Values are secrets: never logged. * * @since 1.3.0 */ cloudSecrets?: Record; promptsDir?: string; projectClaudeMd?: string; sessionId?: string; resumeSessionId?: string; /** * Path to a file whose contents are prepended to the system prompt. * @deprecated Use `agent.prompt-extensions` in skaile.yaml instead. The runner * no longer injects this file's contents. */ externalPromptFile?: string; /** * Pre-resolved workspace config. Avoids re-reading skaile.yaml when the caller * already has it. Optional — falls back to `resolveSkWorkspaceConfig(projectDir)`. */ wsConfig?: SkWorkspaceConfig; /** * Platform-level maxTurns override. Takes precedence over manifest.runtime.max_turns. * Flow/REPL modes omit this field — they use the manifest value only. */ maxTurns?: number; /** Thinking mode override. */ thinking?: "adaptive" | "enabled" | "disabled"; /** Reasoning effort level override. */ effort?: "low" | "medium" | "high" | "max"; /** Watch callbacks for filesystem/sync events (serve mode only). */ watch?: ResourceWatchCallbacks; /** * When true, rclone-backed filesystem-face connectors are mounted in the * background instead of awaited, so a slow remote can't delay session * bootstrap (notably the platform's `session_init_ack` deadline). Serve mode * sets this; CLI/flow/REPL paths leave it unset (mount eagerly). */ deferFilesystemMounts?: boolean; /** Secret provider for connector credential resolution. */ secretProvider?: unknown; /** * Pre-resolved API key for the active provider. When supplied, it takes * precedence over the env-var lookup performed inside `resolveApiKey()`. * Used by `serve.ts` to feed the value from `provision_secrets` straight * into `AgentConfig.apiKeys` without writing it to `process.env`. */ apiKey?: string; /** * Optional callback used by `auth: backend` git connectors to request * short-lived access tokens from the platform's credential mediator. * Threaded into the `ConnectorManager` constructor so all git connectors in * this session share the same mediator. */ tokenMediator?: import("@skaile/workspaces/connectors").TokenMediator; /** * Pre-minted credential provider populated from the v3 `session_init` * envelope's `credentials` map. Threaded into ConnectorManager so that * connectors with `auth: backend` find their token in-process instead of * paying a `request_access_token` round-trip at mount time. * * @since 3.0.0 */ preMintedSecrets?: import("@skaile/workspaces/connectors").PreMintedSecretProvider; /** * Connector declarations used ONLY when the disk (`skaile.yaml` + * materialized assets) yields zero declarations. Serve mode maps the v3 * `session_init` resolvedConfig through `resolvedConfigToDeclarations` and * passes it here so broker-mode sessions (workspace-only bind, no yaml on * disk) still get their mounts/connectors. Disk-first: any disk declaration * disables the fallback, keeping docker-mode sessions byte-identical. * CLI/flow/REPL callers omit this field. */ fallbackConnectorDeclarations?: import("@skaile/workspaces/connectors").ConnectorDeclaration[]; /** * Returns the MCP servers attached at runtime (`runner.attach_instance`) that * `skaile.yaml` does not declare, so a session rebuild respawns them instead * of dropping them as undeclared "ghosts". A thunk, not an array: serve builds * its `AgentSessionConfig` once and reuses it for every recreate, so a snapshot * taken at build time would never see a later attach. A disk declaration of the * same id wins. CLI/flow/REPL callers omit this field. */ getExtraMcpDeclarations?: () => import("@skaile/workspaces/core").McpServerDeclaration[]; /** * Platform `AIProviderConfig.id` for the AI credential provisioned into * this session. Threaded into `AgentConfig.aiProviderConfigId` so that * driver recreates (tier-1 native SDK resume, subagents, etc.) preserve * the link to the platform mediator. The driver itself does not consume * this field; only the runner's 401-mediation handler reads it. */ aiProviderConfigId?: string; /** * Optional auth-refresh callback threaded into the bridge driver. Invoked * by the driver when the underlying agent surfaces an * `authentication_error`; in v3 the runner mediates a fresh credential * via the `host.refresh_credential` capability and returns a typed * {@link CredentialMint}. The driver branches on the discriminator: * `ok: true` triggers a one-shot retry of the in-flight prompt; * `ok: false` surfaces the original auth error to the caller. * * Centralising auth-retry inside the driver removes the need to wrap * every `driver.prompt(...)` call site (compaction, flow turns, serve * handler) with a runner-side retry helper. Standalone CLI / forge * sessions leave it undefined. * * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md` * § "Refresh-on-401 routes through host.refresh_credential". */ onAuthError?: (args: { configId: string; }) => Promise; /** * Optional callback for the driver's seat-failover path: a turn blocked by an * upstream usage limit (not a bad credential). The runner asks the platform * to re-resolve the AI provider config and reports whether the session moved * to a different one, which the driver acts on by respawning its CLI and * replaying the prompt once. Standalone CLI / forge sessions leave it * undefined and the limit error surfaces unchanged. * * @see AgentConfig.onLimitBlocked */ onLimitBlocked?: (ctx: import("@skaile/workspaces/bridge").LimitBlockedContext) => Promise; /** * Optional fire-and-forget hook for observed AI-provider 401 / 429 responses, * forwarded to the platform as `provider_response_seen` for per-seat health * attribution. * * @see AgentConfig.onProviderResponse */ onProviderResponse?: (observation: import("@skaile/workspaces/bridge").ProviderResponseObservation) => void; /** * Additional sections to append to the assembled system prompt. * Used by serve mode to inject the component catalog (AVAILABLE_COMPONENTS) * without modifying the session builder's core assembly logic. */ additionalPromptSections?: string[]; onLog?: (line: string) => void; /** Telemetry provider for observability. Optional — omit to disable tracing. */ telemetry?: TelemetryProvider; /** Active trace to attach to this session's driver. */ trace?: Trace; /** * Protocol v2 capability dispatch hooks built from the runner's * CapabilityRegistry. When present, the driver routes registered tool * calls through `hooks.invoke()` instead of the legacy in-driver dispatch. * * @since 2.0.0 */ capabilities?: BridgeCapabilityHooks; /** * The session's Protocol v2 capability registry. When supplied, external * (declarative / recipe-backed) MCP servers are spawned and connected BY THE * RUNNER during `buildAgentResources`, with each tool registered into this * registry as an `mcp`-origin capability — the bridge-agnostic surface every * driver consumes. Omit (CLI / flow) to fall back to the legacy * claude-sdk-only SDK delegation for external stdio servers. * * @since 3.5.0 */ capabilityRegistry?: CapabilityRegistry; } /** * A configured agent session returned by {@link createAgentSession}. * * Carries a driver instance (not yet started), connected resource and * mount managers, resolved runtime assets, and the parsed agent manifest. * * @docLink packages/runner/dev-guide#flow-execution-turn-based-model */ export interface AgentSession { /** Driver configured but NOT started. Caller must call driver.start(). */ driver: AgentDriver; /** Connected ConnectorManager — null if no connectors declared. any intentional (optional dep). */ resourceManager: any | null; /** * Resolved runtime assets — catalog entries, implicit refs, npm deps, * warnings, plus refresh() / findEntry() helpers. Live-update paths * (add_resource, configure) hold this reference and call refresh() when * a new repository is cloned or driver installed mid-session. */ runtimeAssets: RuntimeAssetsResult | null; /** * Resolved composition — inline blocks (already injected into systemPrompt) * and discoverable entries (caller registers these with CapabilityRegistry). * Null when the agent has no `composes:` field. */ composition: ResolvedComposition | null; /** Parsed agent.yaml manifest — model, maxTurns, tools, name. */ manifest: AgentManifest | undefined; /** Active driver type string ('omp' | 'claude-sdk'). */ driverType: string; provider: string | undefined; model: string | undefined; /** Disconnect all connectors, unmount all mounts, and stop watchers. */ dispose: () => Promise; /** Start filesystem and connector watchers. Call after transport is listening. */ startWatching: () => void; /** * Lazily construct (once) and return the session's external MCP manager — * the live target for `runner.add_mcp_server`. Serve mode only; throws when * no capability registry was supplied (CLI/flow). */ getOrCreateExternalMcpManager: () => import("./external-mcp.js").ExternalMcpManager; /** The external MCP manager if constructed (boot or lazy), else null. */ getExternalMcpManager: () => import("./external-mcp.js").ExternalMcpManager | null; /** Telemetry provider for this session (noop if not configured). */ telemetry?: TelemetryProvider; /** * Labeled per-section breakdown of the system prompt, in the same * order assembleSystemPrompt consumed them. Multi-entry sections * (prompt-extensions, composition, additional) appear as repeated * entries with `source` set. * * Consumed by serve.ts's emitSystemPromptComposed helper to emit * `system_prompt_composed` events to the platform. * @since 2026-05 */ systemPromptSections: SystemPromptSection[]; } /** * Create an agent session by resolving settings, loading the agent * manifest, building resources (connectors, mounts, MCP servers), * and creating the driver. * * The returned driver is NOT started — the caller must call * `driver.start()` after optionally attaching event handlers. * * @param config - Session configuration * @returns A fully configured AgentSession ready for start() * @docLink packages/runner/dev-guide#flow-execution-turn-based-model */ export declare function createAgentSession(config: AgentSessionConfig): Promise; /** Build a disposable managed Codex child with the parent's resolved authority and fresh state. */ export declare function createManagedCodexSubpromptDriver(config: AgentSessionConfig, model: string | undefined, cwd: string, privateDirectory: string): Promise; //# sourceMappingURL=session-builder.d.ts.map