@chrok/braid 0.1.3 → 0.2.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.
- package/CHANGELOG.md +172 -20
- package/CONTRIBUTING.md +9 -0
- package/README.md +232 -191
- package/ROADMAP.md +77 -27
- package/SECURITY.md +30 -4
- 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 +392 -242
- package/dist/types.d.ts +139 -20
- package/dist/validate.d.ts +7 -1
- package/dist/validate.js +140 -15
- package/dist/workspaces.d.ts +14 -4
- package/dist/workspaces.js +161 -116
- package/docs/benchmark.md +3 -0
- package/docs/compatibility.md +47 -11
- package/docs/examples.md +2 -1
- package/docs/execution-control.md +160 -0
- package/docs/releasing.md +66 -1
- package/docs/repository-settings.md +34 -0
- package/docs/resource-limits.md +13 -5
- package/examples/execution-control.ts +38 -0
- package/examples/failure-handling.ts +2 -2
- package/package.json +11 -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,20 +96,22 @@ 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
|
|
75
104
|
|
|
76
|
-
With Pi 0.
|
|
105
|
+
With Pi 1.0.1 and Node.js 22.19+:
|
|
77
106
|
|
|
78
107
|
```sh
|
|
79
108
|
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 the current source checkout, follow the
|
|
114
|
+
[local Pi installation guide](integrations/pi/README.md#install-this-local-checkout-in-pi).
|
|
84
115
|
|
|
85
116
|

|
|
86
117
|
|
|
@@ -163,34 +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
|
-
| { type: "
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
+
};
|
|
220
|
+
```
|
|
221
|
+
|
|
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
|
+
}
|
|
175
273
|
```
|
|
176
274
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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)).
|
|
194
302
|
|
|
195
303
|
### Options
|
|
196
304
|
|
|
@@ -199,9 +307,10 @@ with `status: "failed"` instead of discarding the run's successful outputs.
|
|
|
199
307
|
| `runner` | Required | A fresh, isolated invocation for each call |
|
|
200
308
|
| `defaultModel` | Adapter default | Overridden by each node's `model` |
|
|
201
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 |
|
|
202
311
|
| `maxConcurrency` | `4` | Maximum simultaneous runtime-managed node invocations; positive integer |
|
|
203
312
|
| `nodeTimeoutMs` | `60_000` | Separate deadline for each node, starting when it runs (not while queued) |
|
|
204
|
-
| `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 |
|
|
205
314
|
| `signal` | None | Caller cancellation signal; aborts running nodes and marks queued nodes cancelled |
|
|
206
315
|
| `onEvent` | None | Live observer for graph/node creation, readiness, starts, handoffs, completions, skips, failures, and graph completion |
|
|
207
316
|
|
|
@@ -248,9 +357,16 @@ A pending node waits until **all incoming edges are resolved**. Then:
|
|
|
248
357
|
Failures propagate as context through unconditional edges, allowing successors
|
|
249
358
|
and merge agents to inspect errors and recover partial work. Failed decisions
|
|
250
359
|
cannot activate choice-labelled edges; their unconditional successors can run.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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.
|
|
254
370
|
|
|
255
371
|
## Execution events
|
|
256
372
|
|
|
@@ -260,37 +376,22 @@ timestamped. The log is diagnostic data and does not alter scheduling; observer
|
|
|
260
376
|
exceptions and rejected promises are ignored. Event payloads are frozen before
|
|
261
377
|
being retained and delivered.
|
|
262
378
|
|
|
263
|
-
The event sequence includes
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
`node_completed` and `node_failed` include node latency; `node_completed` also
|
|
281
|
-
includes the selected decision and reported usage when available. Event output
|
|
282
|
-
is diagnostic context and may be previewed by an adapter; `BraidResult.events`
|
|
283
|
-
retains the complete event payloads.
|
|
284
|
-
|
|
285
|
-
The core event stream is intentionally a log, not a second control API. It does
|
|
286
|
-
not permit graph mutation or runtime intervention. A Pi adapter can use it to
|
|
287
|
-
render live topology, handoffs, failures, and active nodes without reconstructing
|
|
288
|
-
scheduler state from final results. Tool selection remains the responsibility of
|
|
289
|
-
the host agent; the optional Pi adapter supplies explicit proactive-use guidance
|
|
290
|
-
so Braid is considered for complex multi-branch reasoning without forcing it for
|
|
291
|
-
every prompt. In the Pi adapter, nodes can inspect the live source read-only or
|
|
292
|
-
edit individual Git worktrees; nodes outside Git stay read-only. Merge agents
|
|
293
|
-
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.
|
|
294
395
|
|
|
295
396
|
## Context isolation and model runners
|
|
296
397
|
|
|
@@ -336,124 +437,66 @@ Read tools follow the host filesystem permissions and are not a security sandbox
|
|
|
336
437
|
|
|
337
438
|
### Worktrees and merge agents
|
|
338
439
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
about files. They read the original `cwd`, including ignored files accessible to
|
|
352
|
-
the adapter, without creating a snapshot, worktree, checkpoint, or merge source.
|
|
353
|
-
In Git they retain inspection commands (`status`, `diff`, `show`, `log`,
|
|
354
|
-
`ls-files`, `rev-parse`), with optional Git index writes disabled. Relative paths
|
|
355
|
-
use the original `cwd`, including when it is a subdirectory of the repository.
|
|
356
|
-
These reads observe the live checkout: parent edits or concurrent merge nodes
|
|
357
|
-
may change files during execution. Use worktree mode when a fixed snapshot is
|
|
358
|
-
needed. Predecessor edits are visible in the source only after integration;
|
|
359
|
-
their worktrees/checkpoints can still be inspected explicitly.
|
|
360
|
-
|
|
361
|
-
Explicit read-only allocations appear in `workspace_updated`, node/predecessor
|
|
362
|
-
workspace metadata, and `result.workspaces` with mode `read-only` and state
|
|
363
|
-
`ready`. They have no cleanup lifecycle or recovery refs. Implicit non-Git
|
|
364
|
-
read-only runs retain their existing event/result behavior. A custom runner is
|
|
365
|
-
trusted code and must honor the workspace capability; this is not an OS sandbox.
|
|
366
|
-
|
|
367
|
-
For a mixed graph, give review branches `workspace: "read-only"` and leave
|
|
368
|
-
implementation branches in worktree mode. Use a read-only execute node to
|
|
369
|
-
summarize findings; a merge node integrates file changes and does not accept
|
|
370
|
-
the `workspace` field.
|
|
371
|
-
|
|
372
|
-
Add `{ type: "merge", id: "integrate" }` with incoming edges from any number of
|
|
373
|
-
sources. The merge agent receives predecessor errors, workspace paths, and Git
|
|
374
|
-
checkpoint refs and operates directly in the invoking checkout. **Core does not
|
|
375
|
-
run merge, cherry-pick, or apply automatically.** The agent reviews each source,
|
|
376
|
-
chooses which changes to integrate and how, resolves conflicts, then calls the
|
|
377
|
-
`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:
|
|
378
452
|
|
|
379
453
|
```json
|
|
380
454
|
{ "dispositions": [
|
|
381
|
-
{ "
|
|
382
|
-
{ "nodeId": "alternative", "disposition": "discarded", "reason": "The selected implementation supersedes this alternative" }
|
|
455
|
+
{ "executionId": "<source-execution-id>", "disposition": "integrated", "reason": "Applied the reviewed checkpoint" }
|
|
383
456
|
] }
|
|
384
457
|
```
|
|
385
458
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
When worktrees with changes remain, core appends an ordinary merge
|
|
418
|
-
agent named `__braid_merge__` (with a suffix if needed), using the run's default
|
|
419
|
-
model. It appears in results, events, usage, and terminal outputs. Merge nodes
|
|
420
|
-
are exclusive within a graph and serialized per source checkout across runs in
|
|
421
|
-
the same process. Avoid concurrent external edits to that checkout while merging;
|
|
422
|
-
this lock does not coordinate other processes or the parent editor.
|
|
423
|
-
|
|
424
|
-
Analysis-only graphs therefore keep their declared terminal outputs and do not
|
|
425
|
-
incur an automatic merge model call. Consumers should not assume that every Git
|
|
426
|
-
run includes `__braid_merge__`; use `terminalOutputs` for the completed endpoints.
|
|
427
|
-
|
|
428
|
-
Cancellation or graph timeout prevents new merge agents from starting. Core waits
|
|
429
|
-
for tracked writes, archives remaining work, and removes its worktrees. Merge
|
|
430
|
-
agent failure follows the same archive/cleanup path. It does not reset the source
|
|
431
|
-
checkout: partial integration or Git conflict state may remain for review, with
|
|
432
|
-
`backupRef` available for recovery. Filesystem/Git cleanup errors are reported as
|
|
433
|
-
`CLEANUP_FAILED` with retained workspace paths; a process crash cannot run cleanup.
|
|
434
|
-
|
|
435
|
-
`result.workspaces` and `node.workspace` report paths, states, reasons, and refs.
|
|
436
|
-
A cleaned worktree path is historical; use `checkpointRef` to recover its contents:
|
|
437
|
-
|
|
438
|
-
```sh
|
|
439
|
-
git show <checkpointRef>:path/to/file
|
|
440
|
-
git diff <snapshotCommit> <checkpointRef>
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
Recovery refs live under `refs/braid/checkpoints/` and `refs/braid/merge-backups/`.
|
|
444
|
-
After reviewing them, remove a particular ref with `git update-ref -d <ref>`.
|
|
445
|
-
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).
|
|
446
489
|
|
|
447
490
|
**The adapter is a trust boundary, not a security sandbox.** It must avoid shared
|
|
448
491
|
conversation state, expose only its declared capabilities, and forward `signal` to its provider.
|
|
449
492
|
The core never gives the model arbitrary code execution or a recursive Braid
|
|
450
493
|
tool. An optional Pi adapter translates this same contract without changing the
|
|
451
|
-
runtime; the core
|
|
494
|
+
runtime; the core package does not depend on Pi. See
|
|
452
495
|
[`integrations/pi/README.md`](integrations/pi/README.md) for installation and testing.
|
|
453
496
|
|
|
454
497
|
The included OpenAI-compatible adapter uses fresh Chat Completions contexts,
|
|
455
498
|
a strict `decide({ choice })` tool and one tool-free continuation for decisions.
|
|
456
|
-
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
|
|
457
500
|
have no filesystem tools. Pi exposes read and guarded write tools plus these
|
|
458
501
|
core Git/merge tools. Tool errors go back to merge agents for recovery. Both
|
|
459
502
|
adapters sum usage across their model calls and forward cancellation.
|
|
@@ -481,16 +524,14 @@ remain 60 seconds per node and 5 minutes per graph.
|
|
|
481
524
|
`BraidResult` contains:
|
|
482
525
|
|
|
483
526
|
- `status`: `completed` or `failed`.
|
|
484
|
-
- `terminalOutputs`:
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
- `
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
choice for debugging; choice-labelled edges still remain blocked.
|
|
493
|
-
- `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.
|
|
494
535
|
- `events`: the immutable execution log described above. `onEvent` observes live
|
|
495
536
|
copies of the same state transitions while the run is in progress.
|
|
496
537
|
- `metadata`: run/root identity, timestamps, monotonic latency, summed reported
|
|
@@ -516,7 +557,7 @@ uncancelled provider work after timeout can outlive a slot.
|
|
|
516
557
|
|
|
517
558
|
- [`src/types.ts`](src/types.ts): public graph, provider, and result types.
|
|
518
559
|
- [`src/validate.ts`](src/validate.ts): strict validation, graph snapshot,
|
|
519
|
-
dependency indexes, and iterative
|
|
560
|
+
dependency indexes, and iterative validation of acyclic regions and structured loops.
|
|
520
561
|
- [`src/runtime.ts`](src/runtime.ts): edge resolution, explicit state transitions,
|
|
521
562
|
bounded concurrent scheduling, invocation deadlines, execution events, and result accounting.
|
|
522
563
|
- [`src/workspaces.ts`](src/workspaces.ts): Git snapshots, checkpoint refs, merge
|
|
@@ -526,18 +567,18 @@ uncancelled provider work after timeout can outlive a slot.
|
|
|
526
567
|
- [`test/`](test/): deterministic scheduling, execution-event, and intercepted HTTP/tool tests.
|
|
527
568
|
|
|
528
569
|
There is one in-memory execution context per run, plus a process-local mutex
|
|
529
|
-
per source checkout for
|
|
570
|
+
per source checkout for integrate agents. Worktree registration and removal are
|
|
530
571
|
serialized per common Git directory within the process; model calls remain
|
|
531
572
|
concurrent. These locks do not coordinate other processes. `rootRunId` equals
|
|
532
|
-
`runId
|
|
573
|
+
`runId`. Centralized invocation admission and
|
|
533
574
|
usage aggregation leave places to thread a shared root budget in a future
|
|
534
575
|
nested-run implementation; **nested runs and shared budget enforcement are not
|
|
535
|
-
implemented**. The current scheduler deliberately rescans a small
|
|
576
|
+
implemented**. The current scheduler deliberately rescans a small graph after
|
|
536
577
|
completions; `onEvent` is an observer for diagnostics and visualization, not a
|
|
537
578
|
scheduler event bus.
|
|
538
579
|
|
|
539
|
-
Out of scope:
|
|
540
|
-
|
|
580
|
+
Out of scope: arbitrary/unstructured cycles, nested/overlapping loops, arbitrary
|
|
581
|
+
code nodes, durable workflow recovery, saved templates, a graphical editing UI,
|
|
541
582
|
and recursive Braid calls from model nodes.
|
|
542
583
|
|
|
543
584
|
## Contributing and project status
|