verdifax 1.1.0 → 1.2.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 +68 -2
- package/dist/index.d.mts +186 -3
- package/dist/index.d.ts +186 -3
- package/dist/index.js +350 -5
- package/dist/index.mjs +347 -5
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -167,7 +167,144 @@ function normalizedJob(job) {
|
|
|
167
167
|
if (job.final) out.final = true;
|
|
168
168
|
return out;
|
|
169
169
|
}
|
|
170
|
+
var POLICY_ACTOR_TYPES = ["agent", "user", "service", "system"];
|
|
171
|
+
var POLICY_OBJECT_TYPES = ["text", "transaction", "code", "data", "model", "file"];
|
|
172
|
+
var POLICY_CLASSIFICATIONS = [
|
|
173
|
+
"PUBLIC",
|
|
174
|
+
"INTERNAL",
|
|
175
|
+
"CONFIDENTIAL",
|
|
176
|
+
"RESTRICTED",
|
|
177
|
+
"TOP_SECRET",
|
|
178
|
+
"TS_SCI"
|
|
179
|
+
];
|
|
180
|
+
var MAX_POLICY_OBJECT_ID_BYTES = 256;
|
|
181
|
+
var POLICY_CONTEXT_FIELDS = {
|
|
182
|
+
actor_type: true,
|
|
183
|
+
mfa_satisfied: true,
|
|
184
|
+
session_age_seconds: true,
|
|
185
|
+
object_type: true,
|
|
186
|
+
object_id: true,
|
|
187
|
+
classification: true
|
|
188
|
+
};
|
|
189
|
+
function refuseOutsideList(value, allowed, fieldName) {
|
|
190
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
191
|
+
throw new VerdifaxError(
|
|
192
|
+
`verdifax: policy_context.${fieldName} must be one of ${allowed.join(", ")}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function checkedPolicyContext(context) {
|
|
197
|
+
if (typeof context !== "object" || context === null || Array.isArray(context)) {
|
|
198
|
+
throw new VerdifaxError("verdifax: policy_context must be an object");
|
|
199
|
+
}
|
|
200
|
+
refuseUnknownFields(context, POLICY_CONTEXT_FIELDS, "policy_context");
|
|
201
|
+
const out = {};
|
|
202
|
+
const {
|
|
203
|
+
actor_type,
|
|
204
|
+
mfa_satisfied,
|
|
205
|
+
session_age_seconds,
|
|
206
|
+
object_type,
|
|
207
|
+
object_id,
|
|
208
|
+
classification
|
|
209
|
+
} = context;
|
|
210
|
+
if (actor_type !== void 0) {
|
|
211
|
+
refuseOutsideList(actor_type, POLICY_ACTOR_TYPES, "actor_type");
|
|
212
|
+
out.actor_type = actor_type;
|
|
213
|
+
}
|
|
214
|
+
if (mfa_satisfied !== void 0) {
|
|
215
|
+
if (typeof mfa_satisfied !== "boolean") {
|
|
216
|
+
throw new VerdifaxError(
|
|
217
|
+
"verdifax: policy_context.mfa_satisfied must be true or false"
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
out.mfa_satisfied = mfa_satisfied;
|
|
221
|
+
}
|
|
222
|
+
if (session_age_seconds !== void 0) {
|
|
223
|
+
if (!Number.isSafeInteger(session_age_seconds) || session_age_seconds < 0) {
|
|
224
|
+
throw new VerdifaxError(
|
|
225
|
+
"verdifax: policy_context.session_age_seconds must be a whole number of seconds, not negative"
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
out.session_age_seconds = session_age_seconds;
|
|
229
|
+
}
|
|
230
|
+
if (object_type !== void 0) {
|
|
231
|
+
refuseOutsideList(object_type, POLICY_OBJECT_TYPES, "object_type");
|
|
232
|
+
out.object_type = object_type;
|
|
233
|
+
}
|
|
234
|
+
if (object_id !== void 0) {
|
|
235
|
+
if (typeof object_id !== "string") {
|
|
236
|
+
throw new VerdifaxError("verdifax: policy_context.object_id must be a string");
|
|
237
|
+
}
|
|
238
|
+
if (Buffer.byteLength(object_id, "utf8") > MAX_POLICY_OBJECT_ID_BYTES) {
|
|
239
|
+
throw new VerdifaxError(
|
|
240
|
+
`verdifax: policy_context.object_id is longer than ${MAX_POLICY_OBJECT_ID_BYTES} bytes`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
out.object_id = object_id;
|
|
244
|
+
}
|
|
245
|
+
if (classification !== void 0) {
|
|
246
|
+
refuseOutsideList(classification, POLICY_CLASSIFICATIONS, "classification");
|
|
247
|
+
out.classification = classification;
|
|
248
|
+
}
|
|
249
|
+
return out;
|
|
250
|
+
}
|
|
251
|
+
function refuseActorFactsWithoutActor(context, attested) {
|
|
252
|
+
const declaresActor = context.actor_type !== void 0 || context.mfa_satisfied === true || (context.session_age_seconds ?? 0) !== 0;
|
|
253
|
+
if (declaresActor && attested?.attested !== true) {
|
|
254
|
+
throw new VerdifaxError(
|
|
255
|
+
"verdifax: policy_context.actor_type, mfa_satisfied and session_age_seconds describe the attested actor; send attested_context with the actor, for example its actor_id"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
var EXECUTE_FIELD_SET = {
|
|
260
|
+
payload: true,
|
|
261
|
+
payload_text: true,
|
|
262
|
+
program_id: true,
|
|
263
|
+
route_id: true,
|
|
264
|
+
registry_record_hash: true,
|
|
265
|
+
ai_output_text: true,
|
|
266
|
+
attested_context: true,
|
|
267
|
+
reproducibility_context: true,
|
|
268
|
+
request_substance: true,
|
|
269
|
+
authorization_chain: true,
|
|
270
|
+
regulatory_scaffolding: true,
|
|
271
|
+
causal_graph: true,
|
|
272
|
+
request_signature: true,
|
|
273
|
+
signed_request: true,
|
|
274
|
+
job: true,
|
|
275
|
+
policy_context: true,
|
|
276
|
+
expected_policy_hash: true
|
|
277
|
+
};
|
|
278
|
+
var EXECUTE_FIELDS = Object.freeze(
|
|
279
|
+
Object.keys(EXECUTE_FIELD_SET)
|
|
280
|
+
);
|
|
281
|
+
var ATTESTED_CONTEXT_FIELDS = {
|
|
282
|
+
attested: true,
|
|
283
|
+
actor_id: true,
|
|
284
|
+
actor_role: true,
|
|
285
|
+
authorization_policy: true,
|
|
286
|
+
actor_signature: true,
|
|
287
|
+
model_provider: true,
|
|
288
|
+
model_name: true,
|
|
289
|
+
model_version: true,
|
|
290
|
+
model_temperature: true,
|
|
291
|
+
prompt_hash: true,
|
|
292
|
+
decision_kind: true,
|
|
293
|
+
decision_result: true,
|
|
294
|
+
decision_note: true
|
|
295
|
+
};
|
|
296
|
+
function refuseUnknownRequestFields(req, label = "request") {
|
|
297
|
+
if (typeof req !== "object" || req === null || Array.isArray(req)) {
|
|
298
|
+
throw new VerdifaxError(`verdifax: ${label} must be an object`);
|
|
299
|
+
}
|
|
300
|
+
refuseUnknownFields(req, EXECUTE_FIELD_SET, label);
|
|
301
|
+
const context = req.attested_context;
|
|
302
|
+
if (typeof context === "object" && context !== null) {
|
|
303
|
+
refuseUnknownFields(context, ATTESTED_CONTEXT_FIELDS, "attested_context");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
170
306
|
function withExecuteFlags(req) {
|
|
307
|
+
refuseUnknownRequestFields(req);
|
|
171
308
|
const body = { ...req };
|
|
172
309
|
if (req.job === void 0 || req.job === null) {
|
|
173
310
|
delete body.job;
|
|
@@ -180,6 +317,17 @@ function withExecuteFlags(req) {
|
|
|
180
317
|
if (req.reproducibility_context) {
|
|
181
318
|
body.reproducibility_context = withDeclaredFlag(req.reproducibility_context);
|
|
182
319
|
}
|
|
320
|
+
if (req.policy_context === void 0 || req.policy_context === null) {
|
|
321
|
+
delete body.policy_context;
|
|
322
|
+
} else {
|
|
323
|
+
body.policy_context = checkedPolicyContext(req.policy_context);
|
|
324
|
+
refuseActorFactsWithoutActor(body.policy_context, body.attested_context);
|
|
325
|
+
}
|
|
326
|
+
if (req.expected_policy_hash === void 0 || req.expected_policy_hash === null) {
|
|
327
|
+
delete body.expected_policy_hash;
|
|
328
|
+
} else {
|
|
329
|
+
validateHex64(req.expected_policy_hash, "expected_policy_hash");
|
|
330
|
+
}
|
|
183
331
|
return body;
|
|
184
332
|
}
|
|
185
333
|
|
|
@@ -190,6 +338,7 @@ var REQUEST_SIGNATURE_VERSION_2 = "2";
|
|
|
190
338
|
var JOB_FORM = "verdifax.job.v1";
|
|
191
339
|
var REQUEST_KEY_PROOF_FORM = "verdifax.request-key.v1";
|
|
192
340
|
var REQUEST_KEY_CHANGE_FORM = "verdifax.request-key-change.v1";
|
|
341
|
+
var POLICY_ACTIVATION_FORM = "verdifax.policy-activation.v1";
|
|
193
342
|
var SIGNING_KEY_BYTES = 32;
|
|
194
343
|
var PKCS8_ED25519_HEADER = Buffer.from(
|
|
195
344
|
"302e020100300506032b657004220420",
|
|
@@ -344,6 +493,36 @@ function requestKeyChangeApproval(currentSigningKey, newPublicKey, options = {})
|
|
|
344
493
|
nonce
|
|
345
494
|
};
|
|
346
495
|
}
|
|
496
|
+
function policyActivationPreimage(fields) {
|
|
497
|
+
return Buffer.from(
|
|
498
|
+
[
|
|
499
|
+
POLICY_ACTIVATION_FORM,
|
|
500
|
+
fields.chain_id,
|
|
501
|
+
fields.document_hash,
|
|
502
|
+
fields.previous_activation_hash,
|
|
503
|
+
fields.signed_at,
|
|
504
|
+
fields.nonce
|
|
505
|
+
].join("\0"),
|
|
506
|
+
"utf8"
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
function policyActivationApproval(signingKey, fields, options = {}) {
|
|
510
|
+
const key = loadSigningKey(signingKey);
|
|
511
|
+
const signed_at = options.signedAt ?? signedAtNow();
|
|
512
|
+
const nonce = options.nonce ?? randomBytes(16).toString("hex");
|
|
513
|
+
const preimage = policyActivationPreimage({
|
|
514
|
+
chain_id: fields.chain_id,
|
|
515
|
+
document_hash: fields.document_hash,
|
|
516
|
+
previous_activation_hash: fields.previous_activation_hash,
|
|
517
|
+
signed_at,
|
|
518
|
+
nonce
|
|
519
|
+
});
|
|
520
|
+
return {
|
|
521
|
+
signature: sign(null, preimage, key).toString("base64"),
|
|
522
|
+
signed_at,
|
|
523
|
+
nonce
|
|
524
|
+
};
|
|
525
|
+
}
|
|
347
526
|
|
|
348
527
|
// src/job.ts
|
|
349
528
|
var STEP_OPTIONS = {
|
|
@@ -359,6 +538,8 @@ var STEP_OPTIONS = {
|
|
|
359
538
|
authorization_chain: true,
|
|
360
539
|
regulatory_scaffolding: true,
|
|
361
540
|
causal_graph: true,
|
|
541
|
+
policy_context: true,
|
|
542
|
+
expected_policy_hash: true,
|
|
362
543
|
after: true,
|
|
363
544
|
external_previous: true,
|
|
364
545
|
final: true
|
|
@@ -443,7 +624,7 @@ var VerdifaxJob = class {
|
|
|
443
624
|
};
|
|
444
625
|
|
|
445
626
|
// src/version.ts
|
|
446
|
-
var SDK_VERSION = "1.
|
|
627
|
+
var SDK_VERSION = "1.2.0";
|
|
447
628
|
|
|
448
629
|
// src/client.ts
|
|
449
630
|
var DEFAULT_BASE_URL = "https://api.verdifax.com";
|
|
@@ -453,10 +634,42 @@ var DEFAULT_RETRY_BASE_DELAY_MS = 200;
|
|
|
453
634
|
var SDK_USER_AGENT = `verdifax-sdk-node/${SDK_VERSION}`;
|
|
454
635
|
var HEADER_API_KEY = "X-Verdifax-Key";
|
|
455
636
|
var REQUEST_KEY_PATH = "/keys/request-key";
|
|
637
|
+
var POLICIES_PATH = "/policies";
|
|
456
638
|
var HEADER_EXPORT_TOTAL = "X-Verdifax-Total";
|
|
457
639
|
var HEADER_EXPORT_TRUNCATED = "X-Verdifax-Truncated";
|
|
458
640
|
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
459
641
|
var MAX_BATCH_ROWS = 1e3;
|
|
642
|
+
var ATTEST_FIELDS = {
|
|
643
|
+
payload: true,
|
|
644
|
+
payload_text: true,
|
|
645
|
+
program_id: true,
|
|
646
|
+
route_id: true,
|
|
647
|
+
registry_record_hash: true,
|
|
648
|
+
ai_output_text: true,
|
|
649
|
+
attested_context: true,
|
|
650
|
+
reproducibility_context: true,
|
|
651
|
+
request_substance: true,
|
|
652
|
+
authorization_chain: true,
|
|
653
|
+
regulatory_scaffolding: true,
|
|
654
|
+
causal_graph: true,
|
|
655
|
+
job: true,
|
|
656
|
+
policy_context: true,
|
|
657
|
+
expected_policy_hash: true
|
|
658
|
+
};
|
|
659
|
+
var PROVIDER_ARGS = Object.fromEntries(
|
|
660
|
+
[
|
|
661
|
+
"program_id",
|
|
662
|
+
"route_id",
|
|
663
|
+
"registry_record_hash",
|
|
664
|
+
"prompt",
|
|
665
|
+
"response",
|
|
666
|
+
"govern",
|
|
667
|
+
"job",
|
|
668
|
+
"policy_context",
|
|
669
|
+
"expected_policy_hash",
|
|
670
|
+
...EXECUTE_FIELDS
|
|
671
|
+
].map((name) => [name, true])
|
|
672
|
+
);
|
|
460
673
|
var VerdifaxClient = class {
|
|
461
674
|
baseURL;
|
|
462
675
|
apiKey;
|
|
@@ -492,6 +705,7 @@ var VerdifaxClient = class {
|
|
|
492
705
|
}
|
|
493
706
|
// ── Execute / Attest (auth) ───────────────────────────────────
|
|
494
707
|
async execute(req) {
|
|
708
|
+
refuseUnknownRequestFields(req);
|
|
495
709
|
this.requireExecuteFields(req);
|
|
496
710
|
const sent = this.prepared(req);
|
|
497
711
|
const out = await this.request("POST", "/execute", sent);
|
|
@@ -502,6 +716,7 @@ var VerdifaxClient = class {
|
|
|
502
716
|
* its nonce once. Both runs are recorded. A run that happens twice
|
|
503
717
|
* cannot be one job step, so a `job` is refused here. */
|
|
504
718
|
async verifyDeterminism(req) {
|
|
719
|
+
refuseUnknownRequestFields(req);
|
|
505
720
|
this.requireExecuteFields(req);
|
|
506
721
|
if (req.job !== void 0 && req.job !== null) {
|
|
507
722
|
throw new VerdifaxError(
|
|
@@ -520,8 +735,9 @@ var VerdifaxClient = class {
|
|
|
520
735
|
* signing key each row carries its own signature and nonce. A row
|
|
521
736
|
* that fails comes back in `results` with `ok: false`, so the call
|
|
522
737
|
* does not throw for it. The rows are not checked here, except a job
|
|
523
|
-
* block,
|
|
524
|
-
* any other bad row
|
|
738
|
+
* block, a `policy_context` and an `expected_policy_hash`, which are
|
|
739
|
+
* refused before anything is sent: the server reports any other bad row
|
|
740
|
+
* in that row's result.
|
|
525
741
|
*
|
|
526
742
|
* This is one POST and it is never retried. After a timeout the
|
|
527
743
|
* server may have run some or all of the rows. */
|
|
@@ -535,6 +751,7 @@ var VerdifaxClient = class {
|
|
|
535
751
|
`verdifax: executeBatch accepts at most ${MAX_BATCH_ROWS} runs (got ${runs.length})`
|
|
536
752
|
);
|
|
537
753
|
}
|
|
754
|
+
runs.forEach((run, i) => refuseUnknownRequestFields(run, `runs[${i}]`));
|
|
538
755
|
const sent = runs.map((run) => this.prepared(run));
|
|
539
756
|
const out = await this.request(
|
|
540
757
|
"POST",
|
|
@@ -555,6 +772,8 @@ var VerdifaxClient = class {
|
|
|
555
772
|
* routes the call through to the canonical /execute. */
|
|
556
773
|
async attest(req) {
|
|
557
774
|
this.requireAPIKey();
|
|
775
|
+
refuseUnknownRequestFields(req, "attest request");
|
|
776
|
+
refuseUnknownFields(req, ATTEST_FIELDS, "attest request");
|
|
558
777
|
if (req.payload !== void 0 && req.payload_text !== void 0) {
|
|
559
778
|
throw new VerdifaxError(
|
|
560
779
|
"verdifax: attest accepts payload OR payload_text, not both"
|
|
@@ -579,7 +798,9 @@ var VerdifaxClient = class {
|
|
|
579
798
|
authorization_chain: req.authorization_chain,
|
|
580
799
|
regulatory_scaffolding: req.regulatory_scaffolding,
|
|
581
800
|
causal_graph: req.causal_graph,
|
|
582
|
-
job: req.job
|
|
801
|
+
job: req.job,
|
|
802
|
+
policy_context: req.policy_context,
|
|
803
|
+
expected_policy_hash: req.expected_policy_hash
|
|
583
804
|
};
|
|
584
805
|
if (req.payload !== void 0) {
|
|
585
806
|
body.payload = Buffer.isBuffer(req.payload) ? req.payload.toString("base64") : Buffer.from(req.payload).toString("base64");
|
|
@@ -654,6 +875,120 @@ var VerdifaxClient = class {
|
|
|
654
875
|
);
|
|
655
876
|
this.signingKey = void 0;
|
|
656
877
|
}
|
|
878
|
+
// ── Policies (auth) ──────────────────────────────────────────
|
|
879
|
+
/** Stores a policy document. Nothing changes until it is activated with
|
|
880
|
+
* {@link activatePolicy}. The server hashes the exact text, so this
|
|
881
|
+
* sends the string it is given unchanged and refuses anything that is
|
|
882
|
+
* not a string: serializing an object here would change the hash.
|
|
883
|
+
* Verdifax does not write, review or approve a customer's policy. */
|
|
884
|
+
async uploadPolicy(document) {
|
|
885
|
+
this.requireAPIKey();
|
|
886
|
+
if (typeof document !== "string") {
|
|
887
|
+
throw new VerdifaxError(
|
|
888
|
+
"verdifax: document must be the policy's exact text, as a string"
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
return this.request("POST", POLICIES_PATH, { document });
|
|
892
|
+
}
|
|
893
|
+
/** This key's stored documents, with the chain its changes belong to,
|
|
894
|
+
* the newest change in it (64 zeros when there is none yet) and the
|
|
895
|
+
* document in force (empty under the built-in policy). */
|
|
896
|
+
async listPolicies() {
|
|
897
|
+
this.requireAPIKey();
|
|
898
|
+
return this.request("GET", POLICIES_PATH);
|
|
899
|
+
}
|
|
900
|
+
/** One stored document, with its exact text as `document`. */
|
|
901
|
+
async getPolicy(documentHash) {
|
|
902
|
+
this.requireAPIKey();
|
|
903
|
+
validateHex64(documentHash, "documentHash");
|
|
904
|
+
return this.request(
|
|
905
|
+
"GET",
|
|
906
|
+
`${POLICIES_PATH}/${documentHash}`
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
/** Shows which rule would fire on sample inputs, for a document given as
|
|
910
|
+
* text or one already stored, named by its hash: exactly one of the two.
|
|
911
|
+
* It seals nothing. */
|
|
912
|
+
async testPolicy(req) {
|
|
913
|
+
this.requireAPIKey();
|
|
914
|
+
const { inputs, document, documentHash } = req ?? {};
|
|
915
|
+
if (document === void 0 === (documentHash === void 0)) {
|
|
916
|
+
throw new VerdifaxError(
|
|
917
|
+
"verdifax: testPolicy needs exactly one of document or documentHash"
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
if (typeof inputs !== "object" || inputs === null || Array.isArray(inputs)) {
|
|
921
|
+
throw new VerdifaxError("verdifax: testPolicy needs inputs as an object");
|
|
922
|
+
}
|
|
923
|
+
const body = { inputs };
|
|
924
|
+
if (document !== void 0) {
|
|
925
|
+
if (typeof document !== "string") {
|
|
926
|
+
throw new VerdifaxError(
|
|
927
|
+
"verdifax: document must be the policy's exact text, as a string"
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
body.document = document;
|
|
931
|
+
} else {
|
|
932
|
+
validateHex64(documentHash, "documentHash");
|
|
933
|
+
body.document_hash = documentHash;
|
|
934
|
+
}
|
|
935
|
+
return this.request(
|
|
936
|
+
"POST",
|
|
937
|
+
`${POLICIES_PATH}/test`,
|
|
938
|
+
body
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
/** This key's policy changes, oldest first, as the list the server sends.
|
|
942
|
+
* Save it as JSON, for example with
|
|
943
|
+
* `writeFileSync("policy-history.json", JSON.stringify(history))`, and
|
|
944
|
+
* check it with `verdifax-verify --policy-history policy-history.json`. */
|
|
945
|
+
async getPolicyHistory() {
|
|
946
|
+
this.requireAPIKey();
|
|
947
|
+
return this.request("GET", `${POLICIES_PATH}/history`);
|
|
948
|
+
}
|
|
949
|
+
/** Makes a stored document the policy in force and returns the change
|
|
950
|
+
* record. It first reads GET /policies for the chain and its newest
|
|
951
|
+
* change, then names that change as the one this follows. With the
|
|
952
|
+
* `signingKey` option the request key signs the change, which the server
|
|
953
|
+
* requires while a request key is registered.
|
|
954
|
+
*
|
|
955
|
+
* Rolling back is activating an earlier document: it is one more change
|
|
956
|
+
* in the chain, so the change it undoes stays on the record.
|
|
957
|
+
*
|
|
958
|
+
* An HTTP 409 means the chain moved because another change came first,
|
|
959
|
+
* or the document is not yet effective. It throws the usual
|
|
960
|
+
* {@link APIError} and is not retried: read the policies again and
|
|
961
|
+
* decide. */
|
|
962
|
+
async activatePolicy(documentHash) {
|
|
963
|
+
this.requireAPIKey();
|
|
964
|
+
validateHex64(documentHash, "documentHash");
|
|
965
|
+
const chain = await this.listPolicies();
|
|
966
|
+
const previous = chain?.newest_activation_hash;
|
|
967
|
+
if (typeof previous !== "string") {
|
|
968
|
+
throw new VerdifaxError(
|
|
969
|
+
"verdifax: the server did not report newest_activation_hash, so the change cannot name the one it follows"
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
const body = { previous_activation_hash: previous };
|
|
973
|
+
if (this.signingKey) {
|
|
974
|
+
if (typeof chain.chain_id !== "string" || chain.chain_id === "") {
|
|
975
|
+
throw new VerdifaxError(
|
|
976
|
+
"verdifax: the server did not report chain_id, so the change cannot be signed"
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
body.signature = policyActivationApproval(this.signingKey, {
|
|
980
|
+
chain_id: chain.chain_id,
|
|
981
|
+
document_hash: documentHash,
|
|
982
|
+
previous_activation_hash: previous
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
const out = await this.request(
|
|
986
|
+
"POST",
|
|
987
|
+
`${POLICIES_PATH}/${documentHash}/activate`,
|
|
988
|
+
body
|
|
989
|
+
);
|
|
990
|
+
return out.activation;
|
|
991
|
+
}
|
|
657
992
|
// ── Runs ──────────────────────────────────────────────────────
|
|
658
993
|
async getRun(runId) {
|
|
659
994
|
this.requireAPIKey();
|
|
@@ -918,11 +1253,13 @@ var VerdifaxClient = class {
|
|
|
918
1253
|
* `ai_output_text`; that is off by default, so the text leaves your
|
|
919
1254
|
* machine for review only when you ask. */
|
|
920
1255
|
async attestClaudeResponse(args) {
|
|
1256
|
+
refuseUnknownFields(args, PROVIDER_ARGS, "attestClaudeResponse arguments");
|
|
921
1257
|
return this.attestProviderResponse({ ...args, provider: "anthropic" });
|
|
922
1258
|
}
|
|
923
1259
|
/** Convenience for attesting an OpenAI API response. `govern` works as
|
|
924
1260
|
* in {@link attestClaudeResponse}. */
|
|
925
1261
|
async attestOpenAIResponse(args) {
|
|
1262
|
+
refuseUnknownFields(args, PROVIDER_ARGS, "attestOpenAIResponse arguments");
|
|
926
1263
|
return this.attestProviderResponse({ ...args, provider: "openai" });
|
|
927
1264
|
}
|
|
928
1265
|
async attestProviderResponse(args) {
|
|
@@ -945,7 +1282,9 @@ var VerdifaxClient = class {
|
|
|
945
1282
|
actor_role: "ai_assistant",
|
|
946
1283
|
model_provider: args.provider
|
|
947
1284
|
},
|
|
948
|
-
job: args.job
|
|
1285
|
+
job: args.job,
|
|
1286
|
+
policy_context: args.policy_context,
|
|
1287
|
+
expected_policy_hash: args.expected_policy_hash
|
|
949
1288
|
});
|
|
950
1289
|
}
|
|
951
1290
|
// ── HTTP plumbing, retry-aware ──────────────────────────────
|
|
@@ -1253,6 +1592,7 @@ export {
|
|
|
1253
1592
|
CRES_PREIMAGE_VERSION,
|
|
1254
1593
|
DCAE_PREIMAGE_VERSION,
|
|
1255
1594
|
DSE_FORMULA_VERSION,
|
|
1595
|
+
EXECUTE_FIELDS,
|
|
1256
1596
|
MAX_BATCH_ROWS,
|
|
1257
1597
|
NotFoundError,
|
|
1258
1598
|
RUN_STATUSES,
|
|
@@ -1266,6 +1606,8 @@ export {
|
|
|
1266
1606
|
formatProviderPayload,
|
|
1267
1607
|
fromEnv,
|
|
1268
1608
|
generateRequestKeyPair,
|
|
1609
|
+
policyActivationApproval,
|
|
1610
|
+
policyActivationPreimage,
|
|
1269
1611
|
requestKeyChangeApproval,
|
|
1270
1612
|
requestKeyChangePreimage,
|
|
1271
1613
|
requestKeyRegistrationBody,
|