@chrok/braid 0.1.2 → 0.2.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/CHANGELOG.md +167 -16
- package/README.md +232 -149
- package/ROADMAP.md +77 -27
- package/SECURITY.md +7 -3
- package/dist/adapters/openai.js +4 -4
- package/dist/budgets.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/merge-tools.d.ts +1 -1
- package/dist/merge-tools.js +8 -8
- package/dist/runtime.d.ts +4 -2
- package/dist/runtime.js +396 -227
- package/dist/types.d.ts +142 -19
- package/dist/validate.d.ts +7 -1
- package/dist/validate.js +143 -13
- package/dist/workspaces.d.ts +15 -2
- package/dist/workspaces.js +172 -88
- package/docs/benchmark.md +3 -0
- package/docs/compatibility.md +44 -3
- package/docs/examples.md +2 -1
- package/docs/execution-control.md +160 -0
- package/docs/releasing.md +66 -1
- package/docs/repository-settings.md +41 -2
- package/docs/resource-limits.md +15 -5
- package/examples/code-review.ts +3 -3
- package/examples/execution-control.ts +38 -0
- package/examples/failure-handling.ts +2 -2
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
# Braid
|
|
2
2
|
|
|
3
|
-
Braid is a small
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Braid is a small TypeScript runtime for LLM agent graphs with bounded loops,
|
|
4
|
+
live updates, and isolated Git worktrees. A parent submits a graph, runs
|
|
5
|
+
independent invocations concurrently, and can revise the graph or pause and
|
|
6
|
+
resume handoffs while it runs. Each execution retains its own outputs and
|
|
7
|
+
checkpoints; explicit `integrate` nodes apply selected work to the source checkout.
|
|
8
8
|
|
|
9
9
|
[](https://github.com/Epsirom/braid/actions/workflows/ci.yml)
|
|
10
|
+
[](https://www.npmjs.com/package/@chrok/braid)
|
|
11
|
+
[](https://www.npmjs.com/package/@chrok/pi-braid)
|
|
10
12
|
[](LICENSE)
|
|
11
13
|
|
|
12
|
-
**
|
|
13
|
-
|
|
14
|
+
**0.2 API:** Experimental, Node.js 22+, ESM. The framework-agnostic core
|
|
15
|
+
has no runtime dependencies; the OpenAI-compatible runner and Pi extension are
|
|
16
|
+
optional integrations. The npm badges show published versions; see
|
|
17
|
+
[GitHub releases](https://github.com/Epsirom/braid/releases) for release notes.
|
|
18
|
+
Read the [0.1 → 0.2 migration guide](docs/compatibility.md#migrating-from-01-to-02)
|
|
19
|
+
before upgrading. For the previous API, use the
|
|
20
|
+
[0.1.3 documentation](https://github.com/Epsirom/braid/tree/v0.1.3).
|
|
21
|
+
|
|
22
|
+
| Package | Purpose |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| [@chrok/braid](https://www.npmjs.com/package/@chrok/braid) | Core runtime and optional OpenAI-compatible runner |
|
|
25
|
+
| [@chrok/pi-braid](https://www.npmjs.com/package/@chrok/pi-braid) | Pi background jobs, execution controls, reminders, and live flow panel; installs the matching core dependency |
|
|
26
|
+
|
|
27
|
+
## What changed in 0.2?
|
|
28
|
+
|
|
29
|
+
- **Editable graphs, captured executions.** `startBraid` exposes revision-checked
|
|
30
|
+
updates and pause/resume; `executionId` identifies a particular invocation,
|
|
31
|
+
while `nodeId` identifies its reusable definition.
|
|
32
|
+
- **Bounded refinement loops.** Explicit feedback edges revisit nodes with fresh
|
|
33
|
+
workspaces. Each loop has a finite iteration limit, and `maxExecutions` caps
|
|
34
|
+
materialized executions across the whole run, including live updates.
|
|
35
|
+
- **Explicit workspace integration.** Read-only workers inspect isolated
|
|
36
|
+
predecessor snapshots. `merge` combines changes in a new worktree;
|
|
37
|
+
`integrate` writes to the source checkout. Final integration is never implicit.
|
|
38
|
+
- **Per-execution failure policy.** Optional failures keep their errors and
|
|
39
|
+
partial work for recovery. Set `requireSuccess: true` to fail the whole run.
|
|
40
|
+
|
|
41
|
+
See [execution control](docs/execution-control.md) for the full contract and
|
|
42
|
+
[ROADMAP.md](ROADMAP.md) for the current scope and remaining work.
|
|
14
43
|
|
|
15
44
|
## Why Braid?
|
|
16
45
|
|
|
@@ -67,8 +96,8 @@ try {
|
|
|
67
96
|
|
|
68
97
|
For a live provider, use the adapter in the API example below. Installation and
|
|
69
98
|
running the example above do not make model requests. In a Git checkout, Braid
|
|
70
|
-
creates
|
|
71
|
-
|
|
99
|
+
creates a fresh worktree for each execution. Only explicit `integrate` nodes
|
|
100
|
+
write results back to the source checkout. Read [workspace behavior](#worktrees-and-merge-agents)
|
|
72
101
|
before running a custom adapter against a repository.
|
|
73
102
|
|
|
74
103
|
## Install in Pi
|
|
@@ -80,7 +109,9 @@ pi install npm:@chrok/pi-braid
|
|
|
80
109
|
```
|
|
81
110
|
|
|
82
111
|
Run `/reload`, ask Pi to analyze a task with Braid, and open `/braid` to inspect
|
|
83
|
-
the job.
|
|
112
|
+
the job. npm installs the exact matching core dependency; no checkout is needed
|
|
113
|
+
for published versions. To try 0.2 before publication, follow the
|
|
114
|
+
[local Pi installation guide](integrations/pi/README.md#install-this-local-checkout-in-pi).
|
|
84
115
|
|
|
85
116
|

|
|
86
117
|
|
|
@@ -163,28 +194,111 @@ output. The graph has no special fork, branch, or join nodes.
|
|
|
163
194
|
### Graph schema
|
|
164
195
|
|
|
165
196
|
```ts
|
|
166
|
-
type
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
197
|
+
type NodePrompt = string | {
|
|
198
|
+
template: string;
|
|
199
|
+
variables: Readonly<Record<string, string>>;
|
|
200
|
+
};
|
|
201
|
+
type BraidInputNode =
|
|
202
|
+
| { type: "execute"; id: string; prompt: NodePrompt; model?: string;
|
|
203
|
+
workspace?: "read-only" | "worktree"; notifyOnCompletion?: boolean;
|
|
204
|
+
requireSuccess?: boolean; pauseAfter?: boolean }
|
|
205
|
+
| { type: "decision"; id: string; prompt: NodePrompt;
|
|
206
|
+
choices: readonly string[]; model?: string; workspace?: "read-only" | "worktree"; notifyOnCompletion?: boolean;
|
|
207
|
+
requireSuccess?: boolean; pauseAfter?: boolean }
|
|
208
|
+
| { type: "merge" | "integrate"; id: string; prompt?: NodePrompt; model?: string;
|
|
209
|
+
notifyOnCompletion?: boolean;
|
|
210
|
+
requireSuccess?: boolean; pauseAfter?: boolean };
|
|
211
|
+
|
|
212
|
+
type Edge = { from: string; to: string; choice?: string; feedback?: string; executionId?: string };
|
|
213
|
+
type BraidInput = {
|
|
214
|
+
goal: string;
|
|
215
|
+
nodes: readonly BraidInputNode[];
|
|
216
|
+
edges: readonly Edge[];
|
|
217
|
+
loops?: readonly { id: string; entry: string; maxIterations: number }[];
|
|
218
|
+
promptTemplates?: Readonly<Record<string, string>>;
|
|
219
|
+
};
|
|
174
220
|
```
|
|
175
221
|
|
|
176
|
-
IDs are unique, non-empty strings.
|
|
177
|
-
non-empty strings when present. Decision choices
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
222
|
+
IDs are unique, non-empty strings. Goals, models, choices, plain-string prompts,
|
|
223
|
+
and rendered prompts must be non-empty strings when present. Decision choices
|
|
224
|
+
must be non-empty and unique.
|
|
225
|
+
`workspace` is optional on execute/decision nodes and forbidden on merge/integrate
|
|
226
|
+
nodes. Both read-only and writable Git executions get isolated predecessor
|
|
227
|
+
snapshots; read-only disables write capabilities. Outside Git all filesystem
|
|
228
|
+
access is read-only. `notifyOnCompletion` requests host reminders, `pauseAfter`
|
|
229
|
+
holds outgoing scheduling, and `requireSuccess` makes failure abort the whole run.
|
|
230
|
+
All three booleans default to false.
|
|
231
|
+
|
|
232
|
+
Unknown fields, missing references, duplicate exact edges, and undeclared cycles
|
|
233
|
+
are rejected. Declared structured loops require a finite iteration limit and an
|
|
234
|
+
explicit feedback edge. Disconnected components are allowed; every root runs.
|
|
235
|
+
Only decision nodes may have choice edges, and choices need not all have exits.
|
|
236
|
+
|
|
237
|
+
`validateGraph(input)` validates without execution. Invalid graphs raise
|
|
238
|
+
`GraphValidationError`; invalid options raise `TypeError`. Optional invocation
|
|
239
|
+
failures remain in execution history; required or infrastructure failures make
|
|
240
|
+
the run fail. See [execution control](docs/execution-control.md) for the complete
|
|
241
|
+
loop, live update, pause/resume, and failure contract.
|
|
242
|
+
|
|
243
|
+
### Reusable prompt templates
|
|
244
|
+
|
|
245
|
+
Define shared instructions once in `promptTemplates`, then give each node a
|
|
246
|
+
template name and explicit string variables. The same representation is accepted
|
|
247
|
+
by the core API and Pi's `braid` tool:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"goal": "Review the runtime and validation code",
|
|
252
|
+
"promptTemplates": {
|
|
253
|
+
"review": "Review {{target}} for {{focus}}. Inspect source and tests, then report findings with file references and supporting evidence."
|
|
254
|
+
},
|
|
255
|
+
"nodes": [
|
|
256
|
+
{
|
|
257
|
+
"type": "execute", "id": "runtime", "workspace": "read-only",
|
|
258
|
+
"prompt": {
|
|
259
|
+
"template": "review",
|
|
260
|
+
"variables": { "target": "src/runtime.ts", "focus": "scheduling and cancellation" }
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"type": "execute", "id": "validation", "workspace": "read-only",
|
|
265
|
+
"prompt": {
|
|
266
|
+
"template": "review",
|
|
267
|
+
"variables": { "target": "src/validate.ts", "focus": "input validation" }
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"edges": []
|
|
272
|
+
}
|
|
273
|
+
```
|
|
183
274
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
275
|
+
- Placeholders use `{{name}}`, with optional surrounding whitespace inside the
|
|
276
|
+
braces. Names match `[A-Za-z_][A-Za-z0-9_]*`; repeated placeholders reuse the
|
|
277
|
+
same value. Template names are any non-empty strings.
|
|
278
|
+
- `variables` is required, including `{}` for a constant template. Values must
|
|
279
|
+
be strings and must match the template's variables exactly. Empty values are
|
|
280
|
+
allowed if the complete rendered prompt is still non-empty.
|
|
281
|
+
- Values are inserted literally once: no expressions, recursive expansion,
|
|
282
|
+
escaping, environment lookup, or access to other nodes' outputs. To insert
|
|
283
|
+
literal double braces into a template, pass them as a variable value.
|
|
284
|
+
- Unknown templates, missing/unused variables, invalid template syntax, and
|
|
285
|
+
blank rendered prompts throw `GraphValidationError` before any model call.
|
|
286
|
+
All declared templates are syntax-checked, including unused ones. Errors
|
|
287
|
+
identify the template and, for node references/rendering, the affected node.
|
|
288
|
+
- Templates work on execute, decision, merge, and integrate prompts. Omitted
|
|
289
|
+
merge/integrate prompts retain their defaults. Plain-string prompts are never rendered,
|
|
290
|
+
even when they contain `{{...}}`.
|
|
291
|
+
|
|
292
|
+
Templates reduce duplicate text in graph/tool-call arguments; every worker still
|
|
293
|
+
receives its fully rendered prompt. Rendering and input snapshotting happen
|
|
294
|
+
before asynchronous execution. Templates are local to one submission, with no
|
|
295
|
+
saved registry or new runtime dependencies.
|
|
296
|
+
|
|
297
|
+
`BraidInputNode`, `NodePrompt`, and `PromptTemplateReference` describe compact
|
|
298
|
+
inputs. `BraidNode`, `ExecuteNode`, `DecisionNode`, `MergeNode`, `IntegrateNode`,
|
|
299
|
+
and `ModelRequest.node` keep their string-prompt types for runners. Core applies the
|
|
300
|
+
same non-empty prompt validation after rendering; it does not impose a prompt
|
|
301
|
+
length or token cap (see [resource limits](docs/resource-limits.md)).
|
|
188
302
|
|
|
189
303
|
### Options
|
|
190
304
|
|
|
@@ -193,9 +307,10 @@ with `status: "failed"` instead of discarding the run's successful outputs.
|
|
|
193
307
|
| `runner` | Required | A fresh, isolated invocation for each call |
|
|
194
308
|
| `defaultModel` | Adapter default | Overridden by each node's `model` |
|
|
195
309
|
| `cwd` | `process.cwd()` | Source checkout for core-managed worktrees; non-Git directories grant read-only capabilities |
|
|
310
|
+
| `maxExecutions` | `1000` | Total materialized executions, including skipped branches, across all rounds and updates; positive safe integer |
|
|
196
311
|
| `maxConcurrency` | `4` | Maximum simultaneous runtime-managed node invocations; positive integer |
|
|
197
312
|
| `nodeTimeoutMs` | `60_000` | Separate deadline for each node, starting when it runs (not while queued) |
|
|
198
|
-
| `graphTimeoutMs` | `300_000` | Whole execution deadline, including node queueing; starts after validation |
|
|
313
|
+
| `graphTimeoutMs` | `300_000` | Whole execution deadline, including node queueing and paused gates; starts after validation |
|
|
199
314
|
| `signal` | None | Caller cancellation signal; aborts running nodes and marks queued nodes cancelled |
|
|
200
315
|
| `onEvent` | None | Live observer for graph/node creation, readiness, starts, handoffs, completions, skips, failures, and graph completion |
|
|
201
316
|
|
|
@@ -242,9 +357,16 @@ A pending node waits until **all incoming edges are resolved**. Then:
|
|
|
242
357
|
Failures propagate as context through unconditional edges, allowing successors
|
|
243
358
|
and merge agents to inspect errors and recover partial work. Failed decisions
|
|
244
359
|
cannot activate choice-labelled edges; their unconditional successors can run.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
360
|
+
There are no automatic retries. Failures are optional by default; a
|
|
361
|
+
`requireSuccess: true` execution failure aborts the whole run, cancels siblings,
|
|
362
|
+
and drains writes and cleanup. The policy is captured when the instance is admitted.
|
|
363
|
+
Graph deadlines and execution limits remain active while paused.
|
|
364
|
+
|
|
365
|
+
Nodes are mutable definitions; execution instances retain captured prompts and
|
|
366
|
+
inputs. `startBraid` exposes revision-checked `update`, `resume`, `snapshot`, and
|
|
367
|
+
`cancel`. Updates can change running nodes, completed nodes, edges, templates, and
|
|
368
|
+
loop topology at any time before finalization. Completion uses the latest graph.
|
|
369
|
+
See [loops and live updates](docs/execution-control.md) for schemas and examples.
|
|
248
370
|
|
|
249
371
|
## Execution events
|
|
250
372
|
|
|
@@ -254,35 +376,22 @@ timestamped. The log is diagnostic data and does not alter scheduling; observer
|
|
|
254
376
|
exceptions and rejected promises are ignored. Event payloads are frozen before
|
|
255
377
|
being retained and delivered.
|
|
256
378
|
|
|
257
|
-
The event sequence includes
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
`node_completed` and `node_failed` include node latency; `node_completed` also
|
|
274
|
-
includes the selected decision and reported usage when available. Event output
|
|
275
|
-
is diagnostic context and may be previewed by an adapter; `BraidResult.events`
|
|
276
|
-
retains the complete event payloads.
|
|
277
|
-
|
|
278
|
-
The core event stream is intentionally a log, not a second control API. It does
|
|
279
|
-
not permit graph mutation or runtime intervention. A Pi adapter can use it to
|
|
280
|
-
render live topology, handoffs, failures, and active nodes without reconstructing
|
|
281
|
-
scheduler state from final results. Tool selection remains the responsibility of
|
|
282
|
-
the host agent; the optional Pi adapter supplies explicit proactive-use guidance
|
|
283
|
-
so Braid is considered for complex multi-branch reasoning without forcing it for
|
|
284
|
-
every prompt. In the Pi adapter, Git nodes can inspect and edit individual
|
|
285
|
-
worktrees; nodes outside Git stay read-only. Merge agents handle integration; the parent reviews results and runs shell commands and tests.
|
|
379
|
+
The event sequence includes initial `graph_created`, `node_created`, and
|
|
380
|
+
`edge_created`; revision-bearing `graph_updated`; and per-instance
|
|
381
|
+
`node_runnable`, `node_started`, `workspace_updated`, `handoff`, `node_completed`,
|
|
382
|
+
`node_failed`, or `node_skipped`. Instance events carry `executionId`, admission
|
|
383
|
+
`revision`, and optional `loopId`/`iteration`. Handoffs also identify their exact
|
|
384
|
+
`fromExecutionId`.
|
|
385
|
+
|
|
386
|
+
`loop_started`/`loop_completed` report rounds. `execution_paused` and
|
|
387
|
+
`execution_resumed` report gates. `graph_completed` or `graph_failed` terminates
|
|
388
|
+
the log. Completion/failure is published after checkpointing. Node events repeat
|
|
389
|
+
with distinct execution IDs when the same definition runs again.
|
|
390
|
+
|
|
391
|
+
Use `startBraid` methods to control a run. Event callbacks are observers; throwing
|
|
392
|
+
or rejecting does not change scheduler behavior. The Pi panel displays current
|
|
393
|
+
topology, iterations, pause gates, and a bounded event preview; complete history
|
|
394
|
+
remains available in the result.
|
|
286
395
|
|
|
287
396
|
## Context isolation and model runners
|
|
288
397
|
|
|
@@ -308,7 +417,8 @@ merge agents. Adapters must enforce workspace capabilities and wrap mutating
|
|
|
308
417
|
file tools in `request.withWorkspaceWrite(operation)`, so cleanup waits for
|
|
309
418
|
in-flight writes and rejects later writes. Core Git mutations use this barrier.
|
|
310
419
|
The included Pi adapter provides guarded `write`/`edit` alongside its read tools.
|
|
311
|
-
|
|
420
|
+
For read-only workspaces, adapters must omit mutating tools; the core write
|
|
421
|
+
barrier also rejects writes. This includes all nodes outside Git. Pi never provides
|
|
312
422
|
`bash`, `powershell`, or a test runner to nodes.
|
|
313
423
|
|
|
314
424
|
`request.predecessors` contains direct active predecessors in incoming-edge
|
|
@@ -327,91 +437,66 @@ Read tools follow the host filesystem permissions and are not a security sandbox
|
|
|
327
437
|
|
|
328
438
|
### Worktrees and merge agents
|
|
329
439
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
sources. The merge agent receives predecessor errors, workspace paths, and Git
|
|
343
|
-
checkpoint refs and operates directly in the invoking checkout. **Core does not
|
|
344
|
-
run merge, cherry-pick, or apply automatically.** The agent reviews each source,
|
|
345
|
-
chooses which changes to integrate and how, resolves conflicts, then calls the
|
|
346
|
-
`finish_merge` tool with exactly one disposition and reason per source:
|
|
440
|
+
Every Git execution owns a fresh worktree. Roots use the job's initial snapshot
|
|
441
|
+
of tracked edits and non-ignored untracked files without changing the real index.
|
|
442
|
+
Successors use immutable predecessor checkpoints; read-only executions use the
|
|
443
|
+
same snapshot rules with writes disabled. Independent code branches require an
|
|
444
|
+
explicit merge before an ordinary worker consumes them together.
|
|
445
|
+
|
|
446
|
+
- `merge` combines selected predecessor checkpoints in a **new isolated worktree**.
|
|
447
|
+
- `integrate` applies selected results to the **invoking working checkout**.
|
|
448
|
+
|
|
449
|
+
There is no automatic final integration. Add an explicit integrate node when
|
|
450
|
+
results should reach the working branch. Both operations accept optional prompts
|
|
451
|
+
and require `finish_merge` with exactly one disposition per source:
|
|
347
452
|
|
|
348
453
|
```json
|
|
349
454
|
{ "dispositions": [
|
|
350
|
-
{ "
|
|
351
|
-
{ "nodeId": "alternative", "disposition": "discarded", "reason": "The selected implementation supersedes this alternative" }
|
|
455
|
+
{ "executionId": "<source-execution-id>", "disposition": "integrated", "reason": "Applied the reviewed checkpoint" }
|
|
352
456
|
] }
|
|
353
457
|
```
|
|
354
458
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
Cancellation or graph timeout prevents new merge agents from starting. Core waits
|
|
387
|
-
for tracked writes, archives remaining work, and removes its worktrees. Merge
|
|
388
|
-
agent failure follows the same archive/cleanup path. It does not reset the source
|
|
389
|
-
checkout: partial integration or Git conflict state may remain for review, with
|
|
390
|
-
`backupRef` available for recovery. Filesystem/Git cleanup errors are reported as
|
|
391
|
-
`CLEANUP_FAILED` with retained workspace paths; a process crash cannot run cleanup.
|
|
392
|
-
|
|
393
|
-
`result.workspaces` and `node.workspace` report paths, states, reasons, and refs.
|
|
394
|
-
A cleaned worktree path is historical; use `checkpointRef` to recover its contents:
|
|
395
|
-
|
|
396
|
-
```sh
|
|
397
|
-
git show <checkpointRef>:path/to/file
|
|
398
|
-
git diff <snapshotCommit> <checkpointRef>
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
Recovery refs live under `refs/braid/checkpoints/` and `refs/braid/merge-backups/`.
|
|
402
|
-
After reviewing them, remove a particular ref with `git update-ref -d <ref>`.
|
|
403
|
-
They preserve recoverable Git objects without retaining worktree directories.
|
|
459
|
+
The agent chooses merge, cherry-pick, apply, restore, or file edits; core never
|
|
460
|
+
chooses for it. Sources include bounded diffs relative to the initial job
|
|
461
|
+
snapshot. Integrate also receives the current source checkout's dirty status and
|
|
462
|
+
must preserve unrelated user changes. Unresolved conflicts, a missing finish
|
|
463
|
+
call, or an `archived` disposition fail the operation.
|
|
464
|
+
|
|
465
|
+
The model-facing Git tool separates `command` from `args`, for example
|
|
466
|
+
`{ "command": "show", "args": ["REF:path"] }`. The programmatic `request.git`
|
|
467
|
+
accepts the complete argument array. Duplicate command prefixes, network Git,
|
|
468
|
+
branch switching, and filesystem-boundary overrides are rejected.
|
|
469
|
+
|
|
470
|
+
Instances checkpoint before downstream admission. All source checkpoints remain
|
|
471
|
+
reusable; no merge consumes or deletes a predecessor's result. Job cleanup
|
|
472
|
+
archives and removes owned worktrees, retaining refs under
|
|
473
|
+
`refs/braid/checkpoints/`. Integration additionally captures a pre-write
|
|
474
|
+
`backupRef` under `refs/braid/merge-backups/`. Target workspace `dispositions`
|
|
475
|
+
record the agent's source selections.
|
|
476
|
+
|
|
477
|
+
`result.workspaces` is keyed by execution ID; `result.nodes[id].workspace` is the
|
|
478
|
+
latest convenience view. Cleaned paths are historical; recover their contents
|
|
479
|
+
with `git show <checkpointRef>:path` or inspect changes with
|
|
480
|
+
`git diff <snapshotCommit> <checkpointRef>`. Remove reviewed refs explicitly with
|
|
481
|
+
`git update-ref -d <ref>`.
|
|
482
|
+
|
|
483
|
+
Cancellation drains tracked writes before cleanup. Checkpoint/cleanup failures
|
|
484
|
+
fail the run and retain unsafe-to-remove paths. Failed integration may leave
|
|
485
|
+
partial source edits or conflict state with its backup available for recovery;
|
|
486
|
+
it does not reset the checkout. Source integrations serialize within one process;
|
|
487
|
+
this does not coordinate parent edits or other processes. Read-only workers remain
|
|
488
|
+
isolated from those source edits. See [execution control](docs/execution-control.md).
|
|
404
489
|
|
|
405
490
|
**The adapter is a trust boundary, not a security sandbox.** It must avoid shared
|
|
406
491
|
conversation state, expose only its declared capabilities, and forward `signal` to its provider.
|
|
407
492
|
The core never gives the model arbitrary code execution or a recursive Braid
|
|
408
493
|
tool. An optional Pi adapter translates this same contract without changing the
|
|
409
|
-
runtime; the core
|
|
494
|
+
runtime; the core package does not depend on Pi. See
|
|
410
495
|
[`integrations/pi/README.md`](integrations/pi/README.md) for installation and testing.
|
|
411
496
|
|
|
412
497
|
The included OpenAI-compatible adapter uses fresh Chat Completions contexts,
|
|
413
498
|
a strict `decide({ choice })` tool and one tool-free continuation for decisions.
|
|
414
|
-
Merge nodes use a local `git` / `finish_merge` tool loop; ordinary OpenAI nodes
|
|
499
|
+
Merge/integrate nodes use a local `git` / `finish_merge` tool loop; ordinary OpenAI nodes
|
|
415
500
|
have no filesystem tools. Pi exposes read and guarded write tools plus these
|
|
416
501
|
core Git/merge tools. Tool errors go back to merge agents for recovery. Both
|
|
417
502
|
adapters sum usage across their model calls and forward cancellation.
|
|
@@ -439,16 +524,14 @@ remain 60 seconds per node and 5 minutes per graph.
|
|
|
439
524
|
`BraidResult` contains:
|
|
440
525
|
|
|
441
526
|
- `status`: `completed` or `failed`.
|
|
442
|
-
- `terminalOutputs`:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
- `
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
choice for debugging; choice-labelled edges still remain blocked.
|
|
451
|
-
- `workspaces`: Git workspace states and recovery refs, including cleaned sources.
|
|
527
|
+
- `terminalOutputs`: latest successful unconsumed outputs keyed by node ID.
|
|
528
|
+
- `terminalExecutionIds`: exact IDs of the successful execution endpoints.
|
|
529
|
+
- `nodes`: latest states/results per node ID, including output, error, usage,
|
|
530
|
+
timing, and workspace metadata. A pending definition has no execution ID yet.
|
|
531
|
+
- `executions`: all immutable invocation definitions and their final results,
|
|
532
|
+
keyed by execution ID, including historical rounds and deleted definitions.
|
|
533
|
+
- `revision`: the last committed graph revision.
|
|
534
|
+
- `workspaces`: execution-keyed workspace states, recovery refs, and merge choices.
|
|
452
535
|
- `events`: the immutable execution log described above. `onEvent` observes live
|
|
453
536
|
copies of the same state transitions while the run is in progress.
|
|
454
537
|
- `metadata`: run/root identity, timestamps, monotonic latency, summed reported
|
|
@@ -474,7 +557,7 @@ uncancelled provider work after timeout can outlive a slot.
|
|
|
474
557
|
|
|
475
558
|
- [`src/types.ts`](src/types.ts): public graph, provider, and result types.
|
|
476
559
|
- [`src/validate.ts`](src/validate.ts): strict validation, graph snapshot,
|
|
477
|
-
dependency indexes, and iterative
|
|
560
|
+
dependency indexes, and iterative validation of acyclic regions and structured loops.
|
|
478
561
|
- [`src/runtime.ts`](src/runtime.ts): edge resolution, explicit state transitions,
|
|
479
562
|
bounded concurrent scheduling, invocation deadlines, execution events, and result accounting.
|
|
480
563
|
- [`src/workspaces.ts`](src/workspaces.ts): Git snapshots, checkpoint refs, merge
|
|
@@ -484,18 +567,18 @@ uncancelled provider work after timeout can outlive a slot.
|
|
|
484
567
|
- [`test/`](test/): deterministic scheduling, execution-event, and intercepted HTTP/tool tests.
|
|
485
568
|
|
|
486
569
|
There is one in-memory execution context per run, plus a process-local mutex
|
|
487
|
-
per source checkout for
|
|
570
|
+
per source checkout for integrate agents. Worktree registration and removal are
|
|
488
571
|
serialized per common Git directory within the process; model calls remain
|
|
489
572
|
concurrent. These locks do not coordinate other processes. `rootRunId` equals
|
|
490
|
-
`runId
|
|
573
|
+
`runId`. Centralized invocation admission and
|
|
491
574
|
usage aggregation leave places to thread a shared root budget in a future
|
|
492
575
|
nested-run implementation; **nested runs and shared budget enforcement are not
|
|
493
|
-
implemented**. The current scheduler deliberately rescans a small
|
|
576
|
+
implemented**. The current scheduler deliberately rescans a small graph after
|
|
494
577
|
completions; `onEvent` is an observer for diagnostics and visualization, not a
|
|
495
578
|
scheduler event bus.
|
|
496
579
|
|
|
497
|
-
Out of scope:
|
|
498
|
-
|
|
580
|
+
Out of scope: arbitrary/unstructured cycles, nested/overlapping loops, arbitrary
|
|
581
|
+
code nodes, durable workflow recovery, saved templates, a graphical editing UI,
|
|
499
582
|
and recursive Braid calls from model nodes.
|
|
500
583
|
|
|
501
584
|
## Contributing and project status
|