/** * Dojo MCP Tools - AI Feature Incubator * * Provides MCP tools for AI to: * - Create proposals for new features * - Scaffold experiments from proposals * - Run experiments in sandbox mode * - Track wishes (capabilities AI wants) * - View experiment results * * Safety: AI cannot enable/disable/graduate experiments. * Those actions are human-only via CLI. * * Project-scoped: All operations require project_id and work within {project_root}/dojo * Role-based: caller_role determines what operations are allowed */ import { CallerRole } from './dojoPolicy.js'; import { type StoreMetaFields } from './shared/storeRead.js'; export type ExperimentType = 'script' | 'tool' | 'check' | 'prompt'; /** * Base input for all Dojo operations * project_id: Optional - identifies which project's Dojo to use (uses default if not provided) * caller_role: Optional - determines permissions (default: 'human') * agent_id: Optional - identifies the calling agent for audit trails */ export interface DojoBaseInput { project_id?: string; caller_role?: CallerRole; agent_id?: string; } /** * What a proposal IS — and therefore which fields it owes. * * "Proposal" was one word doing three jobs. The shape here has always been * experiment-shaped by construction (problem -> hypothesis -> scaffold -> * run -> graduate), which is the right question for a technique and the wrong * one for a surface other software calls, or for a positioning argument that * has no module at all. Both of those turned up in the same hour (WISH-0023), * which is what made the split real rather than theoretical. * * The discriminating question is simply: does this artifact feed an experiment? * * experiment — the original. A belief, and a way to test it. * tool — a contract for something already decided. No hypothesis. * strategy — an argument. No target_module, no code scope. */ export type ProposalKind = 'experiment' | 'tool' | 'strategy'; export interface CreateProposalInput extends DojoBaseInput { /** * Required, deliberately — no default. * * An optional kind defaulting to 'experiment' would let every existing caller * silently keep meaning "experiment", and we would learn nothing about * whether the split is real. Absent kind is treated as 'experiment' when * READING legacy records, which is what the eleven on disk actually are. */ kind: ProposalKind; title: string; problem: string; /** Required for kind:'experiment'. An artifact full of empty hypothesis * fields is worse than a document, so the other kinds must not carry it. */ hypothesis?: string; owner?: 'ai' | 'human'; target_module?: string; scope_in?: string[]; scope_out?: string[]; acceptance?: string[]; risks?: string[]; follows?: string; insight?: string; wish_id?: string; /** Facade this action belongs to, e.g. 'sentinel'. */ facade?: string; /** * Action name, snake_case, e.g. 'close_issue'. * * Named action_name, not action, because every facade call already spends * `action` on selecting the operation — a flat `action` here is swallowed by * the dispatcher and never reaches this tool. Found by the runtime on the * first real call, which is the cheapest place to find it. */ action_name?: string; /** core | pro | apps — and the tier check that bypassed four billed * endpoints was ISS-0171. */ tier?: 'core' | 'pro' | 'apps'; /** How the payload distinguishes "nothing there" from "could not look". * The silent-zero question: ISS-0166 (deck.stores), ISS-0153 (40 reads). */ absence_semantics?: string; /** What backs the read, and what it answers when that is unreachable. * A slow backend is not a broken one: ISS-0162. */ backing_store?: string; /** Optional, but the cheap ones to answer while you are here. */ transports?: string; /** What it mutates, whether it is idempotent, and whether what it writes * can be read back — ISS-0174, six epics that could not. */ write_path?: string; /** Which torture sweeps apply, and any waiver with its reason. */ torture_coverage?: string; } /** * Say what is missing, all of it, in one answer. * * Returns null when the input is well-formed for its kind. */ export declare function validateProposalKind(input: CreateProposalInput): string | null; export interface CreateProposalOutput { proposal_id: string; filepath: string; title: string; } export interface ScaffoldExperimentInput extends DojoBaseInput { proposal_id: string; script_type?: 'py' | 'ts'; experiment_type?: ExperimentType; } export interface ScaffoldExperimentOutput { experiment_id: string; proposal_id: string; directory: string; entrypoint: string; files_created: string[]; } export interface ListDojoInput extends DojoBaseInput { filter?: 'proposals' | 'experiments' | 'wishes' | 'all'; } export interface ProposalSummary { id: string; title: string; owner: 'ai' | 'human'; state: 'draft' | 'has_experiment' | 'enabled'; } export interface ExperimentSummary { id: string; proposal_id: string; title: string; type: string; enabled: boolean; } export interface WishSummary { id: string; capability: string; reason: string; resolved_by?: string; } export interface ListDojoOutput extends StoreMetaFields { proposals: ProposalSummary[]; experiments: ExperimentSummary[]; wishes: WishSummary[]; summary: { total_proposals: number; total_experiments: number; total_wishes: number; enabled_count: number; }; } export interface RunExperimentInput extends DojoBaseInput { experiment_id: string; } export interface RunExperimentOutput { experiment_id: string; run_id: string; status: 'success' | 'failure'; exit_code: number; duration_seconds: number; artifacts: string[]; stdout?: string; } export interface GetResultsInput extends DojoBaseInput { experiment_id: string; run_id?: string; } export interface GetResultsOutput { experiment_id: string; run_id: string; timestamp: string; exit_code: number; artifacts: string[]; stdout?: string; stderr?: string; } export interface ReadArtifactInput extends DojoBaseInput { experiment_id: string; run_id: string; filename: string; } export interface ReadArtifactOutput { experiment_id: string; run_id: string; filename: string; content_type: 'yaml' | 'json' | 'text' | 'binary'; content: unknown; } export interface AddWishInput extends DojoBaseInput { capability: string; reason: string; inputs: string[]; outputs: Record | string; integration_point?: string; success_metric?: string; risks?: string[]; mvp?: string; algorithm_outline?: string; context?: Record; } export interface AddWishOutput { wish_id: string; capability: string; timestamp: string; } export interface ListWishesInput extends DojoBaseInput { unresolved_only?: boolean; } export interface ListWishesOutput extends StoreMetaFields { wishes: WishSummary[]; total: number; unresolved: number; } export interface CanGraduateInput extends DojoBaseInput { experiment_id: string; } export interface CanGraduateOutput { experiment_id: string; can_graduate: boolean; has_tool_definition: boolean; is_enabled: boolean; reasons: string[]; } export interface DojoError { error: string; exitCode: number; stderr: string; } export interface DojoContext { projectId: string; projectRoot: string; dojoRoot: string; callerRole: CallerRole; agentId?: string; } /** * Resolve the Dojo root for a project. * Returns the path to {project_root}/.decibel/dojo * If projectId is not provided, attempts to use the default project. */ export declare function resolveDojoRoot(projectId?: string): Promise<{ projectId: string; projectRoot: string; dojoRoot: string; }>; /** * Build a full Dojo context with policy and rate limit enforcement */ export declare function buildDojoContext(projectId: string | undefined, callerRole: CallerRole | undefined, toolName: string, agentId?: string): Promise; /** * Mark a Dojo request as complete (call in finally block) */ export declare function finishDojoRequest(callerRole?: CallerRole): void; /** * Create a new Dojo proposal (native file operations) */ export declare function createProposal(input: CreateProposalInput): Promise; /** * Scaffold an experiment from a proposal (native file operations) */ export declare function scaffoldExperiment(input: ScaffoldExperimentInput): Promise; /** * List proposals, experiments, and wishes (native file operations) */ export declare function listDojo(input: ListDojoInput): Promise; /** * Run an experiment in sandbox mode (native file operations) * Note: This still uses spawn() to run the experiment script, but not the CLI */ export declare function runExperiment(input: RunExperimentInput): Promise; /** * Get results from a previous experiment run (native file operations) */ export declare function getExperimentResults(input: GetResultsInput): Promise; /** * Add a wish to the wishlist (native file operations) */ export declare function addWish(input: AddWishInput): Promise; /** * List wishes (native file operations) */ export declare function listWishes(input: ListWishesInput): Promise; /** * Check if an experiment can be graduated (read-only) */ export declare function canGraduate(input: CanGraduateInput): Promise; /** * Read an artifact file from experiment results * Returns parsed content for yaml/json, raw text for others */ export declare function readArtifact(input: ReadArtifactInput): Promise; /** * Type guard for DojoError */ export declare function isDojoError(result: unknown): result is DojoError; export interface ListProjectsOutput { projects: { id: string; name?: string; path: string; aliases?: string[]; isDefault: boolean; }[]; defaultProject?: string; hint: string; } /** * List all available projects for Dojo operations. * Helps AI callers discover what projects they can use. */ export declare function dojoListProjects(): ListProjectsOutput; //# sourceMappingURL=dojo.d.ts.map