@chrok/braid 0.2.1 → 0.3.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 +66 -0
- package/README.md +40 -15
- package/ROADMAP.md +20 -6
- package/SECURITY.md +12 -3
- package/dist/merge-tools.js +1 -1
- package/dist/runtime.js +2 -0
- package/dist/types.d.ts +2 -0
- package/dist/validate.js +8 -8
- package/dist/workspaces.d.ts +1 -1
- package/dist/workspaces.js +4 -4
- package/docs/compatibility.md +35 -6
- package/docs/execution-control.md +11 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.0 — 2026-10-04
|
|
6
|
+
|
|
7
|
+
### Features and breaking changes
|
|
8
|
+
|
|
9
|
+
- Give writable Pi nodes built-in shell tools for dependency installation, builds,
|
|
10
|
+
tests, and repair. Shell calls participate in cancellation and the workspace
|
|
11
|
+
write barrier before checkpointing; prompts describe workspace boundaries and
|
|
12
|
+
shared resources. Read-only nodes still have no shell, and parent extension/MCP
|
|
13
|
+
tools are not inherited
|
|
14
|
+
([#41](https://github.com/Epsirom/braid/pull/41)) — @Epsirom.
|
|
15
|
+
- Checkpoint tracked changes and non-ignored new files using normal Git staging
|
|
16
|
+
semantics. Ignored dependencies, caches, and build outputs are no longer
|
|
17
|
+
automatically archived or passed to successors; already tracked and deliberately
|
|
18
|
+
force-added files remain tracked
|
|
19
|
+
([#41](https://github.com/Epsirom/braid/pull/41)) — @Epsirom.
|
|
20
|
+
|
|
21
|
+
See the [0.2 → 0.3 migration guide](docs/compatibility.md#migrating-from-02-to-03)
|
|
22
|
+
before upgrading if you rely on shell-free workers or preservation of ignored
|
|
23
|
+
outputs. Use read-only nodes or a custom runner to restrict worker capabilities,
|
|
24
|
+
and keep successor artifacts in non-ignored or deliberately tracked paths.
|
|
25
|
+
|
|
26
|
+
### Fixes
|
|
27
|
+
|
|
28
|
+
- Include `changes.baseCommit` in merge-source previews and explain cumulative
|
|
29
|
+
diffs versus invocation snapshots. Read-only review checkpoints can otherwise
|
|
30
|
+
suggest an empty patch; multi-parent checkpoints also need an explicit
|
|
31
|
+
strategy instead of a bare cherry-pick
|
|
32
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
33
|
+
- Display Pi submission and status errors even when the host supplies empty
|
|
34
|
+
result details; failed graph definitions no longer appear as background jobs
|
|
35
|
+
with an undefined ID/status. Validation errors identify offending nodes/edges
|
|
36
|
+
and explain how to declare feedback cycles
|
|
37
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
38
|
+
- Clarify Pi node requirements, loop/template parameters, and revision controls
|
|
39
|
+
while retaining a flat provider-facing node schema with type-specific core
|
|
40
|
+
validation. Restrict historical execution pins to updates. Submission/update
|
|
41
|
+
replies echo accepted graph types and policies
|
|
42
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
43
|
+
- Deliver Pi completion/pause reminders at the next model step using steering,
|
|
44
|
+
after the current tool batch, instead of queuing follow-ups behind the entire
|
|
45
|
+
foreground task. Clarify that pause reminders need a current-state check
|
|
46
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
47
|
+
- Explain atomic update failures and retrying complete patches: new nodes and
|
|
48
|
+
their edges/loops must be submitted together to avoid starting disconnected
|
|
49
|
+
roots while another execution is paused. Discourage repeated replacement jobs
|
|
50
|
+
when a definition problem recurs
|
|
51
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
52
|
+
- Show the requested execution's output/error in focused Pi status reads and
|
|
53
|
+
include the current revision and paused IDs, separately from its captured
|
|
54
|
+
revision. Preserve this rendering for older saved Pi sessions
|
|
55
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
56
|
+
- Keep errors, control fields, and Pi usage ahead of large status payloads;
|
|
57
|
+
save complete running snapshots and include failed-worker provider usage as
|
|
58
|
+
`piUsage` in exported final results
|
|
59
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
60
|
+
- Update live status on graph completion/failure and clear resumable pause IDs
|
|
61
|
+
when finalizing, while retaining pause history in the event log. Loop
|
|
62
|
+
validation now names feedback targets and edges that bypass the loop entry
|
|
63
|
+
([#42](https://github.com/Epsirom/braid/pull/42)) — @Epsirom.
|
|
64
|
+
|
|
65
|
+
### New Contributors
|
|
66
|
+
|
|
67
|
+
No first-time human contributors in this release.
|
|
68
|
+
|
|
69
|
+
[Full comparison](https://github.com/Epsirom/braid/compare/v0.2.1...v0.3.0).
|
|
70
|
+
|
|
5
71
|
## 0.2.1 — 2026-10-04
|
|
6
72
|
|
|
7
73
|
### Maintenance
|
package/README.md
CHANGED
|
@@ -11,20 +11,38 @@ checkpoints; explicit `integrate` nodes apply selected work to the source checko
|
|
|
11
11
|
[](https://www.npmjs.com/package/@chrok/pi-braid)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
|
|
14
|
-
**0.
|
|
14
|
+
**0.3 API:** Experimental, Node.js 22+, ESM. The framework-agnostic core
|
|
15
15
|
has no runtime dependencies; the OpenAI-compatible runner and Pi extension are
|
|
16
16
|
optional integrations. The npm badges show published versions; see
|
|
17
17
|
[GitHub releases](https://github.com/Epsirom/braid/releases) for release notes.
|
|
18
|
-
Read the [0.
|
|
19
|
-
before upgrading
|
|
20
|
-
[0.1.
|
|
18
|
+
Read the [0.2 → 0.3 migration guide](docs/compatibility.md#migrating-from-02-to-03)
|
|
19
|
+
before upgrading; users coming from 0.1 also need the
|
|
20
|
+
[0.1 → 0.2 guide](docs/compatibility.md#migrating-from-01-to-02).
|
|
21
|
+
For the previous release, use the
|
|
22
|
+
[0.2.1 documentation](https://github.com/Epsirom/braid/tree/v0.2.1).
|
|
21
23
|
|
|
22
24
|
| Package | Purpose |
|
|
23
25
|
| --- | --- |
|
|
24
26
|
| [@chrok/braid](https://www.npmjs.com/package/@chrok/braid) | Core runtime and optional OpenAI-compatible runner |
|
|
25
27
|
| [@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
28
|
|
|
27
|
-
## What changed in 0.
|
|
29
|
+
## What changed in 0.3?
|
|
30
|
+
|
|
31
|
+
- **Shell tools in writable Pi nodes.** Workers can install dependencies, build,
|
|
32
|
+
test, and repair in their assigned workspaces. Read-only nodes remain shell-free;
|
|
33
|
+
worktrees use host permissions and are not security sandboxes.
|
|
34
|
+
- **Git-aware checkpoints.** Tracked changes and non-ignored new files are
|
|
35
|
+
checkpointed. Keep artifacts needed by successors in non-ignored or deliberately
|
|
36
|
+
tracked paths; ignored dependencies and build outputs are no longer preserved.
|
|
37
|
+
- **Pi reminders between steps.** Completion and pause reminders arrive after
|
|
38
|
+
the current response and tool batch, before the next model step.
|
|
39
|
+
- **Clearer errors and status.** Invalid graphs show actionable errors; focused
|
|
40
|
+
reads show the selected execution's output/error and current control state.
|
|
41
|
+
Full status exports retain snapshots and failed-worker usage.
|
|
42
|
+
|
|
43
|
+
See the [changelog](CHANGELOG.md#030--2026-10-04) for all fixes and credits.
|
|
44
|
+
|
|
45
|
+
## Execution-control foundation from 0.2
|
|
28
46
|
|
|
29
47
|
- **Editable graphs, captured executions.** `startBraid` exposes revision-checked
|
|
30
48
|
updates and pause/resume; `executionId` identifies a particular invocation,
|
|
@@ -416,10 +434,11 @@ provides `request.merge.sources` and `request.merge.finish(dispositions)` to
|
|
|
416
434
|
merge agents. Adapters must enforce workspace capabilities and wrap mutating
|
|
417
435
|
file tools in `request.withWorkspaceWrite(operation)`, so cleanup waits for
|
|
418
436
|
in-flight writes and rejects later writes. Core Git mutations use this barrier.
|
|
419
|
-
The included Pi adapter provides guarded `write`/`edit` alongside its read tools.
|
|
437
|
+
The included Pi adapter provides guarded `write`/`edit` and Pi shell tools alongside its read tools.
|
|
420
438
|
For read-only workspaces, adapters must omit mutating tools; the core write
|
|
421
|
-
barrier also rejects writes. This includes all nodes outside Git. Pi
|
|
422
|
-
`bash
|
|
439
|
+
barrier also rejects writes. This includes all nodes outside Git. Writable Pi nodes
|
|
440
|
+
can use `bash` (and `powershell` on Windows) to build and run tests. Shell calls
|
|
441
|
+
participate in the write barrier and forward cancellation to their processes.
|
|
423
442
|
|
|
424
443
|
`request.predecessors` contains direct active predecessors in incoming-edge
|
|
425
444
|
order, including failures on unconditional edges with an `error` field. Each source appears once:
|
|
@@ -467,7 +486,10 @@ The model-facing Git tool separates `command` from `args`, for example
|
|
|
467
486
|
accepts the complete argument array. Duplicate command prefixes, network Git,
|
|
468
487
|
branch switching, and filesystem-boundary overrides are rejected.
|
|
469
488
|
|
|
470
|
-
Instances checkpoint
|
|
489
|
+
Instances checkpoint tracked changes and non-ignored new files before downstream
|
|
490
|
+
admission. Ignored dependencies, caches, and build products are discarded on cleanup;
|
|
491
|
+
files already tracked or explicitly force-added remain tracked under normal Git rules.
|
|
492
|
+
All source checkpoints remain
|
|
471
493
|
reusable; no merge consumes or deletes a predecessor's result. Job cleanup
|
|
472
494
|
archives and removes owned worktrees, retaining refs under
|
|
473
495
|
`refs/braid/checkpoints/`. Integration additionally captures a pre-write
|
|
@@ -489,19 +511,22 @@ isolated from those source edits. See [execution control](docs/execution-control
|
|
|
489
511
|
|
|
490
512
|
**The adapter is a trust boundary, not a security sandbox.** It must avoid shared
|
|
491
513
|
conversation state, expose only its declared capabilities, and forward `signal` to its provider.
|
|
492
|
-
The core
|
|
493
|
-
tool.
|
|
494
|
-
runtime; the core package does not depend on Pi. See
|
|
514
|
+
The core does not supply shell tools or a recursive Braid tool; adapters choose
|
|
515
|
+
their tool capabilities. The Pi adapter supplies shell tools to writable nodes
|
|
516
|
+
without changing the runtime; the core package does not depend on Pi. See
|
|
495
517
|
[`integrations/pi/README.md`](integrations/pi/README.md) for installation and testing.
|
|
496
518
|
|
|
497
519
|
The included OpenAI-compatible adapter uses fresh Chat Completions contexts,
|
|
498
520
|
a strict `decide({ choice })` tool and one tool-free continuation for decisions.
|
|
499
521
|
Merge/integrate nodes use a local `git` / `finish_merge` tool loop; ordinary OpenAI nodes
|
|
500
|
-
have no filesystem tools. Pi exposes read
|
|
501
|
-
core Git/merge tools. Tool errors go back to
|
|
522
|
+
have no filesystem tools. Pi exposes read, guarded write, and shell tools plus
|
|
523
|
+
these core Git/merge tools. Tool errors go back to nodes for recovery. Both
|
|
502
524
|
adapters sum usage across their model calls and forward cancellation.
|
|
503
525
|
Pi writes reject external paths, Git metadata, symlinks, hard links, and special
|
|
504
|
-
files.
|
|
526
|
+
files. Shell tools can bypass these checks and run with host permissions; prompts
|
|
527
|
+
require nodes to respect workspace boundaries, shared Git state, and external
|
|
528
|
+
resources. Parent extension/MCP tools and hooks are not inherited. These checks
|
|
529
|
+
and cooperation rules are not an OS sandbox.
|
|
505
530
|
See the [Pi filesystem capabilities](integrations/pi/README.md#node-filesystem-capabilities).
|
|
506
531
|
|
|
507
532
|
Pi tool and time budgets are unlimited by default. Its `options.maxToolRounds`
|
package/ROADMAP.md
CHANGED
|
@@ -6,12 +6,25 @@ and thin host adapters. This is a direction for discussion, not a delivery sched
|
|
|
6
6
|
|
|
7
7
|
## Release status
|
|
8
8
|
|
|
9
|
-
The 0.
|
|
10
|
-
availability
|
|
9
|
+
The 0.3 workspace and Pi reliability changes are implemented on top of the 0.2
|
|
10
|
+
execution-control foundation. Source availability and npm availability are
|
|
11
|
+
separate milestones. Check
|
|
11
12
|
[GitHub releases](https://github.com/Epsirom/braid/releases),
|
|
12
13
|
[@chrok/braid](https://www.npmjs.com/package/@chrok/braid), and
|
|
13
14
|
[@chrok/pi-braid](https://www.npmjs.com/package/@chrok/pi-braid) for published versions.
|
|
14
15
|
|
|
16
|
+
## 0.3 implemented changes
|
|
17
|
+
|
|
18
|
+
- [x] Shell tools for writable Pi nodes, with cancellation and checkpoint write
|
|
19
|
+
barriers; keep read-only nodes shell-free.
|
|
20
|
+
- [x] Git-aware checkpoints that preserve tracked changes and non-ignored files.
|
|
21
|
+
- [x] Pi completion/pause reminders between model steps, with actionable graph
|
|
22
|
+
errors and complete focused status reads and exports.
|
|
23
|
+
- [x] Cumulative merge-source baselines and atomic update/retry guidance.
|
|
24
|
+
|
|
25
|
+
Read the [0.2 → 0.3 migration guide](docs/compatibility.md#migrating-from-02-to-03)
|
|
26
|
+
for workspace capabilities and artifact preservation.
|
|
27
|
+
|
|
15
28
|
## 0.2 implemented foundation
|
|
16
29
|
|
|
17
30
|
- [x] Separate editable node definitions from captured execution instances,
|
|
@@ -41,9 +54,10 @@ provenance, and clean installation before marking publication complete.
|
|
|
41
54
|
|
|
42
55
|
- Exercise real edit → review → refine → integrate tasks and use the results to
|
|
43
56
|
improve migration examples, update-conflict diagnostics, and recovery guidance.
|
|
44
|
-
- Refresh scheduler measurements for
|
|
45
|
-
Include execution history, updates, and loops;
|
|
46
|
-
separately. The [checked-in benchmark](docs/benchmark.md)
|
|
57
|
+
- Refresh scheduler measurements for the current execution model before
|
|
58
|
+
optimizing data structures. Include execution history, updates, and loops;
|
|
59
|
+
measure Git workspace costs separately. The [checked-in benchmark](docs/benchmark.md)
|
|
60
|
+
is a 0.1 baseline.
|
|
47
61
|
- Explore explicit retention and cleanup policies for execution history, Git
|
|
48
62
|
recovery refs, and Pi temporary result files without breaking result retrieval,
|
|
49
63
|
reusable checkpoints, or usage accounting.
|
|
@@ -59,7 +73,7 @@ provenance, and clean installation before marking publication complete.
|
|
|
59
73
|
consumption, package builds, and the complete Node/platform matrix. Keep Node
|
|
60
74
|
declarations on 22.x while Node 22 remains the minimum supported runtime.
|
|
61
75
|
|
|
62
|
-
## Scope after 0.
|
|
76
|
+
## Scope after 0.3
|
|
63
77
|
|
|
64
78
|
The original fixed-DAG-only boundary no longer applies. Bounded loops, live
|
|
65
79
|
graph changes, parent-controlled pause/resume, and execution history are part of
|
package/SECURITY.md
CHANGED
|
@@ -44,17 +44,21 @@ answer that question.
|
|
|
44
44
|
- Braid isolates invocation context; it is not a process or filesystem sandbox.
|
|
45
45
|
A custom runner is trusted code with the host process's permissions.
|
|
46
46
|
- The core manages Git snapshots, worktrees, checkpoint refs, and merge tools.
|
|
47
|
-
Pi workers can write/edit
|
|
47
|
+
Writable Pi workers can write/edit and run shell commands in their assigned Git
|
|
48
|
+
worktree. Shells run with host permissions; prompts constrain workspace writes,
|
|
49
|
+
shared Git state, and external side effects. Integrate agents can apply
|
|
48
50
|
changes to the source checkout; merge agents use isolated worktrees; they are not restricted to read-only analysis.
|
|
49
51
|
Outside Git, Pi file tools stay read-only. Read paths can expose files outside
|
|
50
52
|
the checkout and disclose content to a model provider.
|
|
51
53
|
- Execute/decision nodes can request `workspace: "read-only"` inside Git. Pi
|
|
52
|
-
omits write/edit tools, and core rejects write-barrier operations. Git inspection
|
|
54
|
+
omits write/edit and shell tools, and core rejects write-barrier operations. Git inspection
|
|
53
55
|
remains available. These nodes read an isolated predecessor
|
|
54
56
|
snapshot; custom runners must honor this capability themselves.
|
|
55
57
|
- Guarded write tools reject external paths, Git metadata, symlinks, hard links,
|
|
56
58
|
and special files, but are not an OS sandbox against concurrent filesystem
|
|
57
|
-
attacks.
|
|
59
|
+
attacks. Shell commands can bypass these guards. Parent extension/MCP tools and
|
|
60
|
+
their interception policies are not inherited by nodes. Avoid concurrent external
|
|
61
|
+
source edits while integrate agents run. A
|
|
58
62
|
cancellation or failed merge can leave partial integration/conflicts for review;
|
|
59
63
|
checkpoint and backup refs support recovery.
|
|
60
64
|
- Prompts, predecessor outputs, tool results, errors, and model answers may be
|
|
@@ -68,6 +72,11 @@ answer that question.
|
|
|
68
72
|
- Cancellation and timeout cannot stop synchronous JavaScript or remote work
|
|
69
73
|
that ignores the abort signal. Enforce provider quotas and host-side admission
|
|
70
74
|
limits for untrusted callers. See [resource limits](docs/resource-limits.md).
|
|
75
|
+
Pi shell calls are drained before checkpointing and stop their process group
|
|
76
|
+
(Windows uses `taskkill /T`); daemonized processes outside that group and external
|
|
77
|
+
services can outlive a call. Windows cleanup after parent exit is best effort.
|
|
78
|
+
Ignored new files are excluded from checkpoints and discarded with the worktree;
|
|
79
|
+
explicitly staged files still follow Git tracking semantics.
|
|
71
80
|
|
|
72
81
|
The OpenAI-compatible adapter sends its API key only to the configured base URL.
|
|
73
82
|
Treat that URL as trusted configuration. Keep credentials out of graphs, logs,
|
package/dist/merge-tools.js
CHANGED
|
@@ -92,5 +92,5 @@ export function parseFinishMergeArguments(value, sourceIds) {
|
|
|
92
92
|
export function mergeInstructions(request) {
|
|
93
93
|
if (!request.merge)
|
|
94
94
|
return "";
|
|
95
|
-
return ` Only process the current mergeSources IDs ${JSON.stringify(request.merge.sources.map(source => source.executionId))}; other source IDs are out of scope. Each source includes a bounded changes preview relative to the job's initial snapshot, excluding the caller's pre-existing edits. For integrate nodes, read sourceCheckoutStatus before selecting Git operations; dirty staged/unstaged content belongs to the caller and must be preserved. Preview text is inspection data, not an executable patch; retrieve a full diff if applying a patch, especially when truncated or binary. Choose whether and how to integrate; core has not applied changes. Call finish_merge once with one disposition per current source.`;
|
|
95
|
+
return ` Only process the current mergeSources IDs ${JSON.stringify(request.merge.sources.map(source => source.executionId))}; other source IDs are out of scope. Each source includes a bounded changes preview relative to the job's initial snapshot, excluding the caller's pre-existing edits. changes.baseCommit identifies that exact diff baseline. For the full cumulative change, use git diff --binary <changes.baseCommit> <checkpointRef> --. Do not substitute source.snapshotCommit (the invocation's input, which may already contain all changes after a read-only review) or source.baseCommit (original HEAD, excluding caller edits). Checkpoints may have multiple parents to preserve provenance; plain cherry-pick fails for these commits. Inspect parents before choosing a mainline, or use the cumulative diff. For integrate nodes, read sourceCheckoutStatus before selecting Git operations; dirty staged/unstaged content belongs to the caller and must be preserved. Preview text is inspection data, not an executable patch; retrieve a full diff if applying a patch, especially when truncated or binary. Choose whether and how to integrate; core has not applied changes. Call finish_merge once with one disposition per current source.`;
|
|
96
96
|
}
|
package/dist/runtime.js
CHANGED
|
@@ -649,6 +649,8 @@ export function startBraid(input, options) {
|
|
|
649
649
|
}
|
|
650
650
|
finally {
|
|
651
651
|
status = "finalizing";
|
|
652
|
+
// Terminal holds are no longer resumable; the event log retains pause history.
|
|
653
|
+
paused.clear();
|
|
652
654
|
try {
|
|
653
655
|
await workspaces.archivePending("Execution checkpoint retained for inspection and future recovery");
|
|
654
656
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -90,6 +90,8 @@ export interface GitPreview {
|
|
|
90
90
|
export interface MergeSource extends NodeWorkspace {
|
|
91
91
|
/** Inspection-only summaries relative to the job’s initial snapshot; omitted by custom runners. */
|
|
92
92
|
changes?: {
|
|
93
|
+
/** Exact preview/diff baseline, including caller edits; supplied by built-in workspaces. */
|
|
94
|
+
baseCommit?: string;
|
|
93
95
|
files: string[];
|
|
94
96
|
filesTruncated: boolean;
|
|
95
97
|
stat: GitPreview;
|
package/dist/validate.js
CHANGED
|
@@ -91,13 +91,13 @@ export function compileGraph(input) {
|
|
|
91
91
|
const byId = new Map();
|
|
92
92
|
for (const node of input.nodes) {
|
|
93
93
|
requireValid(isRecord(node), "Node must be an object");
|
|
94
|
-
requireValid(
|
|
94
|
+
requireValid(text(node.id), "Node id must be a non-empty string");
|
|
95
|
+
requireValid(node.type === "execute" || node.type === "decision" || (node.type === "merge" || node.type === "integrate"), `Node '${node.id}': unknown node type; expected execute, decision, merge, or integrate`);
|
|
95
96
|
fields(node, node.type === "decision"
|
|
96
97
|
? ["type", "id", "prompt", "model", "choices", "workspace", "notifyOnCompletion", "requireSuccess", "pauseAfter"]
|
|
97
98
|
: node.type === "execute"
|
|
98
99
|
? ["type", "id", "prompt", "model", "workspace", "notifyOnCompletion", "requireSuccess", "pauseAfter"]
|
|
99
|
-
: ["type", "id", "prompt", "model", "notifyOnCompletion", "requireSuccess", "pauseAfter"],
|
|
100
|
-
requireValid(text(node.id), "Node id must be a non-empty string");
|
|
100
|
+
: ["type", "id", "prompt", "model", "notifyOnCompletion", "requireSuccess", "pauseAfter"], `Node '${node.id}' (${node.type})`);
|
|
101
101
|
requireValid(!byId.has(node.id), `Duplicate node id '${node.id}'`);
|
|
102
102
|
const prompt = (node.type === "merge" || node.type === "integrate") && node.prompt === undefined
|
|
103
103
|
? (node.type === "merge" ? "Merge selected predecessor results into this new isolated worktree. Resolve conflicts and account for every source with finish_merge." : "Integrate selected predecessor results into the invoking checkout. Preserve user changes and account for every source with finish_merge.")
|
|
@@ -139,7 +139,7 @@ export function compileGraph(input) {
|
|
|
139
139
|
}
|
|
140
140
|
for (const edge of input.edges) {
|
|
141
141
|
requireValid(isRecord(edge), "Edge must be an object");
|
|
142
|
-
fields(edge, ["from", "to", "choice", "feedback", "executionId"],
|
|
142
|
+
fields(edge, ["from", "to", "choice", "feedback", "executionId"], `Edge '${edge.from}' -> '${edge.to}'`);
|
|
143
143
|
requireValid(edge.executionId === undefined || text(edge.executionId), "Invalid edge executionId");
|
|
144
144
|
requireValid(edge.feedback === undefined || text(edge.feedback), "Invalid edge feedback");
|
|
145
145
|
requireValid(!edge.feedback || !edge.executionId, "Feedback cannot pin an execution");
|
|
@@ -179,7 +179,7 @@ export function compileGraph(input) {
|
|
|
179
179
|
topologicalOrder.push(byId.get(edge.to));
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
-
requireValid(topologicalOrder.length === nodes.length, "Graph contains a cycle without a declared feedback edge");
|
|
182
|
+
requireValid(topologicalOrder.length === nodes.length, "Graph contains a cycle without a declared feedback edge. Declare a bounded loop in loops and label its decision-to-entry edge with choice and feedback=loopId");
|
|
183
183
|
const loops = new Map();
|
|
184
184
|
const membership = new Map();
|
|
185
185
|
requireValid(input.loops === undefined || Array.isArray(input.loops), "Graph loops must be an array");
|
|
@@ -208,7 +208,7 @@ export function compileGraph(input) {
|
|
|
208
208
|
requireValid(feedback.length === 1, `Loop '${loop.id}' needs exactly one feedback edge`);
|
|
209
209
|
const back = feedback[0];
|
|
210
210
|
const decision = byId.get(back.from);
|
|
211
|
-
requireValid(back.to === loop.entry && decision.type === "decision" && back.choice !== undefined, `Loop '${loop.id}' feedback must route a decision choice to its entry`);
|
|
211
|
+
requireValid(back.to === loop.entry && decision.type === "decision" && back.choice !== undefined, `Loop '${loop.id}' feedback edge '${back.from}' -> '${back.to}' must route a decision choice to its entry '${loop.entry}'. Set feedback on the decision-to-entry edge and include choice`);
|
|
212
212
|
requireValid(decision.choices.some(choice => choice !== back.choice), `Loop '${loop.id}' needs an exit choice`);
|
|
213
213
|
const ancestors = reachable(back.from, true);
|
|
214
214
|
const members = new Set([...reachable(loop.entry)].filter(id => ancestors.has(id)));
|
|
@@ -220,8 +220,8 @@ export function compileGraph(input) {
|
|
|
220
220
|
for (const edge of edges) {
|
|
221
221
|
if (edge.feedback || edge.executionId)
|
|
222
222
|
continue;
|
|
223
|
-
requireValid(!(!members.has(edge.from) && members.has(edge.to) && edge.to !== loop.entry), `Loop '${loop.id}' has multiple entries`);
|
|
224
|
-
requireValid(!(members.has(edge.from) && !members.has(edge.to) && edge.from !== back.from), `Loop '${loop.id}' must exit through its feedback decision`);
|
|
223
|
+
requireValid(!(!members.has(edge.from) && members.has(edge.to) && edge.to !== loop.entry), `Loop '${loop.id}' has multiple entries: edge '${edge.from}' -> '${edge.to}' bypasses entry '${loop.entry}'. Route external dependencies to '${loop.entry}'`);
|
|
224
|
+
requireValid(!(members.has(edge.from) && !members.has(edge.to) && edge.from !== back.from), `Loop '${loop.id}' must exit through its feedback decision '${back.from}': edge '${edge.from}' -> '${edge.to}' leaves from another node`);
|
|
225
225
|
}
|
|
226
226
|
loops.set(loop.id, { id: loop.id, entry: loop.entry, maxIterations: loop.maxIterations, feedback: back, members });
|
|
227
227
|
}
|
package/dist/workspaces.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class GitWorkspaces {
|
|
|
22
22
|
private sealCheckpoint;
|
|
23
23
|
all(): Record<string, NodeWorkspace>;
|
|
24
24
|
pending(): string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Preserve tracked changes and non-ignored new files before releasing a worktree. */
|
|
26
26
|
private checkpoint;
|
|
27
27
|
private release;
|
|
28
28
|
/** Only archive/remove here. Choosing merge/cherry-pick/apply always belongs to the agent. */
|
package/dist/workspaces.js
CHANGED
|
@@ -180,7 +180,7 @@ export class GitWorkspaces {
|
|
|
180
180
|
// A temporary index captures tracked edits/deletions and non-ignored new files
|
|
181
181
|
// without changing the parent's real index, branch, or working files.
|
|
182
182
|
await git(sourceRoot, ["add", "--all", "--", "."], options);
|
|
183
|
-
// Include ignored files
|
|
183
|
+
// Include ignored files deliberately tracked by selected sources, without capturing the
|
|
184
184
|
// caller's unrelated ignored build products or dependencies.
|
|
185
185
|
const existing = [];
|
|
186
186
|
for (const file of extraFiles) {
|
|
@@ -345,7 +345,7 @@ export class GitWorkspaces {
|
|
|
345
345
|
.filter(workspace => workspace.worktreeRoot && ["preparing", "ready", "failed"].includes(workspace.state))
|
|
346
346
|
.map(workspace => workspace.executionId ?? workspace.nodeId);
|
|
347
347
|
}
|
|
348
|
-
/**
|
|
348
|
+
/** Preserve tracked changes and non-ignored new files before releasing a worktree. */
|
|
349
349
|
async checkpoint(workspace) {
|
|
350
350
|
if (workspace.checkpointRef)
|
|
351
351
|
return;
|
|
@@ -366,7 +366,7 @@ export class GitWorkspaces {
|
|
|
366
366
|
throw error;
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
|
-
await git(cwd, ["add", "--
|
|
369
|
+
await git(cwd, ["add", "--all", "--", "."], options);
|
|
370
370
|
const tree = await git(cwd, ["write-tree"], options);
|
|
371
371
|
const baseTree = await git(cwd, ["rev-parse", `${workspace.snapshotCommit}^{tree}`]);
|
|
372
372
|
const parents = [...new Set([workspace.snapshotCommit, ...(this.mergeParents.get(workspace.executionId ?? workspace.nodeId) ?? [])])];
|
|
@@ -527,7 +527,7 @@ export class GitWorkspaces {
|
|
|
527
527
|
const stat = await gitPreview(source.sourceRoot, [...diffArgs.slice(0, -1), "--stat", "--"], Math.floor(4_000 / count));
|
|
528
528
|
const diff = await gitPreview(source.sourceRoot, diffArgs, Math.min(6_000, Math.floor(24_000 / count)));
|
|
529
529
|
const names = files.text.slice(0, files.text.lastIndexOf("\0") + 1).split("\0").filter(Boolean);
|
|
530
|
-
sources.push({ ...source, changes: { files: names, filesTruncated: files.truncated, stat, diff } });
|
|
530
|
+
sources.push({ ...source, changes: { baseCommit: baseline.snapshotCommit, files: names, filesTruncated: files.truncated, stat, diff } });
|
|
531
531
|
}
|
|
532
532
|
const sourceStatus = integrating && target ? await gitPreview(target, ["status", "--porcelain=v1", "--untracked-files=all"], 4_000) : undefined;
|
|
533
533
|
return {
|
package/docs/compatibility.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| Pi package | Node.js 22.19+, Pi 1.0.1 is the pinned validation target |
|
|
7
7
|
| CI | Core minimum Node 22.0; both packages on Node 22.19 and 24 on Linux, macOS, Windows |
|
|
8
8
|
| OpenAI-compatible runner | Chat Completions text and function-tool calls; decisions and merge/integrate nodes require tool calling |
|
|
9
|
-
| Browsers / CommonJS | No supported browser build or CommonJS entry point in 0.
|
|
9
|
+
| Browsers / CommonJS | No supported browser build or CommonJS entry point in 0.3 |
|
|
10
10
|
|
|
11
11
|
The CI matrix describes configured checks; see actual workflow results for each
|
|
12
12
|
commit. Offline HTTP fixtures validate the adapter contract. They do not prove
|
|
@@ -19,10 +19,10 @@ The wildcard is a loader/distribution convention, **not a claim that every Pi
|
|
|
19
19
|
version works**. Test the whole Pi suite before updating the supported target.
|
|
20
20
|
The offline host compatibility test loads the extension into a real Pi session
|
|
21
21
|
with an in-memory model provider. It checks worker prompt/tool normalization and
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
reminder delivery between model steps after the current tool batch, idle
|
|
23
|
+
continuation, and acknowledgement, including retrieval of a node output while
|
|
24
|
+
its job still runs. These fixtures run without provider credentials or network
|
|
25
|
+
model calls. Version 0.1.0 was originally validated with
|
|
26
26
|
Pi 0.85.1; the current checkout's pinned validation target is 1.0.1.
|
|
27
27
|
The Pi npm package declares an exact dependency on the matching `@chrok/braid`
|
|
28
28
|
release. npm installs the core automatically; Pi does not bundle another copy of
|
|
@@ -32,7 +32,7 @@ Git must be installed for workspace execution inside a Git checkout. Non-Git
|
|
|
32
32
|
text-only runs do not require Git workspace management.
|
|
33
33
|
## Migrating from 0.1 to 0.2
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Version 0.2 deliberately changed the execution and workspace contracts:
|
|
36
36
|
|
|
37
37
|
- Replace old source-checkout `merge` nodes with `integrate`. The new `merge`
|
|
38
38
|
combines inputs in a fresh isolated worktree.
|
|
@@ -71,6 +71,35 @@ separate gate reminder; when both preferences are enabled it supplies the single
|
|
|
71
71
|
completion reminder for that instance. Cancellation still sends failure reminders
|
|
72
72
|
for opted-in running instances.
|
|
73
73
|
|
|
74
|
+
## Migrating from 0.2 to 0.3
|
|
75
|
+
|
|
76
|
+
Version 0.3 changes workspace capabilities and checkpoint contents:
|
|
77
|
+
|
|
78
|
+
- Writable Pi nodes now receive `bash` and, on Windows, `powershell`, allowing
|
|
79
|
+
dependency installation, builds, and tests within a node. If a graph relies on
|
|
80
|
+
workers having no shell, set execute/decision nodes to `workspace: "read-only"`
|
|
81
|
+
(which also disables file writes), or use a custom runner with the required
|
|
82
|
+
capability policy. Writable shell access uses host permissions and prompt-based
|
|
83
|
+
cooperation rules; worktrees are not a security sandbox. Parent extension/MCP
|
|
84
|
+
tools and their hooks are not inherited.
|
|
85
|
+
- Core checkpoints now follow `git add --all` semantics instead of force-adding
|
|
86
|
+
every output. Move artifacts needed by successors or for recovery to non-ignored
|
|
87
|
+
paths, or deliberately track them. Already tracked files, including files
|
|
88
|
+
explicitly staged with `git add --force`, remain tracked even when they match
|
|
89
|
+
ignore rules. Untracked ignored outputs are discarded when worktrees are removed,
|
|
90
|
+
including after failed or cancelled executions. This applies to every adapter.
|
|
91
|
+
- Shell commands must run in the foreground. On POSIX, Braid stops remaining
|
|
92
|
+
children in the command's process group even on normal completion; Windows uses
|
|
93
|
+
best-effort process-tree cleanup. Run a temporary server and its checks within
|
|
94
|
+
one foreground command and clean it up before returning. Detached daemons and
|
|
95
|
+
external services are not contained by this lifecycle.
|
|
96
|
+
|
|
97
|
+
No graph field, result shape, or event schema migration is required. Pi now
|
|
98
|
+
delivers completion/pause reminders after the current assistant response and
|
|
99
|
+
tool batch, before the next model step, rather than waiting for the entire
|
|
100
|
+
foreground task to finish. Fetch current status before updating or resuming a
|
|
101
|
+
paused execution because the reminder describes the state when it was queued.
|
|
102
|
+
|
|
74
103
|
## Versioning
|
|
75
104
|
|
|
76
105
|
Core and Pi release together with matching versions. During 0.x, patch releases
|
|
@@ -135,6 +135,17 @@ workspace; source worktrees remain available until job cleanup. Integration
|
|
|
135
135
|
captures a `backupRef` first and serializes source-checkout access within the
|
|
136
136
|
process. It must preserve unrelated staged, unstaged, and untracked caller edits.
|
|
137
137
|
|
|
138
|
+
Each merge source's `changes.baseCommit` identifies the frozen job snapshot used
|
|
139
|
+
for its cumulative diff preview, including the caller's initial uncommitted
|
|
140
|
+
edits. Retrieve the full patch with
|
|
141
|
+
`git diff --binary <changes.baseCommit> <checkpointRef> --` when choosing patch
|
|
142
|
+
integration. `source.snapshotCommit` is that invocation's input: after a read-only
|
|
143
|
+
review it may equal the checkpoint, yielding an empty diff despite earlier
|
|
144
|
+
changes. `source.baseCommit` is the original HEAD and excludes uncommitted edits.
|
|
145
|
+
Checkpoints can have multiple parents recording merged sources. A bare
|
|
146
|
+
`cherry-pick` then fails; inspect the parents before choosing a mainline, or use
|
|
147
|
+
the cumulative diff. The runtime does not choose or apply a merge strategy.
|
|
148
|
+
|
|
138
149
|
There is no implicit final integration. Each instance is checkpointed before
|
|
139
150
|
successors are released, including partial work on optional failure. Finalization
|
|
140
151
|
archives/removes owned worktrees while retaining checkpoint refs. Cancelled or
|