@tenuo/core 0.3.1-beta.0 → 0.3.2-beta.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/LICENSE +76 -61
- package/dist/api.d.ts +10 -3
- package/dist/api.js.map +1 -1
- package/dist/build-info.json +3 -3
- package/dist/generated/tenuo_wasm.d.ts +27 -1
- package/dist/generated/tenuo_wasm.js +93 -1
- package/dist/generated/tenuo_wasm_bg.wasm +0 -0
- package/dist/generated/tenuo_wasm_bg.wasm.d.ts +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +33 -65
- package/dist/mcp.js.map +1 -1
- package/dist/wasm.d.ts +19 -0
- package/dist/wasm.js +12 -0
- package/dist/wasm.js.map +1 -1
- package/package.json +12 -5
package/LICENSE
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -32,9 +33,10 @@
|
|
|
32
33
|
not limited to compiled object code, generated documentation,
|
|
33
34
|
and conversions to other media types.
|
|
34
35
|
|
|
35
|
-
"Work" shall mean the work of authorship
|
|
36
|
-
the License, as indicated by a
|
|
37
|
-
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
38
40
|
|
|
39
41
|
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
42
|
form, that is based on (or derived from) the Work and for which the
|
|
@@ -44,24 +46,23 @@
|
|
|
44
46
|
separable from, or merely link (or bind by name) to the interfaces of,
|
|
45
47
|
the Work and Derivative Works thereof.
|
|
46
48
|
|
|
47
|
-
"Contribution" shall mean
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
within the Work.
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
65
66
|
|
|
66
67
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
68
|
this License, each Contributor hereby grants to You a perpetual,
|
|
@@ -77,22 +78,22 @@
|
|
|
77
78
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
79
|
where such license applies only to those patent claims licensable
|
|
79
80
|
by such Contributor that are necessarily infringed by their
|
|
80
|
-
Contribution(s) alone or by
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
82
|
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
-
institute patent litigation against any entity (including a
|
|
83
|
-
or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
-
Contribution
|
|
85
|
-
patent infringement, then any patent licenses
|
|
86
|
-
this License for that Work shall terminate
|
|
87
|
-
litigation is filed.
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
88
89
|
|
|
89
90
|
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
91
|
Work or Derivative Works thereof in any medium, with or without
|
|
91
92
|
modifications, and in Source or Object form, provided that You
|
|
92
93
|
meet the following conditions:
|
|
93
94
|
|
|
94
|
-
(a) You must give any other recipients of the Work or
|
|
95
|
-
Works a copy of this License; and
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
96
97
|
|
|
97
98
|
(b) You must cause any modified files to carry prominent notices
|
|
98
99
|
stating that You changed the files; and
|
|
@@ -104,25 +105,28 @@
|
|
|
104
105
|
the Derivative Works; and
|
|
105
106
|
|
|
106
107
|
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
-
distribution,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
the
|
|
112
|
-
Derivative Works;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
126
130
|
|
|
127
131
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
128
132
|
any Contribution intentionally submitted for inclusion in the Work
|
|
@@ -144,7 +148,7 @@
|
|
|
144
148
|
implied, including, without limitation, any warranties or conditions
|
|
145
149
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
146
150
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
147
|
-
appropriateness of using or
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
148
152
|
risks associated with Your exercise of permissions under this License.
|
|
149
153
|
|
|
150
154
|
8. Limitation of Liability. In no event and under no legal theory,
|
|
@@ -152,27 +156,38 @@
|
|
|
152
156
|
unless required by applicable law (such as deliberate and grossly
|
|
153
157
|
negligent acts) or agreed to in writing, shall any Contributor be
|
|
154
158
|
liable to You for damages, including any direct, indirect, special,
|
|
155
|
-
incidental, or
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
156
160
|
result of this License or out of the use or inability to use the
|
|
157
161
|
Work (including but not limited to damages for loss of goodwill,
|
|
158
|
-
work stoppage, computer failure or malfunction, or all
|
|
159
|
-
commercial damages or losses), even if such Contributor
|
|
160
|
-
advised of the possibility of such damages.
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
161
165
|
|
|
162
166
|
9. Accepting Warranty or Additional Liability. While redistributing
|
|
163
|
-
the Work or Derivative Works thereof, You may offer,
|
|
164
|
-
fee for, acceptance of support, warranty, indemnity,
|
|
165
|
-
liability obligations and/or rights consistent with this
|
|
166
|
-
However, in accepting such obligations, You may
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
169
173
|
defend, and hold each Contributor harmless for any liability
|
|
170
174
|
incurred by, or claims asserted against, such Contributor by reason
|
|
171
175
|
of your accepting any such warranty or additional liability.
|
|
172
176
|
|
|
173
177
|
END OF TERMS AND CONDITIONS
|
|
174
178
|
|
|
175
|
-
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
176
191
|
|
|
177
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
178
193
|
you may not use this file except in compliance with the License.
|
package/dist/api.d.ts
CHANGED
|
@@ -567,7 +567,12 @@ export interface Tenuo {
|
|
|
567
567
|
attestApprovalRequest(session: Session, tool: string, args: Readonly<Record<string, unknown>>): ApprovalContextAttestation;
|
|
568
568
|
/** Authorize locally, then produce warrant + proof-of-possession for another boundary to verify. */
|
|
569
569
|
present(session: Session, tool: string, args: Readonly<Record<string, unknown>>, options?: McpAttachOptions): PresentedCall;
|
|
570
|
-
/**
|
|
570
|
+
/**
|
|
571
|
+
* Verify a presented call at any boundary. Resolves with the authorized
|
|
572
|
+
* arguments; the tool must not run otherwise. `args` is the object already
|
|
573
|
+
* given to this function. A repeated key in the original JSON is not
|
|
574
|
+
* visible. Parse that text with `parseStrictJson` while it is still available.
|
|
575
|
+
*/
|
|
571
576
|
verify(presented: PresentedCall, tool: string, args: Readonly<Record<string, unknown>>, options?: McpVerifyOptions): Promise<Readonly<Record<string, unknown>>>;
|
|
572
577
|
/** Sign a revocation list with this context's issuer key. Load it with `revoke()` where this issuer is trusted. */
|
|
573
578
|
revocationList(input: RevocationListInput): string;
|
|
@@ -653,8 +658,10 @@ export interface TenuoMcp {
|
|
|
653
658
|
/** Local authorize, then put warrant + PoP on `_meta.tenuo`. */
|
|
654
659
|
attach(session: Session, name: string, args: Readonly<Record<string, unknown>>, options?: McpAttachOptions): McpCallParams;
|
|
655
660
|
/**
|
|
656
|
-
* Server path. Verifies a presented warrant + PoP
|
|
657
|
-
*
|
|
661
|
+
* Server path. Verifies a presented warrant + PoP against the object it was
|
|
662
|
+
* given. A repeated key in the original JSON is not visible: the host has
|
|
663
|
+
* already parsed `args`. Parse that text with `parseStrictJson` while it is
|
|
664
|
+
* still available. The tool handler must not run unless this returns.
|
|
658
665
|
*/
|
|
659
666
|
verify(name: string, args: Readonly<Record<string, unknown>>, meta: unknown, options?: McpVerifyOptions): Promise<Readonly<Record<string, unknown>>>;
|
|
660
667
|
/** Wrap a handler: verify from `extra._meta` / `extra.meta`, then execute. */
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG","sourcesContent":["/**\n * Frozen public contract for `@tenuo/core`.\n *\n * Names and shapes here are the review surface — do not grow this file with\n * protocol vocabulary (`Warrant`, `mint`, `guard`) as the lead API.\n */\n\n/** Canonical kebab-case codes from Rust `ErrorCode::name()` / `Denial::code()`. */\nexport type ProtocolDenialCode =\n | \"signature-invalid\"\n | \"untrusted-root\"\n | \"chain-broken\"\n | \"depth-exceeded\"\n | \"warrant-expired\"\n | \"tool-not-authorized\"\n | \"constraint-violation\"\n | \"pop-signature-invalid\"\n | \"approval-required\"\n | \"insufficient-approvals\"\n | \"warrant-revoked\";\n\n/** Legacy SCREAMING_SNAKE aliases accepted by existing TypeScript callers. */\nexport type LegacyTenuoErrorCode =\n | \"TENUO_NOT_READY\"\n | \"TENUO_CONFIGURATION\"\n | \"TENUO_CONSTRAINT_VIOLATION\"\n | \"TENUO_TOOL_NOT_AUTHORIZED\"\n | \"TENUO_WARRANT_EXPIRED\"\n | \"TENUO_UNTRUSTED_ROOT\"\n | \"TENUO_INVALID_POP\"\n | \"TENUO_SIGNATURE_INVALID\"\n | \"TENUO_CHAIN_INVALID\"\n | \"TENUO_DEPTH_EXCEEDED\"\n | \"TENUO_REVOKED\"\n | \"TENUO_APPROVAL_REQUIRED\"\n | \"TENUO_INSUFFICIENT_APPROVALS\"\n | \"TENUO_CANONICALIZATION\"\n | \"TENUO_NOT_IMPLEMENTED\";\n\nexport type TenuoErrorCode = ProtocolDenialCode | LegacyTenuoErrorCode;\n\n// ---------------------------------------------------------------------------\n// Constraints. Every kind is evaluated in the Rust core; TypeScript only\n// builds the marker object. The set matches the core `Constraint` enum.\n// ---------------------------------------------------------------------------\n\n/** Path-traversal-safe prefix. Core evaluates this; TypeScript does not. */\nexport type UnderConstraint = {\n readonly kind: \"under\";\n readonly root: string;\n /** Default true. */\n readonly caseSensitive?: boolean;\n /** Whether the root itself is allowed. Default true. */\n readonly allowEqual?: boolean;\n};\n\nexport type EmailConstraint = {\n readonly kind: \"email\";\n readonly domain: string;\n};\n\nexport type MaxConstraint = {\n readonly kind: \"max\";\n readonly value: number;\n};\n\nexport type MinConstraint = {\n readonly kind: \"min\";\n readonly value: number;\n};\n\nexport type RangeConstraint = {\n readonly kind: \"range\";\n readonly min?: number;\n readonly max?: number;\n readonly minExclusive?: boolean;\n readonly maxExclusive?: boolean;\n};\n\nexport type OneOfConstraint = {\n readonly kind: \"oneOf\";\n readonly values: readonly string[];\n};\n\nexport type NotOneOfConstraint = {\n readonly kind: \"notOneOf\";\n readonly values: readonly string[];\n};\n\nexport type PatternConstraint = {\n readonly kind: \"pattern\";\n readonly pattern: string;\n};\n\nexport type RegexConstraint = {\n readonly kind: \"regex\";\n readonly source: string;\n};\n\nexport type ExactConstraint = {\n readonly kind: \"exact\";\n readonly value: string | number | boolean;\n};\n\nexport type WildcardConstraint = {\n readonly kind: \"wildcard\";\n};\n\nexport type CidrConstraint = {\n readonly kind: \"cidr\";\n readonly network: string;\n};\n\nexport type UrlPatternConstraint = {\n readonly kind: \"urlPattern\";\n readonly pattern: string;\n};\n\nexport type UrlSafeConstraint = {\n readonly kind: \"urlSafe\";\n readonly schemes?: readonly string[];\n readonly allowDomains?: readonly string[];\n readonly denyDomains?: readonly string[];\n};\n\nexport type ShlexConstraint = {\n readonly kind: \"shlex\";\n readonly allow: readonly string[];\n};\n\nexport type ContainsConstraint = {\n readonly kind: \"contains\";\n readonly values: readonly (string | number | boolean)[];\n};\n\nexport type SubsetConstraint = {\n readonly kind: \"subset\";\n readonly values: readonly (string | number | boolean)[];\n};\n\nexport type AnyOfConstraint = {\n readonly kind: \"anyOf\";\n readonly constraints: readonly ConstraintExpr[];\n};\n\nexport type AllConstraint = {\n readonly kind: \"all\";\n readonly constraints: readonly ConstraintExpr[];\n};\n\nexport type NotConstraint = {\n readonly kind: \"not\";\n readonly constraint: ConstraintExpr;\n};\n\nexport type CelConstraint = {\n readonly kind: \"cel\";\n readonly expression: string;\n};\n\nexport type ConstraintExpr =\n | UnderConstraint\n | EmailConstraint\n | MaxConstraint\n | MinConstraint\n | RangeConstraint\n | OneOfConstraint\n | NotOneOfConstraint\n | PatternConstraint\n | RegexConstraint\n | ExactConstraint\n | WildcardConstraint\n | CidrConstraint\n | UrlPatternConstraint\n | UrlSafeConstraint\n | ShlexConstraint\n | ContainsConstraint\n | SubsetConstraint\n | AnyOfConstraint\n | AllConstraint\n | NotConstraint\n | CelConstraint;\n\n/** Per-argument allow policy. Not a Zod schema. */\nexport type AllowPolicy = {\n readonly [field: string]: ConstraintExpr;\n};\n\n/**\n * Framework tool shape we wrap. No dependency on `ai` / Mastra.\n * `parameters` is host-schema (usually Zod) and is never treated as authority.\n */\nexport type ToolLike<\n TArgs extends Record<string, unknown> = Record<string, unknown>,\n TResult = unknown,\n> = {\n readonly description?: string;\n readonly parameters?: unknown;\n execute: (args: TArgs, options?: unknown) => TResult | Promise<TResult>;\n};\n\ntype KeysOfUnion<T> = T extends unknown ? keyof T : never;\n\nexport type ToolPolicy<TArgs = undefined> = {\n /**\n * Host ceiling. AND'd with the session in Rust.\n * Non-empty maps are zero-trust: every call argument must be named here.\n * `{}` means no extra ceiling (session/warrant only).\n */\n readonly allow: [NonNullable<TArgs>] extends [object]\n ? [KeysOfUnion<NonNullable<TArgs>>] extends [never]\n ? AllowPolicy\n : string extends KeysOfUnion<NonNullable<TArgs>>\n ? AllowPolicy\n : number extends KeysOfUnion<NonNullable<TArgs>>\n ? AllowPolicy\n : symbol extends KeysOfUnion<NonNullable<TArgs>>\n ? AllowPolicy\n : { readonly [Field in KeysOfUnion<NonNullable<TArgs>>]?: ConstraintExpr }\n : AllowPolicy;\n readonly capability?: string;\n};\n\n/** Compile-time brand only — never assigned at runtime. Do not `in`/`has` this key. */\ndeclare const protectedBrand: unique symbol;\n\nexport type ExecuteOptions = {\n readonly session?: Session;\n /** Already-signed approval envelopes (hex, standard base64, or CBOR bytes). */\n readonly approvals?: readonly (string | Uint8Array)[];\n /**\n * Signed evidence of the decision. Not a security hook.\n * Exceptions (sync or rejected Promise) are isolated and never deny or\n * fail the tool.\n */\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n /**\n * Correlation handle written to the receipt. Use whatever id the host\n * already has — a JSON-RPC id, a trace or span id — so a receipt can be\n * matched against your own logs. Not an identity: nothing enforces\n * uniqueness, and distinguishing repeated calls is the receipt chain's job.\n */\n readonly requestId?: string;\n};\n\ntype ExtraExecuteOptions<T extends { execute: (args: never) => unknown }> =\n T[\"execute\"] extends (args: never, options?: infer O) => unknown\n ? unknown extends O\n ? object\n : Omit<NonNullable<O>, keyof ExecuteOptions>\n : object;\n\n/** Wrapped tool. `execute` still authorizes; optional `{ session }` overrides ALS. */\nexport type ProtectedTool<T extends { execute: (args: never) => unknown }> = Omit<T, \"execute\"> & {\n execute: (\n args: Parameters<T[\"execute\"]>[0],\n options?: ExecuteOptions & ExtraExecuteOptions<T>,\n ) => Promise<Awaited<ReturnType<T[\"execute\"]>>>;\n readonly [protectedBrand]: true;\n};\n\n// ---------------------------------------------------------------------------\n// Approvals\n// ---------------------------------------------------------------------------\n\n/** Per-argument trigger for an approval gate. */\nexport type ArgApprovalGate =\n /** Every value of this argument fires the gate. */\n | \"all\"\n /** Only values satisfying the constraint fire the gate. */\n | { readonly when: ConstraintExpr }\n /** Every value except those satisfying the constraint fires the gate. Narrowable across delegation. */\n | { readonly exempt: ConstraintExpr };\n\n/** Gate on one tool: a message for the approver and optional per-argument triggers. */\nexport type ToolApprovalGate = {\n readonly message?: string;\n /** Absent: the whole tool is gated. */\n readonly args?: { readonly [field: string]: ArgApprovalGate };\n};\n\n/** Approval requirement. Host collects SignedApproval envelopes; Rust decides. */\nexport type RequireApproval = {\n readonly approvers: readonly PublicKeyHandle[];\n readonly min: number;\n /** Whole-tool gates. Defaults to every tool in the session when `gates` is absent too. */\n readonly tools?: readonly string[];\n /** Per-tool gates with messages and per-argument triggers. */\n readonly gates?: { readonly [tool: string]: ToolApprovalGate };\n};\n\n/** What an approval service needs to present a call to a human. Not a signed artifact. */\nexport type ApprovalRequest = {\n readonly requestId: string;\n readonly warrantId: string;\n readonly tool: string;\n readonly args: Readonly<Record<string, unknown>>;\n /** Hex SHA-256 committing to warrant id, tool, canonical args, and holder. */\n readonly requestHash: string;\n readonly holderPublicKey: string;\n readonly requiredApprovers: readonly string[];\n readonly minApprovals: number;\n /** Unix seconds. An approval cannot outlive the warrant. */\n readonly warrantExpiresAt: number;\n readonly createdAt: number;\n readonly message: string;\n};\n\n/** Holder-signed statement that it is the one asking. Carried in a control-plane request. */\nexport type ApprovalContextAttestation = {\n readonly version: number;\n readonly canonicalization: string;\n readonly warrantId: string;\n readonly tool: string;\n readonly requestHash: string;\n readonly holderKeyHex: string;\n readonly argsCanonicalCborB64: string;\n readonly signerKeyHex: string;\n readonly signatureB64: string;\n};\n\n/** Wire body for a control-plane approval API. Field names are the wire names. */\nexport type ControlPlaneApprovalRequestV1 = {\n readonly schema_version: 1;\n readonly request_id_hex: string;\n readonly warrant_id: string;\n readonly tool: string;\n readonly arguments: Readonly<Record<string, unknown>>;\n readonly request_hash_hex: string;\n readonly holder_public_key_hex: string;\n readonly required_approver_keys_hex: readonly string[];\n readonly min_approvals: number;\n readonly warrant_expires_at_unix: number;\n readonly created_at_unix: number;\n readonly attestation?: Readonly<Record<string, unknown>>;\n readonly temporal?: Readonly<Record<string, string>>;\n readonly message?: string;\n};\n\nexport type ControlPlaneApprovalResponseV1 = {\n readonly status: string;\n readonly signed_approvals_b64?: readonly string[];\n readonly error?: string;\n readonly server_request_id?: string;\n};\n\nexport type SignApprovalOptions = {\n /** Who approved, in the approver system's own terms. Required. */\n readonly externalId: string;\n /** Approval lifetime. Default one hour, always clamped to the warrant's expiry when given. */\n readonly ttlSeconds?: number;\n readonly warrantExpiresAt?: number;\n};\n\n/** Decoded approval envelope. Not authorization. */\nexport type ApprovalInfo = {\n readonly approverPublicKey: string;\n readonly requestHash: string;\n readonly externalId: string;\n readonly approvedAt: number;\n readonly expiresAt: number;\n readonly expired: boolean;\n readonly signatureValid: boolean;\n readonly error?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Sessions\n// ---------------------------------------------------------------------------\n\nexport type SessionAllow = {\n readonly [capability: string]: AllowPolicy;\n};\n\n/** Named level or a number 0-255. Delegation can only lower it. */\nexport type Clearance =\n | number\n | \"untrusted\"\n | \"external\"\n | \"partner\"\n | \"internal\"\n | \"privileged\"\n | \"system\";\n\nexport type SessionInput = {\n /** Capability map. Optional when `tools` is set. Issuer sessions may omit it. */\n readonly allow?: SessionAllow;\n /** Wrapped tools from `tenuo.tool()`. Their `allow` is minted into the session. */\n readonly tools?: readonly object[];\n readonly ttlSeconds?: number;\n readonly requireApproval?: RequireApproval;\n /**\n * Issue to another agent's key instead of a fresh local holder. The\n * returned session cannot authorize here (it has no holder secret); send\n * `toWire()` to that agent, which imports it with `sessionFromWire()`.\n */\n readonly holder?: PublicKeyHandle;\n /**\n * How many times this authority may be delegated below the root.\n * `0` makes the session terminal. Omit for the protocol maximum.\n * Delegation can lower this ceiling and never raise it.\n */\n readonly maxDepth?: number;\n /**\n * `\"execution\"` (default) can call tools. `\"issuer\"` cannot call tools; its\n * holder can mint execution sessions with `tenuo.issue()` for the tools in\n * `issuableTools`, within `constraintBounds`, without the root key.\n */\n readonly kind?: \"execution\" | \"issuer\";\n readonly clearance?: Clearance;\n /** Free-form task correlation id. Inherited unchanged through delegation. */\n readonly sessionId?: string;\n /** Free-form agent label. Delegation may change it. */\n readonly agentId?: string;\n /** Issuer sessions only. */\n readonly issuableTools?: readonly string[];\n /** Issuer sessions only: ceiling on the constraints of anything issued, by field. */\n readonly constraintBounds?: AllowPolicy;\n /** Issuer sessions only: how deep issued sessions may themselves delegate. */\n readonly maxIssueDepth?: number;\n};\n\n/** Options for `tenuo.narrow()`. All optional; the default keeps the holder. */\nexport type NarrowOptions = {\n /**\n * Bind the child to another agent's key. This is delegation: the current\n * holder signs, the child belongs to `holder`, and the returned session\n * cannot authorize here. Hand `toWire()` to that agent.\n */\n readonly holder?: PublicKeyHandle;\n /** Child lifetime. Clamped to what the parent has left. */\n readonly ttlSeconds?: number;\n /** The child's holder cannot delegate further. */\n readonly terminal?: boolean;\n /** Lower the delegation ceiling. Cannot exceed the parent's. */\n readonly maxDepth?: number;\n /** Lower the clearance. Cannot exceed the parent's. */\n readonly clearance?: Clearance;\n readonly agentId?: string;\n /** Approvers can only be added. */\n readonly addApprovers?: readonly PublicKeyHandle[];\n /** The threshold can only go up. */\n readonly minApprovals?: number;\n};\n\n/** Options for `tenuo.issue()`: an execution session minted from an issuer session. */\nexport type IssueInput = {\n readonly allow: SessionAllow;\n /** The distinct agent that will use it. Issuer/holder separation is enforced. */\n readonly holder: PublicKeyHandle;\n readonly ttlSeconds?: number;\n readonly maxDepth?: number;\n readonly clearance?: Clearance;\n readonly sessionId?: string;\n readonly agentId?: string;\n /** Approval policy for the issued execution session. Defaults to gating every issued tool. */\n readonly requireApproval?: RequireApproval;\n};\n\n/** Public view of a session's leaf. Never includes the holder secret. */\nexport type SessionInfo = {\n readonly kind: \"execution\" | \"issuer\";\n /** Hex public key the leaf is bound to. */\n readonly holderPublicKey: string;\n /** Hex public key that signed the root of the chain. */\n readonly rootPublicKey: string;\n /** 0 for a root session, +1 per delegation. */\n readonly depth: number;\n /** Delegation ceiling in force for this leaf. */\n readonly maxDepth: number;\n /** True when `depth >= maxDepth`: cannot be narrowed further. */\n readonly terminal: boolean;\n /** Unix seconds. */\n readonly expiresAt: number;\n readonly tools: readonly string[];\n /** Warrant ids, root first. */\n readonly warrantIds: readonly string[];\n /**\n * True when this process holds the leaf's holder secret. False for a\n * session issued or delegated to another agent: `toWire()` works,\n * `execute` and `narrow` do not.\n */\n readonly canAuthorize: boolean;\n readonly clearance?: number;\n readonly sessionId?: string;\n readonly agentId?: string;\n readonly issuableTools?: readonly string[];\n readonly maxIssueDepth?: number;\n readonly requiredApprovers?: readonly string[];\n readonly minApprovals?: number;\n readonly approvalGatedTools: readonly string[];\n};\n\n/** One constrained argument in an `explain()` result. */\nexport type ExplainedField = {\n readonly field: string;\n /** Core constraint type name, e.g. `\"Subpath\"`, `\"Range\"`. */\n readonly kind: string;\n /** The constraint as core serializes it. */\n readonly constraint: unknown;\n /** Absent when the argument was not supplied. */\n readonly value?: unknown;\n readonly satisfied: boolean;\n /** Stable machine-readable diagnostic, when the core can identify one. */\n readonly reasonCode?: string;\n readonly reason?: string;\n};\n\n/** What a session would decide for `tool(args)`, field by field. No proof-of-possession involved. */\nexport type Explanation = {\n readonly tool: string;\n readonly kind: \"execution\" | \"issuer\";\n readonly outcome: \"allow\" | \"deny\";\n readonly code?: TenuoErrorCode;\n readonly field?: string;\n readonly message?: string;\n readonly toolGranted: boolean;\n readonly fields: readonly ExplainedField[];\n /** Arguments the session does not name. Zero-trust policies reject these. */\n readonly unknownFields: readonly string[];\n /** Constrained arguments the call did not supply. */\n readonly missingFields: readonly string[];\n readonly expired: boolean;\n readonly expiresAt: number;\n readonly chainValid: boolean;\n readonly chainError?: TenuoErrorCode;\n};\n\nexport type DevRoot = {\n readonly kind: \"dev-root\";\n /**\n * Required when `NODE_ENV` is unset or is not `development` / `test`.\n * Unset `NODE_ENV` is not treated as development.\n */\n readonly allowInProduction?: boolean;\n};\n\n/** A stable issuer secret: this process is a control plane. */\nexport type IssuerKey = {\n readonly kind: \"issuer-key\";\n readonly secret: Uint8Array;\n};\n\nexport type PublicKeyHandle = {\n readonly kind: \"public-key\";\n readonly source: \"env\" | \"hex\" | \"bytes\";\n readonly hex: string;\n};\n\nexport type WarrantPart = {\n readonly payload_hex: string;\n readonly signature_hex: string;\n};\n\nexport type SessionFromWireInput = {\n readonly warrant: string | readonly string[] | readonly WarrantPart[];\n readonly holderKey: Uint8Array;\n};\n\n/** Field-level (`{ path: under(\"/data\") }`) or per-capability (`{ read_file: { path: ... } }`). */\nexport type NarrowInput = AllowPolicy | SessionAllow;\n\nexport type CreateTenuoOptions = {\n readonly trustedRoots?: readonly PublicKeyHandle[];\n /**\n * `devRoot()` for development; an `issuerKey*` for a control plane that\n * mints with a stable key; a public key for a verifier. An issuer key\n * trusts itself plus `trustedRoots`.\n */\n readonly root?: DevRoot | IssuerKey | PublicKeyHandle;\n /** Published SignedRevocationList (hex or standard base64). Verified against a trusted root. */\n readonly revocationList?: string | Uint8Array;\n /** 32-byte holder seed used to sign verifier receipts. Runtime supplies this. */\n readonly receiptSigner?: Uint8Array;\n};\n\nexport type ApprovalRequestSummary = {\n readonly tool: string;\n readonly required: number;\n readonly received: number;\n};\n\nexport type Decision =\n | { readonly outcome: \"allow\"; readonly args: Readonly<Record<string, unknown>> }\n | { readonly outcome: \"deny\"; readonly code: TenuoErrorCode; readonly field?: string }\n | { readonly outcome: \"approval_required\"; readonly request: ApprovalRequestSummary };\n\n// ---------------------------------------------------------------------------\n// Revocation and receipts\n// ---------------------------------------------------------------------------\n\nexport type RevocationListInput = {\n readonly revoke: readonly string[];\n /** Monotonic; verifiers refuse an older version. Default 1. */\n readonly version?: number;\n};\n\nexport type RevocationListInfo = {\n readonly version: number;\n readonly issuedAt: number;\n readonly issuerPublicKey: string;\n readonly revokedIds: readonly string[];\n readonly signatureValid: boolean;\n};\n\n/** Signature authenticity of a receipt. Not authorization. */\nexport type ReceiptInfo = {\n readonly authentic: true;\n /** Hex key the receipt is signed under — resolve against your own authorizer set. */\n readonly signerKey: string;\n readonly outcome: \"allow\" | \"deny\";\n readonly action: string;\n readonly decisionCode?: string;\n readonly requestId: string;\n readonly srlVersion?: number;\n readonly srlHash?: string;\n readonly requestHash?: string;\n readonly policyDefinitionHash?: string;\n readonly prevReceiptHash?: string;\n readonly trustedRootsHash?: string;\n};\n\n/** Root-anchored receipt check: the embedded chain against trusted roots at decision time. */\nexport type ReceiptChainInfo = {\n readonly signerKey: string;\n readonly outcome: \"allow\" | \"deny\";\n readonly decisionCode?: string;\n readonly timestamp: number;\n readonly chainValid: boolean;\n readonly chainError?: string;\n readonly corroboratesDenial?: boolean;\n readonly rootIssuer?: string;\n readonly leafHolder?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Presenting authority across any boundary\n// ---------------------------------------------------------------------------\n\n/** Warrant chain plus proof-of-possession for one call. Transport-agnostic. */\nexport type PresentedCall = {\n readonly warrant: string;\n readonly signature: string;\n readonly approvals?: readonly string[];\n};\n\nexport interface Session {\n readonly [Symbol.toStringTag]: \"TenuoSession\";\n /** Warrant tokens, root first. Does not include the holder secret. */\n toWire(): readonly string[];\n /** SHA-256 of `(warrant_id, tool, canonical args)`. App-level idempotency, not PoP. */\n dedupKey(tool: string, args: Readonly<Record<string, unknown>>): string;\n /** Holder public key, depth, ceiling, lifetime, tools. Never the secret. */\n inspect(): SessionInfo;\n /**\n * Receipt helpers below are required for `createTenuo.runtime({ receipts:\n * \"collect\" })`. Implementers that are not runtime sessions can return\n * empty snapshots and `0` from acknowledge.\n */\n /**\n * Receipts collected for this session and not yet drained. Empty when the\n * session was not created by a Runtime with `receipts: \"collect\"`.\n */\n peekReceipts(): readonly string[];\n /**\n * Snapshot of collected receipts. Same as `peekReceipts`; nothing is removed\n * until `acknowledgeReceipts`.\n */\n drainReceipts(): readonly string[];\n /** Remove the first `count` pending receipts. */\n acknowledgeReceipts(count: number): number;\n}\n\nexport interface Tenuo {\n /**\n * Wrap any `{ execute }` tool (Vercel AI SDK, Mastra, plain object).\n * `parameters` / Zod stay on the inner tool and are never treated as authority.\n */\n tool<T extends { execute: (args: never) => unknown }>(\n inner: T,\n policy: ToolPolicy<Parameters<T[\"execute\"]>[0]>,\n ): ProtectedTool<T>;\n session(input: SessionInput): Session;\n /** Import a warrant minted elsewhere (Rust / Python / another process). */\n sessionFromWire(input: SessionFromWireInput): Session;\n withSession<R>(session: Session, fn: () => R): R;\n /**\n * Child session with less authority. Without options the same holder keeps\n * it; with `options.holder` it is delegated to another agent's key. Core\n * rejects any child that is not within its parent before a token exists.\n */\n narrow(session: Session, allow: NarrowInput, options?: NarrowOptions): Session;\n /**\n * Mint an execution session from an issuer session, signed by the issuer\n * session's holder. Core checks tools against `issuableTools`, constraints\n * against `constraintBounds`, clearance, and issue depth. No root key.\n */\n issue(issuer: Session, input: IssueInput): Session;\n /**\n * Public key of the local issuer (dev root or issuer key). Other processes\n * put it in `trustedRoots` to accept warrants this context mints.\n */\n issuerPublicKey(): PublicKeyHandle;\n /** What the session would decide for `tool(args)`, field by field. Never runs the tool. */\n explain(session: Session, tool: string, args: Readonly<Record<string, unknown>>): Explanation;\n /** Everything an approval service needs to present `tool(args)` to a human. */\n approvalRequest(session: Session, tool: string, args: Readonly<Record<string, unknown>>): ApprovalRequest;\n /** Holder-signed proof that this session is the one asking for `tool(args)`. */\n attestApprovalRequest(\n session: Session,\n tool: string,\n args: Readonly<Record<string, unknown>>,\n ): ApprovalContextAttestation;\n /** Authorize locally, then produce warrant + proof-of-possession for another boundary to verify. */\n present(\n session: Session,\n tool: string,\n args: Readonly<Record<string, unknown>>,\n options?: McpAttachOptions,\n ): PresentedCall;\n /** Verify a presented call at any boundary. Resolves with the authorized arguments; the tool must not run otherwise. */\n verify(\n presented: PresentedCall,\n tool: string,\n args: Readonly<Record<string, unknown>>,\n options?: McpVerifyOptions,\n ): Promise<Readonly<Record<string, unknown>>>;\n /** Sign a revocation list with this context's issuer key. Load it with `revoke()` where this issuer is trusted. */\n revocationList(input: RevocationListInput): string;\n /** Load a published SignedRevocationList. Rust verifies the issuer against trusted roots. */\n revoke(list: string | Uint8Array): void;\n /** MCP `_meta.tenuo` attach / verify. No MCP SDK dependency. */\n readonly mcp: TenuoMcp;\n ready(): void;\n}\n\n/** Wire envelope Python and TypeScript both read from `params._meta`. */\nexport type TenuoMcpMeta = {\n readonly tenuo: PresentedCall;\n};\n\nexport type McpAttachOptions = {\n readonly approvals?: readonly (string | Uint8Array)[];\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n /**\n * Correlation handle written to the receipt. Use whatever id the host\n * already has — a JSON-RPC id, a trace or span id — so a receipt can be\n * matched against your own logs. Not an identity: nothing enforces\n * uniqueness, and distinguishing repeated calls is the receipt chain's job.\n */\n readonly requestId?: string;\n};\n\n/** Host ceiling and optional receipt hook on `mcp.handler()`. */\nexport type McpHandlerPolicy = {\n readonly allow?: AllowPolicy;\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n readonly nonceStore?: NonceStore;\n /**\n * Isolated. Called when `nonceStore.checkAndRecord` throws or rejects.\n * The client only ever sees \"Replay store unavailable\".\n */\n readonly onNonceStoreError?: (error: unknown) => void | Promise<void>;\n};\n\nexport type McpVerifyOptions = {\n readonly allow?: AllowPolicy;\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n /**\n * Opt-in exact-PoP replay check. `checkAndRecord` may return a Promise\n * (Redis). A rejected Promise fails closed. In-memory stores do not work\n * across processes. PoP v1 is otherwise replayable in-window, including\n * envelopes that already carry approvals.\n */\n readonly nonceStore?: NonceStore;\n /**\n * Isolated. Called when `nonceStore.checkAndRecord` throws or rejects.\n * The client only ever sees \"Replay store unavailable\".\n */\n readonly onNonceStoreError?: (error: unknown) => void | Promise<void>;\n /**\n * Correlation handle written to the receipt. Use whatever id the host\n * already has — a JSON-RPC id, a trace or span id — so a receipt can be\n * matched against your own logs. Not an identity: nothing enforces\n * uniqueness, and distinguishing repeated calls is the receipt chain's job.\n */\n readonly requestId?: string;\n};\n\n/**\n * Returns true if the PoP is fresh and recorded; false if this exact token\n * was already consumed. Async stores (Redis) return `Promise<boolean>`.\n * `verify()` / handlers await the result. Never return a Promise from a\n * store that is only consulted synchronously — that used to fail open.\n */\nexport type NonceStore = {\n checkAndRecord(popSignature: string): boolean | Promise<boolean>;\n};\n\nexport type McpCallParams = {\n readonly name: string;\n readonly arguments: Readonly<Record<string, unknown>>;\n readonly _meta: TenuoMcpMeta;\n};\n\nexport type McpJsonRpcError = {\n readonly code: -32602 | -32001 | -32002;\n readonly message: string;\n readonly data?: { readonly tenuo?: { readonly code: TenuoErrorCode } };\n};\n\nexport interface TenuoMcp {\n /** Local authorize, then put warrant + PoP on `_meta.tenuo`. */\n attach(\n session: Session,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options?: McpAttachOptions,\n ): McpCallParams;\n /**\n * Server path. Verifies a presented warrant + PoP. Tool handler must not\n * run unless this returns.\n */\n verify(\n name: string,\n args: Readonly<Record<string, unknown>>,\n meta: unknown,\n options?: McpVerifyOptions,\n ): Promise<Readonly<Record<string, unknown>>>;\n /** Wrap a handler: verify from `extra._meta` / `extra.meta`, then execute. */\n handler<TArgs extends Record<string, unknown>, TResult>(\n name: string,\n execute: (args: TArgs) => TResult | Promise<TResult>,\n ): (\n args: TArgs,\n extra?: { readonly _meta?: unknown; readonly meta?: unknown },\n ) => Promise<TResult>;\n handler<TArgs extends Record<string, unknown>, TResult>(\n name: string,\n policy: McpHandlerPolicy,\n execute: (args: TArgs) => TResult | Promise<TResult>,\n ): (\n args: TArgs,\n extra?: { readonly _meta?: unknown; readonly meta?: unknown },\n ) => Promise<TResult>;\n jsonRpcError(error: unknown): McpJsonRpcError;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG","sourcesContent":["/**\n * Frozen public contract for `@tenuo/core`.\n *\n * Names and shapes here are the review surface — do not grow this file with\n * protocol vocabulary (`Warrant`, `mint`, `guard`) as the lead API.\n */\n\n/** Canonical kebab-case codes from Rust `ErrorCode::name()` / `Denial::code()`. */\nexport type ProtocolDenialCode =\n | \"signature-invalid\"\n | \"untrusted-root\"\n | \"chain-broken\"\n | \"depth-exceeded\"\n | \"warrant-expired\"\n | \"tool-not-authorized\"\n | \"constraint-violation\"\n | \"pop-signature-invalid\"\n | \"approval-required\"\n | \"insufficient-approvals\"\n | \"warrant-revoked\";\n\n/** Legacy SCREAMING_SNAKE aliases accepted by existing TypeScript callers. */\nexport type LegacyTenuoErrorCode =\n | \"TENUO_NOT_READY\"\n | \"TENUO_CONFIGURATION\"\n | \"TENUO_CONSTRAINT_VIOLATION\"\n | \"TENUO_TOOL_NOT_AUTHORIZED\"\n | \"TENUO_WARRANT_EXPIRED\"\n | \"TENUO_UNTRUSTED_ROOT\"\n | \"TENUO_INVALID_POP\"\n | \"TENUO_SIGNATURE_INVALID\"\n | \"TENUO_CHAIN_INVALID\"\n | \"TENUO_DEPTH_EXCEEDED\"\n | \"TENUO_REVOKED\"\n | \"TENUO_APPROVAL_REQUIRED\"\n | \"TENUO_INSUFFICIENT_APPROVALS\"\n | \"TENUO_CANONICALIZATION\"\n | \"TENUO_NOT_IMPLEMENTED\";\n\nexport type TenuoErrorCode = ProtocolDenialCode | LegacyTenuoErrorCode;\n\n// ---------------------------------------------------------------------------\n// Constraints. Every kind is evaluated in the Rust core; TypeScript only\n// builds the marker object. The set matches the core `Constraint` enum.\n// ---------------------------------------------------------------------------\n\n/** Path-traversal-safe prefix. Core evaluates this; TypeScript does not. */\nexport type UnderConstraint = {\n readonly kind: \"under\";\n readonly root: string;\n /** Default true. */\n readonly caseSensitive?: boolean;\n /** Whether the root itself is allowed. Default true. */\n readonly allowEqual?: boolean;\n};\n\nexport type EmailConstraint = {\n readonly kind: \"email\";\n readonly domain: string;\n};\n\nexport type MaxConstraint = {\n readonly kind: \"max\";\n readonly value: number;\n};\n\nexport type MinConstraint = {\n readonly kind: \"min\";\n readonly value: number;\n};\n\nexport type RangeConstraint = {\n readonly kind: \"range\";\n readonly min?: number;\n readonly max?: number;\n readonly minExclusive?: boolean;\n readonly maxExclusive?: boolean;\n};\n\nexport type OneOfConstraint = {\n readonly kind: \"oneOf\";\n readonly values: readonly string[];\n};\n\nexport type NotOneOfConstraint = {\n readonly kind: \"notOneOf\";\n readonly values: readonly string[];\n};\n\nexport type PatternConstraint = {\n readonly kind: \"pattern\";\n readonly pattern: string;\n};\n\nexport type RegexConstraint = {\n readonly kind: \"regex\";\n readonly source: string;\n};\n\nexport type ExactConstraint = {\n readonly kind: \"exact\";\n readonly value: string | number | boolean;\n};\n\nexport type WildcardConstraint = {\n readonly kind: \"wildcard\";\n};\n\nexport type CidrConstraint = {\n readonly kind: \"cidr\";\n readonly network: string;\n};\n\nexport type UrlPatternConstraint = {\n readonly kind: \"urlPattern\";\n readonly pattern: string;\n};\n\nexport type UrlSafeConstraint = {\n readonly kind: \"urlSafe\";\n readonly schemes?: readonly string[];\n readonly allowDomains?: readonly string[];\n readonly denyDomains?: readonly string[];\n};\n\nexport type ShlexConstraint = {\n readonly kind: \"shlex\";\n readonly allow: readonly string[];\n};\n\nexport type ContainsConstraint = {\n readonly kind: \"contains\";\n readonly values: readonly (string | number | boolean)[];\n};\n\nexport type SubsetConstraint = {\n readonly kind: \"subset\";\n readonly values: readonly (string | number | boolean)[];\n};\n\nexport type AnyOfConstraint = {\n readonly kind: \"anyOf\";\n readonly constraints: readonly ConstraintExpr[];\n};\n\nexport type AllConstraint = {\n readonly kind: \"all\";\n readonly constraints: readonly ConstraintExpr[];\n};\n\nexport type NotConstraint = {\n readonly kind: \"not\";\n readonly constraint: ConstraintExpr;\n};\n\nexport type CelConstraint = {\n readonly kind: \"cel\";\n readonly expression: string;\n};\n\nexport type ConstraintExpr =\n | UnderConstraint\n | EmailConstraint\n | MaxConstraint\n | MinConstraint\n | RangeConstraint\n | OneOfConstraint\n | NotOneOfConstraint\n | PatternConstraint\n | RegexConstraint\n | ExactConstraint\n | WildcardConstraint\n | CidrConstraint\n | UrlPatternConstraint\n | UrlSafeConstraint\n | ShlexConstraint\n | ContainsConstraint\n | SubsetConstraint\n | AnyOfConstraint\n | AllConstraint\n | NotConstraint\n | CelConstraint;\n\n/** Per-argument allow policy. Not a Zod schema. */\nexport type AllowPolicy = {\n readonly [field: string]: ConstraintExpr;\n};\n\n/**\n * Framework tool shape we wrap. No dependency on `ai` / Mastra.\n * `parameters` is host-schema (usually Zod) and is never treated as authority.\n */\nexport type ToolLike<\n TArgs extends Record<string, unknown> = Record<string, unknown>,\n TResult = unknown,\n> = {\n readonly description?: string;\n readonly parameters?: unknown;\n execute: (args: TArgs, options?: unknown) => TResult | Promise<TResult>;\n};\n\ntype KeysOfUnion<T> = T extends unknown ? keyof T : never;\n\nexport type ToolPolicy<TArgs = undefined> = {\n /**\n * Host ceiling. AND'd with the session in Rust.\n * Non-empty maps are zero-trust: every call argument must be named here.\n * `{}` means no extra ceiling (session/warrant only).\n */\n readonly allow: [NonNullable<TArgs>] extends [object]\n ? [KeysOfUnion<NonNullable<TArgs>>] extends [never]\n ? AllowPolicy\n : string extends KeysOfUnion<NonNullable<TArgs>>\n ? AllowPolicy\n : number extends KeysOfUnion<NonNullable<TArgs>>\n ? AllowPolicy\n : symbol extends KeysOfUnion<NonNullable<TArgs>>\n ? AllowPolicy\n : { readonly [Field in KeysOfUnion<NonNullable<TArgs>>]?: ConstraintExpr }\n : AllowPolicy;\n readonly capability?: string;\n};\n\n/** Compile-time brand only — never assigned at runtime. Do not `in`/`has` this key. */\ndeclare const protectedBrand: unique symbol;\n\nexport type ExecuteOptions = {\n readonly session?: Session;\n /** Already-signed approval envelopes (hex, standard base64, or CBOR bytes). */\n readonly approvals?: readonly (string | Uint8Array)[];\n /**\n * Signed evidence of the decision. Not a security hook.\n * Exceptions (sync or rejected Promise) are isolated and never deny or\n * fail the tool.\n */\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n /**\n * Correlation handle written to the receipt. Use whatever id the host\n * already has — a JSON-RPC id, a trace or span id — so a receipt can be\n * matched against your own logs. Not an identity: nothing enforces\n * uniqueness, and distinguishing repeated calls is the receipt chain's job.\n */\n readonly requestId?: string;\n};\n\ntype ExtraExecuteOptions<T extends { execute: (args: never) => unknown }> =\n T[\"execute\"] extends (args: never, options?: infer O) => unknown\n ? unknown extends O\n ? object\n : Omit<NonNullable<O>, keyof ExecuteOptions>\n : object;\n\n/** Wrapped tool. `execute` still authorizes; optional `{ session }` overrides ALS. */\nexport type ProtectedTool<T extends { execute: (args: never) => unknown }> = Omit<T, \"execute\"> & {\n execute: (\n args: Parameters<T[\"execute\"]>[0],\n options?: ExecuteOptions & ExtraExecuteOptions<T>,\n ) => Promise<Awaited<ReturnType<T[\"execute\"]>>>;\n readonly [protectedBrand]: true;\n};\n\n// ---------------------------------------------------------------------------\n// Approvals\n// ---------------------------------------------------------------------------\n\n/** Per-argument trigger for an approval gate. */\nexport type ArgApprovalGate =\n /** Every value of this argument fires the gate. */\n | \"all\"\n /** Only values satisfying the constraint fire the gate. */\n | { readonly when: ConstraintExpr }\n /** Every value except those satisfying the constraint fires the gate. Narrowable across delegation. */\n | { readonly exempt: ConstraintExpr };\n\n/** Gate on one tool: a message for the approver and optional per-argument triggers. */\nexport type ToolApprovalGate = {\n readonly message?: string;\n /** Absent: the whole tool is gated. */\n readonly args?: { readonly [field: string]: ArgApprovalGate };\n};\n\n/** Approval requirement. Host collects SignedApproval envelopes; Rust decides. */\nexport type RequireApproval = {\n readonly approvers: readonly PublicKeyHandle[];\n readonly min: number;\n /** Whole-tool gates. Defaults to every tool in the session when `gates` is absent too. */\n readonly tools?: readonly string[];\n /** Per-tool gates with messages and per-argument triggers. */\n readonly gates?: { readonly [tool: string]: ToolApprovalGate };\n};\n\n/** What an approval service needs to present a call to a human. Not a signed artifact. */\nexport type ApprovalRequest = {\n readonly requestId: string;\n readonly warrantId: string;\n readonly tool: string;\n readonly args: Readonly<Record<string, unknown>>;\n /** Hex SHA-256 committing to warrant id, tool, canonical args, and holder. */\n readonly requestHash: string;\n readonly holderPublicKey: string;\n readonly requiredApprovers: readonly string[];\n readonly minApprovals: number;\n /** Unix seconds. An approval cannot outlive the warrant. */\n readonly warrantExpiresAt: number;\n readonly createdAt: number;\n readonly message: string;\n};\n\n/** Holder-signed statement that it is the one asking. Carried in a control-plane request. */\nexport type ApprovalContextAttestation = {\n readonly version: number;\n readonly canonicalization: string;\n readonly warrantId: string;\n readonly tool: string;\n readonly requestHash: string;\n readonly holderKeyHex: string;\n readonly argsCanonicalCborB64: string;\n readonly signerKeyHex: string;\n readonly signatureB64: string;\n};\n\n/** Wire body for a control-plane approval API. Field names are the wire names. */\nexport type ControlPlaneApprovalRequestV1 = {\n readonly schema_version: 1;\n readonly request_id_hex: string;\n readonly warrant_id: string;\n readonly tool: string;\n readonly arguments: Readonly<Record<string, unknown>>;\n readonly request_hash_hex: string;\n readonly holder_public_key_hex: string;\n readonly required_approver_keys_hex: readonly string[];\n readonly min_approvals: number;\n readonly warrant_expires_at_unix: number;\n readonly created_at_unix: number;\n readonly attestation?: Readonly<Record<string, unknown>>;\n readonly temporal?: Readonly<Record<string, string>>;\n readonly message?: string;\n};\n\nexport type ControlPlaneApprovalResponseV1 = {\n readonly status: string;\n readonly signed_approvals_b64?: readonly string[];\n readonly error?: string;\n readonly server_request_id?: string;\n};\n\nexport type SignApprovalOptions = {\n /** Who approved, in the approver system's own terms. Required. */\n readonly externalId: string;\n /** Approval lifetime. Default one hour, always clamped to the warrant's expiry when given. */\n readonly ttlSeconds?: number;\n readonly warrantExpiresAt?: number;\n};\n\n/** Decoded approval envelope. Not authorization. */\nexport type ApprovalInfo = {\n readonly approverPublicKey: string;\n readonly requestHash: string;\n readonly externalId: string;\n readonly approvedAt: number;\n readonly expiresAt: number;\n readonly expired: boolean;\n readonly signatureValid: boolean;\n readonly error?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Sessions\n// ---------------------------------------------------------------------------\n\nexport type SessionAllow = {\n readonly [capability: string]: AllowPolicy;\n};\n\n/** Named level or a number 0-255. Delegation can only lower it. */\nexport type Clearance =\n | number\n | \"untrusted\"\n | \"external\"\n | \"partner\"\n | \"internal\"\n | \"privileged\"\n | \"system\";\n\nexport type SessionInput = {\n /** Capability map. Optional when `tools` is set. Issuer sessions may omit it. */\n readonly allow?: SessionAllow;\n /** Wrapped tools from `tenuo.tool()`. Their `allow` is minted into the session. */\n readonly tools?: readonly object[];\n readonly ttlSeconds?: number;\n readonly requireApproval?: RequireApproval;\n /**\n * Issue to another agent's key instead of a fresh local holder. The\n * returned session cannot authorize here (it has no holder secret); send\n * `toWire()` to that agent, which imports it with `sessionFromWire()`.\n */\n readonly holder?: PublicKeyHandle;\n /**\n * How many times this authority may be delegated below the root.\n * `0` makes the session terminal. Omit for the protocol maximum.\n * Delegation can lower this ceiling and never raise it.\n */\n readonly maxDepth?: number;\n /**\n * `\"execution\"` (default) can call tools. `\"issuer\"` cannot call tools; its\n * holder can mint execution sessions with `tenuo.issue()` for the tools in\n * `issuableTools`, within `constraintBounds`, without the root key.\n */\n readonly kind?: \"execution\" | \"issuer\";\n readonly clearance?: Clearance;\n /** Free-form task correlation id. Inherited unchanged through delegation. */\n readonly sessionId?: string;\n /** Free-form agent label. Delegation may change it. */\n readonly agentId?: string;\n /** Issuer sessions only. */\n readonly issuableTools?: readonly string[];\n /** Issuer sessions only: ceiling on the constraints of anything issued, by field. */\n readonly constraintBounds?: AllowPolicy;\n /** Issuer sessions only: how deep issued sessions may themselves delegate. */\n readonly maxIssueDepth?: number;\n};\n\n/** Options for `tenuo.narrow()`. All optional; the default keeps the holder. */\nexport type NarrowOptions = {\n /**\n * Bind the child to another agent's key. This is delegation: the current\n * holder signs, the child belongs to `holder`, and the returned session\n * cannot authorize here. Hand `toWire()` to that agent.\n */\n readonly holder?: PublicKeyHandle;\n /** Child lifetime. Clamped to what the parent has left. */\n readonly ttlSeconds?: number;\n /** The child's holder cannot delegate further. */\n readonly terminal?: boolean;\n /** Lower the delegation ceiling. Cannot exceed the parent's. */\n readonly maxDepth?: number;\n /** Lower the clearance. Cannot exceed the parent's. */\n readonly clearance?: Clearance;\n readonly agentId?: string;\n /** Approvers can only be added. */\n readonly addApprovers?: readonly PublicKeyHandle[];\n /** The threshold can only go up. */\n readonly minApprovals?: number;\n};\n\n/** Options for `tenuo.issue()`: an execution session minted from an issuer session. */\nexport type IssueInput = {\n readonly allow: SessionAllow;\n /** The distinct agent that will use it. Issuer/holder separation is enforced. */\n readonly holder: PublicKeyHandle;\n readonly ttlSeconds?: number;\n readonly maxDepth?: number;\n readonly clearance?: Clearance;\n readonly sessionId?: string;\n readonly agentId?: string;\n /** Approval policy for the issued execution session. Defaults to gating every issued tool. */\n readonly requireApproval?: RequireApproval;\n};\n\n/** Public view of a session's leaf. Never includes the holder secret. */\nexport type SessionInfo = {\n readonly kind: \"execution\" | \"issuer\";\n /** Hex public key the leaf is bound to. */\n readonly holderPublicKey: string;\n /** Hex public key that signed the root of the chain. */\n readonly rootPublicKey: string;\n /** 0 for a root session, +1 per delegation. */\n readonly depth: number;\n /** Delegation ceiling in force for this leaf. */\n readonly maxDepth: number;\n /** True when `depth >= maxDepth`: cannot be narrowed further. */\n readonly terminal: boolean;\n /** Unix seconds. */\n readonly expiresAt: number;\n readonly tools: readonly string[];\n /** Warrant ids, root first. */\n readonly warrantIds: readonly string[];\n /**\n * True when this process holds the leaf's holder secret. False for a\n * session issued or delegated to another agent: `toWire()` works,\n * `execute` and `narrow` do not.\n */\n readonly canAuthorize: boolean;\n readonly clearance?: number;\n readonly sessionId?: string;\n readonly agentId?: string;\n readonly issuableTools?: readonly string[];\n readonly maxIssueDepth?: number;\n readonly requiredApprovers?: readonly string[];\n readonly minApprovals?: number;\n readonly approvalGatedTools: readonly string[];\n};\n\n/** One constrained argument in an `explain()` result. */\nexport type ExplainedField = {\n readonly field: string;\n /** Core constraint type name, e.g. `\"Subpath\"`, `\"Range\"`. */\n readonly kind: string;\n /** The constraint as core serializes it. */\n readonly constraint: unknown;\n /** Absent when the argument was not supplied. */\n readonly value?: unknown;\n readonly satisfied: boolean;\n /** Stable machine-readable diagnostic, when the core can identify one. */\n readonly reasonCode?: string;\n readonly reason?: string;\n};\n\n/** What a session would decide for `tool(args)`, field by field. No proof-of-possession involved. */\nexport type Explanation = {\n readonly tool: string;\n readonly kind: \"execution\" | \"issuer\";\n readonly outcome: \"allow\" | \"deny\";\n readonly code?: TenuoErrorCode;\n readonly field?: string;\n readonly message?: string;\n readonly toolGranted: boolean;\n readonly fields: readonly ExplainedField[];\n /** Arguments the session does not name. Zero-trust policies reject these. */\n readonly unknownFields: readonly string[];\n /** Constrained arguments the call did not supply. */\n readonly missingFields: readonly string[];\n readonly expired: boolean;\n readonly expiresAt: number;\n readonly chainValid: boolean;\n readonly chainError?: TenuoErrorCode;\n};\n\nexport type DevRoot = {\n readonly kind: \"dev-root\";\n /**\n * Required when `NODE_ENV` is unset or is not `development` / `test`.\n * Unset `NODE_ENV` is not treated as development.\n */\n readonly allowInProduction?: boolean;\n};\n\n/** A stable issuer secret: this process is a control plane. */\nexport type IssuerKey = {\n readonly kind: \"issuer-key\";\n readonly secret: Uint8Array;\n};\n\nexport type PublicKeyHandle = {\n readonly kind: \"public-key\";\n readonly source: \"env\" | \"hex\" | \"bytes\";\n readonly hex: string;\n};\n\nexport type WarrantPart = {\n readonly payload_hex: string;\n readonly signature_hex: string;\n};\n\nexport type SessionFromWireInput = {\n readonly warrant: string | readonly string[] | readonly WarrantPart[];\n readonly holderKey: Uint8Array;\n};\n\n/** Field-level (`{ path: under(\"/data\") }`) or per-capability (`{ read_file: { path: ... } }`). */\nexport type NarrowInput = AllowPolicy | SessionAllow;\n\nexport type CreateTenuoOptions = {\n readonly trustedRoots?: readonly PublicKeyHandle[];\n /**\n * `devRoot()` for development; an `issuerKey*` for a control plane that\n * mints with a stable key; a public key for a verifier. An issuer key\n * trusts itself plus `trustedRoots`.\n */\n readonly root?: DevRoot | IssuerKey | PublicKeyHandle;\n /** Published SignedRevocationList (hex or standard base64). Verified against a trusted root. */\n readonly revocationList?: string | Uint8Array;\n /** 32-byte holder seed used to sign verifier receipts. Runtime supplies this. */\n readonly receiptSigner?: Uint8Array;\n};\n\nexport type ApprovalRequestSummary = {\n readonly tool: string;\n readonly required: number;\n readonly received: number;\n};\n\nexport type Decision =\n | { readonly outcome: \"allow\"; readonly args: Readonly<Record<string, unknown>> }\n | { readonly outcome: \"deny\"; readonly code: TenuoErrorCode; readonly field?: string }\n | { readonly outcome: \"approval_required\"; readonly request: ApprovalRequestSummary };\n\n// ---------------------------------------------------------------------------\n// Revocation and receipts\n// ---------------------------------------------------------------------------\n\nexport type RevocationListInput = {\n readonly revoke: readonly string[];\n /** Monotonic; verifiers refuse an older version. Default 1. */\n readonly version?: number;\n};\n\nexport type RevocationListInfo = {\n readonly version: number;\n readonly issuedAt: number;\n readonly issuerPublicKey: string;\n readonly revokedIds: readonly string[];\n readonly signatureValid: boolean;\n};\n\n/** Signature authenticity of a receipt. Not authorization. */\nexport type ReceiptInfo = {\n readonly authentic: true;\n /** Hex key the receipt is signed under — resolve against your own authorizer set. */\n readonly signerKey: string;\n readonly outcome: \"allow\" | \"deny\";\n readonly action: string;\n readonly decisionCode?: string;\n readonly requestId: string;\n readonly srlVersion?: number;\n readonly srlHash?: string;\n readonly requestHash?: string;\n readonly policyDefinitionHash?: string;\n readonly prevReceiptHash?: string;\n readonly trustedRootsHash?: string;\n};\n\n/** Root-anchored receipt check: the embedded chain against trusted roots at decision time. */\nexport type ReceiptChainInfo = {\n readonly signerKey: string;\n readonly outcome: \"allow\" | \"deny\";\n readonly decisionCode?: string;\n readonly timestamp: number;\n readonly chainValid: boolean;\n readonly chainError?: string;\n readonly corroboratesDenial?: boolean;\n readonly rootIssuer?: string;\n readonly leafHolder?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Presenting authority across any boundary\n// ---------------------------------------------------------------------------\n\n/** Warrant chain plus proof-of-possession for one call. Transport-agnostic. */\nexport type PresentedCall = {\n readonly warrant: string;\n readonly signature: string;\n readonly approvals?: readonly string[];\n};\n\nexport interface Session {\n readonly [Symbol.toStringTag]: \"TenuoSession\";\n /** Warrant tokens, root first. Does not include the holder secret. */\n toWire(): readonly string[];\n /** SHA-256 of `(warrant_id, tool, canonical args)`. App-level idempotency, not PoP. */\n dedupKey(tool: string, args: Readonly<Record<string, unknown>>): string;\n /** Holder public key, depth, ceiling, lifetime, tools. Never the secret. */\n inspect(): SessionInfo;\n /**\n * Receipt helpers below are required for `createTenuo.runtime({ receipts:\n * \"collect\" })`. Implementers that are not runtime sessions can return\n * empty snapshots and `0` from acknowledge.\n */\n /**\n * Receipts collected for this session and not yet drained. Empty when the\n * session was not created by a Runtime with `receipts: \"collect\"`.\n */\n peekReceipts(): readonly string[];\n /**\n * Snapshot of collected receipts. Same as `peekReceipts`; nothing is removed\n * until `acknowledgeReceipts`.\n */\n drainReceipts(): readonly string[];\n /** Remove the first `count` pending receipts. */\n acknowledgeReceipts(count: number): number;\n}\n\nexport interface Tenuo {\n /**\n * Wrap any `{ execute }` tool (Vercel AI SDK, Mastra, plain object).\n * `parameters` / Zod stay on the inner tool and are never treated as authority.\n */\n tool<T extends { execute: (args: never) => unknown }>(\n inner: T,\n policy: ToolPolicy<Parameters<T[\"execute\"]>[0]>,\n ): ProtectedTool<T>;\n session(input: SessionInput): Session;\n /** Import a warrant minted elsewhere (Rust / Python / another process). */\n sessionFromWire(input: SessionFromWireInput): Session;\n withSession<R>(session: Session, fn: () => R): R;\n /**\n * Child session with less authority. Without options the same holder keeps\n * it; with `options.holder` it is delegated to another agent's key. Core\n * rejects any child that is not within its parent before a token exists.\n */\n narrow(session: Session, allow: NarrowInput, options?: NarrowOptions): Session;\n /**\n * Mint an execution session from an issuer session, signed by the issuer\n * session's holder. Core checks tools against `issuableTools`, constraints\n * against `constraintBounds`, clearance, and issue depth. No root key.\n */\n issue(issuer: Session, input: IssueInput): Session;\n /**\n * Public key of the local issuer (dev root or issuer key). Other processes\n * put it in `trustedRoots` to accept warrants this context mints.\n */\n issuerPublicKey(): PublicKeyHandle;\n /** What the session would decide for `tool(args)`, field by field. Never runs the tool. */\n explain(session: Session, tool: string, args: Readonly<Record<string, unknown>>): Explanation;\n /** Everything an approval service needs to present `tool(args)` to a human. */\n approvalRequest(session: Session, tool: string, args: Readonly<Record<string, unknown>>): ApprovalRequest;\n /** Holder-signed proof that this session is the one asking for `tool(args)`. */\n attestApprovalRequest(\n session: Session,\n tool: string,\n args: Readonly<Record<string, unknown>>,\n ): ApprovalContextAttestation;\n /** Authorize locally, then produce warrant + proof-of-possession for another boundary to verify. */\n present(\n session: Session,\n tool: string,\n args: Readonly<Record<string, unknown>>,\n options?: McpAttachOptions,\n ): PresentedCall;\n /**\n * Verify a presented call at any boundary. Resolves with the authorized\n * arguments; the tool must not run otherwise. `args` is the object already\n * given to this function. A repeated key in the original JSON is not\n * visible. Parse that text with `parseStrictJson` while it is still available.\n */\n verify(\n presented: PresentedCall,\n tool: string,\n args: Readonly<Record<string, unknown>>,\n options?: McpVerifyOptions,\n ): Promise<Readonly<Record<string, unknown>>>;\n /** Sign a revocation list with this context's issuer key. Load it with `revoke()` where this issuer is trusted. */\n revocationList(input: RevocationListInput): string;\n /** Load a published SignedRevocationList. Rust verifies the issuer against trusted roots. */\n revoke(list: string | Uint8Array): void;\n /** MCP `_meta.tenuo` attach / verify. No MCP SDK dependency. */\n readonly mcp: TenuoMcp;\n ready(): void;\n}\n\n/** Wire envelope Python and TypeScript both read from `params._meta`. */\nexport type TenuoMcpMeta = {\n readonly tenuo: PresentedCall;\n};\n\nexport type McpAttachOptions = {\n readonly approvals?: readonly (string | Uint8Array)[];\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n /**\n * Correlation handle written to the receipt. Use whatever id the host\n * already has — a JSON-RPC id, a trace or span id — so a receipt can be\n * matched against your own logs. Not an identity: nothing enforces\n * uniqueness, and distinguishing repeated calls is the receipt chain's job.\n */\n readonly requestId?: string;\n};\n\n/** Host ceiling and optional receipt hook on `mcp.handler()`. */\nexport type McpHandlerPolicy = {\n readonly allow?: AllowPolicy;\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n readonly nonceStore?: NonceStore;\n /**\n * Isolated. Called when `nonceStore.checkAndRecord` throws or rejects.\n * The client only ever sees \"Replay store unavailable\".\n */\n readonly onNonceStoreError?: (error: unknown) => void | Promise<void>;\n};\n\nexport type McpVerifyOptions = {\n readonly allow?: AllowPolicy;\n readonly onReceipt?: (receipt: string) => void | Promise<void>;\n /**\n * Opt-in exact-PoP replay check. `checkAndRecord` may return a Promise\n * (Redis). A rejected Promise fails closed. In-memory stores do not work\n * across processes. PoP v1 is otherwise replayable in-window, including\n * envelopes that already carry approvals.\n */\n readonly nonceStore?: NonceStore;\n /**\n * Isolated. Called when `nonceStore.checkAndRecord` throws or rejects.\n * The client only ever sees \"Replay store unavailable\".\n */\n readonly onNonceStoreError?: (error: unknown) => void | Promise<void>;\n /**\n * Correlation handle written to the receipt. Use whatever id the host\n * already has — a JSON-RPC id, a trace or span id — so a receipt can be\n * matched against your own logs. Not an identity: nothing enforces\n * uniqueness, and distinguishing repeated calls is the receipt chain's job.\n */\n readonly requestId?: string;\n};\n\n/**\n * Returns true if the PoP is fresh and recorded; false if this exact token\n * was already consumed. Async stores (Redis) return `Promise<boolean>`.\n * `verify()` / handlers await the result. Never return a Promise from a\n * store that is only consulted synchronously — that used to fail open.\n */\nexport type NonceStore = {\n checkAndRecord(popSignature: string): boolean | Promise<boolean>;\n};\n\nexport type McpCallParams = {\n readonly name: string;\n readonly arguments: Readonly<Record<string, unknown>>;\n readonly _meta: TenuoMcpMeta;\n};\n\nexport type McpJsonRpcError = {\n readonly code: -32602 | -32001 | -32002;\n readonly message: string;\n readonly data?: { readonly tenuo?: { readonly code: TenuoErrorCode } };\n};\n\nexport interface TenuoMcp {\n /** Local authorize, then put warrant + PoP on `_meta.tenuo`. */\n attach(\n session: Session,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options?: McpAttachOptions,\n ): McpCallParams;\n /**\n * Server path. Verifies a presented warrant + PoP against the object it was\n * given. A repeated key in the original JSON is not visible: the host has\n * already parsed `args`. Parse that text with `parseStrictJson` while it is\n * still available. The tool handler must not run unless this returns.\n */\n verify(\n name: string,\n args: Readonly<Record<string, unknown>>,\n meta: unknown,\n options?: McpVerifyOptions,\n ): Promise<Readonly<Record<string, unknown>>>;\n /** Wrap a handler: verify from `extra._meta` / `extra.meta`, then execute. */\n handler<TArgs extends Record<string, unknown>, TResult>(\n name: string,\n execute: (args: TArgs) => TResult | Promise<TResult>,\n ): (\n args: TArgs,\n extra?: { readonly _meta?: unknown; readonly meta?: unknown },\n ) => Promise<TResult>;\n handler<TArgs extends Record<string, unknown>, TResult>(\n name: string,\n policy: McpHandlerPolicy,\n execute: (args: TArgs) => TResult | Promise<TResult>,\n ): (\n args: TArgs,\n extra?: { readonly _meta?: unknown; readonly meta?: unknown },\n ) => Promise<TResult>;\n jsonRpcError(error: unknown): McpJsonRpcError;\n}\n"]}
|
package/dist/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@tenuo/core",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"sourceCommit": "
|
|
3
|
+
"version": "0.3.2-beta.0",
|
|
4
|
+
"sourceCommit": "7f27c7d4782f8f19ec15313e09ab95883a1da811",
|
|
5
5
|
"sourceClean": false,
|
|
6
|
-
"wasmSha256": "
|
|
6
|
+
"wasmSha256": "071de0080d616e5519841930f0579e5234a93a9f29724595609744d43b7e9d4a",
|
|
7
7
|
"wasmMatchesCommit": false
|
|
8
8
|
}
|
|
@@ -105,6 +105,12 @@ export class SdkContext {
|
|
|
105
105
|
*/
|
|
106
106
|
narrow(session: SdkSession, allow_json: any, options: any): SdkSession;
|
|
107
107
|
constructor();
|
|
108
|
+
/**
|
|
109
|
+
* `_meta.tenuo` for argument JSON text at `timestamp` (unix seconds).
|
|
110
|
+
*
|
|
111
|
+
* The proof covers the core's parse of `args_json`, including JSON null.
|
|
112
|
+
*/
|
|
113
|
+
signMeta(session: SdkSession, tool: string, args_json: string, timestamp: number, approvals: any): any;
|
|
108
114
|
/**
|
|
109
115
|
* Holder PoP only. Does not authorize. Used to fill `_meta.tenuo.signature`.
|
|
110
116
|
*/
|
|
@@ -118,6 +124,14 @@ export class SdkContext {
|
|
|
118
124
|
* `loadRevocationList` anywhere this issuer is a trusted root.
|
|
119
125
|
*/
|
|
120
126
|
signRevocationListVersioned(ids: any, version?: number | null): string;
|
|
127
|
+
/**
|
|
128
|
+
* Compatibility alias for verifyMetaPop. NOT an authorization check.
|
|
129
|
+
*/
|
|
130
|
+
verifyMeta(warrant: string, signature: string, tool: string, args_json: string, timestamp: number): boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Check ONLY the holder proof, not trust, expiry, constraints, approvals or replay.
|
|
133
|
+
*/
|
|
134
|
+
verifyMetaPop(warrant: string, signature: string, tool: string, args_json: string, timestamp: number): boolean;
|
|
121
135
|
/**
|
|
122
136
|
* Sign verifier receipts with this 32-byte holder secret instead of an
|
|
123
137
|
* ephemeral key. Used by the TypeScript `Runtime`.
|
|
@@ -164,7 +178,7 @@ export class SdkSession {
|
|
|
164
178
|
*/
|
|
165
179
|
static fromWire(warrant: string, holder_secret: Uint8Array): SdkSession;
|
|
166
180
|
/**
|
|
167
|
-
* CBOR warrant stack as
|
|
181
|
+
* CBOR warrant stack as unpadded URL-safe base64. Same bytes as `_meta.tenuo.warrant`.
|
|
168
182
|
*/
|
|
169
183
|
toStackWire(): string;
|
|
170
184
|
/**
|
|
@@ -296,6 +310,18 @@ export function inspect_approval_gate(warrant_b64: string, tool: string): any;
|
|
|
296
310
|
*/
|
|
297
311
|
export function parse_connect_token(token: string): any;
|
|
298
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Reject a repeated key in any object.
|
|
315
|
+
*
|
|
316
|
+
* A tools/call the host has already turned into an object cannot be checked:
|
|
317
|
+
* the duplicate key is gone. Call this while the text is still available.
|
|
318
|
+
* Nested objects are checked too. On success the caller parses `json_text`
|
|
319
|
+
* with the host parser. This function does not build a JavaScript object:
|
|
320
|
+
* that conversion turns `__proto__` into a prototype change and `null` into
|
|
321
|
+
* `undefined`.
|
|
322
|
+
*/
|
|
323
|
+
export function parse_strict_json(json_text: string): void;
|
|
324
|
+
|
|
299
325
|
/**
|
|
300
326
|
* Decode and check an approval envelope. Not authorization.
|
|
301
327
|
*/
|
|
@@ -295,6 +295,29 @@ class SdkContext {
|
|
|
295
295
|
SdkContextFinalization.register(this, this.__wbg_ptr, this);
|
|
296
296
|
return this;
|
|
297
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* `_meta.tenuo` for argument JSON text at `timestamp` (unix seconds).
|
|
300
|
+
*
|
|
301
|
+
* The proof covers the core's parse of `args_json`, including JSON null.
|
|
302
|
+
* @param {SdkSession} session
|
|
303
|
+
* @param {string} tool
|
|
304
|
+
* @param {string} args_json
|
|
305
|
+
* @param {number} timestamp
|
|
306
|
+
* @param {any} approvals
|
|
307
|
+
* @returns {any}
|
|
308
|
+
*/
|
|
309
|
+
signMeta(session, tool, args_json, timestamp, approvals) {
|
|
310
|
+
_assertClass(session, SdkSession);
|
|
311
|
+
const ptr0 = passStringToWasm0(tool, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
312
|
+
const len0 = WASM_VECTOR_LEN;
|
|
313
|
+
const ptr1 = passStringToWasm0(args_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
314
|
+
const len1 = WASM_VECTOR_LEN;
|
|
315
|
+
const ret = wasm.sdkcontext_signMeta(this.__wbg_ptr, session.__wbg_ptr, ptr0, len0, ptr1, len1, timestamp, approvals);
|
|
316
|
+
if (ret[2]) {
|
|
317
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
318
|
+
}
|
|
319
|
+
return takeFromExternrefTable0(ret[0]);
|
|
320
|
+
}
|
|
298
321
|
/**
|
|
299
322
|
* Holder PoP only. Does not authorize. Used to fill `_meta.tenuo.signature`.
|
|
300
323
|
* @param {SdkSession} session
|
|
@@ -371,6 +394,54 @@ class SdkContext {
|
|
|
371
394
|
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
372
395
|
}
|
|
373
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Compatibility alias for verifyMetaPop. NOT an authorization check.
|
|
399
|
+
* @param {string} warrant
|
|
400
|
+
* @param {string} signature
|
|
401
|
+
* @param {string} tool
|
|
402
|
+
* @param {string} args_json
|
|
403
|
+
* @param {number} timestamp
|
|
404
|
+
* @returns {boolean}
|
|
405
|
+
*/
|
|
406
|
+
verifyMeta(warrant, signature, tool, args_json, timestamp) {
|
|
407
|
+
const ptr0 = passStringToWasm0(warrant, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
408
|
+
const len0 = WASM_VECTOR_LEN;
|
|
409
|
+
const ptr1 = passStringToWasm0(signature, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
410
|
+
const len1 = WASM_VECTOR_LEN;
|
|
411
|
+
const ptr2 = passStringToWasm0(tool, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
412
|
+
const len2 = WASM_VECTOR_LEN;
|
|
413
|
+
const ptr3 = passStringToWasm0(args_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
414
|
+
const len3 = WASM_VECTOR_LEN;
|
|
415
|
+
const ret = wasm.sdkcontext_verifyMeta(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, timestamp);
|
|
416
|
+
if (ret[2]) {
|
|
417
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
418
|
+
}
|
|
419
|
+
return ret[0] !== 0;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Check ONLY the holder proof, not trust, expiry, constraints, approvals or replay.
|
|
423
|
+
* @param {string} warrant
|
|
424
|
+
* @param {string} signature
|
|
425
|
+
* @param {string} tool
|
|
426
|
+
* @param {string} args_json
|
|
427
|
+
* @param {number} timestamp
|
|
428
|
+
* @returns {boolean}
|
|
429
|
+
*/
|
|
430
|
+
verifyMetaPop(warrant, signature, tool, args_json, timestamp) {
|
|
431
|
+
const ptr0 = passStringToWasm0(warrant, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
432
|
+
const len0 = WASM_VECTOR_LEN;
|
|
433
|
+
const ptr1 = passStringToWasm0(signature, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
434
|
+
const len1 = WASM_VECTOR_LEN;
|
|
435
|
+
const ptr2 = passStringToWasm0(tool, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
436
|
+
const len2 = WASM_VECTOR_LEN;
|
|
437
|
+
const ptr3 = passStringToWasm0(args_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
438
|
+
const len3 = WASM_VECTOR_LEN;
|
|
439
|
+
const ret = wasm.sdkcontext_verifyMetaPop(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, timestamp);
|
|
440
|
+
if (ret[2]) {
|
|
441
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
442
|
+
}
|
|
443
|
+
return ret[0] !== 0;
|
|
444
|
+
}
|
|
374
445
|
/**
|
|
375
446
|
* Sign verifier receipts with this 32-byte holder secret instead of an
|
|
376
447
|
* ephemeral key. Used by the TypeScript `Runtime`.
|
|
@@ -519,7 +590,7 @@ class SdkSession {
|
|
|
519
590
|
return SdkSession.__wrap(ret[0]);
|
|
520
591
|
}
|
|
521
592
|
/**
|
|
522
|
-
* CBOR warrant stack as
|
|
593
|
+
* CBOR warrant stack as unpadded URL-safe base64. Same bytes as `_meta.tenuo.warrant`.
|
|
523
594
|
* @returns {string}
|
|
524
595
|
*/
|
|
525
596
|
toStackWire() {
|
|
@@ -877,6 +948,27 @@ function parse_connect_token(token) {
|
|
|
877
948
|
}
|
|
878
949
|
exports.parse_connect_token = parse_connect_token;
|
|
879
950
|
|
|
951
|
+
/**
|
|
952
|
+
* Reject a repeated key in any object.
|
|
953
|
+
*
|
|
954
|
+
* A tools/call the host has already turned into an object cannot be checked:
|
|
955
|
+
* the duplicate key is gone. Call this while the text is still available.
|
|
956
|
+
* Nested objects are checked too. On success the caller parses `json_text`
|
|
957
|
+
* with the host parser. This function does not build a JavaScript object:
|
|
958
|
+
* that conversion turns `__proto__` into a prototype change and `null` into
|
|
959
|
+
* `undefined`.
|
|
960
|
+
* @param {string} json_text
|
|
961
|
+
*/
|
|
962
|
+
function parse_strict_json(json_text) {
|
|
963
|
+
const ptr0 = passStringToWasm0(json_text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
964
|
+
const len0 = WASM_VECTOR_LEN;
|
|
965
|
+
const ret = wasm.parse_strict_json(ptr0, len0);
|
|
966
|
+
if (ret[1]) {
|
|
967
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
exports.parse_strict_json = parse_strict_json;
|
|
971
|
+
|
|
880
972
|
/**
|
|
881
973
|
* Decode and check an approval envelope. Not authorization.
|
|
882
974
|
* @param {string} envelope
|
|
Binary file
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export const memory: WebAssembly.Memory;
|
|
4
4
|
export const __wbg_sdkcontext_free: (a: number, b: number) => void;
|
|
5
5
|
export const __wbg_sdksession_free: (a: number, b: number) => void;
|
|
6
|
+
export const parse_strict_json: (a: number, b: number) => [number, number];
|
|
6
7
|
export const sdkInspectParts: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
7
8
|
export const sdkInspectWarrant: (a: number, b: number) => [number, number, number];
|
|
8
9
|
export const sdkProtocolLimits: () => [number, number, number];
|
|
@@ -21,8 +22,11 @@ export const sdkcontext_loadRevocationList: (a: number, b: number, c: number) =>
|
|
|
21
22
|
export const sdkcontext_mint: (a: number, b: any, c: number, d: any, e: number, f: number, g: number) => [number, number, number];
|
|
22
23
|
export const sdkcontext_narrow: (a: number, b: number, c: any, d: any) => [number, number, number];
|
|
23
24
|
export const sdkcontext_new: () => number;
|
|
25
|
+
export const sdkcontext_signMeta: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: any) => [number, number, number];
|
|
24
26
|
export const sdkcontext_signPop: (a: number, b: number, c: number, d: number, e: any) => [number, number, number, number];
|
|
25
27
|
export const sdkcontext_signRevocationList: (a: number, b: any) => [number, number, number, number];
|
|
28
|
+
export const sdkcontext_verifyMeta: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => [number, number, number];
|
|
29
|
+
export const sdkcontext_verifyMetaPop: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => [number, number, number];
|
|
26
30
|
export const sdkcontext_withReceiptSigner: (a: number, b: number, c: number) => [number, number, number];
|
|
27
31
|
export const sdksession_dedupKey: (a: number, b: number, c: number, d: any) => [number, number, number, number];
|
|
28
32
|
export const sdksession_describe: (a: number) => [number, number, number];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { ApprovalGateInspection, ApprovalRequirement, ApprovalRequirementStatus, } from "./wasm.ts";
|
|
2
|
-
export { approvalRequirement, evaluateApprovalGates, inspectApprovalGate } from "./wasm.ts";
|
|
2
|
+
export { approvalRequirement, evaluateApprovalGates, inspectApprovalGate, parseStrictJson, } from "./wasm.ts";
|
|
3
3
|
export type { AllConstraint, AllowPolicy, AnyOfConstraint, ApprovalContextAttestation, ApprovalInfo, ApprovalRequest, ApprovalRequestSummary, ArgApprovalGate, CelConstraint, CidrConstraint, Clearance, ConstraintExpr, ContainsConstraint, ControlPlaneApprovalRequestV1, ControlPlaneApprovalResponseV1, CreateTenuoOptions, Decision, DevRoot, EmailConstraint, ExactConstraint, ExecuteOptions, ExplainedField, Explanation, IssueInput, IssuerKey, MaxConstraint, McpAttachOptions, McpCallParams, McpHandlerPolicy, McpJsonRpcError, McpVerifyOptions, MinConstraint, NarrowInput, NarrowOptions, NonceStore, NotConstraint, NotOneOfConstraint, OneOfConstraint, PatternConstraint, PresentedCall, ProtectedTool, PublicKeyHandle, RangeConstraint, ReceiptChainInfo, ReceiptInfo, RegexConstraint, RequireApproval, RevocationListInfo, RevocationListInput, Session, SessionAllow, SessionFromWireInput, SessionInfo, SessionInput, ShlexConstraint, SignApprovalOptions, SubsetConstraint, Tenuo, LegacyTenuoErrorCode, ProtocolDenialCode, TenuoErrorCode, TenuoMcp, TenuoMcpMeta, ToolApprovalGate, ToolLike, ToolPolicy, UnderConstraint, UrlPatternConstraint, UrlSafeConstraint, WarrantPart, WildcardConstraint, } from "./api.ts";
|
|
4
4
|
export { ApprovalRequiredError, AuthorizationDeniedError, TenuoConfigurationError, TenuoError, } from "./errors.ts";
|
|
5
5
|
export { all, anyOf, cel, cidr, contains, email, exact, max, min, not, notOneOf, oneOf, pathGlob, pattern, range, regex, shlex, subset, under, urlPattern, urlSafe, wildcard, } from "./constraints.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { approvalRequirement, evaluateApprovalGates, inspectApprovalGate } from "./wasm.js";
|
|
1
|
+
export { approvalRequirement, evaluateApprovalGates, inspectApprovalGate, parseStrictJson, } from "./wasm.js";
|
|
2
2
|
export { ApprovalRequiredError, AuthorizationDeniedError, TenuoConfigurationError, TenuoError, } from "./errors.js";
|
|
3
3
|
export { all, anyOf, cel, cidr, contains, email, exact, max, min, not, notOneOf, oneOf, pathGlob, pattern, range, regex, shlex, subset, under, urlPattern, urlSafe, wildcard, } from "./constraints.js";
|
|
4
4
|
export { memoryNonceStore } from "./nonce.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,GAChB,MAAM,WAAW,CAAC;AA4EnB,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,KAAK,EACL,GAAG,EACH,GAAG,EACH,GAAG,EACH,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,UAAU,EACV,OAAO,EACP,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAElF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export type {\n ApprovalGateInspection,\n ApprovalRequirement,\n ApprovalRequirementStatus,\n} from \"./wasm.ts\";\n\nexport {\n approvalRequirement,\n evaluateApprovalGates,\n inspectApprovalGate,\n parseStrictJson,\n} from \"./wasm.ts\";\n\nexport type {\n AllConstraint,\n AllowPolicy,\n AnyOfConstraint,\n ApprovalContextAttestation,\n ApprovalInfo,\n ApprovalRequest,\n ApprovalRequestSummary,\n ArgApprovalGate,\n CelConstraint,\n CidrConstraint,\n Clearance,\n ConstraintExpr,\n ContainsConstraint,\n ControlPlaneApprovalRequestV1,\n ControlPlaneApprovalResponseV1,\n CreateTenuoOptions,\n Decision,\n DevRoot,\n EmailConstraint,\n ExactConstraint,\n ExecuteOptions,\n ExplainedField,\n Explanation,\n IssueInput,\n IssuerKey,\n MaxConstraint,\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n MinConstraint,\n NarrowInput,\n NarrowOptions,\n NonceStore,\n NotConstraint,\n NotOneOfConstraint,\n OneOfConstraint,\n PatternConstraint,\n PresentedCall,\n ProtectedTool,\n PublicKeyHandle,\n RangeConstraint,\n ReceiptChainInfo,\n ReceiptInfo,\n RegexConstraint,\n RequireApproval,\n RevocationListInfo,\n RevocationListInput,\n Session,\n SessionAllow,\n SessionFromWireInput,\n SessionInfo,\n SessionInput,\n ShlexConstraint,\n SignApprovalOptions,\n SubsetConstraint,\n Tenuo,\n LegacyTenuoErrorCode,\n ProtocolDenialCode,\n TenuoErrorCode,\n TenuoMcp,\n TenuoMcpMeta,\n ToolApprovalGate,\n ToolLike,\n ToolPolicy,\n UnderConstraint,\n UrlPatternConstraint,\n UrlSafeConstraint,\n WarrantPart,\n WildcardConstraint,\n} from \"./api.ts\";\n\nexport {\n ApprovalRequiredError,\n AuthorizationDeniedError,\n TenuoConfigurationError,\n TenuoError,\n} from \"./errors.ts\";\n\nexport {\n all,\n anyOf,\n cel,\n cidr,\n contains,\n email,\n exact,\n max,\n min,\n not,\n notOneOf,\n oneOf,\n pathGlob,\n pattern,\n range,\n regex,\n shlex,\n subset,\n under,\n urlPattern,\n urlSafe,\n wildcard,\n} from \"./constraints.ts\";\n\nexport { memoryNonceStore } from \"./nonce.ts\";\n\nexport { Session as SessionHandle, isSession } from \"./session.ts\";\n\nexport { ConnectToken, parseConnectToken } from \"./connect.ts\";\nexport type { ResolveEndpointOptions } from \"./connect.ts\";\n\nexport { HolderIdentity, generateIdentity, identityFromKey } from \"./identity.ts\";\n\nexport { Runtime } from \"./runtime.ts\";\nexport type { ReceiptsMode, RuntimeOptions, SessionWarrant } from \"./runtime.ts\";\n\nexport { createTenuo } from \"./client.ts\";\n"]}
|
package/dist/mcp.js
CHANGED
|
@@ -11,19 +11,19 @@ export function presentCall(context, decide, session, name, args, options, label
|
|
|
11
11
|
assertKnownKeys(options, ATTACH_OPTION_KEYS, `${label} options`);
|
|
12
12
|
}
|
|
13
13
|
const native = nativeSession(session);
|
|
14
|
-
const
|
|
15
|
-
const local = context.authorize(native, name,
|
|
14
|
+
const snapshot = captureArguments(args);
|
|
15
|
+
const local = context.authorize(native, name, snapshot.json, options?.approvals, undefined, options?.requestId);
|
|
16
16
|
emitReceipt(options?.onReceipt, local.receipt, session, host);
|
|
17
|
-
decide(local, name, native,
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
return { presented
|
|
17
|
+
decide(local, name, native, snapshot.executionArgs());
|
|
18
|
+
const envelope = context.signMeta(native, name, snapshot.json, Math.floor(Date.now() / 1000), options?.approvals ?? null);
|
|
19
|
+
const presented = {
|
|
20
|
+
warrant: envelope.warrant,
|
|
21
|
+
signature: envelope.signature,
|
|
22
|
+
...(envelope.approvals !== undefined && envelope.approvals.length > 0
|
|
23
|
+
? { approvals: envelope.approvals }
|
|
24
|
+
: {}),
|
|
25
|
+
};
|
|
26
|
+
return { presented, wireArgs: snapshot.executionArgs() };
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Transport-agnostic half of `verify`: check a presented warrant chain and
|
|
@@ -38,7 +38,10 @@ export async function verifyPresented(context, decide, presented, name, args, op
|
|
|
38
38
|
if (envelope === undefined) {
|
|
39
39
|
throw new TenuoConfigurationError(`${label} needs { warrant, signature } from tenuo.present() or tenuo.mcp.attach()`);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
// Capture once: replay admission and receipt callbacks may yield or mutate
|
|
42
|
+
// the caller's object. Execution must use the exact snapshot verified here.
|
|
43
|
+
const snapshot = captureArguments(args);
|
|
44
|
+
const decision = context.authorizePresented(envelope.warrant, name, snapshot.json, envelope.signature, envelope.approvals, options?.allow, options?.requestId);
|
|
42
45
|
// Rust already signed this envelope. Emit before the nonce store can
|
|
43
46
|
// refuse a replay — otherwise an attacker leaves no audit artifact.
|
|
44
47
|
emitReceipt(options?.onReceipt, decision.receipt, undefined, host);
|
|
@@ -46,7 +49,9 @@ export async function verifyPresented(context, decide, presented, name, args, op
|
|
|
46
49
|
await admitPop(options?.nonceStore, envelope.signature, options?.onNonceStoreError);
|
|
47
50
|
}
|
|
48
51
|
decide(decision, name);
|
|
49
|
-
|
|
52
|
+
// Run the host parse of the JSON text the proof covers. The raw host
|
|
53
|
+
// object can contain values that JSON drops.
|
|
54
|
+
return snapshot.executionArgs();
|
|
50
55
|
}
|
|
51
56
|
export function createMcp(context, decide, host) {
|
|
52
57
|
const mcp = {
|
|
@@ -83,22 +88,16 @@ export function createMcp(context, decide, host) {
|
|
|
83
88
|
};
|
|
84
89
|
return mcp;
|
|
85
90
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const wire = session.toStackWire();
|
|
92
|
-
if (typeof wire !== "string" || wire.length === 0) {
|
|
93
|
-
throw new TenuoConfigurationError("toStackWire() did not return a warrant stack");
|
|
94
|
-
}
|
|
95
|
-
return wire;
|
|
96
|
-
}
|
|
97
|
-
function approvalWire(value) {
|
|
98
|
-
if (typeof value === "string") {
|
|
99
|
-
return value;
|
|
91
|
+
/** A private immutable input snapshot, not an authorization token. */
|
|
92
|
+
function captureArguments(args) {
|
|
93
|
+
const text = JSON.stringify(args);
|
|
94
|
+
if (typeof text !== "string") {
|
|
95
|
+
throw new TenuoConfigurationError("arguments must be JSON");
|
|
100
96
|
}
|
|
101
|
-
return
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
json: text,
|
|
99
|
+
executionArgs: () => plainArgs(JSON.parse(text)),
|
|
100
|
+
});
|
|
102
101
|
}
|
|
103
102
|
function presentedEnvelope(value) {
|
|
104
103
|
if (value === null || typeof value !== "object") {
|
|
@@ -111,9 +110,11 @@ function presentedEnvelope(value) {
|
|
|
111
110
|
if (typeof tenuo.signature !== "string" || tenuo.signature.length === 0) {
|
|
112
111
|
return undefined;
|
|
113
112
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
if (tenuo.approvals != null &&
|
|
114
|
+
(!Array.isArray(tenuo.approvals) || tenuo.approvals.some((item) => typeof item !== "string"))) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
const approvals = tenuo.approvals ?? undefined;
|
|
117
118
|
return {
|
|
118
119
|
warrant: tenuo.warrant,
|
|
119
120
|
signature: tenuo.signature,
|
|
@@ -138,8 +139,6 @@ const VERIFY_OPTION_KEYS = new Set([
|
|
|
138
139
|
]);
|
|
139
140
|
const REPLAY_STORE_UNAVAILABLE = "Replay store unavailable";
|
|
140
141
|
const ATTACH_OPTION_KEYS = new Set(["approvals", "onReceipt", "requestId"]);
|
|
141
|
-
const MAX_STRIP_DEPTH = 32;
|
|
142
|
-
const MAX_STRIP_LEN = 1024;
|
|
143
142
|
function assertKnownKeys(value, known, label) {
|
|
144
143
|
const unknown = Object.keys(value).filter((key) => !known.has(key));
|
|
145
144
|
if (unknown[0] !== undefined) {
|
|
@@ -223,37 +222,6 @@ function plainArgs(value) {
|
|
|
223
222
|
}
|
|
224
223
|
return {};
|
|
225
224
|
}
|
|
226
|
-
/** Same rule as Python `strip_none_values`: drop null so optional MCP args do not break PoP. */
|
|
227
|
-
function stripNulls(args) {
|
|
228
|
-
const keys = Object.keys(args);
|
|
229
|
-
if (keys.length > MAX_STRIP_LEN) {
|
|
230
|
-
throw new TenuoConfigurationError("arguments exceed the TypeScript input budget");
|
|
231
|
-
}
|
|
232
|
-
const out = {};
|
|
233
|
-
for (const [key, value] of Object.entries(args)) {
|
|
234
|
-
if (value === null || value === undefined) {
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
out[key] = Array.isArray(value) ? cleanList(value, 1) : value;
|
|
238
|
-
}
|
|
239
|
-
return out;
|
|
240
|
-
}
|
|
241
|
-
function cleanList(value, depth) {
|
|
242
|
-
if (depth > MAX_STRIP_DEPTH) {
|
|
243
|
-
throw new TenuoConfigurationError("arguments exceed the TypeScript nesting budget");
|
|
244
|
-
}
|
|
245
|
-
if (value.length > MAX_STRIP_LEN) {
|
|
246
|
-
throw new TenuoConfigurationError("arguments exceed the TypeScript input budget");
|
|
247
|
-
}
|
|
248
|
-
const cleaned = [];
|
|
249
|
-
for (const item of value) {
|
|
250
|
-
if (item === null || item === undefined) {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
cleaned.push(Array.isArray(item) ? cleanList(item, depth + 1) : item);
|
|
254
|
-
}
|
|
255
|
-
return cleaned;
|
|
256
|
-
}
|
|
257
225
|
function jsonRpcError(error) {
|
|
258
226
|
if (error instanceof TenuoError) {
|
|
259
227
|
const code = error.code;
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n PresentedCall,\n Session as SessionContract,\n TenuoErrorCode,\n TenuoMcp,\n} from \"./api.ts\";\nimport { AuthorizationDeniedError, TenuoConfigurationError, TenuoError } from \"./errors.ts\";\nimport { collectReceipt, emitIsolatedReceipt } from \"./receipts.ts\";\nimport { nativeSession, type Session } from \"./session.ts\";\nimport type { WasmContext, WasmDecision } from \"./wasm.ts\";\n\nexport type Decide = (decision: WasmDecision, tool: string, native?: object, args?: Record<string, unknown>) => void;\n\n/**\n * Transport-agnostic half of `attach`: authorize locally, then sign\n * proof-of-possession over the call. Shared by `tenuo.present()` and\n * `tenuo.mcp.attach()`.\n */\nexport function presentCall(\n context: WasmContext,\n decide: Decide,\n session: SessionContract,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpAttachOptions | undefined,\n label: string,\n host?: object,\n): { readonly presented: PresentedCall; readonly wireArgs: Record<string, unknown> } {\n if (options !== undefined) {\n assertKnownKeys(options, ATTACH_OPTION_KEYS, `${label} options`);\n }\n const native = nativeSession(session as Session);\n const wireArgs = stripNulls(args);\n const local = context.authorize(\n native,\n name,\n wireArgs,\n options?.approvals,\n undefined,\n options?.requestId,\n );\n emitReceipt(options?.onReceipt, local.receipt, session, host);\n decide(local, name, native, wireArgs);\n const signature = context.signPop(native, name, wireArgs);\n const warrant = stackWire(native);\n if (options?.approvals !== undefined && options.approvals.length > 0) {\n return {\n presented: { warrant, signature, approvals: options.approvals.map(approvalWire) },\n wireArgs,\n };\n }\n return { presented: { warrant, signature }, wireArgs };\n}\n\n/**\n * Transport-agnostic half of `verify`: check a presented warrant chain and\n * proof-of-possession, then apply the host ceiling. Shared by\n * `tenuo.verify()` and `tenuo.mcp.verify()`.\n */\nexport async function verifyPresented(\n context: WasmContext,\n decide: Decide,\n presented: PresentedCall,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpVerifyOptions | undefined,\n label: string,\n host?: object,\n): Promise<Readonly<Record<string, unknown>>> {\n if (options !== undefined) {\n assertKnownKeys(options, VERIFY_OPTION_KEYS, `${label} options`);\n }\n const envelope = presentedEnvelope(presented);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n `${label} needs { warrant, signature } from tenuo.present() or tenuo.mcp.attach()`,\n );\n }\n const decision = context.authorizePresented(\n envelope.warrant,\n name,\n stripNulls(args),\n envelope.signature,\n envelope.approvals,\n options?.allow,\n options?.requestId,\n );\n // Rust already signed this envelope. Emit before the nonce store can\n // refuse a replay — otherwise an attacker leaves no audit artifact.\n emitReceipt(options?.onReceipt, decision.receipt, undefined, host);\n if (decision.outcome === \"allow\") {\n await admitPop(options?.nonceStore, envelope.signature, options?.onNonceStoreError);\n }\n decide(decision, name);\n return plainArgs(decision.args);\n}\n\nexport function createMcp(context: WasmContext, decide: Decide, host?: object): TenuoMcp {\n const mcp: TenuoMcp = {\n attach(session, name, args, options) {\n const { presented, wireArgs } = presentCall(\n context,\n decide,\n session,\n name,\n args,\n options,\n \"mcp.attach()\",\n host,\n );\n return { name, arguments: wireArgs, _meta: { tenuo: presented } };\n },\n\n async verify(name, args, meta, options) {\n const envelope = tenuoEnvelope(meta);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n \"MCP call is missing _meta.tenuo. The client must call tenuo.mcp.attach().\",\n );\n }\n return verifyPresented(context, decide, envelope, name, args, options, \"mcp.verify()\", host);\n },\n\n handler(name, policyOrExecute, maybeExecute?) {\n const policy = isHandlerPolicy(policyOrExecute) ? policyOrExecute : undefined;\n const execute = isHandlerPolicy(policyOrExecute) ? maybeExecute : policyOrExecute;\n if (typeof execute !== \"function\") {\n throw new TenuoConfigurationError(\"mcp.handler() requires an execute function\");\n }\n return async (args, extra) => {\n const authorized = await mcp.verify(name, args, extra?._meta ?? extra?.meta, {\n ...(policy?.allow !== undefined ? { allow: policy.allow } : {}),\n ...(policy?.onReceipt !== undefined ? { onReceipt: policy.onReceipt } : {}),\n ...(policy?.nonceStore !== undefined ? { nonceStore: policy.nonceStore } : {}),\n ...(policy?.onNonceStoreError !== undefined ? { onNonceStoreError: policy.onNonceStoreError } : {}),\n });\n return execute(authorized as never);\n };\n },\n\n jsonRpcError(error) {\n return jsonRpcError(error);\n },\n };\n return mcp;\n}\n\nfunction stackWire(native: object): string {\n const session = native as { toStackWire?: () => unknown };\n if (typeof session.toStackWire !== \"function\") {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n const wire = session.toStackWire();\n if (typeof wire !== \"string\" || wire.length === 0) {\n throw new TenuoConfigurationError(\"toStackWire() did not return a warrant stack\");\n }\n return wire;\n}\n\nfunction approvalWire(value: string | Uint8Array): string {\n if (typeof value === \"string\") {\n return value;\n }\n return Buffer.from(value).toString(\"base64\");\n}\n\nfunction presentedEnvelope(\n value: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (value === null || typeof value !== \"object\") {\n return undefined;\n }\n const tenuo = value as { warrant?: unknown; signature?: unknown; approvals?: unknown };\n if (typeof tenuo.warrant !== \"string\" || tenuo.warrant.length === 0) {\n return undefined;\n }\n if (typeof tenuo.signature !== \"string\" || tenuo.signature.length === 0) {\n return undefined;\n }\n const approvals = Array.isArray(tenuo.approvals)\n ? tenuo.approvals.filter((item): item is string => typeof item === \"string\")\n : undefined;\n return {\n warrant: tenuo.warrant,\n signature: tenuo.signature,\n ...(approvals !== undefined && approvals.length > 0 ? { approvals } : {}),\n };\n}\n\nfunction tenuoEnvelope(\n meta: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (meta === null || typeof meta !== \"object\") {\n return undefined;\n }\n const root = meta as { tenuo?: unknown; _meta?: unknown };\n const block = root.tenuo ?? (root._meta as { tenuo?: unknown } | undefined)?.tenuo;\n return presentedEnvelope(block);\n}\n\nconst HANDLER_POLICY_KEYS = new Set([\"allow\", \"onReceipt\", \"nonceStore\", \"onNonceStoreError\"]);\nconst VERIFY_OPTION_KEYS = new Set([\n \"allow\",\n \"onReceipt\",\n \"nonceStore\",\n \"onNonceStoreError\",\n \"requestId\",\n]);\nconst REPLAY_STORE_UNAVAILABLE = \"Replay store unavailable\";\nconst ATTACH_OPTION_KEYS = new Set([\"approvals\", \"onReceipt\", \"requestId\"]);\nconst MAX_STRIP_DEPTH = 32;\nconst MAX_STRIP_LEN = 1024;\n\nfunction assertKnownKeys(value: object, known: ReadonlySet<string>, label: string): void {\n const unknown = Object.keys(value).filter((key) => !known.has(key));\n if (unknown[0] !== undefined) {\n throw new TenuoConfigurationError(\n `${label} has unknown key '${unknown[0]}'. Use ${[...known].join(\", \")}.`,\n );\n }\n}\n\nfunction isHandlerPolicy(value: unknown): value is McpHandlerPolicy {\n if (value === null || typeof value !== \"object\" || Array.isArray(value) || typeof value === \"function\") {\n return false;\n }\n const keys = Object.keys(value);\n assertKnownKeys(value, HANDLER_POLICY_KEYS, \"mcp.handler() policy\");\n const record = value as {\n allow?: unknown;\n onReceipt?: unknown;\n nonceStore?: unknown;\n onNonceStoreError?: unknown;\n };\n const hasAllow =\n \"allow\" in record && record.allow !== null && typeof record.allow === \"object\" && !Array.isArray(record.allow);\n const hasReceipt = \"onReceipt\" in record && typeof record.onReceipt === \"function\";\n const hasNonce = \"nonceStore\" in record && record.nonceStore !== null && typeof record.nonceStore === \"object\";\n const hasStoreError = \"onNonceStoreError\" in record && typeof record.onNonceStoreError === \"function\";\n return keys.length === 0 || hasAllow || hasReceipt || hasNonce || hasStoreError;\n}\n\nasync function admitPop(\n store: { checkAndRecord(popSignature: string): boolean | Promise<boolean> } | undefined,\n signature: string,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): Promise<void> {\n if (store === undefined) {\n return;\n }\n let admitted: boolean | Promise<boolean>;\n try {\n admitted = store.checkAndRecord(signature);\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n if (isThenable(admitted)) {\n try {\n admitted = await admitted;\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n }\n if (typeof admitted !== \"boolean\") {\n throw replayStoreUnavailable(\n new TenuoConfigurationError(\"NonceStore.checkAndRecord() must return boolean or Promise<boolean>\"),\n onNonceStoreError,\n );\n }\n if (!admitted) {\n throw new AuthorizationDeniedError(\n \"TENUO_INVALID_POP\",\n \"PoP replay detected — this exact authorization token was already consumed.\",\n );\n }\n}\n\nfunction replayStoreUnavailable(\n cause: unknown,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): TenuoConfigurationError {\n emitIsolated(onNonceStoreError, cause);\n const error = new TenuoConfigurationError(REPLAY_STORE_UNAVAILABLE);\n if (cause !== undefined) {\n error.cause = cause;\n }\n return error;\n}\n\nfunction emitIsolated(\n hook: ((error: unknown) => void | Promise<void>) | undefined,\n error: unknown,\n): void {\n if (hook === undefined) {\n return;\n }\n try {\n const result = hook(error);\n if (isThenable(result)) {\n void Promise.resolve(result).catch(() => undefined);\n }\n } catch {\n // Isolated — must not change the public error.\n }\n}\n\nfunction isThenable(value: unknown): value is Promise<boolean> {\n return value !== null && typeof value === \"object\" && \"then\" in value && typeof value.then === \"function\";\n}\n\nfunction emitReceipt(\n onReceipt: ((receipt: string) => void | Promise<void>) | undefined,\n receipt: string | undefined,\n session?: SessionContract,\n host?: object,\n): void {\n collectReceipt(receipt, session, host);\n emitIsolatedReceipt(onReceipt, receipt);\n}\n\nfunction plainArgs(value: unknown): Record<string, unknown> {\n if (value instanceof Map) {\n return Object.fromEntries(value);\n }\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return { ...(value as Record<string, unknown>) };\n }\n return {};\n}\n\n/** Same rule as Python `strip_none_values`: drop null so optional MCP args do not break PoP. */\nfunction stripNulls(args: Readonly<Record<string, unknown>>): Record<string, unknown> {\n const keys = Object.keys(args);\n if (keys.length > MAX_STRIP_LEN) {\n throw new TenuoConfigurationError(\"arguments exceed the TypeScript input budget\");\n }\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(args)) {\n if (value === null || value === undefined) {\n continue;\n }\n out[key] = Array.isArray(value) ? cleanList(value, 1) : value;\n }\n return out;\n}\n\nfunction cleanList(value: readonly unknown[], depth: number): unknown[] {\n if (depth > MAX_STRIP_DEPTH) {\n throw new TenuoConfigurationError(\"arguments exceed the TypeScript nesting budget\");\n }\n if (value.length > MAX_STRIP_LEN) {\n throw new TenuoConfigurationError(\"arguments exceed the TypeScript input budget\");\n }\n const cleaned: unknown[] = [];\n for (const item of value) {\n if (item === null || item === undefined) {\n continue;\n }\n cleaned.push(Array.isArray(item) ? cleanList(item, depth + 1) : item);\n }\n return cleaned;\n}\n\nfunction jsonRpcError(error: unknown): McpJsonRpcError {\n if (error instanceof TenuoError) {\n const code: TenuoErrorCode = error.code;\n const rpc = code === \"TENUO_APPROVAL_REQUIRED\" ? -32002 : code === \"TENUO_CANONICALIZATION\" ? -32602 : -32001;\n return { code: rpc, message: error.message, data: { tenuo: { code } } };\n }\n const message = error instanceof Error ? error.message : String(error);\n return { code: -32001, message };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n PresentedCall,\n Session as SessionContract,\n TenuoErrorCode,\n TenuoMcp,\n} from \"./api.ts\";\nimport { AuthorizationDeniedError, TenuoConfigurationError, TenuoError } from \"./errors.ts\";\nimport { collectReceipt, emitIsolatedReceipt } from \"./receipts.ts\";\nimport { nativeSession, type Session } from \"./session.ts\";\nimport type { WasmContext, WasmDecision } from \"./wasm.ts\";\n\nexport type Decide = (decision: WasmDecision, tool: string, native?: object, args?: Record<string, unknown>) => void;\n\n/**\n * Transport-agnostic half of `attach`: authorize locally, then sign\n * proof-of-possession over the call. Shared by `tenuo.present()` and\n * `tenuo.mcp.attach()`.\n */\nexport function presentCall(\n context: WasmContext,\n decide: Decide,\n session: SessionContract,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpAttachOptions | undefined,\n label: string,\n host?: object,\n): { readonly presented: PresentedCall; readonly wireArgs: Record<string, unknown> } {\n if (options !== undefined) {\n assertKnownKeys(options, ATTACH_OPTION_KEYS, `${label} options`);\n }\n const native = nativeSession(session as Session);\n const snapshot = captureArguments(args);\n const local = context.authorize(\n native,\n name,\n snapshot.json,\n options?.approvals,\n undefined,\n options?.requestId,\n );\n emitReceipt(options?.onReceipt, local.receipt, session, host);\n decide(local, name, native, snapshot.executionArgs());\n const envelope = context.signMeta(\n native,\n name,\n snapshot.json,\n Math.floor(Date.now() / 1000),\n options?.approvals ?? null,\n );\n const presented = {\n warrant: envelope.warrant,\n signature: envelope.signature,\n ...(envelope.approvals !== undefined && envelope.approvals.length > 0\n ? { approvals: envelope.approvals }\n : {}),\n };\n return { presented, wireArgs: snapshot.executionArgs() };\n}\n\n/**\n * Transport-agnostic half of `verify`: check a presented warrant chain and\n * proof-of-possession, then apply the host ceiling. Shared by\n * `tenuo.verify()` and `tenuo.mcp.verify()`.\n */\nexport async function verifyPresented(\n context: WasmContext,\n decide: Decide,\n presented: PresentedCall,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpVerifyOptions | undefined,\n label: string,\n host?: object,\n): Promise<Readonly<Record<string, unknown>>> {\n if (options !== undefined) {\n assertKnownKeys(options, VERIFY_OPTION_KEYS, `${label} options`);\n }\n const envelope = presentedEnvelope(presented);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n `${label} needs { warrant, signature } from tenuo.present() or tenuo.mcp.attach()`,\n );\n }\n // Capture once: replay admission and receipt callbacks may yield or mutate\n // the caller's object. Execution must use the exact snapshot verified here.\n const snapshot = captureArguments(args);\n const decision = context.authorizePresented(\n envelope.warrant,\n name,\n snapshot.json,\n envelope.signature,\n envelope.approvals,\n options?.allow,\n options?.requestId,\n );\n // Rust already signed this envelope. Emit before the nonce store can\n // refuse a replay — otherwise an attacker leaves no audit artifact.\n emitReceipt(options?.onReceipt, decision.receipt, undefined, host);\n if (decision.outcome === \"allow\") {\n await admitPop(options?.nonceStore, envelope.signature, options?.onNonceStoreError);\n }\n decide(decision, name);\n // Run the host parse of the JSON text the proof covers. The raw host\n // object can contain values that JSON drops.\n return snapshot.executionArgs();\n}\n\nexport function createMcp(context: WasmContext, decide: Decide, host?: object): TenuoMcp {\n const mcp: TenuoMcp = {\n attach(session, name, args, options) {\n const { presented, wireArgs } = presentCall(\n context,\n decide,\n session,\n name,\n args,\n options,\n \"mcp.attach()\",\n host,\n );\n return { name, arguments: wireArgs, _meta: { tenuo: presented } };\n },\n\n async verify(name, args, meta, options) {\n const envelope = tenuoEnvelope(meta);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n \"MCP call is missing _meta.tenuo. The client must call tenuo.mcp.attach().\",\n );\n }\n return verifyPresented(context, decide, envelope, name, args, options, \"mcp.verify()\", host);\n },\n\n handler(name, policyOrExecute, maybeExecute?) {\n const policy = isHandlerPolicy(policyOrExecute) ? policyOrExecute : undefined;\n const execute = isHandlerPolicy(policyOrExecute) ? maybeExecute : policyOrExecute;\n if (typeof execute !== \"function\") {\n throw new TenuoConfigurationError(\"mcp.handler() requires an execute function\");\n }\n return async (args, extra) => {\n const authorized = await mcp.verify(name, args, extra?._meta ?? extra?.meta, {\n ...(policy?.allow !== undefined ? { allow: policy.allow } : {}),\n ...(policy?.onReceipt !== undefined ? { onReceipt: policy.onReceipt } : {}),\n ...(policy?.nonceStore !== undefined ? { nonceStore: policy.nonceStore } : {}),\n ...(policy?.onNonceStoreError !== undefined ? { onNonceStoreError: policy.onNonceStoreError } : {}),\n });\n return execute(authorized as never);\n };\n },\n\n jsonRpcError(error) {\n return jsonRpcError(error);\n },\n };\n return mcp;\n}\n\n/** A private immutable input snapshot, not an authorization token. */\nfunction captureArguments(args: Readonly<Record<string, unknown>>): {\n readonly json: string;\n executionArgs(): Record<string, unknown>;\n} {\n const text = JSON.stringify(args);\n if (typeof text !== \"string\") {\n throw new TenuoConfigurationError(\"arguments must be JSON\");\n }\n return Object.freeze({\n json: text,\n executionArgs: () => plainArgs(JSON.parse(text)),\n });\n}\n\nfunction presentedEnvelope(\n value: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (value === null || typeof value !== \"object\") {\n return undefined;\n }\n const tenuo = value as { warrant?: unknown; signature?: unknown; approvals?: unknown };\n if (typeof tenuo.warrant !== \"string\" || tenuo.warrant.length === 0) {\n return undefined;\n }\n if (typeof tenuo.signature !== \"string\" || tenuo.signature.length === 0) {\n return undefined;\n }\n if (tenuo.approvals != null &&\n (!Array.isArray(tenuo.approvals) || tenuo.approvals.some((item) => typeof item !== \"string\"))) {\n return undefined;\n }\n const approvals = tenuo.approvals ?? undefined;\n return {\n warrant: tenuo.warrant,\n signature: tenuo.signature,\n ...(approvals !== undefined && approvals.length > 0 ? { approvals } : {}),\n };\n}\n\nfunction tenuoEnvelope(\n meta: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (meta === null || typeof meta !== \"object\") {\n return undefined;\n }\n const root = meta as { tenuo?: unknown; _meta?: unknown };\n const block = root.tenuo ?? (root._meta as { tenuo?: unknown } | undefined)?.tenuo;\n return presentedEnvelope(block);\n}\n\nconst HANDLER_POLICY_KEYS = new Set([\"allow\", \"onReceipt\", \"nonceStore\", \"onNonceStoreError\"]);\nconst VERIFY_OPTION_KEYS = new Set([\n \"allow\",\n \"onReceipt\",\n \"nonceStore\",\n \"onNonceStoreError\",\n \"requestId\",\n]);\nconst REPLAY_STORE_UNAVAILABLE = \"Replay store unavailable\";\nconst ATTACH_OPTION_KEYS = new Set([\"approvals\", \"onReceipt\", \"requestId\"]);\nfunction assertKnownKeys(value: object, known: ReadonlySet<string>, label: string): void {\n const unknown = Object.keys(value).filter((key) => !known.has(key));\n if (unknown[0] !== undefined) {\n throw new TenuoConfigurationError(\n `${label} has unknown key '${unknown[0]}'. Use ${[...known].join(\", \")}.`,\n );\n }\n}\n\nfunction isHandlerPolicy(value: unknown): value is McpHandlerPolicy {\n if (value === null || typeof value !== \"object\" || Array.isArray(value) || typeof value === \"function\") {\n return false;\n }\n const keys = Object.keys(value);\n assertKnownKeys(value, HANDLER_POLICY_KEYS, \"mcp.handler() policy\");\n const record = value as {\n allow?: unknown;\n onReceipt?: unknown;\n nonceStore?: unknown;\n onNonceStoreError?: unknown;\n };\n const hasAllow =\n \"allow\" in record && record.allow !== null && typeof record.allow === \"object\" && !Array.isArray(record.allow);\n const hasReceipt = \"onReceipt\" in record && typeof record.onReceipt === \"function\";\n const hasNonce = \"nonceStore\" in record && record.nonceStore !== null && typeof record.nonceStore === \"object\";\n const hasStoreError = \"onNonceStoreError\" in record && typeof record.onNonceStoreError === \"function\";\n return keys.length === 0 || hasAllow || hasReceipt || hasNonce || hasStoreError;\n}\n\nasync function admitPop(\n store: { checkAndRecord(popSignature: string): boolean | Promise<boolean> } | undefined,\n signature: string,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): Promise<void> {\n if (store === undefined) {\n return;\n }\n let admitted: boolean | Promise<boolean>;\n try {\n admitted = store.checkAndRecord(signature);\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n if (isThenable(admitted)) {\n try {\n admitted = await admitted;\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n }\n if (typeof admitted !== \"boolean\") {\n throw replayStoreUnavailable(\n new TenuoConfigurationError(\"NonceStore.checkAndRecord() must return boolean or Promise<boolean>\"),\n onNonceStoreError,\n );\n }\n if (!admitted) {\n throw new AuthorizationDeniedError(\n \"TENUO_INVALID_POP\",\n \"PoP replay detected — this exact authorization token was already consumed.\",\n );\n }\n}\n\nfunction replayStoreUnavailable(\n cause: unknown,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): TenuoConfigurationError {\n emitIsolated(onNonceStoreError, cause);\n const error = new TenuoConfigurationError(REPLAY_STORE_UNAVAILABLE);\n if (cause !== undefined) {\n error.cause = cause;\n }\n return error;\n}\n\nfunction emitIsolated(\n hook: ((error: unknown) => void | Promise<void>) | undefined,\n error: unknown,\n): void {\n if (hook === undefined) {\n return;\n }\n try {\n const result = hook(error);\n if (isThenable(result)) {\n void Promise.resolve(result).catch(() => undefined);\n }\n } catch {\n // Isolated — must not change the public error.\n }\n}\n\nfunction isThenable(value: unknown): value is Promise<boolean> {\n return value !== null && typeof value === \"object\" && \"then\" in value && typeof value.then === \"function\";\n}\n\nfunction emitReceipt(\n onReceipt: ((receipt: string) => void | Promise<void>) | undefined,\n receipt: string | undefined,\n session?: SessionContract,\n host?: object,\n): void {\n collectReceipt(receipt, session, host);\n emitIsolatedReceipt(onReceipt, receipt);\n}\n\nfunction plainArgs(value: unknown): Record<string, unknown> {\n if (value instanceof Map) {\n return Object.fromEntries(value);\n }\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return { ...(value as Record<string, unknown>) };\n }\n return {};\n}\n\nfunction jsonRpcError(error: unknown): McpJsonRpcError {\n if (error instanceof TenuoError) {\n const code: TenuoErrorCode = error.code;\n const rpc = code === \"TENUO_APPROVAL_REQUIRED\" ? -32002 : code === \"TENUO_CANONICALIZATION\" ? -32602 : -32001;\n return { code: rpc, message: error.message, data: { tenuo: { code } } };\n }\n const message = error instanceof Error ? error.message : String(error);\n return { code: -32001, message };\n}\n"]}
|
package/dist/wasm.d.ts
CHANGED
|
@@ -223,6 +223,15 @@ export type WasmContext = {
|
|
|
223
223
|
loadRevocationList(srl: string): void;
|
|
224
224
|
signRevocationList(ids: string[]): string;
|
|
225
225
|
signPop(session: WasmSession, tool: string, args: unknown): string;
|
|
226
|
+
signMeta(session: WasmSession, tool: string, argsJson: string, timestamp: number, approvals?: unknown): {
|
|
227
|
+
warrant: string;
|
|
228
|
+
signature: string;
|
|
229
|
+
approvals?: string[];
|
|
230
|
+
};
|
|
231
|
+
/** Holder proof only; does not authorize execution. */
|
|
232
|
+
verifyMetaPop(warrant: string, signature: string, tool: string, argsJson: string, timestamp: number): boolean;
|
|
233
|
+
/** Compatibility alias for verifyMetaPop. */
|
|
234
|
+
verifyMeta(warrant: string, signature: string, tool: string, argsJson: string, timestamp: number): boolean;
|
|
226
235
|
authorizePresented(warrants: unknown, tool: string, args: unknown, pop: string, approvals?: unknown, toolAllow?: unknown, requestId?: string): WasmDecision;
|
|
227
236
|
};
|
|
228
237
|
type Generated = {
|
|
@@ -260,6 +269,7 @@ type Generated = {
|
|
|
260
269
|
tool: string;
|
|
261
270
|
error?: string;
|
|
262
271
|
};
|
|
272
|
+
parse_strict_json(jsonText: string): void;
|
|
263
273
|
};
|
|
264
274
|
export declare function wasmAvailable(): boolean;
|
|
265
275
|
export declare function loadWasm(): Generated;
|
|
@@ -291,6 +301,15 @@ export type ApprovalGateInspection = {
|
|
|
291
301
|
arguments: string[];
|
|
292
302
|
message?: string;
|
|
293
303
|
};
|
|
304
|
+
/**
|
|
305
|
+
* Parse JSON text and reject a repeated key in any object.
|
|
306
|
+
*
|
|
307
|
+
* `verify()` receives an object the host already parsed. A duplicate key is
|
|
308
|
+
* gone by then. Call this while the text is still available. After the check,
|
|
309
|
+
* the value is `JSON.parse` of the original text, so each key is an own data
|
|
310
|
+
* property and `null` stays `null`.
|
|
311
|
+
*/
|
|
312
|
+
export declare function parseStrictJson(text: string): unknown;
|
|
294
313
|
/**
|
|
295
314
|
* Typed preflight of a warrant's approval gates for `(tool, args)`.
|
|
296
315
|
*
|
package/dist/wasm.js
CHANGED
|
@@ -91,6 +91,18 @@ function throwIfGateError(error) {
|
|
|
91
91
|
throw new TenuoConfigurationError(error);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse JSON text and reject a repeated key in any object.
|
|
96
|
+
*
|
|
97
|
+
* `verify()` receives an object the host already parsed. A duplicate key is
|
|
98
|
+
* gone by then. Call this while the text is still available. After the check,
|
|
99
|
+
* the value is `JSON.parse` of the original text, so each key is an own data
|
|
100
|
+
* property and `null` stays `null`.
|
|
101
|
+
*/
|
|
102
|
+
export function parseStrictJson(text) {
|
|
103
|
+
loadWasm().parse_strict_json(text);
|
|
104
|
+
return JSON.parse(text);
|
|
105
|
+
}
|
|
94
106
|
/**
|
|
95
107
|
* Typed preflight of a warrant's approval gates for `(tool, args)`.
|
|
96
108
|
*
|
package/dist/wasm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasm.js","sourceRoot":"","sources":["../src/wasm.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from \"node:module\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { TenuoConfigurationError } from \"./errors.ts\";\nimport type { TenuoErrorCode } from \"./api.ts\";\n\nexport type WasmDecision = {\n outcome: \"allow\" | \"deny\" | \"approval_required\";\n code?: TenuoErrorCode;\n field?: string;\n message?: string;\n args?: Record<string, unknown>;\n tool?: string;\n required?: number;\n received?: number;\n receipt?: string;\n};\n\nexport type WasmInspect = {\n payload_hex: string;\n signature_hex: string;\n id: string;\n};\n\nexport type WasmReceipt = {\n authentic: true;\n /** Hex key the receipt is signed under — resolve against your authorizer set. */\n signer_key: string;\n outcome: \"allow\" | \"deny\";\n action: string;\n decision_code?: string;\n request_id: string;\n /** Version of the revocation list in force, when it carried one. */\n srl_version?: number;\n /**\n * SHA-256 (hex) of the revocation list bytes in force at decision time.\n * Absent means no revocation data was loaded — a different claim from a\n * loaded list that revoked nothing.\n */\n srl_hash?: string;\n /**\n * SHA-256 (hex) commitment to the canonical invocation this decision was\n * made over. Absent when the arguments could not be canonicalized — never\n * zero-filled, so absence is a claim rather than a value.\n */\n request_hash?: string;\n /** SHA-256 (hex) of the host ceiling applied to this decision. */\n policy_definition_hash?: string;\n /**\n * SHA-256 (hex) of the previous receipt from this signer. Absent on the\n * first receipt, or when the deployment does not chain. A broken link means\n * a receipt was removed from the stream.\n */\n prev_receipt_hash?: string;\n /** SHA-256 (hex) of the trusted root set in force at decision time. */\n trusted_roots_hash?: string;\n};\n\nexport type WasmReceiptChain = {\n signer_key: string;\n outcome: \"allow\" | \"deny\";\n decision_code?: string;\n timestamp: number;\n /** The embedded chain verifies to a supplied root at the decision instant. */\n chain_valid: boolean;\n /** Canonical error name when it does not. */\n chain_error?: string;\n /**\n * Deny receipts only: the chain failure matches the stated decision_code,\n * so the embedded authority independently corroborates the refusal.\n */\n corroborates_denial?: boolean;\n root_issuer?: string;\n leaf_holder?: string;\n};\n\nexport type WasmSession = object;\n\n/** Mirrors `SessionInfoDto` in tenuo-wasm. Snake case is the WASM boundary. */\nexport type WasmSessionInfo = {\n kind: \"execution\" | \"issuer\";\n holder_public_key: string;\n root_public_key: string;\n depth: number;\n max_depth: number;\n terminal: boolean;\n expires_at: number;\n tools: string[];\n warrant_ids: string[];\n can_authorize: boolean;\n clearance?: number;\n session_id?: string;\n agent_id?: string;\n issuable_tools?: string[];\n max_issue_depth?: number;\n required_approvers?: string[];\n min_approvals?: number;\n approval_gated_tools: string[];\n};\n\n/** Security limits exported by tenuo-core; TypeScript must not redefine them. */\nexport type WasmProtocolLimits = {\n max_delegation_depth: number;\n max_warrant_ttl_seconds: number;\n};\n\n/** Mirrors `NarrowOptions` in tenuo-wasm. Unknown keys are rejected there. */\nexport type WasmNarrowOptions = {\n holder?: string;\n ttlSeconds?: number;\n terminal?: boolean;\n maxDepth?: number;\n clearance?: number | string;\n agentId?: string;\n addApprovers?: string[];\n minApprovals?: number;\n};\n\nexport type WasmRequireApproval = {\n approvers: string[];\n min: number;\n tools?: string[];\n gates?: Record<string, { message?: string; args?: Record<string, unknown> }>;\n};\n\n/** Mirrors `MintOptions` in tenuo-wasm. */\nexport type WasmMintOptions = {\n kind?: \"execution\" | \"issuer\";\n allow?: unknown;\n ttlSeconds?: number;\n holder?: string;\n maxDepth?: number;\n clearance?: number | string;\n sessionId?: string;\n agentId?: string;\n requireApproval?: WasmRequireApproval;\n issuableTools?: string[];\n constraintBounds?: unknown;\n maxIssueDepth?: number;\n};\n\n/** Mirrors `IssueOptions` in tenuo-wasm. */\nexport type WasmIssueOptions = {\n allow: unknown;\n holder: string;\n ttlSeconds?: number;\n maxDepth?: number;\n clearance?: number | string;\n sessionId?: string;\n agentId?: string;\n requireApproval?: { approvers: string[]; min: number };\n};\n\nexport type WasmExplainField = {\n field: string;\n kind: string;\n constraint: unknown;\n value?: unknown;\n satisfied: boolean;\n reason_code?: string;\n reason?: string;\n};\n\nexport type WasmExplain = {\n tool: string;\n kind: \"execution\" | \"issuer\";\n outcome: \"allow\" | \"deny\";\n code?: TenuoErrorCode;\n field?: string;\n message?: string;\n tool_granted: boolean;\n fields: WasmExplainField[];\n unknown_fields: string[];\n missing_fields: string[];\n expired: boolean;\n expires_at: number;\n chain_valid: boolean;\n chain_error?: TenuoErrorCode;\n};\n\nexport type WasmApprovalRequest = {\n request_id: string;\n warrant_id: string;\n tool: string;\n args: Record<string, unknown>;\n request_hash: string;\n holder_public_key: string;\n required_approvers: string[];\n min_approvals: number;\n warrant_expires_at: number;\n created_at: number;\n message: string;\n};\n\nexport type WasmAttestation = {\n version: number;\n canonicalization: string;\n warrant_id: string;\n tool: string;\n request_hash: string;\n holder_key_hex: string;\n args_canonical_cbor_b64: string;\n signer_key_hex: string;\n signature_b64: string;\n};\n\nexport type WasmApprovalInfo = {\n approver_public_key: string;\n request_hash: string;\n external_id: string;\n approved_at: number;\n expires_at: number;\n expired: boolean;\n signature_valid: boolean;\n error?: string;\n};\n\nexport type WasmSrlInfo = {\n version: number;\n issued_at: number;\n issuer_public_key: string;\n revoked_ids: string[];\n signature_valid: boolean;\n};\n\nexport type WasmContext = {\n withReceiptSigner(secret: Uint8Array): WasmContext;\n mint(\n allow: unknown,\n ttlSeconds: number,\n requireApproval?: unknown,\n holderHex?: string,\n maxDepth?: number,\n ): WasmSession;\n mintExtended(options: WasmMintOptions): WasmSession;\n narrow(session: WasmSession, allow: unknown, options?: WasmNarrowOptions): WasmSession;\n issue(issuer: WasmSession, options: WasmIssueOptions): WasmSession;\n issuerPublicKey(): string;\n explain(session: WasmSession, tool: string, args: unknown): WasmExplain;\n approvalRequest(session: WasmSession, tool: string, args: unknown): WasmApprovalRequest;\n approvalContextAttestation(session: WasmSession, tool: string, args: unknown): WasmAttestation;\n signRevocationListVersioned(ids: string[], version?: number): string;\n authorize(\n session: WasmSession,\n tool: string,\n args: unknown,\n approvals?: unknown,\n toolAllow?: unknown,\n requestId?: string,\n ): WasmDecision;\n authorizeAsOf(\n session: WasmSession,\n tool: string,\n args: unknown,\n asOf: number,\n approvals?: unknown,\n toolAllow?: unknown,\n ): WasmDecision;\n loadRevocationList(srl: string): void;\n signRevocationList(ids: string[]): string;\n signPop(session: WasmSession, tool: string, args: unknown): string;\n authorizePresented(\n warrants: unknown,\n tool: string,\n args: unknown,\n pop: string,\n approvals?: unknown,\n toolAllow?: unknown,\n requestId?: string,\n ): WasmDecision;\n};\n\ntype Generated = {\n SdkContext: {\n new (): WasmContext;\n fromTrustedRoots(roots: string[]): WasmContext;\n fromIssuerSecret(secret: Uint8Array, extraRoots?: string[]): WasmContext;\n };\n SdkSession: {\n fromWire(warrant: string, holder: Uint8Array): WasmSession;\n fromParts(payloadHex: string, signatureHex: string, holder: Uint8Array): WasmSession;\n fromChain(parts: unknown, holder: Uint8Array): WasmSession;\n };\n sdkInspectWarrant(wire: string): WasmInspect;\n sdkInspectParts(payloadHex: string, signatureHex: string): WasmInspect;\n sdkProtocolLimits(): WasmProtocolLimits;\n sdkPublicKeyFromHolderKey(holderSecret: Uint8Array): string;\n sdkSignApproval(\n session: WasmSession,\n tool: string,\n args: unknown,\n approverSecret: Uint8Array,\n externalId: string,\n asOf?: number,\n ): string;\n sdkSignApprovalForRequest(\n requestHashHex: string,\n approverSecret: Uint8Array,\n externalId: string,\n ttlSeconds?: number,\n warrantExpiresAt?: number,\n ): string;\n sdkInspectApproval(envelope: string): WasmApprovalInfo;\n sdkSignRevocationList(ids: string[], issuerSecret: Uint8Array): string;\n sdkSignRevocationListVersioned(ids: string[], version: number | undefined, issuerSecret: Uint8Array): string;\n sdkSignPublishedRevocationList(ids: string[], version: number, issuerSecret: Uint8Array): string;\n sdkInspectRevocationList(wire: string): WasmSrlInfo;\n sdkVerifyReceipt(wire: string): WasmReceipt;\n sdkVerifyReceiptChain(wire: string, roots: string[]): WasmReceiptChain;\n approval_requirement(\n warrantB64: string,\n tool: string,\n args: unknown,\n ): ApprovalRequirement & { error?: string };\n inspect_approval_gate(\n warrantB64: string,\n tool: string,\n ): ApprovalGateInspection & { error?: string };\n evaluate_approval_gates(\n warrantB64: string,\n tool: string,\n args: unknown,\n ): { approval_required: boolean; tool: string; error?: string };\n};\n\nlet loaded: Generated | undefined;\n\nfunction generatedPath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n return join(here, \"generated\", \"tenuo_wasm.js\");\n}\n\nexport function wasmAvailable(): boolean {\n return existsSync(generatedPath());\n}\n\nconst WASM_LOAD_HINT =\n \"WASM core is missing from this @tenuo/core install. @tenuo/core is Node 20+ only (not a bundler target). If this is Next.js/webpack, set serverExternalPackages: ['@tenuo/core']. Do not run wasm-pack in the consuming app.\";\n\nexport function loadWasm(): Generated {\n if (loaded) {\n return loaded;\n }\n const path = generatedPath();\n if (!existsSync(path)) {\n throw new TenuoConfigurationError(WASM_LOAD_HINT, \"TENUO_NOT_READY\");\n }\n try {\n const require = createRequire(import.meta.url);\n loaded = require(path) as Generated;\n return loaded;\n } catch (error) {\n const wrapped = new TenuoConfigurationError(WASM_LOAD_HINT, \"TENUO_NOT_READY\");\n wrapped.cause = error;\n throw wrapped;\n }\n}\n\nexport function createDevContext(receiptSigner?: Uint8Array): WasmContext {\n const { SdkContext } = loadWasm();\n let context: WasmContext = new SdkContext();\n if (receiptSigner !== undefined) {\n context = context.withReceiptSigner(receiptSigner);\n }\n return context;\n}\n\nexport function protocolLimits(): WasmProtocolLimits {\n return loadWasm().sdkProtocolLimits();\n}\n\nexport function createVerifierContext(\n rootHexes: readonly string[],\n revocationList?: string,\n receiptSigner?: Uint8Array,\n): WasmContext {\n const { SdkContext } = loadWasm();\n let context = SdkContext.fromTrustedRoots([...rootHexes]);\n if (receiptSigner !== undefined) {\n context = context.withReceiptSigner(receiptSigner);\n }\n if (revocationList !== undefined && revocationList.length > 0) {\n context.loadRevocationList(revocationList);\n }\n return context;\n}\n\nexport function createIssuerContext(\n secret: Uint8Array,\n extraRootHexes: readonly string[],\n receiptSigner?: Uint8Array,\n): WasmContext {\n const { SdkContext } = loadWasm();\n let context = SdkContext.fromIssuerSecret(secret, [...extraRootHexes]);\n if (receiptSigner !== undefined) {\n context = context.withReceiptSigner(receiptSigner);\n }\n return context;\n}\n\nexport function importSessionFromWire(warrant: string, holderKey: Uint8Array): WasmSession {\n const { SdkSession } = loadWasm();\n return SdkSession.fromWire(warrant, holderKey);\n}\n\nexport function importSessionFromParts(\n payloadHex: string,\n signatureHex: string,\n holderKey: Uint8Array,\n): WasmSession {\n const { SdkSession } = loadWasm();\n return SdkSession.fromParts(payloadHex, signatureHex, holderKey);\n}\n\nexport function importSessionFromChain(parts: unknown, holderKey: Uint8Array): WasmSession {\n const { SdkSession } = loadWasm();\n return SdkSession.fromChain(parts, holderKey);\n}\n\nexport function inspectWarrant(wire: string): WasmInspect {\n return loadWasm().sdkInspectWarrant(wire);\n}\n\nexport function inspectParts(payloadHex: string, signatureHex: string): WasmInspect {\n return loadWasm().sdkInspectParts(payloadHex, signatureHex);\n}\n\n/** Hex public key for a 32-byte holder secret. Ed25519, derived in Rust. */\nexport function publicKeyHexFromHolderKey(holderSecret: Uint8Array): string {\n return loadWasm().sdkPublicKeyFromHolderKey(holderSecret);\n}\n\nexport type ApprovalRequirementStatus = \"not_gated\" | \"exempt\" | \"required\" | \"denied\";\n\nexport type ApprovalRequirement = {\n status: ApprovalRequirementStatus;\n tool: string;\n kind?: \"whole_tool\" | \"argument\";\n argument?: string;\n arguments: string[];\n message?: string;\n code?: string;\n reason?: string;\n};\n\nexport type ApprovalGateInspection = {\n kind: \"none\" | \"whole_tool\" | \"conditional\" | \"unknown\";\n tool: string;\n arguments: string[];\n message?: string;\n};\n\nfunction loadApprovalWasm() {\n return loadWasm();\n}\n\nfunction throwIfGateError(error: string | undefined): void {\n if (error) {\n throw new TenuoConfigurationError(error);\n }\n}\n\n/**\n * Typed preflight of a warrant's approval gates for `(tool, args)`.\n *\n * Capability constraints are checked first. `denied` means the authorizer\n * will refuse the call — do not collect approval. Malformed encodings throw\n * (fail-closed).\n */\nexport function approvalRequirement(\n warrantB64: string,\n tool: string,\n args: Record<string, unknown>,\n): ApprovalRequirement {\n const result = loadApprovalWasm().approval_requirement(warrantB64, tool, args);\n throwIfGateError(result.error);\n return result;\n}\n\n/** Inspect how a warrant gates `tool`, without evaluating arguments. */\nexport function inspectApprovalGate(warrantB64: string, tool: string): ApprovalGateInspection {\n const result = loadApprovalWasm().inspect_approval_gate(warrantB64, tool);\n throwIfGateError(result.error);\n return result;\n}\n\n/**\n * Gate-map-only boolean. Does not run capability checks, so split-view\n * callers (PoP args vs constraint args) still see a firing gate. Prefer\n * {@link approvalRequirement} when the caller must distinguish `denied`.\n */\nexport function evaluateApprovalGates(\n warrantB64: string,\n tool: string,\n args: Record<string, unknown>,\n): boolean {\n const result = loadApprovalWasm().evaluate_approval_gates(warrantB64, tool, args);\n throwIfGateError(result.error);\n return result.approval_required;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"wasm.js","sourceRoot":"","sources":["../src/wasm.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from \"node:module\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { TenuoConfigurationError } from \"./errors.ts\";\nimport type { TenuoErrorCode } from \"./api.ts\";\n\nexport type WasmDecision = {\n outcome: \"allow\" | \"deny\" | \"approval_required\";\n code?: TenuoErrorCode;\n field?: string;\n message?: string;\n args?: Record<string, unknown>;\n tool?: string;\n required?: number;\n received?: number;\n receipt?: string;\n};\n\nexport type WasmInspect = {\n payload_hex: string;\n signature_hex: string;\n id: string;\n};\n\nexport type WasmReceipt = {\n authentic: true;\n /** Hex key the receipt is signed under — resolve against your authorizer set. */\n signer_key: string;\n outcome: \"allow\" | \"deny\";\n action: string;\n decision_code?: string;\n request_id: string;\n /** Version of the revocation list in force, when it carried one. */\n srl_version?: number;\n /**\n * SHA-256 (hex) of the revocation list bytes in force at decision time.\n * Absent means no revocation data was loaded — a different claim from a\n * loaded list that revoked nothing.\n */\n srl_hash?: string;\n /**\n * SHA-256 (hex) commitment to the canonical invocation this decision was\n * made over. Absent when the arguments could not be canonicalized — never\n * zero-filled, so absence is a claim rather than a value.\n */\n request_hash?: string;\n /** SHA-256 (hex) of the host ceiling applied to this decision. */\n policy_definition_hash?: string;\n /**\n * SHA-256 (hex) of the previous receipt from this signer. Absent on the\n * first receipt, or when the deployment does not chain. A broken link means\n * a receipt was removed from the stream.\n */\n prev_receipt_hash?: string;\n /** SHA-256 (hex) of the trusted root set in force at decision time. */\n trusted_roots_hash?: string;\n};\n\nexport type WasmReceiptChain = {\n signer_key: string;\n outcome: \"allow\" | \"deny\";\n decision_code?: string;\n timestamp: number;\n /** The embedded chain verifies to a supplied root at the decision instant. */\n chain_valid: boolean;\n /** Canonical error name when it does not. */\n chain_error?: string;\n /**\n * Deny receipts only: the chain failure matches the stated decision_code,\n * so the embedded authority independently corroborates the refusal.\n */\n corroborates_denial?: boolean;\n root_issuer?: string;\n leaf_holder?: string;\n};\n\nexport type WasmSession = object;\n\n/** Mirrors `SessionInfoDto` in tenuo-wasm. Snake case is the WASM boundary. */\nexport type WasmSessionInfo = {\n kind: \"execution\" | \"issuer\";\n holder_public_key: string;\n root_public_key: string;\n depth: number;\n max_depth: number;\n terminal: boolean;\n expires_at: number;\n tools: string[];\n warrant_ids: string[];\n can_authorize: boolean;\n clearance?: number;\n session_id?: string;\n agent_id?: string;\n issuable_tools?: string[];\n max_issue_depth?: number;\n required_approvers?: string[];\n min_approvals?: number;\n approval_gated_tools: string[];\n};\n\n/** Security limits exported by tenuo-core; TypeScript must not redefine them. */\nexport type WasmProtocolLimits = {\n max_delegation_depth: number;\n max_warrant_ttl_seconds: number;\n};\n\n/** Mirrors `NarrowOptions` in tenuo-wasm. Unknown keys are rejected there. */\nexport type WasmNarrowOptions = {\n holder?: string;\n ttlSeconds?: number;\n terminal?: boolean;\n maxDepth?: number;\n clearance?: number | string;\n agentId?: string;\n addApprovers?: string[];\n minApprovals?: number;\n};\n\nexport type WasmRequireApproval = {\n approvers: string[];\n min: number;\n tools?: string[];\n gates?: Record<string, { message?: string; args?: Record<string, unknown> }>;\n};\n\n/** Mirrors `MintOptions` in tenuo-wasm. */\nexport type WasmMintOptions = {\n kind?: \"execution\" | \"issuer\";\n allow?: unknown;\n ttlSeconds?: number;\n holder?: string;\n maxDepth?: number;\n clearance?: number | string;\n sessionId?: string;\n agentId?: string;\n requireApproval?: WasmRequireApproval;\n issuableTools?: string[];\n constraintBounds?: unknown;\n maxIssueDepth?: number;\n};\n\n/** Mirrors `IssueOptions` in tenuo-wasm. */\nexport type WasmIssueOptions = {\n allow: unknown;\n holder: string;\n ttlSeconds?: number;\n maxDepth?: number;\n clearance?: number | string;\n sessionId?: string;\n agentId?: string;\n requireApproval?: { approvers: string[]; min: number };\n};\n\nexport type WasmExplainField = {\n field: string;\n kind: string;\n constraint: unknown;\n value?: unknown;\n satisfied: boolean;\n reason_code?: string;\n reason?: string;\n};\n\nexport type WasmExplain = {\n tool: string;\n kind: \"execution\" | \"issuer\";\n outcome: \"allow\" | \"deny\";\n code?: TenuoErrorCode;\n field?: string;\n message?: string;\n tool_granted: boolean;\n fields: WasmExplainField[];\n unknown_fields: string[];\n missing_fields: string[];\n expired: boolean;\n expires_at: number;\n chain_valid: boolean;\n chain_error?: TenuoErrorCode;\n};\n\nexport type WasmApprovalRequest = {\n request_id: string;\n warrant_id: string;\n tool: string;\n args: Record<string, unknown>;\n request_hash: string;\n holder_public_key: string;\n required_approvers: string[];\n min_approvals: number;\n warrant_expires_at: number;\n created_at: number;\n message: string;\n};\n\nexport type WasmAttestation = {\n version: number;\n canonicalization: string;\n warrant_id: string;\n tool: string;\n request_hash: string;\n holder_key_hex: string;\n args_canonical_cbor_b64: string;\n signer_key_hex: string;\n signature_b64: string;\n};\n\nexport type WasmApprovalInfo = {\n approver_public_key: string;\n request_hash: string;\n external_id: string;\n approved_at: number;\n expires_at: number;\n expired: boolean;\n signature_valid: boolean;\n error?: string;\n};\n\nexport type WasmSrlInfo = {\n version: number;\n issued_at: number;\n issuer_public_key: string;\n revoked_ids: string[];\n signature_valid: boolean;\n};\n\nexport type WasmContext = {\n withReceiptSigner(secret: Uint8Array): WasmContext;\n mint(\n allow: unknown,\n ttlSeconds: number,\n requireApproval?: unknown,\n holderHex?: string,\n maxDepth?: number,\n ): WasmSession;\n mintExtended(options: WasmMintOptions): WasmSession;\n narrow(session: WasmSession, allow: unknown, options?: WasmNarrowOptions): WasmSession;\n issue(issuer: WasmSession, options: WasmIssueOptions): WasmSession;\n issuerPublicKey(): string;\n explain(session: WasmSession, tool: string, args: unknown): WasmExplain;\n approvalRequest(session: WasmSession, tool: string, args: unknown): WasmApprovalRequest;\n approvalContextAttestation(session: WasmSession, tool: string, args: unknown): WasmAttestation;\n signRevocationListVersioned(ids: string[], version?: number): string;\n authorize(\n session: WasmSession,\n tool: string,\n args: unknown,\n approvals?: unknown,\n toolAllow?: unknown,\n requestId?: string,\n ): WasmDecision;\n authorizeAsOf(\n session: WasmSession,\n tool: string,\n args: unknown,\n asOf: number,\n approvals?: unknown,\n toolAllow?: unknown,\n ): WasmDecision;\n loadRevocationList(srl: string): void;\n signRevocationList(ids: string[]): string;\n signPop(session: WasmSession, tool: string, args: unknown): string;\n signMeta(\n session: WasmSession,\n tool: string,\n argsJson: string,\n timestamp: number,\n approvals?: unknown,\n ): { warrant: string; signature: string; approvals?: string[] };\n /** Holder proof only; does not authorize execution. */\n verifyMetaPop(\n warrant: string,\n signature: string,\n tool: string,\n argsJson: string,\n timestamp: number,\n ): boolean;\n /** Compatibility alias for verifyMetaPop. */\n verifyMeta(\n warrant: string,\n signature: string,\n tool: string,\n argsJson: string,\n timestamp: number,\n ): boolean;\n authorizePresented(\n warrants: unknown,\n tool: string,\n args: unknown,\n pop: string,\n approvals?: unknown,\n toolAllow?: unknown,\n requestId?: string,\n ): WasmDecision;\n};\n\ntype Generated = {\n SdkContext: {\n new (): WasmContext;\n fromTrustedRoots(roots: string[]): WasmContext;\n fromIssuerSecret(secret: Uint8Array, extraRoots?: string[]): WasmContext;\n };\n SdkSession: {\n fromWire(warrant: string, holder: Uint8Array): WasmSession;\n fromParts(payloadHex: string, signatureHex: string, holder: Uint8Array): WasmSession;\n fromChain(parts: unknown, holder: Uint8Array): WasmSession;\n };\n sdkInspectWarrant(wire: string): WasmInspect;\n sdkInspectParts(payloadHex: string, signatureHex: string): WasmInspect;\n sdkProtocolLimits(): WasmProtocolLimits;\n sdkPublicKeyFromHolderKey(holderSecret: Uint8Array): string;\n sdkSignApproval(\n session: WasmSession,\n tool: string,\n args: unknown,\n approverSecret: Uint8Array,\n externalId: string,\n asOf?: number,\n ): string;\n sdkSignApprovalForRequest(\n requestHashHex: string,\n approverSecret: Uint8Array,\n externalId: string,\n ttlSeconds?: number,\n warrantExpiresAt?: number,\n ): string;\n sdkInspectApproval(envelope: string): WasmApprovalInfo;\n sdkSignRevocationList(ids: string[], issuerSecret: Uint8Array): string;\n sdkSignRevocationListVersioned(ids: string[], version: number | undefined, issuerSecret: Uint8Array): string;\n sdkSignPublishedRevocationList(ids: string[], version: number, issuerSecret: Uint8Array): string;\n sdkInspectRevocationList(wire: string): WasmSrlInfo;\n sdkVerifyReceipt(wire: string): WasmReceipt;\n sdkVerifyReceiptChain(wire: string, roots: string[]): WasmReceiptChain;\n approval_requirement(\n warrantB64: string,\n tool: string,\n args: unknown,\n ): ApprovalRequirement & { error?: string };\n inspect_approval_gate(\n warrantB64: string,\n tool: string,\n ): ApprovalGateInspection & { error?: string };\n evaluate_approval_gates(\n warrantB64: string,\n tool: string,\n args: unknown,\n ): { approval_required: boolean; tool: string; error?: string };\n parse_strict_json(jsonText: string): void;\n};\n\nlet loaded: Generated | undefined;\n\nfunction generatedPath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n return join(here, \"generated\", \"tenuo_wasm.js\");\n}\n\nexport function wasmAvailable(): boolean {\n return existsSync(generatedPath());\n}\n\nconst WASM_LOAD_HINT =\n \"WASM core is missing from this @tenuo/core install. @tenuo/core is Node 20+ only (not a bundler target). If this is Next.js/webpack, set serverExternalPackages: ['@tenuo/core']. Do not run wasm-pack in the consuming app.\";\n\nexport function loadWasm(): Generated {\n if (loaded) {\n return loaded;\n }\n const path = generatedPath();\n if (!existsSync(path)) {\n throw new TenuoConfigurationError(WASM_LOAD_HINT, \"TENUO_NOT_READY\");\n }\n try {\n const require = createRequire(import.meta.url);\n loaded = require(path) as Generated;\n return loaded;\n } catch (error) {\n const wrapped = new TenuoConfigurationError(WASM_LOAD_HINT, \"TENUO_NOT_READY\");\n wrapped.cause = error;\n throw wrapped;\n }\n}\n\nexport function createDevContext(receiptSigner?: Uint8Array): WasmContext {\n const { SdkContext } = loadWasm();\n let context: WasmContext = new SdkContext();\n if (receiptSigner !== undefined) {\n context = context.withReceiptSigner(receiptSigner);\n }\n return context;\n}\n\nexport function protocolLimits(): WasmProtocolLimits {\n return loadWasm().sdkProtocolLimits();\n}\n\nexport function createVerifierContext(\n rootHexes: readonly string[],\n revocationList?: string,\n receiptSigner?: Uint8Array,\n): WasmContext {\n const { SdkContext } = loadWasm();\n let context = SdkContext.fromTrustedRoots([...rootHexes]);\n if (receiptSigner !== undefined) {\n context = context.withReceiptSigner(receiptSigner);\n }\n if (revocationList !== undefined && revocationList.length > 0) {\n context.loadRevocationList(revocationList);\n }\n return context;\n}\n\nexport function createIssuerContext(\n secret: Uint8Array,\n extraRootHexes: readonly string[],\n receiptSigner?: Uint8Array,\n): WasmContext {\n const { SdkContext } = loadWasm();\n let context = SdkContext.fromIssuerSecret(secret, [...extraRootHexes]);\n if (receiptSigner !== undefined) {\n context = context.withReceiptSigner(receiptSigner);\n }\n return context;\n}\n\nexport function importSessionFromWire(warrant: string, holderKey: Uint8Array): WasmSession {\n const { SdkSession } = loadWasm();\n return SdkSession.fromWire(warrant, holderKey);\n}\n\nexport function importSessionFromParts(\n payloadHex: string,\n signatureHex: string,\n holderKey: Uint8Array,\n): WasmSession {\n const { SdkSession } = loadWasm();\n return SdkSession.fromParts(payloadHex, signatureHex, holderKey);\n}\n\nexport function importSessionFromChain(parts: unknown, holderKey: Uint8Array): WasmSession {\n const { SdkSession } = loadWasm();\n return SdkSession.fromChain(parts, holderKey);\n}\n\nexport function inspectWarrant(wire: string): WasmInspect {\n return loadWasm().sdkInspectWarrant(wire);\n}\n\nexport function inspectParts(payloadHex: string, signatureHex: string): WasmInspect {\n return loadWasm().sdkInspectParts(payloadHex, signatureHex);\n}\n\n/** Hex public key for a 32-byte holder secret. Ed25519, derived in Rust. */\nexport function publicKeyHexFromHolderKey(holderSecret: Uint8Array): string {\n return loadWasm().sdkPublicKeyFromHolderKey(holderSecret);\n}\n\nexport type ApprovalRequirementStatus = \"not_gated\" | \"exempt\" | \"required\" | \"denied\";\n\nexport type ApprovalRequirement = {\n status: ApprovalRequirementStatus;\n tool: string;\n kind?: \"whole_tool\" | \"argument\";\n argument?: string;\n arguments: string[];\n message?: string;\n code?: string;\n reason?: string;\n};\n\nexport type ApprovalGateInspection = {\n kind: \"none\" | \"whole_tool\" | \"conditional\" | \"unknown\";\n tool: string;\n arguments: string[];\n message?: string;\n};\n\nfunction loadApprovalWasm() {\n return loadWasm();\n}\n\nfunction throwIfGateError(error: string | undefined): void {\n if (error) {\n throw new TenuoConfigurationError(error);\n }\n}\n\n/**\n * Parse JSON text and reject a repeated key in any object.\n *\n * `verify()` receives an object the host already parsed. A duplicate key is\n * gone by then. Call this while the text is still available. After the check,\n * the value is `JSON.parse` of the original text, so each key is an own data\n * property and `null` stays `null`.\n */\nexport function parseStrictJson(text: string): unknown {\n loadWasm().parse_strict_json(text);\n return JSON.parse(text);\n}\n\n/**\n * Typed preflight of a warrant's approval gates for `(tool, args)`.\n *\n * Capability constraints are checked first. `denied` means the authorizer\n * will refuse the call — do not collect approval. Malformed encodings throw\n * (fail-closed).\n */\nexport function approvalRequirement(\n warrantB64: string,\n tool: string,\n args: Record<string, unknown>,\n): ApprovalRequirement {\n const result = loadApprovalWasm().approval_requirement(warrantB64, tool, args);\n throwIfGateError(result.error);\n return result;\n}\n\n/** Inspect how a warrant gates `tool`, without evaluating arguments. */\nexport function inspectApprovalGate(warrantB64: string, tool: string): ApprovalGateInspection {\n const result = loadApprovalWasm().inspect_approval_gate(warrantB64, tool);\n throwIfGateError(result.error);\n return result;\n}\n\n/**\n * Gate-map-only boolean. Does not run capability checks, so split-view\n * callers (PoP args vs constraint args) still see a firing gate. Prefer\n * {@link approvalRequirement} when the caller must distinguish `denied`.\n */\nexport function evaluateApprovalGates(\n warrantB64: string,\n tool: string,\n args: Record<string, unknown>,\n): boolean {\n const result = loadApprovalWasm().evaluate_approval_gates(warrantB64, tool, args);\n throwIfGateError(result.error);\n return result.approval_required;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenuo/core",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.2-beta.0",
|
|
4
|
+
"description": "Task-scoped authorization for AI agents in TypeScript: signed warrants limit which tools an agent can call and with which arguments, verified by the Tenuo Rust core compiled to WASM (beta).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Tenuo Contributors",
|
|
7
7
|
"homepage": "https://github.com/tenuo-ai/tenuo/tree/main/tenuo-ts/packages/core",
|
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
"keywords": [
|
|
17
17
|
"tenuo",
|
|
18
18
|
"authorization",
|
|
19
|
-
"agents",
|
|
19
|
+
"ai-agents",
|
|
20
|
+
"agent-security",
|
|
21
|
+
"llm-security",
|
|
22
|
+
"access-control",
|
|
23
|
+
"capabilities",
|
|
24
|
+
"warrants",
|
|
25
|
+
"delegation",
|
|
26
|
+
"prompt-injection",
|
|
20
27
|
"mcp",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"wasm"
|
|
23
30
|
],
|
|
24
31
|
"publishConfig": {
|
|
25
32
|
"access": "public"
|