@robota-sdk/agent-core 3.0.0-beta.79 → 3.0.0-beta.81
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/CHANGELOG.md +667 -0
- package/README.md +24 -4
- package/dist/browser/bounded-output-DpjpS98g.js +2 -0
- package/dist/browser/bounded-output-DpjpS98g.js.map +1 -0
- package/dist/browser/command-executor-cI7XJ374.js +2 -0
- package/dist/browser/command-executor-cI7XJ374.js.map +1 -0
- package/dist/browser/http-executor-CHy6UeBU.js +2 -0
- package/dist/browser/http-executor-CHy6UeBU.js.map +1 -0
- package/dist/browser/index.d.ts +3337 -1499
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +11 -11
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/verdict-decoder-Dk_w2NG9.js +2 -0
- package/dist/browser/verdict-decoder-Dk_w2NG9.js.map +1 -0
- package/dist/node/bounded-output-DpjpS98g.js +2 -0
- package/dist/node/bounded-output-DpjpS98g.js.map +1 -0
- package/dist/node/command-executor-Aat_q7P2.cjs +1 -0
- package/dist/node/command-executor-DV-H2oD6.js +2 -0
- package/dist/node/command-executor-DV-H2oD6.js.map +1 -0
- package/dist/node/http-executor-71JiIdFP.cjs +1 -0
- package/dist/node/http-executor-cWzI2svn.js +2 -0
- package/dist/node/http-executor-cWzI2svn.js.map +1 -0
- package/dist/node/{index-BKIUt9pk.d.ts → index-CiFlpr7-.d.cts} +3289 -1783
- package/dist/node/index-CiFlpr7-.d.cts.map +1 -0
- package/dist/node/index-DFlMw376.d.ts +6893 -0
- package/dist/node/index-DFlMw376.d.ts.map +1 -0
- package/dist/node/index.cjs +11 -11
- package/dist/node/index.d.cts +3 -0
- package/dist/node/index.d.ts +3 -2
- package/dist/node/index.js +11 -11
- package/dist/node/index.js.map +1 -1
- package/dist/node/node.cjs +1 -0
- package/dist/node/node.d.cts +236 -0
- package/dist/node/node.d.cts.map +1 -0
- package/dist/node/node.d.ts +236 -0
- package/dist/node/node.d.ts.map +1 -0
- package/dist/node/node.js +2 -0
- package/dist/node/node.js.map +1 -0
- package/dist/node/project-tool-schema-DrDjGKnH.cjs +1 -0
- package/dist/node/project-tool-schema-_UKfY7yO.js +2 -0
- package/dist/node/project-tool-schema-_UKfY7yO.js.map +1 -0
- package/dist/node/rolldown-runtime-DK3Fl9T5.js +1 -0
- package/dist/node/rolldown-runtime-DWNI8pZO.cjs +1 -0
- package/dist/node/testing/index.cjs +1 -1
- package/dist/node/testing/index.d.cts +96 -0
- package/dist/node/testing/index.d.cts.map +1 -0
- package/dist/node/testing/index.d.ts +35 -4
- package/dist/node/testing/index.d.ts.map +1 -1
- package/dist/node/testing/index.js +1 -1
- package/dist/node/testing/index.js.map +1 -1
- package/dist/node/types-gNrckgFE.d.cts +335 -0
- package/dist/node/types-gNrckgFE.d.cts.map +1 -0
- package/dist/node/types-gNrckgFE.d.ts +335 -0
- package/dist/node/types-gNrckgFE.d.ts.map +1 -0
- package/dist/node/verdict-decoder-Dk_w2NG9.js +2 -0
- package/dist/node/verdict-decoder-Dk_w2NG9.js.map +1 -0
- package/dist/node/verdict-decoder-Estx9MtY.cjs +1 -0
- package/package.json +75 -24
- package/dist/node/index-BKIUt9pk.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={type:`null`};function t(e){return e.type===`null`||Array.isArray(e.anyOf)&&e.anyOf.some(e=>typeof e==`object`&&!!e&&e.type===`null`)}function n(n){if(typeof n!=`object`||!n||Array.isArray(n))return n;let r=n;return t(r)?r:Array.isArray(r.anyOf)?{...r,anyOf:[...r.anyOf,e]}:{anyOf:[r,e]}}function r(e,t={}){return i(e,t,``)}function i(e,r,a){if(Array.isArray(e))return e.map((e,t)=>i(e,r,`${a}/${t}`));if(typeof e!=`object`||!e)return e;let o=e,s={...o},c=o.type===`object`,l=o.properties&&typeof o.properties==`object`?o.properties:void 0;if(l){let e=new Set(Array.isArray(o.required)?o.required.map(String):[]);s.properties=Object.fromEntries(Object.entries(l).map(([o,s])=>{let c=`${a}/properties/${o}`,l=i(s,r,c),u=e.has(o);if(r.requireAllProperties===!0&&!u&&r.onChange?.({path:c,kind:`required-added`}),!(r.requireAllProperties===!0&&r.optionalAsNullable===!0&&!u))return[o,l];let d=typeof l==`object`&&l&&!Array.isArray(l)?t(l):!1,f=n(l);return d||r.onChange?.({path:c,kind:`nullable-added`}),[o,f]})),r.requireAllProperties===!0&&(s.required=Object.keys(l))}return o.items&&typeof o.items==`object`&&(s.items=i(o.items,r,`${a}/items`)),Array.isArray(o.anyOf)&&(s.anyOf=o.anyOf.map((e,t)=>i(e,r,`${a}/anyOf/${t}`))),o.additionalProperties&&typeof o.additionalProperties==`object`?s.additionalProperties=i(o.additionalProperties,r,`${a}/additionalProperties`):c&&(o.additionalProperties!==!1&&r.onChange?.({path:a,kind:`closed-object`}),s.additionalProperties=!1),s}const a=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),o=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);function s(e,t){return e>>>t|e<<32-t}function c(e){let t=new TextEncoder().encode(e),n=Math.ceil((t.length+9)/64)*64,r=new Uint8Array(n);r.set(t),r[t.length]=128;let i=new DataView(r.buffer),c=BigInt(t.length)*8n;i.setUint32(n-8,Number(c>>32n&4294967295n)),i.setUint32(n-4,Number(c&4294967295n));let l=new Uint32Array(o),u=new Uint32Array(64);for(let e=0;e<n;e+=64){for(let t=0;t<16;t+=1)u[t]=i.getUint32(e+t*4);for(let e=16;e<64;e+=1){let t=u[e-15]??0,n=u[e-2]??0,r=s(t,7)^s(t,18)^t>>>3,i=s(n,17)^s(n,19)^n>>>10;u[e]=(u[e-16]??0)+r+(u[e-7]??0)+i>>>0}let t=l[0]??0,n=l[1]??0,r=l[2]??0,o=l[3]??0,c=l[4]??0,d=l[5]??0,f=l[6]??0,p=l[7]??0;for(let e=0;e<64;e+=1){let i=s(c,6)^s(c,11)^s(c,25),l=c&d^~c&f,m=p+i+l+(a[e]??0)+(u[e]??0)>>>0,h=(s(t,2)^s(t,13)^s(t,22))+(t&n^t&r^n&r)>>>0;p=f,f=d,d=c,c=o+m>>>0,o=r,r=n,n=t,t=m+h>>>0}l[0]=(l[0]??0)+t>>>0,l[1]=(l[1]??0)+n>>>0,l[2]=(l[2]??0)+r>>>0,l[3]=(l[3]??0)+o>>>0,l[4]=(l[4]??0)+c>>>0,l[5]=(l[5]??0)+d>>>0,l[6]=(l[6]??0)+f>>>0,l[7]=(l[7]??0)+p>>>0}return[...l].map(e=>e.toString(16).padStart(8,`0`)).join(``)}const l=2e3,u=Object.keys({type:!0,description:!0,enum:!0,items:!0,properties:!0,required:!0,anyOf:!0,additionalProperties:!0,minimum:!0,maximum:!0,pattern:!0,format:!0,default:!0}),d=new Set(u),f=new Set([`__proto__`,`constructor`,`prototype`]),p=new Set([`title`,`$schema`,`$comment`,`examples`,`default`]);function m(){return{anyOf:[{type:`string`},{type:`number`},{type:`boolean`},{type:`object`},{type:`array`},{type:`null`}]}}const h={closedObjects:!1,requireAllProperties:!1,optionalAsNullable:!1,unknownKeywords:`adopt`,unsupportedMembers:[],maxDepth:32,maxNodes:l},g={closedObjects:!0,requireAllProperties:!0,optionalAsNullable:!0,unknownKeywords:`strip`,unsupportedMembers:[],maxDepth:32,maxNodes:l};function _(e,t){return`${e}/${t}`}function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e,t,n,r,i,a){if(!v(e))return;if(r.has(e))return{path:t,keyword:`cycle`,reason:`object graph revisits a node on its own ancestor chain (cycle)`};if(r.add(e),n>a.maxDepth)return{path:t,keyword:`maxDepth`,reason:`nesting depth exceeds maxDepth (${a.maxDepth})`};if(i.count+=1,i.count>a.maxNodes)return{path:t,keyword:`maxNodes`,reason:`node count exceeds maxNodes (${a.maxNodes})`};if(a.unknownKeywords===`reject`){for(let n of Object.keys(e))if(!d.has(n))return{path:t,keyword:n,reason:`unknown keyword '${n}' is outside the IParameterSchema member set`}}let o=typeof e.type==`string`,s=Array.isArray(e.anyOf);if(o&&s)return{path:t,keyword:`anyOf`,reason:`a node must declare exactly one of 'type' or 'anyOf', not both`};if(!o&&!s&&!Object.keys(e).some(e=>!d.has(e)))return{path:t,keyword:`type`,reason:`a node must declare exactly one of 'type' or 'anyOf'`};let c=e.properties;if(v(c)){for(let e of Object.keys(c))if(f.has(e))return{path:_(_(t,`properties`),e),keyword:e,reason:`property name '${e}' is a prototype key`};for(let e of Object.keys(c)){let o=y(c[e],_(_(t,`properties`),e),n+1,r,i,a);if(o)return o}}if(v(e.items)){let o=y(e.items,_(t,`items`),n+1,r,i,a);if(o)return o}if(Array.isArray(e.anyOf))for(let o=0;o<e.anyOf.length;o+=1){let s=y(e.anyOf[o],_(_(t,`anyOf`),String(o)),n+1,r,i,a);if(s)return s}r.delete(e)}function b(e,t,n,r){let i={...e};for(let e of n.unsupportedMembers){let n=e;n in i&&(delete i[n],r.push({path:t,kind:`member-stripped`,keyword:n}))}if(n.unknownKeywords===`strip`){let e=Object.keys(i).filter(e=>!d.has(e));for(let t of e)delete i[t];let n=typeof i.type==`string`,a=Array.isArray(i.anyOf);if(e.length>0&&!n&&!a){for(let n of e)r.push({path:t,kind:`keyword-replaced`,keyword:n});let n=m();for(let e of Object.keys(i))e===`description`?n.description=i.description:r.push({path:t,kind:`member-stripped`,keyword:e});return n}for(let n of e)r.push({path:t,kind:`keyword-stripped`,keyword:n})}let a=i.properties;return v(a)&&(i.properties=Object.fromEntries(Object.keys(a).map(e=>{let i=a[e];return[e,v(i)?b(i,_(_(t,`properties`),e),n,r):i]}))),v(i.items)&&(i.items=b(i.items,_(t,`items`),n,r)),Array.isArray(i.anyOf)&&(i.anyOf=i.anyOf.map((e,i)=>v(e)?b(e,_(_(t,`anyOf`),String(i)),n,r):e)),i}function x(e){if(Array.isArray(e))return e.map(x);if(v(e)){let t={};for(let n of Object.keys(e).sort())t[n]=x(e[n]);return t}return e}function S(e){return c(JSON.stringify(x(e)))}function C(e,t){let n=e.parameters;if(!v(n)||n.type!==`object`)return{outcome:`rejected`,tool:e,changes:[],rejection:{path:``,keyword:`type`,reason:`the parameters root must declare type: 'object' — no documented provider accepts another root`}};let i=y(n,``,0,new Set,{count:0},t);if(i)return{outcome:`rejected`,tool:e,changes:[],rejection:i};let a=[],o=b(n,``,t,a),s=t.closedObjects?r(o,{requireAllProperties:t.requireAllProperties,optionalAsNullable:t.optionalAsNullable,onChange:e=>a.push(e)}):o;if(a.length===0)return{outcome:`adopted`,tool:e,changes:[]};let c=a.filter(e=>(e.kind===`keyword-stripped`||e.kind===`keyword-replaced`||e.kind===`member-stripped`)&&!p.has(e.keyword??``)),l=c.length===0?e.description:`${e.description}\n\nSchema note: ${c.length} constraint(s) not shown to ${t.providerName}: ${c.map(e=>`${e.kind}@${e.path}`).join(`, `)}`;return{outcome:`adapted`,tool:{...e,parameters:s,description:l},changes:a}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return 32}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return u}});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={type:`null`};function t(e){return e.type===`null`||Array.isArray(e.anyOf)&&e.anyOf.some(e=>typeof e==`object`&&!!e&&e.type===`null`)}function n(n){if(typeof n!=`object`||!n||Array.isArray(n))return n;let r=n;return t(r)?r:Array.isArray(r.anyOf)?{...r,anyOf:[...r.anyOf,e]}:{anyOf:[r,e]}}function r(e,t={}){return i(e,t,``)}function i(e,r,a){if(Array.isArray(e))return e.map((e,t)=>i(e,r,`${a}/${t}`));if(typeof e!=`object`||!e)return e;let o=e,s={...o},c=o.type===`object`,l=o.properties&&typeof o.properties==`object`?o.properties:void 0;if(l){let e=new Set(Array.isArray(o.required)?o.required.map(String):[]);s.properties=Object.fromEntries(Object.entries(l).map(([o,s])=>{let c=`${a}/properties/${o}`,l=i(s,r,c),u=e.has(o);if(r.requireAllProperties===!0&&!u&&r.onChange?.({path:c,kind:`required-added`}),!(r.requireAllProperties===!0&&r.optionalAsNullable===!0&&!u))return[o,l];let d=typeof l==`object`&&l&&!Array.isArray(l)?t(l):!1,f=n(l);return d||r.onChange?.({path:c,kind:`nullable-added`}),[o,f]})),r.requireAllProperties===!0&&(s.required=Object.keys(l))}return o.items&&typeof o.items==`object`&&(s.items=i(o.items,r,`${a}/items`)),Array.isArray(o.anyOf)&&(s.anyOf=o.anyOf.map((e,t)=>i(e,r,`${a}/anyOf/${t}`))),o.additionalProperties&&typeof o.additionalProperties==`object`?s.additionalProperties=i(o.additionalProperties,r,`${a}/additionalProperties`):c&&(o.additionalProperties!==!1&&r.onChange?.({path:a,kind:`closed-object`}),s.additionalProperties=!1),s}const a=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),o=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);function s(e,t){return e>>>t|e<<32-t}function c(e){let t=new TextEncoder().encode(e),n=Math.ceil((t.length+9)/64)*64,r=new Uint8Array(n);r.set(t),r[t.length]=128;let i=new DataView(r.buffer),c=BigInt(t.length)*8n;i.setUint32(n-8,Number(c>>32n&4294967295n)),i.setUint32(n-4,Number(c&4294967295n));let l=new Uint32Array(o),u=new Uint32Array(64);for(let e=0;e<n;e+=64){for(let t=0;t<16;t+=1)u[t]=i.getUint32(e+t*4);for(let e=16;e<64;e+=1){let t=u[e-15]??0,n=u[e-2]??0,r=s(t,7)^s(t,18)^t>>>3,i=s(n,17)^s(n,19)^n>>>10;u[e]=(u[e-16]??0)+r+(u[e-7]??0)+i>>>0}let t=l[0]??0,n=l[1]??0,r=l[2]??0,o=l[3]??0,c=l[4]??0,d=l[5]??0,f=l[6]??0,p=l[7]??0;for(let e=0;e<64;e+=1){let i=s(c,6)^s(c,11)^s(c,25),l=c&d^~c&f,m=p+i+l+(a[e]??0)+(u[e]??0)>>>0,h=(s(t,2)^s(t,13)^s(t,22))+(t&n^t&r^n&r)>>>0;p=f,f=d,d=c,c=o+m>>>0,o=r,r=n,n=t,t=m+h>>>0}l[0]=(l[0]??0)+t>>>0,l[1]=(l[1]??0)+n>>>0,l[2]=(l[2]??0)+r>>>0,l[3]=(l[3]??0)+o>>>0,l[4]=(l[4]??0)+c>>>0,l[5]=(l[5]??0)+d>>>0,l[6]=(l[6]??0)+f>>>0,l[7]=(l[7]??0)+p>>>0}return[...l].map(e=>e.toString(16).padStart(8,`0`)).join(``)}const l=32,u=2e3,d=Object.keys({type:!0,description:!0,enum:!0,items:!0,properties:!0,required:!0,anyOf:!0,additionalProperties:!0,minimum:!0,maximum:!0,pattern:!0,format:!0,default:!0}),f=new Set(d),p=new Set([`__proto__`,`constructor`,`prototype`]),m=new Set([`title`,`$schema`,`$comment`,`examples`,`default`]);function h(){return{anyOf:[{type:`string`},{type:`number`},{type:`boolean`},{type:`object`},{type:`array`},{type:`null`}]}}const g={closedObjects:!1,requireAllProperties:!1,optionalAsNullable:!1,unknownKeywords:`adopt`,unsupportedMembers:[],maxDepth:32,maxNodes:u},_={closedObjects:!0,requireAllProperties:!0,optionalAsNullable:!0,unknownKeywords:`strip`,unsupportedMembers:[],maxDepth:32,maxNodes:u};function v(e,t){return`${e}/${t}`}function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function b(e,t,n,r,i,a){if(!y(e))return;if(r.has(e))return{path:t,keyword:`cycle`,reason:`object graph revisits a node on its own ancestor chain (cycle)`};if(r.add(e),n>a.maxDepth)return{path:t,keyword:`maxDepth`,reason:`nesting depth exceeds maxDepth (${a.maxDepth})`};if(i.count+=1,i.count>a.maxNodes)return{path:t,keyword:`maxNodes`,reason:`node count exceeds maxNodes (${a.maxNodes})`};if(a.unknownKeywords===`reject`){for(let n of Object.keys(e))if(!f.has(n))return{path:t,keyword:n,reason:`unknown keyword '${n}' is outside the IParameterSchema member set`}}let o=typeof e.type==`string`,s=Array.isArray(e.anyOf);if(o&&s)return{path:t,keyword:`anyOf`,reason:`a node must declare exactly one of 'type' or 'anyOf', not both`};if(!o&&!s&&!Object.keys(e).some(e=>!f.has(e)))return{path:t,keyword:`type`,reason:`a node must declare exactly one of 'type' or 'anyOf'`};let c=e.properties;if(y(c)){for(let e of Object.keys(c))if(p.has(e))return{path:v(v(t,`properties`),e),keyword:e,reason:`property name '${e}' is a prototype key`};for(let e of Object.keys(c)){let o=b(c[e],v(v(t,`properties`),e),n+1,r,i,a);if(o)return o}}if(y(e.items)){let o=b(e.items,v(t,`items`),n+1,r,i,a);if(o)return o}if(Array.isArray(e.anyOf))for(let o=0;o<e.anyOf.length;o+=1){let s=b(e.anyOf[o],v(v(t,`anyOf`),String(o)),n+1,r,i,a);if(s)return s}r.delete(e)}function x(e,t,n,r){let i={...e};for(let e of n.unsupportedMembers){let n=e;n in i&&(delete i[n],r.push({path:t,kind:`member-stripped`,keyword:n}))}if(n.unknownKeywords===`strip`){let e=Object.keys(i).filter(e=>!f.has(e));for(let t of e)delete i[t];let n=typeof i.type==`string`,a=Array.isArray(i.anyOf);if(e.length>0&&!n&&!a){for(let n of e)r.push({path:t,kind:`keyword-replaced`,keyword:n});let n=h();for(let e of Object.keys(i))e===`description`?n.description=i.description:r.push({path:t,kind:`member-stripped`,keyword:e});return n}for(let n of e)r.push({path:t,kind:`keyword-stripped`,keyword:n})}let a=i.properties;return y(a)&&(i.properties=Object.fromEntries(Object.keys(a).map(e=>{let i=a[e];return[e,y(i)?x(i,v(v(t,`properties`),e),n,r):i]}))),y(i.items)&&(i.items=x(i.items,v(t,`items`),n,r)),Array.isArray(i.anyOf)&&(i.anyOf=i.anyOf.map((e,i)=>y(e)?x(e,v(v(t,`anyOf`),String(i)),n,r):e)),i}function S(e){if(Array.isArray(e))return e.map(S);if(y(e)){let t={};for(let n of Object.keys(e).sort())t[n]=S(e[n]);return t}return e}function C(e){return c(JSON.stringify(S(e)))}function w(e,t){let n=e.parameters;if(!y(n)||n.type!==`object`)return{outcome:`rejected`,tool:e,changes:[],rejection:{path:``,keyword:`type`,reason:`the parameters root must declare type: 'object' — no documented provider accepts another root`}};let i=b(n,``,0,new Set,{count:0},t);if(i)return{outcome:`rejected`,tool:e,changes:[],rejection:i};let a=[],o=x(n,``,t,a),s=t.closedObjects?r(o,{requireAllProperties:t.requireAllProperties,optionalAsNullable:t.optionalAsNullable,onChange:e=>a.push(e)}):o;if(a.length===0)return{outcome:`adopted`,tool:e,changes:[]};let c=a.filter(e=>(e.kind===`keyword-stripped`||e.kind===`keyword-replaced`||e.kind===`member-stripped`)&&!m.has(e.keyword??``)),l=c.length===0?e.description:`${e.description}\n\nSchema note: ${c.length} constraint(s) not shown to ${t.providerName}: ${c.map(e=>`${e.kind}@${e.path}`).join(`, `)}`;return{outcome:`adapted`,tool:{...e,parameters:s,description:l},changes:a}}export{u as a,r as c,l as i,g as n,C as o,_ as r,w as s,d as t};
|
|
2
|
+
//# sourceMappingURL=project-tool-schema-_UKfY7yO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-tool-schema-_UKfY7yO.js","names":[],"sources":["../../src/schema/close-object-schemas.ts","../../src/schema/sha256.ts","../../src/schema/project-tool-schema.ts"],"sourcesContent":["/**\n * Closing the universal schema subset for providers that reject open-world objects. PROV-007.\n *\n * Two vendors demand a closed schema and neither accepts what this repository emits. Anthropic's\n * structured-output surface requires every `object` node to carry `additionalProperties: false`;\n * OpenAI's strict mode requires that AND that every object list all of its properties in `required`.\n * The universal subset guarantees neither — a Zod-derived node emits `additionalProperties: true`\n * for Zod's default `strip` and for `.passthrough()`, a hand-written one may omit the member, and\n * `required` lists only the genuinely-required fields.\n *\n * ONE recursion, not one per vendor. Anthropic had its own copy and OpenAI was about to get a third;\n * CORE-039 exists because a walk over this subset that misses a route — it missed `anyOf` — leaves\n * exactly the nodes it was written to fix untouched, and every copy has to be found and fixed\n * separately. The vendors differ in POLICY, so the policy is an argument.\n *\n * The consumer's original schema still governs core-side validation. This is an SDK-seam\n * transformation, applied on the way out.\n */\n\n/** One structural edit `closeObjectSchemas` made, at the JSON-pointer-like path it made it. */\nexport interface ISchemaClosureChange {\n readonly path: string;\n readonly kind: 'closed-object' | 'required-added' | 'nullable-added';\n}\n\n/** How far to close, which is where the two vendors differ. */\nexport interface ISchemaClosureOptions {\n /**\n * Also list every property of every object in `required` — OpenAI strict mode.\n *\n * This is LOSSY: it forces genuinely-optional fields into `required`. `optionalAsNullable` is how\n * that is compensated, and the two are meant to travel together.\n */\n requireAllProperties?: boolean;\n /**\n * Give a property that was optional a `null` branch once it is forced into `required`.\n *\n * The representation strict mode has no faithful mapping for, decided here rather than left to\n * each call site: a field the caller marked optional becomes required-but-nullable, which is the\n * compensation OpenAI documents. `anyOf: [T, { type: 'null' }]` is chosen because that is already\n * how this subset expresses a nullable value (CORE-039 emits it for Zod's `.nullable()`), so a\n * forced-optional field and a genuinely nullable one are indistinguishable on the wire — which is\n * the point. Anything else would invent a second nullability spelling for one vendor.\n */\n optionalAsNullable?: boolean;\n /**\n * MCP-005: told once per actual structural edit, so a caller (the tool-schema projector) can build\n * a diagnostic without re-deriving this recursion — one walk, not a second copy that could disagree\n * with this one about which route a nested object arrives by.\n */\n onChange?: (change: ISchemaClosureChange) => void;\n}\n\nconst NULL_BRANCH = { type: 'null' } as const;\n\n/** Whether this node already admits `null`, so the branch is not added twice. */\nfunction admitsNull(node: Record<string, unknown>): boolean {\n if (node.type === 'null') return true;\n return (\n Array.isArray(node.anyOf) &&\n node.anyOf.some(\n (branch) =>\n typeof branch === 'object' &&\n branch !== null &&\n (branch as Record<string, unknown>).type === 'null',\n )\n );\n}\n\n/** Wrap a property schema so it accepts `null` as well as its own shape. */\nfunction withNullBranch(value: unknown): unknown {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return value;\n }\n const node = value as Record<string, unknown>;\n if (admitsNull(node)) return node;\n if (Array.isArray(node.anyOf)) {\n return { ...node, anyOf: [...node.anyOf, NULL_BRANCH] };\n }\n return { anyOf: [node, NULL_BRANCH] };\n}\n\n/**\n * Close every object node in a schema, recursively.\n *\n * Walks `properties`, `items`, `anyOf` and a schema-valued `additionalProperties` — every route a\n * nested object can arrive by in this subset. A route left unwalked is a nested object silently left\n * open, which is the failure this function exists to prevent.\n */\nexport function closeObjectSchemas(node: unknown, options: ISchemaClosureOptions = {}): unknown {\n return closeObjectSchemasAt(node, options, '');\n}\n\nfunction closeObjectSchemasAt(\n node: unknown,\n options: ISchemaClosureOptions,\n path: string,\n): unknown {\n if (Array.isArray(node)) {\n return node.map((entry, index) => closeObjectSchemasAt(entry, options, `${path}/${index}`));\n }\n if (typeof node !== 'object' || node === null) {\n return node;\n }\n\n const record = node as Record<string, unknown>;\n const closed: Record<string, unknown> = { ...record };\n const isObjectNode = record.type === 'object';\n const properties =\n record.properties && typeof record.properties === 'object'\n ? (record.properties as Record<string, unknown>)\n : undefined;\n\n if (properties) {\n const required = new Set(\n Array.isArray(record.required) ? (record.required as unknown[]).map(String) : [],\n );\n closed.properties = Object.fromEntries(\n Object.entries(properties).map(([key, value]) => {\n const propertyPath = `${path}/properties/${key}`;\n const child = closeObjectSchemasAt(value, options, propertyPath);\n const wasRequired = required.has(key);\n if (options.requireAllProperties === true && !wasRequired) {\n options.onChange?.({ path: propertyPath, kind: 'required-added' });\n }\n // Only a property that was NOT already required is compensated. A genuinely required field\n // gaining a null branch would widen the contract rather than preserve it.\n const forcedIntoRequired =\n options.requireAllProperties === true &&\n options.optionalAsNullable === true &&\n !wasRequired;\n if (!forcedIntoRequired) {\n return [key, child];\n }\n const childAlreadyNullable =\n typeof child === 'object' && child !== null && !Array.isArray(child)\n ? admitsNull(child as Record<string, unknown>)\n : false;\n const wrapped = withNullBranch(child);\n if (!childAlreadyNullable) {\n options.onChange?.({ path: propertyPath, kind: 'nullable-added' });\n }\n return [key, wrapped];\n }),\n );\n if (options.requireAllProperties === true) {\n closed.required = Object.keys(properties);\n }\n }\n\n if (record.items && typeof record.items === 'object') {\n closed.items = closeObjectSchemasAt(record.items, options, `${path}/items`);\n }\n\n // CORE-039: a union node's branches are objects too. Without this the spread carries `anyOf`\n // through unrecursed, leaving every object inside a branch open — the exact thing this seam\n // exists to prevent, reached by the one route it did not walk.\n if (Array.isArray(record.anyOf)) {\n closed.anyOf = record.anyOf.map((branch, index) =>\n closeObjectSchemasAt(branch, options, `${path}/anyOf/${index}`),\n );\n }\n\n if (record.additionalProperties && typeof record.additionalProperties === 'object') {\n // Schema-valued `additionalProperties` (record types) passes through recursed; a vendor may\n // reject it, which surfaces as a provider error rather than being masked here.\n closed.additionalProperties = closeObjectSchemasAt(\n record.additionalProperties,\n options,\n `${path}/additionalProperties`,\n );\n } else if (isObjectNode) {\n // Deliberate overwrite, including of an explicit `true`. The converter emits\n // `additionalProperties: true` routinely — Zod's default `strip` means \"accept then drop\" — and\n // these vendors still require every object node closed. The consumer's original schema keeps\n // governing core-side validation, where the `true` is honoured.\n if (record.additionalProperties !== false) {\n options.onChange?.({ path, kind: 'closed-object' });\n }\n closed.additionalProperties = false;\n }\n\n return closed;\n}\n","/** Synchronous SHA-256 over UTF-8, shared by the Node and browser core bundles. */\nconst ROUND_CONSTANTS = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst INITIAL_HASH = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\nfunction rotateRight(value: number, bits: number): number {\n return (value >>> bits) | (value << (32 - bits));\n}\n\nexport function sha256Hex(input: string): string {\n const bytes = new TextEncoder().encode(input);\n const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;\n const padded = new Uint8Array(paddedLength);\n padded.set(bytes);\n padded[bytes.length] = 0x80;\n const view = new DataView(padded.buffer);\n const bitLength = BigInt(bytes.length) * 8n;\n view.setUint32(paddedLength - 8, Number((bitLength >> 32n) & 0xffff_ffffn));\n view.setUint32(paddedLength - 4, Number(bitLength & 0xffff_ffffn));\n\n const hash = new Uint32Array(INITIAL_HASH);\n const words = new Uint32Array(64);\n for (let offset = 0; offset < paddedLength; offset += 64) {\n for (let index = 0; index < 16; index += 1) {\n words[index] = view.getUint32(offset + index * 4);\n }\n for (let index = 16; index < 64; index += 1) {\n const left = words[index - 15] ?? 0;\n const right = words[index - 2] ?? 0;\n const small0 = rotateRight(left, 7) ^ rotateRight(left, 18) ^ (left >>> 3);\n const small1 = rotateRight(right, 17) ^ rotateRight(right, 19) ^ (right >>> 10);\n words[index] = ((words[index - 16] ?? 0) + small0 + (words[index - 7] ?? 0) + small1) >>> 0;\n }\n\n let a = hash[0] ?? 0;\n let b = hash[1] ?? 0;\n let c = hash[2] ?? 0;\n let d = hash[3] ?? 0;\n let e = hash[4] ?? 0;\n let f = hash[5] ?? 0;\n let g = hash[6] ?? 0;\n let h = hash[7] ?? 0;\n for (let index = 0; index < 64; index += 1) {\n const big1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);\n const choose = (e & f) ^ (~e & g);\n const temp1 = (h + big1 + choose + (ROUND_CONSTANTS[index] ?? 0) + (words[index] ?? 0)) >>> 0;\n const big0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);\n const majority = (a & b) ^ (a & c) ^ (b & c);\n const temp2 = (big0 + majority) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + temp1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (temp1 + temp2) >>> 0;\n }\n hash[0] = ((hash[0] ?? 0) + a) >>> 0;\n hash[1] = ((hash[1] ?? 0) + b) >>> 0;\n hash[2] = ((hash[2] ?? 0) + c) >>> 0;\n hash[3] = ((hash[3] ?? 0) + d) >>> 0;\n hash[4] = ((hash[4] ?? 0) + e) >>> 0;\n hash[5] = ((hash[5] ?? 0) + f) >>> 0;\n hash[6] = ((hash[6] ?? 0) + g) >>> 0;\n hash[7] = ((hash[7] ?? 0) + h) >>> 0;\n }\n return [...hash].map((word) => word.toString(16).padStart(8, '0')).join('');\n}\n","/**\n * Project an MCP tool's schema into what one provider's wire format can actually carry — MCP-005.\n *\n * Four converters used to reshape `IToolSchema.parameters` independently and disagreed with each\n * other and with the provider APIs (Anthropic cast it straight through, the openai-compatible family\n * shallow-spread it, OpenAI closed objects only when `strictTools` was on, Gemini rebuilt it field by\n * field and silently dropped anything it did not copy). A schema a provider rejects failed the WHOLE\n * request, or was silently narrowed. `projectToolSchema` is the one shared, tested projector every\n * provider calls instead: pure, deterministic, never throws, never mutates its input. A tool that\n * cannot be projected is REJECTED with a structured diagnostic — never dropped silently, never sent\n * in another shape (No Fallback Policy).\n *\n * This is NOT a second validator. CORE-040 (`agent-mcp/src/third-party-schema.ts`) stays the one\n * narrowing step for untrusted third-party schemas down to `IParameterSchema`; this module only\n * projects an already-`IParameterSchema`-shaped tool down to what one provider's profile accepts.\n * The universal subset (CORE-039, `../interfaces/tool-schema.ts`) is the SSOT this module walks —\n * `PARAMETER_SCHEMA_KEYWORDS` is derived from it once, at the type level, so the two cannot drift.\n *\n * See `docs/SPEC.md` § \"Tool Schema Projection (MCP-005)\" for the full contract.\n */\n\nimport { closeObjectSchemas } from './close-object-schemas';\nimport { sha256Hex } from './sha256';\n\nimport type {\n IObjectParameterSchema,\n IParameterSchema,\n IToolSchema,\n} from '../interfaces/tool-schema';\n\n/**\n * Depth and node ceilings shared by every profile — a schema is unbounded complexity otherwise, and\n * MCP itself sets no ceiling (the incompatibility lives entirely at the provider boundary).\n */\nexport const TOOL_SCHEMA_PROJECTION_MAX_DEPTH = 32;\nexport const TOOL_SCHEMA_PROJECTION_MAX_NODES = 2000;\n\n/**\n * Every member `IParameterSchema` declares (CORE-039, `../interfaces/tool-schema.ts:48-63`), derived\n * ONCE at the type level rather than hand-typed a second time. `Required<IParameterSchema>` forces\n * this record literal to gain or lose a property whenever the interface does — a missing or\n * extraneous key here is a compile error, not a silent drift.\n */\ntype TParameterSchemaKeywordRecord = { [K in keyof Required<IParameterSchema>]: true };\nconst PARAMETER_SCHEMA_KEYWORD_RECORD: TParameterSchemaKeywordRecord = {\n type: true,\n description: true,\n enum: true,\n items: true,\n properties: true,\n required: true,\n anyOf: true,\n additionalProperties: true,\n minimum: true,\n maximum: true,\n pattern: true,\n format: true,\n default: true,\n};\n/** The `IParameterSchema` member set, as a lookup — the SSOT `unknownKeywords` judges every key against. */\nexport const PARAMETER_SCHEMA_KEYWORDS: readonly (keyof IParameterSchema)[] = Object.keys(\n PARAMETER_SCHEMA_KEYWORD_RECORD,\n) as (keyof IParameterSchema)[];\nconst PARAMETER_SCHEMA_KEYWORD_SET = new Set<string>(PARAMETER_SCHEMA_KEYWORDS);\n\n/** Property names that risk prototype pollution if ever assigned as an object key. Refused outright. */\nconst PROTOTYPE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Stripped/replaced keywords that constrain nothing (pure metadata) — removing one produces no\n * `Schema note`, unlike a validation keyword (`minLength`, `pattern`, `additionalProperties`, a\n * replaced `$ref`/`oneOf`/`allOf` node, …) whose removal actually narrows what the schema states.\n */\nconst ANNOTATION_ONLY_KEYWORDS = new Set(['title', '$schema', '$comment', 'examples', 'default']);\n\n/**\n * A node that accepts any JSON value, expressed IN the universal subset — the same shape CORE-040\n * substitutes for an inexpressible node (`agent-mcp/src/third-party-schema.ts`, `anyValueNode`).\n * Replacing (not deleting) a structurally-empty node keeps a declared property declared: an object\n * node with `properties` is CLOSED relative to that set, so deleting the key would turn it into an\n * \"unexpected additional property\" instead of a value whose shape merely could not be projected.\n */\nfunction anyValueNode(): IParameterSchema {\n return {\n anyOf: [\n { type: 'string' },\n { type: 'number' },\n { type: 'boolean' },\n { type: 'object' },\n { type: 'array' },\n { type: 'null' },\n ],\n };\n}\n\n/** How one provider's wire format constrains the universal subset it is handed. */\nexport interface IToolSchemaProjectionProfile {\n /** Named in the `Schema note` and the quarantine line. */\n readonly providerName: string;\n /** `additionalProperties: false` on every object node. */\n readonly closedObjects: boolean;\n /** Every property listed in `required` (lossy — pairs with `optionalAsNullable`). */\n readonly requireAllProperties: boolean;\n /** An optional property becomes `anyOf: [T, { type: 'null' }]` once forced into `required`. */\n readonly optionalAsNullable: boolean;\n /** Keywords outside the `IParameterSchema` member set: pass through, remove/replace, or refuse. */\n readonly unknownKeywords: 'adopt' | 'strip' | 'reject';\n /** Subset members this provider's wire type cannot carry — stripped AND recorded (Gemini: `additionalProperties`). */\n readonly unsupportedMembers: readonly (keyof IParameterSchema)[];\n /** Nesting depth over which a schema is rejected (unbounded complexity). */\n readonly maxDepth: number;\n /** Node count over which a schema is rejected. */\n readonly maxNodes: number;\n}\n\n/**\n * `PERMISSIVE`/`STRICT` without a `providerName` — the two profile SHAPES named providers spread\n * their own name onto (`{ ...PERMISSIVE_TOOL_SCHEMA_PROFILE, providerName: 'anthropic' }`).\n */\nexport type TToolSchemaProjectionProfileBase = Omit<IToolSchemaProjectionProfile, 'providerName'>;\n\n/**\n * Anthropic; OpenAI Chat Completions and Responses without `strictTools`; the openai-compatible\n * family (gemma, qwen incl. its Responses surface, deepseek). Standard JSON Schema passes through —\n * stripping it would be lossy for nothing.\n */\nexport const PERMISSIVE_TOOL_SCHEMA_PROFILE: TToolSchemaProjectionProfileBase = {\n closedObjects: false,\n requireAllProperties: false,\n optionalAsNullable: false,\n unknownKeywords: 'adopt',\n unsupportedMembers: [],\n maxDepth: TOOL_SCHEMA_PROJECTION_MAX_DEPTH,\n maxNodes: TOOL_SCHEMA_PROJECTION_MAX_NODES,\n};\n\n/**\n * OpenAI Responses and Chat Completions with `strictTools: true`. Replaces the converter's own\n * `closeObjectSchemas` call — one closure, in one place.\n */\nexport const STRICT_TOOL_SCHEMA_PROFILE: TToolSchemaProjectionProfileBase = {\n closedObjects: true,\n requireAllProperties: true,\n optionalAsNullable: true,\n unknownKeywords: 'strip',\n unsupportedMembers: [],\n maxDepth: TOOL_SCHEMA_PROJECTION_MAX_DEPTH,\n maxNodes: TOOL_SCHEMA_PROJECTION_MAX_NODES,\n};\n\nexport type TToolSchemaProjectionOutcome = 'adopted' | 'adapted' | 'rejected';\n\nexport interface IToolSchemaProjectionChange {\n readonly path: string;\n readonly kind:\n | 'closed-object'\n | 'required-added'\n | 'nullable-added'\n | 'keyword-stripped'\n | 'keyword-replaced'\n | 'member-stripped';\n readonly keyword?: string;\n}\n\nexport interface IToolSchemaProjection {\n readonly outcome: TToolSchemaProjectionOutcome;\n /** adopted: the input (same reference); adapted: the projected copy; rejected: the input. */\n readonly tool: IToolSchema;\n readonly changes: readonly IToolSchemaProjectionChange[];\n readonly rejection?: { readonly path: string; readonly keyword: string; readonly reason: string };\n}\n\ninterface IRejection {\n readonly path: string;\n readonly keyword: string;\n readonly reason: string;\n}\n\n/** JSON-pointer-like path join — `''` at the root, `/properties/foo/items` below it. */\nfunction childPath(path: string, segment: string): string {\n return `${path}/${segment}`;\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * The refusal walk (§ Decision, \"Refusals\"). Depth and node count are counted over the WHOLE graph,\n * not just the branch that eventually fails, so two calls on the same schema agree regardless of\n * which branch a caller happens to look at (determinism).\n */\nfunction findRejection(\n node: unknown,\n path: string,\n depth: number,\n visited: Set<object>,\n counter: { count: number },\n profile: IToolSchemaProjectionProfile,\n): IRejection | undefined {\n if (!isPlainRecord(node)) {\n return undefined;\n }\n // `visited` is the ANCESTOR chain, not a whole-graph set: a node is added on entry and removed on\n // exit below, so only a node that appears inside its own subtree (a true cycle) is refused. A DAG —\n // the same sub-schema object reused by two sibling properties or branches, common in hand-built\n // in-process tools — is walked twice and accepted (pr-review-reviewer, MCP-005 Round A).\n if (visited.has(node)) {\n return {\n path,\n keyword: 'cycle',\n reason: 'object graph revisits a node on its own ancestor chain (cycle)',\n };\n }\n visited.add(node);\n\n if (depth > profile.maxDepth) {\n return {\n path,\n keyword: 'maxDepth',\n reason: `nesting depth exceeds maxDepth (${profile.maxDepth})`,\n };\n }\n counter.count += 1;\n if (counter.count > profile.maxNodes) {\n return {\n path,\n keyword: 'maxNodes',\n reason: `node count exceeds maxNodes (${profile.maxNodes})`,\n };\n }\n\n // `'reject'` treats ANY foreign keyword as fatal by itself, named ahead of the structural check\n // below so the reported reason names the actual offending keyword rather than the generic\n // type/anyOf shape it happens to also violate.\n if (profile.unknownKeywords === 'reject') {\n for (const key of Object.keys(node)) {\n if (!PARAMETER_SCHEMA_KEYWORD_SET.has(key)) {\n return {\n path,\n keyword: key,\n reason: `unknown keyword '${key}' is outside the IParameterSchema member set`,\n };\n }\n }\n }\n\n const hasType = typeof node.type === 'string';\n const hasAnyOf = Array.isArray(node.anyOf);\n if (hasType && hasAnyOf) {\n return {\n path,\n keyword: 'anyOf',\n reason: \"a node must declare exactly one of 'type' or 'anyOf', not both\",\n };\n }\n if (!hasType && !hasAnyOf) {\n // A node whose ONLY structural indicator is a foreign keyword ($ref/oneOf/allOf — ordinary JSON\n // Schema this repo does not model) is not universally refused when the profile has a way to carry\n // it: `'adopt'` passes it through untouched (Anthropic and OpenAI non-strict accept standard JSON\n // Schema), and `'strip'` deletes the keyword and, finding the node then empty, REPLACES it with\n // the accept-anything node rather than losing the declared property. `'reject'` never reaches\n // here for such a node — the scan above already refused it by keyword. A node with NO foreign\n // keyword at all — genuinely empty, or carrying only other subset members — has nothing to defer\n // to and stays universally refused under every policy.\n const hasForeignKey = Object.keys(node).some((key) => !PARAMETER_SCHEMA_KEYWORD_SET.has(key));\n if (!hasForeignKey) {\n return {\n path,\n keyword: 'type',\n reason: \"a node must declare exactly one of 'type' or 'anyOf'\",\n };\n }\n }\n\n const properties = node.properties;\n if (isPlainRecord(properties)) {\n for (const key of Object.keys(properties)) {\n if (PROTOTYPE_KEYS.has(key)) {\n return {\n path: childPath(childPath(path, 'properties'), key),\n keyword: key,\n reason: `property name '${key}' is a prototype key`,\n };\n }\n }\n for (const key of Object.keys(properties)) {\n const rejection = findRejection(\n properties[key],\n childPath(childPath(path, 'properties'), key),\n depth + 1,\n visited,\n counter,\n profile,\n );\n if (rejection) return rejection;\n }\n }\n\n if (isPlainRecord(node.items)) {\n const rejection = findRejection(\n node.items,\n childPath(path, 'items'),\n depth + 1,\n visited,\n counter,\n profile,\n );\n if (rejection) return rejection;\n }\n\n if (Array.isArray(node.anyOf)) {\n for (let index = 0; index < node.anyOf.length; index += 1) {\n const rejection = findRejection(\n node.anyOf[index],\n childPath(childPath(path, 'anyOf'), String(index)),\n depth + 1,\n visited,\n counter,\n profile,\n );\n if (rejection) return rejection;\n }\n }\n\n visited.delete(node);\n return undefined;\n}\n\n/**\n * The adaptation walk: `unsupportedMembers`, then `unknownKeywords`, at every node; recurses into\n * every route a nested node can arrive by (`properties`, `items`, `anyOf`) so a missed route cannot\n * leave the exact thing this walk exists to fix untouched (the CORE-039 lesson). Closure is NOT done\n * here — it delegates to `closeObjectSchemas` once, afterwards, over the whole result.\n */\nfunction adaptNode(\n node: Record<string, unknown>,\n path: string,\n profile: IToolSchemaProjectionProfile,\n changes: IToolSchemaProjectionChange[],\n): Record<string, unknown> {\n const current: Record<string, unknown> = { ...node };\n\n for (const member of profile.unsupportedMembers) {\n const key = member as string;\n if (key in current) {\n delete current[key];\n changes.push({ path, kind: 'member-stripped', keyword: key });\n }\n }\n\n if (profile.unknownKeywords === 'strip') {\n const foreign = Object.keys(current).filter((key) => !PARAMETER_SCHEMA_KEYWORD_SET.has(key));\n for (const key of foreign) delete current[key];\n const hasType = typeof current.type === 'string';\n const hasAnyOf = Array.isArray(current.anyOf);\n if (foreign.length > 0 && !hasType && !hasAnyOf) {\n // The node's structural indicator was a foreign keyword ($ref/oneOf/allOf): the node is REPLACED\n // by the accept-anything node, one `keyword-replaced` per foreign keyword. Legitimate members that\n // sat beside it are not lost silently: `description` travels onto the replacement (it constrains\n // nothing); every other survivor is recorded as `member-stripped` so the loss reaches the Schema\n // note (pr-review-reviewer, MCP-005 Round A — the `$ref` + local `description` idiom).\n for (const key of foreign) changes.push({ path, kind: 'keyword-replaced', keyword: key });\n const replacement = anyValueNode() as unknown as Record<string, unknown>;\n for (const key of Object.keys(current)) {\n if (key === 'description') {\n replacement.description = current.description;\n } else {\n changes.push({ path, kind: 'member-stripped', keyword: key });\n }\n }\n return replacement;\n }\n for (const key of foreign) changes.push({ path, kind: 'keyword-stripped', keyword: key });\n }\n\n const properties = current.properties;\n if (isPlainRecord(properties)) {\n // `Object.fromEntries` — not a manual `record[key] = value` loop — because `key` is an untrusted\n // property NAME. A prototype-key node is refused before this walk ever runs, but `fromEntries`\n // defines every entry as an own property regardless, so this route stays safe even if that\n // ordering ever changes.\n current.properties = Object.fromEntries(\n Object.keys(properties).map((key) => {\n const child = properties[key];\n return [\n key,\n isPlainRecord(child)\n ? adaptNode(child, childPath(childPath(path, 'properties'), key), profile, changes)\n : child,\n ];\n }),\n );\n }\n\n if (isPlainRecord(current.items)) {\n current.items = adaptNode(current.items, childPath(path, 'items'), profile, changes);\n }\n\n if (Array.isArray(current.anyOf)) {\n current.anyOf = current.anyOf.map((branch: unknown, index: number) =>\n isPlainRecord(branch)\n ? adaptNode(branch, childPath(childPath(path, 'anyOf'), String(index)), profile, changes)\n : branch,\n );\n }\n\n return current;\n}\n\n/** Canonical, key-sorted JSON — the same schema hashes the same regardless of authored key order. */\nfunction canonicalize(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(canonicalize);\n }\n if (isPlainRecord(value)) {\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = canonicalize(value[key]);\n }\n return sorted;\n }\n return value;\n}\n\n/**\n * A stable hash of a tool's `parameters`, over canonical key-sorted JSON. Part of the `projectTools`\n * per-tool cache identity (`provider.name` + `model` + `tool.name` + this hash) — a re-registered\n * tool whose schema changed is judged and reported again, one whose schema did not is not re-reported\n * on every turn. The synchronous digest uses the same implementation in Node and browsers while\n * keeping agent-core free of runtime dependencies.\n */\nexport function hashToolSchema(parameters: IParameterSchema): string {\n return sha256Hex(JSON.stringify(canonicalize(parameters)));\n}\n\n/**\n * Project one tool's schema into what `profile` can carry. Pure, deterministic (same input → deep-\n * equal output), never throws, never mutates `tool` or `tool.parameters`.\n */\nexport function projectToolSchema(\n tool: IToolSchema,\n profile: IToolSchemaProjectionProfile,\n): IToolSchemaProjection {\n const root: unknown = tool.parameters;\n if (!isPlainRecord(root) || root.type !== 'object') {\n return {\n outcome: 'rejected',\n tool,\n changes: [],\n rejection: {\n path: '',\n keyword: 'type',\n reason:\n \"the parameters root must declare type: 'object' — no documented provider accepts another root\",\n },\n };\n }\n\n const rejection = findRejection(root, '', 0, new Set<object>(), { count: 0 }, profile);\n if (rejection) {\n return { outcome: 'rejected', tool, changes: [], rejection };\n }\n\n const changes: IToolSchemaProjectionChange[] = [];\n const keywordAdapted = adaptNode(root, '', profile, changes);\n\n const finalParameters = profile.closedObjects\n ? (closeObjectSchemas(keywordAdapted, {\n requireAllProperties: profile.requireAllProperties,\n optionalAsNullable: profile.optionalAsNullable,\n onChange: (change) => changes.push(change),\n }) as IObjectParameterSchema)\n : (keywordAdapted as unknown as IObjectParameterSchema);\n\n if (changes.length === 0) {\n return { outcome: 'adopted', tool, changes: [] };\n }\n\n const noteworthy = changes.filter(\n (change) =>\n (change.kind === 'keyword-stripped' ||\n change.kind === 'keyword-replaced' ||\n change.kind === 'member-stripped') &&\n !ANNOTATION_ONLY_KEYWORDS.has(change.keyword ?? ''),\n );\n\n const description =\n noteworthy.length === 0\n ? tool.description\n : `${tool.description}\\n\\nSchema note: ${noteworthy.length} constraint(s) not shown to ${profile.providerName}: ${noteworthy\n .map((change) => `${change.kind}@${change.path}`)\n .join(', ')}`;\n\n return {\n outcome: 'adapted',\n tool: { ...tool, parameters: finalParameters, description },\n changes,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};export{t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return o}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../project-tool-schema-DrDjGKnH.cjs");let t=require("node:fs"),n=require("node:crypto");function r(e){let t=[],n=[],r=0;return{provider:{name:`scripted-test-provider`,version:`test`,async chat(i,a){t.push([...i]),n.push(a);let o=e[r];if(o===void 0)throw Error(`Scripted provider: script exhausted at call ${r+1} (script declares ${e.length} turn(s)) — extend the script instead of relying on improvised responses`);r+=1;let s=o.usage?{metadata:{inputTokens:o.usage.inputTokens,outputTokens:o.usage.outputTokens,totalTokens:o.usage.inputTokens+o.usage.outputTokens}}:{};return`text`in o?(o.text.length>0&&a?.onTextDelta?.(o.text),{id:`scripted-${r}`,role:`assistant`,content:o.text,state:`complete`,timestamp:new Date,...s}):{id:`scripted-${r}`,role:`assistant`,content:null,state:`complete`,timestamp:new Date,...s,toolCalls:o.toolCalls.map((e,t)=>({id:`scripted-call-${r}-${t}`,type:`function`,function:{name:e.name,arguments:JSON.stringify(e.args)}}))}},async generateResponse(){return{content:`scripted provider does not implement raw responses`}},supportsTools(){return!0},validateConfig(){return!0}},requests:t,chatOptions:n}}function i(e,t){let n=e;for(let e of t)e&&(n=n.split(e).join(`\0SCRUBBED\0`));return n}function a(e,t){let n=e.filter(e=>e.role!==`system`).map(e=>{let n=`toolCalls`in e?e.toolCalls:void 0;return{role:e.role,content:typeof e.content==`string`?i(e.content,t):e.content??null,toolCalls:n?.map(e=>({name:e.function?.name??null,args:e.function?.arguments?i(e.function.arguments,t):null}))??null}});return JSON.stringify(n)}function o(e,t){return(0,n.createHash)(`sha256`).update(a(e,t)).digest(`hex`).slice(0,16)}function s(e,t){let n=e[e.length-1],r=typeof n?.content==`string`?i(n.content,t):``;return`${e.length} msg(s); last ${n?.role??`?`}: ${r.slice(0,80)}`}function c(e){return{generateResponse:e.generateResponse.bind(e),supportsTools:e.supportsTools.bind(e),validateConfig:e.validateConfig.bind(e)}}function l(e){let n=e.scrub??(e.recordCwd?[e.recordCwd]:[]),r=[],i=()=>{let n={version:1,provider:e.provider.name,...e.model===void 0?{}:{model:e.model},...e.recordCwd===void 0?{}:{recordCwd:e.recordCwd},interactions:r};(0,t.writeFileSync)(e.cassettePath,`${JSON.stringify(n,null,2)}\n`,`utf8`)};return{name:e.provider.name,version:e.provider.version,async chat(t,a){let c=await e.provider.chat(t,a);return r.push({requestHash:o(t,n),requestPreview:s(t,n),response:c}),i(),c},...c(e.provider)}}function u(e,t,n){let r=e;return t&&n&&t!==n&&(r=JSON.parse(JSON.stringify(r).split(n).join(t))),{...r,timestamp:new Date(r.timestamp)}}function d(e){let n=JSON.parse((0,t.readFileSync)(e.cassettePath,`utf8`)),r=e.scrub??(e.rewriteCwd?[e.rewriteCwd]:[]),i=0;return{name:`cassette-replay-provider`,version:`test`,async chat(t){let a=n.interactions[i];if(!a)throw Error(`Cassette exhausted at call ${i+1} (recorded ${n.interactions.length}) — re-record ${e.cassettePath}`);if(o(t,r)!==a.requestHash)throw Error(`Cassette stale at call ${i+1}: the request changed since recording.\n recorded: ${a.requestPreview}\n current : ${s(t,r)}\nRe-record ${e.cassettePath}.`);return i+=1,u(a.response,e.rewriteCwd,n.recordCwd)},async generateResponse(){return{content:`cassette replay does not implement raw responses`}},supportsTools(){return!0},validateConfig(){return!0}}}var f={name:`bulk_upsert`,description:`An array property whose items are an object schema.`,parameters:{type:`object`,properties:{records:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},value:{type:`number`}},required:[`id`]}}},required:[`records`]}},p={name:`deeply_nested_tool`,description:`A schema nested 40 levels deep - over every profile maxDepth (32).`,parameters:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`string`}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},m={name:`update_profile`,description:`Update a user profile; nickname is optional and already nullable on the wire.`,parameters:{type:`object`,properties:{userId:{type:`string`},nickname:{anyOf:[{type:`string`},{type:`null`}]}},required:[`userId`]}},h=[{name:`good_tool_one`,description:`A well-formed tool.`,parameters:{type:`object`,properties:{query:{type:`string`}},required:[`query`]}},{name:`bad_tool`,description:`A tool whose root is a union rather than a single object.`,parameters:{anyOf:[{type:`object`,properties:{a:{type:`string`}},required:[`a`]},{type:`object`,properties:{b:{type:`number`}},required:[`b`]}]}},{name:`good_tool_two`,description:`Another well-formed tool.`,parameters:{type:`object`,properties:{id:{type:`string`}},required:[`id`]}}],g={name:`flexible_root`,description:`A tool whose root is a union rather than a single object — no documented provider accepts this.`,parameters:{anyOf:[{type:`object`,properties:{a:{type:`string`}},required:[`a`]},{type:`object`,properties:{b:{type:`number`}},required:[`b`]}]}},_={name:`search_records`,description:`Search records by query and return matching entries.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`The search query.`},limit:{type:`integer`,minimum:1,maximum:100},tags:{type:`array`,items:{type:`string`}},address:{type:`object`,properties:{city:{type:`string`},zip:{type:`string`,pattern:`^[0-9]{5}$`}},required:[`city`]}},required:[`query`]}},v={name:`third_party_search`,description:`A third-party MCP tool's schema carrying keywords outside the IParameterSchema member set.`,parameters:{type:`object`,properties:{refOnly:{$ref:`#/$defs/Query`},oneOfOnly:{oneOf:[{type:`string`},{type:`number`}]},allOfOnly:{allOf:[{type:`object`,properties:{x:{type:`string`}}},{type:`object`,properties:{y:{type:`string`}}}]},annotated:{type:`string`,title:`Annotated field`,$schema:`https://json-schema.org/draft/2020-12/schema`,$comment:`internal note`,examples:[`abc`]},patternMap:{type:`object`,properties:{known:{type:`string`}},patternProperties:{"^x-":{type:`string`}}},withDefs:{type:`object`,properties:{known:{type:`string`}},$defs:{Query:{type:`string`}}},shortText:{type:`string`,minLength:3}},required:[`refOnly`]}};function y(){return{name:`malicious_property_name`,description:`A property named as a prototype key — reachable from a parsed third-party schema.`,parameters:{type:`object`,properties:{safe:{type:`string`},["__proto__"]:{type:`string`}},required:[`safe`]}}}function b(){let t={},n=e.a+500;for(let e=0;e<n;e+=1)t[`field_${e}`]={type:`string`};return{name:`oversized_tool`,description:`A schema with more nodes than any shipped profile's maxNodes ceiling.`,parameters:{type:`object`,properties:t,required:[]}}}function x(){return{subsetOnly:_,rootAnyOf:g,nestedAnyOfOptional:m,unknownKeywords:v,prototypeKey:y(),deepNesting:p,oversized:b(),arrayItemsObject:f,oneInvalidAmongMany:h}}exports.createRecordingProvider=l,exports.createReplayProvider=d,exports.createScriptedProvider=r,exports.loadToolSchemaProjectionFixtures=x;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Js as IAIProvider, Ys as IChatOptions, dc as IToolSchema, pl as TUniversalMessage } from "../index-CiFlpr7-.cjs";
|
|
2
|
+
//#region src/testing/scripted-provider.d.ts
|
|
3
|
+
/** ANALYTICS-001: optional token usage a scripted turn reports, so usage-based tests are possible. */
|
|
4
|
+
interface IScriptedTurnUsage {
|
|
5
|
+
inputTokens: number;
|
|
6
|
+
outputTokens: number;
|
|
7
|
+
}
|
|
8
|
+
/** One scripted assistant turn: plain text or tool invocations, optionally reporting token usage. */
|
|
9
|
+
type TScriptedTurn = {
|
|
10
|
+
text: string;
|
|
11
|
+
usage?: IScriptedTurnUsage;
|
|
12
|
+
} | {
|
|
13
|
+
toolCalls: ReadonlyArray<{
|
|
14
|
+
name: string;
|
|
15
|
+
args: Record<string, unknown>;
|
|
16
|
+
}>;
|
|
17
|
+
usage?: IScriptedTurnUsage;
|
|
18
|
+
};
|
|
19
|
+
interface IScriptedProvider {
|
|
20
|
+
provider: IAIProvider;
|
|
21
|
+
/** Message arrays of every chat() call, in order, for request assertions. */
|
|
22
|
+
requests: TUniversalMessage[][];
|
|
23
|
+
/**
|
|
24
|
+
* The `IChatOptions` of every chat() call, in order.
|
|
25
|
+
*
|
|
26
|
+
* CORE-042: the two entry points must build the SAME options object, and nothing could assert
|
|
27
|
+
* that while the double discarded them.
|
|
28
|
+
*/
|
|
29
|
+
chatOptions: Array<IChatOptions | undefined>;
|
|
30
|
+
}
|
|
31
|
+
declare function createScriptedProvider(turns: readonly TScriptedTurn[]): IScriptedProvider;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/testing/cassette-provider.d.ts
|
|
34
|
+
/** One recorded request→response interaction. */
|
|
35
|
+
interface ICassetteInteraction {
|
|
36
|
+
requestHash: string;
|
|
37
|
+
requestPreview: string;
|
|
38
|
+
response: TUniversalMessage;
|
|
39
|
+
}
|
|
40
|
+
/** A recorded session: an ordered list of interactions plus metadata. */
|
|
41
|
+
interface ICassette {
|
|
42
|
+
version: 1;
|
|
43
|
+
provider: string;
|
|
44
|
+
model?: string;
|
|
45
|
+
/** Recorded workspace path, stored verbatim so replay can rewrite it into its own workspace. */
|
|
46
|
+
recordCwd?: string;
|
|
47
|
+
interactions: ICassetteInteraction[];
|
|
48
|
+
}
|
|
49
|
+
interface IRecordingProviderOptions {
|
|
50
|
+
provider: IAIProvider;
|
|
51
|
+
cassettePath: string;
|
|
52
|
+
/** Volatile substrings normalized out of the staleness hash (e.g. the workspace path). */
|
|
53
|
+
scrub?: readonly string[];
|
|
54
|
+
/** Recorded workspace path; replay rewrites it into the replay workspace. */
|
|
55
|
+
recordCwd?: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Wrap a real provider, recording every interaction to the cassette file as it runs. */
|
|
59
|
+
declare function createRecordingProvider(options: IRecordingProviderOptions): IAIProvider;
|
|
60
|
+
interface IReplayProviderOptions {
|
|
61
|
+
cassettePath: string;
|
|
62
|
+
/** Volatile substrings normalized out of the staleness hash (must match the record-time scrub). */
|
|
63
|
+
scrub?: readonly string[];
|
|
64
|
+
/** Replay workspace path; the recorded workspace path in responses is rewritten to it. */
|
|
65
|
+
rewriteCwd?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Replay a recorded cassette deterministically, failing clearly on staleness or exhaustion. */
|
|
68
|
+
declare function createReplayProvider(options: IReplayProviderOptions): IAIProvider;
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/testing/tool-schema-projection-fixtures.d.ts
|
|
71
|
+
/** The named tool-schema-projection fixture set (MCP-005). Treat every value as read-only. */
|
|
72
|
+
interface IToolSchemaProjectionFixtures {
|
|
73
|
+
/** Only members of the `IParameterSchema` subset — projects `adopted` under every profile. */
|
|
74
|
+
readonly subsetOnly: IToolSchema;
|
|
75
|
+
/** `parameters` is a root union, not `type: 'object'` — universal rejection. */
|
|
76
|
+
readonly rootAnyOf: IToolSchema;
|
|
77
|
+
/** A nested (non-root) optional `anyOf` that already admits `null` — legitimate union usage. */
|
|
78
|
+
readonly nestedAnyOfOptional: IToolSchema;
|
|
79
|
+
/** `$ref`/`oneOf`/`allOf`-only nodes, annotations, `patternProperties`, `$defs`, `minLength`. */
|
|
80
|
+
readonly unknownKeywords: IToolSchema;
|
|
81
|
+
/** A property named `__proto__` — refused before any spread. */
|
|
82
|
+
readonly prototypeKey: IToolSchema;
|
|
83
|
+
/** Nested 40 levels deep — over every shipped profile's `maxDepth` (32). */
|
|
84
|
+
readonly deepNesting: IToolSchema;
|
|
85
|
+
/** Over every shipped profile's `maxNodes` (2000). */
|
|
86
|
+
readonly oversized: IToolSchema;
|
|
87
|
+
/** An array property whose `items` is an object schema. */
|
|
88
|
+
readonly arrayItemsObject: IToolSchema;
|
|
89
|
+
/** Three tools; the middle one is a root-union rejection — per-tool quarantine, not a whole-request failure. */
|
|
90
|
+
readonly oneInvalidAmongMany: readonly IToolSchema[];
|
|
91
|
+
}
|
|
92
|
+
/** The shared MCP-005 fixture set. Returns the same underlying values on every call — do not mutate them. */
|
|
93
|
+
declare function loadToolSchemaProjectionFixtures(): IToolSchemaProjectionFixtures;
|
|
94
|
+
//#endregion
|
|
95
|
+
export { type ICassette, type ICassetteInteraction, type IRecordingProviderOptions, type IReplayProviderOptions, type IScriptedProvider, type IToolSchemaProjectionFixtures, type TScriptedTurn, createRecordingProvider, createReplayProvider, createScriptedProvider, loadToolSchemaProjectionFixtures };
|
|
96
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/testing/scripted-provider.ts","../../../src/testing/cassette-provider.ts","../../../src/testing/tool-schema-projection-fixtures.ts"],"mappings":";;;UAoBiB;EACf;EACA;;;KAIU;EACN;EAAc,QAAQ;;EAEtB,WAAW;IAAgB;IAAc,MAAM;;EAC/C,QAAQ;;UAGG;EACf,UAAU;;EAEV,UAAU;;;;;;;EAOV,aAAa,MAAM;;iBAGL,uBAAuB,gBAAgB,kBAAkB;;;;UCjBxD;EACf;EACA;EACA,UAAU;;;UAIK;EACf;EACA;EACA;;EAEA;EACA,cAAc;;UA6DC;EACf,UAAU;EACV;;EAEA;;EAEA;EACA;;;iBAIc,wBAAwB,SAAS,4BAA4B;UAmD5D;EACf;;EAEA;;EAEA;;;iBAIc,qBAAqB,SAAS,yBAAyB;;;;UCtGtD;;WAEN,YAAY;;WAEZ,WAAW;;WAEX,qBAAqB;;WAErB,iBAAiB;;WAEjB,cAAc;;WAEd,aAAa;;WAEb,WAAW;;WAEX,kBAAkB;;WAElB,8BAA8B;;;iBAIzB,oCAAoC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { Js as IAIProvider, Ys as IChatOptions, dc as IToolSchema, pl as TUniversalMessage } from "../index-DFlMw376.js";
|
|
3
2
|
//#region src/testing/scripted-provider.d.ts
|
|
4
3
|
/** ANALYTICS-001: optional token usage a scripted turn reports, so usage-based tests are possible. */
|
|
5
4
|
interface IScriptedTurnUsage {
|
|
@@ -21,6 +20,13 @@ interface IScriptedProvider {
|
|
|
21
20
|
provider: IAIProvider;
|
|
22
21
|
/** Message arrays of every chat() call, in order, for request assertions. */
|
|
23
22
|
requests: TUniversalMessage[][];
|
|
23
|
+
/**
|
|
24
|
+
* The `IChatOptions` of every chat() call, in order.
|
|
25
|
+
*
|
|
26
|
+
* CORE-042: the two entry points must build the SAME options object, and nothing could assert
|
|
27
|
+
* that while the double discarded them.
|
|
28
|
+
*/
|
|
29
|
+
chatOptions: Array<IChatOptions | undefined>;
|
|
24
30
|
}
|
|
25
31
|
declare function createScriptedProvider(turns: readonly TScriptedTurn[]): IScriptedProvider;
|
|
26
32
|
//#endregion
|
|
@@ -36,7 +42,7 @@ interface ICassette {
|
|
|
36
42
|
version: 1;
|
|
37
43
|
provider: string;
|
|
38
44
|
model?: string;
|
|
39
|
-
/** Recorded workspace path, stored
|
|
45
|
+
/** Recorded workspace path, stored verbatim so replay can rewrite it into its own workspace. */
|
|
40
46
|
recordCwd?: string;
|
|
41
47
|
interactions: ICassetteInteraction[];
|
|
42
48
|
}
|
|
@@ -61,5 +67,30 @@ interface IReplayProviderOptions {
|
|
|
61
67
|
/** Replay a recorded cassette deterministically, failing clearly on staleness or exhaustion. */
|
|
62
68
|
declare function createReplayProvider(options: IReplayProviderOptions): IAIProvider;
|
|
63
69
|
//#endregion
|
|
64
|
-
|
|
70
|
+
//#region src/testing/tool-schema-projection-fixtures.d.ts
|
|
71
|
+
/** The named tool-schema-projection fixture set (MCP-005). Treat every value as read-only. */
|
|
72
|
+
interface IToolSchemaProjectionFixtures {
|
|
73
|
+
/** Only members of the `IParameterSchema` subset — projects `adopted` under every profile. */
|
|
74
|
+
readonly subsetOnly: IToolSchema;
|
|
75
|
+
/** `parameters` is a root union, not `type: 'object'` — universal rejection. */
|
|
76
|
+
readonly rootAnyOf: IToolSchema;
|
|
77
|
+
/** A nested (non-root) optional `anyOf` that already admits `null` — legitimate union usage. */
|
|
78
|
+
readonly nestedAnyOfOptional: IToolSchema;
|
|
79
|
+
/** `$ref`/`oneOf`/`allOf`-only nodes, annotations, `patternProperties`, `$defs`, `minLength`. */
|
|
80
|
+
readonly unknownKeywords: IToolSchema;
|
|
81
|
+
/** A property named `__proto__` — refused before any spread. */
|
|
82
|
+
readonly prototypeKey: IToolSchema;
|
|
83
|
+
/** Nested 40 levels deep — over every shipped profile's `maxDepth` (32). */
|
|
84
|
+
readonly deepNesting: IToolSchema;
|
|
85
|
+
/** Over every shipped profile's `maxNodes` (2000). */
|
|
86
|
+
readonly oversized: IToolSchema;
|
|
87
|
+
/** An array property whose `items` is an object schema. */
|
|
88
|
+
readonly arrayItemsObject: IToolSchema;
|
|
89
|
+
/** Three tools; the middle one is a root-union rejection — per-tool quarantine, not a whole-request failure. */
|
|
90
|
+
readonly oneInvalidAmongMany: readonly IToolSchema[];
|
|
91
|
+
}
|
|
92
|
+
/** The shared MCP-005 fixture set. Returns the same underlying values on every call — do not mutate them. */
|
|
93
|
+
declare function loadToolSchemaProjectionFixtures(): IToolSchemaProjectionFixtures;
|
|
94
|
+
//#endregion
|
|
95
|
+
export { type ICassette, type ICassetteInteraction, type IRecordingProviderOptions, type IReplayProviderOptions, type IScriptedProvider, type IToolSchemaProjectionFixtures, type TScriptedTurn, createRecordingProvider, createReplayProvider, createScriptedProvider, loadToolSchemaProjectionFixtures };
|
|
65
96
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/testing/scripted-provider.ts","../../../src/testing/cassette-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/testing/scripted-provider.ts","../../../src/testing/cassette-provider.ts","../../../src/testing/tool-schema-projection-fixtures.ts"],"mappings":";;;UAoBiB;EACf;EACA;;;KAIU;EACN;EAAc,QAAQ;;EAEtB,WAAW;IAAgB;IAAc,MAAM;;EAC/C,QAAQ;;UAGG;EACf,UAAU;;EAEV,UAAU;;;;;;;EAOV,aAAa,MAAM;;iBAGL,uBAAuB,gBAAgB,kBAAkB;;;;UCjBxD;EACf;EACA;EACA,UAAU;;;UAIK;EACf;EACA;EACA;;EAEA;EACA,cAAc;;UA6DC;EACf,UAAU;EACV;;EAEA;;EAEA;EACA;;;iBAIc,wBAAwB,SAAS,4BAA4B;UAmD5D;EACf;;EAEA;;EAEA;;;iBAIc,qBAAqB,SAAS,yBAAyB;;;;UCtGtD;;WAEN,YAAY;;WAEZ,WAAW;;WAEX,qBAAqB;;WAErB,iBAAiB;;WAEjB,cAAc;;WAEd,aAAa;;WAEb,WAAW;;WAEX,kBAAkB;;WAElB,8BAA8B;;;iBAIzB,oCAAoC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{a as e}from"../project-tool-schema-_UKfY7yO.js";import{readFileSync as t,writeFileSync as n}from"node:fs";import{createHash as r}from"node:crypto";function i(e){let t=[],n=[],r=0;return{provider:{name:`scripted-test-provider`,version:`test`,async chat(i,a){t.push([...i]),n.push(a);let o=e[r];if(o===void 0)throw Error(`Scripted provider: script exhausted at call ${r+1} (script declares ${e.length} turn(s)) — extend the script instead of relying on improvised responses`);r+=1;let s=o.usage?{metadata:{inputTokens:o.usage.inputTokens,outputTokens:o.usage.outputTokens,totalTokens:o.usage.inputTokens+o.usage.outputTokens}}:{};return`text`in o?(o.text.length>0&&a?.onTextDelta?.(o.text),{id:`scripted-${r}`,role:`assistant`,content:o.text,state:`complete`,timestamp:new Date,...s}):{id:`scripted-${r}`,role:`assistant`,content:null,state:`complete`,timestamp:new Date,...s,toolCalls:o.toolCalls.map((e,t)=>({id:`scripted-call-${r}-${t}`,type:`function`,function:{name:e.name,arguments:JSON.stringify(e.args)}}))}},async generateResponse(){return{content:`scripted provider does not implement raw responses`}},supportsTools(){return!0},validateConfig(){return!0}},requests:t,chatOptions:n}}function a(e,t){let n=e;for(let e of t)e&&(n=n.split(e).join(`\0SCRUBBED\0`));return n}function o(e,t){let n=e.filter(e=>e.role!==`system`).map(e=>{let n=`toolCalls`in e?e.toolCalls:void 0;return{role:e.role,content:typeof e.content==`string`?a(e.content,t):e.content??null,toolCalls:n?.map(e=>({name:e.function?.name??null,args:e.function?.arguments?a(e.function.arguments,t):null}))??null}});return JSON.stringify(n)}function s(e,t){return r(`sha256`).update(o(e,t)).digest(`hex`).slice(0,16)}function c(e,t){let n=e[e.length-1],r=typeof n?.content==`string`?a(n.content,t):``;return`${e.length} msg(s); last ${n?.role??`?`}: ${r.slice(0,80)}`}function l(e){return{generateResponse:e.generateResponse.bind(e),supportsTools:e.supportsTools.bind(e),validateConfig:e.validateConfig.bind(e)}}function u(e){let t=e.scrub??(e.recordCwd?[e.recordCwd]:[]),r=[],i=()=>{let t={version:1,provider:e.provider.name,...e.model===void 0?{}:{model:e.model},...e.recordCwd===void 0?{}:{recordCwd:e.recordCwd},interactions:r};n(e.cassettePath,`${JSON.stringify(t,null,2)}\n`,`utf8`)};return{name:e.provider.name,version:e.provider.version,async chat(n,a){let o=await e.provider.chat(n,a);return r.push({requestHash:s(n,t),requestPreview:c(n,t),response:o}),i(),o},...l(e.provider)}}function d(e,t,n){let r=e;return t&&n&&t!==n&&(r=JSON.parse(JSON.stringify(r).split(n).join(t))),{...r,timestamp:new Date(r.timestamp)}}function f(e){let n=JSON.parse(t(e.cassettePath,`utf8`)),r=e.scrub??(e.rewriteCwd?[e.rewriteCwd]:[]),i=0;return{name:`cassette-replay-provider`,version:`test`,async chat(t){let a=n.interactions[i];if(!a)throw Error(`Cassette exhausted at call ${i+1} (recorded ${n.interactions.length}) — re-record ${e.cassettePath}`);if(s(t,r)!==a.requestHash)throw Error(`Cassette stale at call ${i+1}: the request changed since recording.\n recorded: ${a.requestPreview}\n current : ${c(t,r)}\nRe-record ${e.cassettePath}.`);return i+=1,d(a.response,e.rewriteCwd,n.recordCwd)},async generateResponse(){return{content:`cassette replay does not implement raw responses`}},supportsTools(){return!0},validateConfig(){return!0}}}var p={name:`bulk_upsert`,description:`An array property whose items are an object schema.`,parameters:{type:`object`,properties:{records:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},value:{type:`number`}},required:[`id`]}}},required:[`records`]}},m={name:`deeply_nested_tool`,description:`A schema nested 40 levels deep - over every profile maxDepth (32).`,parameters:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`object`,properties:{child:{type:`string`}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},required:[`child`]}},h={name:`update_profile`,description:`Update a user profile; nickname is optional and already nullable on the wire.`,parameters:{type:`object`,properties:{userId:{type:`string`},nickname:{anyOf:[{type:`string`},{type:`null`}]}},required:[`userId`]}},g=[{name:`good_tool_one`,description:`A well-formed tool.`,parameters:{type:`object`,properties:{query:{type:`string`}},required:[`query`]}},{name:`bad_tool`,description:`A tool whose root is a union rather than a single object.`,parameters:{anyOf:[{type:`object`,properties:{a:{type:`string`}},required:[`a`]},{type:`object`,properties:{b:{type:`number`}},required:[`b`]}]}},{name:`good_tool_two`,description:`Another well-formed tool.`,parameters:{type:`object`,properties:{id:{type:`string`}},required:[`id`]}}],_={name:`flexible_root`,description:`A tool whose root is a union rather than a single object — no documented provider accepts this.`,parameters:{anyOf:[{type:`object`,properties:{a:{type:`string`}},required:[`a`]},{type:`object`,properties:{b:{type:`number`}},required:[`b`]}]}},v={name:`search_records`,description:`Search records by query and return matching entries.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`The search query.`},limit:{type:`integer`,minimum:1,maximum:100},tags:{type:`array`,items:{type:`string`}},address:{type:`object`,properties:{city:{type:`string`},zip:{type:`string`,pattern:`^[0-9]{5}$`}},required:[`city`]}},required:[`query`]}},y={name:`third_party_search`,description:`A third-party MCP tool's schema carrying keywords outside the IParameterSchema member set.`,parameters:{type:`object`,properties:{refOnly:{$ref:`#/$defs/Query`},oneOfOnly:{oneOf:[{type:`string`},{type:`number`}]},allOfOnly:{allOf:[{type:`object`,properties:{x:{type:`string`}}},{type:`object`,properties:{y:{type:`string`}}}]},annotated:{type:`string`,title:`Annotated field`,$schema:`https://json-schema.org/draft/2020-12/schema`,$comment:`internal note`,examples:[`abc`]},patternMap:{type:`object`,properties:{known:{type:`string`}},patternProperties:{"^x-":{type:`string`}}},withDefs:{type:`object`,properties:{known:{type:`string`}},$defs:{Query:{type:`string`}}},shortText:{type:`string`,minLength:3}},required:[`refOnly`]}};function b(){return{name:`malicious_property_name`,description:`A property named as a prototype key — reachable from a parsed third-party schema.`,parameters:{type:`object`,properties:{safe:{type:`string`},["__proto__"]:{type:`string`}},required:[`safe`]}}}function x(){let t={},n=e+500;for(let e=0;e<n;e+=1)t[`field_${e}`]={type:`string`};return{name:`oversized_tool`,description:`A schema with more nodes than any shipped profile's maxNodes ceiling.`,parameters:{type:`object`,properties:t,required:[]}}}function S(){return{subsetOnly:v,rootAnyOf:_,nestedAnyOfOptional:h,unknownKeywords:y,prototypeKey:b(),deepNesting:m,oversized:x(),arrayItemsObject:p,oneInvalidAmongMany:g}}export{u as createRecordingProvider,f as createReplayProvider,i as createScriptedProvider,S as loadToolSchemaProjectionFixtures};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/testing/scripted-provider.ts","../../../src/testing/cassette-provider.ts"],"sourcesContent":["/**\n * Deterministic scripted provider for functional tests (TEST-003 SSOT).\n *\n * Replays a declared sequence of assistant turns through the REAL agent loop — tool execution,\n * permission gate, persistence, and output all run unmocked. It implements only the agent-core\n * `IAIProvider` contract, so it lives at the lowest layer that owns that abstraction; higher\n * layers (`agent-framework`, `agent-transport`) re-export it through their `./testing` subpaths.\n *\n * Test-only: exported via the `@robota-sdk/agent-core/testing` subpath and never imported by\n * runtime code.\n */\n\nimport type { IAIProvider, IRawProviderResponse, TUniversalMessage } from '../index.js';\n\n/** ANALYTICS-001: optional token usage a scripted turn reports, so usage-based tests are possible. */\nexport interface IScriptedTurnUsage {\n inputTokens: number;\n outputTokens: number;\n}\n\n/** One scripted assistant turn: plain text or tool invocations, optionally reporting token usage. */\nexport type TScriptedTurn =\n | { text: string; usage?: IScriptedTurnUsage }\n | {\n toolCalls: ReadonlyArray<{ name: string; args: Record<string, unknown> }>;\n usage?: IScriptedTurnUsage;\n };\n\nexport interface IScriptedProvider {\n provider: IAIProvider;\n /** Message arrays of every chat() call, in order, for request assertions. */\n requests: TUniversalMessage[][];\n}\n\nexport function createScriptedProvider(turns: readonly TScriptedTurn[]): IScriptedProvider {\n const requests: TUniversalMessage[][] = [];\n let cursor = 0;\n\n const provider: IAIProvider = {\n name: 'scripted-test-provider',\n version: 'test',\n async chat(messages: TUniversalMessage[]): Promise<TUniversalMessage> {\n requests.push([...messages]);\n const turn = turns[cursor];\n if (turn === undefined) {\n throw new Error(\n `Scripted provider: script exhausted at call ${cursor + 1} (script declares ${turns.length} turn(s)) — extend the script instead of relying on improvised responses`,\n );\n }\n cursor += 1;\n // ANALYTICS-001: surface declared usage in metadata so the real usage pipeline records it.\n const usageMetadata = turn.usage\n ? {\n metadata: {\n inputTokens: turn.usage.inputTokens,\n outputTokens: turn.usage.outputTokens,\n totalTokens: turn.usage.inputTokens + turn.usage.outputTokens,\n },\n }\n : {};\n if ('text' in turn) {\n return {\n id: `scripted-${cursor}`,\n role: 'assistant',\n content: turn.text,\n state: 'complete',\n timestamp: new Date(),\n ...usageMetadata,\n };\n }\n return {\n id: `scripted-${cursor}`,\n role: 'assistant',\n content: null,\n state: 'complete',\n timestamp: new Date(),\n ...usageMetadata,\n toolCalls: turn.toolCalls.map((call, index) => ({\n id: `scripted-call-${cursor}-${index}`,\n type: 'function' as const,\n function: { name: call.name, arguments: JSON.stringify(call.args) },\n })),\n };\n },\n async generateResponse(): Promise<IRawProviderResponse> {\n return { content: 'scripted provider does not implement raw responses' };\n },\n supportsTools(): boolean {\n return true;\n },\n validateConfig(): boolean {\n return true;\n },\n };\n\n return { provider, requests };\n}\n","/**\n * TEST-005: record-replay (cassette) provider.\n *\n * The scripted provider verifies the deterministic machinery but ignores the prompt and fakes the\n * model's tool-use. A cassette captures a REAL model's behaviour once (real prompts in, real\n * responses + tool-use out) and replays it deterministically — testing the actual prompts and the\n * model's real decisions in CI at zero per-run cost.\n *\n * - `createRecordingProvider({ provider, cassettePath, … })` wraps a real provider and writes each\n * interaction to the cassette. Used in a one-off record run (needs an API key).\n * - `createReplayProvider({ cassettePath, … })` replays the recorded responses in order, with\n * staleness detection: if the incoming request diverges from the recorded one (e.g. a prompt\n * changed), it fails with a clear \"re-record\" error instead of returning a wrong response.\n *\n * Volatile substrings (e.g. the per-run workspace path) are normalized via `scrub` before hashing\n * so an unchanged flow matches across runs; `rewriteCwd` rewrites the recorded workspace path in\n * replayed responses so recorded absolute tool-call paths land in the replay workspace.\n *\n * Test-only: exported via `@robota-sdk/agent-core/testing`; never imported by runtime code.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFileSync, writeFileSync } from 'node:fs';\n\nimport type { IAIProvider, IChatOptions, IToolCall, TUniversalMessage } from '../index.js';\n\nconst SCRUB_TOKEN = '\u0000SCRUBBED\u0000';\nconst CASSETTE_RECORD_CWD = '\u0000RECORD_CWD\u0000';\n\n/** One recorded request→response interaction. */\nexport interface ICassetteInteraction {\n requestHash: string;\n requestPreview: string;\n response: TUniversalMessage;\n}\n\n/** A recorded session: an ordered list of interactions plus metadata. */\nexport interface ICassette {\n version: 1;\n provider: string;\n model?: string;\n /** Recorded workspace path, stored as a placeholder so replay can rewrite it. */\n recordCwd?: string;\n interactions: ICassetteInteraction[];\n}\n\nfunction applyScrub(text: string, scrub: readonly string[]): string {\n let out = text;\n for (const value of scrub) {\n if (value) out = out.split(value).join(SCRUB_TOKEN);\n }\n return out;\n}\n\n/**\n * Normalized, volatile-free projection of a request used for the staleness hash.\n *\n * System messages are excluded: the system prompt embeds host/version-specific content (platform,\n * paths, tool inventory) that differs across machines, so hashing it would make a cassette\n * non-portable (a stale failure in CI for a run recorded on another host). The portable, meaningful\n * content — the user/goal prompts, the assistant tool calls, and the tool results — is retained, so\n * a change to the goal prompts still trips staleness.\n */\nfunction normalizeRequest(messages: TUniversalMessage[], scrub: readonly string[]): string {\n const projection = messages\n .filter((message) => message.role !== 'system')\n .map((message) => {\n const toolCalls =\n 'toolCalls' in message ? (message.toolCalls as IToolCall[] | undefined) : undefined;\n return {\n role: message.role,\n content:\n typeof message.content === 'string'\n ? applyScrub(message.content, scrub)\n : (message.content ?? null),\n toolCalls:\n toolCalls?.map((call) => ({\n name: call.function?.name ?? null,\n args: call.function?.arguments ? applyScrub(call.function.arguments, scrub) : null,\n })) ?? null,\n };\n });\n return JSON.stringify(projection);\n}\n\nfunction hashRequest(messages: TUniversalMessage[], scrub: readonly string[]): string {\n return createHash('sha256').update(normalizeRequest(messages, scrub)).digest('hex').slice(0, 16);\n}\n\nfunction previewRequest(messages: TUniversalMessage[], scrub: readonly string[]): string {\n const last = messages[messages.length - 1];\n const content = typeof last?.content === 'string' ? applyScrub(last.content, scrub) : '';\n return `${messages.length} msg(s); last ${last?.role ?? '?'}: ${content.slice(0, 80)}`;\n}\n\n/** Forward the non-`chat` surface of a wrapped provider unchanged. */\nfunction delegate(provider: IAIProvider): Omit<IAIProvider, 'chat' | 'name' | 'version'> {\n return {\n generateResponse: provider.generateResponse.bind(provider),\n supportsTools: provider.supportsTools.bind(provider),\n validateConfig: provider.validateConfig.bind(provider),\n };\n}\n\nexport interface IRecordingProviderOptions {\n provider: IAIProvider;\n cassettePath: string;\n /** Volatile substrings normalized out of the staleness hash (e.g. the workspace path). */\n scrub?: readonly string[];\n /** Recorded workspace path; replay rewrites it into the replay workspace. */\n recordCwd?: string;\n model?: string;\n}\n\n/** Wrap a real provider, recording every interaction to the cassette file as it runs. */\nexport function createRecordingProvider(options: IRecordingProviderOptions): IAIProvider {\n const scrub = options.scrub ?? (options.recordCwd ? [options.recordCwd] : []);\n const interactions: ICassetteInteraction[] = [];\n\n const flush = (): void => {\n const cassette: ICassette = {\n version: 1,\n provider: options.provider.name,\n ...(options.model !== undefined ? { model: options.model } : {}),\n ...(options.recordCwd !== undefined ? { recordCwd: options.recordCwd } : {}),\n interactions,\n };\n writeFileSync(options.cassettePath, `${JSON.stringify(cassette, null, 2)}\\n`, 'utf8');\n };\n\n return {\n name: options.provider.name,\n version: options.provider.version,\n async chat(\n messages: TUniversalMessage[],\n chatOptions?: IChatOptions,\n ): Promise<TUniversalMessage> {\n const response = await options.provider.chat(messages, chatOptions);\n // Store the raw response; the record workspace path is rewritten at replay time, not here.\n interactions.push({\n requestHash: hashRequest(messages, scrub),\n requestPreview: previewRequest(messages, scrub),\n response,\n });\n flush();\n return response;\n },\n ...delegate(options.provider),\n };\n}\n\nfunction reviveResponse(\n response: TUniversalMessage,\n rewriteCwd: string | undefined,\n recordCwd: string | undefined,\n): TUniversalMessage {\n let revived = response;\n if (rewriteCwd && recordCwd && rewriteCwd !== recordCwd) {\n revived = JSON.parse(\n JSON.stringify(revived).split(recordCwd).join(rewriteCwd),\n ) as TUniversalMessage;\n }\n // The timestamp round-trips through JSON as a string; the loop expects a Date.\n return { ...revived, timestamp: new Date(revived.timestamp as unknown as string) };\n}\n\nexport interface IReplayProviderOptions {\n cassettePath: string;\n /** Volatile substrings normalized out of the staleness hash (must match the record-time scrub). */\n scrub?: readonly string[];\n /** Replay workspace path; the recorded workspace path in responses is rewritten to it. */\n rewriteCwd?: string;\n}\n\n/** Replay a recorded cassette deterministically, failing clearly on staleness or exhaustion. */\nexport function createReplayProvider(options: IReplayProviderOptions): IAIProvider {\n const cassette = JSON.parse(readFileSync(options.cassettePath, 'utf8')) as ICassette;\n // Record-time hashing scrubbed the record workspace path → SCRUB_TOKEN; replay must scrub the\n // replay workspace path the same way so an unchanged flow matches across the two runs.\n const scrub = options.scrub ?? (options.rewriteCwd ? [options.rewriteCwd] : []);\n let cursor = 0;\n\n return {\n name: 'cassette-replay-provider',\n version: 'test',\n async chat(messages: TUniversalMessage[]): Promise<TUniversalMessage> {\n const interaction = cassette.interactions[cursor];\n if (!interaction) {\n throw new Error(\n `Cassette exhausted at call ${cursor + 1} (recorded ${cassette.interactions.length}) — re-record ${options.cassettePath}`,\n );\n }\n const incoming = hashRequest(messages, scrub);\n if (incoming !== interaction.requestHash) {\n throw new Error(\n `Cassette stale at call ${cursor + 1}: the request changed since recording.\\n` +\n ` recorded: ${interaction.requestPreview}\\n` +\n ` current : ${previewRequest(messages, scrub)}\\n` +\n `Re-record ${options.cassettePath}.`,\n );\n }\n cursor += 1;\n return reviveResponse(interaction.response, options.rewriteCwd, cassette.recordCwd);\n },\n async generateResponse() {\n return { content: 'cassette replay does not implement raw responses' };\n },\n supportsTools() {\n return true;\n },\n validateConfig() {\n return true;\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["subsetOnlyFixture","rootAnyOfFixture","nestedAnyOfOptionalFixture","unknownKeywordsFixture","deepNestingFixture","arrayItemsObjectFixture","oneInvalidAmongManyFixture"],"sources":["../../../src/testing/scripted-provider.ts","../../../src/testing/cassette-provider.ts","../../../src/schema/__tests__/fixtures/tool-schema-projection/array-items-object.json","../../../src/schema/__tests__/fixtures/tool-schema-projection/deep-nesting.json","../../../src/schema/__tests__/fixtures/tool-schema-projection/nested-anyof-optional.json","../../../src/schema/__tests__/fixtures/tool-schema-projection/one-invalid-among-many.json","../../../src/schema/__tests__/fixtures/tool-schema-projection/root-anyof.json","../../../src/schema/__tests__/fixtures/tool-schema-projection/subset-only.json","../../../src/schema/__tests__/fixtures/tool-schema-projection/unknown-keywords.json","../../../src/testing/tool-schema-projection-fixtures.ts"],"sourcesContent":["/**\n * Deterministic scripted provider for functional tests (TEST-003 SSOT).\n *\n * Replays a declared sequence of assistant turns through the REAL agent loop — tool execution,\n * permission gate, persistence, and output all run unmocked. It implements only the agent-core\n * `IAIProvider` contract, so it lives at the lowest layer that owns that abstraction; higher\n * layers (`agent-framework`, `agent-transport`) re-export it through their `./testing` subpaths.\n *\n * Test-only: exported via the `@robota-sdk/agent-core/testing` subpath and never imported by\n * runtime code.\n */\n\nimport type {\n IAIProvider,\n IChatOptions,\n IRawProviderResponse,\n TUniversalMessage,\n} from '../index.js';\n\n/** ANALYTICS-001: optional token usage a scripted turn reports, so usage-based tests are possible. */\nexport interface IScriptedTurnUsage {\n inputTokens: number;\n outputTokens: number;\n}\n\n/** One scripted assistant turn: plain text or tool invocations, optionally reporting token usage. */\nexport type TScriptedTurn =\n | { text: string; usage?: IScriptedTurnUsage }\n | {\n toolCalls: ReadonlyArray<{ name: string; args: Record<string, unknown> }>;\n usage?: IScriptedTurnUsage;\n };\n\nexport interface IScriptedProvider {\n provider: IAIProvider;\n /** Message arrays of every chat() call, in order, for request assertions. */\n requests: TUniversalMessage[][];\n /**\n * The `IChatOptions` of every chat() call, in order.\n *\n * CORE-042: the two entry points must build the SAME options object, and nothing could assert\n * that while the double discarded them.\n */\n chatOptions: Array<IChatOptions | undefined>;\n}\n\nexport function createScriptedProvider(turns: readonly TScriptedTurn[]): IScriptedProvider {\n const requests: TUniversalMessage[][] = [];\n const chatOptions: Array<IChatOptions | undefined> = [];\n let cursor = 0;\n\n const provider: IAIProvider = {\n name: 'scripted-test-provider',\n version: 'test',\n async chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage> {\n requests.push([...messages]);\n chatOptions.push(options);\n const turn = turns[cursor];\n if (turn === undefined) {\n throw new Error(\n `Scripted provider: script exhausted at call ${cursor + 1} (script declares ${turns.length} turn(s)) — extend the script instead of relying on improvised responses`,\n );\n }\n cursor += 1;\n // ANALYTICS-001: surface declared usage in metadata so the real usage pipeline records it.\n const usageMetadata = turn.usage\n ? {\n metadata: {\n inputTokens: turn.usage.inputTokens,\n outputTokens: turn.usage.outputTokens,\n totalTokens: turn.usage.inputTokens + turn.usage.outputTokens,\n },\n }\n : {};\n if ('text' in turn) {\n // CORE-042: `IChatOptions.onTextDelta` requires a provider to stream internally and call\n // this per chunk WHILE still returning the assembled message. This double returned the text\n // and emitted nothing, so an agent streaming through it produced no deltas -- the double,\n // not the product, was the reason a streaming test needed a second engine to observe. The\n // whole text goes out as one delta: a scripted turn has no chunk boundaries to honour, and\n // splitting it arbitrarily would invent a fragmentation behaviour no real provider promised.\n if (turn.text.length > 0) {\n options?.onTextDelta?.(turn.text);\n }\n return {\n id: `scripted-${cursor}`,\n role: 'assistant',\n content: turn.text,\n state: 'complete',\n timestamp: new Date(),\n ...usageMetadata,\n };\n }\n return {\n id: `scripted-${cursor}`,\n role: 'assistant',\n content: null,\n state: 'complete',\n timestamp: new Date(),\n ...usageMetadata,\n toolCalls: turn.toolCalls.map((call, index) => ({\n id: `scripted-call-${cursor}-${index}`,\n type: 'function' as const,\n function: { name: call.name, arguments: JSON.stringify(call.args) },\n })),\n };\n },\n async generateResponse(): Promise<IRawProviderResponse> {\n return { content: 'scripted provider does not implement raw responses' };\n },\n supportsTools(): boolean {\n return true;\n },\n validateConfig(): boolean {\n return true;\n },\n };\n\n return { provider, requests, chatOptions };\n}\n","/**\n * TEST-005: record-replay (cassette) provider.\n *\n * The scripted provider verifies the deterministic machinery but ignores the prompt and fakes the\n * model's tool-use. A cassette captures a REAL model's behaviour once (real prompts in, real\n * responses + tool-use out) and replays it deterministically — testing the actual prompts and the\n * model's real decisions in CI at zero per-run cost.\n *\n * - `createRecordingProvider({ provider, cassettePath, … })` wraps a real provider and writes each\n * interaction to the cassette. Used in a one-off record run (needs an API key).\n * - `createReplayProvider({ cassettePath, … })` replays the recorded responses in order, with\n * staleness detection: if the incoming request diverges from the recorded one (e.g. a prompt\n * changed), it fails with a clear \"re-record\" error instead of returning a wrong response.\n *\n * Volatile substrings (e.g. the per-run workspace path) are normalized via `scrub` before hashing\n * so an unchanged flow matches across runs; `rewriteCwd` rewrites the recorded workspace path in\n * replayed responses so recorded absolute tool-call paths land in the replay workspace.\n *\n * Test-only: exported via `@robota-sdk/agent-core/testing`; never imported by runtime code.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFileSync, writeFileSync } from 'node:fs';\n\nimport type { IAIProvider, IChatOptions, IToolCall, TUniversalMessage } from '../index.js';\n\nconst SCRUB_TOKEN = '\u0000SCRUBBED\u0000';\n\n/** One recorded request→response interaction. */\nexport interface ICassetteInteraction {\n requestHash: string;\n requestPreview: string;\n response: TUniversalMessage;\n}\n\n/** A recorded session: an ordered list of interactions plus metadata. */\nexport interface ICassette {\n version: 1;\n provider: string;\n model?: string;\n /** Recorded workspace path, stored verbatim so replay can rewrite it into its own workspace. */\n recordCwd?: string;\n interactions: ICassetteInteraction[];\n}\n\nfunction applyScrub(text: string, scrub: readonly string[]): string {\n let out = text;\n for (const value of scrub) {\n if (value) out = out.split(value).join(SCRUB_TOKEN);\n }\n return out;\n}\n\n/**\n * Normalized, volatile-free projection of a request used for the staleness hash.\n *\n * System messages are excluded: the system prompt embeds host/version-specific content (platform,\n * paths, tool inventory) that differs across machines, so hashing it would make a cassette\n * non-portable (a stale failure in CI for a run recorded on another host). The portable, meaningful\n * content — the user/goal prompts, the assistant tool calls, and the tool results — is retained, so\n * a change to the goal prompts still trips staleness.\n */\nfunction normalizeRequest(messages: TUniversalMessage[], scrub: readonly string[]): string {\n const projection = messages\n .filter((message) => message.role !== 'system')\n .map((message) => {\n const toolCalls =\n 'toolCalls' in message ? (message.toolCalls as IToolCall[] | undefined) : undefined;\n return {\n role: message.role,\n content:\n typeof message.content === 'string'\n ? applyScrub(message.content, scrub)\n : (message.content ?? null),\n toolCalls:\n toolCalls?.map((call) => ({\n name: call.function?.name ?? null,\n args: call.function?.arguments ? applyScrub(call.function.arguments, scrub) : null,\n })) ?? null,\n };\n });\n return JSON.stringify(projection);\n}\n\nfunction hashRequest(messages: TUniversalMessage[], scrub: readonly string[]): string {\n return createHash('sha256').update(normalizeRequest(messages, scrub)).digest('hex').slice(0, 16);\n}\n\nfunction previewRequest(messages: TUniversalMessage[], scrub: readonly string[]): string {\n const last = messages[messages.length - 1];\n const content = typeof last?.content === 'string' ? applyScrub(last.content, scrub) : '';\n return `${messages.length} msg(s); last ${last?.role ?? '?'}: ${content.slice(0, 80)}`;\n}\n\n/** Forward the non-`chat` surface of a wrapped provider unchanged. */\nfunction delegate(provider: IAIProvider): Omit<IAIProvider, 'chat' | 'name' | 'version'> {\n return {\n generateResponse: provider.generateResponse.bind(provider),\n supportsTools: provider.supportsTools.bind(provider),\n validateConfig: provider.validateConfig.bind(provider),\n };\n}\n\nexport interface IRecordingProviderOptions {\n provider: IAIProvider;\n cassettePath: string;\n /** Volatile substrings normalized out of the staleness hash (e.g. the workspace path). */\n scrub?: readonly string[];\n /** Recorded workspace path; replay rewrites it into the replay workspace. */\n recordCwd?: string;\n model?: string;\n}\n\n/** Wrap a real provider, recording every interaction to the cassette file as it runs. */\nexport function createRecordingProvider(options: IRecordingProviderOptions): IAIProvider {\n const scrub = options.scrub ?? (options.recordCwd ? [options.recordCwd] : []);\n const interactions: ICassetteInteraction[] = [];\n\n const flush = (): void => {\n const cassette: ICassette = {\n version: 1,\n provider: options.provider.name,\n ...(options.model !== undefined ? { model: options.model } : {}),\n ...(options.recordCwd !== undefined ? { recordCwd: options.recordCwd } : {}),\n interactions,\n };\n writeFileSync(options.cassettePath, `${JSON.stringify(cassette, null, 2)}\\n`, 'utf8');\n };\n\n return {\n name: options.provider.name,\n version: options.provider.version,\n async chat(\n messages: TUniversalMessage[],\n chatOptions?: IChatOptions,\n ): Promise<TUniversalMessage> {\n const response = await options.provider.chat(messages, chatOptions);\n // Store the raw response; the record workspace path is rewritten at replay time, not here.\n interactions.push({\n requestHash: hashRequest(messages, scrub),\n requestPreview: previewRequest(messages, scrub),\n response,\n });\n flush();\n return response;\n },\n ...delegate(options.provider),\n };\n}\n\nfunction reviveResponse(\n response: TUniversalMessage,\n rewriteCwd: string | undefined,\n recordCwd: string | undefined,\n): TUniversalMessage {\n let revived = response;\n if (rewriteCwd && recordCwd && rewriteCwd !== recordCwd) {\n revived = JSON.parse(\n JSON.stringify(revived).split(recordCwd).join(rewriteCwd),\n ) as TUniversalMessage;\n }\n // The timestamp round-trips through JSON as a string; the loop expects a Date.\n return { ...revived, timestamp: new Date(revived.timestamp as unknown as string) };\n}\n\nexport interface IReplayProviderOptions {\n cassettePath: string;\n /** Volatile substrings normalized out of the staleness hash (must match the record-time scrub). */\n scrub?: readonly string[];\n /** Replay workspace path; the recorded workspace path in responses is rewritten to it. */\n rewriteCwd?: string;\n}\n\n/** Replay a recorded cassette deterministically, failing clearly on staleness or exhaustion. */\nexport function createReplayProvider(options: IReplayProviderOptions): IAIProvider {\n const cassette = JSON.parse(readFileSync(options.cassettePath, 'utf8')) as ICassette;\n // Record-time hashing scrubbed the record workspace path → SCRUB_TOKEN; replay must scrub the\n // replay workspace path the same way so an unchanged flow matches across the two runs.\n const scrub = options.scrub ?? (options.rewriteCwd ? [options.rewriteCwd] : []);\n let cursor = 0;\n\n return {\n name: 'cassette-replay-provider',\n version: 'test',\n async chat(messages: TUniversalMessage[]): Promise<TUniversalMessage> {\n const interaction = cassette.interactions[cursor];\n if (!interaction) {\n throw new Error(\n `Cassette exhausted at call ${cursor + 1} (recorded ${cassette.interactions.length}) — re-record ${options.cassettePath}`,\n );\n }\n const incoming = hashRequest(messages, scrub);\n if (incoming !== interaction.requestHash) {\n throw new Error(\n `Cassette stale at call ${cursor + 1}: the request changed since recording.\\n` +\n ` recorded: ${interaction.requestPreview}\\n` +\n ` current : ${previewRequest(messages, scrub)}\\n` +\n `Re-record ${options.cassettePath}.`,\n );\n }\n cursor += 1;\n return reviveResponse(interaction.response, options.rewriteCwd, cassette.recordCwd);\n },\n async generateResponse() {\n return { content: 'cassette replay does not implement raw responses' };\n },\n supportsTools() {\n return true;\n },\n validateConfig() {\n return true;\n },\n };\n}\n","","","","","","","","/**\n * Shared tool-schema-projection fixtures (MCP-005) — one schema shape per named case, so agent-core's\n * own projector tests and every provider's conformance test (S2) exercise IDENTICAL inputs.\n *\n * Exported from the `./testing` subpath (TEST-003 precedent: `createScriptedProvider` lives the same\n * way) rather than from a `__tests__` directory: `scan-package-boundary-ownership` refuses a\n * cross-package import that reaches another package's private tree, and `__tests__` is exactly that.\n * A provider package's conformance test imports `loadToolSchemaProjectionFixtures` from\n * `@robota-sdk/agent-core/testing`.\n *\n * Most shapes are authored as JSON fixtures under\n * `../schema/__tests__/fixtures/tool-schema-projection/` and imported statically here — a bundler's\n * JSON loader inlines the parsed value into this module at build time, so the compiled `./testing`\n * entry carries the data with no external asset to copy. `prototypeKey` and `oversized` are the two\n * exceptions, built programmatically below (see each function's comment for why).\n */\nimport arrayItemsObjectFixture from '../schema/__tests__/fixtures/tool-schema-projection/array-items-object.json';\nimport deepNestingFixture from '../schema/__tests__/fixtures/tool-schema-projection/deep-nesting.json';\nimport nestedAnyOfOptionalFixture from '../schema/__tests__/fixtures/tool-schema-projection/nested-anyof-optional.json';\nimport oneInvalidAmongManyFixture from '../schema/__tests__/fixtures/tool-schema-projection/one-invalid-among-many.json';\nimport rootAnyOfFixture from '../schema/__tests__/fixtures/tool-schema-projection/root-anyof.json';\nimport subsetOnlyFixture from '../schema/__tests__/fixtures/tool-schema-projection/subset-only.json';\nimport unknownKeywordsFixture from '../schema/__tests__/fixtures/tool-schema-projection/unknown-keywords.json';\nimport { TOOL_SCHEMA_PROJECTION_MAX_NODES } from '../schema/project-tool-schema';\n\nimport type { IToolSchema } from '../interfaces/tool-schema';\n\n/**\n * A property literally named `__proto__`, exactly as a parsed third-party schema could carry it.\n *\n * NOT loaded via a static JSON import: a bundler's JSON loader inlines the parsed value as an object\n * LITERAL, and a literal property written `\"__proto__\": value` (quoted or not) triggers the\n * ECMAScript `[[SetPrototypeOf]]` special case (Annex B.3.1) instead of creating an own property —\n * the opposite of what `JSON.parse` does for the same text over the wire (verified empirically: a\n * fixture authored as `prototype-key.json` and statically imported lost the property entirely). A\n * COMPUTED key (`['__proto__']`) is the one object-literal spelling that is not special-cased, so\n * this one fixture is authored directly in TypeScript instead of as JSON.\n */\nfunction buildPrototypeKeyFixture(): IToolSchema {\n return {\n name: 'malicious_property_name',\n description:\n 'A property named as a prototype key — reachable from a parsed third-party schema.',\n parameters: {\n type: 'object',\n properties: {\n safe: { type: 'string' },\n ['__proto__']: { type: 'string' },\n },\n required: ['safe'],\n },\n };\n}\n\n/**\n * More leaf properties than any shipped profile's `maxNodes` ceiling — generated here rather than\n * stored as a multi-thousand-line JSON file nobody would review line by line.\n */\nfunction buildOversizedFixture(): IToolSchema {\n const properties: Record<string, { type: 'string' }> = {};\n const propertyCount = TOOL_SCHEMA_PROJECTION_MAX_NODES + 500;\n for (let index = 0; index < propertyCount; index += 1) {\n properties[`field_${index}`] = { type: 'string' };\n }\n return {\n name: 'oversized_tool',\n description: \"A schema with more nodes than any shipped profile's maxNodes ceiling.\",\n parameters: { type: 'object', properties, required: [] },\n };\n}\n\n/** The named tool-schema-projection fixture set (MCP-005). Treat every value as read-only. */\nexport interface IToolSchemaProjectionFixtures {\n /** Only members of the `IParameterSchema` subset — projects `adopted` under every profile. */\n readonly subsetOnly: IToolSchema;\n /** `parameters` is a root union, not `type: 'object'` — universal rejection. */\n readonly rootAnyOf: IToolSchema;\n /** A nested (non-root) optional `anyOf` that already admits `null` — legitimate union usage. */\n readonly nestedAnyOfOptional: IToolSchema;\n /** `$ref`/`oneOf`/`allOf`-only nodes, annotations, `patternProperties`, `$defs`, `minLength`. */\n readonly unknownKeywords: IToolSchema;\n /** A property named `__proto__` — refused before any spread. */\n readonly prototypeKey: IToolSchema;\n /** Nested 40 levels deep — over every shipped profile's `maxDepth` (32). */\n readonly deepNesting: IToolSchema;\n /** Over every shipped profile's `maxNodes` (2000). */\n readonly oversized: IToolSchema;\n /** An array property whose `items` is an object schema. */\n readonly arrayItemsObject: IToolSchema;\n /** Three tools; the middle one is a root-union rejection — per-tool quarantine, not a whole-request failure. */\n readonly oneInvalidAmongMany: readonly IToolSchema[];\n}\n\n/** The shared MCP-005 fixture set. Returns the same underlying values on every call — do not mutate them. */\nexport function loadToolSchemaProjectionFixtures(): IToolSchemaProjectionFixtures {\n return {\n subsetOnly: subsetOnlyFixture as unknown as IToolSchema,\n rootAnyOf: rootAnyOfFixture as unknown as IToolSchema,\n nestedAnyOfOptional: nestedAnyOfOptionalFixture as unknown as IToolSchema,\n unknownKeywords: unknownKeywordsFixture as unknown as IToolSchema,\n prototypeKey: buildPrototypeKeyFixture(),\n deepNesting: deepNestingFixture as unknown as IToolSchema,\n oversized: buildOversizedFixture(),\n arrayItemsObject: arrayItemsObjectFixture as unknown as IToolSchema,\n oneInvalidAmongMany: oneInvalidAmongManyFixture as unknown as IToolSchema[],\n };\n}\n"],"mappings":"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"}
|