/** * TrellisVCS Engine * * The composition root that ties together the trellis-core kernel, * the file watcher, the ingestion pipeline, and VCS middleware. * * Usage: * const engine = new TrellisVcsEngine({ rootPath: '/path/to/repo' }); * await engine.init(); // scan + create initial ops * engine.watch(); // start continuous monitoring * engine.stop(); // stop watcher */ import { EAVStore } from './core/store/eav-store.js'; import type { IdentityResolver } from './identity/signing-middleware.js'; import type { OpProvenance } from './core/persist/canonical-op.js'; import type { VcsOp, TrellisVcsConfig } from './vcs/types.js'; import { BlobStore } from './vcs/blob-store.js'; import { BlobResolver } from './vcs/blob-resolver.js'; import type { EngineContext } from './vcs/engine-context.js'; import * as branchMod from './vcs/branch.js'; import * as milestoneMod from './vcs/milestone.js'; import * as checkpointMod from './vcs/checkpoint.js'; import * as diffMod from './vcs/diff.js'; import * as mergeMod from './vcs/merge.js'; import * as issueMod from './vcs/issue.js'; import * as testRunnerMod from './vcs/test-runner.js'; import * as storeMod from './vcs/store.js'; import type { Atom } from './core/store/eav-store.js'; import type { EntityRecord } from './core/kernel/trellis-kernel.js'; import * as decisionMod from './decisions/index.js'; import { IdeaGarden } from './garden/index.js'; import type { ParseResult, SemanticPatch } from './semantic/types.js'; import type { ProjectContext } from './scaffold/infer.js'; import type { OpLog } from './vcs/op-log.js'; import * as laneMod from './vcs/lane.js'; import type { LaneMeta } from './vcs/lane.js'; import * as lanePromoteMod from './vcs/lane-promote.js'; import type { LanePromoteResult } from './vcs/lane-promote.js'; import type { MaterializationStats } from './vcs/lane-materialize.js'; import * as laneCoherenceMod from './vcs/lane-coherence.js'; import { type GitSyncResult } from './git/git-sync.js'; export interface InitProgress { phase: 'discovering' | 'hashing' | 'recording' | 'scaffolding' | 'done'; current: number; total: number; message: string; } export interface InitRepoOptions { onProgress?: (progress: InitProgress) => void; indexWorkspace?: boolean; } export interface InitRepoResult { opsCreated: number; filesIndexed: number; indexWorkspace: boolean; context: ProjectContext; } export interface IndexWorkspaceResult { opsCreated: number; filesIndexed: number; } export type IntegrateOpRejectReason = 'invalid-kind' | 'hash-mismatch' | 'unauthorized' | 'missing-dependency' | 'apply-failed'; export interface IntegrateOpRejection { op: VcsOp; reason: IntegrateOpRejectReason; message: string; } export interface IntegrateOpsResult { applied: number; skipped: number; rejected: IntegrateOpRejection[]; } export declare class TrellisVcsEngine { private config; /** Optional identity resolver for ingest-time signature verification (ADR 0022 Phase 3). */ private identityResolver?; /** Local signing material; when present, the engine mints signed auth ops. */ private signingMaterial?; private store; private opLog; private watcher; private ingestion; private agentId; /** ADR 0021 §2 — stamped onto every op this engine mints. */ private provenance; private currentBranch; private checkpointOpCount; private checkpointThreshold; private _pendingAutoCheckpoint; private _blobStore; private _blobResolver; private activeLaneId?; private activeLaneLog; private integrationCache; private materializationStats; private watchReconcileOnRestart; constructor(opts: { rootPath: string; agentId?: string; /** * Optional custom op-log backend. Defaults to a filesystem-backed * {@link JsonOpLog} at `/.trellis/ops.json`. Browser hosts * can inject an {@link IdbOpLog} or other {@link OpLog} implementation. * * Callers that inject a non-filesystem backend are responsible for * awaiting `opLog.load()` before passing it in if their backend's * load is asynchronous. */ opLog?: OpLog; /** * Provenance stamped onto every op this engine mints (ADR 0021 §2). * Set per construction site — the engine is built by the CLI, the MCP * server and the UI server, each of which knows its own surface. * Defaults to the honest `{ actorType: 'machine', origin: 'sdk' }`. */ provenance?: OpProvenance; /** * Optional identity resolver used to cryptographically verify op * signatures at the ingest boundary (ADR 0022 Phase 3). When present, * authorization-bearing ops (grant/zone) must carry a valid signature; * when absent, the kernel still requires a signature envelope to exist * (deny-by-default for unattributable auth ops). */ identityResolver?: IdentityResolver; /** * Local signing material (ADR 0022 Phase 3). When present, the engine * mints signed authorization ops so a peer's ingest boundary can verify * them. Constructed by hosts that have an identity; absent for * identity-less repos, where no resolver is wired either. */ signingMaterial?: { privateKey: string; identityEntityId: string; signedWith: string; }; } & Partial); private readPersistedConfig; private writePersistedConfig; private indexExistingFiles; /** * Initialize a new TrellisVCS repo. Creates .trellis/ directory and config. */ initRepo(opts?: InitRepoOptions): Promise; /** * Open an existing TrellisVCS repo. Loads ops and replays into EAV store. */ open(): { opsReplayed: number; }; /** * Index all untracked files currently on disk into the Trellis graph. */ indexWorkspace(opts?: { onProgress?: (progress: InitProgress) => void; }): Promise; /** * Start watching the filesystem for changes. */ watch(opts?: { reconcileExisting?: boolean; }): void; private getWatcherRoot; /** * Directory where agents should run tests and edit files for a lane. * Uses the lane worktree when `lanes.worktreeBind` is enabled. */ getEditRoot(laneId?: string): string; private isWorktreeBindEnabled; private rebindWatcher; private startWatcherAt; private provisionLaneWorktree; private materializeLaneWorktree; private removeLaneWorktree; /** * Stop watching. */ stop(): void; /** * Returns all ops in the causal stream. */ getOps(): VcsOp[]; /** * Integrate externally supplied ops exactly as received. * * This is the sync ingestion primitive: callers are responsible for * exchanging, validating, and ordering ops before handing them to the * engine. The engine dedupes by hash, materializes each new op, and avoids * creating local branch-advance follow-up ops for remote history. */ integrateOps(ops: VcsOp[]): Promise; /** * Returns the total number of ops. */ getOpCount(): number; /** * Returns the EAV store for direct querying. */ getStore(): EAVStore; /** * Returns the blob store for content retrieval. */ getBlobStore(): BlobStore | null; /** * Returns the blob resolver (wraps BlobStore with git fallback). */ getBlobResolver(): BlobResolver | null; /** * Returns the current status: tracked files, last op, branch info. */ status(): { branch: string; totalOps: number; trackedFiles: number; lastOp: VcsOp | undefined; recentOps: VcsOp[]; }; /** * Returns op history, optionally filtered by file path. */ log(opts?: { limit?: number; filePath?: string; }): VcsOp[]; /** * Returns all tracked file paths and their content hashes. */ trackedFiles(): Array<{ path: string; contentHash: string | undefined; }>; /** * Returns the root path of the repository. */ getRootPath(): string; /** * Checks if a .trellis directory exists at the root path. */ static isRepo(rootPath: string): boolean; static repair(rootPath: string, opts?: import('./vcs/op-log.js').RepairOptions): import('./vcs/op-log.js').RepairResult; createBranch(name: string): Promise; switchBranch(name: string): void; listBranches(): branchMod.BranchInfo[]; deleteBranch(name: string): Promise; getCurrentBranch(): string; /** * Integration branch head op hash from the materialized store (ADR 0004). * Pass `principal` to resolve a single writer's per-principal ref zone * (ADR 0022 §4) — two writers on the same personal branch keep separate heads. */ getBranchHeadOpHash(branchName?: string, principal?: string): string | undefined; /** * Engine context for the zone capability module (ADR 0022). * * Capability writes mint ops through this rather than touching the store, * so grants survive a reboot, replicate to peers, and are hash-covered. */ capabilityContext(): EngineContext; getActiveLaneId(): string | undefined; /** Whether milestones should auto-commit to git on create (config opt-in). */ get milestoneAutoCommit(): boolean; /** Last enter/leave/open materialization counters (W4). */ getMaterializationStats(): MaterializationStats; listLanes(): LaneMeta[]; getIntegrationOpCount(): number; getLaneOpCount(laneId: string): number; getLaneMeta(laneId: string): LaneMeta | undefined; /** Active lane linked to an issue, if any. */ findLaneForIssue(issueId: string): LaneMeta | undefined; /** Active lane bound to a Cursor/agent session id. */ findLaneForSession(sessionId: string): LaneMeta | undefined; /** * Find or create a lane for a session. Used by Cursor hooks for tab isolation. */ ensureSessionLane(opts: { sessionId: string; issueId?: string; enter?: boolean; }): Promise; /** * Mirror integration branch file state to the primary git worktree and commit. */ syncGitIntegration(opts?: { message?: string; push?: boolean; lane?: LaneMeta; laneOps?: VcsOp[]; /** When true, sync even if git.syncOnPromote is false. */ force?: boolean; }): GitSyncResult; /** * Enter lane from TRELLIS_LANE_ID when set (hooks/MCP/subprocess agents). */ syncEnvLaneFromEnv(): Promise; /** Ops and touched files in a lane journal (for `trellis lane diff`). */ summarizeLane(laneId: string): { meta: LaneMeta; ops: VcsOp[]; filePaths: string[]; integrationHead?: string; coherence: laneCoherenceMod.LaneCoherence; }; /** * Fork a new agent lane from the current integration branch head. * Writes `vcs:laneCreate` to the integration journal only. */ createLane(opts?: { fromBranch?: string; targetBranch?: string; issueId?: string; sessionId?: string; worktreePath?: string; name?: string; parentLaneId?: string; forkKind?: laneMod.LaneForkKind; }): Promise; /** * Open a fresh domain-scoped lane and enter it (TRL-117). * Leaves the current lane if any. Does not require an issue — promote * boundary is the new lane itself. Parent lineage is recorded as sibling. */ splitLane(opts?: { name?: string; fromBranch?: string; sessionId?: string; }): Promise<{ meta: LaneMeta; splitFrom?: string; }>; forkLane(parentLaneId: string, opts?: { sessionId?: string; issueId?: string; worktreePath?: string; forkKind?: laneMod.LaneForkKind; }): Promise; /** * Enter a lane: route subsequent writes to its isolated journal. */ enterLane(laneId: string): Promise; /** Leave the active lane and restore integration-only materialized state. */ leaveLane(): Promise; /** Mark a lane dropped (leaves first if it is the active lane). */ dropLane(laneId: string): Promise; promoteLane(laneId: string, opts?: { dryRun?: boolean; explain?: boolean; toBranch?: string; requireTest?: boolean; /** Break a stale or abandoned promote lock (dangerous if another promote is live). */ forceLock?: boolean; /** Milestone narrative (TRL-117). Auto-drafted when omitted unless milestone:false. */ message?: string; /** Set false to promote without creating a milestone. Default true. */ milestone?: boolean; }): Promise; /** * Promote active issue lane before close when it has unpromoted journal ops. * No-ops when the lane has nothing replayable onto integration (e.g. only * testRun / claim metadata) — that still satisfies the promote boundary. */ private autoPromoteIssueLaneBeforeClose; createMilestone(message: string, opts?: { fromOpHash?: string; toOpHash?: string; }): Promise; listMilestones(): milestoneMod.MilestoneInfo[]; createCheckpoint(trigger?: checkpointMod.CheckpointTrigger): Promise; listCheckpoints(): checkpointMod.CheckpointInfo[]; setCheckpointThreshold(threshold: number): void; /** * Diff two branches by comparing their file states. */ diffBranches(branchA: string, branchB: string): diffMod.DiffResult; /** * Diff between two op hashes in the causal stream. */ diffOps(fromHash: string, toHash: string): diffMod.DiffResult; /** * Diff the current state against a specific op hash (e.g. a milestone). */ diffFromOp(opHash: string): diffMod.DiffResult; /** * Three-way merge: merge source branch state into current branch state. * Uses the fork-point (branch creation op) as the common ancestor. */ mergeBranch(sourceBranch: string): mergeMod.MergeResult; private _parsers; /** * Parse a file's content into AST-level entities. */ parseFile(content: string, filePath: string): ParseResult | null; /** * Compute semantic diff between two versions of a file. */ semanticDiff(oldContent: string, newContent: string, filePath: string): SemanticPatch[]; private _garden; /** * Get the Idea Garden instance for exploring abandoned work. */ garden(): IdeaGarden; createIssue(title: string, opts?: issueMod.IssueCreateOptions): Promise; updateIssue(id: string, updates: { title?: string; description?: string; priority?: 'critical' | 'high' | 'medium' | 'low'; labels?: string[]; assignee?: string; status?: 'backlog' | 'queue' | 'in_progress' | 'paused' | 'closed'; parentId?: string | null; }): Promise; /** * Start an issue: optionally create+enter a lane, optionally create+switch to * a branch, emit `vcs:issueStart`, apply start criteria. * * `branch` is separable from `lane` on purpose. Branch creation used to be * unconditional while the lane was opt-out — so a repo that treats branches as * an antipattern (staying on `main`) had to avoid `issue start` entirely, and * avoiding it silently opted every agent out of LANES too, since this is the * only thing that creates one. Agents then shared the main tree and swept each * other's in-flight edits. The lane is the isolation that matters; the branch * is a naming convenience. */ startIssue(id: string, opts?: { lane?: boolean; branch?: boolean; sessionId?: string; }): Promise; pauseIssue(id: string, note: string): Promise; resumeIssue(id: string, opts?: { lane?: boolean; sessionId?: string; }): Promise; closeIssue(id: string, opts?: { confirm?: boolean; push?: boolean; noPromote?: boolean; requireTest?: boolean; }): Promise<{ op?: VcsOp; criteriaResults: issueMod.CriterionResult[]; gitSync?: GitSyncResult; promoteResult?: lanePromoteMod.LanePromoteResult; }>; triageIssue(id: string): Promise; reopenIssue(id: string): Promise; checkCompletionReadiness(): issueMod.CompletionReadiness; assignIssue(id: string, agentId: string): Promise; blockIssue(id: string, blockedById: string): Promise; unblockIssue(id: string, blockedById: string): Promise; addCriterion(issueId: string, description: string, opts?: string | { command?: string; suite?: string; }): Promise; /** Retract an acceptance criterion by its 1-based index in the live list (TRL-1). */ removeCriterion(issueId: string, criterionIndex: number): Promise; setCriterionStatus(issueId: string, criterionIndex: number, status: 'passed' | 'failed' | 'pending'): Promise; runCriteria(issueId: string): Promise; runTests(opts?: { suiteIds?: string[]; laneId?: string; issueId?: string; trigger?: testRunnerMod.TestRunTrigger; }): Promise; listIssues(filters?: issueMod.IssueFilters): issueMod.IssueInfo[]; getIssue(id: string): issueMod.IssueInfo | null; getActiveIssues(): issueMod.IssueInfo[]; createStoreEntity(entityId: string, type: string, attributes?: Record, opts?: storeMod.StoreEntityCreateOptions): Promise; updateStoreEntity(entityId: string, updates: Record): Promise; deleteStoreEntity(entityId: string): Promise; getStoreEntity(entityId: string): EntityRecord | null; listStoreEntities(type?: string, filters?: Record, opts?: { includeVcs?: boolean; }): EntityRecord[]; /** Raw EAV store (materialized from ops.json in VCS repos). */ getEavStore(): EAVStore; addStoreFact(entityId: string, attribute: string, value: Atom): Promise; removeStoreFact(entityId: string, attribute: string, value: Atom): Promise; addStoreLink(sourceId: string, attribute: string, targetId: string): Promise; removeStoreLink(sourceId: string, attribute: string, targetId: string): Promise; recordDecision(input: decisionMod.DecisionInput): Promise; recordRemotePush(info: { remoteName?: string; remoteRepoId?: string; remoteTailHash?: string; remoteByteLength?: number; }): Promise; recordRemotePull(info: { remoteName?: string; remoteRepoId?: string; remoteTailHash?: string; remoteByteLength?: number; }): Promise; queryDecisions(filter?: decisionMod.DecisionFilter): decisionMod.Decision[]; getDecisionChain(entityId: string): decisionMod.Decision[]; getDecision(id: string): decisionMod.Decision | null; private _ctx; private trellisDir; private getActiveJournal; private invalidateIntegrationCache; private loadLaneJournalOps; private refreshMaterializedStore; /** Swap back to cached integration store without replaying the journal. */ private restoreIntegrationOnlyStore; private rebuildStore; private syncIngestionLastOpHash; /** * Tag an op with the lane it was minted in (TRL-102). * * Writes the ENVELOPE field, not `op.vcs`. `createVcsOp` has already hashed * `vcs` by the time we get here, so mutating the payload silently invalidated * the hash — every op in every lane journal failed `verifyVcsOpHash`, and * would be rejected as `hash-mismatch` at any ingest boundary. * * The lane is ambient context, not identity: the same semantic op in two * lanes must hash identically, or peers lose dedup and cherry-pick rewrites * identity. `laneId` is outside the preimage by construction. */ private stampLaneId; private requireActiveLaneLog; private isIssueIntegrationOp; private applyOp; private appendBranchAdvance; private flushAutoCheckpoint; private loadCurrentBranch; private replayOp; } //# sourceMappingURL=engine.d.ts.map