@brightbeamai/chap-coordinator 0.2.9 → 0.2.10
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 +6 -1
- package/dist/{api-DE78DA0o.d.cts → api-CcT_8H5j.d.cts} +13 -0
- package/dist/{api-CPEk0V6i.d.ts → api-Cvkhcalw.d.ts} +13 -0
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/index.cjs +91 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +91 -3
- package/dist/index.js.map +1 -1
- package/dist/jsonrpc.cjs +2 -0
- package/dist/jsonrpc.cjs.map +1 -1
- package/dist/jsonrpc.d.cts +2 -0
- package/dist/jsonrpc.d.ts +2 -0
- package/dist/jsonrpc.js +2 -0
- package/dist/jsonrpc.js.map +1 -1
- package/dist/methods.cjs.map +1 -1
- package/dist/methods.d.cts +22 -2
- package/dist/methods.d.ts +22 -2
- package/dist/storage/sqlite.cjs +5 -2
- package/dist/storage/sqlite.cjs.map +1 -1
- package/dist/storage/sqlite.js +4 -9
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/store.cjs.map +1 -1
- package/dist/storage/store.d.cts +26 -1
- package/dist/storage/store.d.ts +26 -1
- package/dist/storage/store.js.map +1 -1
- package/package.json +79 -27
package/README.md
CHANGED
|
@@ -216,7 +216,12 @@ task's `assignee`.
|
|
|
216
216
|
|
|
217
217
|
Audit entries are hash-linked when the profile is active.
|
|
218
218
|
`audit.verify_chain` replays the chain locally and confirms
|
|
219
|
-
`prev_hash` continuity.
|
|
219
|
+
`prev_hash` continuity. The verdict names the range it covered:
|
|
220
|
+
`status` is `verified` with `ok: true` only when every entry was
|
|
221
|
+
checked, and `not_evaluated` with `ok: false` when part of the log
|
|
222
|
+
lies outside the chain, which happens when the profile is added to a
|
|
223
|
+
workspace that already has entries. A broken chain is an error. Read
|
|
224
|
+
`status`, or read `ok` and fail closed.
|
|
220
225
|
|
|
221
226
|
`audit.submit_to_scitt` builds a SCITT-shaped signed statement for
|
|
222
227
|
each audit entry in the requested range and passes it to a
|
|
@@ -175,6 +175,19 @@ declare class Coordinator {
|
|
|
175
175
|
private opTaskComplete;
|
|
176
176
|
private opAuditRead;
|
|
177
177
|
private opReviewRequest;
|
|
178
|
+
/**
|
|
179
|
+
* Verify an optional `approved_artefact_digest` against the artefact under
|
|
180
|
+
* review (review/1.0 S3.2).
|
|
181
|
+
*
|
|
182
|
+
* The digest sits in params, so it is inside whatever the envelope signature
|
|
183
|
+
* covers: the decision then attests the content the reviewer saw rather than
|
|
184
|
+
* a task reference, and a relying party can check it without trusting the
|
|
185
|
+
* Coordinator that produced it. Absent, behaviour is exactly as before.
|
|
186
|
+
*
|
|
187
|
+
* On mismatch nothing is recorded and no state changes, so a client that
|
|
188
|
+
* computes the wrong digest refuses only its own decision.
|
|
189
|
+
*/
|
|
190
|
+
private checkArtefactDigest;
|
|
178
191
|
private opDecide;
|
|
179
192
|
private opDecideOverride;
|
|
180
193
|
private opAbstainDeclare;
|
|
@@ -175,6 +175,19 @@ declare class Coordinator {
|
|
|
175
175
|
private opTaskComplete;
|
|
176
176
|
private opAuditRead;
|
|
177
177
|
private opReviewRequest;
|
|
178
|
+
/**
|
|
179
|
+
* Verify an optional `approved_artefact_digest` against the artefact under
|
|
180
|
+
* review (review/1.0 S3.2).
|
|
181
|
+
*
|
|
182
|
+
* The digest sits in params, so it is inside whatever the envelope signature
|
|
183
|
+
* covers: the decision then attests the content the reviewer saw rather than
|
|
184
|
+
* a task reference, and a relying party can check it without trusting the
|
|
185
|
+
* Coordinator that produced it. Absent, behaviour is exactly as before.
|
|
186
|
+
*
|
|
187
|
+
* On mismatch nothing is recorded and no state changes, so a client that
|
|
188
|
+
* computes the wrong digest refuses only its own decision.
|
|
189
|
+
*/
|
|
190
|
+
private checkArtefactDigest;
|
|
178
191
|
private opDecide;
|
|
179
192
|
private opDecideOverride;
|
|
180
193
|
private opAbstainDeclare;
|
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-CcT_8H5j.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-Cvkhcalw.js';
|
|
3
3
|
import './types.js';
|
|
4
4
|
import './storage/store.js';
|
package/dist/index.cjs
CHANGED
|
@@ -259,6 +259,7 @@ var E = {
|
|
|
259
259
|
NOT_AUTHORISED: -32011,
|
|
260
260
|
PATCH_FAILED: -32012,
|
|
261
261
|
REVIEW_LAPSED: -32013,
|
|
262
|
+
REVIEW_ALREADY_OPEN: -32014,
|
|
262
263
|
// whisper/1.0 profile (profiles/whisper.md S6)
|
|
263
264
|
WHISPER_ALREADY_ANSWERED: -32020,
|
|
264
265
|
WHISPER_LAPSED: -32021,
|
|
@@ -285,6 +286,7 @@ var E = {
|
|
|
285
286
|
SIG_KEY_NOT_FOUND: -32071,
|
|
286
287
|
SIG_KEY_REVOKED: -32072,
|
|
287
288
|
SIG_ROTATION_KEY_MISMATCH: -32073,
|
|
289
|
+
SIG_ARTEFACT_DIGEST_MISMATCH: -32074,
|
|
288
290
|
// audit-scitt/1.0 profile (profiles/audit-scitt.md S8)
|
|
289
291
|
SCITT_UNREACHABLE: -32080,
|
|
290
292
|
SCITT_STATEMENT_REJECTED: -32081,
|
|
@@ -752,6 +754,12 @@ function registerAuditScitt(coord) {
|
|
|
752
754
|
if (!(ws.chain_enabled || coord.options.enableChain)) {
|
|
753
755
|
return { error: rpcError(E.PARAMS, "Chain not enabled for this workspace") };
|
|
754
756
|
}
|
|
757
|
+
if (p.from_seq !== void 0 || p.to_seq !== void 0) {
|
|
758
|
+
return { error: rpcError(
|
|
759
|
+
E.PARAMS,
|
|
760
|
+
"Range verification is not implemented; omit from_seq and to_seq to verify the whole log."
|
|
761
|
+
) };
|
|
762
|
+
}
|
|
755
763
|
let start = ws.audit.findIndex((e) => e.prev_hash != null);
|
|
756
764
|
if (start < 0) start = ws.audit.length;
|
|
757
765
|
const errors = [];
|
|
@@ -763,16 +771,35 @@ function registerAuditScitt(coord) {
|
|
|
763
771
|
}
|
|
764
772
|
prev = sha256Hex(Buffer.concat([canonicalize(e.envelope), Buffer.from(expectedPrev, "utf-8")]));
|
|
765
773
|
}
|
|
766
|
-
const storedHead = ws.chain_head
|
|
774
|
+
const storedHead = ws.chain_head === void 0 || ws.chain_head === null ? ZERO_HASH : ws.chain_head;
|
|
767
775
|
if (prev !== storedHead) {
|
|
768
776
|
errors.push("chain_head mismatch: replay does not match stored head");
|
|
769
777
|
}
|
|
770
778
|
if (errors.length) {
|
|
771
779
|
return { error: rpcError(E.PARAMS, errors.join("; ")) };
|
|
772
780
|
}
|
|
781
|
+
const entriesTotal = ws.audit.length;
|
|
782
|
+
const entriesChecked = entriesTotal - start;
|
|
783
|
+
const entriesUnchecked = start;
|
|
784
|
+
if (entriesUnchecked > 0) {
|
|
785
|
+
return { result: {
|
|
786
|
+
status: "not_evaluated",
|
|
787
|
+
ok: false,
|
|
788
|
+
reason: "unchained_prefix",
|
|
789
|
+
entries_total: entriesTotal,
|
|
790
|
+
entries_checked: entriesChecked,
|
|
791
|
+
entries_unchecked: entriesUnchecked,
|
|
792
|
+
checked_from_seq: entriesChecked > 0 ? ws.audit[start].seq : null,
|
|
793
|
+
chain_head: storedHead
|
|
794
|
+
} };
|
|
795
|
+
}
|
|
773
796
|
return { result: {
|
|
797
|
+
status: "verified",
|
|
774
798
|
ok: true,
|
|
775
|
-
|
|
799
|
+
entries_total: entriesTotal,
|
|
800
|
+
entries_checked: entriesChecked,
|
|
801
|
+
entries_unchecked: 0,
|
|
802
|
+
checked_from_seq: entriesChecked > 0 ? ws.audit[start].seq : null,
|
|
776
803
|
chain_head: storedHead
|
|
777
804
|
} };
|
|
778
805
|
});
|
|
@@ -2463,12 +2490,38 @@ var Coordinator = class {
|
|
|
2463
2490
|
const reviewers = Array.isArray(to) ? to : typeof to === "string" ? [to] : [];
|
|
2464
2491
|
if (!reviewers.length) return { error: rpcError(E.PARAMS, "review.request needs 'to'") };
|
|
2465
2492
|
const now = this.now();
|
|
2493
|
+
const rule = p.rule || "any_one_approves";
|
|
2494
|
+
if (task.state === "review_requested" && task.review) {
|
|
2495
|
+
if (contentHash(task.pending_artefact) !== contentHash(p.artefact)) {
|
|
2496
|
+
return { error: rpcError(
|
|
2497
|
+
E.REVIEW_ALREADY_OPEN,
|
|
2498
|
+
"A review is already open on this task with different content. Decide, abstain, escalate or cancel it before requesting review of a new artefact."
|
|
2499
|
+
) };
|
|
2500
|
+
}
|
|
2501
|
+
if (rule !== task.review.rule) {
|
|
2502
|
+
return { error: rpcError(
|
|
2503
|
+
E.REVIEW_ALREADY_OPEN,
|
|
2504
|
+
`Cannot change the decision rule of an open review (currently ${task.review.rule})`
|
|
2505
|
+
) };
|
|
2506
|
+
}
|
|
2507
|
+
for (const r of reviewers) {
|
|
2508
|
+
if (!task.review.requested_to.includes(r)) task.review.requested_to.push(r);
|
|
2509
|
+
}
|
|
2510
|
+
if (p.deadline !== void 0) task.review.deadline = p.deadline;
|
|
2511
|
+
task.updated_at = now;
|
|
2512
|
+
return { result: {
|
|
2513
|
+
state: "review_requested",
|
|
2514
|
+
review_id: task.id,
|
|
2515
|
+
amended: true,
|
|
2516
|
+
requested_to: [...task.review.requested_to]
|
|
2517
|
+
} };
|
|
2518
|
+
}
|
|
2466
2519
|
task.state = "review_requested";
|
|
2467
2520
|
task.updated_at = now;
|
|
2468
2521
|
task.review = {
|
|
2469
2522
|
requested_at: now,
|
|
2470
2523
|
requested_to: reviewers,
|
|
2471
|
-
rule
|
|
2524
|
+
rule,
|
|
2472
2525
|
deadline: p.deadline,
|
|
2473
2526
|
decisions: []
|
|
2474
2527
|
};
|
|
@@ -2476,6 +2529,37 @@ var Coordinator = class {
|
|
|
2476
2529
|
task.pending_artefact = p.artefact;
|
|
2477
2530
|
return { result: { state: "review_requested", review_id: task.id } };
|
|
2478
2531
|
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Verify an optional `approved_artefact_digest` against the artefact under
|
|
2534
|
+
* review (review/1.0 S3.2).
|
|
2535
|
+
*
|
|
2536
|
+
* The digest sits in params, so it is inside whatever the envelope signature
|
|
2537
|
+
* covers: the decision then attests the content the reviewer saw rather than
|
|
2538
|
+
* a task reference, and a relying party can check it without trusting the
|
|
2539
|
+
* Coordinator that produced it. Absent, behaviour is exactly as before.
|
|
2540
|
+
*
|
|
2541
|
+
* On mismatch nothing is recorded and no state changes, so a client that
|
|
2542
|
+
* computes the wrong digest refuses only its own decision.
|
|
2543
|
+
*/
|
|
2544
|
+
checkArtefactDigest(p, task) {
|
|
2545
|
+
const declared = p.approved_artefact_digest;
|
|
2546
|
+
if (declared === void 0 || declared === null) return null;
|
|
2547
|
+
if (typeof declared !== "string") {
|
|
2548
|
+
return { error: rpcError(E.PARAMS, "approved_artefact_digest must be a string") };
|
|
2549
|
+
}
|
|
2550
|
+
if (task.pending_artefact === void 0) {
|
|
2551
|
+
return { error: rpcError(E.PARAMS, "No artefact under review to bind a digest to") };
|
|
2552
|
+
}
|
|
2553
|
+
const actual = contentHash(task.pending_artefact);
|
|
2554
|
+
if (declared !== actual) {
|
|
2555
|
+
return { error: rpcError(
|
|
2556
|
+
E.SIG_ARTEFACT_DIGEST_MISMATCH,
|
|
2557
|
+
"approved_artefact_digest does not match the artefact under review",
|
|
2558
|
+
{ declared, actual }
|
|
2559
|
+
) };
|
|
2560
|
+
}
|
|
2561
|
+
return null;
|
|
2562
|
+
}
|
|
2479
2563
|
opDecide(p, kind) {
|
|
2480
2564
|
const miss = missing(p, ["workspace", "from", "task_id"]);
|
|
2481
2565
|
if (miss) return { error: rpcError(E.PARAMS, `Missing field: ${miss}`) };
|
|
@@ -2492,6 +2576,8 @@ var Coordinator = class {
|
|
|
2492
2576
|
}
|
|
2493
2577
|
const notReviewer = this.requireReviewer(task, p.from);
|
|
2494
2578
|
if (notReviewer) return notReviewer;
|
|
2579
|
+
const digestError = this.checkArtefactDigest(p, task);
|
|
2580
|
+
if (digestError) return digestError;
|
|
2495
2581
|
const now = this.now();
|
|
2496
2582
|
task.review.decisions.push({
|
|
2497
2583
|
reviewer: p.from,
|
|
@@ -2526,6 +2612,8 @@ var Coordinator = class {
|
|
|
2526
2612
|
}
|
|
2527
2613
|
const notReviewer = this.requireReviewer(task, p.from);
|
|
2528
2614
|
if (notReviewer) return notReviewer;
|
|
2615
|
+
const digestError = this.checkArtefactDigest(p, task);
|
|
2616
|
+
if (digestError) return digestError;
|
|
2529
2617
|
const base = task.pending_artefact;
|
|
2530
2618
|
if (base === void 0) return { error: rpcError(E.PARAMS, "No base artefact for override") };
|
|
2531
2619
|
let applied;
|