/** * openlore Pi extension — src/pi/extension.ts * * Compiled to dist/pi/extension.js and declared in package.json "pi" field so * `pi install npm:openlore` drops it into the Pi extension registry automatically. * * Two halves: * C — context injection (before_agent_start): model starts grounded with the * architecture digest + spec index + task-grounded orient call, so weak * tool-callers benefit even without calling a tool. * B — native tools (registerTool): the substrate surface for on-demand structural * queries — NAV_TOOLS spans navigate + change + remember + verify + governance * (it already supersets the MCP `substrate` preset; the family taxonomy and the * preset/breadth selectors are MCP-wire concepts the native Pi host does not use), * each round-tripping to the warm daemon via fetch. * * Uses ctx.mode (0.78.1+): full injection in tui/rpc (interactive), none in * json/print (one-shot). rpc = headless interactive over stdin/stdout (IDE, * custom UI) — same injection needs as tui. * * Config onboarding: runs on first session when .openlore/config.json is absent; * also available anytime via the openlore_configure tool. * * change: harden-pi-config-and-daemon-fidelity */ import type { AgentToolResult, ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent'; import { type TObject } from 'typebox'; import type { ContextInjectionConfig } from '../types/index.js'; interface OpenLoreConfig { [key: string]: unknown; version: string; projectType: string; openspecPath: string; analysis: { maxFiles: number; includePatterns: string[]; excludePatterns: string[]; }; generation: { [key: string]: unknown; provider?: string; model?: string; openaiCompatBaseUrl?: string; skipSslVerify?: boolean; domains?: string | string[]; }; embedding?: { [key: string]: unknown; baseUrl: string; model: string; apiKey?: string; skipSslVerify?: boolean; }; /** Task-scoped context injection settings (gate + token budget + opt-out). */ contextInjection?: ContextInjectionConfig; createdAt: string; lastRun: string | null; } type ExistingOpenLoreConfig = Record; export type ExistingConfigLoad = { state: 'absent'; } | { state: 'invalid'; detail: string; } | { state: 'valid'; config: ExistingOpenLoreConfig; }; /** Treat a config as absent unless it has the minimum viable fields. */ export declare function isUsableConfig(raw: unknown): raw is OpenLoreConfig; export declare function readConfig(cwd: string): Promise; /** * Read just the `contextInjection` block, independent of `isUsableConfig`. * The injection opt-out must work even before an LLM provider is configured — * `readConfig` returns null until `generation.provider` is set (a headless/rpc * session may never run the wizard), which would silently drop `mode: "off"`. * Mirrors the CLI path, which reads config unconditionally. */ export declare function readContextInjection(cwd: string): Promise; /** * May this extension spawn a daemon of its own? * * Read like `readContextInjection` and NOT through `readConfig`: the opt-out must work before an * LLM provider is configured, and `readConfig` returns null until `generation.provider` is set — * which would silently restore spawn authority the operator revoked. The environment variable wins * over the config key, because it is the host's per-process statement about who owns the process. * Only the exact `false` disables spawning; an absent or malformed key leaves today's default. */ export declare function piMaySpawnDaemon(cwd: string): Promise; /** * Build the `/v1/models` URL for a provider base URL, tolerating a trailing * slash and an already-present `/v1` segment (e.g. https://api.mistral.ai/v1/). */ export declare function modelsUrl(baseUrl: string): string; /** Strip the trailing " *" current-value marker added to select-list entries. */ export declare function stripMarker(label: string): string; export declare function loadExistingConfig(cwd: string): Promise; export declare function runConfigWizard(ctx: ExtensionContext, existing?: ExistingOpenLoreConfig | null): Promise; interface Daemon { baseUrl: string; token?: string; incompatibility?: string; } export declare const PI_ORIENT_TIMEOUT_MS = 4000; export declare const PI_SPEC_INDEX_MAX_DOMAINS = 50; export declare const PI_DAEMON_PRESET = "full"; /** * Auditable mapping from the Generate/Repair protocol observations to the * existing daemon primitives used by Pi's task entry points. Keep this list * closed in tests: adding a protocol observation must either wire it here or * add a documented exclusion below. */ export declare const PI_SPEC_WORKFLOW_OBSERVATIONS: { readonly generation: { readonly domainEvidence: "prepare_spec_generation"; readonly domainBehavior: "prepare_spec_generation"; readonly specValidation: "prepare_spec_generation"; }; readonly repair: { readonly domainEvidence: "prepare_spec_repair"; readonly existingSpec: "prepare_spec_repair"; readonly coveredFunction: "prepare_spec_repair"; readonly uncoveredFunction: "prepare_spec_repair"; readonly staleMapping: "prepare_spec_repair"; readonly orphanRequirement: "prepare_spec_repair"; readonly structuralChange: "prepare_spec_repair"; readonly mappingCoverage: "prepare_spec_repair"; readonly specValidation: "prepare_spec_repair"; readonly domainBehavior: "prepare_spec_repair"; }; }; export declare const PI_SPEC_WORKFLOW_EXCLUSIONS: Readonly>; export declare function missingDaemonTools(available: readonly string[], required: readonly string[]): string[]; /** Launch the packaged CLI without a command shell so repository paths stay data. */ export declare function piDaemonSpawnCommand(cwd: string): { command: string; args: string[]; }; export type EnsureDaemonResult = { daemon: Daemon; failure?: undefined; } | { daemon: null; failure: string; failureKind: 'draining' | 'launch' | 'preparation' | 'early-exit' | 'health-timeout' | 'spawn-disabled'; }; export declare function shouldNegativeCacheDaemonFailure(kind: Exclude['failureKind']): boolean; export declare function ensureDaemonResult(cwd: string, options?: { timeoutMs?: number; launch?: { command: string; args: string[]; }; }): Promise; export declare function ensureDaemon(cwd: string): Promise; export declare function callTool(daemon: Daemon, name: string, args: Record, cwd: string, signal?: AbortSignal): Promise; export declare class PiDaemonConnectionError extends Error { constructor(message: string); } /** Bound a best-effort Pi operation without abandoning useful background work. */ export declare function awaitWithSignal(work: Promise, signal: AbortSignal): Promise; export declare function isUsableDaemon(daemon: Daemon): boolean; export declare function readSpecIndex(cwd: string): Promise; interface NavToolSpec { name: string; label: string; description: string; guideline: string; parameters: TObject; } export declare const NAV_TOOLS: NavToolSpec[]; export declare const PI_EXCLUDED_CONCLUSION_TOOLS: Record; /** * Forward a spec-workflow composite envelope WITHOUT generic clipping. * * The daemon already packed the page to `PI_COMPOSITE_RESPONSE_BYTES`, so a * valid envelope fits by construction and its completeness receipt is * meaningful. Clipping it here would silently invalidate that receipt — the * exact failure this change removes. A page that is somehow still oversized is a * transport fault, so it returns a typed error instead of clipped JSON the model * would try to parse as evidence. */ export declare function compositeToolResult(result: unknown): AgentToolResult; /** * One-line summary of a tool call's arguments for renderCall — the descriptive * arg, quoted (e.g. orient "add rate limiting"). Pathfinding reads as `a → b`. * Returns '' when there's no descriptive arg (e.g. get_health_map) so the caller * shows the bare tool title. */ export declare function formatCallArgs(args: Record): string; /** * Turn a structured tool result into readable text. Strings pass through; * `{ error }` becomes a warning line; objects render as labelled sections with * arrays shown as bounded bullet lists. `toolName` selects per-tool skips so an * ambient tool (orient) can hide enrichment a deliberate one (analyze_impact) * keeps. Resilient to schema drift — unknown shapes degrade to key/value lines. */ export declare function formatToolResult(result: unknown, toolName?: string): string; /** Build an extension registration function with bounded runtime overrides. @internal */ export declare function createPiExtension(runtime?: { orientTimeoutMs?: number; }): (pi: ExtensionAPI) => void; /** Pi package entry point. */ export default function openlore(pi: ExtensionAPI): void; export declare const installPaths: { project: (cwd: string) => string; global: () => string; }; export {}; //# sourceMappingURL=extension.d.ts.map