vericify 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.
- package/README.md +389 -0
- package/package.json +57 -0
- package/src/adapters/index.js +37 -0
- package/src/adapters/local-state.js +86 -0
- package/src/adapters/registry.js +126 -0
- package/src/api.js +12 -0
- package/src/checkpoints/policy.js +96 -0
- package/src/compare/engine.js +220 -0
- package/src/core/fs.js +86 -0
- package/src/core/util.js +59 -0
- package/src/index.js +464 -0
- package/src/post/process-posts.js +72 -0
- package/src/projection/runs.js +809 -0
- package/src/publish/artifact.js +91 -0
- package/src/similarity/semantic-hash.js +95 -0
- package/src/store/adapter-attachments.js +47 -0
- package/src/store/common.js +38 -0
- package/src/store/handoffs.js +64 -0
- package/src/store/paths.js +40 -0
- package/src/store/run-ledger.js +46 -0
- package/src/store/status-events.js +39 -0
- package/src/store/todo-state.js +49 -0
- package/src/sync/outbox.js +29 -0
- package/src/tui/app.js +571 -0
- package/src/tui/commands.js +224 -0
- package/src/tui/panels.js +440 -0
- package/src/tui/runtime-activity.js +172 -0
package/README.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# Vericify
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
__ _______ ____ ___ ____ ___ _______ __
|
|
5
|
+
\ \ / / ____| _ \|_ _/ ___|_ _| ___\ \ / /
|
|
6
|
+
\ \ / /| _| | |_) || | | | || |_ \ V /
|
|
7
|
+
\ V / | |___| _ < | | |___ | || _| | |
|
|
8
|
+
\_/ |_____|_| \_\___\____|___|_| |_|
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Vericify is a local-first operations hub for agent work.
|
|
12
|
+
|
|
13
|
+
It gives a workspace a durable run model instead of making you reconstruct intent from chat scrollback, shell history, and raw logs.
|
|
14
|
+
|
|
15
|
+
It works on its own, and it also plugs neatly into [ACE / ace-swarm](https://www.npmjs.com/package/ace-swarm) workspaces that already emit `agent-state/*`.
|
|
16
|
+
|
|
17
|
+
## Product Summary
|
|
18
|
+
|
|
19
|
+
Vericify is for people building with agents who want to answer questions like:
|
|
20
|
+
|
|
21
|
+
- What is moving right now?
|
|
22
|
+
- What is blocked?
|
|
23
|
+
- Which run diverged?
|
|
24
|
+
- Which earlier run looks like a recovery path?
|
|
25
|
+
- What should I publish or hand off?
|
|
26
|
+
|
|
27
|
+
The package installs one CLI:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
vericify
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Default behavior is simple:
|
|
34
|
+
|
|
35
|
+
- run it inside the repo you want to observe
|
|
36
|
+
- store native state in `.vericify/`
|
|
37
|
+
- read partner state from `agent-state/*` when present
|
|
38
|
+
- open the terminal hub with `vericify` or `vericify hub`
|
|
39
|
+
|
|
40
|
+
## What Vericify Does
|
|
41
|
+
|
|
42
|
+
Vericify gives you:
|
|
43
|
+
|
|
44
|
+
- a terminal cockpit for run inspection
|
|
45
|
+
- a structured run model built from handoffs, posts, todos, events, and checkpoints
|
|
46
|
+
- a compare engine that explains divergence and recovery
|
|
47
|
+
- publishable run artifacts under `.vericify/published/`
|
|
48
|
+
- a local-first sync outbox under `.vericify/sync-outbox/`
|
|
49
|
+
- partner compatibility with [ACE / ace-swarm](https://www.npmjs.com/package/ace-swarm)
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install -g vericify
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Node `18+` is required.
|
|
58
|
+
|
|
59
|
+
## Five-Minute Start
|
|
60
|
+
|
|
61
|
+
### 1) Start in any repo
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd /path/to/repo
|
|
65
|
+
vericify adapters
|
|
66
|
+
vericify hub
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If Vericify already sees `.vericify/` or `agent-state/*`, the hub can immediately project runs from that workspace.
|
|
70
|
+
|
|
71
|
+
### 2) If you already use [ACE / ace-swarm](https://www.npmjs.com/package/ace-swarm)
|
|
72
|
+
|
|
73
|
+
This is the easiest path.
|
|
74
|
+
|
|
75
|
+
If your workspace already contains `agent-state/*`, Vericify reads it automatically:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
cd /path/to/ace-workspace
|
|
79
|
+
vericify adapters
|
|
80
|
+
vericify hub
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
No extra conversion step is required.
|
|
84
|
+
|
|
85
|
+
### 3) If you are using another agent client and want to label it explicitly
|
|
86
|
+
|
|
87
|
+
Attach the adapter to the current workspace:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
vericify attach --adapter codex --label "Primary Codex session"
|
|
91
|
+
vericify adapters
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
That creates or updates `.vericify/adapters.json` for the current repo and marks the adapter as attached.
|
|
95
|
+
|
|
96
|
+
## Do I Need `--session-id`?
|
|
97
|
+
|
|
98
|
+
No. `--session-id` is optional.
|
|
99
|
+
|
|
100
|
+
Use no session ID when:
|
|
101
|
+
|
|
102
|
+
- one active session per tool is enough
|
|
103
|
+
- you only want to label the workspace relationship
|
|
104
|
+
- you want the lightest setup
|
|
105
|
+
|
|
106
|
+
Add a session ID when:
|
|
107
|
+
|
|
108
|
+
- you run multiple sessions from the same tool
|
|
109
|
+
- you want stable names in artifacts and dashboards
|
|
110
|
+
- you want to distinguish roles like `claude-main` and `claude-review`
|
|
111
|
+
|
|
112
|
+
Good session IDs look like:
|
|
113
|
+
|
|
114
|
+
- `claude-main`
|
|
115
|
+
- `codex-bugfix`
|
|
116
|
+
- `cursor-review-01`
|
|
117
|
+
|
|
118
|
+
## What Attachment Looks Like
|
|
119
|
+
|
|
120
|
+
### A) Attach without a session ID
|
|
121
|
+
|
|
122
|
+
This is valid:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
vericify attach --adapter codex --label "Primary Codex session"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Simplified output:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"path": ".vericify/adapters.json",
|
|
133
|
+
"attachment": {
|
|
134
|
+
"adapter_id": "codex",
|
|
135
|
+
"capture_mode": "manual",
|
|
136
|
+
"label": "Primary Codex session"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Notes:
|
|
142
|
+
|
|
143
|
+
- `capture_mode` defaults to `manual` if you do not set one
|
|
144
|
+
- this is enough to tell Vericify "track this tool in this workspace"
|
|
145
|
+
|
|
146
|
+
### B) Attach with a session ID
|
|
147
|
+
|
|
148
|
+
Use this when you want a named session:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
vericify attach --adapter claude-code --session-id claude-main --capture-mode attachment --label "Claude main"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Simplified output:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"path": ".vericify/adapters.json",
|
|
159
|
+
"attachment": {
|
|
160
|
+
"adapter_id": "claude-code",
|
|
161
|
+
"capture_mode": "attachment",
|
|
162
|
+
"session_id": "claude-main",
|
|
163
|
+
"label": "Claude main"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
After that, verify status:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
vericify adapters
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Look for:
|
|
175
|
+
|
|
176
|
+
- `detection_status: "attached"`
|
|
177
|
+
- `session_id` when you supplied one
|
|
178
|
+
- `label_override` with your human-friendly name
|
|
179
|
+
|
|
180
|
+
## Easy Use Cases
|
|
181
|
+
|
|
182
|
+
### Use case 1: Observe an existing [ACE / ace-swarm](https://www.npmjs.com/package/ace-swarm) workspace
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
cd /path/to/ace-workspace
|
|
186
|
+
vericify adapters
|
|
187
|
+
vericify hub
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Best when you already have `agent-state/*` and want a cockpit, history, and compare surface.
|
|
191
|
+
|
|
192
|
+
### Use case 2: Tag a Codex or Claude Code workspace before richer capture exists
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
vericify attach --adapter codex --label "Primary Codex"
|
|
196
|
+
vericify attach --adapter claude-code --session-id claude-main --capture-mode attachment
|
|
197
|
+
vericify hub
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Best when you want durable workspace metadata now, even before vendor-specific live capture bridges are added.
|
|
201
|
+
|
|
202
|
+
### Use case 3: Create a native run trail manually
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
vericify handoff --id h1 --from capability-ops --to capability-build --title "Implement compare engine" --status open
|
|
206
|
+
vericify post --run-id handoff:h1 --agent-id capability-build --kind progress --summary "Builder started compare engine"
|
|
207
|
+
vericify snapshot
|
|
208
|
+
vericify hub
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Best when you want to model agent work directly in Vericify.
|
|
212
|
+
|
|
213
|
+
### Use case 4: Compare two runs
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
vericify compare --run-id handoff:run-a --compare-run-id workspace:current
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The comparison report includes:
|
|
220
|
+
|
|
221
|
+
- composite similarity
|
|
222
|
+
- latest checkpoint similarity
|
|
223
|
+
- actor overlap
|
|
224
|
+
- node overlap
|
|
225
|
+
- layer-level divergence cues
|
|
226
|
+
- recovery explanations
|
|
227
|
+
- recommended next actions
|
|
228
|
+
|
|
229
|
+
### Use case 5: Publish or queue a run artifact
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
vericify publish --run-id handoff:run-a --compare-run-id workspace:current --title "Run A artifact"
|
|
233
|
+
vericify sync --run-id handoff:run-a --compare-run-id workspace:current --endpoint https://sync.example.test
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
This writes:
|
|
237
|
+
|
|
238
|
+
- `.vericify/published/<artifact-id>/run-artifact.json`
|
|
239
|
+
- `.vericify/published/<artifact-id>/SUMMARY.md`
|
|
240
|
+
- `.vericify/sync-outbox/<item>.json`
|
|
241
|
+
|
|
242
|
+
## Product Spec
|
|
243
|
+
|
|
244
|
+
### Core Objects
|
|
245
|
+
|
|
246
|
+
- `Run`: one coordinated attempt to complete an objective
|
|
247
|
+
- `Branch`: an alternate path inside a run
|
|
248
|
+
- `Lane`: one concurrent execution stream
|
|
249
|
+
- `Handoff`: transfer of responsibility
|
|
250
|
+
- `Checkpoint`: durable snapshot at a meaningful transition
|
|
251
|
+
- `Delta`: structural, semantic, or operational change between checkpoints
|
|
252
|
+
|
|
253
|
+
### Storage Contract
|
|
254
|
+
|
|
255
|
+
Vericify writes native state here:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
.vericify/
|
|
259
|
+
|-- adapters.json
|
|
260
|
+
|-- handoffs.json
|
|
261
|
+
|-- todo-state.json
|
|
262
|
+
|-- run-ledger.json
|
|
263
|
+
|-- status-events.ndjson
|
|
264
|
+
|-- process-posts.json
|
|
265
|
+
|-- published/
|
|
266
|
+
`-- sync-outbox/
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Vericify also reads partner state here when available:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
agent-state/
|
|
273
|
+
`-- ...
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
This is why it works naturally with [ACE / ace-swarm](https://www.npmjs.com/package/ace-swarm) workspaces.
|
|
277
|
+
|
|
278
|
+
### Adapter Contract
|
|
279
|
+
|
|
280
|
+
Current adapter registry:
|
|
281
|
+
|
|
282
|
+
- `local-state`
|
|
283
|
+
- `ace`
|
|
284
|
+
- `codex`
|
|
285
|
+
- `claude-code`
|
|
286
|
+
- `cursor`
|
|
287
|
+
- `vscode-copilot-chat`
|
|
288
|
+
- `antigravity`
|
|
289
|
+
|
|
290
|
+
Current truth:
|
|
291
|
+
|
|
292
|
+
- `local-state` is fully implemented
|
|
293
|
+
- `ace` is the partner path for [ACE / ace-swarm](https://www.npmjs.com/package/ace-swarm) style workspaces
|
|
294
|
+
- peer adapters are currently attachment and detection contracts
|
|
295
|
+
- auto-detection is heuristic for several tools, so explicit attachment is the reliable path
|
|
296
|
+
|
|
297
|
+
### Checkpoint Policy
|
|
298
|
+
|
|
299
|
+
Checkpoint triggers currently include:
|
|
300
|
+
|
|
301
|
+
- `handoff`
|
|
302
|
+
- `status_transition`
|
|
303
|
+
- `process_milestone`
|
|
304
|
+
- `ledger_update`
|
|
305
|
+
- `operator_save`
|
|
306
|
+
- `branch_fork`
|
|
307
|
+
|
|
308
|
+
Capture modes currently include:
|
|
309
|
+
|
|
310
|
+
- `semantic`
|
|
311
|
+
- `git`
|
|
312
|
+
- `hybrid`
|
|
313
|
+
|
|
314
|
+
Today the package is semantic-first. Git-backed provenance can be attached when available.
|
|
315
|
+
|
|
316
|
+
## CLI Reference
|
|
317
|
+
|
|
318
|
+
### High-signal commands
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
vericify help
|
|
322
|
+
vericify adapters
|
|
323
|
+
vericify attach --adapter codex --label "Primary Codex session"
|
|
324
|
+
vericify attach --adapter claude-code --session-id claude-main --capture-mode attachment --label "Claude main"
|
|
325
|
+
vericify hub
|
|
326
|
+
vericify snapshot
|
|
327
|
+
vericify compare --run-id handoff:run-a --compare-run-id workspace:current
|
|
328
|
+
vericify publish --run-id handoff:run-a
|
|
329
|
+
vericify sync --run-id handoff:run-a --endpoint https://sync.example.test
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Native writer commands
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
vericify handoff --id h1 --from capability-ops --to capability-build --title "Review" --status open
|
|
336
|
+
vericify todo --id todo-1 --title "Write store" --status in_progress
|
|
337
|
+
vericify ledger --tool vericify --category major_update --message "Writer landed"
|
|
338
|
+
vericify event --source-module capability-build --event-type STORE_WRITE --status started --payload-json '{"summary":"native write"}'
|
|
339
|
+
vericify post --run-id handoff:h1 --agent-id capability-build --kind progress --summary "Checkpoint emitted"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Helpful defaults
|
|
343
|
+
|
|
344
|
+
- current working directory is the workspace unless you pass `--workspace-root`
|
|
345
|
+
- `vericify` with no command opens `hub`
|
|
346
|
+
- `--session-id` is optional
|
|
347
|
+
- `--compare-run-id` is optional for `publish` and `sync`
|
|
348
|
+
|
|
349
|
+
## Terminal Hub Controls
|
|
350
|
+
|
|
351
|
+
Start the cockpit:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
vericify hub
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Keys:
|
|
358
|
+
|
|
359
|
+
- `q` quit
|
|
360
|
+
- `j` / `k` move between runs
|
|
361
|
+
- `Enter` inspect selected run
|
|
362
|
+
- `c` compare selected run
|
|
363
|
+
- `y` history view
|
|
364
|
+
- `h` back to hub
|
|
365
|
+
- `r` refresh
|
|
366
|
+
- `/` command palette
|
|
367
|
+
- `Ctrl+R` command history search
|
|
368
|
+
|
|
369
|
+
## Programmatic API
|
|
370
|
+
|
|
371
|
+
The package also exports:
|
|
372
|
+
|
|
373
|
+
- `loadWorkspaceState`
|
|
374
|
+
- `projectWorkspaceState`
|
|
375
|
+
- `detectAdapters`
|
|
376
|
+
- `attachAdapter`
|
|
377
|
+
- `buildRunComparison`
|
|
378
|
+
- `publishRunArtifact`
|
|
379
|
+
- `enqueueSyncOutboxItem`
|
|
380
|
+
|
|
381
|
+
## Honest Status
|
|
382
|
+
|
|
383
|
+
Vericify is intentionally honest about what is implemented now.
|
|
384
|
+
|
|
385
|
+
- peer client adapters are not full live capture bridges yet
|
|
386
|
+
- hosted sync is an outbox contract today, not a running SaaS backend
|
|
387
|
+
- git-backed diffing exists as optional provenance, not the whole product
|
|
388
|
+
|
|
389
|
+
That is deliberate: the package is meant to be useful now, without pretending unfinished infrastructure already exists.
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vericify",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Local-first run intelligence and operations hub for agent systems.",
|
|
7
|
+
"main": "./src/api.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/api.js",
|
|
10
|
+
"./api": "./src/api.js",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"vericify": "./src/index.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"adapters": "node src/index.js adapters",
|
|
22
|
+
"attach": "node src/index.js attach",
|
|
23
|
+
"hub": "node src/index.js hub",
|
|
24
|
+
"compare": "node src/index.js compare",
|
|
25
|
+
"publish": "node src/index.js publish",
|
|
26
|
+
"sync": "node src/index.js sync",
|
|
27
|
+
"handoff": "node src/index.js handoff",
|
|
28
|
+
"todo": "node src/index.js todo",
|
|
29
|
+
"ledger": "node src/index.js ledger",
|
|
30
|
+
"event": "node src/index.js event",
|
|
31
|
+
"post": "node src/index.js post",
|
|
32
|
+
"snapshot": "node src/index.js snapshot",
|
|
33
|
+
"help": "node src/index.js help",
|
|
34
|
+
"test": "node --test test/**/*.test.mjs"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"agents",
|
|
38
|
+
"ai",
|
|
39
|
+
"observability",
|
|
40
|
+
"tui",
|
|
41
|
+
"runs",
|
|
42
|
+
"checkpoints",
|
|
43
|
+
"diff",
|
|
44
|
+
"codex",
|
|
45
|
+
"claude-code",
|
|
46
|
+
"cursor",
|
|
47
|
+
"copilot",
|
|
48
|
+
"ace-swarm"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"license": "UNLICENSED",
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { listLocalStatePaths, loadLocalState } from "./local-state.js";
|
|
2
|
+
import {
|
|
3
|
+
attachWorkspaceAdapter,
|
|
4
|
+
detectWorkspaceAdapters,
|
|
5
|
+
listAdapterDefinitions,
|
|
6
|
+
readWorkspaceAdapterAttachments,
|
|
7
|
+
} from "./registry.js";
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_ADAPTER = "local-state";
|
|
10
|
+
|
|
11
|
+
export function listDefaultWorkspacePaths(workspaceRoot) {
|
|
12
|
+
return listLocalStatePaths(workspaceRoot);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function loadWorkspaceState(workspaceRoot) {
|
|
16
|
+
const state = loadLocalState(workspaceRoot);
|
|
17
|
+
return {
|
|
18
|
+
...state,
|
|
19
|
+
adapterProfiles: detectWorkspaceAdapters(workspaceRoot, state.adapterAttachments),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function listAvailableAdapters() {
|
|
24
|
+
return listAdapterDefinitions();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function detectAdapters(workspaceRoot) {
|
|
28
|
+
return detectWorkspaceAdapters(workspaceRoot);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function readAdapterAttachments(workspaceRoot) {
|
|
32
|
+
return readWorkspaceAdapterAttachments(workspaceRoot);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function attachAdapter(workspaceRoot, input) {
|
|
36
|
+
return attachWorkspaceAdapter(workspaceRoot, input);
|
|
37
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { hashText, unique } from "../core/util.js";
|
|
2
|
+
import { readJson, readNdjson } from "../core/fs.js";
|
|
3
|
+
import { listStorePaths, resolveStoreLayout } from "../store/paths.js";
|
|
4
|
+
import { readAdapterAttachments } from "../store/adapter-attachments.js";
|
|
5
|
+
|
|
6
|
+
export function listLocalStatePaths(workspaceRoot) {
|
|
7
|
+
return listStorePaths(workspaceRoot);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function mergeByKey(preferredItems, partnerItems, getKey) {
|
|
11
|
+
const merged = new Map();
|
|
12
|
+
for (const item of partnerItems) merged.set(getKey(item), item);
|
|
13
|
+
for (const item of preferredItems) merged.set(getKey(item), item);
|
|
14
|
+
return [...merged.values()];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function mergeOrderedIds(preferred, partner) {
|
|
18
|
+
return unique([...preferred, ...partner]);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadLocalState(workspaceRoot) {
|
|
22
|
+
const paths = resolveStoreLayout(workspaceRoot);
|
|
23
|
+
const adapterAttachmentState = readAdapterAttachments(workspaceRoot);
|
|
24
|
+
const vericifyState = {
|
|
25
|
+
handoffRegistry: readJson(paths.vericify.handoffRegistry, { handoffs: {} }),
|
|
26
|
+
todoState: readJson(paths.vericify.todoState, { nodes: {}, order: [] }),
|
|
27
|
+
runLedger: readJson(paths.vericify.runLedger, { entries: [] }),
|
|
28
|
+
processPosts: readJson(paths.vericify.processPosts, { posts: [] }),
|
|
29
|
+
statusEvents: readNdjson(paths.vericify.statusEvents),
|
|
30
|
+
};
|
|
31
|
+
const partnerState = {
|
|
32
|
+
handoffRegistry: readJson(paths.partner.handoffRegistry, { handoffs: {} }),
|
|
33
|
+
todoState: readJson(paths.partner.todoState, { nodes: {}, order: [] }),
|
|
34
|
+
runLedger: readJson(paths.partner.runLedger, { entries: [] }),
|
|
35
|
+
processPosts: readJson(paths.partner.processPosts, { posts: [] }),
|
|
36
|
+
statusEvents: readNdjson(paths.partner.statusEvents),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handoffs = mergeByKey(
|
|
40
|
+
Object.values(vericifyState.handoffRegistry.handoffs ?? {}),
|
|
41
|
+
Object.values(partnerState.handoffRegistry.handoffs ?? {}),
|
|
42
|
+
(handoff) => handoff.handoff_id ?? hashText(JSON.stringify(handoff))
|
|
43
|
+
);
|
|
44
|
+
const todoNodes = mergeByKey(
|
|
45
|
+
Object.values(vericifyState.todoState.nodes ?? {}),
|
|
46
|
+
Object.values(partnerState.todoState.nodes ?? {}),
|
|
47
|
+
(node) => node.id ?? hashText(JSON.stringify(node))
|
|
48
|
+
);
|
|
49
|
+
const ledgerEntries = mergeByKey(
|
|
50
|
+
Array.isArray(vericifyState.runLedger.entries) ? vericifyState.runLedger.entries : [],
|
|
51
|
+
Array.isArray(partnerState.runLedger.entries) ? partnerState.runLedger.entries : [],
|
|
52
|
+
(entry) => entry.id ?? hashText(`${entry.timestamp_utc}:${entry.tool}:${entry.message}`)
|
|
53
|
+
);
|
|
54
|
+
const statusEvents = mergeByKey(
|
|
55
|
+
vericifyState.statusEvents,
|
|
56
|
+
partnerState.statusEvents,
|
|
57
|
+
(event) => event.event_id ?? hashText(`${event.timestamp}:${event.source_module}:${event.event_type}:${event.status}`)
|
|
58
|
+
).sort((left, right) => String(left.timestamp).localeCompare(String(right.timestamp)));
|
|
59
|
+
const processPosts = mergeByKey(
|
|
60
|
+
Array.isArray(vericifyState.processPosts.posts) ? vericifyState.processPosts.posts : [],
|
|
61
|
+
Array.isArray(partnerState.processPosts.posts) ? partnerState.processPosts.posts : [],
|
|
62
|
+
(post) => post.process_post_id ?? hashText(`${post.timestamp}:${post.agent_id}:${post.summary}`)
|
|
63
|
+
).sort((left, right) => String(left.timestamp).localeCompare(String(right.timestamp)));
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
workspaceRoot,
|
|
67
|
+
handoffs,
|
|
68
|
+
todoNodes,
|
|
69
|
+
todoOrder: mergeOrderedIds(
|
|
70
|
+
Array.isArray(vericifyState.todoState.order) ? vericifyState.todoState.order : [],
|
|
71
|
+
Array.isArray(partnerState.todoState.order) ? partnerState.todoState.order : []
|
|
72
|
+
),
|
|
73
|
+
ledgerEntries,
|
|
74
|
+
statusEvents,
|
|
75
|
+
processPosts,
|
|
76
|
+
adapterAttachments: Array.isArray(adapterAttachmentState.attachments) ? adapterAttachmentState.attachments : [],
|
|
77
|
+
sourceRefs: {
|
|
78
|
+
adapterAttachments: [paths.vericify.adapterAttachments],
|
|
79
|
+
handoffRegistry: [paths.vericify.handoffRegistry, paths.partner.handoffRegistry],
|
|
80
|
+
todoState: [paths.vericify.todoState, paths.partner.todoState],
|
|
81
|
+
runLedger: [paths.vericify.runLedger, paths.partner.runLedger],
|
|
82
|
+
statusEvents: [paths.vericify.statusEvents, paths.partner.statusEvents],
|
|
83
|
+
processPosts: [paths.vericify.processPosts, paths.partner.processPosts],
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { readAdapterAttachments, upsertAdapterAttachment } from "../store/adapter-attachments.js";
|
|
4
|
+
|
|
5
|
+
const ADAPTER_DEFINITIONS = [
|
|
6
|
+
{
|
|
7
|
+
adapter_id: "local-state",
|
|
8
|
+
label: "Local State",
|
|
9
|
+
category: "native",
|
|
10
|
+
description: "Reads Vericify-native .vericify/* storage plus partner agent-state/* when present.",
|
|
11
|
+
detection_paths: [".vericify", "agent-state"],
|
|
12
|
+
capture_modes: ["filesystem"],
|
|
13
|
+
notes: ["Always available as the baseline local-first adapter."],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
adapter_id: "ace",
|
|
17
|
+
label: "ACE / ace-swarm",
|
|
18
|
+
category: "partner",
|
|
19
|
+
description: "Partner adapter for ace-swarm workspaces that already emit agent-state/* artifacts.",
|
|
20
|
+
detection_paths: ["agent-state", ".agents", "AGENTS.md"],
|
|
21
|
+
capture_modes: ["filesystem", "attachment"],
|
|
22
|
+
notes: ["Out-of-box partner path when an ace-swarm style workspace is present."],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
adapter_id: "codex",
|
|
26
|
+
label: "Codex",
|
|
27
|
+
category: "peer",
|
|
28
|
+
description: "Peer adapter contract for Codex sessions and workspace-attached process traces.",
|
|
29
|
+
detection_paths: [".codex", "AGENTS.md"],
|
|
30
|
+
capture_modes: ["attachment", "filesystem"],
|
|
31
|
+
notes: ["Auto-detection is heuristic; explicit attachment is the reliable path."],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
adapter_id: "claude-code",
|
|
35
|
+
label: "Claude Code",
|
|
36
|
+
category: "peer",
|
|
37
|
+
description: "Peer adapter contract for Claude Code sessions and workspace-attached traces.",
|
|
38
|
+
detection_paths: [".claude", "CLAUDE.md"],
|
|
39
|
+
capture_modes: ["attachment", "filesystem"],
|
|
40
|
+
notes: ["Use adapter attachment when workspace hints are absent."],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
adapter_id: "cursor",
|
|
44
|
+
label: "Cursor",
|
|
45
|
+
category: "peer",
|
|
46
|
+
description: "Peer adapter contract for Cursor-driven agent sessions.",
|
|
47
|
+
detection_paths: [".cursor", ".cursor/rules", ".cursorrules"],
|
|
48
|
+
capture_modes: ["attachment", "filesystem"],
|
|
49
|
+
notes: ["Workspace rules are treated as detection hints, not proof of live capture."],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
adapter_id: "vscode-copilot-chat",
|
|
53
|
+
label: "VS Code Copilot Chat",
|
|
54
|
+
category: "peer",
|
|
55
|
+
description: "Peer adapter contract for VS Code Copilot Chat sessions and workspace traces.",
|
|
56
|
+
detection_paths: [".vscode", ".github/copilot-instructions.md"],
|
|
57
|
+
capture_modes: ["attachment", "filesystem"],
|
|
58
|
+
notes: ["Detection relies on editor/workspace hints; attach explicitly for certainty."],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
adapter_id: "antigravity",
|
|
62
|
+
label: "Antigravity",
|
|
63
|
+
category: "peer",
|
|
64
|
+
description: "Peer adapter contract for Antigravity sessions and workspace traces.",
|
|
65
|
+
detection_paths: [".antigravity", "antigravity.config.json"],
|
|
66
|
+
capture_modes: ["attachment", "filesystem"],
|
|
67
|
+
notes: ["Manual attachment is expected until a dedicated capture bridge ships."],
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
function detectPaths(workspaceRoot, relPaths) {
|
|
72
|
+
return relPaths
|
|
73
|
+
.map((relPath) => resolve(workspaceRoot, relPath))
|
|
74
|
+
.filter((path) => existsSync(path));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function profileStatus(definition, attached, detectedPaths) {
|
|
78
|
+
if (definition.adapter_id === "local-state") return "ready";
|
|
79
|
+
if (attached) return "attached";
|
|
80
|
+
if (detectedPaths.length) return "detected";
|
|
81
|
+
return "manual";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function listAdapterDefinitions() {
|
|
85
|
+
return ADAPTER_DEFINITIONS.map((definition) => ({ ...definition }));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function readWorkspaceAdapterAttachments(workspaceRoot) {
|
|
89
|
+
return readAdapterAttachments(workspaceRoot);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function attachWorkspaceAdapter(workspaceRoot, input) {
|
|
93
|
+
return upsertAdapterAttachment(workspaceRoot, input);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function detectWorkspaceAdapters(workspaceRoot, attachments = undefined) {
|
|
97
|
+
const attachmentList = attachments ?? readAdapterAttachments(workspaceRoot).attachments ?? [];
|
|
98
|
+
const byId = new Map(attachmentList.map((attachment) => [attachment.adapter_id, attachment]));
|
|
99
|
+
return ADAPTER_DEFINITIONS.map((definition) => {
|
|
100
|
+
const attached = byId.get(definition.adapter_id);
|
|
101
|
+
const detectedPaths = detectPaths(workspaceRoot, definition.detection_paths);
|
|
102
|
+
const status = profileStatus(definition, attached, detectedPaths);
|
|
103
|
+
return {
|
|
104
|
+
adapter_id: definition.adapter_id,
|
|
105
|
+
label: definition.label,
|
|
106
|
+
category: definition.category,
|
|
107
|
+
description: definition.description,
|
|
108
|
+
capture_modes: [...definition.capture_modes],
|
|
109
|
+
detection_status: status,
|
|
110
|
+
attached: Boolean(attached),
|
|
111
|
+
ready: status === "ready" || status === "attached" || status === "detected",
|
|
112
|
+
detection_paths: definition.detection_paths.map((relPath) => resolve(workspaceRoot, relPath)),
|
|
113
|
+
detected_paths: detectedPaths,
|
|
114
|
+
session_id: attached?.session_id,
|
|
115
|
+
source_refs: detectedPaths,
|
|
116
|
+
notes: [
|
|
117
|
+
...definition.notes,
|
|
118
|
+
...(attached?.notes ? [String(attached.notes)] : []),
|
|
119
|
+
],
|
|
120
|
+
attached_at: attached?.attached_at,
|
|
121
|
+
updated_at: attached?.updated_at,
|
|
122
|
+
capture_mode: attached?.capture_mode,
|
|
123
|
+
label_override: attached?.label,
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|