@brightbeamai/chap-coordinator 0.2.12 → 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/README.md +20 -7
- package/dist/{api-DiiL0sil.d.cts → api-BkvwN42r.d.cts} +40 -0
- package/dist/{api-BOGEBNmX.d.ts → api-Ddy9fqkY.d.ts} +40 -0
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/index.cjs +667 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -7
- package/dist/index.d.ts +24 -7
- package/dist/index.js +663 -143
- package/dist/index.js.map +1 -1
- package/dist/methods.cjs.map +1 -1
- package/dist/methods.d.cts +1 -1
- package/dist/methods.d.ts +1 -1
- package/dist/patch.cjs +22 -6
- package/dist/patch.cjs.map +1 -1
- package/dist/patch.js +22 -6
- package/dist/patch.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +28 -9
- package/dist/types.d.ts +28 -9
- package/dist/types.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 {
|
|
@@ -210,7 +220,10 @@ const coord = new Coordinator({
|
|
|
210
220
|
|
|
211
221
|
Every call to `task.route`, `review.depth`, and `escalate.auto`
|
|
212
222
|
emits a `route_decision` artefact. `task.route` also updates the
|
|
213
|
-
task's `assignee`.
|
|
223
|
+
task's `assignee`. The caller must be a workspace member. The default
|
|
224
|
+
policy takes the first candidate that is a member and is not paused; a
|
|
225
|
+
`routingPolicy` selection that is not a member is refused with
|
|
226
|
+
`-32510`, and one that is paused with `-32063`.
|
|
214
227
|
|
|
215
228
|
## Audit and SCITT (audit-scitt/1.0)
|
|
216
229
|
|
|
@@ -259,9 +272,9 @@ and response shapes. The full audit notes are in the parent repo's
|
|
|
259
272
|
the original TypeScript expansion and 0.2.3 / 0.2.4 covering the MCP
|
|
260
273
|
and A2A transport additions.
|
|
261
274
|
|
|
262
|
-
##
|
|
275
|
+
## Licence
|
|
263
276
|
|
|
264
|
-
Apache 2.0.
|
|
277
|
+
Apache 2.0. This package ships the licence in its own [LICENSE](./LICENSE) file.
|
|
265
278
|
|
|
266
279
|
## Specification
|
|
267
280
|
|
|
@@ -79,6 +79,10 @@ interface CoordinatorOptions {
|
|
|
79
79
|
maxEnvelopeBytes?: number;
|
|
80
80
|
onAudit?: AuditListener;
|
|
81
81
|
onAutoEscalate?: (task: Task, to: ParticipantUri) => void;
|
|
82
|
+
onStoreError?: (err: unknown, record: {
|
|
83
|
+
id: string;
|
|
84
|
+
version: number;
|
|
85
|
+
}) => void;
|
|
82
86
|
verifyOidcToken?: TokenVerifier;
|
|
83
87
|
verifyVc?: CredentialVerifier;
|
|
84
88
|
scittSubmitter?: ScittSubmitter;
|
|
@@ -110,8 +114,16 @@ declare class Coordinator {
|
|
|
110
114
|
readonly ids: IdFactory;
|
|
111
115
|
readonly store: Store;
|
|
112
116
|
private wsVersions;
|
|
117
|
+
private saveChains;
|
|
113
118
|
private clockMs?;
|
|
119
|
+
private frozenNow?;
|
|
114
120
|
private auditListeners;
|
|
121
|
+
/**
|
|
122
|
+
* The signed calls on each workspace's log, by what their senders signed:
|
|
123
|
+
* recorded refusals with their seq and code, and accepted calls. Rebuilt
|
|
124
|
+
* from the log when missing, so a snapshot never carries it.
|
|
125
|
+
*/
|
|
126
|
+
private signedIndexes;
|
|
115
127
|
/** Method handler registry; profiles plug into this. */
|
|
116
128
|
readonly handlers: Map<string, Handler>;
|
|
117
129
|
/** Custom lapse-check function (whisper/1.0). Set by the whisper profile. */
|
|
@@ -129,6 +141,10 @@ declare class Coordinator {
|
|
|
129
141
|
private applyRecords;
|
|
130
142
|
/** Persist the current snapshot of one workspace to the store. */
|
|
131
143
|
private persist;
|
|
144
|
+
private trackSave;
|
|
145
|
+
private reportStoreError;
|
|
146
|
+
drainSaves(): Promise<void>;
|
|
147
|
+
private advanceClock;
|
|
132
148
|
now(): string;
|
|
133
149
|
onAudit(listener: AuditListener): () => void;
|
|
134
150
|
/** Convenience: get a workspace by id, or undefined. */
|
|
@@ -160,10 +176,34 @@ declare class Coordinator {
|
|
|
160
176
|
* any existing in-memory workspaces. */
|
|
161
177
|
restore(data: unknown): void;
|
|
162
178
|
dispatch(envelope: Envelope): Envelope;
|
|
179
|
+
private _dispatch;
|
|
163
180
|
recordAudit(ws: Workspace, envelope: Envelope): void;
|
|
181
|
+
/**
|
|
182
|
+
* SPECIFICATION 10.1: record a refused call when it is a governed attempt.
|
|
183
|
+
* The caller must be a member of an existing workspace, the call must be a
|
|
184
|
+
* JSON-RPC call for a method that is not a read, and the refusal must be one
|
|
185
|
+
* `refusalIsRecorded` names. The request is kept as it arrived, so a
|
|
186
|
+
* signature on it still verifies. Every call that reaches a recorded refusal
|
|
187
|
+
* has passed the canonical check, so it can be hashed into the chain.
|
|
188
|
+
*/
|
|
189
|
+
private recordRefusal;
|
|
190
|
+
/** The signed calls on this workspace's log, built from the log when missing. */
|
|
191
|
+
private signedIndex;
|
|
192
|
+
private appendEntry;
|
|
164
193
|
private verifySignature;
|
|
165
194
|
/** Lookup the key valid for a given participant at a given time. */
|
|
166
195
|
lookupKey(member: Member, kid: string, ts: string): KeyRecord | null;
|
|
196
|
+
/**
|
|
197
|
+
* The SPECIFICATION 15.4 check, per method rather than per namespace.
|
|
198
|
+
*
|
|
199
|
+
* Core is always present, so a core method is never gated. The reads in
|
|
200
|
+
* ALWAYS_AVAILABLE are never gated either: a workspace must be able to ask
|
|
201
|
+
* what it is and to check its own chain, and `audit.verify_chain` belongs to
|
|
202
|
+
* `audit-scitt/1.0` while chaining also turns on through an option, so
|
|
203
|
+
* gating it would let a workspace write a hash-linked chain it is refused
|
|
204
|
+
* permission to verify.
|
|
205
|
+
*/
|
|
206
|
+
private profileGate;
|
|
167
207
|
private checkStepUp;
|
|
168
208
|
private registerCoreHandlers;
|
|
169
209
|
private registerProfileHandlers;
|
|
@@ -79,6 +79,10 @@ interface CoordinatorOptions {
|
|
|
79
79
|
maxEnvelopeBytes?: number;
|
|
80
80
|
onAudit?: AuditListener;
|
|
81
81
|
onAutoEscalate?: (task: Task, to: ParticipantUri) => void;
|
|
82
|
+
onStoreError?: (err: unknown, record: {
|
|
83
|
+
id: string;
|
|
84
|
+
version: number;
|
|
85
|
+
}) => void;
|
|
82
86
|
verifyOidcToken?: TokenVerifier;
|
|
83
87
|
verifyVc?: CredentialVerifier;
|
|
84
88
|
scittSubmitter?: ScittSubmitter;
|
|
@@ -110,8 +114,16 @@ declare class Coordinator {
|
|
|
110
114
|
readonly ids: IdFactory;
|
|
111
115
|
readonly store: Store;
|
|
112
116
|
private wsVersions;
|
|
117
|
+
private saveChains;
|
|
113
118
|
private clockMs?;
|
|
119
|
+
private frozenNow?;
|
|
114
120
|
private auditListeners;
|
|
121
|
+
/**
|
|
122
|
+
* The signed calls on each workspace's log, by what their senders signed:
|
|
123
|
+
* recorded refusals with their seq and code, and accepted calls. Rebuilt
|
|
124
|
+
* from the log when missing, so a snapshot never carries it.
|
|
125
|
+
*/
|
|
126
|
+
private signedIndexes;
|
|
115
127
|
/** Method handler registry; profiles plug into this. */
|
|
116
128
|
readonly handlers: Map<string, Handler>;
|
|
117
129
|
/** Custom lapse-check function (whisper/1.0). Set by the whisper profile. */
|
|
@@ -129,6 +141,10 @@ declare class Coordinator {
|
|
|
129
141
|
private applyRecords;
|
|
130
142
|
/** Persist the current snapshot of one workspace to the store. */
|
|
131
143
|
private persist;
|
|
144
|
+
private trackSave;
|
|
145
|
+
private reportStoreError;
|
|
146
|
+
drainSaves(): Promise<void>;
|
|
147
|
+
private advanceClock;
|
|
132
148
|
now(): string;
|
|
133
149
|
onAudit(listener: AuditListener): () => void;
|
|
134
150
|
/** Convenience: get a workspace by id, or undefined. */
|
|
@@ -160,10 +176,34 @@ declare class Coordinator {
|
|
|
160
176
|
* any existing in-memory workspaces. */
|
|
161
177
|
restore(data: unknown): void;
|
|
162
178
|
dispatch(envelope: Envelope): Envelope;
|
|
179
|
+
private _dispatch;
|
|
163
180
|
recordAudit(ws: Workspace, envelope: Envelope): void;
|
|
181
|
+
/**
|
|
182
|
+
* SPECIFICATION 10.1: record a refused call when it is a governed attempt.
|
|
183
|
+
* The caller must be a member of an existing workspace, the call must be a
|
|
184
|
+
* JSON-RPC call for a method that is not a read, and the refusal must be one
|
|
185
|
+
* `refusalIsRecorded` names. The request is kept as it arrived, so a
|
|
186
|
+
* signature on it still verifies. Every call that reaches a recorded refusal
|
|
187
|
+
* has passed the canonical check, so it can be hashed into the chain.
|
|
188
|
+
*/
|
|
189
|
+
private recordRefusal;
|
|
190
|
+
/** The signed calls on this workspace's log, built from the log when missing. */
|
|
191
|
+
private signedIndex;
|
|
192
|
+
private appendEntry;
|
|
164
193
|
private verifySignature;
|
|
165
194
|
/** Lookup the key valid for a given participant at a given time. */
|
|
166
195
|
lookupKey(member: Member, kid: string, ts: string): KeyRecord | null;
|
|
196
|
+
/**
|
|
197
|
+
* The SPECIFICATION 15.4 check, per method rather than per namespace.
|
|
198
|
+
*
|
|
199
|
+
* Core is always present, so a core method is never gated. The reads in
|
|
200
|
+
* ALWAYS_AVAILABLE are never gated either: a workspace must be able to ask
|
|
201
|
+
* what it is and to check its own chain, and `audit.verify_chain` belongs to
|
|
202
|
+
* `audit-scitt/1.0` while chaining also turns on through an option, so
|
|
203
|
+
* gating it would let a workspace write a hash-linked chain it is refused
|
|
204
|
+
* permission to verify.
|
|
205
|
+
*/
|
|
206
|
+
private profileGate;
|
|
167
207
|
private checkStepUp;
|
|
168
208
|
private registerCoreHandlers;
|
|
169
209
|
private registerProfileHandlers;
|
package/dist/api.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import './methods.cjs';
|
|
2
|
-
export { b as CoordinatorApi, c as CoordinatorError, g as call, m as makeApi } from './api-
|
|
2
|
+
export { b as CoordinatorApi, c as CoordinatorError, g as call, m as makeApi } from './api-BkvwN42r.cjs';
|
|
3
3
|
import './types.cjs';
|
|
4
4
|
import './storage/store.cjs';
|
package/dist/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import './methods.js';
|
|
2
|
-
export { b as CoordinatorApi, c as CoordinatorError, g as call, m as makeApi } from './api-
|
|
2
|
+
export { b as CoordinatorApi, c as CoordinatorError, g as call, m as makeApi } from './api-Ddy9fqkY.js';
|
|
3
3
|
import './types.js';
|
|
4
4
|
import './storage/store.js';
|