@pellux/goodvibes-sdk 0.36.0 → 0.37.1

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.
Files changed (39) hide show
  1. package/dist/contracts/artifacts/operator-contract.json +1 -1
  2. package/dist/platform/core/compaction-sections.d.ts +8 -0
  3. package/dist/platform/core/compaction-sections.d.ts.map +1 -1
  4. package/dist/platform/core/compaction-sections.js +71 -2
  5. package/dist/platform/core/context-compaction.d.ts +13 -0
  6. package/dist/platform/core/context-compaction.d.ts.map +1 -1
  7. package/dist/platform/core/context-compaction.js +24 -2
  8. package/dist/platform/core/orchestrator-context-runtime.d.ts.map +1 -1
  9. package/dist/platform/core/orchestrator-context-runtime.js +1 -2
  10. package/dist/platform/core/orchestrator-tool-runtime.d.ts.map +1 -1
  11. package/dist/platform/core/orchestrator-tool-runtime.js +30 -2
  12. package/dist/platform/permissions/manager.d.ts.map +1 -1
  13. package/dist/platform/permissions/manager.js +3 -2
  14. package/dist/platform/permissions/prompt.d.ts +8 -1
  15. package/dist/platform/permissions/prompt.d.ts.map +1 -1
  16. package/dist/platform/permissions/types.d.ts +6 -0
  17. package/dist/platform/permissions/types.d.ts.map +1 -1
  18. package/dist/platform/runtime/services.d.ts +2 -0
  19. package/dist/platform/runtime/services.d.ts.map +1 -1
  20. package/dist/platform/runtime/services.js +15 -0
  21. package/dist/platform/tools/index.d.ts +1 -0
  22. package/dist/platform/tools/index.d.ts.map +1 -1
  23. package/dist/platform/version.js +1 -1
  24. package/dist/platform/workspace/checkpoint/index.d.ts +9 -0
  25. package/dist/platform/workspace/checkpoint/index.d.ts.map +1 -0
  26. package/dist/platform/workspace/checkpoint/index.js +7 -0
  27. package/dist/platform/workspace/checkpoint/manager.d.ts +214 -0
  28. package/dist/platform/workspace/checkpoint/manager.d.ts.map +1 -0
  29. package/dist/platform/workspace/checkpoint/manager.js +543 -0
  30. package/dist/platform/workspace/checkpoint/side-git.d.ts +130 -0
  31. package/dist/platform/workspace/checkpoint/side-git.d.ts.map +1 -0
  32. package/dist/platform/workspace/checkpoint/side-git.js +264 -0
  33. package/dist/platform/workspace/checkpoint/types.d.ts +96 -0
  34. package/dist/platform/workspace/checkpoint/types.d.ts.map +1 -0
  35. package/dist/platform/workspace/checkpoint/types.js +20 -0
  36. package/dist/platform/workspace/index.d.ts +1 -0
  37. package/dist/platform/workspace/index.d.ts.map +1 -1
  38. package/dist/platform/workspace/index.js +1 -0
  39. package/package.json +9 -9
@@ -0,0 +1,264 @@
1
+ /**
2
+ * side-git.ts
3
+ *
4
+ * SideGitRunner — a hidden git repository ("side repo") whose object store
5
+ * (GIT_DIR) lives under <workspaceRoot>/.goodvibes/checkpoints/git while its
6
+ * GIT_WORK_TREE is the live workspace itself.
7
+ *
8
+ * This is the dotfiles-bare-repo trick (`git --git-dir=X --work-tree=Y`):
9
+ * git tracks arbitrary files in Y using an object store rooted at X,
10
+ * completely independent of whatever real .git directory Y may or may not
11
+ * already have. It gives us, for free:
12
+ * - content-addressed, deduped storage (unchanged files cost ~nothing)
13
+ * - `git diff` / `git diff --stat` between any two snapshots
14
+ * - whole-tree restore via `read-tree` + `checkout-index`
15
+ * - correct behavior in a workspace that is NOT itself a git repo
16
+ *
17
+ * DO NOT reuse GitService (../../git/service.ts) for this: it binds a single
18
+ * baseDir with no GIT_DIR support, and it fires Pre/Post hook events on
19
+ * every commit/add — automatic silent snapshots must never trigger a user's
20
+ * PreCommit/PostCommit hooks. AgentWorktree (../../agents/worktree.ts) already
21
+ * proves the `simpleGit(...).raw([...])` + explicit env pattern used here.
22
+ *
23
+ * Checkpoints are addressed entirely through our own ref namespace
24
+ * (refs/goodvibes/checkpoints/<id>) and through commit objects created via
25
+ * `commit-tree`, never through the side repo's HEAD/branch. There is no
26
+ * meaningful "current branch" in this design — parent/lineage is tracked in
27
+ * the manifest (manager.ts), not via git HEAD — so there is nothing to leave
28
+ * "detached" and no branch state to pollute.
29
+ */
30
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
31
+ import { join } from 'node:path';
32
+ import { simpleGit } from 'simple-git';
33
+ import { logger } from '../../utils/logger.js';
34
+ import { summarizeError } from '../../utils/error-display.js';
35
+ /** Git's well-known empty-tree object hash, valid in every repository. */
36
+ export const EMPTY_TREE_HASH = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
37
+ /** Ref namespace all workspace checkpoints live under. */
38
+ export const CHECKPOINT_REF_PREFIX = 'refs/goodvibes/checkpoints/';
39
+ /** Fallback identity used for checkpoint commits so this never depends on the user's global git config. */
40
+ const FALLBACK_IDENTITY = { name: 'GoodVibes Checkpoints', email: 'checkpoints@goodvibes.local' };
41
+ /**
42
+ * Environment variable names that simple-git's built-in vulnerability scanner
43
+ * treats as unsafe (it would otherwise throw on every raw call rather than
44
+ * risk spawning an interactive editor/pager/askpass/proxy). We never need
45
+ * any of these — every commit here is created via `commit-tree -m <message>`,
46
+ * never a bare `commit`/`rebase -i`/`merge` — so stripping them from the
47
+ * inherited environment we pass through is both safe and avoids depending on
48
+ * simple-git's `unsafe.allowUnsafe*` escape hatches (not part of this
49
+ * package version's public option types).
50
+ */
51
+ const UNSAFE_GIT_ENV_KEYS = new Set([
52
+ 'editor',
53
+ 'git_editor',
54
+ 'git_sequence_editor',
55
+ 'git_askpass',
56
+ 'ssh_askpass',
57
+ 'git_config',
58
+ 'git_config_global',
59
+ 'git_config_system',
60
+ 'git_config_count',
61
+ 'git_exec_path',
62
+ 'git_external_diff',
63
+ 'git_pager',
64
+ 'pager',
65
+ 'git_proxy_command',
66
+ 'git_template_dir',
67
+ 'git_ssh',
68
+ 'git_ssh_command',
69
+ 'prefix',
70
+ ]);
71
+ /** Copy `env`, dropping keys (case-insensitively) that simple-git's vulnerability scanner blocks by default. */
72
+ function sanitizeGitEnv(env) {
73
+ const sanitized = {};
74
+ for (const [key, value] of Object.entries(env)) {
75
+ if (UNSAFE_GIT_ENV_KEYS.has(key.toLowerCase()))
76
+ continue;
77
+ sanitized[key] = value;
78
+ }
79
+ return sanitized;
80
+ }
81
+ /**
82
+ * Thin runner around a `simple-git` instance permanently scoped (via `.env()`)
83
+ * to an isolated GIT_DIR/GIT_WORK_TREE pair. Every method here is a small,
84
+ * named wrapper around a raw git invocation — no hook emission, no shared
85
+ * state with the user's real repository.
86
+ */
87
+ export class SideGitRunner {
88
+ workspaceRoot;
89
+ gitDir;
90
+ git;
91
+ constructor(opts) {
92
+ this.workspaceRoot = opts.workspaceRoot;
93
+ this.gitDir = opts.gitDir;
94
+ this.git = simpleGit({ baseDir: opts.workspaceRoot }).env({
95
+ ...sanitizeGitEnv(process.env),
96
+ GIT_DIR: opts.gitDir,
97
+ GIT_WORK_TREE: opts.workspaceRoot,
98
+ });
99
+ }
100
+ /** Run an arbitrary raw git command against the side repo, returning stdout. */
101
+ async raw(args) {
102
+ return this.git.raw(args);
103
+ }
104
+ /**
105
+ * Idempotently initialize the side repo: `git init` the GIT_DIR if it does
106
+ * not already look initialized, set a local (side-repo-only) fallback
107
+ * identity so commits never depend on the user's global git config, and
108
+ * ensure `.goodvibes` is gitignored from the user's own repo's perspective.
109
+ */
110
+ async init() {
111
+ if (!existsSync(this.gitDir)) {
112
+ mkdirSync(this.gitDir, { recursive: true });
113
+ }
114
+ const alreadyInit = existsSync(join(this.gitDir, 'HEAD'));
115
+ if (!alreadyInit) {
116
+ await this.git.raw(['init', '--quiet']);
117
+ logger.debug('SideGitRunner.init: initialized side repo', { gitDir: this.gitDir });
118
+ }
119
+ // Local (side-repo-scoped) identity — never touches the user's ~/.gitconfig
120
+ // or the workspace's real .git/config (there is no --global here, and
121
+ // GIT_DIR points at our own directory, so `git config` without --global
122
+ // writes to <gitDir>/config).
123
+ await this.git.raw(['config', 'user.name', FALLBACK_IDENTITY.name]);
124
+ await this.git.raw(['config', 'user.email', FALLBACK_IDENTITY.email]);
125
+ this.ensureGoodvibesIgnored();
126
+ }
127
+ /**
128
+ * Ensure `<workspaceRoot>/.goodvibes/.gitignore` contains a bare `*` line so
129
+ * the side repo's own storage (and every other tool's state under
130
+ * `.goodvibes`) is invisible to the user's OWN git repo (if any). Without
131
+ * this, `git status`/`git add -A` in the user's real repo would see our
132
+ * GIT_DIR as an untracked directory and could accidentally stage it.
133
+ *
134
+ * This intentionally only ever writes inside `.goodvibes/` — it never
135
+ * touches the workspace's own top-level `.gitignore`.
136
+ */
137
+ ensureGoodvibesIgnored() {
138
+ const goodvibesDir = join(this.workspaceRoot, '.goodvibes');
139
+ const ignorePath = join(goodvibesDir, '.gitignore');
140
+ if (!existsSync(goodvibesDir)) {
141
+ mkdirSync(goodvibesDir, { recursive: true });
142
+ }
143
+ try {
144
+ if (!existsSync(ignorePath)) {
145
+ writeFileSync(ignorePath, '*\n', 'utf-8');
146
+ return;
147
+ }
148
+ const existing = readFileSync(ignorePath, 'utf-8');
149
+ const hasIgnoreAll = existing.split('\n').some((line) => line.trim() === '*');
150
+ if (!hasIgnoreAll) {
151
+ writeFileSync(ignorePath, `${existing.replace(/\s*$/, '')}\n*\n`, 'utf-8');
152
+ }
153
+ }
154
+ catch (err) {
155
+ logger.warn('SideGitRunner.ensureGoodvibesIgnored: failed to write .goodvibes/.gitignore', {
156
+ error: summarizeError(err),
157
+ });
158
+ }
159
+ }
160
+ /**
161
+ * Stage changes into the side index.
162
+ *
163
+ * @param paths When provided (non-empty), stage only these pathspecs
164
+ * (scoped snapshot). Otherwise sweep the whole work tree, always excluding
165
+ * `.goodvibes` (our own storage) via pathspec magic — the same
166
+ * `:(exclude)` pattern AgentWorktree already uses for the same reason.
167
+ * Beyond that exclusion, git's normal `.gitignore` handling already applies
168
+ * here: `.gitignore` matching is a work-tree-relative feature of git and
169
+ * works identically regardless of where GIT_DIR points, so the workspace's
170
+ * own `.gitignore` (node_modules, build output, etc.) is honored with no
171
+ * extra configuration.
172
+ */
173
+ async stageAll(paths) {
174
+ const pathspecs = paths && paths.length > 0
175
+ ? paths
176
+ : ['.', ':(exclude).goodvibes', ':(exclude).goodvibes/**'];
177
+ await this.git.raw(['add', '-A', '--', ...pathspecs]);
178
+ }
179
+ /** Write the currently-staged index out as a tree object, without committing. Returns the tree hash. */
180
+ async writeTree() {
181
+ return (await this.git.raw(['write-tree'])).trim();
182
+ }
183
+ /** Resolve `<commit>^{tree}` for an existing commit hash. */
184
+ async treeOf(commit) {
185
+ return (await this.git.raw(['rev-parse', `${commit}^{tree}`])).trim();
186
+ }
187
+ /**
188
+ * Create a commit object from a tree, deliberately WITHOUT a git parent
189
+ * (no `-p`) and WITHOUT moving any branch/HEAD.
190
+ *
191
+ * Checkpoint lineage is tracked exclusively via the manifest's `parentId`
192
+ * field (manager.ts) — never via git ancestry. This isn't just a style
193
+ * choice: if checkpoint commits chained via `-p` the way a normal git
194
+ * history does, deleting an OLD checkpoint's ref in `gc()` would free
195
+ * nothing, because that commit stays reachable through every NEWER
196
+ * checkpoint's parent pointer — the ref is gone but the commit (and any
197
+ * tree/blob objects unique to it) is still walkable from every surviving
198
+ * descendant. Parentless commits mean a checkpoint's own ref is the ONLY
199
+ * thing keeping its commit reachable, so once that ref is deleted,
200
+ * `git gc --prune=now` can genuinely reclaim it.
201
+ *
202
+ * The returned hash is only reachable once a ref is pointed at it via
203
+ * `updateRef`.
204
+ */
205
+ async commitTree(treeHash, message) {
206
+ return (await this.git.raw(['commit-tree', treeHash, '-m', message])).trim();
207
+ }
208
+ async updateRef(refName, commit) {
209
+ await this.git.raw(['update-ref', refName, commit]);
210
+ }
211
+ async deleteRef(refName) {
212
+ await this.git.raw(['update-ref', '-d', refName]);
213
+ }
214
+ /** List every ref under CHECKPOINT_REF_PREFIX as `{ id, commit }`. */
215
+ async listCheckpointRefs() {
216
+ let out;
217
+ try {
218
+ out = await this.git.raw(['for-each-ref', '--format=%(refname) %(objectname)', CHECKPOINT_REF_PREFIX]);
219
+ }
220
+ catch {
221
+ return [];
222
+ }
223
+ return out
224
+ .split('\n')
225
+ .map((line) => line.trim())
226
+ .filter(Boolean)
227
+ .map((line) => {
228
+ const [refName, commit] = line.split(' ');
229
+ const id = (refName ?? '').slice(CHECKPOINT_REF_PREFIX.length);
230
+ return { id, commit: commit ?? '' };
231
+ })
232
+ .filter((entry) => entry.id.length > 0 && entry.commit.length > 0);
233
+ }
234
+ /** Files tracked in a commit's tree (recursive, name-only). */
235
+ async listTrackedFiles(commitOrTree) {
236
+ const out = await this.git.raw(['ls-tree', '-r', '--name-only', commitOrTree]);
237
+ return out.split('\n').map((line) => line.trim()).filter(Boolean);
238
+ }
239
+ /** Reset the side index to exactly match a commit's tree (does not touch the working tree). */
240
+ async readTreeReset(commit) {
241
+ await this.git.raw(['read-tree', '--reset', commit]);
242
+ }
243
+ /** Write every file currently in the side index out to the working tree, overwriting existing files. */
244
+ async checkoutIndexAll() {
245
+ await this.git.raw(['checkout-index', '-a', '-f']);
246
+ }
247
+ /** `git diff` between two commit-ish values. Omit `to` to diff against the live working tree. */
248
+ async diff(from, to) {
249
+ return this.git.raw(to ? ['diff', from, to] : ['diff', from]);
250
+ }
251
+ /** `git diff --stat` between two commit-ish values. Omit `to` to diff against the live working tree. */
252
+ async diffStat(from, to) {
253
+ return this.git.raw(to ? ['diff', '--stat', from, to] : ['diff', '--stat', from]);
254
+ }
255
+ /** `git diff --name-only` between two commit-ish values. Omit `to` to diff against the live working tree. */
256
+ async diffNameOnly(from, to) {
257
+ const out = await this.git.raw(to ? ['diff', '--name-only', from, to] : ['diff', '--name-only', from]);
258
+ return out.split('\n').map((line) => line.trim()).filter(Boolean);
259
+ }
260
+ /** `git gc --prune=now` on the side repo. */
261
+ async gc() {
262
+ await this.git.raw(['gc', '--prune=now', '--quiet']);
263
+ }
264
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * types.ts
3
+ *
4
+ * Types for the workspace checkpoint engine (`WorkspaceCheckpointManager`).
5
+ *
6
+ * NAMING: this is deliberately "WorkspaceCheckpoint" / `wcp_` ids, NOT
7
+ * "checkpoint" bare. "checkpoint" is already used for two unrelated concepts
8
+ * in this codebase:
9
+ * - compaction boundary commits (`cpt_` ids, ../../runtime/compaction/strategies/boundary-commit.ts)
10
+ * — a CONVERSATION snapshot (messages/tokens), not a filesystem snapshot.
11
+ * - the generic retention `CheckpointRecord` (../../runtime/retention/types.ts)
12
+ * — a size/age/count bookkeeping record reused by both the boundary-commit
13
+ * GC path and this module's GC path, but tracked in independent
14
+ * `RetentionPolicy` instances so pruning one never touches the other.
15
+ *
16
+ * A WorkspaceCheckpoint is a whole-workspace filesystem snapshot stored as a
17
+ * commit object in a hidden side git repository (see side-git.ts) — never a
18
+ * conversation snapshot, never mixed with the user's real git history.
19
+ */
20
+ import type { RetentionClass } from '../../runtime/retention/types.js';
21
+ export type { RetentionClass };
22
+ /**
23
+ * What triggered a checkpoint to be created.
24
+ *
25
+ * - `turn` — automatic snapshot taken at a conversation turn boundary
26
+ * (TURN_COMPLETED / TURN_ERROR / TURN_CANCEL).
27
+ * - `agent-run` — automatic snapshot taken after a spawned agent's changes
28
+ * have been merged into the main workspace (AGENT_COMPLETED).
29
+ * - `manual` — explicit user-requested checkpoint (e.g. TUI `/checkpoint`).
30
+ */
31
+ export type CheckpointKind = 'turn' | 'agent-run' | 'manual';
32
+ /**
33
+ * Metadata record for a single workspace checkpoint.
34
+ *
35
+ * Persisted in the manifest (`index.json`, via JsonFileStore) alongside the
36
+ * side git repo that actually holds the commit/blob/tree objects. The
37
+ * manifest exists because git commit messages are a lossy encoding for
38
+ * structured metadata and because `list()` / retention need it without
39
+ * reading git object contents.
40
+ */
41
+ export interface WorkspaceCheckpoint {
42
+ /** Unique id, format `wcp_<ts36>_<rand8>`. Also the ref name suffix under refs/goodvibes/checkpoints/<id>. */
43
+ readonly id: string;
44
+ /** What triggered this checkpoint. */
45
+ readonly kind: CheckpointKind;
46
+ /** Human-readable label. Auto-generated for turn/agent-run kinds, user-supplied for manual. */
47
+ readonly label: string;
48
+ /** Unix timestamp (ms) when the checkpoint was created. */
49
+ readonly createdAt: number;
50
+ /** id of the checkpoint this one was created on top of, or null for the first checkpoint ever taken. */
51
+ readonly parentId: string | null;
52
+ /** Turn id, when kind === 'turn' (or when passed explicitly). */
53
+ readonly turnId?: string | undefined;
54
+ /** Agent id, when kind === 'agent-run' (or when passed explicitly). */
55
+ readonly agentId?: string | undefined;
56
+ /** Retention class controlling how long this checkpoint survives gc(). */
57
+ readonly retentionClass: RetentionClass;
58
+ /** Git commit hash (in the side repo's object store) this checkpoint points to. */
59
+ readonly commit: string;
60
+ /**
61
+ * Approximate bytes of NEW content this checkpoint introduced relative to
62
+ * its parent (unchanged files are deduped by git and cost ~0; this is not
63
+ * exact object-store accounting, it is the sum of on-disk sizes of the
64
+ * files that differ from the parent, read at commit time).
65
+ */
66
+ readonly sizeBytes: number;
67
+ }
68
+ /** Result of `WorkspaceCheckpointManager.diff()`. */
69
+ export interface CheckpointDiff {
70
+ /** The checkpoint id (or ref) diffed from. */
71
+ readonly from: string;
72
+ /** The checkpoint id diffed to, or the literal string 'WORKING' when diffed against the live working tree. */
73
+ readonly to: string;
74
+ /** Paths that differ between `from` and `to`. */
75
+ readonly files: string[];
76
+ /** Unified diff text (`git diff`). */
77
+ readonly unifiedDiff: string;
78
+ /** `git diff --stat` text. */
79
+ readonly stat: string;
80
+ }
81
+ /** Result of `WorkspaceCheckpointManager.restore()`. */
82
+ export interface RestoreResult {
83
+ /** The checkpoint id that was restored. */
84
+ readonly checkpointId: string;
85
+ /**
86
+ * id of the safety checkpoint taken immediately before restoring, or null
87
+ * when `safetyCheckpoint: false` was passed, or when the working tree was
88
+ * already identical to the checkpoint being restored (no-op dedupe).
89
+ */
90
+ readonly safetyCheckpointId: string | null;
91
+ /** Paths written or overwritten to match the restored checkpoint. */
92
+ readonly restoredFiles: string[];
93
+ /** Paths that existed (and were tracked by this engine) after the checkpoint but not in it, and were removed. */
94
+ readonly removedFiles: string[];
95
+ }
96
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/platform/workspace/checkpoint/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEvE,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC,8GAA8G;IAC9G,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wGAAwG;IACxG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,uEAAuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8GAA8G;IAC9G,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB,sCAAsC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IACjC,iHAAiH;IACjH,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;CACjC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * types.ts
3
+ *
4
+ * Types for the workspace checkpoint engine (`WorkspaceCheckpointManager`).
5
+ *
6
+ * NAMING: this is deliberately "WorkspaceCheckpoint" / `wcp_` ids, NOT
7
+ * "checkpoint" bare. "checkpoint" is already used for two unrelated concepts
8
+ * in this codebase:
9
+ * - compaction boundary commits (`cpt_` ids, ../../runtime/compaction/strategies/boundary-commit.ts)
10
+ * — a CONVERSATION snapshot (messages/tokens), not a filesystem snapshot.
11
+ * - the generic retention `CheckpointRecord` (../../runtime/retention/types.ts)
12
+ * — a size/age/count bookkeeping record reused by both the boundary-commit
13
+ * GC path and this module's GC path, but tracked in independent
14
+ * `RetentionPolicy` instances so pruning one never touches the other.
15
+ *
16
+ * A WorkspaceCheckpoint is a whole-workspace filesystem snapshot stored as a
17
+ * commit object in a hidden side git repository (see side-git.ts) — never a
18
+ * conversation snapshot, never mixed with the user's real git history.
19
+ */
20
+ export {};
@@ -1,3 +1,4 @@
1
1
  export * from './daemon-home.js';
2
2
  export * from './workspace-swap-manager.js';
3
+ export { WorkspaceCheckpointManager, type CreateCheckpointOptions, type RestoreOptions, type ListCheckpointsFilter, type WorkspaceCheckpointManagerOptions, type WorkspaceCheckpoint, type CheckpointKind, type CheckpointDiff, type RestoreResult, } from './checkpoint/index.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/platform/workspace/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/platform/workspace/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EACL,0BAA0B,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,iCAAiC,EACtC,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './daemon-home.js';
2
2
  export * from './workspace-swap-manager.js';
3
+ export { WorkspaceCheckpointManager, } from './checkpoint/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-sdk",
3
- "version": "0.36.0",
3
+ "version": "0.37.1",
4
4
  "description": "TypeScript SDK for building GoodVibes operator, peer, web, mobile, and daemon-connected apps with typed contracts, auth, realtime events, and transport layers.",
5
5
  "keywords": [
6
6
  "goodvibes",
@@ -453,14 +453,14 @@
453
453
  "sideEffects": false,
454
454
  "type": "module",
455
455
  "dependencies": {
456
- "@pellux/goodvibes-contracts": "0.36.0",
457
- "@pellux/goodvibes-daemon-sdk": "0.36.0",
458
- "@pellux/goodvibes-errors": "0.36.0",
459
- "@pellux/goodvibes-operator-sdk": "0.36.0",
460
- "@pellux/goodvibes-peer-sdk": "0.36.0",
461
- "@pellux/goodvibes-transport-core": "0.36.0",
462
- "@pellux/goodvibes-transport-http": "0.36.0",
463
- "@pellux/goodvibes-transport-realtime": "0.36.0"
456
+ "@pellux/goodvibes-contracts": "0.37.1",
457
+ "@pellux/goodvibes-daemon-sdk": "0.37.1",
458
+ "@pellux/goodvibes-errors": "0.37.1",
459
+ "@pellux/goodvibes-operator-sdk": "0.37.1",
460
+ "@pellux/goodvibes-peer-sdk": "0.37.1",
461
+ "@pellux/goodvibes-transport-core": "0.37.1",
462
+ "@pellux/goodvibes-transport-http": "0.37.1",
463
+ "@pellux/goodvibes-transport-realtime": "0.37.1"
464
464
  },
465
465
  "optionalDependencies": {
466
466
  "@agentclientprotocol/sdk": "^0.21.0",