@omgbase/git-adapter 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,113 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { GitRepo, gitAvailable } from "./git.js";
3
+ import { GitHistoryReader } from "./reader.js";
4
+ import { GIT_PROVIDER, gitConfigFor, readGitConfig } from "./config.js";
5
+ import { fetchRemote, pushAfterCheckpoint, pushBranch, reconcileRemote, remoteSet, remoteStatus, remoteSummary, remoteSync, remoteUnset, resolveConflict } from "./remote.js";
6
+ import { commitSnapshot, runCheckpoint } from "./worker.js";
7
+ import { clearGitCursor } from "./cursor.js";
8
+ import { ensureExcluded } from "./exclude.js";
9
+ // The git history provider (spec/store §5.12; modular-core Phase 6a): the
10
+ // `HistoryProvider` the composition layer registers under the name `git`
11
+ // (`registerHistoryProvider("git", gitHistoryProvider(opts))` in the CLI's entry
12
+ // point, the MCP host and the fixture runners). One instance per `Store`; the
13
+ // bare repository of a repository lives at `<workspace>/.omgbase/git`
14
+ // (`config.git_dir`, relative to the workspace root — the parent of the
15
+ // store's `omgbaseDir`).
16
+ /** The committer identity in production. The author's name is the commit's actor when it has one. */
17
+ export const DEFAULT_IDENTITY = { name: "omgbase", email: "omgbase@localhost" };
18
+ /** The identity the fixture runners and the CLI under the spec seams use (spec/store §9.4 "The git provider"): pinned shas follow from it. */
19
+ export const FIXTURE_IDENTITY = { name: "omgbase", email: "omgbase@fixture" };
20
+ export class GitHistoryProvider {
21
+ store;
22
+ name = GIT_PROVIDER;
23
+ identity;
24
+ constructor(store, opts = {}) {
25
+ this.store = store;
26
+ this.identity = opts.identity ?? DEFAULT_IDENTITY;
27
+ }
28
+ available() {
29
+ if (this.store.omgbaseDir === null)
30
+ return { ok: false, reason: "the store has no workspace directory to keep a bare repository under" };
31
+ return gitAvailable();
32
+ }
33
+ /** The workspace root: the parent of `.omgbase`. */
34
+ root() {
35
+ return dirname(this.store.omgbaseDir);
36
+ }
37
+ repoOf(config) {
38
+ return new GitRepo(resolve(this.root(), config.git_dir));
39
+ }
40
+ context(repoId, config) {
41
+ return { store: this.store, repoId, git: this.repoOf(config), config, identity: this.identity };
42
+ }
43
+ reader(repoId, config) {
44
+ const c = readGitConfig(config);
45
+ return new GitHistoryReader(this.store, repoId, this.repoOf(c), c);
46
+ }
47
+ enable(repoId, commit, opts) {
48
+ const config = gitConfigFor({ ...(typeof opts.branch === "string" ? { branch: opts.branch } : {}), ...(opts.cadence !== undefined ? { cadence: opts.cadence } : {}) });
49
+ const ctx = this.context(repoId, config);
50
+ ctx.git.init(config.branch);
51
+ ensureExcluded(this.root());
52
+ const snap = commitSnapshot(ctx, commit.ts, commit);
53
+ return { config, baseline: { revisions: snap.documents, tip: snap.tip } };
54
+ }
55
+ purge(repoId, config) {
56
+ const c = readGitConfig(config);
57
+ const git = this.repoOf(c);
58
+ const removed = git.remove();
59
+ clearGitCursor(this.store.db, repoId);
60
+ return { removed, path: git.gitDir };
61
+ }
62
+ checkpoint(repoId, config, opts = {}) {
63
+ const ctx = this.context(repoId, readGitConfig(config));
64
+ const result = runCheckpoint(ctx, opts);
65
+ // `push: "on-checkpoint"` (17.6): the push follows a checkpoint that committed; its outcome rides on the receipt.
66
+ const push = pushAfterCheckpoint(ctx, opts.now ?? new Date().toISOString(), result.committed);
67
+ return push === undefined ? result : { ...result, push };
68
+ }
69
+ // ---- the remote half (17.6, Phase 6b; `remote.ts`) ----
70
+ remoteSummary(repoId, config) {
71
+ return remoteSummary(this.context(repoId, readGitConfig(config)));
72
+ }
73
+ remoteSync(repoId, config, opts) {
74
+ return remoteSync(this.context(repoId, readGitConfig(config)), opts.now, opts.vault);
75
+ }
76
+ /** `history remote set`: returns the config as written to the row. */
77
+ remoteSet(repoId, config, options, now = new Date().toISOString()) {
78
+ return remoteSet(this.context(repoId, readGitConfig(config)), options, now);
79
+ }
80
+ /** `history remote unset`. */
81
+ remoteUnset(repoId, config, now = new Date().toISOString()) {
82
+ return remoteUnset(this.context(repoId, readGitConfig(config)), now);
83
+ }
84
+ /** `history remote status` (local refs only). */
85
+ remoteStatus(repoId, config) {
86
+ return remoteStatus(this.context(repoId, readGitConfig(config)));
87
+ }
88
+ /** `history push`. */
89
+ push(repoId, config, now = new Date().toISOString()) {
90
+ return pushBranch(this.context(repoId, readGitConfig(config)), now);
91
+ }
92
+ /** `history fetch`: inbound content through `vault`. */
93
+ fetch(repoId, config, vault, now = new Date().toISOString()) {
94
+ return fetchRemote(this.context(repoId, readGitConfig(config)), now, vault);
95
+ }
96
+ /** `history remote resolve <path> --ours|--theirs`. */
97
+ resolve(repoId, config, vault, path, choice, now = new Date().toISOString()) {
98
+ return resolveConflict(this.context(repoId, readGitConfig(config)), now, vault, path, choice);
99
+ }
100
+ /** `history remote reconcile --rebase|--merge|--adopt|--fork`. */
101
+ reconcile(repoId, config, vault, mode, now = new Date().toISOString()) {
102
+ return reconcileRemote(this.context(repoId, readGitConfig(config)), now, vault, mode);
103
+ }
104
+ /** The bare repository of a repository (the measurement script, tests). */
105
+ gitRepo(config) {
106
+ return this.repoOf(readGitConfig(config));
107
+ }
108
+ }
109
+ /** The factory the composition layer registers: `registerHistoryProvider("git", gitHistoryProvider())`. */
110
+ export function gitHistoryProvider(opts = {}) {
111
+ return (store) => new GitHistoryProvider(store, opts);
112
+ }
113
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1,44 @@
1
+ import type { Store, HistoryReader, RevisionQueryRow, DocsReadAtResult, DocHistoryResult, NodeChange, DiffEntry, DocDiffUnified } from "@omgbase/core";
2
+ import { type GitRepo } from "./git.js";
3
+ import type { GitHistoryConfig } from "./config.js";
4
+ export declare class GitHistoryReader implements HistoryReader {
5
+ private readonly store;
6
+ private readonly repoId;
7
+ private readonly git;
8
+ private readonly config;
9
+ constructor(store: Store, repoId: string, git: GitRepo, config: GitHistoryConfig);
10
+ private parse;
11
+ /** Every version of one document, oldest first, with the blob's sha256 (versions whose path is not in the commit's tree — superseded within a pending span — are dropped). */
12
+ private versionsOf;
13
+ private withHashes;
14
+ private toVersion;
15
+ private toRow;
16
+ private headOf;
17
+ /** The bytes of `docId` at `rev`, or null when `rev` is a head token (or an unknown form) the commits do not name; throws the table's answer for a sha. */
18
+ private bytesAt;
19
+ readAtRevision(docId: string, revId: string): DocsReadAtResult | null;
20
+ blocksHistory(_blockId: string, _opts?: {
21
+ limit?: number;
22
+ }): NodeChange[];
23
+ diffBlocks(_docId: string, _fromRev: string, _toRev: string): DiffEntry[];
24
+ diffUnified(docId: string, fromRev: string, toRev: string): string;
25
+ docDiffUnified(docId: string, opts?: {
26
+ fromRev?: string;
27
+ toRev?: string;
28
+ label?: string;
29
+ }): DocDiffUnified;
30
+ docsHistory(repoId: string, opts?: {
31
+ pathGlob?: string;
32
+ doc?: string;
33
+ includeDeleted?: boolean;
34
+ limit?: number;
35
+ }): DocHistoryResult;
36
+ dispositionCounts(_commitId: string): {
37
+ kind: string;
38
+ n: number;
39
+ }[] | null;
40
+ revisionRows(repoId: string): RevisionQueryRow[];
41
+ docRevisionRows(docId: string): RevisionQueryRow[];
42
+ hasRevision(revId: string): boolean;
43
+ uncapturedHeads(repoId: string, since: string): number;
44
+ }
@@ -0,0 +1,190 @@
1
+ import { CapabilityUnavailable, EngineError, RevisionNotFound, TargetMissingRevision, inCoverage, listHistoryDocs, readCapability, referencePath, sha256, unifiedDiff, unretainedHeadVersion } from "@omgbase/core";
2
+ import { SHA_RE } from "./git.js";
3
+ import { parseMessage, subjectOf } from "./trailers.js";
4
+ import { tipPosition } from "./worker.js";
5
+ export class GitHistoryReader {
6
+ store;
7
+ repoId;
8
+ git;
9
+ config;
10
+ constructor(store, repoId, git, config) {
11
+ this.store = store;
12
+ this.repoId = repoId;
13
+ this.git = git;
14
+ this.config = config;
15
+ }
16
+ // ---- versions ----
17
+ parse(records, docId) {
18
+ const out = [];
19
+ for (const r of records) {
20
+ const t = parseMessage(r.message);
21
+ if (!t)
22
+ continue;
23
+ for (const d of t.docs) {
24
+ if (docId !== null && d.docId !== docId)
25
+ continue;
26
+ if (d.head === null)
27
+ continue; // a deletion mints no version
28
+ out.push({ sha: r.sha, trailers: t, doc: d, subject: subjectOf(r.message) });
29
+ }
30
+ }
31
+ return out;
32
+ }
33
+ /** Every version of one document, oldest first, with the blob's sha256 (versions whose path is not in the commit's tree — superseded within a pending span — are dropped). */
34
+ versionsOf(docId) {
35
+ if (!this.git.exists())
36
+ return [];
37
+ const versions = this.parse(this.git.log(this.config.branch, { grep: `omgbase-doc: ${docId} ` }), docId);
38
+ return this.withHashes(versions);
39
+ }
40
+ withHashes(versions) {
41
+ const blobs = this.git.blobsAt(versions.map((v) => `${v.sha}:${v.doc.path}`));
42
+ const out = [];
43
+ versions.forEach((v, i) => {
44
+ const b = blobs[i];
45
+ if (b)
46
+ out.push({ ...v, hash: sha256(b).toString("hex") });
47
+ });
48
+ return out;
49
+ }
50
+ toVersion(v, head) {
51
+ return {
52
+ _rev: v.doc.head,
53
+ _seq: v.trailers.to,
54
+ _commit: v.sha,
55
+ _ts: v.trailers.ts,
56
+ _origin: v.trailers.origin,
57
+ _actor: v.trailers.actor,
58
+ _hash: v.hash,
59
+ _is_current: v.doc.head === head,
60
+ _archived: false,
61
+ retained: true,
62
+ };
63
+ }
64
+ toRow(v, head) {
65
+ return {
66
+ rev_id: v.doc.head,
67
+ doc_id: v.doc.docId,
68
+ seq: v.trailers.to,
69
+ rendered_hash: Buffer.from(v.hash, "hex"),
70
+ path: v.doc.path,
71
+ commit_id: v.sha,
72
+ ts: v.trailers.ts,
73
+ origin: v.trailers.origin ?? "observed",
74
+ actor: v.trailers.actor,
75
+ reason: v.subject,
76
+ is_current: v.doc.head === head ? 1 : 0,
77
+ archived: 0,
78
+ };
79
+ }
80
+ headOf(docId) {
81
+ return this.store.db.prepare("SELECT head FROM docs WHERE doc_id = ?").get(docId)?.head ?? null;
82
+ }
83
+ // ---- resolving a token (spec/surface §3, the git rows of the decision table) ----
84
+ /** The bytes of `docId` at `rev`, or null when `rev` is a head token (or an unknown form) the commits do not name; throws the table's answer for a sha. */
85
+ bytesAt(docId, rev, fix) {
86
+ if (SHA_RE.test(rev)) {
87
+ if (!this.git.exists() || this.git.objectType(rev) === null)
88
+ throw CapabilityUnavailable.historyPruned(rev, fix);
89
+ if (this.git.objectType(rev) !== "commit")
90
+ throw new TargetMissingRevision(rev, docId);
91
+ const t = parseMessage(this.git.message(rev));
92
+ if (!t)
93
+ throw new TargetMissingRevision(rev, docId);
94
+ const coverage = readCapability(this.store.db, this.repoId, "history")?.coverage ?? [];
95
+ if (coverage.length > 0 && !inCoverage(coverage, t.ts))
96
+ throw CapabilityUnavailable.historyOutsideCoverage(rev, coverage[0].from, fix);
97
+ const d = t.docs.find((x) => x.docId === docId && x.head !== null);
98
+ if (!d)
99
+ throw new TargetMissingRevision(rev, docId);
100
+ const bytes = this.git.blobsAt([`${rev}:${d.path}`])[0];
101
+ if (!bytes)
102
+ throw CapabilityUnavailable.historyPruned(rev, fix);
103
+ return { path: d.path, bytes, sha: rev };
104
+ }
105
+ if (/^r_/.test(rev)) {
106
+ const v = this.versionsOf(docId).find((x) => x.doc.head === rev);
107
+ if (!v)
108
+ return null;
109
+ const bytes = this.git.blobsAt([`${v.sha}:${v.doc.path}`])[0];
110
+ if (!bytes)
111
+ return null;
112
+ return { path: v.doc.path, bytes, sha: v.sha };
113
+ }
114
+ return null;
115
+ }
116
+ // ---- HistoryReader ----
117
+ readAtRevision(docId, revId) {
118
+ const at = this.bytesAt(docId, revId, `docs_read ${JSON.stringify({ _id: docId })}`);
119
+ if (!at)
120
+ return null;
121
+ return { _id: docId, _path: referencePath(at.path), rev: revId, _raw: at.bytes.toString("utf8"), renderedHashMatch: true };
122
+ }
123
+ blocksHistory(_blockId, _opts) {
124
+ throw CapabilityUnavailable.historyUnsupported("git", "blocks_history", "docs_history");
125
+ }
126
+ diffBlocks(_docId, _fromRev, _toRev) {
127
+ throw CapabilityUnavailable.historyUnsupported("git", "docs_diff", "docs_diff_unified");
128
+ }
129
+ diffUnified(docId, fromRev, toRev) {
130
+ const text = (rev) => {
131
+ const at = this.bytesAt(docId, rev, undefined);
132
+ if (!at)
133
+ throw new RevisionNotFound(docId, rev);
134
+ return at.bytes.toString("utf8");
135
+ };
136
+ return unifiedDiff(text(fromRev), text(toRev));
137
+ }
138
+ docDiffUnified(docId, opts = {}) {
139
+ const versions = this.versionsOf(docId);
140
+ const to = opts.toRev ?? versions[versions.length - 1]?.sha;
141
+ const from = opts.fromRev ?? versions[versions.length - 2]?.sha ?? versions[versions.length - 1]?.sha;
142
+ if (!to || !from)
143
+ throw new EngineError("target_missing", `no revisions to diff for ${opts.label ?? docId}`);
144
+ const path = this.store.db.prepare("SELECT path FROM docs WHERE doc_id = ?").get(docId)?.path;
145
+ return { _id: docId, _path: path === undefined ? "" : referencePath(path), from, to, diff: this.diffUnified(docId, from, to) };
146
+ }
147
+ docsHistory(repoId, opts = {}) {
148
+ const { page, truncated } = listHistoryDocs(this.store, repoId, opts);
149
+ const docs = page.map((d) => {
150
+ const versions = this.versionsOf(d.doc_id).map((v) => this.toVersion(v, d.head));
151
+ if (d.head !== null && !versions.some((v) => v._rev === d.head))
152
+ versions.push(unretainedHeadVersion(this.store, d.doc_id, d.head));
153
+ return { _id: d.doc_id, _path: referencePath(d.path), deleted: d.deleted_at !== null, currentRev: d.head, versions };
154
+ });
155
+ return { docs, truncated, coverage: readCapability(this.store.db, repoId, "history")?.coverage ?? [] };
156
+ }
157
+ dispositionCounts(_commitId) {
158
+ return null;
159
+ }
160
+ revisionRows(repoId) {
161
+ if (!this.git.exists())
162
+ return [];
163
+ const heads = new Map(this.store.db.prepare("SELECT doc_id, head FROM docs WHERE repo_id = ?").all(repoId).map((r) => [r.doc_id, r.head]));
164
+ const versions = this.withHashes(this.parse(this.git.log(this.config.branch), null).filter((v) => heads.has(v.doc.docId)));
165
+ return versions.map((v) => this.toRow(v, heads.get(v.doc.docId) ?? null)).sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : a.rev_id < b.rev_id ? -1 : a.rev_id > b.rev_id ? 1 : 0));
166
+ }
167
+ docRevisionRows(docId) {
168
+ const head = this.headOf(docId);
169
+ return this.versionsOf(docId).map((v) => this.toRow(v, head));
170
+ }
171
+ hasRevision(revId) {
172
+ if (!this.git.exists())
173
+ return false;
174
+ if (SHA_RE.test(revId))
175
+ return this.git.objectType(revId) === "commit" && parseMessage(this.git.message(revId)) !== null;
176
+ if (/^r_[A-Za-z0-9_]+$/.test(revId))
177
+ return this.git.log(this.config.branch, { regex: `^omgbase-doc: .* ${revId}$` }).length > 0;
178
+ return false;
179
+ }
180
+ uncapturedHeads(repoId, since) {
181
+ const position = this.git.exists() ? tipPosition(this.git, this.config.branch) : null;
182
+ const db = this.store.db;
183
+ if (!position)
184
+ return db.prepare("SELECT count(*) c FROM docs WHERE repo_id = ? AND deleted_at IS NULL AND head IS NOT NULL AND updated_at >= ?").get(repoId, since).c;
185
+ return db
186
+ .prepare("SELECT count(*) c FROM docs d WHERE d.repo_id = ? AND d.deleted_at IS NULL AND d.head IS NOT NULL AND d.updated_at >= ? AND EXISTS (SELECT 1 FROM feed_entries e WHERE e.repo_id = d.repo_id AND e.head = d.head AND e.seq > ?)")
187
+ .get(repoId, since, position.seq).c;
188
+ }
189
+ }
190
+ //# sourceMappingURL=reader.js.map
@@ -0,0 +1,113 @@
1
+ import type { HistoryPushResult, HistoryRemoteState, HistoryRemoteSummary, HistoryVaultSink, HistoryRemoteSyncResult } from "@omgbase/core";
2
+ import { type GitHistoryConfig, type GitRemoteOptions } from "./config.js";
3
+ import { type WorkerContext } from "./worker.js";
4
+ export declare const GIT_REMOTE_DIVERGED_ALERT = "git_remote_diverged";
5
+ export declare const GIT_MERGE_CONFLICT_ALERT = "git_merge_conflict";
6
+ export declare const GIT_REMOTE_FORKED_ALERT = "git_remote_forked";
7
+ export type FetchState = "nothing" | "fast-forward" | "merged" | "conflict" | "diverged";
8
+ export type ReconcileMode = "rebase" | "merge" | "adopt" | "fork";
9
+ export type ResolveChoice = "ours" | "theirs";
10
+ export interface RemoteStatus {
11
+ remote: string;
12
+ branch: string;
13
+ local_tip: string | null;
14
+ remote_tip: string | null;
15
+ pushed_through: number | null;
16
+ state: HistoryRemoteState;
17
+ }
18
+ export interface FetchResult {
19
+ state: FetchState;
20
+ /** the remote's tip after the fetch (null when the remote branch does not exist yet) */
21
+ remote_tip: string | null;
22
+ /** `diverged` alone: the tip we last knew the remote at (null for unrelated histories) */
23
+ old_remote_tip?: string | null;
24
+ /** our branch tip after the fetch */
25
+ tip: string | null;
26
+ /** the paths written into the vault from the remote (sidecars included), in path order */
27
+ applied: string[];
28
+ /** the conflicting paths (a sidecar beside each) */
29
+ conflicts: string[];
30
+ }
31
+ export interface ResolveResult {
32
+ path: string;
33
+ choice: ResolveChoice;
34
+ remaining: string[];
35
+ /** the merge commit written when the last conflict was settled, else null */
36
+ merged: string | null;
37
+ tip: string | null;
38
+ }
39
+ export interface ReconcileResult {
40
+ mode: ReconcileMode;
41
+ tip: string | null;
42
+ /** the previous tip, kept on `refs/omgbase/backup/<ts>` (rebase, merge, adopt) */
43
+ backup: string | null;
44
+ /** `--rebase`: the commits rewritten; `--merge`: 1 when the merge commit was written, 0 while conflicts stand */
45
+ rewritten: number;
46
+ /** `--fork`: the branch pushed */
47
+ fork: string | null;
48
+ applied: string[];
49
+ conflicts: string[];
50
+ }
51
+ /** `<ts>` as a ref/branch segment: `20260927T000000Z`. */
52
+ export declare function tsSegment(ts: string): string;
53
+ /** The sidecar beside `path` holding the OTHER side's version: `<stem>.remote-<short>.md` / `<stem>.local-<short>.md` (a path without `.md` keeps its name and gains the suffix). */
54
+ export declare function sidecarPath(path: string, side: "remote" | "local", sha: string): string;
55
+ /**
56
+ * Are push and fetch PAUSED? While the remote stands rewritten
57
+ * (`git_remote_diverged` carrying `old_remote_tip`) or forked, nothing moves
58
+ * either way until a remediation; a plain rejected push (no `old_remote_tip`)
59
+ * pauses nothing — `history fetch` merges the remote's commits and clears it.
60
+ */
61
+ export declare function pausedBy(ctx: WorkerContext): {
62
+ code: string;
63
+ detail: Record<string, unknown>;
64
+ } | null;
65
+ export declare function rejectedMessage(remote: string, branch: string, remoteTip: string | null, localTip: string | null): string;
66
+ export declare function rewrittenMessage(remote: string, branch: string, remoteTip: string | null, oldRemoteTip: string | null): string;
67
+ export declare function conflictMessage(remote: string, branch: string, remoteTip: string | null, paths: string[]): string;
68
+ export declare function forkedMessage(remote: string, branch: string, fork: string): string;
69
+ /** `history remote set`: the remote in the bare repository and the row's `remote` member; returns the config as written. */
70
+ export declare function remoteSet(ctx: WorkerContext, opts: GitRemoteOptions, now: string): GitHistoryConfig;
71
+ /** `history remote unset`: the remote, its tracking and bookkeeping refs, the alerts about it and the row's binding leave; the pushed record is cleared. */
72
+ export declare function remoteUnset(ctx: WorkerContext, now: string): GitHistoryConfig;
73
+ /** `history remote status` (local refs only — no network): where the two sides stand. */
74
+ export declare function remoteStatus(ctx: WorkerContext): RemoteStatus;
75
+ /** `version.capabilities.history.remote`. */
76
+ export declare function remoteSummary(ctx: WorkerContext): HistoryRemoteSummary | null;
77
+ /**
78
+ * `history push`: the content branch (`refs/heads/<b>:refs/heads/<rb>`) and,
79
+ * when it exists, the ids ref — never `--force`. A rejected push raises
80
+ * `git_remote_diverged` (warning; `{ remote, branch, local_tip, remote_tip }`)
81
+ * and throws it; the ids ref's rejection alone is reported (`ids:
82
+ * "rejected"`) — two engines writing one remote's identities is 6c's.
83
+ */
84
+ export declare function pushBranch(ctx: WorkerContext, now: string): HistoryPushResult & {
85
+ ids: "pushed" | "rejected" | "none";
86
+ };
87
+ /**
88
+ * `history fetch` (spec/store §5.12 "Fetch"): fetch `<name>/<rb>` into the
89
+ * tracking ref (and the remote's ids ref when it has one), then — nothing new:
90
+ * `nothing`; the remote rewritten (its tip does not descend from the tip we
91
+ * last knew, or unrelated histories): `diverged` — the alert, the old tip
92
+ * kept, push and fetch paused; we are behind: the remote's tree changes into
93
+ * the vault through the sink, then our branch fast-forwarded; both moved: the
94
+ * three-way merge — clean: the remote's changes into the vault and one merge
95
+ * commit; conflicts: the remote's versions beside ours, the alert, no merge
96
+ * commit until `resolve`.
97
+ */
98
+ export declare function fetchRemote(ctx: WorkerContext, now: string, vault: HistoryVaultSink): FetchResult;
99
+ /**
100
+ * `history remote resolve <path> --ours|--theirs`: the sidecar leaves the
101
+ * vault (`--theirs` first writes the remote's version over ours); the path
102
+ * leaves the alert; when none remains the pending merge — if one waits — is
103
+ * committed over the resolution the worker just checkpointed.
104
+ */
105
+ export declare function resolveConflict(ctx: WorkerContext, now: string, vault: HistoryVaultSink, path: string, choice: ResolveChoice): ResolveResult;
106
+ /** `history remote reconcile --rebase|--merge|--adopt|--fork`. */
107
+ export declare function reconcileRemote(ctx: WorkerContext, now: string, vault: HistoryVaultSink, mode: ReconcileMode): ReconcileResult;
108
+ /** `remoteSync` of the provider seam (spec/sync §4.3 steps 7–8): push on `periodic`, fetch on `periodic`; both skipped while paused or while a merge waits. */
109
+ export declare function remoteSync(ctx: WorkerContext, now: string, vault: HistoryVaultSink): HistoryRemoteSyncResult | null;
110
+ /** `push: "on-checkpoint"`: after a checkpoint that committed, the push — its error reported on the receipt, never thrown (the checkpoint stands). */
111
+ export declare function pushAfterCheckpoint(ctx: WorkerContext, now: string, committed: number): HistoryPushResult | {
112
+ error: string;
113
+ } | undefined;