@brightbeamai/chap-coordinator 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -4
- package/dist/index.cjs +15 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,9 @@ surface, so the two interoperate on the same JSON-RPC 2.0 wire.
|
|
|
13
13
|
npm install @brightbeamai/chap-coordinator
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Node 20+ required.
|
|
17
|
-
|
|
16
|
+
Node 20+ required. No required runtime dependencies; uses Node's built-in
|
|
17
|
+
`crypto` for Ed25519 and JCS. `better-sqlite3` is an optional dependency,
|
|
18
|
+
needed only for `SqliteStore`.
|
|
18
19
|
|
|
19
20
|
## Companion packages
|
|
20
21
|
|
|
@@ -85,7 +86,7 @@ console.log(resp.result); // { task_id: "tsk_...", state: "created" }
|
|
|
85
86
|
|
|
86
87
|
| Profile | Methods (per profile spec) |
|
|
87
88
|
|-----------------------|-------------------------------------------------------------------------------------------|
|
|
88
|
-
| `core/1.0` | `workspace.create`, `workspace.describe`, `workspace.set_profiles`, `participant.join`, `participant.leave`, `task.create`, `task.update`, `task.complete`, `audit.read` |
|
|
89
|
+
| `core/1.0` | `workspace.create`, `workspace.describe`, `workspace.set_profiles`, `participant.join`, `participant.leave`, `task.create`, `task.update`, `task.complete`, `audit.read`; a task created with `review_required` completes only on a reviewer decision |
|
|
89
90
|
| `review/1.0` | `review.request`, `decide.approve`, `decide.reject`, `decide.override`, `abstain.declare`, `escalate.raise` |
|
|
90
91
|
| `whisper/1.0` | `whisper.ask`, `whisper.answer`; lapse hook via `coord.checkWhisperLapses(workspaceId, now?)` |
|
|
91
92
|
| `deliberation/1.0` | `deliberate.open`, `deliberate.comment`, `deliberate.vote`, `deliberate.close` |
|
|
@@ -100,6 +101,15 @@ console.log(resp.result); // { task_id: "tsk_...", state: "created" }
|
|
|
100
101
|
|
|
101
102
|
**39 method handlers in total**, matching the Python reference exactly.
|
|
102
103
|
|
|
104
|
+
**The review gate.** A task carrying `review_required` does not complete on
|
|
105
|
+
`task.complete`. The call opens a review, holding the submitted output as the
|
|
106
|
+
artefact under review, and only a `decide.*` reaches `completed`. `task.update`
|
|
107
|
+
cannot complete such a task either, and answers `-32602` naming the route
|
|
108
|
+
through. Where no reviewer was named, the set is the human members other than
|
|
109
|
+
the completer and the assignee; a workspace with no eligible human refuses the
|
|
110
|
+
completion rather than opening a review nobody can decide. Under `modes/1.0` a
|
|
111
|
+
trial-mode task has `review_required` set for it.
|
|
112
|
+
|
|
103
113
|
## Architecture
|
|
104
114
|
|
|
105
115
|
The coordinator is transport-agnostic, persistence-agnostic, and
|
|
@@ -171,7 +181,7 @@ Key lifecycle:
|
|
|
171
181
|
- `participant.revoke_key` is an admin operation; envelopes signed
|
|
172
182
|
with the revoked key after revocation are rejected.
|
|
173
183
|
|
|
174
|
-
Helpers exported from `@brightbeamai/chap-coordinator
|
|
184
|
+
Helpers exported from `@brightbeamai/chap-coordinator`:
|
|
175
185
|
|
|
176
186
|
```typescript
|
|
177
187
|
import {
|
package/dist/index.cjs
CHANGED
|
@@ -1795,6 +1795,7 @@ function reply(env, body) {
|
|
|
1795
1795
|
else out.result = body.result;
|
|
1796
1796
|
return out;
|
|
1797
1797
|
}
|
|
1798
|
+
var REVIEW_REQUEST_STOPPED = ["cancelled", "superseded", "paused"];
|
|
1798
1799
|
var Coordinator = class {
|
|
1799
1800
|
workspaces = /* @__PURE__ */ new Map();
|
|
1800
1801
|
options;
|
|
@@ -2470,6 +2471,12 @@ var Coordinator = class {
|
|
|
2470
2471
|
if (!legal[task.state]?.includes(newState)) {
|
|
2471
2472
|
return { error: rpcError(E.PARAMS, `Illegal transition ${task.state} -> ${newState}`) };
|
|
2472
2473
|
}
|
|
2474
|
+
if (newState === "completed" && task.review_required) {
|
|
2475
|
+
return { error: rpcError(
|
|
2476
|
+
E.PARAMS,
|
|
2477
|
+
"This task requires review. Submit the output with task.complete, which opens the review; a reviewer decision completes it."
|
|
2478
|
+
) };
|
|
2479
|
+
}
|
|
2473
2480
|
task.state = newState;
|
|
2474
2481
|
task.updated_at = this.now();
|
|
2475
2482
|
task.history.push({
|
|
@@ -2495,11 +2502,11 @@ var Coordinator = class {
|
|
|
2495
2502
|
task.pending_artefact = p.output;
|
|
2496
2503
|
if (!task.review) {
|
|
2497
2504
|
const producer = p.from;
|
|
2498
|
-
const eligible = [...ws.members.
|
|
2505
|
+
const eligible = [...ws.members.entries()].filter(([uri, member]) => uri !== producer && uri !== task.assignee && member.type === "human").map(([uri]) => uri);
|
|
2499
2506
|
if (eligible.length === 0) {
|
|
2500
2507
|
return { error: rpcError(
|
|
2501
2508
|
E.NOT_AUTHORISED,
|
|
2502
|
-
"Task requires review but has no eligible reviewer (needs a member other than its assignee and completer)"
|
|
2509
|
+
"Task requires review but has no eligible human reviewer (needs a human member other than its assignee and completer)"
|
|
2503
2510
|
) };
|
|
2504
2511
|
}
|
|
2505
2512
|
task.review = { requested_at: now, requested_to: eligible, rule: "any_one_approves", decisions: [] };
|
|
@@ -2560,11 +2567,15 @@ var Coordinator = class {
|
|
|
2560
2567
|
if (notMember) return notMember;
|
|
2561
2568
|
const task = ws.tasks.get(p.task_id);
|
|
2562
2569
|
if (!task) return { error: rpcError(E.PARAMS, "Unknown task") };
|
|
2570
|
+
if (REVIEW_REQUEST_STOPPED.includes(task.state)) {
|
|
2571
|
+
return { error: rpcError(E.NOT_REVIEWABLE, `Cannot request review for task in state: ${task.state}`) };
|
|
2572
|
+
}
|
|
2563
2573
|
const to = p.to;
|
|
2564
2574
|
const reviewers = Array.isArray(to) ? to : typeof to === "string" ? [to] : [];
|
|
2565
2575
|
if (!reviewers.length) return { error: rpcError(E.PARAMS, "review.request needs 'to'") };
|
|
2566
2576
|
const now = this.now();
|
|
2567
|
-
const
|
|
2577
|
+
const declaredRule = p.rule;
|
|
2578
|
+
const rule = declaredRule || "any_one_approves";
|
|
2568
2579
|
if (!reviewRuleSupported(rule)) {
|
|
2569
2580
|
return { error: rpcError(E.PARAMS, `Unsupported review rule ${JSON.stringify(rule)}: review/1.0 supports any_one_approves, all_approve, quorum:<n>`) };
|
|
2570
2581
|
}
|
|
@@ -2575,7 +2586,7 @@ var Coordinator = class {
|
|
|
2575
2586
|
"A review is already open on this task with different content. Decide, abstain, escalate or cancel it before requesting review of a new artefact."
|
|
2576
2587
|
) };
|
|
2577
2588
|
}
|
|
2578
|
-
if (
|
|
2589
|
+
if (declaredRule !== void 0 && declaredRule !== task.review.rule) {
|
|
2579
2590
|
return { error: rpcError(
|
|
2580
2591
|
E.REVIEW_ALREADY_OPEN,
|
|
2581
2592
|
`Cannot change the decision rule of an open review (currently ${task.review.rule})`
|