@nomosprotocol/sdk 2.3.0 → 2.3.1
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 +3 -3
- package/dist/index.cjs +142 -4
- package/dist/index.d.cts +83 -3
- package/dist/index.d.ts +83 -3
- package/dist/index.js +138 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ Use an authenticated client for artifacts belonging to your organization:
|
|
|
172
172
|
```ts
|
|
173
173
|
import { Nomos } from "@nomosprotocol/sdk";
|
|
174
174
|
|
|
175
|
-
const client = new Nomos("
|
|
175
|
+
const client = new Nomos("nmk_...");
|
|
176
176
|
|
|
177
177
|
const verdict = await client.can({
|
|
178
178
|
artifact_id: "loan_approval_v1",
|
|
@@ -263,7 +263,7 @@ itself needs.
|
|
|
263
263
|
|
|
264
264
|
A `.nomos` file is a sealed, machine-executable representation of institutional authority.
|
|
265
265
|
|
|
266
|
-
It can contain the rules, constraints, permissions, obligations, provenance, issuer information, version, and other
|
|
266
|
+
It can contain the rules, constraints, permissions, obligations, provenance, issuer information, version, and other authority metadata required to evaluate an action.
|
|
267
267
|
|
|
268
268
|
The artifact is cryptographically sealed.
|
|
269
269
|
|
|
@@ -470,7 +470,7 @@ The authenticated `Nomos` client exposes the broader platform:
|
|
|
470
470
|
```ts
|
|
471
471
|
import { Nomos } from "@nomosprotocol/sdk";
|
|
472
472
|
|
|
473
|
-
const client = new Nomos("
|
|
473
|
+
const client = new Nomos("nmk_...");
|
|
474
474
|
|
|
475
475
|
// Authorization
|
|
476
476
|
await client.can({
|
package/dist/index.cjs
CHANGED
|
@@ -41,12 +41,14 @@ __export(index_exports, {
|
|
|
41
41
|
WELL_KNOWN: () => WELL_KNOWN,
|
|
42
42
|
accept: () => accept,
|
|
43
43
|
act: () => act,
|
|
44
|
+
applyToolBindings: () => applyToolBindings,
|
|
44
45
|
approver: () => approver,
|
|
45
46
|
bindingDigest: () => bindingDigest,
|
|
46
47
|
buildAct: () => buildAct,
|
|
47
48
|
can: () => can,
|
|
48
49
|
certificateRegistry: () => certificateRegistry,
|
|
49
50
|
certify: () => certify,
|
|
51
|
+
describeSuggestion: () => describeSuggestion,
|
|
50
52
|
fetchDocument: () => fetchDocument,
|
|
51
53
|
generateSigner: () => generateSigner,
|
|
52
54
|
issuer: () => issuer,
|
|
@@ -57,6 +59,7 @@ __export(index_exports, {
|
|
|
57
59
|
memoryStore: () => memoryStore,
|
|
58
60
|
nomos: () => nomos,
|
|
59
61
|
originOf: () => originOf,
|
|
62
|
+
parseToolBindings: () => parseToolBindings,
|
|
60
63
|
passkeyApprover: () => passkeyApprover,
|
|
61
64
|
postgresPasskeyStore: () => postgresPasskeyStore,
|
|
62
65
|
postgresRegistryStore: () => postgresRegistryStore,
|
|
@@ -75,6 +78,7 @@ __export(index_exports, {
|
|
|
75
78
|
signRevocationList: () => signRevocationList,
|
|
76
79
|
signTestimony: () => signTestimony,
|
|
77
80
|
statusFor: () => statusFor,
|
|
81
|
+
suggestToolBindings: () => suggestToolBindings,
|
|
78
82
|
verifyAct: () => verifyAct,
|
|
79
83
|
verifyArtifact: () => verifyArtifact,
|
|
80
84
|
verifyChain: () => verifyChain,
|
|
@@ -187,7 +191,7 @@ function isRetryable(status) {
|
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
// src/http-client.ts
|
|
190
|
-
var SDK_VERSION = "
|
|
194
|
+
var SDK_VERSION = "2.3.1";
|
|
191
195
|
var HttpClient = class {
|
|
192
196
|
constructor(opts) {
|
|
193
197
|
this.opts = opts;
|
|
@@ -852,7 +856,7 @@ async function canAuthenticated(http, params, opts) {
|
|
|
852
856
|
var nomos = { can };
|
|
853
857
|
|
|
854
858
|
// src/nomos.ts
|
|
855
|
-
var DEFAULT_BASE_URL = "https://
|
|
859
|
+
var DEFAULT_BASE_URL = "https://www.nomosprotocol.com";
|
|
856
860
|
var DEFAULT_MAX_RETRIES = 3;
|
|
857
861
|
var DEFAULT_TIMEOUT = 3e4;
|
|
858
862
|
var Nomos = class {
|
|
@@ -862,7 +866,7 @@ var Nomos = class {
|
|
|
862
866
|
constructor(apiKey, config = {}) {
|
|
863
867
|
if (!apiKey || typeof apiKey !== "string") {
|
|
864
868
|
throw new NomosAuthenticationError(
|
|
865
|
-
'A valid API key is required. Pass it as the first argument: new Nomos("
|
|
869
|
+
'A valid API key is required. Pass it as the first argument: new Nomos("nmk_...")'
|
|
866
870
|
);
|
|
867
871
|
}
|
|
868
872
|
this.mode = apiKey.startsWith("nms_test_") ? "test" : "live";
|
|
@@ -884,7 +888,7 @@ var Nomos = class {
|
|
|
884
888
|
* return shape either way, so calling code never needs to know which rail answered.
|
|
885
889
|
*
|
|
886
890
|
* @example
|
|
887
|
-
* const client = new Nomos('
|
|
891
|
+
* const client = new Nomos('nmk_...');
|
|
888
892
|
* const verdict = await client.can({ artifact_id: 'loan_approval_v1', credit_score: 720 });
|
|
889
893
|
*/
|
|
890
894
|
async can(params, opts) {
|
|
@@ -5473,6 +5477,136 @@ async function verifyChainRemote(params, opts) {
|
|
|
5473
5477
|
throw new NomosAPIError(message, res.status, requestId);
|
|
5474
5478
|
}
|
|
5475
5479
|
|
|
5480
|
+
// src/bindings.ts
|
|
5481
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
5482
|
+
var hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
5483
|
+
var isScalar = (v) => typeof v === "string" || typeof v === "boolean" || typeof v === "number" && Number.isFinite(v);
|
|
5484
|
+
function parseToolBindings(raw) {
|
|
5485
|
+
const no = (why) => {
|
|
5486
|
+
throw new Error(why);
|
|
5487
|
+
};
|
|
5488
|
+
const obj = raw;
|
|
5489
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) no("it is not a JSON object");
|
|
5490
|
+
if (obj.version !== 1) no('"version" must be 1');
|
|
5491
|
+
if (typeof obj.artifact !== "string" || !obj.artifact) no('"artifact" must name the sealed policy');
|
|
5492
|
+
if (typeof obj.reviewed !== "boolean") no('"reviewed" must be true or false');
|
|
5493
|
+
if (!obj.tools || typeof obj.tools !== "object" || Array.isArray(obj.tools)) no('"tools" must be an object');
|
|
5494
|
+
for (const k of Object.keys(obj)) if (!["version", "artifact", "reviewed", "tools", "_review"].includes(k)) no(`unknown setting "${k}"`);
|
|
5495
|
+
const tools = {};
|
|
5496
|
+
for (const tool of Object.keys(obj.tools)) {
|
|
5497
|
+
if (FORBIDDEN_KEYS.has(tool)) no(`"${tool}" isn't a usable tool name`);
|
|
5498
|
+
const entry = obj.tools[tool];
|
|
5499
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) no(`tool "${tool}" must be an object with "facts"`);
|
|
5500
|
+
for (const k of Object.keys(entry)) if (!["facts", "_why"].includes(k)) no(`tool "${tool}": unknown setting "${k}"`);
|
|
5501
|
+
const facts = entry.facts;
|
|
5502
|
+
if (!facts || typeof facts !== "object" || Array.isArray(facts)) no(`tool "${tool}" needs "facts"`);
|
|
5503
|
+
tools[tool] = { facts: {} };
|
|
5504
|
+
for (const fact of Object.keys(facts)) {
|
|
5505
|
+
const where = `${tool}.${fact}`;
|
|
5506
|
+
if (fact === "tool") no(`${where}: "tool" is set by the guard to the tool's name`);
|
|
5507
|
+
if (FORBIDDEN_KEYS.has(fact)) no(`${where} isn't a usable fact name`);
|
|
5508
|
+
const b = facts[fact];
|
|
5509
|
+
if (!b || typeof b !== "object" || Array.isArray(b)) no(`${where} must be an object with "arg"`);
|
|
5510
|
+
for (const k of Object.keys(b)) if (!["arg", "scale", "map", "_why"].includes(k)) no(`${where}: "${k}" isn't allowed. A fact can only come from an argument of the call`);
|
|
5511
|
+
if (typeof b.arg !== "string" || !b.arg) no(`${where} needs "arg", the argument it comes from`);
|
|
5512
|
+
if (b.arg.split(".").some((s) => !s || FORBIDDEN_KEYS.has(s))) no(`${where}: "${b.arg}" isn't a usable argument path`);
|
|
5513
|
+
if (b.scale !== void 0 && b.map !== void 0) no(`${where}: use "scale" or "map", not both`);
|
|
5514
|
+
if (b.scale !== void 0 && (typeof b.scale !== "number" || !Number.isFinite(b.scale) || b.scale === 0)) no(`${where}: "scale" must be a non-zero number`);
|
|
5515
|
+
if (b.map !== void 0) {
|
|
5516
|
+
if (!b.map || typeof b.map !== "object" || Array.isArray(b.map)) no(`${where}: "map" must be an object`);
|
|
5517
|
+
for (const k of Object.keys(b.map)) if (FORBIDDEN_KEYS.has(k) || !isScalar(b.map[k])) no(`${where}: "map" can only hold plain values`);
|
|
5518
|
+
}
|
|
5519
|
+
tools[tool].facts[fact] = { arg: b.arg, ...b.scale !== void 0 ? { scale: b.scale } : {}, ...b.map !== void 0 ? { map: b.map } : {} };
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
return { version: 1, artifact: obj.artifact, reviewed: obj.reviewed, tools };
|
|
5523
|
+
}
|
|
5524
|
+
function applyToolBindings(bindings, tool, args) {
|
|
5525
|
+
const inputs = { tool };
|
|
5526
|
+
const unresolved = [];
|
|
5527
|
+
const facts = hasOwn(bindings.tools, tool) ? bindings.tools[tool].facts : {};
|
|
5528
|
+
for (const [fact, b] of Object.entries(facts)) {
|
|
5529
|
+
let v = args;
|
|
5530
|
+
for (const seg of b.arg.split(".")) v = v && typeof v === "object" && !Array.isArray(v) && hasOwn(v, seg) ? v[seg] : void 0;
|
|
5531
|
+
if (v === void 0 || v === null) {
|
|
5532
|
+
unresolved.push({ fact, arg: b.arg, why: "missing" });
|
|
5533
|
+
continue;
|
|
5534
|
+
}
|
|
5535
|
+
if (b.scale !== void 0) {
|
|
5536
|
+
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
5537
|
+
unresolved.push({ fact, arg: b.arg, why: "not_a_number" });
|
|
5538
|
+
continue;
|
|
5539
|
+
}
|
|
5540
|
+
inputs[fact] = Number((v * b.scale).toFixed(9));
|
|
5541
|
+
continue;
|
|
5542
|
+
}
|
|
5543
|
+
if (!isScalar(v)) {
|
|
5544
|
+
unresolved.push({ fact, arg: b.arg, why: "not_a_value" });
|
|
5545
|
+
continue;
|
|
5546
|
+
}
|
|
5547
|
+
if (b.map) {
|
|
5548
|
+
if (!hasOwn(b.map, String(v))) {
|
|
5549
|
+
unresolved.push({ fact, arg: b.arg, why: "not_in_map" });
|
|
5550
|
+
continue;
|
|
5551
|
+
}
|
|
5552
|
+
inputs[fact] = b.map[String(v)];
|
|
5553
|
+
continue;
|
|
5554
|
+
}
|
|
5555
|
+
inputs[fact] = v;
|
|
5556
|
+
}
|
|
5557
|
+
return { inputs, unresolved };
|
|
5558
|
+
}
|
|
5559
|
+
var WEAK = /* @__PURE__ */ new Set(["is", "has", "the", "a", "an", "of", "to", "in", "id", "name", "type", "value", "status", "code", "data", "info", "flag", "number", "num", "total", "ai", "system"]);
|
|
5560
|
+
var UNITS = { cents: 0.01, pence: 0.01, fils: 0.01, usd: null, dollars: null, aed: null, eur: null };
|
|
5561
|
+
var words = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).map((w) => w.toLowerCase()).filter(Boolean);
|
|
5562
|
+
var flatProperties = (props, prefix = "") => Object.entries(props ?? {}).flatMap(([k, s]) => FORBIDDEN_KEYS.has(k) ? [] : s?.type === "object" && s.properties ? flatProperties(s.properties, `${prefix}${k}.`) : [{ path: `${prefix}${k}`, schema: s }]);
|
|
5563
|
+
function suggestToolBindings(artifact, tools, fields) {
|
|
5564
|
+
const bindings = { version: 1, artifact, reviewed: false, tools: {} };
|
|
5565
|
+
const proposals = [];
|
|
5566
|
+
const unbound = [];
|
|
5567
|
+
for (const tool of tools) {
|
|
5568
|
+
bindings.tools[tool.name] = { facts: {} };
|
|
5569
|
+
const args = flatProperties(tool.inputSchema?.properties);
|
|
5570
|
+
for (const field of fields) {
|
|
5571
|
+
if (field.name === "tool") continue;
|
|
5572
|
+
const want = words(field.name).filter((w) => !WEAK.has(w) && !(w in UNITS));
|
|
5573
|
+
const scored = args.map((a) => {
|
|
5574
|
+
const raw = words(a.path);
|
|
5575
|
+
const have = raw.filter((w) => !WEAK.has(w) && !(w in UNITS));
|
|
5576
|
+
const shared = want.filter((w) => have.includes(w));
|
|
5577
|
+
const unit = raw.find((w) => w in UNITS);
|
|
5578
|
+
const same = words(field.name).join("_") === raw.join("_");
|
|
5579
|
+
const sameCore = want.length > 0 && want.length === have.length && shared.length === want.length;
|
|
5580
|
+
return { a, shared, unit, same, sameCore, score: same ? 3 : sameCore ? 2 : shared.length ? shared.length / Math.max(want.length, have.length) : 0 };
|
|
5581
|
+
}).filter((s) => s.score > 0).sort((x, y) => y.score - x.score);
|
|
5582
|
+
if (!scored.length) {
|
|
5583
|
+
unbound.push({ tool: tool.name, fact: field.name, candidates: [] });
|
|
5584
|
+
continue;
|
|
5585
|
+
}
|
|
5586
|
+
const best = scored[0];
|
|
5587
|
+
if (scored[1] && scored[1].score === best.score) {
|
|
5588
|
+
unbound.push({ tool: tool.name, fact: field.name, candidates: scored.filter((s) => s.score === best.score).map((s) => s.a.path) });
|
|
5589
|
+
continue;
|
|
5590
|
+
}
|
|
5591
|
+
const scale = best.unit ? UNITS[best.unit] ?? void 0 : void 0;
|
|
5592
|
+
const why = best.same ? "the same name" : best.sameCore ? "the same words" : `shares "${best.shared.join(", ")}"`;
|
|
5593
|
+
proposals.push({ tool: tool.name, fact: field.name, arg: best.a.path, confidence: best.same || best.sameCore ? "exact" : "likely", why: scale ? `${why}; ${best.unit} scaled by ${scale}` : why, ...scale ? { scale } : {} });
|
|
5594
|
+
bindings.tools[tool.name].facts[field.name] = { arg: best.a.path, ...scale ? { scale } : {} };
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
return { bindings, proposals, unbound };
|
|
5598
|
+
}
|
|
5599
|
+
function describeSuggestion(s) {
|
|
5600
|
+
const lines = [];
|
|
5601
|
+
for (const tool of Object.keys(s.bindings.tools)) {
|
|
5602
|
+
lines.push(`
|
|
5603
|
+
${tool}`);
|
|
5604
|
+
for (const p of s.proposals.filter((x) => x.tool === tool)) lines.push(` ${p.confidence === "exact" ? "matched " : "CHECK "} ${p.fact} <- ${p.arg}${p.scale ? ` x ${p.scale}` : ""} (${p.why})`);
|
|
5605
|
+
for (const u of s.unbound.filter((x) => x.tool === tool)) lines.push(` NO MATCH ${u.fact}${u.candidates.length ? ` (could be: ${u.candidates.join(", ")})` : ""}`);
|
|
5606
|
+
}
|
|
5607
|
+
return lines.join("\n");
|
|
5608
|
+
}
|
|
5609
|
+
|
|
5476
5610
|
// src/act/bytes.ts
|
|
5477
5611
|
var utf82 = (s) => new TextEncoder().encode(s);
|
|
5478
5612
|
var fromUtf82 = (b) => new TextDecoder().decode(b);
|
|
@@ -7034,12 +7168,14 @@ ${buttons}</body>`;
|
|
|
7034
7168
|
WELL_KNOWN,
|
|
7035
7169
|
accept,
|
|
7036
7170
|
act,
|
|
7171
|
+
applyToolBindings,
|
|
7037
7172
|
approver,
|
|
7038
7173
|
bindingDigest,
|
|
7039
7174
|
buildAct,
|
|
7040
7175
|
can,
|
|
7041
7176
|
certificateRegistry,
|
|
7042
7177
|
certify,
|
|
7178
|
+
describeSuggestion,
|
|
7043
7179
|
fetchDocument,
|
|
7044
7180
|
generateSigner,
|
|
7045
7181
|
issuer,
|
|
@@ -7050,6 +7186,7 @@ ${buttons}</body>`;
|
|
|
7050
7186
|
memoryStore,
|
|
7051
7187
|
nomos,
|
|
7052
7188
|
originOf,
|
|
7189
|
+
parseToolBindings,
|
|
7053
7190
|
passkeyApprover,
|
|
7054
7191
|
postgresPasskeyStore,
|
|
7055
7192
|
postgresRegistryStore,
|
|
@@ -7068,6 +7205,7 @@ ${buttons}</body>`;
|
|
|
7068
7205
|
signRevocationList,
|
|
7069
7206
|
signTestimony,
|
|
7070
7207
|
statusFor,
|
|
7208
|
+
suggestToolBindings,
|
|
7071
7209
|
verifyAct,
|
|
7072
7210
|
verifyArtifact,
|
|
7073
7211
|
verifyChain,
|
package/dist/index.d.cts
CHANGED
|
@@ -395,7 +395,7 @@ interface GenerateGovernanceResult {
|
|
|
395
395
|
download_url?: string;
|
|
396
396
|
}
|
|
397
397
|
interface NomosConfig {
|
|
398
|
-
/** Base URL override — defaults to https://
|
|
398
|
+
/** Base URL override — defaults to https://www.nomosprotocol.com */
|
|
399
399
|
baseUrl?: string;
|
|
400
400
|
/** Max retries on 429 / 5xx (default: 3) */
|
|
401
401
|
maxRetries?: number;
|
|
@@ -776,7 +776,7 @@ declare class Nomos {
|
|
|
776
776
|
* return shape either way, so calling code never needs to know which rail answered.
|
|
777
777
|
*
|
|
778
778
|
* @example
|
|
779
|
-
* const client = new Nomos('
|
|
779
|
+
* const client = new Nomos('nmk_...');
|
|
780
780
|
* const verdict = await client.can({ artifact_id: 'loan_approval_v1', credit_score: 720 });
|
|
781
781
|
*/
|
|
782
782
|
can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
|
|
@@ -807,6 +807,86 @@ declare const nomos: {
|
|
|
807
807
|
*/
|
|
808
808
|
declare function verifyChain(params: VerifyChainParams, opts?: VerifyChainOptions): Promise<VerifyChainResult>;
|
|
809
809
|
|
|
810
|
+
/**
|
|
811
|
+
* Tool bindings: which of a tool's arguments is which fact a sealed policy reads. A policy reads
|
|
812
|
+
* `loan_amount`; a tool sends `amount_cents`. Without a binding the policy has nothing to decide on,
|
|
813
|
+
* and a fail-closed check holds every call.
|
|
814
|
+
*
|
|
815
|
+
* A binding decides what a policy sees, so it is built to be safe:
|
|
816
|
+
* - It is an explicit, reviewed file. Suggestions are proposals, marked unreviewed.
|
|
817
|
+
* - A fact only ever comes from an argument of the call. There are no constants, so nobody can
|
|
818
|
+
* hard-code a fact to quiet a policy.
|
|
819
|
+
* - With a binding, only bound facts reach the policy, so an agent can't state a fact the tool
|
|
820
|
+
* wouldn't act on by adding an argument of that name.
|
|
821
|
+
* - A missing argument or an unmapped value leaves the fact undecided. It is never guessed.
|
|
822
|
+
*
|
|
823
|
+
* Pure: no I/O, no model. Specified in NOMOS-SPEC-011 (Draft), nomos-spec/spec; see also
|
|
824
|
+
* docs/design-notes/tool-fact-binding.md.
|
|
825
|
+
*/
|
|
826
|
+
interface FactBinding {
|
|
827
|
+
/** The argument this fact comes from. Dots reach into objects: `customer.kyc`. */
|
|
828
|
+
arg: string;
|
|
829
|
+
/** Multiply a number: `amount_cents` into dollars is 0.01. */
|
|
830
|
+
scale?: number;
|
|
831
|
+
/** Translate a value: { "ok": "verified" }. A value not listed leaves the fact undecided. */
|
|
832
|
+
map?: Record<string, string | number | boolean>;
|
|
833
|
+
}
|
|
834
|
+
interface ToolBindings {
|
|
835
|
+
version: 1;
|
|
836
|
+
/** The sealed policy these facts belong to. */
|
|
837
|
+
artifact: string;
|
|
838
|
+
/** False until a person has checked it. The guard won't enforce an unreviewed file. */
|
|
839
|
+
reviewed: boolean;
|
|
840
|
+
tools: Record<string, {
|
|
841
|
+
facts: Record<string, FactBinding>;
|
|
842
|
+
}>;
|
|
843
|
+
}
|
|
844
|
+
/** Reads a bindings file, refusing anything it doesn't fully understand. Throws with a plain reason. */
|
|
845
|
+
declare function parseToolBindings(raw: unknown): ToolBindings;
|
|
846
|
+
interface UnresolvedFact {
|
|
847
|
+
fact: string;
|
|
848
|
+
arg: string;
|
|
849
|
+
why: 'missing' | 'not_a_number' | 'not_a_value' | 'not_in_map';
|
|
850
|
+
}
|
|
851
|
+
/** The facts a policy gets for this call, and which it couldn't get. */
|
|
852
|
+
declare function applyToolBindings(bindings: ToolBindings, tool: string, args: Record<string, unknown>): {
|
|
853
|
+
inputs: Record<string, string | number | boolean>;
|
|
854
|
+
unresolved: UnresolvedFact[];
|
|
855
|
+
};
|
|
856
|
+
interface ToolDescription {
|
|
857
|
+
name: string;
|
|
858
|
+
inputSchema?: {
|
|
859
|
+
properties?: Record<string, any>;
|
|
860
|
+
} | null;
|
|
861
|
+
}
|
|
862
|
+
interface PolicyField {
|
|
863
|
+
name: string;
|
|
864
|
+
type?: string;
|
|
865
|
+
description?: string | null;
|
|
866
|
+
values?: string[];
|
|
867
|
+
}
|
|
868
|
+
interface BindingProposal {
|
|
869
|
+
tool: string;
|
|
870
|
+
fact: string;
|
|
871
|
+
arg: string;
|
|
872
|
+
confidence: 'exact' | 'likely';
|
|
873
|
+
why: string;
|
|
874
|
+
scale?: number;
|
|
875
|
+
}
|
|
876
|
+
interface BindingSuggestion {
|
|
877
|
+
bindings: ToolBindings;
|
|
878
|
+
proposals: BindingProposal[];
|
|
879
|
+
unbound: Array<{
|
|
880
|
+
tool: string;
|
|
881
|
+
fact: string;
|
|
882
|
+
candidates: string[];
|
|
883
|
+
}>;
|
|
884
|
+
}
|
|
885
|
+
/** Proposes bindings from names alone, for a person to check. Never guesses a value translation. */
|
|
886
|
+
declare function suggestToolBindings(artifact: string, tools: ToolDescription[], fields: PolicyField[]): BindingSuggestion;
|
|
887
|
+
/** A plain report of a suggestion, for the person who has to check it. */
|
|
888
|
+
declare function describeSuggestion(s: BindingSuggestion): string;
|
|
889
|
+
|
|
810
890
|
type SignatureSuite = 'Ed25519' | 'ES256';
|
|
811
891
|
interface Signer {
|
|
812
892
|
/** NOMOS-SPEC-001 §8.4 key id of the public key. */
|
|
@@ -1542,4 +1622,4 @@ declare function approver(o: ApproverOptions): {
|
|
|
1542
1622
|
pending: Map<string, Pending>;
|
|
1543
1623
|
};
|
|
1544
1624
|
|
|
1545
|
-
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type BuildActOptions, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, approver, buildAct, can, certificateRegistry, certify, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, verifyChain, verifyEnrolmentToken, witness };
|
|
1625
|
+
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
|
package/dist/index.d.ts
CHANGED
|
@@ -395,7 +395,7 @@ interface GenerateGovernanceResult {
|
|
|
395
395
|
download_url?: string;
|
|
396
396
|
}
|
|
397
397
|
interface NomosConfig {
|
|
398
|
-
/** Base URL override — defaults to https://
|
|
398
|
+
/** Base URL override — defaults to https://www.nomosprotocol.com */
|
|
399
399
|
baseUrl?: string;
|
|
400
400
|
/** Max retries on 429 / 5xx (default: 3) */
|
|
401
401
|
maxRetries?: number;
|
|
@@ -776,7 +776,7 @@ declare class Nomos {
|
|
|
776
776
|
* return shape either way, so calling code never needs to know which rail answered.
|
|
777
777
|
*
|
|
778
778
|
* @example
|
|
779
|
-
* const client = new Nomos('
|
|
779
|
+
* const client = new Nomos('nmk_...');
|
|
780
780
|
* const verdict = await client.can({ artifact_id: 'loan_approval_v1', credit_score: 720 });
|
|
781
781
|
*/
|
|
782
782
|
can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
|
|
@@ -807,6 +807,86 @@ declare const nomos: {
|
|
|
807
807
|
*/
|
|
808
808
|
declare function verifyChain(params: VerifyChainParams, opts?: VerifyChainOptions): Promise<VerifyChainResult>;
|
|
809
809
|
|
|
810
|
+
/**
|
|
811
|
+
* Tool bindings: which of a tool's arguments is which fact a sealed policy reads. A policy reads
|
|
812
|
+
* `loan_amount`; a tool sends `amount_cents`. Without a binding the policy has nothing to decide on,
|
|
813
|
+
* and a fail-closed check holds every call.
|
|
814
|
+
*
|
|
815
|
+
* A binding decides what a policy sees, so it is built to be safe:
|
|
816
|
+
* - It is an explicit, reviewed file. Suggestions are proposals, marked unreviewed.
|
|
817
|
+
* - A fact only ever comes from an argument of the call. There are no constants, so nobody can
|
|
818
|
+
* hard-code a fact to quiet a policy.
|
|
819
|
+
* - With a binding, only bound facts reach the policy, so an agent can't state a fact the tool
|
|
820
|
+
* wouldn't act on by adding an argument of that name.
|
|
821
|
+
* - A missing argument or an unmapped value leaves the fact undecided. It is never guessed.
|
|
822
|
+
*
|
|
823
|
+
* Pure: no I/O, no model. Specified in NOMOS-SPEC-011 (Draft), nomos-spec/spec; see also
|
|
824
|
+
* docs/design-notes/tool-fact-binding.md.
|
|
825
|
+
*/
|
|
826
|
+
interface FactBinding {
|
|
827
|
+
/** The argument this fact comes from. Dots reach into objects: `customer.kyc`. */
|
|
828
|
+
arg: string;
|
|
829
|
+
/** Multiply a number: `amount_cents` into dollars is 0.01. */
|
|
830
|
+
scale?: number;
|
|
831
|
+
/** Translate a value: { "ok": "verified" }. A value not listed leaves the fact undecided. */
|
|
832
|
+
map?: Record<string, string | number | boolean>;
|
|
833
|
+
}
|
|
834
|
+
interface ToolBindings {
|
|
835
|
+
version: 1;
|
|
836
|
+
/** The sealed policy these facts belong to. */
|
|
837
|
+
artifact: string;
|
|
838
|
+
/** False until a person has checked it. The guard won't enforce an unreviewed file. */
|
|
839
|
+
reviewed: boolean;
|
|
840
|
+
tools: Record<string, {
|
|
841
|
+
facts: Record<string, FactBinding>;
|
|
842
|
+
}>;
|
|
843
|
+
}
|
|
844
|
+
/** Reads a bindings file, refusing anything it doesn't fully understand. Throws with a plain reason. */
|
|
845
|
+
declare function parseToolBindings(raw: unknown): ToolBindings;
|
|
846
|
+
interface UnresolvedFact {
|
|
847
|
+
fact: string;
|
|
848
|
+
arg: string;
|
|
849
|
+
why: 'missing' | 'not_a_number' | 'not_a_value' | 'not_in_map';
|
|
850
|
+
}
|
|
851
|
+
/** The facts a policy gets for this call, and which it couldn't get. */
|
|
852
|
+
declare function applyToolBindings(bindings: ToolBindings, tool: string, args: Record<string, unknown>): {
|
|
853
|
+
inputs: Record<string, string | number | boolean>;
|
|
854
|
+
unresolved: UnresolvedFact[];
|
|
855
|
+
};
|
|
856
|
+
interface ToolDescription {
|
|
857
|
+
name: string;
|
|
858
|
+
inputSchema?: {
|
|
859
|
+
properties?: Record<string, any>;
|
|
860
|
+
} | null;
|
|
861
|
+
}
|
|
862
|
+
interface PolicyField {
|
|
863
|
+
name: string;
|
|
864
|
+
type?: string;
|
|
865
|
+
description?: string | null;
|
|
866
|
+
values?: string[];
|
|
867
|
+
}
|
|
868
|
+
interface BindingProposal {
|
|
869
|
+
tool: string;
|
|
870
|
+
fact: string;
|
|
871
|
+
arg: string;
|
|
872
|
+
confidence: 'exact' | 'likely';
|
|
873
|
+
why: string;
|
|
874
|
+
scale?: number;
|
|
875
|
+
}
|
|
876
|
+
interface BindingSuggestion {
|
|
877
|
+
bindings: ToolBindings;
|
|
878
|
+
proposals: BindingProposal[];
|
|
879
|
+
unbound: Array<{
|
|
880
|
+
tool: string;
|
|
881
|
+
fact: string;
|
|
882
|
+
candidates: string[];
|
|
883
|
+
}>;
|
|
884
|
+
}
|
|
885
|
+
/** Proposes bindings from names alone, for a person to check. Never guesses a value translation. */
|
|
886
|
+
declare function suggestToolBindings(artifact: string, tools: ToolDescription[], fields: PolicyField[]): BindingSuggestion;
|
|
887
|
+
/** A plain report of a suggestion, for the person who has to check it. */
|
|
888
|
+
declare function describeSuggestion(s: BindingSuggestion): string;
|
|
889
|
+
|
|
810
890
|
type SignatureSuite = 'Ed25519' | 'ES256';
|
|
811
891
|
interface Signer {
|
|
812
892
|
/** NOMOS-SPEC-001 §8.4 key id of the public key. */
|
|
@@ -1542,4 +1622,4 @@ declare function approver(o: ApproverOptions): {
|
|
|
1542
1622
|
pending: Map<string, Pending>;
|
|
1543
1623
|
};
|
|
1544
1624
|
|
|
1545
|
-
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type BuildActOptions, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, approver, buildAct, can, certificateRegistry, certify, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, verifyChain, verifyEnrolmentToken, witness };
|
|
1625
|
+
export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
|
package/dist/index.js
CHANGED
|
@@ -107,7 +107,7 @@ function isRetryable(status) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/http-client.ts
|
|
110
|
-
var SDK_VERSION = "
|
|
110
|
+
var SDK_VERSION = "2.3.1";
|
|
111
111
|
var HttpClient = class {
|
|
112
112
|
constructor(opts) {
|
|
113
113
|
this.opts = opts;
|
|
@@ -772,7 +772,7 @@ async function canAuthenticated(http, params, opts) {
|
|
|
772
772
|
var nomos = { can };
|
|
773
773
|
|
|
774
774
|
// src/nomos.ts
|
|
775
|
-
var DEFAULT_BASE_URL = "https://
|
|
775
|
+
var DEFAULT_BASE_URL = "https://www.nomosprotocol.com";
|
|
776
776
|
var DEFAULT_MAX_RETRIES = 3;
|
|
777
777
|
var DEFAULT_TIMEOUT = 3e4;
|
|
778
778
|
var Nomos = class {
|
|
@@ -782,7 +782,7 @@ var Nomos = class {
|
|
|
782
782
|
constructor(apiKey, config = {}) {
|
|
783
783
|
if (!apiKey || typeof apiKey !== "string") {
|
|
784
784
|
throw new NomosAuthenticationError(
|
|
785
|
-
'A valid API key is required. Pass it as the first argument: new Nomos("
|
|
785
|
+
'A valid API key is required. Pass it as the first argument: new Nomos("nmk_...")'
|
|
786
786
|
);
|
|
787
787
|
}
|
|
788
788
|
this.mode = apiKey.startsWith("nms_test_") ? "test" : "live";
|
|
@@ -804,7 +804,7 @@ var Nomos = class {
|
|
|
804
804
|
* return shape either way, so calling code never needs to know which rail answered.
|
|
805
805
|
*
|
|
806
806
|
* @example
|
|
807
|
-
* const client = new Nomos('
|
|
807
|
+
* const client = new Nomos('nmk_...');
|
|
808
808
|
* const verdict = await client.can({ artifact_id: 'loan_approval_v1', credit_score: 720 });
|
|
809
809
|
*/
|
|
810
810
|
async can(params, opts) {
|
|
@@ -5393,6 +5393,136 @@ async function verifyChainRemote(params, opts) {
|
|
|
5393
5393
|
throw new NomosAPIError(message, res.status, requestId);
|
|
5394
5394
|
}
|
|
5395
5395
|
|
|
5396
|
+
// src/bindings.ts
|
|
5397
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
5398
|
+
var hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
5399
|
+
var isScalar = (v) => typeof v === "string" || typeof v === "boolean" || typeof v === "number" && Number.isFinite(v);
|
|
5400
|
+
function parseToolBindings(raw) {
|
|
5401
|
+
const no = (why) => {
|
|
5402
|
+
throw new Error(why);
|
|
5403
|
+
};
|
|
5404
|
+
const obj = raw;
|
|
5405
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) no("it is not a JSON object");
|
|
5406
|
+
if (obj.version !== 1) no('"version" must be 1');
|
|
5407
|
+
if (typeof obj.artifact !== "string" || !obj.artifact) no('"artifact" must name the sealed policy');
|
|
5408
|
+
if (typeof obj.reviewed !== "boolean") no('"reviewed" must be true or false');
|
|
5409
|
+
if (!obj.tools || typeof obj.tools !== "object" || Array.isArray(obj.tools)) no('"tools" must be an object');
|
|
5410
|
+
for (const k of Object.keys(obj)) if (!["version", "artifact", "reviewed", "tools", "_review"].includes(k)) no(`unknown setting "${k}"`);
|
|
5411
|
+
const tools = {};
|
|
5412
|
+
for (const tool of Object.keys(obj.tools)) {
|
|
5413
|
+
if (FORBIDDEN_KEYS.has(tool)) no(`"${tool}" isn't a usable tool name`);
|
|
5414
|
+
const entry = obj.tools[tool];
|
|
5415
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) no(`tool "${tool}" must be an object with "facts"`);
|
|
5416
|
+
for (const k of Object.keys(entry)) if (!["facts", "_why"].includes(k)) no(`tool "${tool}": unknown setting "${k}"`);
|
|
5417
|
+
const facts = entry.facts;
|
|
5418
|
+
if (!facts || typeof facts !== "object" || Array.isArray(facts)) no(`tool "${tool}" needs "facts"`);
|
|
5419
|
+
tools[tool] = { facts: {} };
|
|
5420
|
+
for (const fact of Object.keys(facts)) {
|
|
5421
|
+
const where = `${tool}.${fact}`;
|
|
5422
|
+
if (fact === "tool") no(`${where}: "tool" is set by the guard to the tool's name`);
|
|
5423
|
+
if (FORBIDDEN_KEYS.has(fact)) no(`${where} isn't a usable fact name`);
|
|
5424
|
+
const b = facts[fact];
|
|
5425
|
+
if (!b || typeof b !== "object" || Array.isArray(b)) no(`${where} must be an object with "arg"`);
|
|
5426
|
+
for (const k of Object.keys(b)) if (!["arg", "scale", "map", "_why"].includes(k)) no(`${where}: "${k}" isn't allowed. A fact can only come from an argument of the call`);
|
|
5427
|
+
if (typeof b.arg !== "string" || !b.arg) no(`${where} needs "arg", the argument it comes from`);
|
|
5428
|
+
if (b.arg.split(".").some((s) => !s || FORBIDDEN_KEYS.has(s))) no(`${where}: "${b.arg}" isn't a usable argument path`);
|
|
5429
|
+
if (b.scale !== void 0 && b.map !== void 0) no(`${where}: use "scale" or "map", not both`);
|
|
5430
|
+
if (b.scale !== void 0 && (typeof b.scale !== "number" || !Number.isFinite(b.scale) || b.scale === 0)) no(`${where}: "scale" must be a non-zero number`);
|
|
5431
|
+
if (b.map !== void 0) {
|
|
5432
|
+
if (!b.map || typeof b.map !== "object" || Array.isArray(b.map)) no(`${where}: "map" must be an object`);
|
|
5433
|
+
for (const k of Object.keys(b.map)) if (FORBIDDEN_KEYS.has(k) || !isScalar(b.map[k])) no(`${where}: "map" can only hold plain values`);
|
|
5434
|
+
}
|
|
5435
|
+
tools[tool].facts[fact] = { arg: b.arg, ...b.scale !== void 0 ? { scale: b.scale } : {}, ...b.map !== void 0 ? { map: b.map } : {} };
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
return { version: 1, artifact: obj.artifact, reviewed: obj.reviewed, tools };
|
|
5439
|
+
}
|
|
5440
|
+
function applyToolBindings(bindings, tool, args) {
|
|
5441
|
+
const inputs = { tool };
|
|
5442
|
+
const unresolved = [];
|
|
5443
|
+
const facts = hasOwn(bindings.tools, tool) ? bindings.tools[tool].facts : {};
|
|
5444
|
+
for (const [fact, b] of Object.entries(facts)) {
|
|
5445
|
+
let v = args;
|
|
5446
|
+
for (const seg of b.arg.split(".")) v = v && typeof v === "object" && !Array.isArray(v) && hasOwn(v, seg) ? v[seg] : void 0;
|
|
5447
|
+
if (v === void 0 || v === null) {
|
|
5448
|
+
unresolved.push({ fact, arg: b.arg, why: "missing" });
|
|
5449
|
+
continue;
|
|
5450
|
+
}
|
|
5451
|
+
if (b.scale !== void 0) {
|
|
5452
|
+
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
5453
|
+
unresolved.push({ fact, arg: b.arg, why: "not_a_number" });
|
|
5454
|
+
continue;
|
|
5455
|
+
}
|
|
5456
|
+
inputs[fact] = Number((v * b.scale).toFixed(9));
|
|
5457
|
+
continue;
|
|
5458
|
+
}
|
|
5459
|
+
if (!isScalar(v)) {
|
|
5460
|
+
unresolved.push({ fact, arg: b.arg, why: "not_a_value" });
|
|
5461
|
+
continue;
|
|
5462
|
+
}
|
|
5463
|
+
if (b.map) {
|
|
5464
|
+
if (!hasOwn(b.map, String(v))) {
|
|
5465
|
+
unresolved.push({ fact, arg: b.arg, why: "not_in_map" });
|
|
5466
|
+
continue;
|
|
5467
|
+
}
|
|
5468
|
+
inputs[fact] = b.map[String(v)];
|
|
5469
|
+
continue;
|
|
5470
|
+
}
|
|
5471
|
+
inputs[fact] = v;
|
|
5472
|
+
}
|
|
5473
|
+
return { inputs, unresolved };
|
|
5474
|
+
}
|
|
5475
|
+
var WEAK = /* @__PURE__ */ new Set(["is", "has", "the", "a", "an", "of", "to", "in", "id", "name", "type", "value", "status", "code", "data", "info", "flag", "number", "num", "total", "ai", "system"]);
|
|
5476
|
+
var UNITS = { cents: 0.01, pence: 0.01, fils: 0.01, usd: null, dollars: null, aed: null, eur: null };
|
|
5477
|
+
var words = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).map((w) => w.toLowerCase()).filter(Boolean);
|
|
5478
|
+
var flatProperties = (props, prefix = "") => Object.entries(props ?? {}).flatMap(([k, s]) => FORBIDDEN_KEYS.has(k) ? [] : s?.type === "object" && s.properties ? flatProperties(s.properties, `${prefix}${k}.`) : [{ path: `${prefix}${k}`, schema: s }]);
|
|
5479
|
+
function suggestToolBindings(artifact, tools, fields) {
|
|
5480
|
+
const bindings = { version: 1, artifact, reviewed: false, tools: {} };
|
|
5481
|
+
const proposals = [];
|
|
5482
|
+
const unbound = [];
|
|
5483
|
+
for (const tool of tools) {
|
|
5484
|
+
bindings.tools[tool.name] = { facts: {} };
|
|
5485
|
+
const args = flatProperties(tool.inputSchema?.properties);
|
|
5486
|
+
for (const field of fields) {
|
|
5487
|
+
if (field.name === "tool") continue;
|
|
5488
|
+
const want = words(field.name).filter((w) => !WEAK.has(w) && !(w in UNITS));
|
|
5489
|
+
const scored = args.map((a) => {
|
|
5490
|
+
const raw = words(a.path);
|
|
5491
|
+
const have = raw.filter((w) => !WEAK.has(w) && !(w in UNITS));
|
|
5492
|
+
const shared = want.filter((w) => have.includes(w));
|
|
5493
|
+
const unit = raw.find((w) => w in UNITS);
|
|
5494
|
+
const same = words(field.name).join("_") === raw.join("_");
|
|
5495
|
+
const sameCore = want.length > 0 && want.length === have.length && shared.length === want.length;
|
|
5496
|
+
return { a, shared, unit, same, sameCore, score: same ? 3 : sameCore ? 2 : shared.length ? shared.length / Math.max(want.length, have.length) : 0 };
|
|
5497
|
+
}).filter((s) => s.score > 0).sort((x, y) => y.score - x.score);
|
|
5498
|
+
if (!scored.length) {
|
|
5499
|
+
unbound.push({ tool: tool.name, fact: field.name, candidates: [] });
|
|
5500
|
+
continue;
|
|
5501
|
+
}
|
|
5502
|
+
const best = scored[0];
|
|
5503
|
+
if (scored[1] && scored[1].score === best.score) {
|
|
5504
|
+
unbound.push({ tool: tool.name, fact: field.name, candidates: scored.filter((s) => s.score === best.score).map((s) => s.a.path) });
|
|
5505
|
+
continue;
|
|
5506
|
+
}
|
|
5507
|
+
const scale = best.unit ? UNITS[best.unit] ?? void 0 : void 0;
|
|
5508
|
+
const why = best.same ? "the same name" : best.sameCore ? "the same words" : `shares "${best.shared.join(", ")}"`;
|
|
5509
|
+
proposals.push({ tool: tool.name, fact: field.name, arg: best.a.path, confidence: best.same || best.sameCore ? "exact" : "likely", why: scale ? `${why}; ${best.unit} scaled by ${scale}` : why, ...scale ? { scale } : {} });
|
|
5510
|
+
bindings.tools[tool.name].facts[field.name] = { arg: best.a.path, ...scale ? { scale } : {} };
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
return { bindings, proposals, unbound };
|
|
5514
|
+
}
|
|
5515
|
+
function describeSuggestion(s) {
|
|
5516
|
+
const lines = [];
|
|
5517
|
+
for (const tool of Object.keys(s.bindings.tools)) {
|
|
5518
|
+
lines.push(`
|
|
5519
|
+
${tool}`);
|
|
5520
|
+
for (const p of s.proposals.filter((x) => x.tool === tool)) lines.push(` ${p.confidence === "exact" ? "matched " : "CHECK "} ${p.fact} <- ${p.arg}${p.scale ? ` x ${p.scale}` : ""} (${p.why})`);
|
|
5521
|
+
for (const u of s.unbound.filter((x) => x.tool === tool)) lines.push(` NO MATCH ${u.fact}${u.candidates.length ? ` (could be: ${u.candidates.join(", ")})` : ""}`);
|
|
5522
|
+
}
|
|
5523
|
+
return lines.join("\n");
|
|
5524
|
+
}
|
|
5525
|
+
|
|
5396
5526
|
// src/act/bytes.ts
|
|
5397
5527
|
var utf82 = (s) => new TextEncoder().encode(s);
|
|
5398
5528
|
var fromUtf82 = (b) => new TextDecoder().decode(b);
|
|
@@ -6953,12 +7083,14 @@ export {
|
|
|
6953
7083
|
WELL_KNOWN,
|
|
6954
7084
|
accept,
|
|
6955
7085
|
act,
|
|
7086
|
+
applyToolBindings,
|
|
6956
7087
|
approver,
|
|
6957
7088
|
bindingDigest,
|
|
6958
7089
|
buildAct,
|
|
6959
7090
|
can,
|
|
6960
7091
|
certificateRegistry,
|
|
6961
7092
|
certify,
|
|
7093
|
+
describeSuggestion,
|
|
6962
7094
|
fetchDocument,
|
|
6963
7095
|
generateSigner,
|
|
6964
7096
|
issuer,
|
|
@@ -6969,6 +7101,7 @@ export {
|
|
|
6969
7101
|
memoryStore,
|
|
6970
7102
|
nomos,
|
|
6971
7103
|
originOf,
|
|
7104
|
+
parseToolBindings,
|
|
6972
7105
|
passkeyApprover,
|
|
6973
7106
|
postgresPasskeyStore,
|
|
6974
7107
|
postgresRegistryStore,
|
|
@@ -6987,6 +7120,7 @@ export {
|
|
|
6987
7120
|
signRevocationList,
|
|
6988
7121
|
signTestimony,
|
|
6989
7122
|
statusFor,
|
|
7123
|
+
suggestToolBindings,
|
|
6990
7124
|
verifyAct,
|
|
6991
7125
|
verifyArtifact,
|
|
6992
7126
|
verifyChain,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomosprotocol/sdk",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "NOMOS for TypeScript: accept() for the system that makes a change, act() for the agent that asks, the issuer, witness and approver roles, and the Exchange client. Verification is local (@nomosprotocol/verify).",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|